From 0779b37f24624d7e44edda5eefdc9414389e0e69 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Tue, 17 Mar 2026 19:01:38 +0200 Subject: [PATCH 1/2] Refresh website video docs and transcript tooling --- .gitignore | 5 + .../001-introduction.md | 25 +- .../002-creating-a-hello-world-app.md | 35 +- ...003-core-concepts-of-mobile-development.md | 31 +- .../004-what-is-codename-one.md | 30 +- ...5-anatomy-of-a-codename-one-application.md | 31 +- ...6-internationalization-and-localization.md | 30 +- .../007-layout-basics.md | 31 +- .../008-theme-basics.md | 31 +- .../009-adapting-a-ui-design.md | 34 +- .../010-storage-filesystem-and-sql.md | 29 +- .../011-threading-and-the-edt.md | 30 +- .../012-understanding-properties.md | 27 +- .../013-push-notification.md | 30 +- ...nterfaces-access-native-device-features.md | 32 +- .../001-working-with-css.md | 36 +- .../002-introduction-to-spring-boot.md | 28 +- .../003-connecting-to-a-web-service.md | 29 +- .../004-introduction.md | 27 +- .../005-cutting-images-in-photoshop.md | 29 +- .../006-css.md | 31 +- .../007-baseform.md | 29 +- .../008-mainmenuform.md | 29 +- .../009-checkoutform.md | 27 +- .../010-introduction.md | 27 +- .../011-the-new-forms.md | 29 +- .../012-fixing-the-checkout-experience.md | 28 +- .../013-css-changes.md | 29 +- .../014-code-changes-and-summary.md | 29 +- .../015-overview-and-basic-model.md | 28 +- .../016-integration-and-summary.md | 27 +- .../017-the-native-interface-callback.md | 28 +- .../018-dependencies-gradle-and-cocoapods.md | 26 +- .../019-the-native-code.md | 29 +- .../020-introduction-and-generic-code.md | 26 +- ...plementing-the-native-camera-on-android.md | 28 +- .../022-camera-ios-port-basics.md | 28 +- .../023-arc-and-view-on-ios.md | 28 +- .../024-capture-and-callbacks-in-ios.md | 26 +- .../025-communicating-with-the-server.md | 28 +- .../026-address-and-validation.md | 28 +- .../027-categories-and-search.md | 26 +- ...security-basics-and-certificate-pinning.md | 28 +- ...e-encryption-and-misc-security-features.md | 26 +- .../030-introduction-and-installation.md | 26 +- .../031-hello-world-and-devices.md | 26 +- ...ers-lightweight-overlays-and-map-layout.md | 26 +- .../033-introduction-and-setup.md | 26 +- ...nning-the-kitchen-sink-in-the-simulator.md | 24 +- ...g-a-desktop-version-of-the-kitchen-sink.md | 26 +- ...roid-native-version-of-the-kitchen-sink.md | 24 +- ...-ios-native-version-of-the-kitchen-sink.md | 24 +- ...s-performance-breaking-down-the-problem.md | 28 +- ...t-threading-caching-and-soft-references.md | 30 +- ...pes-and-their-impact-on-performance-ram.md | 28 +- ...-network-parsing-and-resource-file-size.md | 28 +- ...ktop-using-the-performance-monitor-tool.md | 28 +- ...43-profiling-on-devices-ios-and-android.md | 26 +- ...erformance-problems-in-the-kitchen-sink.md | 26 +- .../001-server.md | 28 +- .../002-scope-and-basic-ui-design.md | 28 +- .../003-fleshing-out-the-ui-design.md | 28 +- .../004-architecture-of-mockup.md | 26 +- ...-base-navigation-form-and-shape-effects.md | 26 +- .../006-dish-list-and-edit.md | 28 +- ...oduction-architecture-and-authorization.md | 28 +- .../008-rest-api-design.md | 26 +- .../009-communicating-from-the-client.md | 26 +- ...traction-with-object-relational-mapping.md | 26 +- .../011-integrating-sqlite-into-the-code.md | 26 +- .../012-details-categories-and-validation.md | 26 +- .../013-billing-and-global-server.md | 26 +- .../014-sidemenu-and-preview.md | 26 +- .../015-about-forms.md | 28 +- ...customization-1-introduction-and-basics.md | 28 +- ...customization-2-the-customization-popup.md | 26 +- ...-customization-3-font-and-color-pickers.md | 26 +- ...e-customization-4-saving-style-settings.md | 26 +- ...020-push-1-initial-registration-process.md | 33 +- .../021-push-2-client-side-code.md | 35 +- ...-push-3-the-server-side-and-build-logic.md | 33 +- .../023-push-http-fallback.md | 31 +- .../024-push-websockets-fallback.md | 35 +- .../025-in-app-purchase.md | 33 +- .../026-setting-up-the-vps-server.md | 31 +- .../027-yum-mariadb-security-and-iptables.md | 29 +- .../028-starting-the-server-on-boot.md | 27 +- ...let-s-encrypt-https-certificate-support.md | 29 +- ...automating-lets-encrypt-renewal-process.md | 27 +- .../031-abstraction-and-architecture.md | 29 +- .../032-the-uiabstraction-class.md | 31 +- .../033-the-tabletui-class.md | 29 +- .../034-putting-it-all-together.md | 29 +- .../035-transitions.md | 29 +- .../036-layout-animations.md | 31 +- ...yle-animations-and-low-level-animations.md | 31 +- .../038-1-introduction.md | 230 ++++++- .../039-2-basic-setup.md | 200 +++++- .../040-3-login-and-country-code.md | 178 +++++- .../041-4-login-shadow-and-rotation.md | 190 +++++- .../042-5-social-login-and-country-picker.md | 183 +++++- .../043-6-sms-activation-flow.md | 212 ++++++- .../044-7-map-form.md | 159 ++++- .../045-8-where-to-ui.md | 126 +++- .../046-9-where-to-ui-part-ii.md | 154 ++++- .../047-10-side-menu.md | 72 ++- .../048-11-the-spring-boot-server.md | 394 +++++++++++- .../049-12-server-websocket-handler.md | 193 +++++- .../050-13-client-side-userservice.md | 149 ++++- .../051-14-sms-activation-and-interception.md | 106 +++- .../052-15-location-service-client-side.md | 111 +++- ...ng-the-location-service-to-the-map-form.md | 67 +- ...4-17-reverse-gecoding-google-webservice.md | 221 ++++++- ...rections-and-places-google-web-services.md | 101 ++- ...056-19-auto-complete-location-search-ui.md | 168 ++++- .../057-20-search-completion-container.md | 81 ++- ...-21-plotting-the-route-on-the-map-setup.md | 104 +++- ...tting-the-route-on-the-map-to-from-tags.md | 133 +++- ...lotting-the-route-on-the-map-completion.md | 122 +++- ...-hailing-in-the-client-showing-a-beacon.md | 75 ++- ...nt-networking-and-sending-push-messages.md | 132 +++- .../063-26-driver-app-server.md | 188 +++++- ...-27-driver-app-server-websocket-portion.md | 50 +- ...28-the-driver-app-2-apps-in-one-project.md | 167 ++++- ...ng-the-driver-app-and-push-notification.md | 115 +++- .../067-30-driver-and-user-hailing-process.md | 196 +++++- ...raintree-flow-explained-and-server-side.md | 196 +++++- ...69-32-braintree-client-side-integration.md | 67 +- ...33-social-login-basics-and-facebook-app.md | 165 ++++- .../071-34-facebook-and-google-login-code.md | 94 ++- .../072-35-google-login-process.md | 89 ++- ...sition-animating-elements-between-forms.md | 116 +++- ...conditionally-showing-a-form-transition.md | 50 +- ...rcular-floating-action-button-animation.md | 29 +- ...ings-form-and-fetching-the-avatar-image.md | 29 +- ...r-ui-binding-and-multipart-image-upload.md | 29 +- .../078-1-introduction.md | 29 +- .../079-2-creating-the-project-and-css.md | 27 +- .../080-3-splash-screen.md | 27 +- .../081-4-login-form.md | 29 +- .../082-5-rich-text-view-and-signup-form.md | 27 +- .../083-6-signup-form-terms-and-conditions.md | 27 +- ...-7-signup-form-name-birthday-and-gender.md | 27 +- ...m-phone-email-password-and-confirmation.md | 27 +- .../086-9-the-main-form.md | 27 +- ...client-data-model-user-post-and-comment.md | 29 +- .../088-11-serverapi-abstraction-mockup.md | 29 +- .../089-12-the-newsfeed-container.md | 27 +- .../090-13-friends-container.md | 27 +- .../091-14-notifications-container.md | 27 +- .../092-15-the-more-container.md | 27 +- .../093-16-the-new-post-form.md | 27 +- ...server-architecture-and-the-user-entity.md | 29 +- .../095-18-media-entity.md | 27 +- .../096-19-post-and-comment-entities.md | 29 +- ...cation-newsfeed-and-shadowuser-entities.md | 29 +- .../098-21-service-layer-and-userservice.md | 29 +- .../099-22-userservice-part-ii.md | 29 +- ...23-notificationservice-and-mediaservice.md | 29 +- .../101-24-postservice.md | 29 +- ...-25-webservice-layer-and-userwebservice.md | 29 +- ...3-26-postwebservice-and-mediawebservice.md | 27 +- .../104-27-client-side-serverapi.md | 29 +- .../105-28-client-server-signup-process.md | 29 +- .../106-29-newsfeed-and-posts-from-server.md | 27 +- ...-synchronization-accept-reject-requests.md | 29 +- ...ver-side-with-spring-boot-and-hibernate.md | 29 +- ...09-32-search-webservice-and-client-code.md | 27 +- ...110-33-search-client-side-ui-searchform.md | 29 +- ...search-results-ui-userform-and-postform.md | 27 +- ...12-35-threaded-comments-ui-commentsform.md | 27 +- .../113-36-settingsform-cover-and-avatar.md | 27 +- ...stantui-automatic-dynamic-ui-generation.md | 27 +- ...15-38-server-side-post-media-attachment.md | 27 +- ...39-imagepicker-video-and-custom-support.md | 27 +- ...-attachments-client-side-business-logic.md | 27 +- ...1-post-image-and-video-from-newpostform.md | 27 +- ...videos-and-styled-posts-in-the-newsfeed.md | 27 +- .../120-43-low-level-camera-integration.md | 27 +- ...cation-theory-entity-and-service-layers.md | 289 ++++++++- ...push-notification-server-implementation.md | 148 ++++- .../123-46-push-client-side-integration.md | 99 ++- .../124-1-getting-started.md | 104 +++- .../125-2-client-to-server-abstraction.md | 432 ++++++++++++- .../126-3-the-model-package.md | 149 ++++- .../127-4-the-main-class.md | 166 ++++- .../128-5-main-form.md | 296 ++++++++- .../129-6-theme-css.md | 163 ++++- .../130-7-bubble-border.md | 33 +- .../131-8-chat-form.md | 410 +++++++++++- .../132-9-the-new-message-form.md | 52 +- .../133-10-server-entities.md | 122 +++- .../134-11-server-dao-and-entities.md | 81 ++- .../135-12-user-service.md | 168 ++++- .../136-13-user-web-service.md | 49 +- .../137-14-web-socket.md | 98 ++- .../138-introduction.md | 140 ++++- .../139-server-part-i.md | 295 ++++++++- .../140-server-part-ii.md | 195 +++++- .../141-server-part-iii.md | 159 ++++- .../142-client-model.md | 55 +- .../143-client-ui.md | 192 +++++- ...-functionality-invoke-native-interfaces.md | 68 +- ...rvices-perform-operations-on-the-server.md | 26 +- .../how-do-i-create-a-9-piece-image-border.md | 87 +-- ...tion-send-it-to-my-device-using-eclipse.md | 52 +- ...end-it-to-my-device-using-intellij-idea.md | 53 +- ...ion-send-it-to-my-device-using-netbeans.md | 55 +- ...o-i-create-a-list-of-items-the-easy-way.md | 61 +- .../howdoi/how-do-i-create-a-simple-theme.md | 32 +- ...do-i-create-an-ios-provisioning-profile.md | 26 +- .../how-do-i-create-gorgeous-sidemenu.md | 55 +- ...one-source-modify-it-contribute-it-back.md | 29 +- .../how-do-i-debug-on-an-android-device.md | 30 +- ...from-the-resource-file-add-a-multiimage.md | 57 +- ...me-one-tools-and-the-standard-ide-tools.md | 28 +- ...codename-one-use-the-versioning-feature.md | 28 +- ...gui-builder-populate-the-form-from-code.md | 26 +- ...rmance-or-track-down-performance-issues.md | 29 +- ...ernationalizationlocalization-to-my-app.md | 62 +- ...ioning-components-using-layout-managers.md | 68 +- ...how-do-i-take-a-picture-with-the-camera.md | 25 +- .../howdoi/how-do-i-use-cloud-connect.md | 39 +- ...-i-use-crash-protection-get-device-logs.md | 34 +- .../how-do-i-use-desktop-javascript-ports.md | 53 +- ...use-http-sockets-webservices-websockets.md | 59 +- .../howdoi/how-do-i-use-offline-build.md | 74 +-- ...o-i-use-properties-to-speed-development.md | 37 +- ...-notification-send-server-push-messages.md | 93 +-- .../how-do-i-use-storage-file-system-sql.md | 41 +- ...debug-the-native-code-on-iosandroid-etc.md | 69 +-- .../bootstrap_video_update_sections.py | 113 ++++ .../scripts/build_video_refresh_inventory.py | 221 +++++++ .../scripts/download_youtube_captions.py | 272 ++++++++ .../scripts/fetch_missing_transcripts.py | 243 ++++++++ docs/website/scripts/inject_transcripts.py | 104 ++++ .../scripts/normalize_transcript_cache.py | 43 ++ .../scripts/report_video_refresh_progress.py | 43 ++ docs/website/scripts/video_refresh_lib.py | 283 +++++++++ .../video-transcripts/-7XHkBMK4NY.json | 8 + .../website/video-transcripts/-7XHkBMK4NY.txt | 120 ++++ .../video-transcripts/-M957AAi-vk.json | 9 + .../website/video-transcripts/-M957AAi-vk.txt | 48 ++ .../video-transcripts/-aTpqxDa1Ag.json | 8 + .../website/video-transcripts/-aTpqxDa1Ag.txt | 117 ++++ .../video-transcripts/-brmZYVWb0Y.json | 8 + .../website/video-transcripts/-brmZYVWb0Y.txt | 194 ++++++ .../video-transcripts/-f1yue3hDEk.json | 10 + .../website/video-transcripts/-f1yue3hDEk.txt | 162 +++++ .../video-transcripts/008AK1GfHA8.json | 9 + .../website/video-transcripts/008AK1GfHA8.txt | 18 + .../video-transcripts/0ETli_N__ZY.json | 8 + .../website/video-transcripts/0ETli_N__ZY.txt | 46 ++ .../video-transcripts/0a6mPI412C4.json | 8 + .../website/video-transcripts/0a6mPI412C4.txt | 114 ++++ .../video-transcripts/0l4R049tSOY.json | 8 + .../website/video-transcripts/0l4R049tSOY.txt | 52 ++ .../video-transcripts/0m7Bay4g93k.json | 9 + .../website/video-transcripts/0m7Bay4g93k.txt | 50 ++ .../video-transcripts/16-Vkgcx2kg.json | 10 + .../website/video-transcripts/16-Vkgcx2kg.txt | 147 +++++ .../video-transcripts/17ISIksjcPM.json | 10 + .../website/video-transcripts/17ISIksjcPM.txt | 266 ++++++++ .../video-transcripts/1wHGnmO-vtE.json | 8 + .../website/video-transcripts/1wHGnmO-vtE.txt | 155 +++++ .../video-transcripts/2nD75pODPWk.json | 8 + .../website/video-transcripts/2nD75pODPWk.txt | 139 +++++ .../video-transcripts/2xKvvv7XoVQ.json | 9 + .../website/video-transcripts/2xKvvv7XoVQ.txt | 55 ++ .../video-transcripts/3-ZH2IFIIMY.json | 8 + .../website/video-transcripts/3-ZH2IFIIMY.txt | 187 ++++++ .../video-transcripts/32mkZymqa6E.json | 9 + .../website/video-transcripts/32mkZymqa6E.txt | 49 ++ .../video-transcripts/3B7C0ZbV8Bc.json | 8 + .../website/video-transcripts/3B7C0ZbV8Bc.txt | 338 ++++++++++ .../video-transcripts/3IC2qZ3wUO4.json | 8 + .../website/video-transcripts/3IC2qZ3wUO4.txt | 132 ++++ .../video-transcripts/3rLf9A9RYPY.json | 8 + .../website/video-transcripts/3rLf9A9RYPY.txt | 206 +++++++ .../video-transcripts/46TpE-Cgtw8.json | 8 + .../website/video-transcripts/46TpE-Cgtw8.txt | 143 +++++ .../video-transcripts/47WhIiLxv78.json | 8 + .../website/video-transcripts/47WhIiLxv78.txt | 189 ++++++ .../video-transcripts/4D_KUa2qv2o.json | 9 + .../website/video-transcripts/4D_KUa2qv2o.txt | 55 ++ .../video-transcripts/4jIqplr19HA.json | 8 + .../website/video-transcripts/4jIqplr19HA.txt | 117 ++++ .../video-transcripts/54POZ4PFFBw.json | 8 + .../website/video-transcripts/54POZ4PFFBw.txt | 126 ++++ .../video-transcripts/5EyrMpQqR-k.json | 8 + .../website/video-transcripts/5EyrMpQqR-k.txt | 162 +++++ .../video-transcripts/5WpZmIkdUfs.json | 8 + .../website/video-transcripts/5WpZmIkdUfs.txt | 142 +++++ .../video-transcripts/5eMvwRcDcug.json | 10 + .../website/video-transcripts/5eMvwRcDcug.txt | 209 +++++++ .../video-transcripts/65LciCzyRNQ.json | 8 + .../website/video-transcripts/65LciCzyRNQ.txt | 160 +++++ .../video-transcripts/65jD9oGw61w.json | 8 + .../website/video-transcripts/65jD9oGw61w.txt | 404 ++++++++++++ .../video-transcripts/6oTy-LcTm0s.json | 9 + .../website/video-transcripts/6oTy-LcTm0s.txt | 55 ++ .../video-transcripts/73d65cvyQv4.json | 9 + .../website/video-transcripts/73d65cvyQv4.txt | 35 ++ .../video-transcripts/77N2t2n8rbQ.json | 9 + .../website/video-transcripts/77N2t2n8rbQ.txt | 27 + .../video-transcripts/7CoD9u6KM2Q.json | 8 + .../website/video-transcripts/7CoD9u6KM2Q.txt | 289 +++++++++ .../video-transcripts/7G4OkjgTWIQ.json | 10 + .../website/video-transcripts/7G4OkjgTWIQ.txt | 159 +++++ .../website/video-transcripts/7XHkBMK4NY.json | 8 + docs/website/video-transcripts/7XHkBMK4NY.txt | 120 ++++ .../video-transcripts/85MyytvMS9I.json | 8 + .../website/video-transcripts/85MyytvMS9I.txt | 226 +++++++ .../video-transcripts/8WER-8R0WqA.json | 8 + .../website/video-transcripts/8WER-8R0WqA.txt | 126 ++++ .../video-transcripts/8fxZVc1hw6Q.json | 8 + .../website/video-transcripts/8fxZVc1hw6Q.txt | 343 +++++++++++ .../video-transcripts/8wzBpEp81Kc.json | 9 + .../website/video-transcripts/8wzBpEp81Kc.txt | 81 +++ .../video-transcripts/91BrBoia4nM.json | 8 + .../website/video-transcripts/91BrBoia4nM.txt | 161 +++++ .../video-transcripts/99DAeP9LG6c.json | 9 + .../website/video-transcripts/99DAeP9LG6c.txt | 44 ++ .../video-transcripts/9T8MBBuWBDs.json | 8 + .../website/video-transcripts/9T8MBBuWBDs.txt | 110 ++++ .../video-transcripts/A72rY4rU7E0.json | 8 + .../website/video-transcripts/A72rY4rU7E0.txt | 215 +++++++ .../video-transcripts/ACsZ8qiwR8Q.json | 9 + .../website/video-transcripts/ACsZ8qiwR8Q.txt | 76 +++ .../video-transcripts/AFvuY7Ev-XA.json | 8 + .../website/video-transcripts/AFvuY7Ev-XA.txt | 66 ++ .../video-transcripts/AIsD5MXEK-c.json | 8 + .../website/video-transcripts/AIsD5MXEK-c.txt | 80 +++ .../video-transcripts/AMlnslUn1bA.json | 8 + .../website/video-transcripts/AMlnslUn1bA.txt | 78 +++ .../video-transcripts/B4QCJRFpG-k.json | 8 + .../website/video-transcripts/B4QCJRFpG-k.txt | 126 ++++ .../video-transcripts/BSj3KRM5Sj0.json | 8 + .../website/video-transcripts/BSj3KRM5Sj0.txt | 75 +++ .../video-transcripts/BSrupbUahRM.json | 8 + .../website/video-transcripts/BSrupbUahRM.txt | 43 ++ .../video-transcripts/BXwh2T7wvfc.json | 8 + .../website/video-transcripts/BXwh2T7wvfc.txt | 92 +++ .../video-transcripts/BY1lMQz873g.json | 8 + .../website/video-transcripts/BY1lMQz873g.txt | 193 ++++++ .../video-transcripts/BbVoa3vw7OM.json | 8 + .../website/video-transcripts/BbVoa3vw7OM.txt | 157 +++++ .../video-transcripts/BwWhFcHc6LM.json | 8 + .../website/video-transcripts/BwWhFcHc6LM.txt | 157 +++++ .../video-transcripts/C3PLjAWQ-XA.json | 9 + .../website/video-transcripts/C3PLjAWQ-XA.txt | 20 + .../video-transcripts/Cc8YG-_M02M.json | 8 + .../website/video-transcripts/Cc8YG-_M02M.txt | 172 ++++++ .../video-transcripts/CvbcWC4hLYk.json | 8 + .../website/video-transcripts/CvbcWC4hLYk.txt | 64 ++ .../video-transcripts/DWifLQZyPDE.json | 8 + .../website/video-transcripts/DWifLQZyPDE.txt | 362 +++++++++++ .../video-transcripts/Dh1bYqiKBQo.json | 8 + .../website/video-transcripts/Dh1bYqiKBQo.txt | 116 ++++ .../video-transcripts/DyzEgAGyRcA.json | 8 + .../website/video-transcripts/DyzEgAGyRcA.txt | 84 +++ .../video-transcripts/EkiTDQn9Cpg.json | 8 + .../website/video-transcripts/EkiTDQn9Cpg.txt | 388 ++++++++++++ .../video-transcripts/FKTM8jAepJs.json | 8 + .../website/video-transcripts/FKTM8jAepJs.txt | 98 +++ .../video-transcripts/FSek5IOQ0IE.json | 8 + .../website/video-transcripts/FSek5IOQ0IE.txt | 245 ++++++++ .../video-transcripts/FcHVK9ObONg.json | 8 + .../website/video-transcripts/FcHVK9ObONg.txt | 105 ++++ .../video-transcripts/FczBAgUIb1c.json | 8 + .../website/video-transcripts/FczBAgUIb1c.txt | 64 ++ .../video-transcripts/GEG_S-dyxaM.json | 8 + .../website/video-transcripts/GEG_S-dyxaM.txt | 65 ++ .../video-transcripts/GEzM1MXkqnk.json | 8 + .../website/video-transcripts/GEzM1MXkqnk.txt | 93 +++ .../video-transcripts/GMnWMB_JfaQ.json | 8 + .../website/video-transcripts/GMnWMB_JfaQ.txt | 130 ++++ .../video-transcripts/H7WW4GwXK8o.json | 8 + .../website/video-transcripts/H7WW4GwXK8o.txt | 45 ++ .../video-transcripts/HJvMQKM5FPY.json | 8 + .../website/video-transcripts/HJvMQKM5FPY.txt | 49 ++ .../video-transcripts/HRTLnLecoMA.json | 10 + .../website/video-transcripts/HRTLnLecoMA.txt | 110 ++++ .../video-transcripts/IaalGP1UDeU.json | 8 + .../website/video-transcripts/IaalGP1UDeU.txt | 44 ++ .../video-transcripts/IqsUSCgSVTo.json | 9 + .../website/video-transcripts/IqsUSCgSVTo.txt | 62 ++ .../video-transcripts/IxVUX0s2Nms.json | 8 + .../website/video-transcripts/IxVUX0s2Nms.txt | 136 ++++ .../video-transcripts/JWLsSbbo4N4.json | 8 + .../website/video-transcripts/JWLsSbbo4N4.txt | 83 +++ .../video-transcripts/JZvrsZzdays.json | 8 + .../website/video-transcripts/JZvrsZzdays.txt | 93 +++ .../video-transcripts/Jk3tTyZroP0.json | 8 + .../website/video-transcripts/Jk3tTyZroP0.txt | 162 +++++ .../video-transcripts/KckMUmmSzd4.json | 8 + .../website/video-transcripts/KckMUmmSzd4.txt | 182 ++++++ .../video-transcripts/Ke8bjFdD1bc.json | 8 + .../website/video-transcripts/Ke8bjFdD1bc.txt | 95 +++ .../video-transcripts/KhSWyE6rAN8.json | 8 + .../website/video-transcripts/KhSWyE6rAN8.txt | 95 +++ .../video-transcripts/L7ulPCUxIsw.json | 8 + .../website/video-transcripts/L7ulPCUxIsw.txt | 121 ++++ .../video-transcripts/LFoSl_a2rs8.json | 8 + .../website/video-transcripts/LFoSl_a2rs8.txt | 283 +++++++++ .../video-transcripts/LUHb5fuWzJE.json | 8 + .../website/video-transcripts/LUHb5fuWzJE.txt | 171 ++++++ .../video-transcripts/LZ_ydua__gY.json | 8 + .../website/video-transcripts/LZ_ydua__gY.txt | 74 +++ .../video-transcripts/Lai--eYYJTw.json | 8 + .../website/video-transcripts/Lai--eYYJTw.txt | 148 +++++ .../video-transcripts/M518p4_Horg.json | 8 + .../website/video-transcripts/M518p4_Horg.txt | 104 ++++ .../video-transcripts/MLbOdCt67Rw.json | 10 + .../website/video-transcripts/MLbOdCt67Rw.txt | 63 ++ .../video-transcripts/MbTEz-K8iwY.json | 10 + .../website/video-transcripts/MbTEz-K8iwY.txt | 193 ++++++ .../video-transcripts/Mcw8z_uP3BA.json | 10 + .../website/video-transcripts/Mcw8z_uP3BA.txt | 273 ++++++++ .../video-transcripts/NVPIXnkoxv0.json | 9 + .../website/video-transcripts/NVPIXnkoxv0.txt | 29 + .../video-transcripts/Nv_0NVgCbSk.json | 8 + .../website/video-transcripts/Nv_0NVgCbSk.txt | 139 +++++ .../video-transcripts/OWHizrNyizQ.json | 8 + .../website/video-transcripts/OWHizrNyizQ.txt | 29 + .../video-transcripts/OnZIaGXDZSo.json | 8 + .../website/video-transcripts/OnZIaGXDZSo.txt | 154 +++++ .../video-transcripts/Otujb_KyofA.json | 8 + .../website/video-transcripts/Otujb_KyofA.txt | 176 ++++++ .../video-transcripts/PERdJq3I-As.json | 8 + .../website/video-transcripts/PERdJq3I-As.txt | 77 +++ .../video-transcripts/PHb5YO77OQk.json | 8 + .../website/video-transcripts/PHb5YO77OQk.txt | 120 ++++ .../video-transcripts/PRk7EhXQRqs.json | 8 + .../website/video-transcripts/PRk7EhXQRqs.txt | 213 +++++++ .../video-transcripts/PbRbzCGQCQE.json | 10 + .../website/video-transcripts/PbRbzCGQCQE.txt | 151 +++++ .../video-transcripts/Q0fkApAGMZA.json | 8 + .../website/video-transcripts/Q0fkApAGMZA.txt | 125 ++++ .../video-transcripts/QY7josfVbdg.json | 8 + .../website/video-transcripts/QY7josfVbdg.txt | 246 ++++++++ .../video-transcripts/Qw7moLv-dE4.json | 8 + .../website/video-transcripts/Qw7moLv-dE4.txt | 152 +++++ .../video-transcripts/RBYGdCllnww.json | 8 + .../website/video-transcripts/RBYGdCllnww.txt | 61 ++ .../video-transcripts/Rat-FDqDCTU.json | 8 + .../website/video-transcripts/Rat-FDqDCTU.txt | 103 ++++ .../video-transcripts/Rok0Ubr4xes.json | 8 + .../website/video-transcripts/Rok0Ubr4xes.txt | 116 ++++ .../video-transcripts/RsVOanGYjqo.json | 10 + .../website/video-transcripts/RsVOanGYjqo.txt | 156 +++++ .../video-transcripts/Rt68rA2c6A8.json | 8 + .../website/video-transcripts/Rt68rA2c6A8.txt | 40 ++ .../video-transcripts/S1SHZLXQxSI.json | 8 + .../website/video-transcripts/S1SHZLXQxSI.txt | 148 +++++ .../video-transcripts/S5pAGkV4h4w.json | 8 + .../website/video-transcripts/S5pAGkV4h4w.txt | 341 ++++++++++ .../video-transcripts/SBqYZSdIdec.json | 8 + .../website/video-transcripts/SBqYZSdIdec.txt | 118 ++++ .../video-transcripts/T8l7k2OeGpo.json | 8 + .../website/video-transcripts/T8l7k2OeGpo.txt | 75 +++ .../video-transcripts/TOWhbEhiRe4.json | 8 + .../website/video-transcripts/TOWhbEhiRe4.txt | 100 +++ .../video-transcripts/TRF76P1Dwwc.json | 8 + .../website/video-transcripts/TRF76P1Dwwc.txt | 105 ++++ .../video-transcripts/U0Ms65vcVr4.json | 8 + .../website/video-transcripts/U0Ms65vcVr4.txt | 145 +++++ .../video-transcripts/UU6HCbenVAA.json | 8 + .../website/video-transcripts/UU6HCbenVAA.txt | 153 +++++ .../video-transcripts/UhLFHJj8qnM.json | 8 + .../website/video-transcripts/UhLFHJj8qnM.txt | 143 +++++ .../video-transcripts/UwdI_tMqYgU.json | 8 + .../website/video-transcripts/UwdI_tMqYgU.txt | 88 +++ .../video-transcripts/UxATbrfWveU.json | 8 + .../website/video-transcripts/UxATbrfWveU.txt | 117 ++++ .../video-transcripts/UxZ1HeheGwU.json | 10 + .../website/video-transcripts/UxZ1HeheGwU.txt | 335 ++++++++++ .../video-transcripts/VCNFKMeud-w.json | 8 + .../website/video-transcripts/VCNFKMeud-w.txt | 117 ++++ .../video-transcripts/VDyltmk_Hu8.json | 8 + .../website/video-transcripts/VDyltmk_Hu8.txt | 58 ++ .../video-transcripts/VnYaxvVn6OA.json | 8 + .../website/video-transcripts/VnYaxvVn6OA.txt | 49 ++ .../video-transcripts/WT4cFceBWRA.json | 8 + .../website/video-transcripts/WT4cFceBWRA.txt | 581 ++++++++++++++++++ .../video-transcripts/WYlM2utu4Ps.json | 8 + .../website/video-transcripts/WYlM2utu4Ps.txt | 32 + .../video-transcripts/W_1S2Rzgff8.json | 8 + .../website/video-transcripts/W_1S2Rzgff8.txt | 99 +++ .../video-transcripts/Wiy2goFwfQA.json | 8 + .../website/video-transcripts/Wiy2goFwfQA.txt | 30 + .../video-transcripts/XSztvFzQAr0.json | 8 + .../website/video-transcripts/XSztvFzQAr0.txt | 392 ++++++++++++ .../video-transcripts/Xrolmjg8Dr0.json | 8 + .../website/video-transcripts/Xrolmjg8Dr0.txt | 43 ++ .../video-transcripts/YE7OQFQE0yA.json | 8 + .../website/video-transcripts/YE7OQFQE0yA.txt | 86 +++ .../video-transcripts/YwD35TG6WAg.json | 8 + .../website/video-transcripts/YwD35TG6WAg.txt | 92 +++ .../video-transcripts/YwkwqXkHGwg.json | 8 + .../website/video-transcripts/YwkwqXkHGwg.txt | 103 ++++ .../video-transcripts/ZEe_hb1Lz6Y.json | 8 + .../website/video-transcripts/ZEe_hb1Lz6Y.txt | 224 +++++++ .../video-transcripts/ZzSCPcnFkxs.json | 8 + .../website/video-transcripts/ZzSCPcnFkxs.txt | 105 ++++ .../video-transcripts/_EXEN52wQvs.json | 9 + .../website/video-transcripts/_EXEN52wQvs.txt | 31 + .../video-transcripts/_JCsyyIH02E.json | 8 + .../website/video-transcripts/_JCsyyIH02E.txt | 126 ++++ .../video-transcripts/_WKRuO0Izxg.json | 8 + .../website/video-transcripts/_WKRuO0Izxg.txt | 76 +++ .../video-transcripts/a6q1M_wqEYY.json | 8 + .../website/video-transcripts/a6q1M_wqEYY.txt | 171 ++++++ .../video-transcripts/a7MF3oSCASE.json | 8 + .../website/video-transcripts/a7MF3oSCASE.txt | 70 +++ .../video-transcripts/aNFKBDeky2s.json | 8 + .../website/video-transcripts/aNFKBDeky2s.txt | 127 ++++ .../video-transcripts/aVkeOIx-Is8.json | 8 + .../website/video-transcripts/aVkeOIx-Is8.txt | 178 ++++++ .../video-transcripts/aW3OlGTmasw.json | 8 + .../website/video-transcripts/aW3OlGTmasw.txt | 116 ++++ .../video-transcripts/aZ2Y74xPj_0.json | 8 + .../website/video-transcripts/aZ2Y74xPj_0.txt | 87 +++ .../video-transcripts/anfVMvBXXX0.json | 8 + .../website/video-transcripts/anfVMvBXXX0.txt | 150 +++++ .../video-transcripts/bH7cS5ENNlw.json | 8 + .../website/video-transcripts/bH7cS5ENNlw.txt | 75 +++ .../video-transcripts/bU6pZPs3uto.json | 8 + .../website/video-transcripts/bU6pZPs3uto.txt | 110 ++++ .../video-transcripts/cRqvkIpJlkg.json | 8 + .../website/video-transcripts/cRqvkIpJlkg.txt | 206 +++++++ .../video-transcripts/cZiHmpw2FVI.json | 8 + .../website/video-transcripts/cZiHmpw2FVI.txt | 62 ++ .../video-transcripts/csTtSj6TqRE.json | 8 + .../website/video-transcripts/csTtSj6TqRE.txt | 290 +++++++++ .../video-transcripts/cxllJwt10VU.json | 10 + .../website/video-transcripts/cxllJwt10VU.txt | 141 +++++ .../video-transcripts/d1T25sbFUKE.json | 10 + .../website/video-transcripts/d1T25sbFUKE.txt | 102 +++ .../video-transcripts/exL7bS0StP4.json | 10 + .../website/video-transcripts/exL7bS0StP4.txt | 174 ++++++ .../video-transcripts/exQ8xXAxtoU.json | 8 + .../website/video-transcripts/exQ8xXAxtoU.txt | 103 ++++ .../video-transcripts/fJD45Mz8SZM.json | 8 + .../website/video-transcripts/fJD45Mz8SZM.txt | 98 +++ .../video-transcripts/fmNpMFLwABA.json | 8 + .../website/video-transcripts/fmNpMFLwABA.txt | 54 ++ .../video-transcripts/gJoJQST5jyM.json | 8 + .../website/video-transcripts/gJoJQST5jyM.txt | 426 +++++++++++++ .../video-transcripts/gM-InTtdhVE.json | 8 + .../website/video-transcripts/gM-InTtdhVE.txt | 177 ++++++ .../video-transcripts/gmqFd2bU_fM.json | 8 + .../website/video-transcripts/gmqFd2bU_fM.txt | 109 ++++ .../video-transcripts/h-I62aFYBNA.json | 8 + .../website/video-transcripts/h-I62aFYBNA.txt | 78 +++ .../video-transcripts/hCjmHoktlrU.json | 9 + .../website/video-transcripts/hCjmHoktlrU.txt | 43 ++ .../video-transcripts/hwsWdhJHl8Q.json | 8 + .../website/video-transcripts/hwsWdhJHl8Q.txt | 377 ++++++++++++ .../video-transcripts/hys6Rpkru50.json | 8 + .../website/video-transcripts/hys6Rpkru50.txt | 95 +++ .../video-transcripts/iCY64ThCleI.json | 8 + .../website/video-transcripts/iCY64ThCleI.txt | 403 ++++++++++++ .../video-transcripts/jR7_OTg-aG0.json | 8 + .../website/video-transcripts/jR7_OTg-aG0.txt | 27 + .../video-transcripts/jVuNrLw4e-A.json | 10 + .../website/video-transcripts/jVuNrLw4e-A.txt | 199 ++++++ .../video-transcripts/jhPep9vHv_U.json | 8 + .../website/video-transcripts/jhPep9vHv_U.txt | 109 ++++ .../video-transcripts/kfKsVAx659s.json | 8 + .../website/video-transcripts/kfKsVAx659s.txt | 48 ++ .../video-transcripts/kjUFJro0yUQ.json | 8 + .../website/video-transcripts/kjUFJro0yUQ.txt | 283 +++++++++ .../video-transcripts/l1TPKuHYr9c.json | 10 + .../website/video-transcripts/l1TPKuHYr9c.txt | 76 +++ .../video-transcripts/l97sDefhHMM.json | 8 + .../website/video-transcripts/l97sDefhHMM.txt | 143 +++++ .../video-transcripts/m43A68sNcvg.json | 10 + .../website/video-transcripts/m43A68sNcvg.txt | 82 +++ .../video-transcripts/mFFjxs9EDW8.json | 8 + .../website/video-transcripts/mFFjxs9EDW8.txt | 93 +++ .../video-transcripts/mqNtfN2C3fM.json | 8 + .../website/video-transcripts/mqNtfN2C3fM.txt | 117 ++++ .../video-transcripts/nF4eqzVcsic.json | 8 + .../website/video-transcripts/nF4eqzVcsic.txt | 92 +++ .../video-transcripts/nImSppBdgkY.json | 8 + .../website/video-transcripts/nImSppBdgkY.txt | 186 ++++++ .../video-transcripts/oR3KHYf5OrY.json | 9 + .../website/video-transcripts/oR3KHYf5OrY.txt | 37 ++ .../video-transcripts/owhInk5YAtg.json | 8 + .../website/video-transcripts/owhInk5YAtg.txt | 184 ++++++ .../video-transcripts/p05yMCleSLo.json | 8 + .../website/video-transcripts/p05yMCleSLo.txt | 139 +++++ .../video-transcripts/p6UFNw0nGik.json | 8 + .../website/video-transcripts/p6UFNw0nGik.txt | 166 +++++ .../video-transcripts/q4YjUClRHBk.json | 8 + .../website/video-transcripts/q4YjUClRHBk.txt | 88 +++ .../video-transcripts/qEgDZHZJEYo.json | 8 + .../website/video-transcripts/qEgDZHZJEYo.txt | 69 +++ .../video-transcripts/qGJaVaQ_MNY.json | 8 + .../website/video-transcripts/qGJaVaQ_MNY.txt | 115 ++++ .../video-transcripts/qKc1hZyw360.json | 8 + .../website/video-transcripts/qKc1hZyw360.txt | 159 +++++ .../video-transcripts/qUJ4FwZMEQY.json | 8 + .../website/video-transcripts/qUJ4FwZMEQY.txt | 150 +++++ .../video-transcripts/qaYExTzcCVY.json | 8 + .../website/video-transcripts/qaYExTzcCVY.txt | 128 ++++ .../video-transcripts/r_ti2QAhm9s.json | 8 + .../website/video-transcripts/r_ti2QAhm9s.txt | 134 ++++ .../video-transcripts/rjIQqfrFvbI.json | 8 + .../website/video-transcripts/rjIQqfrFvbI.txt | 106 ++++ .../video-transcripts/sBAiPOnjX6o.json | 8 + .../website/video-transcripts/sBAiPOnjX6o.txt | 190 ++++++ .../video-transcripts/sH5GY816sRc.json | 8 + .../website/video-transcripts/sH5GY816sRc.txt | 205 ++++++ .../video-transcripts/sK-u1TBWFX8.json | 9 + .../website/video-transcripts/sK-u1TBWFX8.txt | 46 ++ .../video-transcripts/sUhpCwd0YJg.json | 8 + .../website/video-transcripts/sUhpCwd0YJg.txt | 11 + .../video-transcripts/sed5OPQSfe0.json | 10 + .../website/video-transcripts/sed5OPQSfe0.txt | 165 +++++ .../video-transcripts/set12jVQl_A.json | 8 + .../website/video-transcripts/set12jVQl_A.txt | 335 ++++++++++ .../video-transcripts/swgT_aDsv3U.json | 10 + .../website/video-transcripts/swgT_aDsv3U.txt | 164 +++++ .../video-transcripts/sxCADu-SjpQ.json | 8 + .../website/video-transcripts/sxCADu-SjpQ.txt | 111 ++++ .../video-transcripts/tBBtpdz-JJg.json | 8 + .../website/video-transcripts/tBBtpdz-JJg.txt | 113 ++++ .../video-transcripts/tWMVUDScyfQ.json | 8 + .../website/video-transcripts/tWMVUDScyfQ.txt | 109 ++++ .../video-transcripts/tiY5ofnJop8.json | 8 + .../website/video-transcripts/tiY5ofnJop8.txt | 120 ++++ .../video-transcripts/tjgyigzxo5U.json | 8 + .../website/video-transcripts/tjgyigzxo5U.txt | 108 ++++ .../video-transcripts/ts-Q1zBGXco.json | 8 + .../website/video-transcripts/ts-Q1zBGXco.txt | 86 +++ .../video-transcripts/tugJ_7xMdzs.json | 8 + .../website/video-transcripts/tugJ_7xMdzs.txt | 331 ++++++++++ .../video-transcripts/uYCCbE70kE0.json | 8 + .../website/video-transcripts/uYCCbE70kE0.txt | 128 ++++ .../video-transcripts/uvQKs_PdB64.json | 8 + .../website/video-transcripts/uvQKs_PdB64.txt | 155 +++++ .../video-transcripts/v-kJ_nIYq3I.json | 8 + .../website/video-transcripts/v-kJ_nIYq3I.txt | 105 ++++ .../video-transcripts/vAiTW4Y8QuA.json | 8 + .../website/video-transcripts/vAiTW4Y8QuA.txt | 100 +++ .../video-transcripts/vLBQhAJ6aTk.json | 8 + .../website/video-transcripts/vLBQhAJ6aTk.txt | 153 +++++ .../video-transcripts/vOcKbbW9HBM.json | 8 + .../website/video-transcripts/vOcKbbW9HBM.txt | 56 ++ .../video-transcripts/vdFr815Dhpg.json | 8 + .../website/video-transcripts/vdFr815Dhpg.txt | 262 ++++++++ .../video-transcripts/vjTexRDCihA.json | 8 + .../website/video-transcripts/vjTexRDCihA.txt | 127 ++++ .../video-transcripts/vxsUf3gw1zg.json | 8 + .../website/video-transcripts/vxsUf3gw1zg.txt | 151 +++++ .../video-transcripts/w7xvlw3rI6Y.json | 9 + .../website/video-transcripts/w7xvlw3rI6Y.txt | 16 + .../video-transcripts/wp6gqYLD-XM.json | 8 + .../website/video-transcripts/wp6gqYLD-XM.txt | 69 +++ .../video-transcripts/wqcM8pSOGTY.json | 10 + .../website/video-transcripts/wqcM8pSOGTY.txt | 270 ++++++++ .../video-transcripts/x-mUTz23Cd4.json | 8 + .../website/video-transcripts/x-mUTz23Cd4.txt | 180 ++++++ .../video-transcripts/xCEOuV43Uqw.json | 8 + .../website/video-transcripts/xCEOuV43Uqw.txt | 224 +++++++ .../video-transcripts/xLa5yeeVsE8.json | 8 + .../website/video-transcripts/xLa5yeeVsE8.txt | 130 ++++ .../video-transcripts/xzwq4P9ogoU.json | 8 + .../website/video-transcripts/xzwq4P9ogoU.txt | 65 ++ .../video-transcripts/yamsuV5Airc.json | 8 + .../website/video-transcripts/yamsuV5Airc.txt | 227 +++++++ .../video-transcripts/ymocxBIQn0o.json | 8 + .../website/video-transcripts/ymocxBIQn0o.txt | 61 ++ .../video-transcripts/yv3WXt8o88k.json | 8 + .../website/video-transcripts/yv3WXt8o88k.txt | 133 ++++ .../video-transcripts/zJ7L5fi60H8.json | 8 + .../website/video-transcripts/zJ7L5fi60H8.txt | 75 +++ 679 files changed, 46423 insertions(+), 1312 deletions(-) create mode 100644 docs/website/scripts/bootstrap_video_update_sections.py create mode 100644 docs/website/scripts/build_video_refresh_inventory.py create mode 100644 docs/website/scripts/download_youtube_captions.py create mode 100644 docs/website/scripts/fetch_missing_transcripts.py create mode 100644 docs/website/scripts/inject_transcripts.py create mode 100644 docs/website/scripts/normalize_transcript_cache.py create mode 100644 docs/website/scripts/report_video_refresh_progress.py create mode 100644 docs/website/scripts/video_refresh_lib.py create mode 100644 docs/website/video-transcripts/-7XHkBMK4NY.json create mode 100644 docs/website/video-transcripts/-7XHkBMK4NY.txt create mode 100644 docs/website/video-transcripts/-M957AAi-vk.json create mode 100644 docs/website/video-transcripts/-M957AAi-vk.txt create mode 100644 docs/website/video-transcripts/-aTpqxDa1Ag.json create mode 100644 docs/website/video-transcripts/-aTpqxDa1Ag.txt create mode 100644 docs/website/video-transcripts/-brmZYVWb0Y.json create mode 100644 docs/website/video-transcripts/-brmZYVWb0Y.txt create mode 100644 docs/website/video-transcripts/-f1yue3hDEk.json create mode 100644 docs/website/video-transcripts/-f1yue3hDEk.txt create mode 100644 docs/website/video-transcripts/008AK1GfHA8.json create mode 100644 docs/website/video-transcripts/008AK1GfHA8.txt create mode 100644 docs/website/video-transcripts/0ETli_N__ZY.json create mode 100644 docs/website/video-transcripts/0ETli_N__ZY.txt create mode 100644 docs/website/video-transcripts/0a6mPI412C4.json create mode 100644 docs/website/video-transcripts/0a6mPI412C4.txt create mode 100644 docs/website/video-transcripts/0l4R049tSOY.json create mode 100644 docs/website/video-transcripts/0l4R049tSOY.txt create mode 100644 docs/website/video-transcripts/0m7Bay4g93k.json create mode 100644 docs/website/video-transcripts/0m7Bay4g93k.txt create mode 100644 docs/website/video-transcripts/16-Vkgcx2kg.json create mode 100644 docs/website/video-transcripts/16-Vkgcx2kg.txt create mode 100644 docs/website/video-transcripts/17ISIksjcPM.json create mode 100644 docs/website/video-transcripts/17ISIksjcPM.txt create mode 100644 docs/website/video-transcripts/1wHGnmO-vtE.json create mode 100644 docs/website/video-transcripts/1wHGnmO-vtE.txt create mode 100644 docs/website/video-transcripts/2nD75pODPWk.json create mode 100644 docs/website/video-transcripts/2nD75pODPWk.txt create mode 100644 docs/website/video-transcripts/2xKvvv7XoVQ.json create mode 100644 docs/website/video-transcripts/2xKvvv7XoVQ.txt create mode 100644 docs/website/video-transcripts/3-ZH2IFIIMY.json create mode 100644 docs/website/video-transcripts/3-ZH2IFIIMY.txt create mode 100644 docs/website/video-transcripts/32mkZymqa6E.json create mode 100644 docs/website/video-transcripts/32mkZymqa6E.txt create mode 100644 docs/website/video-transcripts/3B7C0ZbV8Bc.json create mode 100644 docs/website/video-transcripts/3B7C0ZbV8Bc.txt create mode 100644 docs/website/video-transcripts/3IC2qZ3wUO4.json create mode 100644 docs/website/video-transcripts/3IC2qZ3wUO4.txt create mode 100644 docs/website/video-transcripts/3rLf9A9RYPY.json create mode 100644 docs/website/video-transcripts/3rLf9A9RYPY.txt create mode 100644 docs/website/video-transcripts/46TpE-Cgtw8.json create mode 100644 docs/website/video-transcripts/46TpE-Cgtw8.txt create mode 100644 docs/website/video-transcripts/47WhIiLxv78.json create mode 100644 docs/website/video-transcripts/47WhIiLxv78.txt create mode 100644 docs/website/video-transcripts/4D_KUa2qv2o.json create mode 100644 docs/website/video-transcripts/4D_KUa2qv2o.txt create mode 100644 docs/website/video-transcripts/4jIqplr19HA.json create mode 100644 docs/website/video-transcripts/4jIqplr19HA.txt create mode 100644 docs/website/video-transcripts/54POZ4PFFBw.json create mode 100644 docs/website/video-transcripts/54POZ4PFFBw.txt create mode 100644 docs/website/video-transcripts/5EyrMpQqR-k.json create mode 100644 docs/website/video-transcripts/5EyrMpQqR-k.txt create mode 100644 docs/website/video-transcripts/5WpZmIkdUfs.json create mode 100644 docs/website/video-transcripts/5WpZmIkdUfs.txt create mode 100644 docs/website/video-transcripts/5eMvwRcDcug.json create mode 100644 docs/website/video-transcripts/5eMvwRcDcug.txt create mode 100644 docs/website/video-transcripts/65LciCzyRNQ.json create mode 100644 docs/website/video-transcripts/65LciCzyRNQ.txt create mode 100644 docs/website/video-transcripts/65jD9oGw61w.json create mode 100644 docs/website/video-transcripts/65jD9oGw61w.txt create mode 100644 docs/website/video-transcripts/6oTy-LcTm0s.json create mode 100644 docs/website/video-transcripts/6oTy-LcTm0s.txt create mode 100644 docs/website/video-transcripts/73d65cvyQv4.json create mode 100644 docs/website/video-transcripts/73d65cvyQv4.txt create mode 100644 docs/website/video-transcripts/77N2t2n8rbQ.json create mode 100644 docs/website/video-transcripts/77N2t2n8rbQ.txt create mode 100644 docs/website/video-transcripts/7CoD9u6KM2Q.json create mode 100644 docs/website/video-transcripts/7CoD9u6KM2Q.txt create mode 100644 docs/website/video-transcripts/7G4OkjgTWIQ.json create mode 100644 docs/website/video-transcripts/7G4OkjgTWIQ.txt create mode 100644 docs/website/video-transcripts/7XHkBMK4NY.json create mode 100644 docs/website/video-transcripts/7XHkBMK4NY.txt create mode 100644 docs/website/video-transcripts/85MyytvMS9I.json create mode 100644 docs/website/video-transcripts/85MyytvMS9I.txt create mode 100644 docs/website/video-transcripts/8WER-8R0WqA.json create mode 100644 docs/website/video-transcripts/8WER-8R0WqA.txt create mode 100644 docs/website/video-transcripts/8fxZVc1hw6Q.json create mode 100644 docs/website/video-transcripts/8fxZVc1hw6Q.txt create mode 100644 docs/website/video-transcripts/8wzBpEp81Kc.json create mode 100644 docs/website/video-transcripts/8wzBpEp81Kc.txt create mode 100644 docs/website/video-transcripts/91BrBoia4nM.json create mode 100644 docs/website/video-transcripts/91BrBoia4nM.txt create mode 100644 docs/website/video-transcripts/99DAeP9LG6c.json create mode 100644 docs/website/video-transcripts/99DAeP9LG6c.txt create mode 100644 docs/website/video-transcripts/9T8MBBuWBDs.json create mode 100644 docs/website/video-transcripts/9T8MBBuWBDs.txt create mode 100644 docs/website/video-transcripts/A72rY4rU7E0.json create mode 100644 docs/website/video-transcripts/A72rY4rU7E0.txt create mode 100644 docs/website/video-transcripts/ACsZ8qiwR8Q.json create mode 100644 docs/website/video-transcripts/ACsZ8qiwR8Q.txt create mode 100644 docs/website/video-transcripts/AFvuY7Ev-XA.json create mode 100644 docs/website/video-transcripts/AFvuY7Ev-XA.txt create mode 100644 docs/website/video-transcripts/AIsD5MXEK-c.json create mode 100644 docs/website/video-transcripts/AIsD5MXEK-c.txt create mode 100644 docs/website/video-transcripts/AMlnslUn1bA.json create mode 100644 docs/website/video-transcripts/AMlnslUn1bA.txt create mode 100644 docs/website/video-transcripts/B4QCJRFpG-k.json create mode 100644 docs/website/video-transcripts/B4QCJRFpG-k.txt create mode 100644 docs/website/video-transcripts/BSj3KRM5Sj0.json create mode 100644 docs/website/video-transcripts/BSj3KRM5Sj0.txt create mode 100644 docs/website/video-transcripts/BSrupbUahRM.json create mode 100644 docs/website/video-transcripts/BSrupbUahRM.txt create mode 100644 docs/website/video-transcripts/BXwh2T7wvfc.json create mode 100644 docs/website/video-transcripts/BXwh2T7wvfc.txt create mode 100644 docs/website/video-transcripts/BY1lMQz873g.json create mode 100644 docs/website/video-transcripts/BY1lMQz873g.txt create mode 100644 docs/website/video-transcripts/BbVoa3vw7OM.json create mode 100644 docs/website/video-transcripts/BbVoa3vw7OM.txt create mode 100644 docs/website/video-transcripts/BwWhFcHc6LM.json create mode 100644 docs/website/video-transcripts/BwWhFcHc6LM.txt create mode 100644 docs/website/video-transcripts/C3PLjAWQ-XA.json create mode 100644 docs/website/video-transcripts/C3PLjAWQ-XA.txt create mode 100644 docs/website/video-transcripts/Cc8YG-_M02M.json create mode 100644 docs/website/video-transcripts/Cc8YG-_M02M.txt create mode 100644 docs/website/video-transcripts/CvbcWC4hLYk.json create mode 100644 docs/website/video-transcripts/CvbcWC4hLYk.txt create mode 100644 docs/website/video-transcripts/DWifLQZyPDE.json create mode 100644 docs/website/video-transcripts/DWifLQZyPDE.txt create mode 100644 docs/website/video-transcripts/Dh1bYqiKBQo.json create mode 100644 docs/website/video-transcripts/Dh1bYqiKBQo.txt create mode 100644 docs/website/video-transcripts/DyzEgAGyRcA.json create mode 100644 docs/website/video-transcripts/DyzEgAGyRcA.txt create mode 100644 docs/website/video-transcripts/EkiTDQn9Cpg.json create mode 100644 docs/website/video-transcripts/EkiTDQn9Cpg.txt create mode 100644 docs/website/video-transcripts/FKTM8jAepJs.json create mode 100644 docs/website/video-transcripts/FKTM8jAepJs.txt create mode 100644 docs/website/video-transcripts/FSek5IOQ0IE.json create mode 100644 docs/website/video-transcripts/FSek5IOQ0IE.txt create mode 100644 docs/website/video-transcripts/FcHVK9ObONg.json create mode 100644 docs/website/video-transcripts/FcHVK9ObONg.txt create mode 100644 docs/website/video-transcripts/FczBAgUIb1c.json create mode 100644 docs/website/video-transcripts/FczBAgUIb1c.txt create mode 100644 docs/website/video-transcripts/GEG_S-dyxaM.json create mode 100644 docs/website/video-transcripts/GEG_S-dyxaM.txt create mode 100644 docs/website/video-transcripts/GEzM1MXkqnk.json create mode 100644 docs/website/video-transcripts/GEzM1MXkqnk.txt create mode 100644 docs/website/video-transcripts/GMnWMB_JfaQ.json create mode 100644 docs/website/video-transcripts/GMnWMB_JfaQ.txt create mode 100644 docs/website/video-transcripts/H7WW4GwXK8o.json create mode 100644 docs/website/video-transcripts/H7WW4GwXK8o.txt create mode 100644 docs/website/video-transcripts/HJvMQKM5FPY.json create mode 100644 docs/website/video-transcripts/HJvMQKM5FPY.txt create mode 100644 docs/website/video-transcripts/HRTLnLecoMA.json create mode 100644 docs/website/video-transcripts/HRTLnLecoMA.txt create mode 100644 docs/website/video-transcripts/IaalGP1UDeU.json create mode 100644 docs/website/video-transcripts/IaalGP1UDeU.txt create mode 100644 docs/website/video-transcripts/IqsUSCgSVTo.json create mode 100644 docs/website/video-transcripts/IqsUSCgSVTo.txt create mode 100644 docs/website/video-transcripts/IxVUX0s2Nms.json create mode 100644 docs/website/video-transcripts/IxVUX0s2Nms.txt create mode 100644 docs/website/video-transcripts/JWLsSbbo4N4.json create mode 100644 docs/website/video-transcripts/JWLsSbbo4N4.txt create mode 100644 docs/website/video-transcripts/JZvrsZzdays.json create mode 100644 docs/website/video-transcripts/JZvrsZzdays.txt create mode 100644 docs/website/video-transcripts/Jk3tTyZroP0.json create mode 100644 docs/website/video-transcripts/Jk3tTyZroP0.txt create mode 100644 docs/website/video-transcripts/KckMUmmSzd4.json create mode 100644 docs/website/video-transcripts/KckMUmmSzd4.txt create mode 100644 docs/website/video-transcripts/Ke8bjFdD1bc.json create mode 100644 docs/website/video-transcripts/Ke8bjFdD1bc.txt create mode 100644 docs/website/video-transcripts/KhSWyE6rAN8.json create mode 100644 docs/website/video-transcripts/KhSWyE6rAN8.txt create mode 100644 docs/website/video-transcripts/L7ulPCUxIsw.json create mode 100644 docs/website/video-transcripts/L7ulPCUxIsw.txt create mode 100644 docs/website/video-transcripts/LFoSl_a2rs8.json create mode 100644 docs/website/video-transcripts/LFoSl_a2rs8.txt create mode 100644 docs/website/video-transcripts/LUHb5fuWzJE.json create mode 100644 docs/website/video-transcripts/LUHb5fuWzJE.txt create mode 100644 docs/website/video-transcripts/LZ_ydua__gY.json create mode 100644 docs/website/video-transcripts/LZ_ydua__gY.txt create mode 100644 docs/website/video-transcripts/Lai--eYYJTw.json create mode 100644 docs/website/video-transcripts/Lai--eYYJTw.txt create mode 100644 docs/website/video-transcripts/M518p4_Horg.json create mode 100644 docs/website/video-transcripts/M518p4_Horg.txt create mode 100644 docs/website/video-transcripts/MLbOdCt67Rw.json create mode 100644 docs/website/video-transcripts/MLbOdCt67Rw.txt create mode 100644 docs/website/video-transcripts/MbTEz-K8iwY.json create mode 100644 docs/website/video-transcripts/MbTEz-K8iwY.txt create mode 100644 docs/website/video-transcripts/Mcw8z_uP3BA.json create mode 100644 docs/website/video-transcripts/Mcw8z_uP3BA.txt create mode 100644 docs/website/video-transcripts/NVPIXnkoxv0.json create mode 100644 docs/website/video-transcripts/NVPIXnkoxv0.txt create mode 100644 docs/website/video-transcripts/Nv_0NVgCbSk.json create mode 100644 docs/website/video-transcripts/Nv_0NVgCbSk.txt create mode 100644 docs/website/video-transcripts/OWHizrNyizQ.json create mode 100644 docs/website/video-transcripts/OWHizrNyizQ.txt create mode 100644 docs/website/video-transcripts/OnZIaGXDZSo.json create mode 100644 docs/website/video-transcripts/OnZIaGXDZSo.txt create mode 100644 docs/website/video-transcripts/Otujb_KyofA.json create mode 100644 docs/website/video-transcripts/Otujb_KyofA.txt create mode 100644 docs/website/video-transcripts/PERdJq3I-As.json create mode 100644 docs/website/video-transcripts/PERdJq3I-As.txt create mode 100644 docs/website/video-transcripts/PHb5YO77OQk.json create mode 100644 docs/website/video-transcripts/PHb5YO77OQk.txt create mode 100644 docs/website/video-transcripts/PRk7EhXQRqs.json create mode 100644 docs/website/video-transcripts/PRk7EhXQRqs.txt create mode 100644 docs/website/video-transcripts/PbRbzCGQCQE.json create mode 100644 docs/website/video-transcripts/PbRbzCGQCQE.txt create mode 100644 docs/website/video-transcripts/Q0fkApAGMZA.json create mode 100644 docs/website/video-transcripts/Q0fkApAGMZA.txt create mode 100644 docs/website/video-transcripts/QY7josfVbdg.json create mode 100644 docs/website/video-transcripts/QY7josfVbdg.txt create mode 100644 docs/website/video-transcripts/Qw7moLv-dE4.json create mode 100644 docs/website/video-transcripts/Qw7moLv-dE4.txt create mode 100644 docs/website/video-transcripts/RBYGdCllnww.json create mode 100644 docs/website/video-transcripts/RBYGdCllnww.txt create mode 100644 docs/website/video-transcripts/Rat-FDqDCTU.json create mode 100644 docs/website/video-transcripts/Rat-FDqDCTU.txt create mode 100644 docs/website/video-transcripts/Rok0Ubr4xes.json create mode 100644 docs/website/video-transcripts/Rok0Ubr4xes.txt create mode 100644 docs/website/video-transcripts/RsVOanGYjqo.json create mode 100644 docs/website/video-transcripts/RsVOanGYjqo.txt create mode 100644 docs/website/video-transcripts/Rt68rA2c6A8.json create mode 100644 docs/website/video-transcripts/Rt68rA2c6A8.txt create mode 100644 docs/website/video-transcripts/S1SHZLXQxSI.json create mode 100644 docs/website/video-transcripts/S1SHZLXQxSI.txt create mode 100644 docs/website/video-transcripts/S5pAGkV4h4w.json create mode 100644 docs/website/video-transcripts/S5pAGkV4h4w.txt create mode 100644 docs/website/video-transcripts/SBqYZSdIdec.json create mode 100644 docs/website/video-transcripts/SBqYZSdIdec.txt create mode 100644 docs/website/video-transcripts/T8l7k2OeGpo.json create mode 100644 docs/website/video-transcripts/T8l7k2OeGpo.txt create mode 100644 docs/website/video-transcripts/TOWhbEhiRe4.json create mode 100644 docs/website/video-transcripts/TOWhbEhiRe4.txt create mode 100644 docs/website/video-transcripts/TRF76P1Dwwc.json create mode 100644 docs/website/video-transcripts/TRF76P1Dwwc.txt create mode 100644 docs/website/video-transcripts/U0Ms65vcVr4.json create mode 100644 docs/website/video-transcripts/U0Ms65vcVr4.txt create mode 100644 docs/website/video-transcripts/UU6HCbenVAA.json create mode 100644 docs/website/video-transcripts/UU6HCbenVAA.txt create mode 100644 docs/website/video-transcripts/UhLFHJj8qnM.json create mode 100644 docs/website/video-transcripts/UhLFHJj8qnM.txt create mode 100644 docs/website/video-transcripts/UwdI_tMqYgU.json create mode 100644 docs/website/video-transcripts/UwdI_tMqYgU.txt create mode 100644 docs/website/video-transcripts/UxATbrfWveU.json create mode 100644 docs/website/video-transcripts/UxATbrfWveU.txt create mode 100644 docs/website/video-transcripts/UxZ1HeheGwU.json create mode 100644 docs/website/video-transcripts/UxZ1HeheGwU.txt create mode 100644 docs/website/video-transcripts/VCNFKMeud-w.json create mode 100644 docs/website/video-transcripts/VCNFKMeud-w.txt create mode 100644 docs/website/video-transcripts/VDyltmk_Hu8.json create mode 100644 docs/website/video-transcripts/VDyltmk_Hu8.txt create mode 100644 docs/website/video-transcripts/VnYaxvVn6OA.json create mode 100644 docs/website/video-transcripts/VnYaxvVn6OA.txt create mode 100644 docs/website/video-transcripts/WT4cFceBWRA.json create mode 100644 docs/website/video-transcripts/WT4cFceBWRA.txt create mode 100644 docs/website/video-transcripts/WYlM2utu4Ps.json create mode 100644 docs/website/video-transcripts/WYlM2utu4Ps.txt create mode 100644 docs/website/video-transcripts/W_1S2Rzgff8.json create mode 100644 docs/website/video-transcripts/W_1S2Rzgff8.txt create mode 100644 docs/website/video-transcripts/Wiy2goFwfQA.json create mode 100644 docs/website/video-transcripts/Wiy2goFwfQA.txt create mode 100644 docs/website/video-transcripts/XSztvFzQAr0.json create mode 100644 docs/website/video-transcripts/XSztvFzQAr0.txt create mode 100644 docs/website/video-transcripts/Xrolmjg8Dr0.json create mode 100644 docs/website/video-transcripts/Xrolmjg8Dr0.txt create mode 100644 docs/website/video-transcripts/YE7OQFQE0yA.json create mode 100644 docs/website/video-transcripts/YE7OQFQE0yA.txt create mode 100644 docs/website/video-transcripts/YwD35TG6WAg.json create mode 100644 docs/website/video-transcripts/YwD35TG6WAg.txt create mode 100644 docs/website/video-transcripts/YwkwqXkHGwg.json create mode 100644 docs/website/video-transcripts/YwkwqXkHGwg.txt create mode 100644 docs/website/video-transcripts/ZEe_hb1Lz6Y.json create mode 100644 docs/website/video-transcripts/ZEe_hb1Lz6Y.txt create mode 100644 docs/website/video-transcripts/ZzSCPcnFkxs.json create mode 100644 docs/website/video-transcripts/ZzSCPcnFkxs.txt create mode 100644 docs/website/video-transcripts/_EXEN52wQvs.json create mode 100644 docs/website/video-transcripts/_EXEN52wQvs.txt create mode 100644 docs/website/video-transcripts/_JCsyyIH02E.json create mode 100644 docs/website/video-transcripts/_JCsyyIH02E.txt create mode 100644 docs/website/video-transcripts/_WKRuO0Izxg.json create mode 100644 docs/website/video-transcripts/_WKRuO0Izxg.txt create mode 100644 docs/website/video-transcripts/a6q1M_wqEYY.json create mode 100644 docs/website/video-transcripts/a6q1M_wqEYY.txt create mode 100644 docs/website/video-transcripts/a7MF3oSCASE.json create mode 100644 docs/website/video-transcripts/a7MF3oSCASE.txt create mode 100644 docs/website/video-transcripts/aNFKBDeky2s.json create mode 100644 docs/website/video-transcripts/aNFKBDeky2s.txt create mode 100644 docs/website/video-transcripts/aVkeOIx-Is8.json create mode 100644 docs/website/video-transcripts/aVkeOIx-Is8.txt create mode 100644 docs/website/video-transcripts/aW3OlGTmasw.json create mode 100644 docs/website/video-transcripts/aW3OlGTmasw.txt create mode 100644 docs/website/video-transcripts/aZ2Y74xPj_0.json create mode 100644 docs/website/video-transcripts/aZ2Y74xPj_0.txt create mode 100644 docs/website/video-transcripts/anfVMvBXXX0.json create mode 100644 docs/website/video-transcripts/anfVMvBXXX0.txt create mode 100644 docs/website/video-transcripts/bH7cS5ENNlw.json create mode 100644 docs/website/video-transcripts/bH7cS5ENNlw.txt create mode 100644 docs/website/video-transcripts/bU6pZPs3uto.json create mode 100644 docs/website/video-transcripts/bU6pZPs3uto.txt create mode 100644 docs/website/video-transcripts/cRqvkIpJlkg.json create mode 100644 docs/website/video-transcripts/cRqvkIpJlkg.txt create mode 100644 docs/website/video-transcripts/cZiHmpw2FVI.json create mode 100644 docs/website/video-transcripts/cZiHmpw2FVI.txt create mode 100644 docs/website/video-transcripts/csTtSj6TqRE.json create mode 100644 docs/website/video-transcripts/csTtSj6TqRE.txt create mode 100644 docs/website/video-transcripts/cxllJwt10VU.json create mode 100644 docs/website/video-transcripts/cxllJwt10VU.txt create mode 100644 docs/website/video-transcripts/d1T25sbFUKE.json create mode 100644 docs/website/video-transcripts/d1T25sbFUKE.txt create mode 100644 docs/website/video-transcripts/exL7bS0StP4.json create mode 100644 docs/website/video-transcripts/exL7bS0StP4.txt create mode 100644 docs/website/video-transcripts/exQ8xXAxtoU.json create mode 100644 docs/website/video-transcripts/exQ8xXAxtoU.txt create mode 100644 docs/website/video-transcripts/fJD45Mz8SZM.json create mode 100644 docs/website/video-transcripts/fJD45Mz8SZM.txt create mode 100644 docs/website/video-transcripts/fmNpMFLwABA.json create mode 100644 docs/website/video-transcripts/fmNpMFLwABA.txt create mode 100644 docs/website/video-transcripts/gJoJQST5jyM.json create mode 100644 docs/website/video-transcripts/gJoJQST5jyM.txt create mode 100644 docs/website/video-transcripts/gM-InTtdhVE.json create mode 100644 docs/website/video-transcripts/gM-InTtdhVE.txt create mode 100644 docs/website/video-transcripts/gmqFd2bU_fM.json create mode 100644 docs/website/video-transcripts/gmqFd2bU_fM.txt create mode 100644 docs/website/video-transcripts/h-I62aFYBNA.json create mode 100644 docs/website/video-transcripts/h-I62aFYBNA.txt create mode 100644 docs/website/video-transcripts/hCjmHoktlrU.json create mode 100644 docs/website/video-transcripts/hCjmHoktlrU.txt create mode 100644 docs/website/video-transcripts/hwsWdhJHl8Q.json create mode 100644 docs/website/video-transcripts/hwsWdhJHl8Q.txt create mode 100644 docs/website/video-transcripts/hys6Rpkru50.json create mode 100644 docs/website/video-transcripts/hys6Rpkru50.txt create mode 100644 docs/website/video-transcripts/iCY64ThCleI.json create mode 100644 docs/website/video-transcripts/iCY64ThCleI.txt create mode 100644 docs/website/video-transcripts/jR7_OTg-aG0.json create mode 100644 docs/website/video-transcripts/jR7_OTg-aG0.txt create mode 100644 docs/website/video-transcripts/jVuNrLw4e-A.json create mode 100644 docs/website/video-transcripts/jVuNrLw4e-A.txt create mode 100644 docs/website/video-transcripts/jhPep9vHv_U.json create mode 100644 docs/website/video-transcripts/jhPep9vHv_U.txt create mode 100644 docs/website/video-transcripts/kfKsVAx659s.json create mode 100644 docs/website/video-transcripts/kfKsVAx659s.txt create mode 100644 docs/website/video-transcripts/kjUFJro0yUQ.json create mode 100644 docs/website/video-transcripts/kjUFJro0yUQ.txt create mode 100644 docs/website/video-transcripts/l1TPKuHYr9c.json create mode 100644 docs/website/video-transcripts/l1TPKuHYr9c.txt create mode 100644 docs/website/video-transcripts/l97sDefhHMM.json create mode 100644 docs/website/video-transcripts/l97sDefhHMM.txt create mode 100644 docs/website/video-transcripts/m43A68sNcvg.json create mode 100644 docs/website/video-transcripts/m43A68sNcvg.txt create mode 100644 docs/website/video-transcripts/mFFjxs9EDW8.json create mode 100644 docs/website/video-transcripts/mFFjxs9EDW8.txt create mode 100644 docs/website/video-transcripts/mqNtfN2C3fM.json create mode 100644 docs/website/video-transcripts/mqNtfN2C3fM.txt create mode 100644 docs/website/video-transcripts/nF4eqzVcsic.json create mode 100644 docs/website/video-transcripts/nF4eqzVcsic.txt create mode 100644 docs/website/video-transcripts/nImSppBdgkY.json create mode 100644 docs/website/video-transcripts/nImSppBdgkY.txt create mode 100644 docs/website/video-transcripts/oR3KHYf5OrY.json create mode 100644 docs/website/video-transcripts/oR3KHYf5OrY.txt create mode 100644 docs/website/video-transcripts/owhInk5YAtg.json create mode 100644 docs/website/video-transcripts/owhInk5YAtg.txt create mode 100644 docs/website/video-transcripts/p05yMCleSLo.json create mode 100644 docs/website/video-transcripts/p05yMCleSLo.txt create mode 100644 docs/website/video-transcripts/p6UFNw0nGik.json create mode 100644 docs/website/video-transcripts/p6UFNw0nGik.txt create mode 100644 docs/website/video-transcripts/q4YjUClRHBk.json create mode 100644 docs/website/video-transcripts/q4YjUClRHBk.txt create mode 100644 docs/website/video-transcripts/qEgDZHZJEYo.json create mode 100644 docs/website/video-transcripts/qEgDZHZJEYo.txt create mode 100644 docs/website/video-transcripts/qGJaVaQ_MNY.json create mode 100644 docs/website/video-transcripts/qGJaVaQ_MNY.txt create mode 100644 docs/website/video-transcripts/qKc1hZyw360.json create mode 100644 docs/website/video-transcripts/qKc1hZyw360.txt create mode 100644 docs/website/video-transcripts/qUJ4FwZMEQY.json create mode 100644 docs/website/video-transcripts/qUJ4FwZMEQY.txt create mode 100644 docs/website/video-transcripts/qaYExTzcCVY.json create mode 100644 docs/website/video-transcripts/qaYExTzcCVY.txt create mode 100644 docs/website/video-transcripts/r_ti2QAhm9s.json create mode 100644 docs/website/video-transcripts/r_ti2QAhm9s.txt create mode 100644 docs/website/video-transcripts/rjIQqfrFvbI.json create mode 100644 docs/website/video-transcripts/rjIQqfrFvbI.txt create mode 100644 docs/website/video-transcripts/sBAiPOnjX6o.json create mode 100644 docs/website/video-transcripts/sBAiPOnjX6o.txt create mode 100644 docs/website/video-transcripts/sH5GY816sRc.json create mode 100644 docs/website/video-transcripts/sH5GY816sRc.txt create mode 100644 docs/website/video-transcripts/sK-u1TBWFX8.json create mode 100644 docs/website/video-transcripts/sK-u1TBWFX8.txt create mode 100644 docs/website/video-transcripts/sUhpCwd0YJg.json create mode 100644 docs/website/video-transcripts/sUhpCwd0YJg.txt create mode 100644 docs/website/video-transcripts/sed5OPQSfe0.json create mode 100644 docs/website/video-transcripts/sed5OPQSfe0.txt create mode 100644 docs/website/video-transcripts/set12jVQl_A.json create mode 100644 docs/website/video-transcripts/set12jVQl_A.txt create mode 100644 docs/website/video-transcripts/swgT_aDsv3U.json create mode 100644 docs/website/video-transcripts/swgT_aDsv3U.txt create mode 100644 docs/website/video-transcripts/sxCADu-SjpQ.json create mode 100644 docs/website/video-transcripts/sxCADu-SjpQ.txt create mode 100644 docs/website/video-transcripts/tBBtpdz-JJg.json create mode 100644 docs/website/video-transcripts/tBBtpdz-JJg.txt create mode 100644 docs/website/video-transcripts/tWMVUDScyfQ.json create mode 100644 docs/website/video-transcripts/tWMVUDScyfQ.txt create mode 100644 docs/website/video-transcripts/tiY5ofnJop8.json create mode 100644 docs/website/video-transcripts/tiY5ofnJop8.txt create mode 100644 docs/website/video-transcripts/tjgyigzxo5U.json create mode 100644 docs/website/video-transcripts/tjgyigzxo5U.txt create mode 100644 docs/website/video-transcripts/ts-Q1zBGXco.json create mode 100644 docs/website/video-transcripts/ts-Q1zBGXco.txt create mode 100644 docs/website/video-transcripts/tugJ_7xMdzs.json create mode 100644 docs/website/video-transcripts/tugJ_7xMdzs.txt create mode 100644 docs/website/video-transcripts/uYCCbE70kE0.json create mode 100644 docs/website/video-transcripts/uYCCbE70kE0.txt create mode 100644 docs/website/video-transcripts/uvQKs_PdB64.json create mode 100644 docs/website/video-transcripts/uvQKs_PdB64.txt create mode 100644 docs/website/video-transcripts/v-kJ_nIYq3I.json create mode 100644 docs/website/video-transcripts/v-kJ_nIYq3I.txt create mode 100644 docs/website/video-transcripts/vAiTW4Y8QuA.json create mode 100644 docs/website/video-transcripts/vAiTW4Y8QuA.txt create mode 100644 docs/website/video-transcripts/vLBQhAJ6aTk.json create mode 100644 docs/website/video-transcripts/vLBQhAJ6aTk.txt create mode 100644 docs/website/video-transcripts/vOcKbbW9HBM.json create mode 100644 docs/website/video-transcripts/vOcKbbW9HBM.txt create mode 100644 docs/website/video-transcripts/vdFr815Dhpg.json create mode 100644 docs/website/video-transcripts/vdFr815Dhpg.txt create mode 100644 docs/website/video-transcripts/vjTexRDCihA.json create mode 100644 docs/website/video-transcripts/vjTexRDCihA.txt create mode 100644 docs/website/video-transcripts/vxsUf3gw1zg.json create mode 100644 docs/website/video-transcripts/vxsUf3gw1zg.txt create mode 100644 docs/website/video-transcripts/w7xvlw3rI6Y.json create mode 100644 docs/website/video-transcripts/w7xvlw3rI6Y.txt create mode 100644 docs/website/video-transcripts/wp6gqYLD-XM.json create mode 100644 docs/website/video-transcripts/wp6gqYLD-XM.txt create mode 100644 docs/website/video-transcripts/wqcM8pSOGTY.json create mode 100644 docs/website/video-transcripts/wqcM8pSOGTY.txt create mode 100644 docs/website/video-transcripts/x-mUTz23Cd4.json create mode 100644 docs/website/video-transcripts/x-mUTz23Cd4.txt create mode 100644 docs/website/video-transcripts/xCEOuV43Uqw.json create mode 100644 docs/website/video-transcripts/xCEOuV43Uqw.txt create mode 100644 docs/website/video-transcripts/xLa5yeeVsE8.json create mode 100644 docs/website/video-transcripts/xLa5yeeVsE8.txt create mode 100644 docs/website/video-transcripts/xzwq4P9ogoU.json create mode 100644 docs/website/video-transcripts/xzwq4P9ogoU.txt create mode 100644 docs/website/video-transcripts/yamsuV5Airc.json create mode 100644 docs/website/video-transcripts/yamsuV5Airc.txt create mode 100644 docs/website/video-transcripts/ymocxBIQn0o.json create mode 100644 docs/website/video-transcripts/ymocxBIQn0o.txt create mode 100644 docs/website/video-transcripts/yv3WXt8o88k.json create mode 100644 docs/website/video-transcripts/yv3WXt8o88k.txt create mode 100644 docs/website/video-transcripts/zJ7L5fi60H8.json create mode 100644 docs/website/video-transcripts/zJ7L5fi60H8.txt 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..c8b30d3e3a 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,30 @@ 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..2f3afcb176 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,39 @@ 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..2620b0afd6 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,36 @@ 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..6e85dc3133 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,35 @@ 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..4d465ee363 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,36 @@ 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..3cbb91fc69 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,35 @@ 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..fdc2b2cc6f 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,35 @@ 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..f5c304cd47 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,35 @@ 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..203072369b 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,38 @@ 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 >}} + +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. -The demo source code for this section and the resource files are hosted in this github project. +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. -{{< youtube WT4cFceBWRA >}} +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..d8581954de 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,34 @@ 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..281b852cb9 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,35 @@ 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..81b7db255a 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,32 @@ 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..cc3773668a 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,35 @@ 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..e2e12df278 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,37 @@ 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..9ac5a6a8a8 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,40 @@ 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. + +{{< youtube UxZ1HeheGwU >}} -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. +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. -The source code for this part is in this github repository. +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. -{{< youtube UxZ1HeheGwU >}} +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..1da96fb44d 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,32 @@ 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..444d0f2091 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,33 @@ 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..05444bc52b 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,31 @@ 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 >}} -The source code used in this part is available in this github repository. +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. -{{< youtube BXwh2T7wvfc >}} +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. + +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..77627a2677 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,33 @@ 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..c75fb9821c 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,35 @@ 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..5c0660d3cf 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,33 @@ 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..79bfca6584 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,33 @@ 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..65d14bc4f4 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,31 @@ 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..6b997aca5d 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,31 @@ 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..4b880d6578 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,33 @@ 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..915488612a 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,32 @@ 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..d6c2a241cc 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,33 @@ 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..f7803e6c9d 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,33 @@ 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..f3dff01449 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,32 @@ 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..87df40370e 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,31 @@ 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..2e23604224 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,32 @@ 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..4a3be3b0ed 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,30 @@ 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..47ded0e267 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,33 @@ 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..7f3157ac31 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,30 @@ 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..5585ec3c34 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,32 @@ 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..458b85e800 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,32 @@ 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..5d63b91bde 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,32 @@ 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..8ab020f9bd 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,30 @@ 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..fc32a60ba0 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,32 @@ 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..a040c5650d 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,32 @@ 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..689f40f63f 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,30 @@ 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..67be61d146 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,32 @@ 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..728f1e5bee 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,30 @@ 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..bb081d903f 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,30 @@ 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..ccf02c632e 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,30 @@ 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..933e1da6e9 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,30 @@ 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..3c2a89ca65 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,30 @@ 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..c7eb2a6575 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,28 @@ 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..db70147a56 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,30 @@ 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..dd8df88aa2 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,28 @@ 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..b192a99bbb 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,28 @@ 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..e4641c1ce6 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,32 @@ 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..08fdb61940 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,34 @@ 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..6795c06f33 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,32 @@ 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..ddbd25bf9b 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,32 @@ 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..5749f45593 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,32 @@ 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..0a0b71aa7c 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,30 @@ 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..e167d852ad 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,30 @@ 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..594c0aa587 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,32 @@ 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..4ee01e3cdd 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,32 @@ 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..9dc31a15b1 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,32 @@ 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..baf60ccc7d 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,30 @@ 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..79193edafb 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,30 @@ 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..2886317910 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,32 @@ 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..f13ae8aa78 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,32 @@ 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..90e20aaa79 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,30 @@ 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..609f419133 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,30 @@ 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..8c5d374ef1 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,30 @@ 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..f9309d8202 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,30 @@ 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..f1d891fc5e 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,30 @@ 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..b19cb3e6fb 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,30 @@ 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..2cdc12d7d1 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,30 @@ 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..1b61362986 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,32 @@ 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..98be6eceda 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,32 @@ 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..ea57b5ea99 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,30 @@ 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..e4f806ae1a 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,30 @@ 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..60d985dd6a 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,30 @@ 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..3a969cb625 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,37 @@ 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..2ebd48fde2 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,39 @@ 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..4d125621e8 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,37 @@ 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..25904625dc 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,35 @@ 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..2e59a9bc5c 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,39 @@ 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..bbbb6f5682 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,37 @@ 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..b1996b9ed5 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,35 @@ 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..2e17781a7d 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,33 @@ 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..859420a8ef 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,31 @@ 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..68dce80da0 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,33 @@ 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..bda6514646 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,31 @@ 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..df67e91879 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,33 @@ 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..f640e3c419 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,35 @@ 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..b20a256999 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,33 @@ 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..62dfe73cc3 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,33 @@ 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..42b3f40571 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,33 @@ 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..316a911313 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,35 @@ 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..09bcc60d92 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,35 @@ 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..21ede3e388 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,236 @@ weight: 38 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube xCEOuV43Uqw >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..825650970c 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,206 @@ weight: 39 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube -brmZYVWb0Y >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..c472759877 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,184 @@ 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 + +_Transcript source: fetched-manual._ + +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..0d89b0cdb1 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,196 @@ weight: 41 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube owhInk5YAtg >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..3637f50ec7 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,189 @@ 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 + +_Transcript source: fetched-manual._ + +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..9d162d551a 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,218 @@ weight: 43 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube cRqvkIpJlkg >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..92a2838283 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,165 @@ weight: 44 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube UU6HCbenVAA >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..8a4491caba 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,132 @@ weight: 45 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube -7XHkBMK4NY >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..14f5faddbc 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,160 @@ 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 + +_Transcript source: fetched-manual._ + +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..7fc9fb7573 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,78 @@ 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 + +_Transcript source: fetched-manual._ + +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..ea1f6a91d4 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,400 @@ weight: 48 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube EkiTDQn9Cpg >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..e1522a4d20 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,199 @@ 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 + +_Transcript source: fetched-manual._ + +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..915dc0fd1e 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,155 @@ weight: 50 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube l97sDefhHMM >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..60180f92cf 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,112 @@ weight: 51 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube TOWhbEhiRe4 >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..f43f0bc79b 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,117 @@ weight: 52 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube TRF76P1Dwwc >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..4e8df10bee 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,73 @@ weight: 53 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube ymocxBIQn0o >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..f130bbbbb9 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,227 @@ weight: 54 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube A72rY4rU7E0 >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..af7f998095 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,107 @@ weight: 55 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube KhSWyE6rAN8 >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..e59d8822de 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,174 @@ weight: 56 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube Jk3tTyZroP0 >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..5b89a081e6 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,87 @@ weight: 57 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube T8l7k2OeGpo >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..754af0c5e9 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,110 @@ weight: 58 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube FKTM8jAepJs >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..5cdd6c6974 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,139 @@ weight: 59 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube vjTexRDCihA >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..25185c4f73 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,128 @@ weight: 60 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube aW3OlGTmasw >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..588abdc5a0 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,81 @@ weight: 61 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube qEgDZHZJEYo >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..343cf5d2e0 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,138 @@ weight: 62 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube 54POZ4PFFBw >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..8002c8cbdf 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,194 @@ weight: 63 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube KckMUmmSzd4 >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..9872c5c4f0 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,56 @@ weight: 64 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube IaalGP1UDeU >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..f1013a2b22 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,173 @@ weight: 65 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube 91BrBoia4nM >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..a550f36f83 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,121 @@ 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 + +_Transcript source: fetched-manual._ + +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..5e30906720 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,202 @@ weight: 67 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube sBAiPOnjX6o >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..92145895c4 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,202 @@ weight: 68 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube sBAiPOnjX6o >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..e3dc2a4ac2 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,73 @@ weight: 69 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube RBYGdCllnww >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..5d5197fad1 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,171 @@ weight: 70 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube qKc1hZyw360 >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..80877efaf0 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,100 @@ 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 + +_Transcript source: fetched-manual._ + +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..54a12fd19e 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,95 @@ weight: 72 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube JWLsSbbo4N4 >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..dd657ed1fe 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,122 @@ weight: 73 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube 9T8MBBuWBDs >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..6865fc401f 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,56 @@ weight: 74 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 12: Creating an Uber Clone {{< youtube IaalGP1UDeU >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..2c4e1351ef 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,33 @@ 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..da88e42701 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,33 @@ 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..c9b3bbe991 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,33 @@ 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..cdeed1be98 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,33 @@ 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..e45c7716e2 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,31 @@ 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..f6f3984a89 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,31 @@ 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..c811f6ce5a 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,33 @@ 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..a5523e6d8f 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,31 @@ 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..dd54e495f3 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,31 @@ 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..2167a56574 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,31 @@ 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..bc473e5ab7 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,31 @@ 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..81220c7725 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,31 @@ 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..33e13be374 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,33 @@ 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..f68af1588b 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,33 @@ 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..b38939f30a 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,31 @@ 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..2cec120bed 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,31 @@ 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..be327c9100 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,31 @@ 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..1ce0743b1c 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,31 @@ 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..3246730bbb 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,31 @@ 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..9779b655a3 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,33 @@ 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..02204d2375 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,31 @@ 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..1ff3c30adb 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,33 @@ 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..f09158488d 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,33 @@ 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..33a460b5f6 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,33 @@ 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..a5a45a76c0 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,33 @@ 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..2b12decebe 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,33 @@ 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..b1a1b96078 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,33 @@ 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..c176e0799b 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,33 @@ 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..d24db73b93 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,31 @@ 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..5e17484ec7 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,33 @@ 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..bb65815917 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,33 @@ 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..69428d242d 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,31 @@ 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..dae79ede2d 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,33 @@ 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..784c123553 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,33 @@ 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..90a318a921 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,31 @@ 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..1ae40ac7ae 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,33 @@ 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..c26aaf3794 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,31 @@ 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..7f6208cafe 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,31 @@ 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..97faed2ea0 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,31 @@ 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..0bf4a52fc1 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,31 @@ 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..adb6852d83 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,31 @@ 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..c4d7bd6f43 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,31 @@ 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..17767d97f1 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,31 @@ 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..4fa5c66a4f 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,31 @@ 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..f5a8a2b3b4 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,31 @@ 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..d85fac47c2 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,31 @@ 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..4d55558b86 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,295 @@ 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 + +_Transcript source: fetched-manual._ + +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..816472c9b6 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,154 @@ weight: 122 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 13: Creating a Facebook Clone {{< youtube 5WpZmIkdUfs >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..0bb7b5a905 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,105 @@ weight: 123 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 13: Creating a Facebook Clone {{< youtube GEzM1MXkqnk >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..aa6819a5fa 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,110 @@ weight: 124 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube fJD45Mz8SZM >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..0ccfaae93a 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,438 @@ weight: 125 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube gJoJQST5jyM >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..e1dc7df0d1 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,155 @@ 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 + +_Transcript source: fetched-manual._ + +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..3ae4f6d6f9 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,172 @@ weight: 127 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube 65LciCzyRNQ >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..d866e6b9d5 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,302 @@ weight: 128 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube csTtSj6TqRE >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..1d519da41c 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,169 @@ weight: 129 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube BbVoa3vw7OM >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..deb3ddbca5 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,39 @@ 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 + +_Transcript source: fetched-manual._ + +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..9c8874584a 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,416 @@ weight: 131 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube 65jD9oGw61w >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..7f9d5c0913 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,58 @@ 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 + +_Transcript source: fetched-manual._ + +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..979f024506 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,128 @@ weight: 133 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube Rok0Ubr4xes >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..39075c6b15 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,87 @@ weight: 134 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube BSj3KRM5Sj0 >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..34996a4a26 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,174 @@ 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 + +_Transcript source: fetched-manual._ + +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..638213b2cf 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,55 @@ weight: 136 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube BSrupbUahRM >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..915a6e945a 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,104 @@ weight: 137 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 14: Creating a WhatsApp Clone {{< youtube YwD35TG6WAg >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..173224d8ce 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,146 @@ 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 + +_Transcript source: fetched-manual._ + +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..f4a84f28c5 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,301 @@ weight: 139 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube 7CoD9u6KM2Q >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..9fb18dcd55 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,201 @@ weight: 140 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube 47WhIiLxv78 >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..9c0d39db97 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,165 @@ weight: 141 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube vLBQhAJ6aTk >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..6206e70462 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,61 @@ weight: 142 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube HJvMQKM5FPY >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..03c9b3b31e 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,198 @@ weight: 143 is_course_lesson: true description: "Watch the lesson and follow the accompanying resources." --- - > Module 15: Create a Netflix Clone {{< youtube nImSppBdgkY >}} + +## Transcript + +_Transcript source: fetched-manual._ + +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..4af35e69e9 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,40 @@ 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. +- [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/) -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… + ## 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..571fa7e757 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,32 @@ 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..6fef00fb42 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,34 @@ 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. +- [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/) -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. + ## 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..5bbb6c7861 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,36 @@ 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". +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. -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. +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. -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. +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. -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 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. -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. +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. -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! +- [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/) -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. + ## 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..da0d826df2 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,37 @@ 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. +- [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/) -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. + ## 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..7105715970 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,36 @@ 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. +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. -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". +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. -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. +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. -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. +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. -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. +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. -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. +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 -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. +- [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/) -The designer tool is also used for countless other features, such as: resolution independent images, localization and more! + ## 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..62899d375c 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,34 @@ 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. +- [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/) -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. + ## 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..e31d624589 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,38 @@ 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..826ed98e3e 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,32 @@ 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..48adbbf429 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,32 @@ 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. +- [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/) -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. + ## 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..5d2816facb 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,35 @@ 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..94895d4c41 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,33 @@ 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. +- [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/) -Thanks for watching, please let us know what you think and get help at [codenameone.com](https://www.codenameone.com/). + ## 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..c62f43af03 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,32 @@ 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… +- [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/) -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. + ## 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..826c7dfe89 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,34 @@ 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..45ffbb6e05 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,33 @@ 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. +- [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/) -Thanks for watching, I hope you found this helpful. + ## 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..a3c968df48 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,32 @@ 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..b0ccd28843 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,34 @@ 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..49d1f6f793 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,36 @@ 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… +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. -You can obviously invoke the localize method of UIManager if you have a special case but you don’t need to for most cases. +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 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. +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. -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. +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. -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. +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. -You can add additional locales using this button. In this case I added the iw locale. +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. -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 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. -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. +## Further Reading -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. +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Layout Basics](/layout-basics/) +- [Hello World](/hello-world/) +- [Properties Are Amazing](/blog/properties-are-amazing/) -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. + ## 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..cbbb927a6b 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,36 @@ 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 +- [Layout Basics](/layout-basics/) +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Getting Started](/getting-started/) -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 + ## 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..3cafc4a048 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,31 @@ 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..b2c6c36749 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,27 @@ 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. +- [Developer Guide](/developer-guide/) +- [Themeing](/themeing/) +- [Hello World](/hello-world/) -Note that this requires an up to date version of Codename One Build and the GUI builder. + ## 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..d540aeaa22 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,33 @@ 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..90ba2d54b4 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,33 @@ 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. +- [Developer Guide](/developer-guide/) +- [Introduction For Android Developers](/introduction-for-android-developers/) +- [Hello World](/hello-world/) -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. + ## 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..05f4b97f83 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,36 @@ 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. +- [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/) -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? + ## 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..91d19fe9b0 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,35 @@ 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. +- [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/) -Cocoapods allows us to install extensions in the iOS build. + ## 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..e24fc844cf 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,31 @@ 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 +- [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/) -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 + ## 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..d5edd85cec 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,37 @@ 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. +- [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/) -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. + ## 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..d79c19677c 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,33 @@ 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! +- [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/) -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. + ## 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..5179b2f3c0 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,37 @@ 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. +- [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/) -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! + ## Discussion diff --git a/docs/website/scripts/bootstrap_video_update_sections.py b/docs/website/scripts/bootstrap_video_update_sections.py new file mode 100644 index 0000000000..6724f92ab3 --- /dev/null +++ b/docs/website/scripts/bootstrap_video_update_sections.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path + +from video_refresh_lib import INVENTORY_PATH, heading_title, split_frontmatter + + +TEMPLATE = """ +## What This Covers + + + +## Current Recommended Approach + + + +## Step-by-Step + + + +## Common Pitfalls + + + +## Current Codename One Notes + + + +## Further Reading + + + +## Video Update Script + +### Recommended New Video Angle + + + +### Target Audience + + + +### Runtime Goal + + + +### Script Outline + +1. Problem statement +2. Current Codename One approach +3. Key concepts +4. Demo sequence +5. Common mistakes +6. Summary and next steps + +### Slide Outline + +1. Title / objective +2. Why this matters +3. Current workflow +4. Key APIs, tools, or files +5. Demo checkpoints +6. Pitfalls / compatibility notes +7. Further reading / CTA + +### Demo Outline + + +""".strip() + + +def load_inventory(path: Path): + return json.loads(path.read_text(encoding="utf-8"))["items"] + + +def insert_after_first_youtube(body: str, block: str) -> str: + match = re.search(r"\{\{<\s*youtube.*?>\}\}", body) + if not match: + return body.rstrip() + "\n\n" + block + "\n" + return body[: match.end()].rstrip() + "\n\n" + block + "\n\n" + body[match.end() :].lstrip() + + +def main(): + parser = argparse.ArgumentParser(description="Bootstrap guide/script sections into selected video pages.") + parser.add_argument("--inventory", default=str(INVENTORY_PATH)) + parser.add_argument("--slug", action="append", required=True) + args = parser.parse_args() + + wanted_slugs = set(args.slug) + inventory_path = Path(args.inventory) + items = load_inventory(inventory_path) + updated = 0 + + for item in items: + if item["slug"] not in wanted_slugs or item["page_kind"] == "course-hub": + continue + path = inventory_path.parents[1] / item["source_path"] + text = path.read_text(encoding="utf-8") + if "## What This Covers" in text: + continue + frontmatter, body = split_frontmatter(text) + new_body = insert_after_first_youtube(body, TEMPLATE) + path.write_text(f"---\n{frontmatter}\n---\n{new_body}", encoding="utf-8") + updated += 1 + print(f"bootstrapped: {path}") + + print(f"completed: updated={updated}") + + +if __name__ == "__main__": + main() diff --git a/docs/website/scripts/build_video_refresh_inventory.py b/docs/website/scripts/build_video_refresh_inventory.py new file mode 100644 index 0000000000..6f529a96b4 --- /dev/null +++ b/docs/website/scripts/build_video_refresh_inventory.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +import argparse +import json +from collections import Counter, defaultdict +from pathlib import Path + +from video_refresh_lib import ( + COURSE_HUBS, + COURSES_DIR, + HOWDOI_DIR, + INVENTORY_PATH, + determine_priority, + derive_public_url, + extract_section, + extract_transcript, + find_youtube_id, + inventory_sort_key, + load_howdoi_index, + load_markdown_page, + load_transcript_meta, + load_transcript_text, + normalize_slug_from_lesson_filename, + stable_relpath, +) + + +def build_items(): + howdoi_index = load_howdoi_index() + items = [] + + for path in sorted(HOWDOI_DIR.glob("*.md")): + page = load_markdown_page(path) + slug = str(page.meta.get("slug", path.stem)) + youtube_id = str(page.meta.get("youtube_id", find_youtube_id(page.body) or "")) or None + embedded_transcript = extract_transcript(page.body) + transcript_meta = load_transcript_meta(youtube_id) if youtube_id else {} + transcript_text = load_transcript_text(youtube_id) if youtube_id else None + anomalies = [] + if slug not in howdoi_index: + anomalies.append("missing-from-howdoi-index") + if not youtube_id: + anomalies.append("missing-youtube-embed") + if page.meta.get("type") != "howdoi": + anomalies.append("unexpected-type") + if extract_section(page.body, "transcript") and not embedded_transcript: + anomalies.append("transcript-heading-without-usable-content") + if not page.meta.get("title"): + anomalies.append("missing-title") + if not page.meta.get("description"): + anomalies.append("missing-description") + + transcript_source = transcript_meta.get("source") + transcript_status = transcript_meta.get("status") + if not transcript_source and embedded_transcript: + transcript_source = "embedded" + if not transcript_status: + transcript_status = "transcript-fetched" if (embedded_transcript or transcript_text) else "transcript-missing" + + item = { + "content_type": "howdoi", + "page_kind": "howdoi", + "course_id": "", + "module_title": "", + "lesson_title": str(page.meta.get("title", slug)), + "slug": slug, + "source_path": stable_relpath(path), + "public_url": derive_public_url(page, "howdoi"), + "youtube_id": youtube_id, + "page_has_youtube": bool(youtube_id), + "page_has_transcript": bool(embedded_transcript or transcript_text), + "transcript_source": transcript_source or "", + "transcript_status": transcript_status, + "guide_status": "guide-in-progress" if extract_section(page.body, "what this covers") else "guide-not-started", + "script_status": "script-drafted" if extract_section(page.body, "video update script") else "script-not-started", + "priority": 0, + "owner": "", + "notes": "", + "anomalies": anomalies, + } + item["priority"] = determine_priority(item) + items.append(item) + + for path in sorted(COURSE_HUBS): + page = load_markdown_page(path) + slug = str(page.meta.get("slug", path.stem)) + anomalies = [] + if not page.meta.get("course_id"): + anomalies.append("missing-course-id") + if not page.meta.get("layout"): + anomalies.append("missing-layout") + item = { + "content_type": "course", + "page_kind": "course-hub", + "course_id": str(page.meta.get("course_id", slug)), + "module_title": "", + "lesson_title": str(page.meta.get("title", slug)), + "slug": slug, + "source_path": stable_relpath(path), + "public_url": derive_public_url(page, "course-hub"), + "youtube_id": None, + "page_has_youtube": False, + "page_has_transcript": False, + "transcript_source": "", + "transcript_status": "transcript-missing", + "guide_status": "guide-in-progress" if extract_section(page.body, "what this covers") else "guide-not-started", + "script_status": "script-drafted" if extract_section(page.body, "video update script") else "script-not-started", + "priority": 1, + "owner": "", + "notes": "", + "anomalies": anomalies, + } + item["priority"] = determine_priority(item) + items.append(item) + + for path in sorted(COURSES_DIR.rglob("*.md")): + page = load_markdown_page(path) + youtube_id = find_youtube_id(page.body) + embedded_transcript = extract_transcript(page.body) + transcript_meta = load_transcript_meta(youtube_id) if youtube_id else {} + transcript_text = load_transcript_text(youtube_id) if youtube_id else None + anomalies = [] + if not youtube_id: + anomalies.append("missing-youtube-embed") + if not page.meta.get("course_id"): + anomalies.append("missing-course-id") + if not page.meta.get("module_title"): + anomalies.append("missing-module-title") + if not page.meta.get("title"): + anomalies.append("missing-title") + if extract_section(page.body, "transcript") and not embedded_transcript: + anomalies.append("transcript-heading-without-usable-content") + + transcript_source = transcript_meta.get("source") + transcript_status = transcript_meta.get("status") + if not transcript_source and embedded_transcript: + transcript_source = "embedded" + if not transcript_status: + transcript_status = "transcript-fetched" if (embedded_transcript or transcript_text) else "transcript-missing" + + item = { + "content_type": "course", + "page_kind": "course-lesson", + "course_id": str(page.meta.get("course_id", "")), + "module_title": str(page.meta.get("module_title", "")), + "lesson_title": str(page.meta.get("title", normalize_slug_from_lesson_filename(path.stem))), + "slug": normalize_slug_from_lesson_filename(path.stem), + "source_path": stable_relpath(path), + "public_url": derive_public_url(page, "course-lesson"), + "youtube_id": youtube_id, + "page_has_youtube": bool(youtube_id), + "page_has_transcript": bool(embedded_transcript or transcript_text), + "transcript_source": transcript_source or "", + "transcript_status": transcript_status, + "guide_status": "guide-in-progress" if extract_section(page.body, "what this covers") else "guide-not-started", + "script_status": "script-drafted" if extract_section(page.body, "video update script") else "script-not-started", + "priority": 2, + "owner": "", + "notes": "", + "anomalies": anomalies, + } + item["priority"] = determine_priority(item) + items.append(item) + + by_youtube = defaultdict(list) + for item in items: + youtube_id = item.get("youtube_id") + if youtube_id: + by_youtube[str(youtube_id)].append(item) + for youtube_id, grouped in by_youtube.items(): + if len(grouped) > 1: + for item in grouped: + item["anomalies"].append(f"duplicate-youtube-id:{youtube_id}") + + return sorted(items, key=inventory_sort_key) + + +def build_summary(items): + counts = Counter() + anomalies = Counter() + for item in items: + counts[f'content_type:{item["content_type"]}'] += 1 + counts[f'page_kind:{item["page_kind"]}'] += 1 + counts[f'priority:{item["priority"]}'] += 1 + counts[f'transcript_status:{item["transcript_status"]}'] += 1 + counts[f'guide_status:{item["guide_status"]}'] += 1 + counts[f'script_status:{item["script_status"]}'] += 1 + if item["page_has_youtube"]: + counts["video_backed_pages"] += 1 + if item["page_has_transcript"]: + counts["pages_with_transcripts"] += 1 + for anomaly in item["anomalies"]: + anomalies[anomaly] += 1 + return { + "counts": dict(sorted(counts.items())), + "anomalies": dict(sorted(anomalies.items())), + } + + +def main(): + parser = argparse.ArgumentParser(description="Build video refresh inventory.") + parser.add_argument("--output", default=str(INVENTORY_PATH), help="Output JSON path") + parser.add_argument("--stdout", action="store_true", help="Print inventory to stdout") + args = parser.parse_args() + + items = build_items() + payload = { + "summary": build_summary(items), + "items": items, + } + output = Path(args.output) + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(json.dumps(payload, indent=2, sort_keys=False) + "\n", encoding="utf-8") + + if args.stdout: + print(json.dumps(payload, indent=2, sort_keys=False)) + else: + print(f"Wrote {len(items)} inventory entries to {output}") + + +if __name__ == "__main__": + main() diff --git a/docs/website/scripts/download_youtube_captions.py b/docs/website/scripts/download_youtube_captions.py new file mode 100644 index 0000000000..97333fcee0 --- /dev/null +++ b/docs/website/scripts/download_youtube_captions.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +""" +Download caption tracks for owned YouTube videos via the YouTube Data API. + +This script uses OAuth for an installed application. It requires a Google OAuth +client secrets JSON file created in Google Cloud Console with the YouTube Data +API enabled. + +Recommended venv packages: + pip install google-auth google-auth-oauthlib + +Typical usage from docs/website: + .venv-video-refresh/bin/python3 scripts/download_youtube_captions.py \ + --client-secrets client_secret.json \ + --limit 10 +""" + +import argparse +import json +import sys +import urllib.error +import urllib.parse +import urllib.request +from pathlib import Path +from typing import Dict, Iterable, List, Optional, Tuple + +from video_refresh_lib import ( + INVENTORY_PATH, + load_transcript_meta, + normalize_transcript_text, + save_transcript, + transcript_has_meaningful_content, + transcript_paths, +) + +API_BASE = "https://www.googleapis.com/youtube/v3" +OAUTH_SCOPES = ["https://www.googleapis.com/auth/youtube.force-ssl"] + + +def load_inventory(path: Path): + payload = json.loads(path.read_text(encoding="utf-8")) + return payload["items"] + + +def dedupe_items(items: Iterable[Dict[str, object]]) -> List[Dict[str, object]]: + seen = set() + deduped = [] + for item in items: + youtube_id = item.get("youtube_id") + if not youtube_id or youtube_id in seen: + continue + seen.add(youtube_id) + deduped.append(item) + return deduped + + +def select_items(items, args): + selected = [] + wanted_slugs = set(args.slug or []) + wanted_ids = set(args.youtube_id or []) + for item in items: + if item["page_kind"] == "course-hub": + continue + if wanted_slugs and item["slug"] not in wanted_slugs: + continue + if wanted_ids and item.get("youtube_id") not in wanted_ids: + continue + if args.only_missing and item["transcript_status"] != "transcript-missing": + continue + selected.append(item) + selected = dedupe_items(selected) + if args.limit: + selected = selected[: args.limit] + return selected + + +def get_credentials(client_secrets: Path, token_path: Path): + try: + from google.auth.transport.requests import Request + from google.oauth2.credentials import Credentials + from google_auth_oauthlib.flow import InstalledAppFlow + except ImportError as err: + raise RuntimeError( + "Missing OAuth dependencies. Install google-auth and google-auth-oauthlib in your venv." + ) from err + + credentials = None + if token_path.exists(): + credentials = Credentials.from_authorized_user_file(str(token_path), OAUTH_SCOPES) + if credentials and credentials.valid: + return credentials + if credentials and credentials.expired and credentials.refresh_token: + credentials.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file(str(client_secrets), OAUTH_SCOPES) + credentials = flow.run_local_server(port=0) + token_path.write_text(credentials.to_json(), encoding="utf-8") + return credentials + + +def api_json_get(path: str, access_token: str, params: Dict[str, str]) -> Dict[str, object]: + url = f"{API_BASE}/{path}?{urllib.parse.urlencode(params)}" + request = urllib.request.Request(url) + request.add_header("Authorization", f"Bearer {access_token}") + request.add_header("Accept", "application/json") + with urllib.request.urlopen(request) as response: + return json.loads(response.read().decode("utf-8")) + + +def api_download(path: str, access_token: str, params: Dict[str, str]) -> str: + query = dict(params) + query["alt"] = "media" + url = f"{API_BASE}/{path}?{urllib.parse.urlencode(query)}" + request = urllib.request.Request(url) + request.add_header("Authorization", f"Bearer {access_token}") + with urllib.request.urlopen(request) as response: + return response.read().decode("utf-8", errors="replace") + + +def format_http_error(err: urllib.error.HTTPError) -> str: + try: + body = err.read().decode("utf-8", errors="replace") + except Exception: + body = "" + if not body: + return f"http-{err.code}" + try: + payload = json.loads(body) + return f"http-{err.code}: {json.dumps(payload, sort_keys=True)}" + except Exception: + compact = " ".join(body.split()) + return f"http-{err.code}: {compact}" + + +def score_caption_track(item: Dict[str, object], preferred_languages: List[str]) -> Tuple[int, int]: + snippet = item.get("snippet", {}) + language = str(snippet.get("language", "")).lower() + track_kind = str(snippet.get("trackKind", "")).lower() + is_asr = track_kind == "asr" + language_rank = preferred_languages.index(language) if language in preferred_languages else len(preferred_languages) + 1 + kind_rank = 1 if is_asr else 0 + return (language_rank, kind_rank) + + +def choose_caption_track(items: List[Dict[str, object]], preferred_languages: List[str]) -> Optional[Dict[str, object]]: + if not items: + return None + ordered = sorted(items, key=lambda item: score_caption_track(item, preferred_languages)) + return ordered[0] + + +def vtt_to_text(vtt_text: str) -> str: + lines = vtt_text.replace("\r\n", "\n").split("\n") + text_lines: List[str] = [] + for raw in lines: + line = raw.strip() + if not line: + text_lines.append("") + continue + if line == "WEBVTT": + continue + if "-->" in line: + continue + if line.startswith("Kind:") or line.startswith("Language:"): + continue + if line.isdigit(): + continue + line = urllib.parse.unquote(line) + text_lines.append(line) + return normalize_transcript_text("\n".join(text_lines)) + + +def list_captions(access_token: str, youtube_id: str) -> List[Dict[str, object]]: + payload = api_json_get( + "captions", + access_token, + { + "part": "id,snippet", + "videoId": youtube_id, + "maxResults": "50", + }, + ) + return list(payload.get("items", [])) + + +def download_caption(access_token: str, caption_id: str, tfmt: str) -> str: + return api_download("captions/" + urllib.parse.quote(caption_id), access_token, {"tfmt": tfmt}) + + +def fetch_owned_caption(access_token: str, youtube_id: str, preferred_languages: List[str]) -> Tuple[Optional[str], str]: + try: + items = list_captions(access_token, youtube_id) + except urllib.error.HTTPError as err: + return None, format_http_error(err) + except Exception as err: # pragma: no cover - network/runtime dependent + return None, str(err) + if not items: + return None, "no-owned-captions" + chosen = choose_caption_track(items, preferred_languages) + if not chosen: + return None, "no-matching-caption-track" + caption_id = str(chosen["id"]) + try: + vtt = download_caption(access_token, caption_id, "vtt") + except urllib.error.HTTPError as err: + return None, format_http_error(err) + except Exception as err: # pragma: no cover - network/runtime dependent + return None, str(err) + text = vtt_to_text(vtt) + return (text if transcript_has_meaningful_content(text) else None), caption_id + + +def main(): + parser = argparse.ArgumentParser(description="Download owned YouTube captions via the YouTube Data API.") + parser.add_argument("--inventory", default=str(INVENTORY_PATH)) + parser.add_argument("--client-secrets", required=True, help="OAuth client secrets JSON path") + parser.add_argument("--token-file", default=".youtube-oauth-token.json", help="Cached OAuth token path") + parser.add_argument("--slug", action="append", help="Process only a specific slug") + parser.add_argument("--youtube-id", action="append", help="Process only a specific YouTube ID") + parser.add_argument("--limit", type=int, default=0) + parser.add_argument("--language", action="append", default=["en", "en-us", "en-gb"], help="Preferred caption language in priority order") + parser.add_argument("--only-missing", action="store_true", default=True) + parser.add_argument("--include-existing", dest="only_missing", action="store_false") + args = parser.parse_args() + + inventory_path = Path(args.inventory) + client_secrets = Path(args.client_secrets) + token_path = Path(args.token_file) + + credentials = get_credentials(client_secrets, token_path) + access_token = credentials.token + + items = select_items(load_inventory(inventory_path), args) + wrote = 0 + skipped = 0 + + for item in items: + youtube_id = str(item["youtube_id"]) + text_path, meta_path = transcript_paths(youtube_id) + if text_path.exists() and meta_path.exists() and args.only_missing: + print(f"cached transcript: {youtube_id}") + skipped += 1 + continue + text, detail = fetch_owned_caption(access_token, youtube_id, args.language) + if not text: + print(f"skipped transcript: {youtube_id} ({detail})") + skipped += 1 + continue + save_transcript( + youtube_id, + text, + { + "source": "fetched-owner-api", + "status": "transcript-fetched", + "quality": "needs-review", + "fetch_method": "youtube-data-api", + "caption_track_id": detail, + "source_path": item["source_path"], + }, + ) + print(f"fetched transcript: {youtube_id} via youtube-data-api") + wrote += 1 + + print(f"completed: wrote={wrote} skipped={skipped} processed={len(items)}") + + +if __name__ == "__main__": + try: + main() + except RuntimeError as err: + print(str(err), file=sys.stderr) + sys.exit(2) diff --git a/docs/website/scripts/fetch_missing_transcripts.py b/docs/website/scripts/fetch_missing_transcripts.py new file mode 100644 index 0000000000..1c36d8c1a9 --- /dev/null +++ b/docs/website/scripts/fetch_missing_transcripts.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +import argparse +import json +import subprocess +import time +from pathlib import Path + +from video_refresh_lib import ( + INVENTORY_PATH, + extract_transcript, + load_markdown_page, + normalize_transcript_text, + save_transcript, + transcript_has_meaningful_content, + transcript_paths, +) + + +def load_inventory(path: Path): + payload = json.loads(path.read_text(encoding="utf-8")) + return payload["items"] + + +def fetch_with_youtube_transcript_api(youtube_id: str): + try: + from youtube_transcript_api import YouTubeTranscriptApi + except ImportError: + return None, "youtube-transcript-api-not-installed" + + try: + if hasattr(YouTubeTranscriptApi, "get_transcript"): + transcript = YouTubeTranscriptApi.get_transcript(youtube_id, languages=["en"]) + text = "\n".join(chunk["text"].strip() for chunk in transcript if chunk.get("text")) + else: + transcript = YouTubeTranscriptApi().fetch(youtube_id, languages=["en"]) + text = "\n".join(getattr(chunk, "text", "").strip() for chunk in transcript if getattr(chunk, "text", "")) + except Exception as err: # pragma: no cover - network/runtime dependent + return None, str(err) + return text, "youtube_transcript_api" + + +def api_failure_is_authoritative(error_text: str) -> bool: + text = (error_text or "").lower() + return any( + token in text + for token in ( + "transcriptsdisabled", + "notranscriptfound", + "novideofound", + "video unavailable", + "the video is no longer available", + "subtitles are disabled", + "no transcripts were found", + ) + ) + + +def api_failure_is_ip_block(error_text: str) -> bool: + text = (error_text or "").lower() + return "requestblocked" in text or "ipblocked" in text or "youtube is blocking requests from your ip" in text + + +def fetch_with_yt_dlp(youtube_id: str): + command = [ + "yt-dlp", + "--skip-download", + "--write-auto-sub", + "--write-sub", + "--sub-langs", + "en.*", + "--sub-format", + "vtt", + "--output", + "-", + f"https://www.youtube.com/watch?v={youtube_id}", + ] + try: + result = subprocess.run(command, capture_output=True, text=True, check=False) + except FileNotFoundError: + return None, "yt-dlp-not-installed" + if result.returncode != 0: + return None, result.stderr.strip() or f"yt-dlp-exit-{result.returncode}" + text = normalize_transcript_text(result.stdout) + return text if transcript_has_meaningful_content(text) else None, "yt-dlp" + + +def select_items(items, args): + selected = [] + wanted_slugs = set(args.slug or []) + wanted_ids = set(args.youtube_id or []) + for item in items: + if item["page_kind"] == "course-hub": + continue + if wanted_slugs and item["slug"] not in wanted_slugs: + continue + if wanted_ids and item.get("youtube_id") not in wanted_ids: + continue + if args.only_missing: + allowed_statuses = {"transcript-missing"} + if args.retry_unavailable: + allowed_statuses.add("unavailable") + if item["transcript_status"] not in allowed_statuses: + continue + selected.append(item) + if args.limit: + selected = selected[: args.limit] + return selected + + +def should_mark_unavailable(args, fetch_error: str, used_fallback: bool) -> bool: + if not args.mark_unavailable: + return False + if used_fallback and not args.api_only: + return False + return api_failure_is_authoritative(fetch_error) + + +def should_stop_on_block(args, fetch_error: str) -> bool: + return args.stop_on_ip_block and api_failure_is_ip_block(fetch_error) + + +def remove_existing_transcript_artifacts(youtube_id: str): + text_path, meta_path = transcript_paths(youtube_id) + if text_path.exists(): + text_path.unlink() + if meta_path.exists(): + meta_path.unlink() + + +def update_unavailable_meta(meta_path: Path, youtube_id: str, source_path: str, fetch_error: str): + meta_path.parent.mkdir(parents=True, exist_ok=True) + meta = { + "youtube_id": youtube_id, + "source": "unavailable", + "status": "unavailable", + "quality": "unavailable", + "source_path": source_path, + "fetch_error": fetch_error or "authoritative-api-failure", + } + meta_path.write_text(json.dumps(meta, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def main(): + parser = argparse.ArgumentParser(description="Fetch or extract transcripts for video pages.") + parser.add_argument("--inventory", default=str(INVENTORY_PATH)) + parser.add_argument("--slug", action="append", help="Process only a specific slug") + parser.add_argument("--youtube-id", action="append", help="Process only a specific YouTube ID") + parser.add_argument("--limit", type=int, default=0) + parser.add_argument("--only-missing", action="store_true", default=True) + parser.add_argument("--include-existing", dest="only_missing", action="store_false") + parser.add_argument("--retry-unavailable", action="store_true", help="Reprocess items currently marked unavailable") + parser.add_argument("--embedded-only", action="store_true", help="Only extract embedded transcripts") + parser.add_argument("--allow-fetch", action="store_true", help="Attempt network-based transcript fetching") + parser.add_argument("--api-only", action="store_true", help="Use youtube-transcript-api only and skip yt-dlp fallback") + parser.add_argument("--mark-unavailable", action="store_true", help="Write unavailable metadata only for authoritative API failures") + parser.add_argument("--delay-seconds", type=float, default=0.0, help="Sleep between network fetch attempts") + parser.add_argument("--stop-on-ip-block", action="store_true", help="Stop immediately if YouTube starts blocking transcript requests") + args = parser.parse_args() + + inventory_path = Path(args.inventory) + items = select_items(load_inventory(inventory_path), args) + written = 0 + unavailable = 0 + skipped = 0 + + for item in items: + youtube_id = item.get("youtube_id") + if not youtube_id: + continue + text_path, meta_path = transcript_paths(youtube_id) + if text_path.exists() and meta_path.exists() and args.only_missing: + continue + if args.retry_unavailable and item["transcript_status"] == "unavailable": + remove_existing_transcript_artifacts(youtube_id) + + page = load_markdown_page(Path(inventory_path.parents[1] / item["source_path"])) + embedded = extract_transcript(page.body) + if embedded: + save_transcript( + youtube_id, + embedded, + { + "source": "embedded", + "status": "transcript-fetched", + "quality": "needs-review", + "source_path": item["source_path"], + }, + ) + written += 1 + print(f"embedded transcript: {youtube_id} <- {item['source_path']}") + continue + + if args.embedded_only: + continue + + fetched = None + source = None + fetch_error = None + used_fallback = False + if args.allow_fetch: + if args.delay_seconds > 0: + time.sleep(args.delay_seconds) + fetched, source = fetch_with_youtube_transcript_api(youtube_id) + if not fetched: + fetch_error = source + if should_stop_on_block(args, fetch_error or ""): + print(f"stopping on ip block: {youtube_id} ({fetch_error})") + break + if not args.api_only: + used_fallback = True + if args.delay_seconds > 0: + time.sleep(args.delay_seconds) + fetched, source = fetch_with_yt_dlp(youtube_id) + if not fetched: + fetch_error = source + + if fetched: + save_transcript( + youtube_id, + fetched, + { + "source": "fetched-automated", + "status": "transcript-fetched", + "quality": "needs-review", + "fetch_method": source, + "source_path": item["source_path"], + }, + ) + written += 1 + print(f"fetched transcript: {youtube_id} via {source}") + elif should_mark_unavailable(args, fetch_error or "", used_fallback): + update_unavailable_meta(meta_path, youtube_id, item["source_path"], fetch_error or "") + unavailable += 1 + print(f"unavailable transcript: {youtube_id} ({fetch_error or 'authoritative-api-failure'})") + else: + skipped += 1 + print(f"skipped transcript: {youtube_id} ({fetch_error or 'not fetched'})") + + print(f"completed: wrote={written} unavailable={unavailable} skipped={skipped} processed={len(items)}") + + +if __name__ == "__main__": + main() diff --git a/docs/website/scripts/inject_transcripts.py b/docs/website/scripts/inject_transcripts.py new file mode 100644 index 0000000000..09bc87265a --- /dev/null +++ b/docs/website/scripts/inject_transcripts.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +import argparse +import json +import re +from pathlib import Path + +from video_refresh_lib import ( + INVENTORY_PATH, + build_transcript_section, + extract_discussion, + extract_transcript, + heading_title, + load_markdown_page, + load_transcript_meta, + load_transcript_text, + split_frontmatter, +) + + +def remove_section(body: str, section_name: str) -> str: + lines = body.splitlines() + target_index = None + target_level = None + wanted = section_name.strip().lower() + for index, line in enumerate(lines): + heading = heading_title(line) + if not heading: + continue + level, title = heading + if title == wanted: + target_index = index + target_level = level + break + if target_index is None or target_level is None: + return body.strip() + "\n" + + kept = lines[:target_index] + skip = False + for line in lines[target_index + 1 :]: + heading = heading_title(line) + if heading and heading[0] <= target_level: + skip = True + if skip: + kept.append(line) + return "\n".join(kept).strip() + "\n" + + +def insert_before_discussion(body: str, block: str) -> str: + discussion_match = re.search(r"(?im)^##\s+discussion\s*$", body) + if discussion_match: + text = body[: discussion_match.start()].rstrip() + "\n\n" + block.strip() + "\n\n" + body[discussion_match.start() :].lstrip() + else: + text = body.rstrip() + "\n\n" + block.strip() + "\n" + text = re.sub(r"\n+---\s*\n*$", "\n", text.rstrip() + "\n") + return text.rstrip() + "\n" + + +def load_inventory(path: Path): + return json.loads(path.read_text(encoding="utf-8"))["items"] + + +def main(): + parser = argparse.ArgumentParser(description="Inject cached transcripts into markdown pages.") + parser.add_argument("--inventory", default=str(INVENTORY_PATH)) + parser.add_argument("--slug", action="append") + parser.add_argument("--youtube-id", action="append") + parser.add_argument("--include-unavailable-note", action="store_true") + args = parser.parse_args() + + wanted_slugs = set(args.slug or []) + wanted_ids = set(args.youtube_id or []) + inventory_path = Path(args.inventory) + items = load_inventory(inventory_path) + updated = 0 + + for item in items: + if item["page_kind"] == "course-hub": + continue + if wanted_slugs and item["slug"] not in wanted_slugs: + continue + if wanted_ids and item.get("youtube_id") not in wanted_ids: + continue + + youtube_id = item.get("youtube_id") + transcript = load_transcript_text(youtube_id) if youtube_id else None + meta = load_transcript_meta(youtube_id) if youtube_id else {} + if not transcript and not (args.include_unavailable_note and meta.get("status") == "unavailable"): + continue + + path = inventory_path.parents[1] / item["source_path"] + text = path.read_text(encoding="utf-8") + frontmatter, body = split_frontmatter(text) + cleaned_body = remove_section(body, "transcript") + block = build_transcript_section(transcript, meta) + new_body = insert_before_discussion(cleaned_body, block) + path.write_text(f"---\n{frontmatter}\n---\n{new_body}", encoding="utf-8") + updated += 1 + print(f"updated transcript section: {path}") + + print(f"completed: updated={updated}") + + +if __name__ == "__main__": + main() diff --git a/docs/website/scripts/normalize_transcript_cache.py b/docs/website/scripts/normalize_transcript_cache.py new file mode 100644 index 0000000000..a7715d47ee --- /dev/null +++ b/docs/website/scripts/normalize_transcript_cache.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +import argparse +from pathlib import Path + +from video_refresh_lib import TRANSCRIPTS_DIR, load_transcript_meta, save_transcript + + +def main(): + parser = argparse.ArgumentParser(description="Normalize transcript cache files and backfill metadata.") + parser.add_argument("--transcripts-dir", default=str(TRANSCRIPTS_DIR)) + parser.add_argument("--youtube-id", action="append", help="Normalize only specific YouTube IDs") + args = parser.parse_args() + + base = Path(args.transcripts_dir) + wanted_ids = set(args.youtube_id or []) + updated = 0 + + for text_path in sorted(base.glob("*.txt")): + youtube_id = text_path.stem + if wanted_ids and youtube_id not in wanted_ids: + continue + raw = text_path.read_text(encoding="utf-8", errors="replace") + meta = load_transcript_meta(youtube_id) + if not meta: + meta = { + "source": "fetched-manual", + "status": "transcript-fetched", + "quality": "needs-review", + } + else: + meta = dict(meta) + meta.setdefault("source", "fetched-manual") + meta.setdefault("status", "transcript-fetched") + meta.setdefault("quality", "needs-review") + save_transcript(youtube_id, raw, meta) + updated += 1 + print(f"normalized: {youtube_id}") + + print(f"completed: updated={updated}") + + +if __name__ == "__main__": + main() diff --git a/docs/website/scripts/report_video_refresh_progress.py b/docs/website/scripts/report_video_refresh_progress.py new file mode 100644 index 0000000000..33cbce686a --- /dev/null +++ b/docs/website/scripts/report_video_refresh_progress.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +import argparse +import json +from collections import Counter, defaultdict +from pathlib import Path + +from video_refresh_lib import INVENTORY_PATH + + +def load_inventory(path: Path): + return json.loads(path.read_text(encoding="utf-8"))["items"] + + +def print_counter(title, counter): + print(title) + for key in sorted(counter): + print(f" {key}: {counter[key]}") + print() + + +def main(): + parser = argparse.ArgumentParser(description="Report video refresh progress.") + parser.add_argument("--inventory", default=str(INVENTORY_PATH)) + args = parser.parse_args() + + items = load_inventory(Path(args.inventory)) + by_kind = Counter(item["page_kind"] for item in items) + by_priority = Counter(item["priority"] for item in items) + by_transcript = Counter(item["transcript_status"] for item in items) + by_guide = Counter(item["guide_status"] for item in items) + by_script = Counter(item["script_status"] for item in items) + by_course = Counter(item["course_id"] or item["page_kind"] for item in items) + + print_counter("By Page Kind", by_kind) + print_counter("By Priority", by_priority) + print_counter("By Transcript Status", by_transcript) + print_counter("By Guide Status", by_guide) + print_counter("By Script Status", by_script) + print_counter("By Course", by_course) + + +if __name__ == "__main__": + main() diff --git a/docs/website/scripts/video_refresh_lib.py b/docs/website/scripts/video_refresh_lib.py new file mode 100644 index 0000000000..30fc24f221 --- /dev/null +++ b/docs/website/scripts/video_refresh_lib.py @@ -0,0 +1,283 @@ +#!/usr/bin/env python3 +import json +import re +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, List, Optional, Tuple + + +ROOT = Path(__file__).resolve().parents[1] +CONTENT = ROOT / "content" +DATA = ROOT / "data" +HOWDOI_DIR = CONTENT / "howdoi" +COURSES_DIR = CONTENT / "courses" +TRANSCRIPTS_DIR = ROOT / "video-transcripts" +INVENTORY_PATH = DATA / "video_refresh_inventory.json" +HOWDOI_INDEX_PATH = DATA / "howdoi_index.json" +COURSE_HUBS = [ + CONTENT / "course-01-java-for-mobile-devices.md", + CONTENT / "course-02-deep-dive-mobile-development-with-codename-one.md", + CONTENT / "course-03-build-real-world-full-stack-mobile-apps-java.md", +] + +YOUTUBE_RE = re.compile(r"\{\{<\s*youtube\s+\"?([A-Za-z0-9_-]+)\"?\s*>\}\}") +HEADING_RE = re.compile(r"^(#{1,6})\s+(.*?)\s*$") + + +@dataclass +class MarkdownPage: + path: Path + meta: Dict[str, object] + body: str + + +def split_frontmatter(text: str) -> Tuple[str, str]: + if text.startswith("---\n"): + end = text.find("\n---\n", 4) + if end != -1: + return text[4:end], text[end + 5 :] + return "", text + + +def parse_frontmatter(frontmatter: str) -> Dict[str, object]: + meta: Dict[str, object] = {} + current_key: Optional[str] = None + for raw_line in frontmatter.splitlines(): + line = raw_line.rstrip() + if not line.strip(): + continue + if line.startswith("- ") and current_key: + meta.setdefault(current_key, []) + assert isinstance(meta[current_key], list) + meta[current_key].append(parse_scalar(line[2:].strip())) + continue + if ":" not in line: + current_key = None + continue + key, value = line.split(":", 1) + key = key.strip() + value = value.strip() + if value == "": + meta[key] = [] + current_key = key + continue + meta[key] = parse_scalar(value) + current_key = key + return meta + + +def parse_scalar(value: str): + if value.startswith('"') and value.endswith('"'): + return value[1:-1] + if value.startswith("'") and value.endswith("'"): + return value[1:-1] + if value.lower() == "true": + return True + if value.lower() == "false": + return False + if re.fullmatch(r"-?\d+", value): + try: + return int(value) + except ValueError: + return value + return value + + +def load_markdown_page(path: Path) -> MarkdownPage: + text = path.read_text(encoding="utf-8") + frontmatter, body = split_frontmatter(text) + return MarkdownPage(path=path, meta=parse_frontmatter(frontmatter), body=body) + + +def derive_public_url(page: MarkdownPage, page_kind: str) -> str: + url = str(page.meta.get("url", "")).strip() + if url: + return url + slug = str(page.meta.get("slug", page.path.stem)) + if page_kind == "howdoi": + return f"/how-do-i/{slug}/" + if page_kind == "course-hub": + return f"/{slug}/" + course_id = str(page.meta.get("course_id", "")) + return f"/courses/{course_id}/{page.path.stem}/" + + +def find_youtube_id(body: str) -> Optional[str]: + match = YOUTUBE_RE.search(body) + return match.group(1) if match else None + + +def heading_title(line: str) -> Optional[Tuple[int, str]]: + match = HEADING_RE.match(line) + if not match: + return None + return len(match.group(1)), match.group(2).strip().lower() + + +def extract_section(body: str, section_name: str) -> Optional[str]: + lines = body.splitlines() + target_index = None + target_level = None + wanted = section_name.strip().lower() + for index, line in enumerate(lines): + heading = heading_title(line) + if not heading: + continue + level, title = heading + if title == wanted: + target_index = index + target_level = level + break + if target_index is None or target_level is None: + return None + + collected: List[str] = [] + for line in lines[target_index + 1 :]: + heading = heading_title(line) + if heading and heading[0] <= target_level: + break + collected.append(line) + text = "\n".join(collected).strip() + return text or None + + +def extract_transcript(body: str) -> Optional[str]: + transcript = extract_section(body, "transcript") + if not transcript: + return None + cleaned = normalize_transcript_text(transcript) + return cleaned if transcript_has_meaningful_content(cleaned) else None + + +def extract_discussion(body: str) -> Optional[str]: + return extract_section(body, "discussion") + + +def transcript_has_meaningful_content(text: str) -> bool: + if not text: + return False + words = text.split() + if len(words) < 20: + return False + alnum = sum(ch.isalnum() for ch in text) + return alnum >= 80 + + +def normalize_transcript_text(text: str) -> str: + lines = [line.rstrip() for line in text.replace("\r\n", "\n").split("\n")] + normalized: List[str] = [] + last_non_empty = None + for raw in lines: + line = re.sub(r"\s+", " ", raw).strip() + if line == "---": + continue + if re.fullmatch(r"\d{1,2}:\d{2}(?::\d{2})?", line): + continue + if re.fullmatch(r"\d+\s*-->\s*\d+", line): + continue + if line == last_non_empty and line: + continue + if line: + normalized.append(line) + last_non_empty = line + elif normalized and normalized[-1] != "": + normalized.append("") + return "\n".join(normalized).strip() + + +def transcript_paths(youtube_id: str) -> Tuple[Path, Path]: + return TRANSCRIPTS_DIR / f"{youtube_id}.txt", TRANSCRIPTS_DIR / f"{youtube_id}.json" + + +def load_transcript_meta(youtube_id: str) -> Dict[str, object]: + _, meta_path = transcript_paths(youtube_id) + if not meta_path.exists(): + return {} + return json.loads(meta_path.read_text(encoding="utf-8")) + + +def load_transcript_text(youtube_id: str) -> Optional[str]: + text_path, _ = transcript_paths(youtube_id) + if not text_path.exists(): + return None + return text_path.read_text(encoding="utf-8").strip() or None + + +def save_transcript(youtube_id: str, text: str, meta: Dict[str, object]) -> None: + TRANSCRIPTS_DIR.mkdir(parents=True, exist_ok=True) + normalized = normalize_transcript_text(text) + text_path, meta_path = transcript_paths(youtube_id) + text_path.write_text(normalized.rstrip() + "\n", encoding="utf-8") + meta = dict(meta) + meta["youtube_id"] = youtube_id + meta["line_count"] = len([line for line in normalized.splitlines() if line.strip()]) + meta["word_count"] = len(normalized.split()) + meta_path.write_text(json.dumps(meta, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def load_howdoi_index() -> Dict[str, Dict[str, object]]: + if not HOWDOI_INDEX_PATH.exists(): + return {} + items = json.loads(HOWDOI_INDEX_PATH.read_text(encoding="utf-8")) + return {str(item["slug"]): item for item in items} + + +def stable_relpath(path: Path) -> str: + return path.relative_to(ROOT).as_posix() + + +def normalize_slug_from_lesson_filename(name: str) -> str: + return re.sub(r"^\d+-", "", name) + + +def determine_priority(item: Dict[str, object]) -> int: + text = " ".join( + str(item.get(key, "")).lower() + for key in ("lesson_title", "module_title", "slug", "course_id") + ) + if any( + token in text + for token in ( + "hello-world", + "hello world", + "setup", + "installation", + "layout", + "theme", + "css", + "build", + "debug", + "native interface", + "native-code", + "native code", + "push", + ) + ): + return 0 + if str(item.get("content_type")) == "howdoi" or "course-01" in text: + return 1 + return 2 + + +def inventory_sort_key(item: Dict[str, object]): + return ( + int(item.get("priority", 99)), + str(item.get("content_type", "")), + str(item.get("course_id", "")), + str(item.get("source_path", "")), + ) + + +def build_transcript_section(transcript: Optional[str], meta: Dict[str, object]) -> str: + lines = ["## Transcript", ""] + if transcript: + source = str(meta.get("source", "unknown")) + lines.append(f"_Transcript source: {source}._") + lines.append("") + lines.append(transcript.strip()) + else: + status = str(meta.get("status", "transcript-missing")) + lines.append(f"_Transcript status: {status}._") + lines.append("") + lines.append("Transcript not yet available for this video.") + return "\n".join(lines).strip() diff --git a/docs/website/video-transcripts/-7XHkBMK4NY.json b/docs/website/video-transcripts/-7XHkBMK4NY.json new file mode 100644 index 0000000000..e3b4325f26 --- /dev/null +++ b/docs/website/video-transcripts/-7XHkBMK4NY.json @@ -0,0 +1,8 @@ +{ + "line_count": 120, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 652, + "youtube_id": "-7XHkBMK4NY" +} diff --git a/docs/website/video-transcripts/-7XHkBMK4NY.txt b/docs/website/video-transcripts/-7XHkBMK4NY.txt new file mode 100644 index 0000000000..eda5b0b75c --- /dev/null +++ b/docs/website/video-transcripts/-7XHkBMK4NY.txt @@ -0,0 +1,120 @@ +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/video-transcripts/-M957AAi-vk.json b/docs/website/video-transcripts/-M957AAi-vk.json new file mode 100644 index 0000000000..159c40962a --- /dev/null +++ b/docs/website/video-transcripts/-M957AAi-vk.json @@ -0,0 +1,9 @@ +{ + "line_count": 28, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-use-http-sockets-webservices-websockets.md", + "status": "transcript-fetched", + "word_count": 1159, + "youtube_id": "-M957AAi-vk" +} diff --git a/docs/website/video-transcripts/-M957AAi-vk.txt b/docs/website/video-transcripts/-M957AAi-vk.txt new file mode 100644 index 0000000000..859c711a81 --- /dev/null +++ b/docs/website/video-transcripts/-M957AAi-vk.txt @@ -0,0 +1,48 @@ +_Transcript source: embedded._ + +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. + +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. + +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 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. + +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. + +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. + +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. + +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 diff --git a/docs/website/video-transcripts/-aTpqxDa1Ag.json b/docs/website/video-transcripts/-aTpqxDa1Ag.json new file mode 100644 index 0000000000..1ed7f411af --- /dev/null +++ b/docs/website/video-transcripts/-aTpqxDa1Ag.json @@ -0,0 +1,8 @@ +{ + "line_count": 117, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 707, + "youtube_id": "-aTpqxDa1Ag" +} diff --git a/docs/website/video-transcripts/-aTpqxDa1Ag.txt b/docs/website/video-transcripts/-aTpqxDa1Ag.txt new file mode 100644 index 0000000000..07cf6af9c0 --- /dev/null +++ b/docs/website/video-transcripts/-aTpqxDa1Ag.txt @@ -0,0 +1,117 @@ +how do we get friends +we already implemented contact upload +both in the server and the server api +class +we need to map that to the ui though +facebook nags about uploading contacts +all over the place which is probably +something you should do to if you are +building a social network i don't want +to get into that deep level of nagging +so i decided to add a floating action +button to the friends container +this is a bit tricky though +normally when we add a floating action +button we add it to a form +this hides some complexity it seems like +we are adding it to a form but in fact +it's hidden to a hidden layered pane +when we want to add it only to a +specific tab we need a better +understanding of the underlying +implementation +the floating action button wraps the +given container in a layered layout and +it places itself on top it then returns +a new container which you should use +instead of the original container +normally that's pretty easy to do but +it's obviously impossible to do within +friends container as it's the container +the solution is to implement this code +in the main form class +here we created a tab +for the contact upload +we find the fab and grab the return +value instead of the friends container +instance +pressing the fab will invoke the upload +contacts method +notice we add the returned friends +container that we wrapped with the fab +now that we have that we can implement +the upload contacts method +we need to fetch contacts from the +system it makes sense to do this on a +separate thread as this is a heavy task +when we fetch the contacts we specify +the fields we are interested in +if we specify fewer fields the fetch +operation will be faster +despite the name of the start thread +method we still need to invoke start on +the returned thread +once this is done pressing that button +should automatically prompt for +permission and upload the contacts from +your phone +in order to make the fab look good we +also need a couple of css changes +this mostly sets the fab to facebook +style blue color +each friend's suggestion has a button to +accept or remove the suggestion each +friend request has +similar button pairs +we added the bind confirm delete event +or bind add remove friends event call to +bind event listing to the buttons +when we remove or add a button we need +to remove the container of +the friend from the hierarchy but at the +stage where we bind the event we don't +have an instance of that container yet +a workaround is to locate that container +by traversing through the hierarchy of +the components +here we recursively look through parent +containers we stop when the parent is +the friend's container itself +this relies on the fact that friends +suggestion is added directly to the +parent friends container +once we have this implementing bind add +remove friend event +and bind confirm delete event becomes +trivial +we send a friend request +to the server when the button is pressed +after removing the container with the +friend's suggestion we animate the +remaining ui into place +we refresh the me object as it might be +stale after this change +the rest of the code is nearly identical +we invoke the server api code to trigger +the appropriate server changes +with this we can now send and accept +friend requests +the last piece are in the ui mapping is +notification support this is pretty +trivial now that we finished everything +just like before we no longer need the +last time value and can use the paging +support instead +we iterate over the notification objects +and create entry components for each one +new notifications will arrive via push +when we implement the push support later +on +with that we are effectively done with +the first part of the app +we have a fully working client server +mock-up there are still a lot of missing +pieces but this just became a process of +filling in the gaps it's much easier to +fill in the gaps once the foundation is +laid out in place diff --git a/docs/website/video-transcripts/-brmZYVWb0Y.json b/docs/website/video-transcripts/-brmZYVWb0Y.json new file mode 100644 index 0000000000..57e4ae456e --- /dev/null +++ b/docs/website/video-transcripts/-brmZYVWb0Y.json @@ -0,0 +1,8 @@ +{ + "line_count": 194, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1085, + "youtube_id": "-brmZYVWb0Y" +} diff --git a/docs/website/video-transcripts/-brmZYVWb0Y.txt b/docs/website/video-transcripts/-brmZYVWb0Y.txt new file mode 100644 index 0000000000..b3792bf469 --- /dev/null +++ b/docs/website/video-transcripts/-brmZYVWb0Y.txt @@ -0,0 +1,194 @@ +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/video-transcripts/-f1yue3hDEk.json b/docs/website/video-transcripts/-f1yue3hDEk.json new file mode 100644 index 0000000000..e892255b87 --- /dev/null +++ b/docs/website/video-transcripts/-f1yue3hDEk.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 162, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css.md", + "status": "transcript-fetched", + "word_count": 857, + "youtube_id": "-f1yue3hDEk" +} diff --git a/docs/website/video-transcripts/-f1yue3hDEk.txt b/docs/website/video-transcripts/-f1yue3hDEk.txt new file mode 100644 index 0000000000..849fde7c7f --- /dev/null +++ b/docs/website/video-transcripts/-f1yue3hDEk.txt @@ -0,0 +1,162 @@ +in this part we'll review the css +required to bind the application ui +if you haven't done so i'd recommend +checking out the video or material about +css integration in codename one as this +section assumes it's something that you +are familiar with +the constant selector is a special case +for theme constants +normally an include native ball must be +true +but in this case we have a layered theme +and setting this to force works around +some issues +the renderer show numbers ball flag +hides numbers in the list renderer +which you normally should define if you +use lists +for most themes container and label are +transparent but not in all themes +so it's important to define these +zero padding and margin are also very +important for a container +they should be there by default but this +makes sure +other than that +we just set a decent font for the label +the only entry for form +sets the background color of the form we +can leave the defaults in place +the title uid +makes sure the title is transparent +but most importantly +it disables a border that might be +applied to that ui id by the native +theme +it also enforces center alignment which +is the default in ios +but not on android +the command and menu color is set to +these two ui ids +i +i mentioned before that the area here is +a special component that abstracts the +view of the underlying image +this component is of the same color as +the form color with no margin so we will +get the visual effect +of the buttons peaking below the image +we place the title image that we cut out +before +in the background on top with a scale to +fill option +this allows us to fill up available +space +and possibly overflow +from the sides top or bottom +the order section +on top +uses a round rectangle border +which in css is defined as a pill +border notice the generous amount of +padding to push the border +so it won't seem cramped +also notice that the right padding isn't +even +the reason for that +is the shadow of the round border that +is technically a part of the border +since the shadow is cast to the bottom +right +we need more padding on those sides +the shopping cart icon +uses a regular round border +it's the same border type as the pull +border in codename one +but in css they are defined differently +notice the usage of margin to push the +border +from the right side +to distance it from the right edge +of the form +the list renderer is opaque by default +so we need to specify it as transparent +in the design +the text is slightly translucent +but that's a bit expensive and makes the +text too hard to read +instead i chose to just give it a +grayish color +which produces a similar effect without +performance implications and better +readability +i also +had to align the text to the center +as it's aligned to the left by the fault +and that looks weird in the carousel +mode +the selected version of the renderer is +white +and appears in the center +the positioning of the selected element +and its behavior are determined in the +code +the title +of each entry +is determined here +i use a decent font +and size although in a later revision i +made the one larger +which improved the design +i copied the color of the text from the +psd file +for the body +and used a thinner font to differentiate +from the title +the border of the buttons is a nine +piece image border +i defined it in the theme itself with +the gui tool as i found that to be +easier and more reliable +that might be due to personal habit as +i'm not used to css +i used some padding to space out the +content of the button +a common trick to implementing a +separator line is to use one pixel +padding with an opaque or translucent +color +that's what i did here +i also added three millimeter padding on +both sides to make sure the separator +spans correctly in this case +moving on to the checkout ui +most of the details in this page are +relatively simple +the one thing that's slightly different +but reasonably obvious +is the right alignment of the price +total entry +the checkout button derives from the add +to border button +and increases the size padding and +margin of everything +using inheritance in this way is +valuable as it allows us to reuse a +design element and thus save ram in +cases like this +where a nine piece border is used +the pieces we cut for the recipe +background we're cut into a nine piece +border in the theme designer +here +we define the padding and margin +the padding is required +so the text won't exceed the boundaries +of the nine piece image and the margin +helps us space the component from the +edges +notice the top bottom margins here +are zero +so the two separate pieces can touch diff --git a/docs/website/video-transcripts/008AK1GfHA8.json b/docs/website/video-transcripts/008AK1GfHA8.json new file mode 100644 index 0000000000..74ae6469e1 --- /dev/null +++ b/docs/website/video-transcripts/008AK1GfHA8.json @@ -0,0 +1,9 @@ +{ + "line_count": 11, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-debug-on-an-android-device.md", + "status": "transcript-fetched", + "word_count": 284, + "youtube_id": "008AK1GfHA8" +} diff --git a/docs/website/video-transcripts/008AK1GfHA8.txt b/docs/website/video-transcripts/008AK1GfHA8.txt new file mode 100644 index 0000000000..9516986323 --- /dev/null +++ b/docs/website/video-transcripts/008AK1GfHA8.txt @@ -0,0 +1,18 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +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. + +Some additional copying of gradle script snippets might be required based on your app! + +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/). diff --git a/docs/website/video-transcripts/0ETli_N__ZY.json b/docs/website/video-transcripts/0ETli_N__ZY.json new file mode 100644 index 0000000000..2387b17bb4 --- /dev/null +++ b/docs/website/video-transcripts/0ETli_N__ZY.json @@ -0,0 +1,8 @@ +{ + "line_count": 46, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 257, + "youtube_id": "0ETli_N__ZY" +} diff --git a/docs/website/video-transcripts/0ETli_N__ZY.txt b/docs/website/video-transcripts/0ETli_N__ZY.txt new file mode 100644 index 0000000000..6afea75b1f --- /dev/null +++ b/docs/website/video-transcripts/0ETli_N__ZY.txt @@ -0,0 +1,46 @@ +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/video-transcripts/0a6mPI412C4.json b/docs/website/video-transcripts/0a6mPI412C4.json new file mode 100644 index 0000000000..d2e795f787 --- /dev/null +++ b/docs/website/video-transcripts/0a6mPI412C4.json @@ -0,0 +1,8 @@ +{ + "line_count": 114, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 670, + "youtube_id": "0a6mPI412C4" +} diff --git a/docs/website/video-transcripts/0a6mPI412C4.txt b/docs/website/video-transcripts/0a6mPI412C4.txt new file mode 100644 index 0000000000..9f7b37a801 --- /dev/null +++ b/docs/website/video-transcripts/0a6mPI412C4.txt @@ -0,0 +1,114 @@ +the one last form we'll work on in the +markup stage is the new post form +right now i'll only support simple +variations of the new post i'll deal +with image and video posting later +we will discuss some built-in styles and +use html to display formatted posts +this could allow for logic that +implements things such as hashtags etc +in a future implementation +unlock the tabs we had until now the new +post is a separate form +the new post form includes styles that +can be applied to the editing such as +colors background images etc +the form itself uses a border layout to +position title area +on the top and the rest of the ui over +the entire screen +we save the current form before showing +so we can go back to it +the avatar of the user +goes on the left side of the title area +followed by a button that should let us +pick the right visibility mode for the +post +the text of the post is just a standard +text area with 3 rows and 80 columns +they are just hints for the preferred +size of the component +if we input more than 3 rows the ui +shouldn't grow +it might go on top of the post style +container and it would be hard to +differentiate a tap +there from +text edit gesture +this method creates the bar at the +bottom of the screen +when we click an entry in the bar +the look changes and the post gets a new +style +the bar on the bottom of the form is an +x-axis box layout +that's scrollable on the x-axis +every icon is eight millimeters in size +we loop over the style types the first +style is label +which means an unstyled regular post +we create a radio toggle button for +every style and set the style to the +button +for the rounded corners we use the round +rect border class and apply it manually +as the style is designed for the entire +entry +the first entry is a special case +because text fields are sometimes +transparent and so are labels +this implements the selected line border +around an entry +notice stroke color can be black when +surrounding the white label +entry when the user clicks on one of +these radio buttons we invoke the change +style method +this brings us directly to the change +style method +we need a special case for the first +entry again as the behavior is slightly +different +separate ui ids are needed for the text +area and the foreground +before we go into the css we need one +more thing to get this working +we need the following line in the +newsfeed container class +in the create +post bar method +there is quite a bit of css listed here +let's start with the simple stuff the +friend combo +this is the gray round wrecked border +that's applied to the visibility button +this button determines if something is +seen only by friends or is +public this is the base ui id for every +one of the uids listed in the array it +isn't mentioned in the code but it's +used in the css itself +next we have the css definitions of the +text styles +for this code we'll need +hearts.jpg and hands.jpg that represent +two of the designs +notice that all the styles here derive +from post style bass +i fill the hearts pattern into place +even though +it might make sense to tile it in some +cases +i scale to fit the hands entry i'm not +sure if it's the best way as even in the +native app the hands disappear in some +cases +the rest of the styles are pretty +trivial and should be easy to understand +this is the style of the foreground text +when another style is applied to the +background +with that we are done +you should have a working mock-up with a +sign-up wizard and core functionality +the core functionality we will work on diff --git a/docs/website/video-transcripts/0l4R049tSOY.json b/docs/website/video-transcripts/0l4R049tSOY.json new file mode 100644 index 0000000000..6d16ca6906 --- /dev/null +++ b/docs/website/video-transcripts/0l4R049tSOY.json @@ -0,0 +1,8 @@ +{ + "line_count": 52, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 267, + "youtube_id": "0l4R049tSOY" +} diff --git a/docs/website/video-transcripts/0l4R049tSOY.txt b/docs/website/video-transcripts/0l4R049tSOY.txt new file mode 100644 index 0000000000..2d5aeeb9f7 --- /dev/null +++ b/docs/website/video-transcripts/0l4R049tSOY.txt @@ -0,0 +1,52 @@ +the last piece of the puzzle +is the automatic update +since certificates expire every 90 days +we need to automate their replacement +and luckily linux is pretty darn +powerful with these capabilities +first we need to create a new script +file +with the root user +we use the nano text editor to create a +new script +the script is pretty simple we do the +exact steps i mentioned previously +one by one +all of the commands +the one big problem is that the server +will effectively go down every two +months for a couple of minutes +to perform this task +we could probably come up with a better +approach for this +but this is what i came up with +after saving the script +i converted to an executable file using +the change mode command +this step is important for the next +command which edits the chrome tab by +default it would launch vim which i +don't really like so i set the editor +variable to nano this means the next +command will launch nano +the chrome tab tools +tool allows us to define +tasks that run at a specific schedule +it launches an editor +where we can define the syntax of the +chrome +command +this essentially defines that the script +will run +on the first of every other month +as we only list the odd number months +in the list +these cases +in these cases let's encrypt will be +invoked +this is pretty much it +the certificate should be renewed +implicitly on the first of every other +month +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/0m7Bay4g93k.json b/docs/website/video-transcripts/0m7Bay4g93k.json new file mode 100644 index 0000000000..e75076b7fa --- /dev/null +++ b/docs/website/video-transcripts/0m7Bay4g93k.json @@ -0,0 +1,9 @@ +{ + "line_count": 29, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-create-a-list-of-items-the-easy-way.md", + "status": "transcript-fetched", + "word_count": 1200, + "youtube_id": "0m7Bay4g93k" +} diff --git a/docs/website/video-transcripts/0m7Bay4g93k.txt b/docs/website/video-transcripts/0m7Bay4g93k.txt new file mode 100644 index 0000000000..1172cd5097 --- /dev/null +++ b/docs/website/video-transcripts/0m7Bay4g93k.txt @@ -0,0 +1,50 @@ +_Transcript source: embedded._ + +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! + +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. + +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. + +Lets start with a hello world for a box layout list. This is pretty standard Codename One code, lets go over the different pieces. + +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. + +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. + +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. diff --git a/docs/website/video-transcripts/16-Vkgcx2kg.json b/docs/website/video-transcripts/16-Vkgcx2kg.json new file mode 100644 index 0000000000..e176cd6e59 --- /dev/null +++ b/docs/website/video-transcripts/16-Vkgcx2kg.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 147, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/021-implementing-the-native-camera-on-android.md", + "status": "transcript-fetched", + "word_count": 873, + "youtube_id": "16-Vkgcx2kg" +} diff --git a/docs/website/video-transcripts/16-Vkgcx2kg.txt b/docs/website/video-transcripts/16-Vkgcx2kg.txt new file mode 100644 index 0000000000..cc5661725f --- /dev/null +++ b/docs/website/video-transcripts/16-Vkgcx2kg.txt @@ -0,0 +1,147 @@ +now that we have the generic code of the +library let's go into the Android port +if this is the first tutorial on +building native interfaces you are going +through then here's an important tip use +the include Source feature of codename +one and build an Android Studio project +then implement the native interface +within Android Studio you can then copy +and paste the code back into the +codename one project +this is far more convenient as you can +Implement and debug the code right +within the native IDE +the same is true for xcode when working +on a Mac in this case because the native +interface was so simple albeit large +I skipped that stage but I have a bit of +experience doing this +most of you would probably feel more +comfortable building the Hello World app +with the native interface and +implementing it in Android Studio +the Android port is Trivial once we have +the native interface we can right click +on the project and choose generate +native stubs +which produces the skeleton code for +this implementation class +unlike before I'll start with the +Imports as some of them are a bit +challenging +I just imported everything as it was +simpler +this is an important helper class when +writing native Android code you will see +me use it soon +I wanted one method from the +implementation run on UI thread and wait +it's really useful in some cases +let's get into the code itself +the view is the camera preview UI +I keep it as a member member to return +later +The Listener is bound later in the code +it just delegates all the calls into the +Callback +notice I have only one listener on the +native side +all calls are effectively the same they +just invoke the camera callbacks class +methods +Android native file paths should be +prepended with file URL to work and +codename ones file system storage +the next step is life cycle and view +methods +after the start method is invoked I'd +like the view object to be valid +otherwise we might have a problem +this code must run on Android's native +EDT hence the need for run on UI thread +and block +we initialize the view by The Listener +and stopped notice we can stop and start +more than once +we don't need to wait for stop to +complete but it still needs to run on +Android's dispatch to it +this method returns a peer component in +the Java side +the Android view is translated +automatically +there is supported method is a standard +method in Native interfaces which +defaults to false when you implement a +version in a platform set it to true +next let's do a quick recap of the +setter methods in the native interface +we can't use a Lambda yet because we are +still using Java 5 for Android native +code this might change by the time you +see this +we hope to upgrade Android native code +for newer builds to use Java 8 syntax +which will shorten the boilerplate here +significantly +all of the methods here work roughly in +the same way by delegating to the native +core via run on UI thread core I could +go for a couple of additional pages of +Setters but you get the drift it's more +of the same +next we can review the Getters which are +shorter notice we don't need to use +run-on UI thread as the Getters aren't +as sensitive to threading +as you may recall some of the methods +here mapped to properties of The +Returned objects as is the case here and +with the preview slash capture methods +below +like the Setters this goes on a bit +further so I'll spare you an additional +slide where I don't have anything +interesting to see +finally we have the capture method calls +we need run on UI thread as capture is +thread sensitive +files and codename one have file URL +prepended so they will act as urls +we remove this to translate the file +into an Android file +all the other methods are exactly the +same they just invoke the native Android +version +this is it for the code but it won't +work yet +we need three build hands to get this to +work +the camera kit page asks that we add the +Gradle bin build dependency +codename one the codename one equivalent +of that is the build hint Android dot +Gradle dep +th the page also mentions a pro God +entry a bit down in the page +this is handled by the android.progod +keep build hand +unfortunately if you go through all of +that things still won't compile because +of the old build tools version and +Target so we need to use the latest +version of build tools which camera kit +depends on +specifically Android dot build tools +version 27. +this line is a temporary fix +we will soon flip the default build +tools version 227 at which point it will +be redundant +once this is done we recommend removing +it so when we go to 28 it will use that +once all of that is done you should be +able to use camera kit on Android the +challenge is now +iOS diff --git a/docs/website/video-transcripts/17ISIksjcPM.json b/docs/website/video-transcripts/17ISIksjcPM.json new file mode 100644 index 0000000000..b4c0270076 --- /dev/null +++ b/docs/website/video-transcripts/17ISIksjcPM.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 266, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/022-camera-ios-port-basics.md", + "status": "transcript-fetched", + "word_count": 1422, + "youtube_id": "17ISIksjcPM" +} diff --git a/docs/website/video-transcripts/17ISIksjcPM.txt b/docs/website/video-transcripts/17ISIksjcPM.txt new file mode 100644 index 0000000000..03753d9794 --- /dev/null +++ b/docs/website/video-transcripts/17ISIksjcPM.txt @@ -0,0 +1,266 @@ +the ios port is a steep climb unlike the +android version which maps almost +directly to the native code +still one of the advantages in ios +programming is the cleaner underlying +api that often simplifies common use +cases +due to this i chose to skip third-party +libraries and try to implement the +functionality of camera kit directly on +the native ios apis +when we use generate native stubs the +ios stubs include two files an h and an +m file +let's review the h file first i'll look +at what was generated in both +before we begin +this is a standard objective c +import statement that adds the basic +apple ios api +important objective c are more like c +includes than java imports +this is the class definition for the +native interface notice that ns object +is the common base class here +the method signatures should be pretty +readable as they map directly to the +java equivalent +getview expects a peer component on the +java side from this side +will return a ui view instance +once we get this +the implementation code is more of the +same +we import the header file we just +reviewed +just like includes in c +method implementations match their +declaration with curly braces for the +implementation +parameter names are important in ios +if you change the name of a parameter +you will break compilation as the name +is a part of the method signature +most types are familiar +but some like boolean +use a slightly different syntax with yes +and no instead of true and false +the same is true for nil instead of null +we'll start by bringing in constants +from the java constants interface +in the java implementation we could +ignore their values and the native side +because the implementation +already used the exact same values +we don't have that privilege +i'll also add an import to the native av +foundation +audio video foundation which is the ios +api for media +there are copies directly +from the java code with the public +static final portion replaced +to const +this will make coding the rest +easier +so now that we have the sense +of the scope let's start implementing +the important methods one by one +the natural place to start +is the start method +for this to work we first need to define +the first time camera kit launch +variable +the first time start is invoked we +initialize the default values of the +various constants to identify two +identical values we have in the android +version +this method initializes the camera view +the first time around notice that self +is the equivalent of this +dispatch sync is the ios equivalent of +call serially and wait +we want the block below to execute on +the native ios thread +and we want to wait until +it's defined it's finished +the capture session is stopped on the +stop call so if this isn't the first +time around we need to restart the +captcha session +before we proceed to the lazy init +method +let's look at the variables we added +into the header file +the direction the camera is facing front +or back +where the flash is on off or out of +flash mode +focus can be based on point or automatic +allows for several modes of capture i +didn't implement this for now +a set of constants indicating the +resolution for recorded video +current camera zoom value i saw yes here +if the app was given permission to +access the camera +since some callbacks for video and photo +might be similar i set this to indicate +what i'm capturing +this is the actual ui element we will +see on the screen +a ui view +is the ios parallel to component +i'll discuss this soon +notice that i imported the camera kit +view class here +it's a class i added +and i'll cover it soon +this is the native capture device +representing the camera +a different device instance is used when +we flip between back and front cameras +a session encapsulates the capture +process +we need to acquire access to the camera +with a session and relinquish it and +stop +the preview layer is where the video +from the camera is drawn this is a ca +layer which is a graphics surface that +we can assign to a ui view +i'll discuss this when covering camera +kit view +this class is responsible for capturing +a movie and saving it to a file +the last two entries handle photos the +former works on ios 10 +and newer devices +and the latter +works on older devices slash os +that's a lot to do to digest but we are +just getting started +let's move right into the lazy init +method +when i started with the code i thought +the camera will initialize lazily +but that didn't fit the +the rest of the api +so i abandoned that approach and +initialized onstart +i didn't bother +changing the name +since it isn't user visible anyway +the content of the following block runs +on the native ios thread synchronously +the method won't return until the code +is finished +this is the equivalent of +new object in objective c +we allocate the object and invoke its +init method which is sort of a +constructor +we're asking the av capture device +whether we have permission to use the +media device +this can result in one of four outcomes +not determined means we need to ask for +permission +so we ask which should prompt the user +with permission dialogue that he can +accept or reject +if he accepted we moved to the second +phase of +authorization +in lazy +init post authorization +if permission was denied in some way +there isn't much we can do the user will +see +a blank view +if authorization was already granted +previously we move on notice that for +this to work we need the ios ns camera +usage description constant i discussed +before +without that build hint permission is +denied automatically +let's move on to the lazy init post +authorization method +this is a complex method so i'll divide +it into two parts for simplicity +the first part of the method deals with +detection of the device +meaning picking the right camera +this checks if a specific class exists +ios 10 deprecated an api and introduced +a new one +if the new api doesn't exist we'll fall +back to the old api +if we reach this block we are in ios 10 +or newer +you will notice that getting a device in +ios 10 is one method with the only +difference being the position argument +value +notice objective c method invocations +use argument names +as part of the invocation +notice i referred to objective c +messages as methods +there is a difference between the two +but it's not something you need to +understand as a casual objective c user +this code is running on a device with an +ios os prior to ios 10. +here we loop over all the devices within +av capture device +if a device is in the right position we +update the device value and exit the +loop +the bottom portion of the method +is common to both ios 10 plus and prior +objective c often accepts pointers to +error variables which is it assigns +in case of an error +i don't check for error here +which i should +the input object can be created from the +device +we needed to start a session and donate +it after that at this time +we allocate a new +captcha session and set the input value +preview shows the video of the session +in our view +it's a ca layer +which we can't add directly to the +screen +set layer is a method i added to camera +kit view +i'll discuss that when covering +camera kit view +this is how you show a ca layer within a +ui view +these methods allow us to keep common +code between the initialization code and +the setter methods +that means a call to set flash will +trigger +update flash internally +i'll cover all four methods +soon +the final line starts the capture +session +this seems like a lot and it is a lot +we went through the heavy lifting +portion of the code and as you can see +it might not be trivial but it isn't +hard +i didn't know half of these methods when +i started out +but that's the great thing about being a +programmer in this day and age +we can google it diff --git a/docs/website/video-transcripts/1wHGnmO-vtE.json b/docs/website/video-transcripts/1wHGnmO-vtE.json new file mode 100644 index 0000000000..ec6981c547 --- /dev/null +++ b/docs/website/video-transcripts/1wHGnmO-vtE.json @@ -0,0 +1,8 @@ +{ + "line_count": 155, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1018, + "youtube_id": "1wHGnmO-vtE" +} diff --git a/docs/website/video-transcripts/1wHGnmO-vtE.txt b/docs/website/video-transcripts/1wHGnmO-vtE.txt new file mode 100644 index 0000000000..0a3ba58396 --- /dev/null +++ b/docs/website/video-transcripts/1wHGnmO-vtE.txt @@ -0,0 +1,155 @@ +hello guys uh some this time I want to +show you how to track performance in +your +application so to do this I'm going to +use the kitchen sync demo and not the +uhow world because I want to show off +some of the tools that perform much +better with a more elaborate application +like the kitchen sink so here I just ran +Debugger +the debugger the standard Java debugger +it works just as it would in any +standard Java project and you should use +it here I have an actual breako on +something but +that's something else uh you can use it +just like you would any uh debugger I +can just open uh say the kitchen sink +here or I can even just pause it and +instantly see where everything is for +instance where the EDT is where +everything is standing in case something +got stuck so +here I have uh the standard kitchen sink +say I want to break on the effects and +see what's going in there I can just go +into the +effects section right here place a break +point and debug it really convenient I +Debug +can just hover over everything inspect +everything and see where everything is I +hope you're familiar with these tools +they're really really useful and I'm +showing them just because Java +developers often don't look at them but +we offer a lot +more so for instance if you want to see +uh if you have violations in the EDT so +the EDT is one of the most important +things in COD name +one when +EDT Violations +uh when every event or every paint +operation happens in code name when it +happens on the event dispatch thread the +EDT and if you if if your code is too +slow and executes on the EDT well your +application will be slow if on the other +hand your code is +um uh accessing the EDT on a separate +thread then you might get uh crash or uh +behaviors that are just unexplainable +and really really hard to +track so the EDT um debugging tool here +which I've activated will show you when +EDT Debugging Tool +you have an EDT violation but the thing +is it's not always totally accurate +because of the way things are +implemented sometimes so for instance it +will fail on something like this uh see +it says it took too long to render a +frame which is probably true and then it +detects a violation of the EDT because +there are some issues there related to +the native components so that's what +essentially you'll see when you get a +violation the problem is you also see it +every time you edit text and you'll +notice that you actually see the +violation here as a stack pointing you +at the specific method that did +something supposedly bad in this +particular case we didn't do anything +bad it was completely fine it's +just uh text input doesn't really uh +work well with this tool but it's a +really really useful tool for when +things don't act as you would expect +them to and it also gives you warnings +about things like uh slow rendering for +certain things and and all sorts so +that's that's a cool tool the second one +Network Monitor +I've showed in previous uh session so I +won't spend too much time on it is the +network monitor so if I use something +like a web service and I send the web +service request I'll actually see the +request appear here I see the details of +the request the response length +everything and the full response data +including headers including everything +some headers are are trimmed just +because of uh some limitations uh of uh +the Java environment but it's pretty +complete for the most +part uh the next thing test recorder I +won't go into there's a separate +tutorial I'll talk about testing all all +Performance Monitor +as well uh but the last thing I want to +do is talk about the performance +Monitor and the performance monitor is +very interesting tool notice I'm playing +a bit with the UI and suddenly I see the +components that are in the UI now here +because the UI was hand coded most UI +components don't have a name because I +didn't actually name them but in +agreeable UI you will actually see names +and you can arrange uh the components by +the speed they take to render which +obviously the big container takes the +longest to render uh but it will tell +you what is taking up the most time and +Memory Monitor +what is drawn the most amount of times +in a particular screen and that is +useful information now you'll notice +that here we had some additional +information uh dumped into the screen +like images being created so this is +very interesting for you guys if uh you +have a user interface where memory is +running out for some reason well you can +use this tool to +monitor uh which image are being created +and images are usually the main reason +for memory issues and it tells you +pretty much how much memory and how much +uh in terms of size every image created +takes and that's again very useful +information to keep in mind when working +with a tool like that so I hope you've +enjoyed this short tutorial and I hope +you will make use of uh the tools we +have available for uh testing +performance and debugging and and I hope +you'll be able to resolve your issues +and if not talk to us in the discussion +forum where we'll try and help you uh +with these +things uh I didn't go into things like +logging and everything because that's +not so much the the a part of it but you +should be should be using the log apis +of cod name +one which provide a very useful way to +debugging on the devices at least in +terms of +logging so that's all for now and thanks +thank you for +watching diff --git a/docs/website/video-transcripts/2nD75pODPWk.json b/docs/website/video-transcripts/2nD75pODPWk.json new file mode 100644 index 0000000000..23a507236b --- /dev/null +++ b/docs/website/video-transcripts/2nD75pODPWk.json @@ -0,0 +1,8 @@ +{ + "line_count": 139, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 968, + "youtube_id": "2nD75pODPWk" +} diff --git a/docs/website/video-transcripts/2nD75pODPWk.txt b/docs/website/video-transcripts/2nD75pODPWk.txt new file mode 100644 index 0000000000..f51502b530 --- /dev/null +++ b/docs/website/video-transcripts/2nD75pODPWk.txt @@ -0,0 +1,139 @@ +in this tutorial we're going to talk +about debugging code name 1's source +code and your application based on that +code as well as forking it and making +changes and contributing them back so I +start by watching and starring the C +name one project and also forking it +obviously and we'll do the same for the +Cod name one skins project which is also +an important project we will need uh +notice that I'm copying the htps uh URL +for the repository here we'll soon see +it being used when we clone these forked +projects +locally so now we need to actually copy +the code locally through +clone now that we have this URL we can +just use gits clone and our credentials +from GitHub in order to fetch the +repository for codame 1 this will take a +while I'm skipping some of that uh in +the +video uh but eventually when it's +finished it will uh offer net beans will +offer to open the projects you don't +need to do that you just need to open +the code name one project and the ports +Javas Port project notice that uh these +projects will include errors don't worry +we'll fix all of +that now we need to fetch uh the Skins +project and that's pretty similar we're +just using the urls the URL for the +Skins uh project and uh pretty much the +same process exactly notice that here +you don't need to actually open the +directory and there's no projects within +that directory it's just used internally +as part of running the +simulator the next thing to do is to +download the cn1 binaries project you +don't need to Fork it because it doesn't +contain anything of value mostly stubs +and things like that so we just download +uh directly using the download button at +that project unzip it in the same +hierarchy as the other projects and make +sure the name is cn1 binaries and not +something else so it will be found by +the build scripts when we try to +build now comes the fun part we can +actually run the project and to do that +we need to remove the existing jars +within the project that allow us to +debug it and replace them with the +projects themselves in the library +section just remove all the jars and +instead add the actual code name one +project do the same in the Run section +make sure by the way that everything is +at the top of the class path I removed +the Javas se. jar and replaced it with +the ports Javas project and moved it to +the top as well that's it all I need to +do now is run and once I run the uh the +actual sources will be used in order to +run the simulator and it's it's mostly +seamless so here we'll talk about both +debugging code name one itself changing +it and then also contributing it back so +to debug code name one itself we can +just open this project and go to any +piece of it and when you click control +click on it you can go to the actual C +name one source uh and not to a library +source that means you can change +anything there you can debug directly +into Cod name one you can inspect +variables you can change their values +you can change anything and use apply +code changes you can literally do +anything as if this was a standard +project of your own which it is in this +case so now I'm going to actually change +a file uh the UI timer file under this +different user which isn't my standard +user in +GitHub and here I'm adding a new Factory +method for UI time which is actually +something quite useful that I wanted to +do for quite a while instead of creating +uh UI timers using a Constructor and +then invoking schedule so this is really +a simple shortcut method which is very +trivial to add and very useful so I'm +just adding this method placing +documentation and everything related to +that and once I do that I can just uh +use git to commit the change and then +use the git menu to push it +and notice that this is seamless because +it's pushing it to my own forked +repository and not directly to code name +one's repository so there's no checks +and you can do any change that you think +is right obviously keep in mind if you +want to contribute it to us in the +future that you need to abide by our +coding standards and levels and also +where the code came +from so this is all pretty trivial and +you can follow this with every get +tutorial so I'd like to talk about +contributions first you need to make +sure that you own every line of code +that you are +contributing if you don't make sure that +you didn't remove copyright headers or +anything like that and I suggest +discussing these things in the +discussion forum First Once you want to +contribute go to your fork of code name +one in that fork just press uh pull +request and submit it through the wizard +it's that +simple now once it's done we +automatically receive an email and can +process that P request or discuss it +with +you so I'd like to thank you for +watching this and we really appreciate +that and we appreciate all our users and +developers those that contribute and +those that do not uh but I do hope that +you play a bit with the source code +regardless of your attention to +contribute because I think it's very +useful and helpful and a great teaching +tool to us all thank you diff --git a/docs/website/video-transcripts/2xKvvv7XoVQ.json b/docs/website/video-transcripts/2xKvvv7XoVQ.json new file mode 100644 index 0000000000..bfe1d1ef48 --- /dev/null +++ b/docs/website/video-transcripts/2xKvvv7XoVQ.json @@ -0,0 +1,9 @@ +{ + "line_count": 31, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-access-native-device-functionality-invoke-native-interfaces.md", + "status": "transcript-fetched", + "word_count": 1210, + "youtube_id": "2xKvvv7XoVQ" +} diff --git a/docs/website/video-transcripts/2xKvvv7XoVQ.txt b/docs/website/video-transcripts/2xKvvv7XoVQ.txt new file mode 100644 index 0000000000..9baf3a0dc8 --- /dev/null +++ b/docs/website/video-transcripts/2xKvvv7XoVQ.txt @@ -0,0 +1,55 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +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. + +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. + +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? + +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. + +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. + +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. diff --git a/docs/website/video-transcripts/3-ZH2IFIIMY.json b/docs/website/video-transcripts/3-ZH2IFIIMY.json new file mode 100644 index 0000000000..93be41ddef --- /dev/null +++ b/docs/website/video-transcripts/3-ZH2IFIIMY.json @@ -0,0 +1,8 @@ +{ + "line_count": 187, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 980, + "youtube_id": "3-ZH2IFIIMY" +} diff --git a/docs/website/video-transcripts/3-ZH2IFIIMY.txt b/docs/website/video-transcripts/3-ZH2IFIIMY.txt new file mode 100644 index 0000000000..735fb385d0 --- /dev/null +++ b/docs/website/video-transcripts/3-ZH2IFIIMY.txt @@ -0,0 +1,187 @@ +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/video-transcripts/32mkZymqa6E.json b/docs/website/video-transcripts/32mkZymqa6E.json new file mode 100644 index 0000000000..da9fb60874 --- /dev/null +++ b/docs/website/video-transcripts/32mkZymqa6E.json @@ -0,0 +1,9 @@ +{ + "line_count": 29, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-localizetranslate-my-application-apply-i18nl10n-internationalizationlocalization-to-my-app.md", + "status": "transcript-fetched", + "word_count": 1216, + "youtube_id": "32mkZymqa6E" +} diff --git a/docs/website/video-transcripts/32mkZymqa6E.txt b/docs/website/video-transcripts/32mkZymqa6E.txt new file mode 100644 index 0000000000..d5ead2a989 --- /dev/null +++ b/docs/website/video-transcripts/32mkZymqa6E.txt @@ -0,0 +1,49 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +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. + +You can add additional locales using this button. In this case I added the iw locale. + +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 + +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. + +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. + +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. diff --git a/docs/website/video-transcripts/3B7C0ZbV8Bc.json b/docs/website/video-transcripts/3B7C0ZbV8Bc.json new file mode 100644 index 0000000000..90674c0b4d --- /dev/null +++ b/docs/website/video-transcripts/3B7C0ZbV8Bc.json @@ -0,0 +1,8 @@ +{ + "line_count": 338, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 5119, + "youtube_id": "3B7C0ZbV8Bc" +} diff --git a/docs/website/video-transcripts/3B7C0ZbV8Bc.txt b/docs/website/video-transcripts/3B7C0ZbV8Bc.txt new file mode 100644 index 0000000000..3edd7dac6e --- /dev/null +++ b/docs/website/video-transcripts/3B7C0ZbV8Bc.txt @@ -0,0 +1,338 @@ +in this uh lesson i'm going to talk about connecting to a web service +a simple json web service using codename one +now there's lots of ways to do web service communication including a wizard that +i'm not going to use in this particular case uh for many reasons but mostly because i +want to show the actual json connection which is useful if you're connecting +to any third-party solution so it will work outside of java even if +you're not doing full stack and it's really easy to work uh with full stack with json anyway when you're using java +so the value of the wizard isn't as huge as it was +before spring boot and things like that existed +so i'm going to use just the plain json and web connection and i'm not going to use +any special tools like xpath and which we do have in the processing +package or uh any of the third-party libraries or libraries from other +developers in codename one because i want to keep it very very simple to the +apis that you'd use normally day to day +i might go deeper into some of these later on but uh the the basic apis are actually pretty +simple so i'd like to focus on those so +the web service that we'll use in your you'd be familiar with that if you went through the spring boot uh +video it just has this sort of sort of curl syntax which means essentially we're +to add an element to the web service and this is the the web service that i'm going to attack first +we just use a put request that places that element into the item +url and it responds by actually returning the actual item that was added which +would normally be the exact same json that we submitted plus an id value which is auto incremented +from the database with everything related to that so that's a really simple web service just a put +request that includes the json within the body +so to do that i'd like to have an item class if you recall from +uh that video if you've seen it the item class is just it's a business +object and here i need to actually redo it in the client in this case it's not an entity because obviously i can't copy +the database object locally and it's also slightly different +i added a couple of methods to convert the item into a map structure and the +reason to and out of a map structure and the reason i need that is so i can +convert it to and from json using the json parser +now uh i chose to use a way that's very similar to the way i worked in the server +there's another way and i'll discuss it a bit later with the properties api +but i won't demonstrate it at this time uh you'll also noticed i trimmed quite a +lot of code here including the constructors and the getters and the setters they're all +trimmed because i'm assuming they're trivial for everyone and because i just didn't have space i didn't want the +fonts to be tiny and completely unreadable +the to actually send an item or get an eye in this +case get send an item i've added a server class and in that server class i have +variables for the url and the owner and within that server class i have +several methods to send and respond and get items +so the first method to send an item to put it is add item sync and notice the sync in +the end because it's a synchronous method that means it sends an item and +waits to return the value and that's why we have the return statement here and this is really important this is a +blocking method you'll call it and then thread that calls into that method will stop its execution until the +method completes and returns the item value that's a synchronous method +now normally synchronous calls are very bad on the ui thread you don't want to +do too much i o on the ui thread or anything like that so because the ui thread the the main thread of code in +one the one that handles the events handles the painting handles all of these things and i'll talk about it extensively in +the edt uh lesson that thread is it must be +protected from blocking normally because if you block it for too long and too long can be +milliseconds literally if you block it for too long you +you might cause a delay in the painting in the drawing and the app will feel +unresponsive and it would be a problem so i'd highly recommend that you be +careful with these things but this particular case is fine because here we use +built-in code codename one method that's uh blocking in a legal way and +there's a special loophole called invoking block and the trick for that is used by +the add to queue and wait method that time that i call here and that's legal that will not block +painting it will not block the the events from going on and the application should still work +completely fine and i'll talk about it in the next slide but that's why it's called sync +the other things you should notice here is uh the way we generate json +so the request i have i'm requesting the addition of an item so as you saw in the previous slide i +added a to map method to my item class so +i use that in order to convert the map object representation into adjacent string +and that literally generates a standard valid json string that matches +the the structure i would expect to receive in the server so now the string json literally has +that item request next i'm creating a connection request object +in this particular case i don't subclass it often we subclass it in this case when the sync calls i often +don't uh and i just build it to the url in this case the url is just the localhost +and i add the item extension so the code would be more generic +essentially the last argument it's true that means that this is a post request +but notice that i'm using put as the set http method below +the reason for um connection request has two modes post +and and get it doesn't have put delete or any of those methods you literally have to use the http method +entry below and the reason for that difference is that post and get +embed arguments in a very different way get adds them to the url to the end of the url and post places them in the body +so there's generally just two ways to add arguments to a url in http and +that's one of them is post and one of them is get there's also multiparts and other complexities but those that's a +very different subject basically those are the two important ways to add entries +and everything falls into either one or the other and normally get is obviously +a get request put is similar to post in that regard so i'm selecting that this would be a post +request and i'm setting it to to put explicitly +so essentially put as a variance of post that means it adds arguments in a +similar way although in this particular case it doesn't really matter because we're overriding the body so +it mostly matters because uh post accepts uh +an output stream to write into and get doesn't so in this particular case the +instead of using the output stream approach we just use set request body and just place the body of the request +instead of adding arguments we just have the json as the body of the request and that's it +and that works rather nicely also set obviously the content type and everything related exactly like we do in +the uh in the curl request +now after i do that uh the next line after add to key and wait actually results +with a response and at that point i just parse the response +check uh that no exception was turned when uh parsing although most of that is uh +redundant because um this is the data is already fetched so there won't be an ir exception but i +need to catch it because it's a checked exception and that's it i have a map that i can +parse out of the json and generate an item from that and +that's relatively simple not all that much to understand here +but let's dig a bit a bit deeper into the sync versus async because the next method we'll look into is an async +method so i implemented both of the methods uh in +this class the add and the put uh the add and the get sorry +with um both async and sync versions so you'll be able to see the differences in the +implementation and get a feel to both approaches to the api +now that's the main reason i did that but +usually you pick one and go with it for most cases uh +for each api that you use although you don't have to be consistent because most developers and myself included +mix and we use sync for this and we use async for that and we use we use both uh when possible and that's +one of the advantages of code name one that we have the ability to do both most apis +don't have that flexibility and need need to use +usually async exclusively [Music] now +the the sync api has mostly advantages in terms of fluidity +it's really really easy to use because when you have things like +a transactional process so for instance if you need to request something from the server and if the server answers x then +you need to proceed to stage two then you need to ask the user and then the user answers something and then you need to again request the server to do +something rel based on the answer from the user and then again the response something and then again back +and forth so you have a process that's really really easy to do stage by stage +with uh ifs dialogue prompts and things like that in that case sync works +really simplifies a lot because you can literally write code that goes top to bottom with ifs and it's +a really simple very readable code when you use async everything is a callback so your code +starts leaping between methods and every stage becomes a map to +okay what's where's the next stage the code isn't readable from top to bottom unless you +and even if you organize it it's it becomes a mess very quickly +so sync is really really intuitive for some specific use cases it's much easier +especially when we do things like dialogues or things like that they are synced exactly because of that a +dialogue by default blocks and that makes it much much simpler to +to do some basic things like are you sure is a great example +of something i'm asking the user something i'm waiting for a response and until i get the response there's no +point in the application moving forward so using sync for things like that is really +really convenient unfortunately sync does have its +drawbacks one of the drawbacks is that it's slightly slower +and in some cases it it's usually not noticeable +but in some cases it might happen especially when you have a sync api and then with a call and then +another sync api call and then again and at some point you even reach a limit because you can't nest them too deeply +and that's where things start getting kind of wonky when you use sync things sometimes behave very uh badly +when you're using a sync so things like uh if a +method call is uh blocking and you're nested uh +in uh for instance if you're in a series of an event chain and you're blocking because +the sinkhole the event chain won't proceed now that's a bit of a heady discussion +so i'll leave it to the discussion on the adt but generally um if you're having weird problems try +switching some of your sinkholes to async calls and see if that works +but you could you should usually pick what's convenient and what's right in the particular case +it's hard to give an exact guideline here +other than very specific things and i'll show you at least one point later on +where sync doesn't make sense at all and where we +have to do async to to move ahead and i'll show that later on and highlight it +so the async version of the same method is a bit more complex +first of all we have to subclass the connection request with the sync version we could have done +it but we didn't have to because we could just rely on the read response working here there is no way for us to +know when read response will actually happen so we must override it +or use an action listener which is effectively the same thing we'd be binding a listener to +uh to a callback and it would still be async but it's roughly the same thing so +might as well subclass so in this case you'll notice that uh +another interesting thing is that we don't catch i o exceptions suddenly +and the reason for that is that the callbacks of the connection request uh throw ire exception to begin with +and have a global error handling uh section within uh +within the network manager where they handle all of these sort of things +so that's really convenient because you don't need to to deal with all of the error handling nuances and things like +that you can just rely on global code sort of solving and closing we don't need to close the input even you'll +notice it isn't closed or the output because the connection request again the network manager +handles that for you it opens the streams passes them to you and then cleans up after you so it's like uh +an ideal uh uh housemate and that does all +all of the dirty work for you and in that respect it's really really convenient no +cleanup no none of that uh but it is a bit more boilerplate so +it's it's a matter of choice though you'll notice there's a couple of +other big different big things here first of all i'm placing the json within the build request body +i could have used the same code that i used before of setting the json through the method but i prefer doing it this +way for no real reason other than to show you how how it can be done +otherwise if i'm subclassing the difference isn't that big so i use this approach +uh the json is generated by the way in the exact same way so +there is no difference there and then the response from the server i read within read response +now technically i could have done you'll notice there's a separation between read response +and post response within read response i have roughly the same code as i have in the sync method +only in this particular case it doesn't have the try catch or the close of the output stream input stream or +anything like that because it's all encapsulated but you'll notice that the on success +callback is called from post response now that method is +a mistake that i made because i named it that way and i've been hitting myself on the head +over it for a while uh it's unrelated to post it's kind of confusing it's unrelated to post it in +this case um i meant post as in after the response and +i probably probably should have said response on edt or something like that which is what it actually is i just +didn't like that name so i didn't use it which was stupid +and uh in this particular case that this method is invoked after read response completes +but it's invoked on the event dispatch thread that means that's the ui thread the read response and the build request +body happen on one of the network threads and they occurred there and +you essentially get that's why this is actually +slightly more efficient than the previous code because all of this happens in a separate thread from the ui +thread so it isn't blocking the ui in any way whatsoever +the previous code didn't block the ui +when it was reading the data but then the parsing code happened on the ui thread which might have caused the +stutter if you do that too much this code happens entirely off the ui in +terms of parsing because i do i also do the parsing within the read response section so this is +noticeably faster than uh well not noticeably actually you know if +if it's a large json it would be noticeable than the previous one both because it's +async and because uh some of the functionality happens off the main thread +and the post response happens on the main thread and it sends the result item back to +whoever called this now you'll notice that the method returns void because it returns immediately you will invoke this method +because it's asynchronous it will return instantly and will not have the result at that +point so if you need to add the item and know that it was added this isn't the method for you the other +method when you add the item you will know that the item was added because it will be returned to you a new instance +of it in this method you'll add the item and you won't yet know +whether the item was added actually or if a network suddenly went out +and the way to use it is you need to use the on success callback +to do the logic of what is done when the item was added +or failed to add so +that that's hopefully more understandable the next web service that we're going to map is a +very similar one based on get where we get the data for a specific owner in this particular +case i used shy but i made it generic for any type of owner so +again this is relatively simple stuff we just make a request get back json and +it's just a simple get request and we'll again see the sync version of that and the async version of that +so the sync version of that and again we're starting with sync again very simple we use a +request this time we pass force because it's a get request and we don't need to set the request method because get is +the default when we have a get request so a connection request to the item url +and that's it now the next line and this is interesting i could have just written +item question mark uh plus owner something like that +plus owner equals owner and it would have worked actually for for this particular owner +for instance that and would have worked i don't really need the ad argument and i would have saved the line of code +so why use the add argument at all well add argument +encodes the data and encodes the data in case the data uses an illegal character or something +like that then it will implicitly work also if you suddenly decide to change +this to a post request or something like that then add argument will implicitly work +as well because you wouldn't be relying on the fact that the argument goes in the url and not in the body +so using add argument makes your code more more generic and more safe in terms of +working with the characters that aren't necessarily one of the standard +uh characters you're allowed to use in the url so that's really useful +uh the next line again uh jason request and add your key and wait as usual +and this is pretty much the same boilerplate as we had before in this case we're just parsing +an array of items and building it uh into place so there's not all that much of a +difference with get items than the room there is with the add items +except for the basic end result +the async version is uh again +similarly structured to to that version you'll notice that i need to store the +item eraser well we'll return it in the post response again it's +again not a big method and very similar and practically every way +again add argument as used instead of building the url +this is very simple code and i hope it makes sense uh to everyone +so in this uh section i'm going to integrate it to the +ui that we actually had in uh the the one of the previous lessons +where we showed a to-do list so if we think about the list of items can +actually be a list of shopping items and i totally didn't plan that or even think about that this is completely +random just thinking about it right now and that list of items we can add an item to +the shopping list and then list the items so what luck we've got the web service +working we've got the ui for that working and now let's bind it all together so +the first thing we need is to add two members to uh the +uh to the main class that we had in the ui uh +one of them is the owner variable which represents the owner +previously in the web service i used shy as the owner in this case i'm just using owner so it won't show all of the +experiments i did in the database uh obviously uh create a server instance +with the owner and and notice the server is just the class that includes the async and the sync +methods that i showed you earlier so it's just that and i'm +i'll make the calls on top of those variables and we'll see soon um +just this so if you remember though there's a for +loop that just added uh the set of shopping elements into uh the ui +literally a for loop that iterated all over the ui very similar to the one here only it didn't include the +item variables it just went over strings without too many calculations or anything +related to that so in this case i'm changing a couple of +things i'm looping over item objects +and if an item isn't deleted i'll just add it just like before +only uh one by one based on the item value +and that's relatively simple now you'll notice i'm using the async +method and you might recall that i said before that i'll highlight a point where +this is essential that i use async and not sync well this is the case +and technically it's not a hundred percent accurate i could have used the sync method and wrapped it with a core serially but that's kind of like +um not using sync at all because essentially wrapping and sync +method and call serially sort of disables it uh as a sync but in this particular case +i'm uh uh i'm before the stage where the shopping +list form has already shown so we initially need to show the form +before we actually start doing anything and this is the very first form in the application +and we also want it to appear regardless if you're before show form you don't want to use a sync call +because it will block the form from showing and that's kind of an important thing you want the ui to appear first +and then you can block the the edt so this is a bad place to to do a sinkhole +sinkhole works best and uh action listeners and things like that and in this particular case it would uh +essentially block the form from showing and because this is the first form in the application that would be really bad +so that's one place with sync where you mustn't use sync +now the the next thing is i want to add +an actual new element so i want to call the add method uh the add method +should work through the the floating action button and so far i didn't bind any action to +it so now i'm binding an action to it and actually most of this is actually ui code and not so much web services +but it's still interesting so i'm biting an action listener to the +floating action button the plus button there and the first thing is i want to scroll +to the top because i want to be at the the top of the ui uh for doing that and i want to stick a +text field right there and start editing it which is exactly what i do i i literally open the virtual keyboard and start +editing the text field and once the user completed editing if he entered text +i remove the text field and i create a new +checkbox with a separator and animate it into place +and then i call the async version of the method to add the new item +with the owner and with everything related to that then when the callback is received i +notify the user that the item was added on the server and +this works really well and pops up a text entry where you can +literally type in the new entry and it appears into place you'll notice i used the async call +i could use the sinkhole in this particular case this is one of those cases where it +almost doesn't matter i used async mostly because i felt it fit here +but honestly there's no reason to have gone one way or the other with this particular call i +wouldn't have got an advantage noticeable otherwise from one choice or +another and this is how it looks on a simulator uh i didn't run it on the device for one +simple simple reason and that's because the server is on my local +machine so to connect your server on your local machine from a phone is kind of painful you need +to discover the ip and the lan and sort of make sure that you're +connected to the lan and do it that way it's unpleasant to say the least so i chose +to uh just do this in the simulator for now and uh +it still looks nice so +i did that this was relatively simple i didn't go too deep into that because there's an infinite amount of depth we +can go into but if you want to go further +uh there's a lot that we can do to practice this and +if you want to warm yourself up and get something uh +some learning done then at least think about how you would solve these um +missing functionalities if not actually go ahead and implement them and and see how it works so +the the first one is to implement the check functionality i didn't implement that because i didn't implement an edit +uh feature so one would need to build a web service for editing on the server to +actually modify an item and that would be the approach you take +there and obviously also listen to the events and do that +communicate all of the states in the ui and update the server synchronously asynchronously you name it +uh extra credit is if you actually do a delete as well because there's a feature +for delete there that are used as a flag and not as physically deleting by the way which is +a very common practice because sometimes you know you need history and things like that +but if you can implement delete as well i would do it with a swipe where you can actually swipe the entries to the side +and have a trash can icon or something like that and delete entries +i'd be interested knowing if any of you do that actually that +level and super triple extra credit as if you port everything +to use properties now properties is the thing that i mentioned earlier as more +uh advanced uh actually it's simpler in some regards uh that are sort of skipped +in this presentation and i'll talk about it about them later probably in the boot camp itself +properties allow you to essentially avoid getters and setters and just +write more concise code and one of the nice things about them is that they automatically export an import from json +and structures like that without you having to write almost any code +and which is pretty nice i find you say so myself so uh if you have that extra +energy to go into that i'd be interested in uh if someone gets all the way to doing +doing this with properties as well that would probably be cool +so i hope this was uh educational and don't forget to ask questions let me +know how we're doing so i can tailor everything to +include information that might be missing in these presentations so +i hope you understood how to use a json web service how to build something like +that how to communicate with uh service like we created the last time around +that you understand the difference between async and sync calls in the network manager i'm sure you don't +understand completely without a background on the edt which i will cover in a future lesson +uh and i hope you understand that uh add to uh q and block +doesn't uh disturb the ui thread that means it's +it's a legal way to block the ui thread unlike normally illegal methods of doing that +and i hope you understand how to wire a remote call to the ui and essentially +make it seamless thanks for watching +again i hope you enjoyed it and let me know what you think diff --git a/docs/website/video-transcripts/3IC2qZ3wUO4.json b/docs/website/video-transcripts/3IC2qZ3wUO4.json new file mode 100644 index 0000000000..eaca2386f4 --- /dev/null +++ b/docs/website/video-transcripts/3IC2qZ3wUO4.json @@ -0,0 +1,8 @@ +{ + "line_count": 132, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 985, + "youtube_id": "3IC2qZ3wUO4" +} diff --git a/docs/website/video-transcripts/3IC2qZ3wUO4.txt b/docs/website/video-transcripts/3IC2qZ3wUO4.txt new file mode 100644 index 0000000000..529c3ef5c9 --- /dev/null +++ b/docs/website/video-transcripts/3IC2qZ3wUO4.txt @@ -0,0 +1,132 @@ +hello guys and welcome to a tutorial +about creating events and navigation in +the codename one GUI builder so we +created a hello world application +following the wizard and here we have +the theme the stator of the application +the state machine classes and everything +so I can just double click the resource +file which I did a moment ago and open +it up I can click the theme and see left +this is just a plain hello world +application and I want to add an +additional form to this application so +how do I do that let's start by just +pressing the add new GUI element right +here and give it a name like second and +now I can give it a title I can go here +and select this and just give it a title +like second so we all know where we are +and I'm going to go back to the first +form and create functionality to +navigate to the second form form so the +easiest thing is to just add a button +right here and I'll just drag it into +place and I'll say call it go to second +form now the easiest way to do this is +to a command now once we assign a +command to a button that's it it's got +the command it won't handle other events +won't do anything it will handle the +command so we can have lots of actions +with the command lock exit execute which +opens a URL in the browser go backwards +or just go to the second form so we do +that and this is the easiest and +probably the best way to do that because +now we can actually see the navigation +happening right here and you'll notice +you get the back command automatically +so this just works so that's cool that's +the best way because you can actually +see the navigation happening so now +we're on our second form and we say ok +we want a button +here that will actually do something +like show a dialogue or anything like +that so okay so let's select this button +which we have right here and go to the +events tab now here I can handle lots of +events and I'll just press the action +event and go into NetBeans NetBeans +automatically opens up the state machine +cross for me when my machine actually +starts moving and goes to the right +method to perform the action yeah now +you'll notice the override is giving me +an error yeah that's because I didn't +save the resource so now that I've saved +the resource file this is actually fixed +this is something very important that +you need to keep track of because when +you save the resource file the state +machine base is generated and as you can +see this method is really defined in the +state machine base you're just +overriding it so that's a very important +thing that you should notice when you're +building events so anyway this is my +event handling code and I just want to +show a dialog that just says hi world +and something like this and I'm going to +give it an OK to dismiss and nothing +else so that's basically it if I run it +in the simulator then I'll actually see +the functionality completely this +feature because it's in code you can't +preview in the designer so how I can go +to the second form and the second form +has a button that the form opens a +dialog and I can go back so that's +really cool and that handles the events +now the last portion and maybe slightly +harder to grasp is how do I populate +this so for instance if I have a label +right here or any other component and I +want the string on this label to be +something that comes out of code well +let's call this label we need to give it +a name to reference a top leaf from code +so we can give it a name my code label +and my coat label should now be edited +from code so how do I do that +well we go to the before show math +section and this is specific to the form +it doesn't matter if you press it when +this is selected or when this is +selected you just press it and it opens +the event handling code for the before +the second form cross so this method +will get executed before this form is +shown and gives you a pointer to the +actual form so now all we need to do is +open the write finder method so let's +find what's the name find my code label +I didn't save again see that's really +important and that's a big stumbling +block +here we go find my code label I can give +us the form as an argument and I can set +the text of the label from code now if I +run this then I'll see that the label +was set from code now why did we need to +do that why couldn't I just do this +manually well the explanation is really +simple +the second form only exists before you +actually show it and once we leave a +form we discard it so when we went back +from this form so this is a new form and +this is again a new form and when we go +back this is also a new form because we +always create the forms on the fly to +save memory so we discard them +immediately that is why the only place +where this label actually exists is +right before you create that's the point +where it starts living and afterwards +it's instantly discarded so that's why +you need to initialize things at that +particular point +so thanks a lot for watching and I hope +you enjoyed this video diff --git a/docs/website/video-transcripts/3rLf9A9RYPY.json b/docs/website/video-transcripts/3rLf9A9RYPY.json new file mode 100644 index 0000000000..aaa2a1f1c0 --- /dev/null +++ b/docs/website/video-transcripts/3rLf9A9RYPY.json @@ -0,0 +1,8 @@ +{ + "line_count": 206, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1131, + "youtube_id": "3rLf9A9RYPY" +} diff --git a/docs/website/video-transcripts/3rLf9A9RYPY.txt b/docs/website/video-transcripts/3rLf9A9RYPY.txt new file mode 100644 index 0000000000..99a86c0023 --- /dev/null +++ b/docs/website/video-transcripts/3rLf9A9RYPY.txt @@ -0,0 +1,206 @@ +with the facebook clone i ended up +constantly +contrasting uber and other apps with the +appearance of the facebook app +uber chose to use material design in the +ios version and the app looks great on +ios and android the flip side is that +uber is orientation locked and doesn't +support tablets properly +in that sense i don't want to make an +accurate facebook clone i want to make +something better +so the goal isn't an exact clone +but i will draw heavily heavy +inspiration i will copy the following +principles from facebook +native ui look +i will use native os widget look where +applicable +screenflow +i will keep a similar screenflow to +facebook +tablet and orientation i will support +orientation changes in the app and +tablet +mode plain text password i use this +behavior as facebook does even though i +don't like it +with that in mind i'll focus on the +following big ticket items +i won't go over each one right now but +i'll try to implement all of those in +the clone +before we proceed let's go over the +things i won't touch +the facebook app is huge +it contains a lot of nuanced hidden +functionality +most of that is simple but the breadth +of data makes it a huge undertaking +i've tried to address the complex +features of facebook but i'll skip some +big ticket items that i won't be able to +cover +events are complex they would require a +lot of time but won't necessarily teach +a new coding skill +camera special effects +this is probably possible but will also +probably take me an entire week to +implement +this is probably outside of the scope of +what we are trying to do +additional apps +the facebook app connects to instagram +messenger etc +this shouldn't be hard but i want to +focus on the app itself +find friends and complex searches +this is mostly server-side logic i'll +implement search but i won't go too deep +into this as there's a lot of server +side code +hashtags and complex posts +i won't go into all of the nuances of +post formatting supported by facebook +i'll include some common templates and +leave the rest for you to fill slash +enhance +i won't cover a lot of things such as +albums etc +privacy and visibility +while we will implement search in some +aspects of a social network we won't do +a lot of the important things +there are a lot of nuances to private +friends +friend only public postings that i won't +cover +these aren't hard to implement but they +mostly involve server side juggling +i prefer focusing on the client side +pretty much all the details business +pages ads promotions proper ranking +complex media detection etc facebook is +huge we will implement only a small +subset +supporting orientation tablets etc +requires a flexible architecture +furthermore facebook is filled with data +to a ridiculous extent there's so much +information about every attribute +we can support +that volume of data with properties +properties allow us to store data +transmit data +and automatically create uis from data +this is tremendous for a tool such as +facebook a facebook clone we can +eliminate a lot of the grind of ui +database passing overhead by leveraging +properties +because of the flexible nature of the +application i will use an approach of a +central controller class for the user +interface to keep things simple +now that we have a sense of what we are +trying to do +we can start with mocking up the initial +ui +we first need to start by creating the +facebook clone project +i created a standard codename one +project with the following settings +project name facebook loan package name +com dot codename one dot fb clone +template bare bones and theme native +i will use css for this project +the css plugin is getting +a complete rework right now and would be +integrated into codename one soon +check out the css project on github for +installation activation instruction as +an instructions as these will probably +change by the time you see this +we will need the facebook logo for the +markup and for that we will use an icon +font +icon fonts are fonts +that map a character glyph to an icon +that means that icon will have one color +but we can color it in any way we want +and size it draw it easily in every +platform +that's very convenient and there are +several providers +of such fonts +you can also convert svgs to fonts +but +the facebook logo is specifically widely +available +we'll make use of fontello which you can +find at fontello.com to generate a font +that contains the three facebook logos +from font awesome and the gender icons +pontello is a tool that can package +specific icons from multiple fonts into +a single file for your convenience +because of that we'll also use this +opportunity to bundle into icons +icon characters for male and female +gender +we'll use these during the sign up +these are icons from the mfg labs font +we need the gender icons as the material +design for icons don't have proper +gender representation +step one +type facebook into the search select the +three font awesome entries +step two type mail into the search and +select icons to match genders +step 3. type facebook into the name +field and download +step 4 copy the ttf file into the css +res directory of the facebook clone +project +you will need to create the rest +directory under the css directory +step 5 +open demo.html +in your browser +step six check the show codes box +step seven this is the code for each +icon we'll use +now that we have all of the pieces in +place we can create the initial css file +there are a few important things here so +let's review it one line at a time +this is a special css selector for theme +constants +you should have this in any theme by +default to derive the native theme +even if you don't think you are using +native theme features you probably are +this hides the scroll bars by default +sets the default gap between a label and +its icon +it applies to buttons and all label +subclasses +these +should already be transparent +this is here just to verify +the default font for the styles might be +ignored if a specific built-in style +already defines a font of its own +we define the ttf of the facebook icon +font +we downloaded before +using this we can draw a large facebook +logo with the font +while i'm here i'll define the main form +to be white +the app is a bit inconsistent with this +but white seems to be +the main background color diff --git a/docs/website/video-transcripts/46TpE-Cgtw8.json b/docs/website/video-transcripts/46TpE-Cgtw8.json new file mode 100644 index 0000000000..1a1c243936 --- /dev/null +++ b/docs/website/video-transcripts/46TpE-Cgtw8.json @@ -0,0 +1,8 @@ +{ + "line_count": 143, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 859, + "youtube_id": "46TpE-Cgtw8" +} diff --git a/docs/website/video-transcripts/46TpE-Cgtw8.txt b/docs/website/video-transcripts/46TpE-Cgtw8.txt new file mode 100644 index 0000000000..eecc6e4e7b --- /dev/null +++ b/docs/website/video-transcripts/46TpE-Cgtw8.txt @@ -0,0 +1,143 @@ +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/video-transcripts/47WhIiLxv78.json b/docs/website/video-transcripts/47WhIiLxv78.json new file mode 100644 index 0000000000..c8cda10094 --- /dev/null +++ b/docs/website/video-transcripts/47WhIiLxv78.json @@ -0,0 +1,8 @@ +{ + "line_count": 189, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1110, + "youtube_id": "47WhIiLxv78" +} diff --git a/docs/website/video-transcripts/47WhIiLxv78.txt b/docs/website/video-transcripts/47WhIiLxv78.txt new file mode 100644 index 0000000000..2d1d852cd4 --- /dev/null +++ b/docs/website/video-transcripts/47WhIiLxv78.txt @@ -0,0 +1,189 @@ +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/video-transcripts/4D_KUa2qv2o.json b/docs/website/video-transcripts/4D_KUa2qv2o.json new file mode 100644 index 0000000000..6018b2c066 --- /dev/null +++ b/docs/website/video-transcripts/4D_KUa2qv2o.json @@ -0,0 +1,9 @@ +{ + "line_count": 28, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-positioning-components-using-layout-managers.md", + "status": "transcript-fetched", + "word_count": 1032, + "youtube_id": "4D_KUa2qv2o" +} diff --git a/docs/website/video-transcripts/4D_KUa2qv2o.txt b/docs/website/video-transcripts/4D_KUa2qv2o.txt new file mode 100644 index 0000000000..28b39f4ff9 --- /dev/null +++ b/docs/website/video-transcripts/4D_KUa2qv2o.txt @@ -0,0 +1,55 @@ +_Transcript source: embedded._ + +In this short video I’ll try to address one of the most challenging basic features of Codename One: Layouts + +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 + +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 + +This begs the question: Why? Why not place the components where we want them to be on the screen? + +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. + +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 + +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 diff --git a/docs/website/video-transcripts/4jIqplr19HA.json b/docs/website/video-transcripts/4jIqplr19HA.json new file mode 100644 index 0000000000..59a0217d81 --- /dev/null +++ b/docs/website/video-transcripts/4jIqplr19HA.json @@ -0,0 +1,8 @@ +{ + "line_count": 117, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 622, + "youtube_id": "4jIqplr19HA" +} diff --git a/docs/website/video-transcripts/4jIqplr19HA.txt b/docs/website/video-transcripts/4jIqplr19HA.txt new file mode 100644 index 0000000000..4aa2418db3 --- /dev/null +++ b/docs/website/video-transcripts/4jIqplr19HA.txt @@ -0,0 +1,117 @@ +we continue with installations of many +packages we need to run everything +our server is headless +that means it has no monitor or video +display +so +if we try to use an api like swing or +java fx +the app will fail as it won't be able to +display anything +normally that is not a problem +but since we might want to run the css +compiler +it would need access to graphics +it's also useful for other features so +if you want to generate images on the +server the ability to use java2d would +be useful +that's where xvfb comes in handy +x11 is the windowing system for linux +and other unix flavors +xvfb +uses x on frame buffer and effectively +allows us to draw even without a display +that's useful if we need to run gui code +that doesn't actually need the +screen +Unzip +we will next install unzip +we'll need it later to install and +Install MariaDB +the next step is the installation of +mariadb +which i mentioned earlier it's a fork of +mysql that is supported by centos +this installs the server itself +but we need a few additional steps +Add MariaDB +this adds mariadb +to the startup script so it loads on +system boot +this step verifies that the previous +step succeeded and mariadb is running +i would go into more details on this but +boot process and linux is a bit +different between distributions so i'd +rather not dig in too much +Security +mysql and mariadb ship with a great +script to harden security +i followed the advice and restricted as +much as possible although you might want +to allow your ip to have remote access +to the server +this might make it easier to administer +the server remotely +this is an important step +having a server discoverable on the +internet is pretty dangerous +one thing i did which is important +was setting the database password to the +same value as the one i have +in the development server +that means the code is exactly the same +albeit slightly less secure +to be fair that's not a problem +if the database can't be accessed +remotely +Port +the next step +is exposing the right port +unix-based systems block tcp ports below +1024 +you can't listen on that port without +root privileges +this is generally a security measure so +a random user who logs into the system +won't start a server on the machine +there are many ways around it but the +one i use most often is ip tables which +you need to run as root and effectively +redirect traffic in the kernel level +so traffic on port 80 gets redirected to +8080 in this command +in case you don't know port 80 is the +default port of the http protocol +Port 80 +port 443 isn't as known as port 80. +it's the default port for https protocol +so we are doing here the exact same +thing +for that and port 8443 +Builder +finally it's time to become the builder +notice we don't need a password to do +this as we are assuming the builder user +from the root +user +Home Directory +tild is a special character in +unix +it represents the home directory +of the user +so when we are root tild is slash root +and when we are builder it's slash home +slash builder +currently we are at slash root because +that's where we logged in +so we need to go to the home directory +of +builder +and that's what this command does +notice the sign next to the command is a +dollar sign +and not a pound sign or hash sign +that's because we are now using the user +account and not the root account diff --git a/docs/website/video-transcripts/54POZ4PFFBw.json b/docs/website/video-transcripts/54POZ4PFFBw.json new file mode 100644 index 0000000000..172c8cb5d9 --- /dev/null +++ b/docs/website/video-transcripts/54POZ4PFFBw.json @@ -0,0 +1,8 @@ +{ + "line_count": 126, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 714, + "youtube_id": "54POZ4PFFBw" +} diff --git a/docs/website/video-transcripts/54POZ4PFFBw.txt b/docs/website/video-transcripts/54POZ4PFFBw.txt new file mode 100644 index 0000000000..20b7ef9cd2 --- /dev/null +++ b/docs/website/video-transcripts/54POZ4PFFBw.txt @@ -0,0 +1,126 @@ +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/video-transcripts/5EyrMpQqR-k.json b/docs/website/video-transcripts/5EyrMpQqR-k.json new file mode 100644 index 0000000000..f53bd2dbd3 --- /dev/null +++ b/docs/website/video-transcripts/5EyrMpQqR-k.json @@ -0,0 +1,8 @@ +{ + "line_count": 162, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 949, + "youtube_id": "5EyrMpQqR-k" +} diff --git a/docs/website/video-transcripts/5EyrMpQqR-k.txt b/docs/website/video-transcripts/5EyrMpQqR-k.txt new file mode 100644 index 0000000000..198965a2f2 --- /dev/null +++ b/docs/website/video-transcripts/5EyrMpQqR-k.txt @@ -0,0 +1,162 @@ +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/video-transcripts/5WpZmIkdUfs.json b/docs/website/video-transcripts/5WpZmIkdUfs.json new file mode 100644 index 0000000000..e90aa9d0c1 --- /dev/null +++ b/docs/website/video-transcripts/5WpZmIkdUfs.json @@ -0,0 +1,8 @@ +{ + "line_count": 142, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 814, + "youtube_id": "5WpZmIkdUfs" +} diff --git a/docs/website/video-transcripts/5WpZmIkdUfs.txt b/docs/website/video-transcripts/5WpZmIkdUfs.txt new file mode 100644 index 0000000000..f214ae4ede --- /dev/null +++ b/docs/website/video-transcripts/5WpZmIkdUfs.txt @@ -0,0 +1,142 @@ +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/video-transcripts/5eMvwRcDcug.json b/docs/website/video-transcripts/5eMvwRcDcug.json new file mode 100644 index 0000000000..f01e302f1f --- /dev/null +++ b/docs/website/video-transcripts/5eMvwRcDcug.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 209, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/036-building-an-android-native-version-of-the-kitchen-sink.md", + "status": "transcript-fetched", + "word_count": 1214, + "youtube_id": "5eMvwRcDcug" +} diff --git a/docs/website/video-transcripts/5eMvwRcDcug.txt b/docs/website/video-transcripts/5eMvwRcDcug.txt new file mode 100644 index 0000000000..2f8742f429 --- /dev/null +++ b/docs/website/video-transcripts/5eMvwRcDcug.txt @@ -0,0 +1,209 @@ +now that we have the general drift let's +move on to android +in one aspect android is easier than ios +as it's based on java +however other aspects make it a bit +painful +and in most regards harder to work with +than ios +we will first use android studio 3 to +build the project since this is pretty +much what google demands +the first step in building on android is +mostly about running through the ide +steps +the first step is the new project option +when setting the project details the +most important bit is the package name +it should match our main package name +i left the default settings for the +target platform and changed this should +work fine +i chose the blank activity i'll create +one manually later +we are now presented with a progress +indicator and then after a while android +studio finally loads +notice that you might need to resolve +issues in the id if it connects to older +versions of the android sdk installed in +your system +the first thing we need to do +once the ide has finished loading is +configure java 8 support the codename +one build servers translate the built +byte codes so they will work with java 8 +support on android +we need to do that since we have no +access to the source code in the servers +and we want to remain compatible +however java 8 is now supported in +android studio and this will allow your +code to compile unchanged notice that +the core codename one code is still java +5 compatible +to do this we need to first pick the +project structure option from the menu +we then need to set the source and +target compatibility +to java 8. +the next step is to edit the +build.gradle file and add support for +our sources notice that there are two +build.gradle files in the project you +will only need to edit the one for the +android project itself which in my case +was marked as module +colon app +after you make changes to +system files android studio offers to +sync the project again +which you should accept +this is the full code but you should +probably take the lines i highlight in +the modifications +let's scroll down and look at the +changes +i added a source set section +that points at the sources of the +kitchen sink project +that way they appear in the hierarchy +but we don't need to copy them +i added all of the dependencies to the +build +notice that we don't actually need all +of the dependencies the build servers +delete code that you don't need based on +build hints so if facebook support isn't +needed necessary it will delete facebook +import class +and won't place the facebook dependency +the same is true for the other +dependencies mentioned +we also need to make a minor change to +the gradle properties file by adding the +this line +this is required for some of the star +code later on +the next step is copying the android +implementation sources into the project +since the android implementation sources +don't include the codename one sources +we need to start with that +these commands are exclu executed from +the kitchen sink directory so they are +relative to that path +notice we copy the codename one code +first and the android code second so +files get overwritten +also notice we delete all of the files +and the root of the project source not +recursively +the root of the codename one +and android projects include resource +files that files that should be placed +in a different location +android expects all resources that +aren't in the resident file +to be within the assets directory +we need to create that directory in the +hierarchy where the res directory +resides +this copies the files from the roots of +all these projects notice the order of +commands +sets the priority since projects on the +way can override the parent project +following these instructions you might +be thinking why copy +we included the source of the kitchen +sink why not do the same for the +implementation +the main problem is override overridden +files codename one redefines the +codename one thread class +in the android implementation and relies +on that redefinition taking the priority +we might do more of that in the future +this is also important because you might +want to delete some files such as +facebook support so copy makes more +sense in this special case +the next step +is the activity class which is +android's lifecycle object +this is pretty a pretty standard class +in android so i'll add the code and step +over it like before +we have a codename one activity base +class which is important for the main +activity +as we handle some nuanced events there +most android apps use oncreate to detect +app launch we use onresume which is more +consistent for our needs in terms of +suspend resume behavior +these lines effectively initialize +codename one and start the edt if it +isn't running +we also allocate the kitchen sink main +class if it isn't allocated yet +i did the allocation on the android +thread which isn't ideal but the +callback should be invoked on the +codename one thread which is why we have +this call serially here +now that +now on the edt +this is the first time we need to invoke +the init object method of the main class +otherwise we are resuming we try to be +smart about the current form since +android has a tendency to restart +activities for everything +start is invoked with every call to +resume to match the life cycle behavior +of codename one the same holds true to +stop and destroy both of which are +pretty trivial +the rest of the code is just boilerplate +and uninteresting we invoke stop and +onstop and invoke destroy and on destroy +now that all of that is out of the way +we just need to configure the xml files +first we have the manifest file which +represents all of the activities in the +application +the manifest isn't really important here +i'll just use the default manifest +generated by the ide and updated the +activity entry +notice that most of the entries within +the activity are essential for the app +we also need a layout +for the main application so under the +res slash layout directory we need to +add the file main.xml +that's a pretty simple layout just to +give room for codename one itself +we need three style files to support all +of this the first is styles.xml which +resides in the directory src slash main +slash res values +which just includes theme and not much +else +the second bears the same name but +resides in the directory src main res +values v11 again this is pretty trivial +and hard coded for all +the apps +the final one also has the same name but +resides in the directory src slash main +slash red slash values +21 it includes some additional settings +for newer versions of android +now that all of this is done you can +just press play in the ide and run +an android on an android device +there was a bit of boilerplate but i +hope it's clear diff --git a/docs/website/video-transcripts/65LciCzyRNQ.json b/docs/website/video-transcripts/65LciCzyRNQ.json new file mode 100644 index 0000000000..803ea9f28f --- /dev/null +++ b/docs/website/video-transcripts/65LciCzyRNQ.json @@ -0,0 +1,8 @@ +{ + "line_count": 160, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 910, + "youtube_id": "65LciCzyRNQ" +} diff --git a/docs/website/video-transcripts/65LciCzyRNQ.txt b/docs/website/video-transcripts/65LciCzyRNQ.txt new file mode 100644 index 0000000000..a561c7948a --- /dev/null +++ b/docs/website/video-transcripts/65LciCzyRNQ.txt @@ -0,0 +1,160 @@ +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/video-transcripts/65jD9oGw61w.json b/docs/website/video-transcripts/65jD9oGw61w.json new file mode 100644 index 0000000000..d1f9740a28 --- /dev/null +++ b/docs/website/video-transcripts/65jD9oGw61w.json @@ -0,0 +1,8 @@ +{ + "line_count": 404, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 2398, + "youtube_id": "65jD9oGw61w" +} diff --git a/docs/website/video-transcripts/65jD9oGw61w.txt b/docs/website/video-transcripts/65jD9oGw61w.txt new file mode 100644 index 0000000000..90573b7b09 --- /dev/null +++ b/docs/website/video-transcripts/65jD9oGw61w.txt @@ -0,0 +1,404 @@ +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/video-transcripts/6oTy-LcTm0s.json b/docs/website/video-transcripts/6oTy-LcTm0s.json new file mode 100644 index 0000000000..54ae18c25f --- /dev/null +++ b/docs/website/video-transcripts/6oTy-LcTm0s.json @@ -0,0 +1,9 @@ +{ + "line_count": 31, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc.md", + "status": "transcript-fetched", + "word_count": 1010, + "youtube_id": "6oTy-LcTm0s" +} diff --git a/docs/website/video-transcripts/6oTy-LcTm0s.txt b/docs/website/video-transcripts/6oTy-LcTm0s.txt new file mode 100644 index 0000000000..78c9b8c025 --- /dev/null +++ b/docs/website/video-transcripts/6oTy-LcTm0s.txt @@ -0,0 +1,55 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +For iOS we have the sources tar.bz2 file which includes an xcode project. + +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 + +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. + +When we download and unzip the respective source archives we can see the Android & iOS source structures. + +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. + +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. diff --git a/docs/website/video-transcripts/73d65cvyQv4.json b/docs/website/video-transcripts/73d65cvyQv4.json new file mode 100644 index 0000000000..435fbe5c94 --- /dev/null +++ b/docs/website/video-transcripts/73d65cvyQv4.json @@ -0,0 +1,9 @@ +{ + "line_count": 22, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-netbeans.md", + "status": "transcript-fetched", + "word_count": 599, + "youtube_id": "73d65cvyQv4" +} diff --git a/docs/website/video-transcripts/73d65cvyQv4.txt b/docs/website/video-transcripts/73d65cvyQv4.txt new file mode 100644 index 0000000000..4cb47efc04 --- /dev/null +++ b/docs/website/video-transcripts/73d65cvyQv4.txt @@ -0,0 +1,35 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +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/). diff --git a/docs/website/video-transcripts/77N2t2n8rbQ.json b/docs/website/video-transcripts/77N2t2n8rbQ.json new file mode 100644 index 0000000000..86946c4052 --- /dev/null +++ b/docs/website/video-transcripts/77N2t2n8rbQ.json @@ -0,0 +1,9 @@ +{ + "line_count": 15, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-use-properties-to-speed-development.md", + "status": "transcript-fetched", + "word_count": 534, + "youtube_id": "77N2t2n8rbQ" +} diff --git a/docs/website/video-transcripts/77N2t2n8rbQ.txt b/docs/website/video-transcripts/77N2t2n8rbQ.txt new file mode 100644 index 0000000000..30489a94c9 --- /dev/null +++ b/docs/website/video-transcripts/77N2t2n8rbQ.txt @@ -0,0 +1,27 @@ +_Transcript source: embedded._ + +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. + +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. + +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 + +We can also construct an object by chaining setters together. This removes the need to create many obtuse constructors for an object + +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. + +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 diff --git a/docs/website/video-transcripts/7CoD9u6KM2Q.json b/docs/website/video-transcripts/7CoD9u6KM2Q.json new file mode 100644 index 0000000000..5ed497235a --- /dev/null +++ b/docs/website/video-transcripts/7CoD9u6KM2Q.json @@ -0,0 +1,8 @@ +{ + "line_count": 289, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1590, + "youtube_id": "7CoD9u6KM2Q" +} diff --git a/docs/website/video-transcripts/7CoD9u6KM2Q.txt b/docs/website/video-transcripts/7CoD9u6KM2Q.txt new file mode 100644 index 0000000000..7354663b8d --- /dev/null +++ b/docs/website/video-transcripts/7CoD9u6KM2Q.txt @@ -0,0 +1,289 @@ +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/video-transcripts/7G4OkjgTWIQ.json b/docs/website/video-transcripts/7G4OkjgTWIQ.json new file mode 100644 index 0000000000..1a62210566 --- /dev/null +++ b/docs/website/video-transcripts/7G4OkjgTWIQ.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 159, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/023-arc-and-view-on-ios.md", + "status": "transcript-fetched", + "word_count": 955, + "youtube_id": "7G4OkjgTWIQ" +} diff --git a/docs/website/video-transcripts/7G4OkjgTWIQ.txt b/docs/website/video-transcripts/7G4OkjgTWIQ.txt new file mode 100644 index 0000000000..43f085dfbb --- /dev/null +++ b/docs/website/video-transcripts/7G4OkjgTWIQ.txt @@ -0,0 +1,159 @@ +in this part we continue the iOS Port +work +starting with camera kit View +before I go into the four new update +methods I'd like to detour to the camera +kit view class first +this is pretty standard we just derived +from UI view which is the standard +component type for iOS UI +this is the layer we set keeping it as a +variable is convenient +this is a method we're overriding from +UI view it's invoked when the UI view is +arranged by iOS which also has something +similar to a layout manager +this is the set layer method we +discussed before +the implementation of the class is even +simpler we store the layer into the +member field in set layer +when laying out the view I update the +frame based on the bounds both represent +the physical location of The View on the +screen +codename one positions the UI view +automatically +but the ca layer within is positioned by +this class so when coding one places the +UI view based on the layout manager the +bonds of the UI view are copied into the +layer so it shows in the same place +now that this is out of the way let's go +into the update methods before I go into +the ones we already saw there is a +hidden one which is invoked when a user +invokes set facing to change the +direction +it's not in the code from before as the +functionality was embedded into that +code +when we change the camera direction we +need to pick a new device and +effectively start over again +which is what lazy init post +authorization does +here we discard the preview layer and +stop the capture session +Objective C uses reference counting we +need to release objects we allocated +this is handled automatically by Arc +normally +but Arc collides with the GC +because this is essentially recreating +the UI we go through the second part of +initialization over again +Objective C and Swift don't have a +garbage collector like Java does +instead they use reference counting +which means every object has a number +representing the areas of the code that +need it +when I don't want an object deleted I +invoke my object retain +and it's saved +when I don't care about it anymore I do +my object release a retain operation +increments a number and a release call +decrements it +when the number reaches zero the object +is released +there is more to it but that's the basic +gist of it +a few years back Apple introduced a +compiler enhancement that automatically +injects retain release calls into the +code so developers don't see this and it +feels more like working with a garbage +collector +this is called Arc which stands for +automatic reference Counting +unfortunately we can't get armed to play +nicely with our garbage collector +I won't go too too deep into the subject +of GC versus reference counting as it's +a problematic subject but here's the +gist of it reference counting can fail +with cyclic references object a needs +object B and vice versa +GCS are immune to such cases +reference counting provides more +deterministic behavior that means it +will always perform exactly +at exactly the same speed as we can rely +on its Behavior +garbage collectors are faster but +sometimes too +for uis this can sometimes be a problem +as a GC will behave one way and one +execution and differently in another +execution +we looked at using a hybrid reference +counter GC solution When developing rvm +and eventually scrapped that as there +were no benefits in that approach +both approaches are workable and you +need to be ready to debug their pitfalls +moving on let's look at the update video +quality method while it's a bit bigger +The Core Concepts are relatively simple +this is invoked before start +it's totally fine this method will be +invoked again when start is in a vote +when we manipulate some configurations +we need to acquire a device lock to +prevent concurrent modifications +the rest is a standard switch case to +map the standard constants to iOS +constants +this was pretty simple next on the line +is update flash which is also as simple +I can go over the method but there is +really nothing here that we didn't +discuss in the previous method we return +for null device we lock for +configuration and we convert the +constant type +surprisingly update Focus does have +something new to say despite being +pretty identical to the first two +I'll skip the identical part and discuss +the final section +there is no built-in focus on tap in iOS +so we need to use some code to do this +this invokes the temp to focus method on +self which is this object in Objective C +when the user Taps container +so let's look at the tab to focus method +notice that this logic isn't something I +came up with I took it from a stack +Overflow answer which is very convenient +for implementing these sort of things +a tap returns a point on the screen +which we can convert to a point relative +to the coordinate space of the preview +layer +if we can declare a focus point of +Interest we can just set the focus to +the right point +it's not necessarily trivial but moistly +mostly boilerplate +this brings us to the last and simplest +of these methods this is mostly a rehash +of the other method so I won't discuss +it notice this at Gods against zooming +too much or too little +and that's it with these changes camera +will basically work we just need to fill +in a few more details which are mostly +boilerplate diff --git a/docs/website/video-transcripts/7XHkBMK4NY.json b/docs/website/video-transcripts/7XHkBMK4NY.json new file mode 100644 index 0000000000..dda5697f38 --- /dev/null +++ b/docs/website/video-transcripts/7XHkBMK4NY.json @@ -0,0 +1,8 @@ +{ + "line_count": 120, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 652, + "youtube_id": "7XHkBMK4NY" +} diff --git a/docs/website/video-transcripts/7XHkBMK4NY.txt b/docs/website/video-transcripts/7XHkBMK4NY.txt new file mode 100644 index 0000000000..eda5b0b75c --- /dev/null +++ b/docs/website/video-transcripts/7XHkBMK4NY.txt @@ -0,0 +1,120 @@ +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/video-transcripts/85MyytvMS9I.json b/docs/website/video-transcripts/85MyytvMS9I.json new file mode 100644 index 0000000000..0353748afd --- /dev/null +++ b/docs/website/video-transcripts/85MyytvMS9I.json @@ -0,0 +1,8 @@ +{ + "line_count": 226, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1234, + "youtube_id": "85MyytvMS9I" +} diff --git a/docs/website/video-transcripts/85MyytvMS9I.txt b/docs/website/video-transcripts/85MyytvMS9I.txt new file mode 100644 index 0000000000..3ee3baa1cd --- /dev/null +++ b/docs/website/video-transcripts/85MyytvMS9I.txt @@ -0,0 +1,226 @@ +we'll start with common strategies and +performance pitfalls +this list is by no means exhaustive but +it represents a lot of experience we've +had over the years +Dont block the ET +don't block the edt +this is the number one performance tip +if you have one takeaway from this it +should be this statement +the event dispatch thread is the thread +that is invoked for every listener event +or paint operation it does everything in +codename one +the edt runs a cycle that's supposed to +finish +well below the 16 millisecond mark +that's important because the edt is only +one part and after it is done the native +thread needs to perform some work as +well +if you perform lengthy operations on the +edt such as passing you might see a +performance penalty to be fair we do +these things too a lot of our demos +perform some passing and image loading +on the edt +most apis must be invoked on the edt +which makes it hard to avoid blocking it +the core workaround for this penalty is +to delegate work into another thread +we can do this with the network manager +which implicitly uses the network thread +for communication +you can also open a thread to do +intensive work and return to the edt +with a call serially invocation +a simple example of this +would be something something like this +we launch a separate thread to run slow +code and then return the response to the +edt +with qual serially +one approach to offload processing is +invoke block which spawns a thread while +blocking the edt in a non-destructive +way +this makes development very convenient +as you can avoid nested callbacks +however invoke and block is more +expensive in terms of performance than a +regular thread and callback approach +so if you are concerned about +performance invoking block might not be +the best option +these concerns should also apply to the +add to cue and weight method from the +network manager which would also incur a +similar cost +one of the tricks i did +in the uber application for features +such as +shadows +was leverage the core serially on idle +method +this method works like call serially but +it invokes the code only when the edt is +idle +idle state happens when the edt is ready +to sleep +and doesn't have too much to do +initially i rendered the component +without a shadow +and added the shadow after the fact +using a call like this +Thread safety +threads aren't magic +this is somewhat contradictory to the +recent statements but threads aren't +enough +developers often perform a slow +operation on a thread only to discover +that rendering is seriously impacted +mobile cpus aren't as fast as people +think +and +they are bad at task switching +if you spawn a thread that takes too +long and uses a lot of cpu resources it +will starve the edt +and potentially cause rendering to be +sluggish as a result +in fact we force the edt to sleep with +every cycle +so it won't starve the native rendering +thread +the solution is to explicitly yield the +thread occasionally +if you don't do i o +notice that if you are reading or +writing to a stream +the underlying kernel stream reading +code should yield the cpu for you +assuming you have a long calculation +you can just +sprinkle util dot slip sleep one or even +longer through the thread code to make +sure the cpu delegates time to other +threads +while we are on the subject i'd like to +say a couple of things about thread +safety +creating threads is important but +performance synchronization is often +challenging and error prone +it might negatively impact the +performance benefit delivered +from threads to begin with +one of the advantages of the single +thread approach in the edt is that we +can avoid synchronization almost +entirely within the codename one code +this reduces overhead noticeably as +synchronization is a very expensive +operation +that can be optimized by a compiler +and even impacts jets +if you need to use a thread i would +recommend the easy thread class which +simplifies the process of communicating +with the edt +in a way similar to call serially +caching is at the core of almost every +optimization you will make +caching can be as simple as keeping a +value in a variable or as complex as +weak references with purge strategies +a core decision about caching +is where to cache and what to cache +let's say i have this code +should we cache the image or should we +cache the label +there is no simple answer +normally i wouldn't cache the label +preferring to cache the form +but the logic of image caching is tricky +if i keep an image and then create a +scaled version of that image i'll +effectively have two instances of that +image in ram +the only tip i can give about this is +awareness +look at the things you cache and try to +avoid caching in the wrong place +notice that a two gigabyte ram device +doesn't allocate that ram to the +application +the application gets far less and this +should be further restricted as device +os's kill applications that take up too +much ram +Reuse +that brings up a common question +should we reuse form instances +in the past we used to recommend that +developers discard form instances +once they were done with them +then if they needed to go back to the +form they could create a new instance +this was the strategy and the old call +name one gui builder and it has +advantages in terms of ram usage +however +it makes some things very hard to code +going back to the exact previous state +of the form +and it is arguably slower in terms of +performance +today for the most part we +advocate the use the use of form +instances +but not dialogues which are more +transient by nature +the logic behind reuse relates to the +amount of ram available on the devices +and taking taken up by a form +it should be okay for the most part the +problems start with forms that contain +many images +in those cases we need a strategy to +purge memory when leaving the form +there are some elements that might be +expensive to cache such as images +in these cases we want to cache the data +for the performance benefit but if we +don't have enough ram +we'd rather discard it +this is in fact the strategy taken by +encoded image which i'll discuss later +javasc has built-in classes that cover a +lot of the details +such as weak reference soft reference +etc +unfortunately soft references which are +the most useful option aren't available +the solution is available in the cn +class +javasc also offers +weak hash map which we refactored into +com codename one dot ui dot util +it uses the soft reference +references built +into codename one +this class is essentially a regular map +that can lose its value objects when +memory gets low +Cache +another common class for caching is the +cache map unlike weak hashmap it caches +data and saves extra data to storage +so it's meant to cache data that you +might fetch from the network or a +similar source it makes sense for larger +elements +for small cached data +i would just use the preferences diff --git a/docs/website/video-transcripts/8WER-8R0WqA.json b/docs/website/video-transcripts/8WER-8R0WqA.json new file mode 100644 index 0000000000..dcfee82a9a --- /dev/null +++ b/docs/website/video-transcripts/8WER-8R0WqA.json @@ -0,0 +1,8 @@ +{ + "line_count": 126, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 755, + "youtube_id": "8WER-8R0WqA" +} diff --git a/docs/website/video-transcripts/8WER-8R0WqA.txt b/docs/website/video-transcripts/8WER-8R0WqA.txt new file mode 100644 index 0000000000..6e8e295d1b --- /dev/null +++ b/docs/website/video-transcripts/8WER-8R0WqA.txt @@ -0,0 +1,126 @@ +the sensible place to start is with the +login and sign up processes +and the way they tie to the server +the login change is trivial +we need to replace the action listener +of the login button with this new one +we use the user object as a data +transfer object +we set the password and phone slash +email to send them to the server +the infinite progress ui is a bit of a +shortcut but for a feature like login it +makes more sense as we can't avoid a +delay +a successful login shows the main ui +whereas an error just shows the error +message +both dispose the infinite progress +dialog +that's pretty simple and should work +right away if we had a user object in +place +to add a new user we need to overall the +signup form +as you may recall the signup form +has a series of methods to create the +wizard flow ui +this series of methods should generate a +user object that we can use for sign up +the first in the series +is the create terms method since that +method doesn't include any data there is +no need for a user object there +the second method is create name +and there we already have some work +since this is the first stage in the +wizard we create the user and binding +instances +binding means changes to the ui +instantly change this field and vice +versa +that's very convenient +the create birthday method now accepts +the user and binding objects so we can +fill them up with further details +that's simple enough +the binding implicitly updates the user +object with changes from the ui +next we can proceed to the changes in +the birthday method of pre obviously the +first change in the method is the +signature which now accepts the user and +binding +other than the method signature we added +this line to the bind +to bind the date picker to the birthday +and we added the argument +arguments to the gender method two +this leads us to the changes in the +create gender method +since most of the changes are repetitive +i'll skip the method signature and the +signature of the follow-up create number +method suffice to say we pass the user +and binding onward +this is the binding code for the gender +that automatically sets male female for +the gender attribute based on the radio +button selection it's a special case +because there are buttons +these are buttons so we need to provide +the values that match each button +the create number changes are a bit +different because of the symbiotic +relationship to the create email method +i'll list more of the code but i'll skip +the create email method as the changes +there are obvious +the change itself +is in +create the create mobile or email method +which is common to create number and +create +email this is the only change in this +method +it's right under the declaration of text +entry but in effect it can be anywhere +in the method +we bind the property to the text +component both of which are passed as +arguments to this method +the final piece of the wizard is the +create password method which follows up +with a code change that's pretty similar +to everything we saw so far +at least initially +the binding call is pretty standard as +we had up until now +we launch a progress dialog that runs +while we wait for sign up to complete +if signup was successful +we go to the confirmation ui and wait +for that +that effectively creates a user on the +server and triggered an email or sms to +us we can now check the confirmation +value to see if the user is indeed +valid +we do that by adding this code to the +create confirmation method +if verification succeeded +we show the main ui +otherwise we show an error message and +remain in the current form +we still have one more change to make to +the ui controller class +up until now we always started with the +login form but we should only start +there when we are logged out we can just +change the call to show main ui to this +if we aren't logged in correct correctly +we behave as before after the splash +screen however if we are logged in we go +to the main ui +with that a new user is in place and +should allow us to log in to the app diff --git a/docs/website/video-transcripts/8fxZVc1hw6Q.json b/docs/website/video-transcripts/8fxZVc1hw6Q.json new file mode 100644 index 0000000000..f832727837 --- /dev/null +++ b/docs/website/video-transcripts/8fxZVc1hw6Q.json @@ -0,0 +1,8 @@ +{ + "line_count": 343, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 5031, + "youtube_id": "8fxZVc1hw6Q" +} diff --git a/docs/website/video-transcripts/8fxZVc1hw6Q.txt b/docs/website/video-transcripts/8fxZVc1hw6Q.txt new file mode 100644 index 0000000000..1e9ef63511 --- /dev/null +++ b/docs/website/video-transcripts/8fxZVc1hw6Q.txt @@ -0,0 +1,343 @@ +hey everyone today we will look at the anatomy of a code name one application +now what does that actually mean well a codename one application has +lots of files and directories and some conventions and some weird things and +some of these applications look different if you're creating an application eclipse they look a bit +different from the same application and netbeans and the same goes to idea +although idea netbeans are far more similar in terms of the way the plugin structures the +application there but either way there are some files that +aren't aren't very clear for even people who have developed and +codenamed one for quite a while don't necessarily know and understand the underlying +reason for some files that exist in the project and here i'd like to give you +both a bird's-eye view in terms of how you can use it but also how it works +underneath because i think that helps understand uh by pulling away the sort of the veil +that the magician uses in this case codename one sort of magically builds an application the +moment i expose how the cog wheels work that might help you understand better +how to use it and how to manipulate it in the best way possible +so before we begin it's crucial that you have some basic +code name one experience which means basically just creating and running a hello world application +obviously installing codename one because the hello world video and the +website is so good i'm not going to try to replace it uh +so please go there install codename one on whichever id you prefer +and then we'll proceed from that point where i assume you ran your hello world +application so this is uh an aptly named high cn1 +world application and i did the most basic bare bones +native theme application but it would work pretty much any application when you create it it looks +like this i did build the application so it will be consistent between the ids because +eclipse builds applications automatically it had the build in this directory and +neither netbeans nor idea had uh these directories and i wanted +everything to look as similar as possible in the case of netbeans the default view +hides some of the actual files that exist within the ide +so i'm shifted to the files mode if you you're just learning that ide and chose +to start using it then you're by default in the projects mode +and some of the files that you see here are hidden so if you select files from +the view option you'll see something that's more similar to to this view and actually as you can +see all of the ids are very very similar they do different some things +you'll notice eclipse has a lot of additional files and those are mostly configuration files they're not that +important well not for us at least if +if you use eclipse and you want to manipulate them be wary of that because we depend on +things like the class path to remain consistent and we depend on all sorts of configurations like that so i'd suggest +not manipulating them when possible the same is true by the way for netbeans +an idea to one degree or another you'll notice that the nb project exists +both in uh netbeans and in idea and that is because +the idea plug-in that we wrote we tried to make it as close as possible to the netbeans projects so it can literally +open a netbeans project and that's very helpful for for us at least when we generate uh +demos and things like that it really helps +it does have an iml file which is again a unique +project file for idea as well as the idea directory both of which contain project internal files +that don't really matter but you'll notice that there is far more similar between +all three ides then there is a difference and that's because it's code name one throughout +and the plugins are generally as i said in the previous presentation very a very thin layer +so two of the directories that i'll start with which are probably the easiest to just kick off the bat or the +build in this directory their temporary directories that you can literally delete you can just go to +these directories and delete them and they'll be recreated and rebuilt without a problem +uh they the build directory is a temporary directory that contains the class files +that get compiled and all sorts of temporary files that we need for the build process +so it's interesting to look in that and see what we build and how we build it +but it's not very useful for after the build because we don't use it once the build is finished +that this directory contains the final jar file and that is used in some ids for the +simulator in the case of netbeans the simulator uses it in cases of eclipse and +idea it's not used as much in the simulator the simulator behaves a bit differently +there because the build process for debug and run doesn't go through the ant +xml file and in those ides but it does for netbeans +so but building for the cloud always goes through this process so that this +directory will include the jar file that we literally send to the cloud for the build process +and a lot of times this information is really useful so one of the things that we do when +people run into problems and they say oh there is this file that's uh or the size of my application is too +big or there's there's this file that's causing problems with the build process +well one of the first steps is to look in the disk directory after you've sent the build +and unzip the this jar file and looking inside it looking +at what was actually sent to the cloud and you can see the files that literally +reach our server uh and inspect them so if there's uh the jar size is huge when you +send it the resulting application will be huge i recently got a +support query from a user who said my app is 70 megabytes when i build it for +ios well his jar file was pretty big to begin with and +arm code is much larger than bart code and by a lot +and it all adds up and that's why i reached that +and one of the basic tips is to look inside the this direction you look at the jar +and see what is causing the big size issue +so the next file that's important is the codename one build client dot jar +and that's practically the first thing that we wrote in codename one and you'll notice the wrong spelling in the +in the file name might not be initially obvious but n is capitalized which is something that we +don't do because codename is one word and not two words but when we started it wasn't clear for to us either so +it was capitalized and we sort of didn't notice and that's it it's gone because it's people +use it in a case sensitive platforms and you can't change case uh +in a compatible way so we're stuck with that forever uh this file is essentially codename one uh +in a nutshell it's the file that sends the jars to the cloud it does a lot of +other things so for instance unit testing so it generates the sort of +uh text file that we need internally to run the right unit tests and that mode and +it uh generates the agree builder code the source code for the new gui builder +it's all sort of uh jack of all trades but generally it's the thing that sends the jar to the cloud build servers and +it does that through and tasks and i'll talk about them in a later slide +javascript jar is really the the thing that you work with most +without really realizing it so this is the simulator literally it's a port of codename one two swing to +java sc and it has some java effects in it that we added to support things like a web +browser and media and things like that that just aren't available in plain java +and that is a double edged sword in some regards because javafx is so flaky +but we don't really have much of a choice if we want to simulate uh complex things like web browsers and media +so this essentially uh includes the skin functionality but not the skins +themselves only the default iphone skin and it literally allows you to +to run the simulator and all the simulator menus and all the test recorder and everything all of that is +packaged into that one single file and you can test that by +literally running this line of code +right here notice that it's important to do that from the project working directory +because it will look for the codename one settings properties and and that and that +uh the main class name from there in order +to actually launch the simulator and it's essentially +everything that you need is in there and by the way it's a fat jar so it includes everything that's necessary you can take +that jar as is and one of the things that we sometimes uh +mention to people is that you can literally take that jar and package your own application +uh without using the desktop build and things like that because technically the +desktop build is relatively simple it's just that jar with some configuration and a special main file to configure it +correctly and you can get +codename one working on the desktop with very little effort if you look at the source code a bit and +go through it so and here's one of the more interesting files that we have here +it's the kernel one settings and the codename one settings properties file +is a standard properties file now you can edit it directly +sometimes it's a bit painful so normally you should open the codename1 +settings application and the right-click menu and use that almost all of the functionality in the +codename1 settings properties file is mapped into this application sometimes not in the most intuitive way +but usually in a more correct way however even if you use that application for +everything which i would recommend because it saves you from a lot of error-prone mistakes like +escaping incorrectly and things like of that type of that nature +it's still recommended that you look within the file occasionally to verify that the +settings that you've uh applied are indeed there and it sometimes gives you +a better bird's-eye view of everything because the code m1 settings application sometimes hides +information that might be relevant for ease of use +but you might want the ability to search within the entire configuration of your +application and that's where looking at the property file itself is useful +now i've snipped it here a bit because i don't want to go into all of +the attributes that we support because there is a lot of them but you get the general drift uh that we +overused the codename one prefix for one +but another valuable thing here is that we +we can configure literally everything from here and that includes the signing details +and you'll notice the app id entry at the bottom that represents how to sign +the specific application obviously package name main class name +or display name everything is configured within here one thing that's also very important is +the codenameone.org value everything that's prefixed with codenameone.org is a build hint +now this is sometimes confusing to new codename one users because the first thing you think is isn't +everything a build hint well not exactly most of these settings are +either hard coded or not even relevant to the server but rather than locally in +the client or in one way or another so a build hint +is some something that we mostly ignore in the client and then provide +the means to send it directly all the way to the build server not just +it sort of passes through the cloud server and goes directly to the build server that can query for that build +hint so obviously you can't make up any built-in willy-nilly +but the thing is that we as current one can expose one for you +relatively easily that means that when we build a feature into codename one so for +instance we want to expose a new capability and the android port that's very specific to +android and that the specific customer asked us for and we don't want to expose it to everyone +so we can say to that customer oh you add that build hint and type it like that +and within the android build code we can query for that build hint and do +that thing only for that particular customer without saying oh this is for customer x +we literally code everything to the build hints and that's really really useful +for us and because we it allows us to provide new features in a very agar way +and change code name one itself really quickly without impacting +everyone and also offering a lot of choice so a few we can just push out a feature +and someone can use it or not use it uh accordingly and we don't need to +get into the entire process in the middle we don't need to build ui into the codename one settings we don't need +to do a lot of things to expose functionality like that so it's a very valuable feature +now the next file that's really important is the build xml which is +essentially the thing that operates everything and i wanted to start with the +the file i mentioned earlier the codename one build client jar and you'll notice it featuring prominently here i've snipped +a lot of the code there are several task defs related to that and the whole build +for ios device target is much larger i just cut out lines +completely to focus on these specific lines so you'll get a sense of it but if +you want to get to really dig into it open the build xml of any project uh +code in one project that you have and look for these lines and you'll see the rest of them because there's quite a few +so at first you can see a task dev which essentially imports the jar and says +okay i want to use this specific class to implement that tag +and later on if you look at the actual target for build for ios device which happens when you right click the project +and say build for ios device naturally +in that case we use that specific tag +and we can actually uh +it actually calls the class from within the codename one build client jar +and this is literally the code that sends the jar to the cloud to our build server +and passes in all of the various uh values there's lots of other values i'll just snip them out +for simplicity but you will notice the package name main name and the target type obviously are all passed +in and this is very valuable because you'll also notice that the target definition +has lots of dependencies like copying ios override and all sorts of other things like that that we +do as part of the build process to make it simpler now you +can hack into that by adding things to be done before or after a +build is sent and that's a very powerful thing to do you can literally automate everything +within the build xml to a very high level and uh +it's very very useful and we do that a lot for things like uh continuous +integration where we can send the synchronous build and get the result back +and that allows us to automate some tasks very beautifully +uh not sure i have time to talk about that but it's it's very helpful +so one of the things about the build xml is that +we sometimes need to update it so a good example is when we came up +with the new uwp port of codename one for the universal windows platform and we didn't want to +replace the windows build because that was still supported so we needed to create a new build target +and that meant a new entry with the target like that and the build xml +so how do we provide that well we needed to +replace the existing xml file so if you look at the top of the xml file we'll see there's a version number there +and our codename one settings application and the system preferences for from the +id check that version number and check that it's up to date and if it's not up to date they will +literally show you a dialog that says you want to update the build xml and if you say yes it will override and +replace that file including any change that you might have made to that build xml file +and that that's what might not be desired and that's why we ask but obviously one of the side effects of +that is that sometimes people aren't aware of that so for instance when we came up with the uwp +port people tried to send the build and it failed and the reason it failed was because the +build xml was out of date so it's an important thing a distinction to +understand i'll talk about uh the other uh distinctions and update +later on another thing that's important to understand is that eclipse build xml +is very different from the netbeans and intellij build xml +and the reasons for that are due to the big different conceptual differences +between eclipse and any other java eye major java ide +and there isn't all that much we can do about it uh +there's just if you look at the build xml you'll still see the things that i mentioned are in eclipse as well so +these portions are identical between all ides but if you take the build xml from +netbeans try or from netbeans project and try to stick it into eclipse uh +it won't look pretty it's it's a very different system of uh build +there so but nb project uh is common between +netbeans and idea and it is used in uh in netbeans internally because the ide +obviously uses it for uh supplementary xml files and +preferences and things like that the thing is that it's also used in intellij because we try to keep the +build as close as possible so this is sometimes an intuitive when people tried to hack the internals of +the project they found out that they needed to edit values also within the nb project file +which wasn't intuitive for people working an idea as they assumed it was something special +remnant that wasn't important but it is +the next uh thing on the agenda is the lib directory +now the lib directory serves uh two slightly different purposes +uh but they're in and still in the same category of libraries so the first purpose is that it hosts +our built-in libraries that aren't javascript jar +in retrospect javascript should have been in the libs directory we just didn't do that for some reason +but uh well we're stuck with it forever now and uh +the lib directory includes the other jars the javascript is the one that we used +we use to run things the libraries here are used only to compile so in that sense essentially +the lib is the these three jars two jars in zip +sorry uh are really uh just during the compilation time +and the javascript jar is just used during runtime and in a sense they're sort of the +opposites of one another cldc11 is +well it was a standard used during the j2m era and when we started we limited the api +to to essentially that and as we moved forward we started +supporting java 5 and then java 8 and the problem is that we named that jar in +a particular way and when i reached the update process it might be clearer why +we can't change it and we're sort of stuck with that specific name +so one thing that lesson learned here is never give +files uh names that rely rely on a specific +version of something it would have been much nicer if would have called it jpi or something like +that it would have worked nicely but we're stuck with that name +this file only contains stubs it doesn't contain any code so sometimes people use +the ide option to go to code and they open a java native code a java +api code and they see stubs nothing else and +they say wait is codename one even implemented even string doesn't have any methods in it +well yeah it's implemented this is just for compilation it doesn't include any +of the code because we don't need it for compilation the implementation is completely different for every os so +obviously we we don't need to distribute that implementation +and and obviously there's complexities involved so we prefer just having stubs without any logic +the codename one jar is just the jar from the kernel one project it again allows us to compile it does include the +code mostly because we're moving it it's more is more of a pain than just keeping it in and the last one is the codename one src +which might not seem necessary but it is there to provide your code completion +with javadocs otherwise when you press the dot key and want to get completion +on a codename one application you will see just the the name of the api without the javadoc +description and by having the source and having it in the class path the ides know that when +you click component and click and try to create an instance of +component they will show you the constructors and also show you the documentation for each constructor +and that's really important for usability and that's why we have it +here the other uh use of the lib directory is +for cn1 libs which are codename one extensions that you can either install via +by downloading them and placing them within the lib directory or you can use the extension manager to +install them now one of the things that you do is you right click the +project and say refresh refresh libs and it refreshes um +and it essentially installs the cn1 libs that you have within the lib directory +the way that this works is through the build xml the build xml has a refresh +libs target and that uses the ant task to essentially +uh again codename one uh that kind of one jar and uh +turn in one build client jar so to avoid the confusion it uses that to unzip the +the cn1 lib and places it into the impul directory that you see in front of you +right now you'll notice the ample directory is effectively empty but when you when it's uh cn1 lib is +unzipped it's unzipped into these directories which include all of the classes within the cn1 lib and the +native code and the stubs which are source code stubs +which is a bit of a complexity here that also again allows code completions uh +with javadoc for four cn1 libs which is something that jars never supported natively which kind +of sucked so so with cn1 lips that actually works +normally you don't really need to know about this import portion but it's +useful because sometimes you install a cn1 lib and it doesn't work and you want to look wait where is the +code for that where is the at least the byte code for that where where is it so it's here it's under the impul directory +you should be able to literally see it after you do the refresh libs unless the c1 lib is corrupted all cn1 +libs are just zip files so you can unzip them and see that they're not corrupted and that they contain everything that +you need so that's again another useful thing +last but not least native directory now i'm going to skip that i reach this and say i'm going to skip +that because we'll talk about native interfaces later on and there's a lot to +talk about uh it isn't interesting unless we talk about native interfaces it's just +directory you can literally ignore without talking about native interfaces +so i'm going to take a rain check on that and that brings us to the final uh point +and that's the update process this is one of the most complex +issues uh that we sometimes face and even a long +time kerning one developers are still confused by +the the process that we have in place here mostly because +we have all of these different moving parts and each one of them updates separately +now i won't go into server updates and the other complexities which i sometimes do because there's +a lot more than these three but basically i'm only talking about the current +project so the current project can be affected by three update processes there are more +so the three are the build xml which we've already talked about so we're covered there the build xml updates to +the current one settings the plugin update and that's when you update the ide plugin and that has some +implications and the libs update so let's start with the plugin update +you can update the plugin through the ide every one of the ids that we support +has its own process for updating the plugin we don't issue plugin updates very often +because it requires more work and it's not as necessary +for most things we issue a plugin update when we need to update the gui builder or the designer or other pieces like +that we don't need to update these with all with regular consistency because +they're mostly stable uh one file is impacted by a plug-in update +and that's the codename one buildclient.jar now again that's also a very stable +piece of codename one it's core to codename one and its functionality doesn't change that much +recently we added to it support for the gui builder code generation +so things like that sometimes require more frequent updates and they require +that you go into codename one settings or or do something in the project but normally it just +works this update is seamless it doesn't prompt you for anything it doesn't ask it just updates the current one build +client because it's such a low level implementation detail that asking for or notifying you of that +update is sometimes would probably be more confusing than +just doing the update so that's one update +the build xml update we've discussed it's through the kernel one settings +we're good there and the final the third type of update is the libs update +now that updates the four uh libraries that we've discussed the java +c jar the chromium one jar cld c11 jar and the kernel one src jar +and usually when we do when we announce an update every friday +that's the piece we're updating because that's the most important thing that's the api that's what you're using +that's uh the simulator that's literally all the pieces that you feel as codename +one and these are updated on a very regular basis once a week +maybe less during the boot camp but generally once a week we produce an update for that +you can trigger an update manually to the latest version by going to codename one settings +basic select update client libs the button on the bottom there and it will fetch the latest version if there's a +newer one available but it also happens implicitly when you send the build to the cloud so when you +send the bill to the cloud the cloud might say wait wait you have an older version of the libraries +better update and that also happens implicitly and fetches the latest version +there is also a special case of sending a version build a version build allows +you to build to a specific version of codename one so for instance if i want +to maintain compatibility to version 3.6 of codename one i can select that +and just say build for 3.6 and it will build for that specific version and +that's it and uh +that when i select the version 3.6 it will fetch the +jars the the versions of these libraries that was the right one during 3.6 and +that will allow me to simulate exactly as it would be after the build +and the server as it was back then essentially +so that's that brings us to the end of the session +and i hope uh i didn't lose you along the way because this is a bit +uh technical nitty-gritty and it's not as sexy as some of the other subjects that +we might be talking about but i think it's important to understand and it will +help you when you want when you start looking at the more interesting things down the road +so i hope you understand better how things work and got a bit of a glimpse behind +the curtain at how these files look and how they are +relevant and if you have any questions i'm always here as usual +and thank you so much for listening all the way through diff --git a/docs/website/video-transcripts/8wzBpEp81Kc.json b/docs/website/video-transcripts/8wzBpEp81Kc.json new file mode 100644 index 0000000000..8a74a14538 --- /dev/null +++ b/docs/website/video-transcripts/8wzBpEp81Kc.json @@ -0,0 +1,9 @@ +{ + "line_count": 45, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-use-push-notification-send-server-push-messages.md", + "status": "transcript-fetched", + "word_count": 1770, + "youtube_id": "8wzBpEp81Kc" +} diff --git a/docs/website/video-transcripts/8wzBpEp81Kc.txt b/docs/website/video-transcripts/8wzBpEp81Kc.txt new file mode 100644 index 0000000000..733ce742e8 --- /dev/null +++ b/docs/website/video-transcripts/8wzBpEp81Kc.txt @@ -0,0 +1,81 @@ +_Transcript source: embedded._ + +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. + +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. + +Push is initiated by your server to notify a device that something happened + +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 + +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 + +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 + +As I mentioned before each OS implements push differently + +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 diff --git a/docs/website/video-transcripts/91BrBoia4nM.json b/docs/website/video-transcripts/91BrBoia4nM.json new file mode 100644 index 0000000000..73e83cb078 --- /dev/null +++ b/docs/website/video-transcripts/91BrBoia4nM.json @@ -0,0 +1,8 @@ +{ + "line_count": 161, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 885, + "youtube_id": "91BrBoia4nM" +} diff --git a/docs/website/video-transcripts/91BrBoia4nM.txt b/docs/website/video-transcripts/91BrBoia4nM.txt new file mode 100644 index 0000000000..5f8c443c78 --- /dev/null +++ b/docs/website/video-transcripts/91BrBoia4nM.txt @@ -0,0 +1,161 @@ +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/video-transcripts/99DAeP9LG6c.json b/docs/website/video-transcripts/99DAeP9LG6c.json new file mode 100644 index 0000000000..59337c535e --- /dev/null +++ b/docs/website/video-transcripts/99DAeP9LG6c.json @@ -0,0 +1,9 @@ +{ + "line_count": 25, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-create-gorgeous-sidemenu.md", + "status": "transcript-fetched", + "word_count": 748, + "youtube_id": "99DAeP9LG6c" +} diff --git a/docs/website/video-transcripts/99DAeP9LG6c.txt b/docs/website/video-transcripts/99DAeP9LG6c.txt new file mode 100644 index 0000000000..e72049d526 --- /dev/null +++ b/docs/website/video-transcripts/99DAeP9LG6c.txt @@ -0,0 +1,44 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +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. + +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. + +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. diff --git a/docs/website/video-transcripts/9T8MBBuWBDs.json b/docs/website/video-transcripts/9T8MBBuWBDs.json new file mode 100644 index 0000000000..f19baa9372 --- /dev/null +++ b/docs/website/video-transcripts/9T8MBBuWBDs.json @@ -0,0 +1,8 @@ +{ + "line_count": 110, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 617, + "youtube_id": "9T8MBBuWBDs" +} diff --git a/docs/website/video-transcripts/9T8MBBuWBDs.txt b/docs/website/video-transcripts/9T8MBBuWBDs.txt new file mode 100644 index 0000000000..ecd53a7382 --- /dev/null +++ b/docs/website/video-transcripts/9T8MBBuWBDs.txt @@ -0,0 +1,110 @@ +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/video-transcripts/A72rY4rU7E0.json b/docs/website/video-transcripts/A72rY4rU7E0.json new file mode 100644 index 0000000000..03073a13d5 --- /dev/null +++ b/docs/website/video-transcripts/A72rY4rU7E0.json @@ -0,0 +1,8 @@ +{ + "line_count": 215, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1165, + "youtube_id": "A72rY4rU7E0" +} diff --git a/docs/website/video-transcripts/A72rY4rU7E0.txt b/docs/website/video-transcripts/A72rY4rU7E0.txt new file mode 100644 index 0000000000..52af8437c9 --- /dev/null +++ b/docs/website/video-transcripts/A72rY4rU7E0.txt @@ -0,0 +1,215 @@ +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/video-transcripts/ACsZ8qiwR8Q.json b/docs/website/video-transcripts/ACsZ8qiwR8Q.json new file mode 100644 index 0000000000..f9c33e0c61 --- /dev/null +++ b/docs/website/video-transcripts/ACsZ8qiwR8Q.json @@ -0,0 +1,9 @@ +{ + "line_count": 42, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-create-a-9-piece-image-border.md", + "status": "transcript-fetched", + "word_count": 1876, + "youtube_id": "ACsZ8qiwR8Q" +} diff --git a/docs/website/video-transcripts/ACsZ8qiwR8Q.txt b/docs/website/video-transcripts/ACsZ8qiwR8Q.txt new file mode 100644 index 0000000000..88fe4c862b --- /dev/null +++ b/docs/website/video-transcripts/ACsZ8qiwR8Q.txt @@ -0,0 +1,76 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +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. + +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. + +A round border can be either a circle like a floating action button in Android or a pill style border like an iOS hint. + +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. diff --git a/docs/website/video-transcripts/AFvuY7Ev-XA.json b/docs/website/video-transcripts/AFvuY7Ev-XA.json new file mode 100644 index 0000000000..8046eed7a9 --- /dev/null +++ b/docs/website/video-transcripts/AFvuY7Ev-XA.json @@ -0,0 +1,8 @@ +{ + "line_count": 66, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 379, + "youtube_id": "AFvuY7Ev-XA" +} diff --git a/docs/website/video-transcripts/AFvuY7Ev-XA.txt b/docs/website/video-transcripts/AFvuY7Ev-XA.txt new file mode 100644 index 0000000000..89217f67b6 --- /dev/null +++ b/docs/website/video-transcripts/AFvuY7Ev-XA.txt @@ -0,0 +1,66 @@ +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/video-transcripts/AIsD5MXEK-c.json b/docs/website/video-transcripts/AIsD5MXEK-c.json new file mode 100644 index 0000000000..a84de0dbb8 --- /dev/null +++ b/docs/website/video-transcripts/AIsD5MXEK-c.json @@ -0,0 +1,8 @@ +{ + "line_count": 80, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 443, + "youtube_id": "AIsD5MXEK-c" +} diff --git a/docs/website/video-transcripts/AIsD5MXEK-c.txt b/docs/website/video-transcripts/AIsD5MXEK-c.txt new file mode 100644 index 0000000000..dd86e80f1c --- /dev/null +++ b/docs/website/video-transcripts/AIsD5MXEK-c.txt @@ -0,0 +1,80 @@ +mapping the news feed is pretty simple +now that we have a functional server api +first we need a new utility method for +the ui utils class +this is mostly boilerplate code notice +that a lot of the java developers +would do something like component +2 array +but that's problematic as it requires +reflection to detect the generic type of +the list +and allocate an array of that specific +type codename one doesn't support that +as it would include runtime overhead +Generic Code +once this is out of the way +the generic code in the newsfeed +container class +is simpler +we no longer need the last time variable +as the code works with standard paging +now +this is a special case for +the edge of the data where the number of +entries is smaller than a full page size +we request the page data using +a synchronous api call +this specific api call uses invoke and +block internally and thus safe on the +edt +we build +components for the responses and then +convert them to an array as required by +the api that's pretty simple and will +automatically fetch additional data as +we scroll down in the ui +Post Form +so the big missing piece is that actual +content that fits into that feed +to do that we need to make some changes +to the new post form +first i extracted two of the local +fields into class level variables that i +can use later +we can then change the event handling +code to invoke the post method instead +of the blank block we had +Post Object +after that all the logic is in the post +method itself +we create a post object on the fly right +now all posts are public as i don't want +to get into friends only ui etc +if the post succeeded we can go back to +the previous form +notice that you will need to refresh +after adding a post right now this is +something we'll resolve when integrating +push later +with that we can post and see the +entries appear in the news +feed because of the way login works our +User Object +user object +stored in server api dot me will include +the friend requests and people we should +know +this should work out of the box and +suggest friend requests whenever we log +in +one missing piece is refresh +since we cache the user objects we might +have additional friends waiting +we can implement that in the add port +refresh core within the friends +container constructor +that updates the user object and after +that the ui is rebuilt with the new +friends +English (auto-genera diff --git a/docs/website/video-transcripts/AMlnslUn1bA.json b/docs/website/video-transcripts/AMlnslUn1bA.json new file mode 100644 index 0000000000..dd232c3fde --- /dev/null +++ b/docs/website/video-transcripts/AMlnslUn1bA.json @@ -0,0 +1,8 @@ +{ + "line_count": 78, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 414, + "youtube_id": "AMlnslUn1bA" +} diff --git a/docs/website/video-transcripts/AMlnslUn1bA.txt b/docs/website/video-transcripts/AMlnslUn1bA.txt new file mode 100644 index 0000000000..3a8d5dd270 --- /dev/null +++ b/docs/website/video-transcripts/AMlnslUn1bA.txt @@ -0,0 +1,78 @@ +in the final segment we'll review how +this was integrated into the main code +i won't review every form but i'll look +at base navigation form which hasn't +changed much and i'll review one other +form that represents the changes made +in the base navigation form not a lot +has changed besides inheriting the ui +abstraction +in the previous iterations we had this +code in the init method and now it's +moved into the init toolbar method +other than that it's pretty much the +same +the subclasses of base navigation form +are more of the same so i wanted to +focus +on one of the forms that isn't a +subclass of that +and +chose address form +it now derives from ui abstraction +directly +and as you can see +not a whole lot has changed as a result +of that +in the validation code you will notice +that instead of just +binding the submit button i invoked +the add submit button from the base +class since i don't know where the ok +button will be +instead of binding listeners i use +the on back override to implement ok +back behavior +but it works exactly the same as before +the same is true for +on ok +which also works exactly the same as +before +both are invoked because we returned +true +from has ok +back method +we also override the color theme of the +toolbar but this will only apply to the +phone ui and not for the tablet ui +i i know this seems a bit much for a +four-part module but the work we did +here was relatively simple +adapting to tablets +isn't hard work +one of the reasons it took four modules +is the fact that codename one is very +flexible +and provides mostly low a low level api +the obvious question is why not move the +abstraction layer up a notch and make +tablet support an implicit part of +codename one +that's something we're considering but +so far it was very hard to pin down the +exact api we want +in this case we were able to simplify a +lot +as we knew the application domain +however +in codename one proper +we would need a more flexible api +and an abstraction like that can lead to +replication +once we commit the ap to an api +we are stuck with it for years +or even forever +so we are rather cautious +with this api +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/B4QCJRFpG-k.json b/docs/website/video-transcripts/B4QCJRFpG-k.json new file mode 100644 index 0000000000..2a869d5386 --- /dev/null +++ b/docs/website/video-transcripts/B4QCJRFpG-k.json @@ -0,0 +1,8 @@ +{ + "line_count": 126, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 763, + "youtube_id": "B4QCJRFpG-k" +} diff --git a/docs/website/video-transcripts/B4QCJRFpG-k.txt b/docs/website/video-transcripts/B4QCJRFpG-k.txt new file mode 100644 index 0000000000..7143700a8d --- /dev/null +++ b/docs/website/video-transcripts/B4QCJRFpG-k.txt @@ -0,0 +1,126 @@ +we are now ready to implement the news +feed +the newsfeed container class is a bit +big so i'll split down the functionality +into smaller methods that build the very +various pieces of the ui +will use infinite container which lets +us scroll an infinite list of entries +and implements paul to refresh +now that we have a sense of what we are +building +let's look at the high level class +skeleton then fill in the pieces +we use the last time value +when invoking fetch timeline posts so +the latest data arrives +this method is invoked when the user +scrolls and needs more data or when pull +to refresh is used +it's hard to tell how many components +will return so we'll use a list and +convert it to an array when we are done +if this is the first entry we reset the +value of lost time before making the +request +the top is always the same component +the post bar +that's where we can add a new post +we check with the server if there are +more posts +if we reached the end null is returned +and we return that to +otherwise we loop over the responses and +create a news item for each post +we add it with a space separator +notice the usage of create news item in +the code +we update the last time value to the +last element so the next request +continues from the last place +we convert the list to an array and +return that data +we referenced create post bar and create +news item +let's look at the former first +it's a relatively simple method +there are three buttons here none of +which look like a button and each one +should take us to a different form in +the finished app +we lay out the entire title area in a +border layout this allows the right post +field to grow and occupy the available +space +next we have +the create news title method which is +very similar in some regards +we need to format the time as something +that's more human readable such as three +minutes ago +again we place the elements in a border +layout but this time we wrap them in +flow layouts within +so they vertically align properly +this is subtle and hard to notice in the +image +notice that the name jots up just a +little bit in the unaligned version but +this can be more obvious when the font +is smaller or the icon is larger +also notice the placement of the menu +button +next up is create post stats +it's again a very simple method +i broke it out just to keep the code +size smaller so we can review the blocks +more easily +the status bar appears above +the like button +it's only there if there are likes or +comments otherwise it shouldn't show +i could have made this code better so it +will write one comment and two comments +etc but i wanted to keep the code simple +with that we are ready for the final +method in this class create news item +this method assembles the post entry +from the pieces we already built +the title entry +the rich text view or +span label etc +the three like comment share buttons are +packaged in a grid so they will have the +same size and align properly +everything else is packaged together in +a +y asus container with good padding +we have quite a few uids we didn't cover +in css yet so let's go into those +the button looks like a text field when +we press it +we go directly to the post editing form +the gallery button has small font and +padding so it can fit in the same row as +the other elements +the padded container uid +is often too heavily padded for some ui +elements this uses 1.5 millimeters +instead of three millimeters in the +standard padding +we'll use this in several places +as we go on +here it doesn't matter as much as it +mostly shows an icon +the title and subtitle use regular font +which makes them stand out next to the +light font used in the rest of the app +this is literally a round blue circle +with enough padding to show the thumb +within the circle +i just need a label with a font small +enough to fit +that's all the css entries we need so we +can lean back press play in the rd and +watch the first form element in the +application come to life diff --git a/docs/website/video-transcripts/BSj3KRM5Sj0.json b/docs/website/video-transcripts/BSj3KRM5Sj0.json new file mode 100644 index 0000000000..12f821c548 --- /dev/null +++ b/docs/website/video-transcripts/BSj3KRM5Sj0.json @@ -0,0 +1,8 @@ +{ + "line_count": 75, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 425, + "youtube_id": "BSj3KRM5Sj0" +} diff --git a/docs/website/video-transcripts/BSj3KRM5Sj0.txt b/docs/website/video-transcripts/BSj3KRM5Sj0.txt new file mode 100644 index 0000000000..f8be5a241d --- /dev/null +++ b/docs/website/video-transcripts/BSj3KRM5Sj0.txt @@ -0,0 +1,75 @@ +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/video-transcripts/BSrupbUahRM.json b/docs/website/video-transcripts/BSrupbUahRM.json new file mode 100644 index 0000000000..ba326f1365 --- /dev/null +++ b/docs/website/video-transcripts/BSrupbUahRM.json @@ -0,0 +1,8 @@ +{ + "line_count": 43, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 240, + "youtube_id": "BSrupbUahRM" +} diff --git a/docs/website/video-transcripts/BSrupbUahRM.txt b/docs/website/video-transcripts/BSrupbUahRM.txt new file mode 100644 index 0000000000..67bca1b285 --- /dev/null +++ b/docs/website/video-transcripts/BSrupbUahRM.txt @@ -0,0 +1,43 @@ +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/video-transcripts/BXwh2T7wvfc.json b/docs/website/video-transcripts/BXwh2T7wvfc.json new file mode 100644 index 0000000000..4cdabcd371 --- /dev/null +++ b/docs/website/video-transcripts/BXwh2T7wvfc.json @@ -0,0 +1,8 @@ +{ + "line_count": 92, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 480, + "youtube_id": "BXwh2T7wvfc" +} diff --git a/docs/website/video-transcripts/BXwh2T7wvfc.txt b/docs/website/video-transcripts/BXwh2T7wvfc.txt new file mode 100644 index 0000000000..db129559e2 --- /dev/null +++ b/docs/website/video-transcripts/BXwh2T7wvfc.txt @@ -0,0 +1,92 @@ +in this module we'll take a psd file and +convert it into an application mockup +we will use the css plugin to implement +most of the ui +although we will mix it with some +plain theming +our goal isn't pixel perfect design +the goal is a close enough ui experience +i went a bit overboard with some of the +design decisions i took +normal code name one applications don't +need to be this complex +to achieve this level of design +Result +this is the screenshot +of the original psd design that we will +adapt +there are many things to notice here and +i'll go over some of them when we +implement them +but let's see the final result first +the screenshot on the right is taken +from my personal android device +and shows off +the mock-up running there +some differences are instantly apparent +some are intentional +and some are not +notice i chose to use material design +icons for the shopping cart menu and +search +instead of using the icons from the +design +i think +these just look better and they adapt +better to various resolutions +this is the second ui element from the +original design +on the right you can see the end result +from my device +there are many design decisions i had to +make +in order to get this right for instance +i had to make everything a shade darker +so the title will be visible +enough on a mobile screen +here is the mock-up running on my device +you will notice that it isn't functional +but can be manipulated +and the incoming transition for the +shopping cart ui +is implemented +as one would expect +Top area +let's zoom a bit to the top area +which is one of the more challenging +parts of the application +i use a layered toolbar in this form +so the background ui can be deeply +customized +the layered toolbar places the toolbar +area +above the form itself +so the form components stretch all the +way to the top +the image of the city in the background +is literally added to the parent form +as well as all the other elements you +see here +the form itself +is a border layout +and the whole top portion +is just placed in the north section +the toolbar itself floats above +includes the section +of the menu and search +i initially looked at this ui +and thought it made sense +as tabs +after reviewing the functionality it +became apparent that this is a filter +and shouldn't replace the entire ui +so +i went with a horizontal list +the separation line on which the +shopping cart and order are placed +is really just a layered layout +that hides the bottom part of an of the +image with an opaque label +that has the same color as the form i'll +discuss this further as we move +forward diff --git a/docs/website/video-transcripts/BY1lMQz873g.json b/docs/website/video-transcripts/BY1lMQz873g.json new file mode 100644 index 0000000000..d2a9de3dbd --- /dev/null +++ b/docs/website/video-transcripts/BY1lMQz873g.json @@ -0,0 +1,8 @@ +{ + "line_count": 193, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1083, + "youtube_id": "BY1lMQz873g" +} diff --git a/docs/website/video-transcripts/BY1lMQz873g.txt b/docs/website/video-transcripts/BY1lMQz873g.txt new file mode 100644 index 0000000000..2734309326 --- /dev/null +++ b/docs/website/video-transcripts/BY1lMQz873g.txt @@ -0,0 +1,193 @@ +in this section we'll talk about +profiling profiling is the process of +using specialized tools to measure the +performance bottlenecks of your +application +despite the amazing ubiquity of these +tools many developers just don't use +them +Story Time +fifteen years ago i was contracted by a +bank to help them solve performance +problem +they were having where a huge query was +taking them seven minutes to perform +they needed sub sub-second performance +for that specific query and were lost +they had a hard time getting profiling +to work so they wasted quite a bit of my +time trying to guess the reason +i wasted a bit of time in a meeting +where everyone involved raised their own +theory of why this is happening +you can probably guess the end +i spent an afternoon setting up +profiling which was hard for that huge +distributed system +once that ran i had data that showed +everyone was wrong +it pointed at a specific set of calls +that should have taken almost no time +time at all +but each was invoked thousands of times +turns out that two guys sitting at +adjoining tables had each assumed that +the other guy was caching the results of +a query +so they performed it each time for every +entry of the table +that one change brought performance from +seven minutes to seven seconds +everyone has stories like these martin +fowler mentioned how a change +in how they were getting a default +constant date in a project reduced +processing time from more than a day to +a couple of hours +profiling is crucial +Performance Monitor +the performance monitor is a simple tool +built into the simulator +that provides some insight into +performance issues you might run into +the performance monitor tool focuses on +rendering speed and won't help you with +logical performance issues +perform the performance monitor monitor +tool is launched via the simulator +performance monitor menu option +in the simulator +this launches the following ui that can +help you improve application performance +the first tab of the performance monitor +includes a table of the drawn components +each entry includes the number of times +it was drawn and the slowest fastest and +average drawing time this is useful if a +form is slow you might be able to +pinpoint it to a specific component +using this tool +the log at the bottom of the screen +includes the bug related information +for instance it warns about the usage of +mutable images which might be slow in +some platforms this also displays +warnings when an unlocked image is drawn +etc +the rendering tree view +allows us to inspect the hierarchy +painting +you can press the refresh button which +will trigger the painting of the current +form +every graphics operation is logged and +so is the stack to it you can then +inspect the hierarchy and see what was +drawn by the various components +you can click the stack button to see +the specific stack traces that led to +that specific specific drawing operation +this is a remarkably powerful debugging +tool as you can literally see overdraw +within this tool +for instance if you see full wrecked or +similar apis invoked in the parent and +then again and again in the children +this could indicate a problem +i highly recommend traversing the paint +hierarchy in this tool and trying to +explain why every graphic operation +within this tree occurred i think it +would help you understand your +application +and codename one +Performance Monitoring +performance problems are usually +portable +the problem is that because one platform +can be faster than another +we'd often perceive of them only on the +slower platform +however with profilers we can measure +performance penalties and overhead on +the desktop +we can then assume and extrapolate to +perform optimizations that make sense on +the device +there are plenty of performance +monitoring tools for java sc +and they all should work just fine with +the codename one simulator +in this case i used the netbeans +built-in performance monitoring tool on +the kitchen sink demo +the tool has two applicable modes for +this case +cpu and memory +Performance Monitoring Results +after running kitchen sync under the +netbeans performance monitoring tool i +came up with these results +this shows the threads of the +application +and as we dig into them we can see which +method takes the line share of cpu time +the hotspots mode shows the method in +which the cpu spends the most time +both are a bit misleading as they give a +lot of time to methods that use invoke +and block +another reason to be wary of this api +which is why it's always useful to +isolate the area where the performance +problem starts and try to measure only +that section with this tool +at that point you will be able to +pinpoint the suspicious methods +it's important to read the values in the +table carefully they include both cpu +time and invocations +these can expose behaviors weren't +expecting +notice that you can click any method in +the hotspots view and see the stack +traces to that method +the memory mode of the profiler is a bit +more limiting +as you can see +we can see the battery +take up most of all active memory which +typic effectively means images are +responsible for the memory of the +application +that's not exactly useful as it doesn't +disclose which image is responsible +we can still get a lot of useful +information out of this by +taking a memory snapshot and then +another one after performing an +operation +we can then check the change in memory +and determine it acted as we expected +Back of napkin math +one of the important things we need to +do when looking at a profiler is basic +back of the napkin math +the numbers we see make sense +this is true both for both memory and +cpu profiler +say your code allocates 30 images that +are 130 by 130 pixels +your memory should grow by +30 times 130 130 times 4 +which means 2 million bytes +this will mostly show up in the byte +array growth if the memory grows by a +bigger margin then you have a problem +and should try to isolate that +the same is true for method invocations +fma method takes a lot of time then look +at the elements within it +add them up if a method is invoked too +many times try to understand why and who +is invoking that method diff --git a/docs/website/video-transcripts/BbVoa3vw7OM.json b/docs/website/video-transcripts/BbVoa3vw7OM.json new file mode 100644 index 0000000000..873f116384 --- /dev/null +++ b/docs/website/video-transcripts/BbVoa3vw7OM.json @@ -0,0 +1,8 @@ +{ + "line_count": 157, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 915, + "youtube_id": "BbVoa3vw7OM" +} diff --git a/docs/website/video-transcripts/BbVoa3vw7OM.txt b/docs/website/video-transcripts/BbVoa3vw7OM.txt new file mode 100644 index 0000000000..957886c638 --- /dev/null +++ b/docs/website/video-transcripts/BbVoa3vw7OM.txt @@ -0,0 +1,157 @@ +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/video-transcripts/BwWhFcHc6LM.json b/docs/website/video-transcripts/BwWhFcHc6LM.json new file mode 100644 index 0000000000..18b0dcb192 --- /dev/null +++ b/docs/website/video-transcripts/BwWhFcHc6LM.json @@ -0,0 +1,8 @@ +{ + "line_count": 157, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 902, + "youtube_id": "BwWhFcHc6LM" +} diff --git a/docs/website/video-transcripts/BwWhFcHc6LM.txt b/docs/website/video-transcripts/BwWhFcHc6LM.txt new file mode 100644 index 0000000000..3b6788873a --- /dev/null +++ b/docs/website/video-transcripts/BwWhFcHc6LM.txt @@ -0,0 +1,157 @@ +we'll continue from the client source +part of push notification +going back to the app we can see +the main class implements push callback +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 and start methods it must be in +that class with no exception as it +represents the object life cycle which +is the underlying os concept push binds +to +we need one call to enable push and that +is the register push method +notice that this call was changed +recently and both arguments were removed +since both are no longer needed in +recent versions of the api +register push +should be invoked every time the app +runs +let's scroll down 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 +push invokes on build result method +this is important as the result call +is also invoked from the polling +fallback +when push isn't available so having +common code is valuable here +the first core +is of interest and might require some +explaining of the various types of push +messages +so let's skip for a second into a slide +to review these types +Push Types +i'll only talk about the three most +important push types but there are quite +a few other types and i recommend +checking the developer guide +when in doubt +first and most common push type is +push type zero or one +this is the push type you see visually +when an app isn't running +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 and 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 an 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 combina 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 mostly used for +signaling +type 3 is here to work around the issues +of type 2 and type 1. +with type 1 we might not have the data +we need to understand the content of +push +so for our specific case we might say +app build completed visually but we +would expect to +tap to trigger a download +and for that we would need a url +type 2 is problematic +as we do want to show something to the +user in this case +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 +let's go back to the code again +Push Code +as i said before this if statement is +interesting +the reason we have it is simple +we sent a type 3 push that includes both +a download completed message and a url +to launch +if the push message isn't +for the download url then it's just a +visual message and i don't care about it +i relied on the fact that this is a url +but it would have worked even for a json +string for example checking that the +string starts with a curly bracket +the rest of the code isn't really +related to push +it just chose a dialog to download the +finished app if necessary but it's not +about push so i'll skip it +it's relatively simple code anyway +Push Registration +these are the two other callbacks for +push registered for push 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 we can send push messages +to this device +however +in this case +we only use push when a build is sent +and by then registration either +completed or failed so we can check the +push there +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 dot get push key +the second method is invoked when there +is a registration error +notice that this +isn't always invoked and sometimes it +isn't called for a device when +where push is unavailable +another important thing to highlight is +that this is a very bad implementation +when i did this i didn't reproduce the +error +but this could trigger an error at a +relatively early point in launch and +produce a very problematic toast message +normally this should be handled later on +or logged diff --git a/docs/website/video-transcripts/C3PLjAWQ-XA.json b/docs/website/video-transcripts/C3PLjAWQ-XA.json new file mode 100644 index 0000000000..1e0b0a160b --- /dev/null +++ b/docs/website/video-transcripts/C3PLjAWQ-XA.json @@ -0,0 +1,9 @@ +{ + "line_count": 14, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-use-crash-protection-get-device-logs.md", + "status": "transcript-fetched", + "word_count": 497, + "youtube_id": "C3PLjAWQ-XA" +} diff --git a/docs/website/video-transcripts/C3PLjAWQ-XA.txt b/docs/website/video-transcripts/C3PLjAWQ-XA.txt new file mode 100644 index 0000000000..2e13cccbff --- /dev/null +++ b/docs/website/video-transcripts/C3PLjAWQ-XA.txt @@ -0,0 +1,20 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +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 + +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. + +Thanks for watching, I hope you found this helpful diff --git a/docs/website/video-transcripts/Cc8YG-_M02M.json b/docs/website/video-transcripts/Cc8YG-_M02M.json new file mode 100644 index 0000000000..ceee499fa5 --- /dev/null +++ b/docs/website/video-transcripts/Cc8YG-_M02M.json @@ -0,0 +1,8 @@ +{ + "line_count": 172, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 988, + "youtube_id": "Cc8YG-_M02M" +} diff --git a/docs/website/video-transcripts/Cc8YG-_M02M.txt b/docs/website/video-transcripts/Cc8YG-_M02M.txt new file mode 100644 index 0000000000..e7f31b3626 --- /dev/null +++ b/docs/website/video-transcripts/Cc8YG-_M02M.txt @@ -0,0 +1,172 @@ +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/video-transcripts/CvbcWC4hLYk.json b/docs/website/video-transcripts/CvbcWC4hLYk.json new file mode 100644 index 0000000000..7a3127017f --- /dev/null +++ b/docs/website/video-transcripts/CvbcWC4hLYk.json @@ -0,0 +1,8 @@ +{ + "line_count": 64, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 322, + "youtube_id": "CvbcWC4hLYk" +} diff --git a/docs/website/video-transcripts/CvbcWC4hLYk.txt b/docs/website/video-transcripts/CvbcWC4hLYk.txt new file mode 100644 index 0000000000..484c75477a --- /dev/null +++ b/docs/website/video-transcripts/CvbcWC4hLYk.txt @@ -0,0 +1,64 @@ +the next step is fixing the checkout +form +which has some issues +a huge problem in checkout is the +inability to change the order +i can't remove or change quantity +currently i can't even see the quantity +of items +i'm assuming this is a design mistake +although it's possible the designer +planned another form for editing the +order +Using Swipe +we can approach this problem in several +ways +the obvious first approach can be to use +swipe +it has a huge advantage of leaving the +design exactly as is +however +it's hard to discover that a swipe +action exists if you don't know the ui +and it might be less intuitive to those +that aren't tech savvy +it is easy to implement in codename one +with swipe container +Edit Mode Button +another approach is to provide an edit +mode button +probably on the top right +this would show modification controls +for every entry which is pretty common +approach on ios +however +we'd still need designs for everything +and we might as well show the +modification controls all the time +Quantity Button +we can include quantity +in the end of the price line +it might be hard to convey visually that +this quantity element is editable +we can use a text field design +but that might not be attractive +a delete button on the right can work +too +but might disrupt the ui +Delete Button +this is what i did eventually +i used the button design for the cart so +this should be consistent +i mixed the x with the number +so people understand this is a +multiplication +the reason for that is space if i had +the x outside of the button space ran +out +clicking the button opens the native +picker +allowing us to select the quantity or a +zero to delete +this way we don't need validation +or keyboard both of which are harder to +work with on the device diff --git a/docs/website/video-transcripts/DWifLQZyPDE.json b/docs/website/video-transcripts/DWifLQZyPDE.json new file mode 100644 index 0000000000..32c5f77865 --- /dev/null +++ b/docs/website/video-transcripts/DWifLQZyPDE.json @@ -0,0 +1,8 @@ +{ + "line_count": 362, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1983, + "youtube_id": "DWifLQZyPDE" +} diff --git a/docs/website/video-transcripts/DWifLQZyPDE.txt b/docs/website/video-transcripts/DWifLQZyPDE.txt new file mode 100644 index 0000000000..29bae4e6e9 --- /dev/null +++ b/docs/website/video-transcripts/DWifLQZyPDE.txt @@ -0,0 +1,362 @@ +in this section we'll discuss the +various types of images in codename one +and their impact on performance +codename one has a few image types +loaded rgb built in rgb codename one +mutable encoded image multi-image font +image and more +there are a few others but i won't go +into every nuance +all image types are mostly seamless to +use and will just work with draw image +and various image related apis for the +most part with caveats on performance +performance and memory wise i'll review +each type and try to explain the +trade-offs involved with the image type +you use +the codename one designer tries to +conserve memory and be clever by using +only encoded image +while these are great for low memory you +need to understand +the complexities of image locking and be +aware that you might pay a penalty if +you don't +here are the pros and cons +and logic behind every image type +this covers the logic of how it's +created +this is the basic image you get when +loading an image from the jar or network +using image dot create image string +or image create image input stream image +dot create image +battery intent etc +the image is stored in ram based on +device logic and should be reasonably +efficient in terms of drawing speed +however it usually takes up a lot of ram +to calculate the amount of ram taken by +a loaded image we use the following +formula +image width times image height times 4 +equals the size of ram and bytes +so a 50 by 100 image +will take up 50 times 100 times 4 +and that's 20 000 bytes of ram +the logic behind this is simple +every pixel contains three color +channels +and an alpha component +hence three bytes for color and one for +alpha +this isn't the case for all images +but it's very common and we prefer +calculate calculating for the worst case +scenario +even with jpegs that don't include an +alpha channel some os's might require +that additional byte +there are two types of rgb constructed +images that are very different from one +another +but since +they are both technically rgb images we +are bundling them under the same +subsection +internal is a close cousin of the loaded +image +this image is created using the method +image dot create image into array int +and receives the argb data +to form the image +it's more efficient than the codename +one rgb image +but can't be modified after it's created +at least not on the pixel level +the goal of this image type is to +provide an easy way to render static rgb +data efficiently at platform native +speeds +it's technically a loaded image +internally +rgb image is effectively an argb array +that can be drawn by codename one +on most platforms this is quite +inefficient but for some pixel level +manipulations there is just no other way +an rgb image is constructed with an end +array that includes width times height +elements +you can then modify the colors and alpha +channel directly within the array and +draw the image to any source using +standard image drawing apis +this api is very inefficient in terms of +rendering speed and memory overhead +only use this technique if there is +absolutely no other way +encoded image is the workhorse of +codename one +images returned from resource files are +encoded images +and many apis expect it +encoded image can be instantiated via +the create method in the encoded image +class +pretty much any image can be converted +into an encoded image via the create +from image boolean method +encoded image is effectively a loaded +image that is hidden and extracted as +needed +to remove the memory overhead associated +with loaded image +when creating an encoded image only the +png or jpeg +is loaded into an array and ram +normally such images are very small +relatively so they can be kept in memory +without much overhead +when image information is needed pixels +the image is decoded into ram and kept +in a weak slash soft reference +this allows the image to be cached for +performance and allows the garbage +collector to reclaim it when memory +becomes scarce +when drawing an encoded image it checks +the weak reference cache and if the +image is cached then it sure is shown +otherwise the image is loaded and cached +since the fully decoded image can be big +width times height times four +the ability to store just the encoded +image can save a lot of ram +so taking our example from before 50 by +100 image will take up 20 000 bytes of +ram for a loaded image but an encoded +image can reduce that to 1 or 2 +kilobytes of ram +notice that an encoded image might be +more expensive than a loaded image +as it will take up both the encoded size +and the loaded size +so the cost might be slightly bigger in +some cases +its main value is its ability to shrink +when it isn't used +encoded image is not final and can be +derived to produce complex image +fetching strategies +for instance the url image class can +dynamically download its content from +the web +naturally loading the image is more +expensive so we want the images that are +on the current form to remain in cache +otherwise the gc will thrash a lot +that's where lock kicks in +when lock is active we keep a hard +reference to the actual native image so +it won't get gc'd +this significantly improves performance +internally +this is invoked automatically for +background images icons etc which +results in a huge performance boost +this makes sense since these images are +currently showing +and they will be in ram anyway +however +if you use a complex renderer or custom +drawing ui you should lock your images +where possible to verify that locking +might be a problem you can launch the +performance monitor tool +accessible from the simulator menu if +you get log messages that indicate that +an unlocked image was drawn you might +have a problem +i will discuss the performance monitor +tool +later +multi images don't physically exist as a +concept within codename one +api so there is no way to actually +create them +and they are in no way distinguishable +from encoded image +the only built-in support for +multi-images is in the resource file +loading logic where multi-image is +decoded +and only the version that matches the +current dpi is physically loaded +from that point on user code can treat +it like any other encoded image +performance of multi images should be +identical to encoded image with one +major caveat +they increase the size of the resource +file +since most apps keep the resource file +in ram the usage +later +for usage later this might impact rams +hence performance negatively +workaround a workaround might be to +split some of the data into separate +resource files +that won't be cached +however in recent years we try to reduce +the usage of multi images in favor of +font icons and similar strategies +mutable images are created via image dot +create image and end or image dot create +image and int +they accept the width +height of the image and potentially the +argb background color +once created a developer can invoke get +graphics on a mutable image +and draw to its surface +the image can then be drawn onto the +screen like any other image +in terms of performance mutable images +are +the most interesting and most +problematic +features +historically when we started luit and +codename one mutable images were fast +in fact we used them to implement common +optimization technique of the day called +double buffering +in double buffering a developer draws +onto a background image then draws that +onto the screen resulting in fast +flicker free rendering +this is strongly discouraged for modern +devices as specifically android versions +newer than 2.3 and ios versions newer +than full +since both of these platforms are +effectively dead you should no longer +use mutable images for performance +we even have an api in display to detect +that +display dot get instance are mutable +images fast +this usually returns false on ios and +android +on ios creating a mutable image is even +slower and might cause noticeable +rendering delay if we are creating a +large image +to understand why we need to understand +how modern rendering works +modern devices use hardware acceleration +aggressively which means every rendering +operation is sent to the gpu the +graphics processing unit +for fast processing +the more you can throw on the gpu the +faster your processing will be +images like this are rendered in +software and not in the graph by the +graphics hardware +so the underlying native os will spend +expensive cpu time drawing graphics and +memory +and then more time copying that memory +from the cpu ram to the gpu ram to +actually draw it +you would also take up memory +specifically with times heights times 4 +amount and sometimes even more than that +mutable images are problematic but +sometimes they are the only way to +accomplish something for instance if you +want to create +and save a new image the only way to do +it is through mutable image +however there is also a performance case +to be made for mutable images +if you have code that includes a lot of +rendering operations that repeat +themselves it might make sense to pay +the penalty of a mutable image once and +then draw that +it's something we do with some special +effects which would be expensive to +render repeatedly +we can just generate a rubber stamp +once the mutable image is rendered its +strong performance is as fast as a +loaded image +even though it has noticeable ram +overhead it might still be worthwhile +for a lot of use cases if you are +willing to make the trade-off +one important +bit of information is that mutable +images aren't thread safe +this is a bit problematic but there is +no way around it +mutable image access +must be done on the edt +some optimizers have tried to use a +separate thread to render mutable images +this might work for most platforms but +it doesn't work for ios which relies on +a global drawing state +so this is one performance optimization +tip you shouldn't +use +font image allows using an icon font as +if it was an image +you can specify the character color and +size then treat the font image as if +it's a regular image +the huge benefits are that +the font image can adapt to platform +conventions in terms of color +and easily scale to adapt to the dpi +it also comes bundled with material +design icons representing common +functionality +in terms of performance fonts are +smaller than pngs and jpegs +they are much smaller than multi-image +both in ram and distribution size +rendering performance is hard to measure +and depends on a lot on the os +performance difference shouldn't be +noticeable in the rendering layer +as a result if you can use font image +then you should use font image the image +type has almost no overhead in terms of +ram which makes it ideal +some optimizations in the label +rendering pipeline might be lost when +using font image however that is an edge +case behavior and you should only deal +with that if profiling highlights this +as a performance bottleneck +it's possible to convert a font image to +image which +internally uses mutable image +or encoded image +there is +no measurable performance advantage for +doing that +the main motivation for doing this is +apis that might rely on image data +behaving in a specific way +font image doesn't implement some common +apis such as get rgb +and some low-level apis might rely on +native image drawing which won't work +with font image diff --git a/docs/website/video-transcripts/Dh1bYqiKBQo.json b/docs/website/video-transcripts/Dh1bYqiKBQo.json new file mode 100644 index 0000000000..b8522bca1c --- /dev/null +++ b/docs/website/video-transcripts/Dh1bYqiKBQo.json @@ -0,0 +1,8 @@ +{ + "line_count": 116, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 607, + "youtube_id": "Dh1bYqiKBQo" +} diff --git a/docs/website/video-transcripts/Dh1bYqiKBQo.txt b/docs/website/video-transcripts/Dh1bYqiKBQo.txt new file mode 100644 index 0000000000..5fd850a089 --- /dev/null +++ b/docs/website/video-transcripts/Dh1bYqiKBQo.txt @@ -0,0 +1,116 @@ +in this module we'll talk about the +process of adapting the application to +look good +on tablets and desktops +by default apps work as is in those form +factors but this can produce a situation +when app feels like a scaled up phone +app +The Problem +case in point if we run the current app +on a tablet it looks horrible +it just doesn't fit +the images look bad and overscaled +the side menu and elements +positioning doesn't make use of the +available space +this is how the app looks after the +changes we will review today +the icons and the layout look great +the side menu is permanent and doesn't +change as we move through the elements +the app feels more like an app that was +designed for tablets +and can work reasonably well on a +desktop +Desktop +when we switch to other entries such as +the details entry +the form doesn't change only the content +in the center +you will notice that instead of using +commands in the title area +we use large and convenient buttons at +the bottom +which is possible now since we have more +screen real estate +Form +this is the key +with a regular app every entry is a form +and with a tablet app that doesn't work +as nicely +although it is possible to do so it's +often not the best approach +usually for tablet apps +we like having one form and replacing +the content within +to achieve this we derive a common base +class instead of form +to implement that i just looked at +everything that failed when i switched +the base class +and we implemented this in an +abstraction class +the implementation for form is pretty +straightforward +as a result +UI abstraction +going to the base navigation form +the changes here are instantly familiar +we now derive from ui abstraction +instead of four +let's open up ui abstraction and see +what we have there +ui abstraction opens with an inner class +representing the form delegate +this is effectively the class that +implements the standard form-based +navigation +naturally it does derive from form +as +all the calls apply to it +so instead of inheritance we now have a +form of composition +the constructor for ui abstraction +accepts the title and layout which are +both passed onwards to the form and the +default behavior +we don't need the fourth set of form +constructors in this case +this is a special callback +that subclasses can override +since many forms have an ok cancel pair +of buttons i generally generalize that +code so if a ui abstraction subclass +overrides this method to return true +we add an ok +or cancel +this allows us to have toolbar commands +in regular forms and ok cancel buttons +in tablet mode +we implement this in the form in a very +generic way +we just add a command and implicitly +show the previous form +we have an on back callback that allows +the subclass of the ui abstraction to +intercept this behavior and bind logic +to the cancel operation +ok is identical with two small +differences +the first is the fact that on ok is +invoked instead of on back +the second is that we keep a reference +to the ok button +we need that for the validation code so +validators can still +disable the ok button +if the input is valid +scrolling down a bit +we can see several methods of form that +we overrode to provide sensible +toolbar behavior in all cases +if you look at the existing forms before +this change you will see most of the +forms already had similar code within +them diff --git a/docs/website/video-transcripts/DyzEgAGyRcA.json b/docs/website/video-transcripts/DyzEgAGyRcA.json new file mode 100644 index 0000000000..133c0a09e5 --- /dev/null +++ b/docs/website/video-transcripts/DyzEgAGyRcA.json @@ -0,0 +1,8 @@ +{ + "line_count": 84, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 424, + "youtube_id": "DyzEgAGyRcA" +} diff --git a/docs/website/video-transcripts/DyzEgAGyRcA.txt b/docs/website/video-transcripts/DyzEgAGyRcA.txt new file mode 100644 index 0000000000..eb776b2e02 --- /dev/null +++ b/docs/website/video-transcripts/DyzEgAGyRcA.txt @@ -0,0 +1,84 @@ +in this module we dig into the tablet ui +class +the tablet ui is created by the main +class of the application we initialize +it here +so when base navigation form uses +generic code +it would already be initialized we also +initialize the permanent side menu which +makes the side menu stay open all the +time +without the button +that's a ui paradigm that makes sense on +tablets and desktops +Tablet UI Class +the tablet ui class is a form that +encapsulates the full layout on a tablet +it's a singleton because we have just +one form in the application +and one ui +we use a border layout and place the +content of the ui +in the center +so we can replace it when navigating +between ui abstraction instances +Border Layout +the title and subtitle are editable here +too +this is effectively a copy of the code +from base navigation form +adapted for the tablet form factor +Navigation Form +this is also a copy from base navigation +form but thanks to the available space +we don't need to jump through hoops to +make this look good or fit +so the code here is much simpler +the logo image is now just an icon on +the side menu which makes it look far +better overall +Side Menu +you'll notice that all the side menu +entries are marked here +but i only highlight one as they are all +mostly the same since we don't derive +from the base navigation form +this code is a bit duplicate but it's +much simpler as we don't need the whole +hack with marking the selection +notice we aren't adding commands but +rather adding components using the +generic +create site navigation entry method +we'll discuss that method soon but +notice the bg argument we pass to each +one of them +that's a button group +and these methods return a radio button +this allows us to have the selection +effect +Background Image +we also add the edit background image +button +to the side +it's relatively trivial as there isn't +much we need to do here +Navigation +creates our navigation entry just +generalizes the code that creates a +radio but toggle button +it mostly saves some boilerplate and +doesn't feature much logic +Show Container +however show container is pretty +interesting +it's the method that's effectively +invoked when show or show back are +invoked +there are two modes here +for the very first screen we need to use +add +from that point on we'll always use +replace to show the next ui abstraction +instance diff --git a/docs/website/video-transcripts/EkiTDQn9Cpg.json b/docs/website/video-transcripts/EkiTDQn9Cpg.json new file mode 100644 index 0000000000..4e12b90ac4 --- /dev/null +++ b/docs/website/video-transcripts/EkiTDQn9Cpg.json @@ -0,0 +1,8 @@ +{ + "line_count": 388, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 2157, + "youtube_id": "EkiTDQn9Cpg" +} diff --git a/docs/website/video-transcripts/EkiTDQn9Cpg.txt b/docs/website/video-transcripts/EkiTDQn9Cpg.txt new file mode 100644 index 0000000000..ea20ea12ec --- /dev/null +++ b/docs/website/video-transcripts/EkiTDQn9Cpg.txt @@ -0,0 +1,388 @@ +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/video-transcripts/FKTM8jAepJs.json b/docs/website/video-transcripts/FKTM8jAepJs.json new file mode 100644 index 0000000000..e3dee882a8 --- /dev/null +++ b/docs/website/video-transcripts/FKTM8jAepJs.json @@ -0,0 +1,8 @@ +{ + "line_count": 98, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 524, + "youtube_id": "FKTM8jAepJs" +} diff --git a/docs/website/video-transcripts/FKTM8jAepJs.txt b/docs/website/video-transcripts/FKTM8jAepJs.txt new file mode 100644 index 0000000000..ddf7800ecd --- /dev/null +++ b/docs/website/video-transcripts/FKTM8jAepJs.txt @@ -0,0 +1,98 @@ +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/video-transcripts/FSek5IOQ0IE.json b/docs/website/video-transcripts/FSek5IOQ0IE.json new file mode 100644 index 0000000000..43e93d8d27 --- /dev/null +++ b/docs/website/video-transcripts/FSek5IOQ0IE.json @@ -0,0 +1,8 @@ +{ + "line_count": 245, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1239, + "youtube_id": "FSek5IOQ0IE" +} diff --git a/docs/website/video-transcripts/FSek5IOQ0IE.txt b/docs/website/video-transcripts/FSek5IOQ0IE.txt new file mode 100644 index 0000000000..0cb16601e7 --- /dev/null +++ b/docs/website/video-transcripts/FSek5IOQ0IE.txt @@ -0,0 +1,245 @@ +before i start this section +i'll start with a small disclaimer +this section was developed after the +application was almost finished +and so +the code you see attached to it +includes many additional features +that if you will look through +aren't covered in previous modules +however +it does fit in this location so i took +the liberty of sticking it in +i mentioned before push is fragile +and can be blocked by the user +in order for the app to work properly we +need to properly handle the situation +where push isn't available +and we can do that with a fallback +implementation +i already demonstrated the http polling +implementation +now i'd like to move to the better +solution which is websockets +but first it's bet if it's a better +solution +why didn't i start with that +websockets are newer +and some server developers +might +not be as comfortable with them +they are worth your time and effort +though +as they make event-based communication +trivial and efficient +http is a relatively expensive protocol +besides the cost of opening a socket we +also have the cost of the headers +this gets really bad when you go back +and forth from client to server +for many small requests +http works great when you return large +files which effectively mask the +overhead of the protocol +sockets are too low level +for the web environment +they don't work nicely with most +application servers +and it's really difficult to punch +through corporate firewalls +sockets are also very fragile +and it's really hard to deal effectively +with failure when working with them +websockets were devised as a middle of +the road solution +they start as an http or https +connection +but instead of closing the connection +the server keeps the socket open +and now the client and server can +communicate freely back and forth +this has several advantages +you can go through corporate firewalls +who are mostly oblivious to websockets +as they seem like a regular web traffic +which they are +a lot of application servers have +standardized support for websockets +since they are a w3c standard +you can easily work with your favorite +server +coding is as efficient +as regular websockets +as regular sockets once you paid +the connection overhead cost +and finally you can use web sockets from +codename one as well as javascript and +pretty much everywhere else on the +client +websockets aren't as flexible as regular +sockets since the api is a bit +simplified +you can send and receive messages either +as bytes or as strings +that's very convenient +one of the harder problems with +websockets +is +actually in the server +a lot of server apis are +very synchronous by nature +and it's sometimes hard to bind an event +from the server to a socket event +in fact that's the biggest difficulty i +had +when working with websockets +doing the right thing +in spring boot +spring boot has an optional standardized +protocol +on top of websockets +which i didn't use as i wanted something +very low level +and that seemed redundant +this +is the one problem with websockets +they are relatively new and a lot of the +best practices +haven't caught up yet +websockets support in codename one +is implemented as a cn1 lib +which you can install by launching the +codename one settings app and going to +the extensions +section when you press the download +button the library will be installed +just use refresh libs in the right click +menu +and you can proceed to use the websocket +library +the code in the code you might recall +the build app method +that builds the actual app +we now open a connection to the server +with the websocket class +the websocket class includes callbacks +representing the various states of the +web socket +the first callback is an +on open +you will notice i'm sending the secret +to the server +so we can bind to the right restaurant +initially i didn't pay attention to it +and just called the send method directly +after creating the socket object +this didn't work +but i didn't get any error either +since the socket worked works +asynchronously +it wasn't open and my call was failing +this method is crucial for the initial +handshake code +you'll notice we have two on message +methods +as the websocket api +one accepts string +and the other a byte array +this maps to the way websocket api works +where a special case is made for both of +these types +we only pass strings here +which maps +map directly to the messages we would +have gotten from the push notification +api +scrolling a bit downwards we can now see +the on error callback +it's important to override that to +handle errors +we can then see the sock.connect method +which initializes the connection +for this specific trivial usage of +websockets +this is +literally the entire client side but +even more elaborate usage +isn't much more complicated than that +websockets are just messages +we send and receive and the client side +is remarkably +simple +on the server side we need to first +create a configuration class to handle +web sockets +i defined the standard 8 kilobyte buffer +size +and add a handler for the specific +websocket url +scrolling further down +we can see the handler instance +that's created when a handler is needed +the handler class will handle a +websocket request +the hander class implements a text based +websocket which means it works with text +objects for the web socket +as you recall websockets work with +either binary data or text and in this +case we only work with strings +this is a bit of a hack +i probably could have found a better way +to implement it if i had more time +but as i mentioned before websockets are +new and some of the older apis just +don't fit very well +into the paradigm +i want +asynchronous built the asynchronous +build process to send a websocket +message +but there is no way to do that without +somehow passing +the websocket reference to the build +unfortunately the builders started via a +web service +a long term solution might be to use +only websockets for everything +but for now i kept +a static instance +this is bad practice as it won't allow +the app to scale +but i can live with this at this point +i remove the session entries as a +connection is closed +so we don't have a memory leak +the rest of the code in this class is +pretty simple but let's go over it block +by block +since websockets are very low level we +can potentially stream data as we work +this isn't as easy to handle but might +be more efficient in some cases +we don't need that +the push method allows us to send a push +message to the restaurant with the given +secret +this is just a look +look up within the session static object +and +sending the message +if we have a session this effectively +works exactly like push +we only send one message to the server +and that's the client secret +we rely on that here as we cache the +session +on the server +on the secret value +this effectively is the server +the build process calls push in all of +the places it sends a push notification +this is pretty trivial so i won't go +into that +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/FcHVK9ObONg.json b/docs/website/video-transcripts/FcHVK9ObONg.json new file mode 100644 index 0000000000..3cef79656c --- /dev/null +++ b/docs/website/video-transcripts/FcHVK9ObONg.json @@ -0,0 +1,8 @@ +{ + "line_count": 105, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 543, + "youtube_id": "FcHVK9ObONg" +} diff --git a/docs/website/video-transcripts/FcHVK9ObONg.txt b/docs/website/video-transcripts/FcHVK9ObONg.txt new file mode 100644 index 0000000000..9f8d29a031 --- /dev/null +++ b/docs/website/video-transcripts/FcHVK9ObONg.txt @@ -0,0 +1,105 @@ +up until now we dealt mostly with client +code +but it's time to look at the full stack +and leverage the work we did in spring +boot +for this application as well +Server Code +we'll use the same server code as the +restaurant app we built earlier this +simplifies a lot of details although +in long term scaling it might be a +hindrance +i don't think this is the time to think +of that +premature optimization might not be the +root of all evil but it sure slows down +your project +one huge benefit is that we can just set +up the restaurant code directly +into the database and reuse some +business logic between the apps +again this is a depth first +implementation +with the goal of getting something +working and building up from there this +applies to the server just as much as it +applies to the client +Secret Key +in order to fetch data about a +restaurant we use a unique restaurant id +since the information is public +knowledge that's not a problem +purchase +uses its own key so it's secure in that +sense +however +when we want to edit a restaurant +we'd want a completely different and +separate key to work with +and that's where the secret key +comes into play +we can't use the restaurant id in order +to edit the restaurant details it's +something that any person +who reverse engineers the application or +its communications protocol can discover +we could use a password scheme but that +would be a hassle to manage +edit reset password etc +it's much easier to just generate a +completely secret +unique id +this is effectively a key that only the +restaurant owner has +but it's internal so he isn't aware of +its existence +the second key is used for write +requests to the server +and thus it's meaningless for the +regular restaurant +restaurant app which is unaware of it +Code +this is actually pretty simple in the +code +we add a secret field and a new find by +secret method +the main challenge is in maintaining the +discipline +of working only with the secret +when changing data +Challenges +normally when we add data like a dish +you would expect the submission to +include both the image of the dish and +all the properties +it's possible but i was having issues +with spring boot and multipart requests +conflicting with the json parsing +it's probably something i could have +solved but i wanted to move forward so i +separated this into two calls +for simplicity +i upload the images in post requests +which is the default for multi-part +request +and put the json in a put request +when the server finishes a build it +should send a push to the device with a +link to the final url +the url +just points to a file saved in the +server local file system +where we generated the app +the way the build +can +run that way the build can run +asynchronously and send the results +later +this also allows us to implement a build +queue +the queue is valuable as we don't want +to overburden the server +i'm skipping push at this moment it's a +hassle to implement and i'd rather focus +on the wider picture diff --git a/docs/website/video-transcripts/FczBAgUIb1c.json b/docs/website/video-transcripts/FczBAgUIb1c.json new file mode 100644 index 0000000000..2417150433 --- /dev/null +++ b/docs/website/video-transcripts/FczBAgUIb1c.json @@ -0,0 +1,8 @@ +{ + "line_count": 64, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 339, + "youtube_id": "FczBAgUIb1c" +} diff --git a/docs/website/video-transcripts/FczBAgUIb1c.txt b/docs/website/video-transcripts/FczBAgUIb1c.txt new file mode 100644 index 0000000000..4c135352d5 --- /dev/null +++ b/docs/website/video-transcripts/FczBAgUIb1c.txt @@ -0,0 +1,64 @@ +the notifications container lists alerts +received by the app +it's relatively simple +to support this page we'll need to add a +few things into the data model +first we need to add a new notification +business object to the data package +the text of the notification should +arrive from the server +reaction is the material icon associated +with a notification +for instance a heart or a thumbs up +the background color can come from the +server as the original ui has different +color for every notification reaction +this is all pretty simple +to mark this in the ui +we'll need a new method in the server +api class +there can be a lot of notifications so +we are asking for the notifications +since a given time +we construct hard-coded notifications +for every entry with a specific +hard-coded reaction +just so we are on the same page this is +the ui of the notification tab +and these are the reactions i was +talking about +once we have +all these changes in place the +notification ui is pretty simple +we use an infinite container just like +we did in the news feed +and use the timestamp of the last entry +to fetch more +we update the timestamp for the next +request and create a notification +component +the code is simple since there are no +titles or special components +the reaction entry uses the blue circle +uiid to annotate the reaction +setting the color +won't work correctly and might impact +other borders +the solution is creating a new border +instance +the avatar placed in the layered layout +and the reaction is placed above the +z-axis +we then use the flow layout to align the +reaction to the bottom right +just like the other forms we enclose the +html label +and the blue label below into a y asus +container +there is just one missing piece in the +css +from the listing +the code here is pretty self-explanatory +it's a small blue label +with that we only have one last tab to +address diff --git a/docs/website/video-transcripts/GEG_S-dyxaM.json b/docs/website/video-transcripts/GEG_S-dyxaM.json new file mode 100644 index 0000000000..a427d848de --- /dev/null +++ b/docs/website/video-transcripts/GEG_S-dyxaM.json @@ -0,0 +1,8 @@ +{ + "line_count": 65, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 356, + "youtube_id": "GEG_S-dyxaM" +} diff --git a/docs/website/video-transcripts/GEG_S-dyxaM.txt b/docs/website/video-transcripts/GEG_S-dyxaM.txt new file mode 100644 index 0000000000..02bfde0c63 --- /dev/null +++ b/docs/website/video-transcripts/GEG_S-dyxaM.txt @@ -0,0 +1,65 @@ +in this section we'll discuss selection +in the category list and the search +functionality +when i initially built this code i had +some issues with the search feature in +the overlay toolbar which have since +been resolved +i ended up implementing this code +manually instead of using the built-in +search toolbar api +i think it's still a good exercise and +helps understand how these things work +so i didn't change the code back to +search toolbar despite the fact that it +should work now +Enabling Search +to enable search i started with this +method that might look intimidating but +it's really quite +simple +this is the command representing search +which we use instead of +the built-in command +search has two modes +in one of them the title is a regular +label title +and in the other the title is a text +field +where we are searching +when we press the search button we +effectively toggle between these two +we do several big things here first we +create the new text field representing +the search text and give it the hint +search +then we style everything to have the +title ui id +we then bind the listener to the text +field to call the onsearch method +when a user types into the text field +and then open the virtual keyboard +when we invoke the start editing method +OnSearch +the on search +method is actually pretty simple +if searches null we just show all the +entries and move on +if search contains a value we loop over +the dish entries and hide the ones that +don't have the search value within +notice that we do this by looking at the +dish object stored within the client +properties in advance +this makes the search logic simpler +when a user +flips through the category list +we filter the dishes using a method that +is almost identical to the search method +getting an app all the way to the finish +line is challenging but don't let +perfectionism stop you +you need to focus on what's important +about the app and get moving +thanks for watching i hope you found +this educational diff --git a/docs/website/video-transcripts/GEzM1MXkqnk.json b/docs/website/video-transcripts/GEzM1MXkqnk.json new file mode 100644 index 0000000000..02cebdd667 --- /dev/null +++ b/docs/website/video-transcripts/GEzM1MXkqnk.json @@ -0,0 +1,8 @@ +{ + "line_count": 93, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 530, + "youtube_id": "GEzM1MXkqnk" +} diff --git a/docs/website/video-transcripts/GEzM1MXkqnk.txt b/docs/website/video-transcripts/GEzM1MXkqnk.txt new file mode 100644 index 0000000000..7a4ce893cd --- /dev/null +++ b/docs/website/video-transcripts/GEzM1MXkqnk.txt @@ -0,0 +1,93 @@ +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/video-transcripts/GMnWMB_JfaQ.json b/docs/website/video-transcripts/GMnWMB_JfaQ.json new file mode 100644 index 0000000000..d906005507 --- /dev/null +++ b/docs/website/video-transcripts/GMnWMB_JfaQ.json @@ -0,0 +1,8 @@ +{ + "line_count": 130, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 649, + "youtube_id": "GMnWMB_JfaQ" +} diff --git a/docs/website/video-transcripts/GMnWMB_JfaQ.txt b/docs/website/video-transcripts/GMnWMB_JfaQ.txt new file mode 100644 index 0000000000..fd87e20ad2 --- /dev/null +++ b/docs/website/video-transcripts/GMnWMB_JfaQ.txt @@ -0,0 +1,130 @@ +in the second part we'll cover some of +the other big ticket items in security +we'll start with storage encryption +which allows us to encrypt the storage +data +notice that storage isn't accessible by +default and is isolated +however if a hacker gets +physical access to the device or can get +through an os exploit +this might be an important second line +of defense +notice that this is about storage +encryption +people often confuse file system storage +and storage +they are very different things +file system is low level and pretty +similar to file systems on desktop os's +storage can be implemented on top of +file system +but it's more of an abstract concept +because of that we can apply encryption +seamlessly to the storage class +but can't do +that seamlessness +for the file system or sql +class +the bouncy castle cn1 lib +implements a wide range of encryption +algorithms +that you can use for most common +encryption tasks +we enhanced it with the ability to +seamlessly encrypt the storage +using the encrypted storage class +once this is applied the constant +content of storage will be completely +encrypted +we use aes +for the storage encryption +that's a multi-military grade and +algorithm that balances performance and +security +it should be enough for most purposes +the main weakness with storage +encryption +relates to key storage +you need to pass the key +to the encrypted storage class +this means the key will be accessible to +a hacker that might use it +if you need perfect security you have +the option of encrypting the data with a +randomly generated key +and save that key in the file system +obviously +this poses the problem of +where you will need to encrypt the +randomly +generated key +another option +is to store an encryption key in the +server that will be provided on login +this is the most secure option but it +will block the option of working offline +another important feature is screenshot +blocking +this can prevent potential attack or +even misuse when a screen containing +private information can be captured +you can't block screenshots on ios +that feature isn't available there +on android we can block screenshots +using the build hint to disable +screenshots +this also hides the screenshot from the +android task manager which might impact +the user experience +if you have a banking app you might be +concerned that user will grab a +screenshot that includes potentially +private information +and you might want to block that +some organizations have a security +policy that requires this flag +copy and paste is a common vector of +attack for spyware +if you have a malicious app in your +device one of the first things +it might do is peek into the clipboard +to see if you have +proprietary information there +that's why copying from +password fields is always disabled +but you might want to block some other +fields that contain proprietary +information +you can block copy and paste for the +entire app which might be a bit much for +most applications +alternatively you can block copy and +paste on a specific field +using a client property on that field +jailbroken devices remove important +layers of security +they allow apps to be installed outside +of the app store on ios +which means a lot of the security +measures for +an app are forfeited +they also allow root access for apps +which allows more exploits where +jailbroken devices +allow +full-on spyware to run in the background +the problem is that it's really hard to +detect a jailbroken device +as the very idea of jailbreaking is +hiding this fact +so the is jailbroken device method +makes the best effort but can't +guarantee +if the method returns true the device is +probably jailbroken but if it returns +false +it might still be a jailbroken device +that we failed to detect +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/H7WW4GwXK8o.json b/docs/website/video-transcripts/H7WW4GwXK8o.json new file mode 100644 index 0000000000..7cf8977330 --- /dev/null +++ b/docs/website/video-transcripts/H7WW4GwXK8o.json @@ -0,0 +1,8 @@ +{ + "line_count": 45, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 232, + "youtube_id": "H7WW4GwXK8o" +} diff --git a/docs/website/video-transcripts/H7WW4GwXK8o.txt b/docs/website/video-transcripts/H7WW4GwXK8o.txt new file mode 100644 index 0000000000..81646849a4 --- /dev/null +++ b/docs/website/video-transcripts/H7WW4GwXK8o.txt @@ -0,0 +1,45 @@ +we're proceeding with image and video +integration +the changes to the client-side +post-class are trivial +we added the map entry for the +attachments +and added it to the list of properties +this will automatically pass it from +json and generate it to json when we +send a server request +we'll need one small change from ser +server api +this is the last infrastructure change +before we start wiring everything +together +when we submit an image post +we'll upload the media before the post +is written +this shouldn't be a problem when +for small images but anything larger or +on a slow connection could be a problem +so the right thing to do is show a +progress indicator for the upload +process +unfortunately the upload method in +server api doesn't allow us to do this +furthermore in one case we have a file +path with no byte array data +obviously we can load it +ourselves but the multi-part api already +has support for that +so we'll leverage it in the api +for now +we now return the multi-part request +so we can bind a progress indicator to +it +if there is no data byte array will fall +back to a file +now i didn't want to get into the i o +exception issue +and just converted it to runtime +exception +this is a simple change +and the final piece before the upload +code diff --git a/docs/website/video-transcripts/HJvMQKM5FPY.json b/docs/website/video-transcripts/HJvMQKM5FPY.json new file mode 100644 index 0000000000..822e57747b --- /dev/null +++ b/docs/website/video-transcripts/HJvMQKM5FPY.json @@ -0,0 +1,8 @@ +{ + "line_count": 49, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 299, + "youtube_id": "HJvMQKM5FPY" +} diff --git a/docs/website/video-transcripts/HJvMQKM5FPY.txt b/docs/website/video-transcripts/HJvMQKM5FPY.txt new file mode 100644 index 0000000000..45d23502de --- /dev/null +++ b/docs/website/video-transcripts/HJvMQKM5FPY.txt @@ -0,0 +1,49 @@ +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/video-transcripts/HRTLnLecoMA.json b/docs/website/video-transcripts/HRTLnLecoMA.json new file mode 100644 index 0000000000..cb87331ad2 --- /dev/null +++ b/docs/website/video-transcripts/HRTLnLecoMA.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 110, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/030-introduction-and-installation.md", + "status": "transcript-fetched", + "word_count": 561, + "youtube_id": "HRTLnLecoMA" +} diff --git a/docs/website/video-transcripts/HRTLnLecoMA.txt b/docs/website/video-transcripts/HRTLnLecoMA.txt new file mode 100644 index 0000000000..b831e07aa7 --- /dev/null +++ b/docs/website/video-transcripts/HRTLnLecoMA.txt @@ -0,0 +1,110 @@ +in this module we'll discuss maps in +codename one +focusing on google native maps +and we'll also delve a bit into placing +components on top +but first +let's explain the different kinds of +maps and why this is so confusing +currently the cn1 lib for native maps is +based on google maps +however the architecture is generic +enough +and this functionality can be ported to +apple maps bing +or other map providers +this would obviously require some work +on your part +but the logic within the maps +implementation doesn't include any +special code +and one could in theory adapt the cn1 +lib to pretty much any other map +provider +currently we support four different map +implementations +in the cn1 lib +most of them are based on google maps +but not all of them +the ios implementation +uses the native google maps api for ios +the android implementation uses the +standard native android maps from google +then we have two fallbacks +the javascript fallback uses the browser +component to show google maps within the +browser +and map component can work with either +google maps +or open street maps +we considered map component as legacy +code +and have deprecated it +the original code made a lot of sense in +2012 +when tiled maps were still used by +providers +but modern map implementations are +vector-based +and much faster +that's why we promote the javascript +option as a +fallback +on android or ios the native version for +the os is used +if that fails +the fallback is used +the fallback is also used in other +platforms such as the simulator web etc +map component is generally a subpar +fallback +that is too far apart from the native +map implementation +on the other hand the javascript maps +are closer to the final result +they are officially supported by google +and generally smoother to use +in order to use the maps +you need to obtain keys from google +this isn't too hard and has been greatly +simplified by google +notice that google imposes quotas on map +usage and you might be charged if your +app gets significant +usage volume +for security you should always store +keys in the server and fetch them upon +authentication +this is something i demonstrated in the +key service code in the builder app +however +to keep this code tutorial simple and +self-contained +i won't do this here and will include +the keys within the source +since this will use my keys +i won't leave them in the packaged +source you will need to fill out your +own keys based on the instructions here +these are the urls that allow you to +create the three map keys +you will need +one for javascript which will be used +for fallback +second for android and third for ios +notice that you can just google for +these urls +as they might change in the future +the process to generate the key requires +that you provide the app package name +notice that this must match your unique +app package +once this is done you can install the +native maps library from codename one +settings under the extensions section +you can do that by pressing the download +button and waiting for it to finish +once download finished you can right +click the project and select +refresh cn1 lib files which will finish +the installation of the cn1 lib diff --git a/docs/website/video-transcripts/IaalGP1UDeU.json b/docs/website/video-transcripts/IaalGP1UDeU.json new file mode 100644 index 0000000000..f03974eac7 --- /dev/null +++ b/docs/website/video-transcripts/IaalGP1UDeU.json @@ -0,0 +1,8 @@ +{ + "line_count": 44, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 238, + "youtube_id": "IaalGP1UDeU" +} diff --git a/docs/website/video-transcripts/IaalGP1UDeU.txt b/docs/website/video-transcripts/IaalGP1UDeU.txt new file mode 100644 index 0000000000..8e1ce78594 --- /dev/null +++ b/docs/website/video-transcripts/IaalGP1UDeU.txt @@ -0,0 +1,44 @@ +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/video-transcripts/IqsUSCgSVTo.json b/docs/website/video-transcripts/IqsUSCgSVTo.json new file mode 100644 index 0000000000..cc539d4ff6 --- /dev/null +++ b/docs/website/video-transcripts/IqsUSCgSVTo.json @@ -0,0 +1,9 @@ +{ + "line_count": 34, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-use-offline-build.md", + "status": "transcript-fetched", + "word_count": 1178, + "youtube_id": "IqsUSCgSVTo" +} diff --git a/docs/website/video-transcripts/IqsUSCgSVTo.txt b/docs/website/video-transcripts/IqsUSCgSVTo.txt new file mode 100644 index 0000000000..3d0f77b882 --- /dev/null +++ b/docs/website/video-transcripts/IqsUSCgSVTo.txt @@ -0,0 +1,62 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +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 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. + +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. + +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 diff --git a/docs/website/video-transcripts/IxVUX0s2Nms.json b/docs/website/video-transcripts/IxVUX0s2Nms.json new file mode 100644 index 0000000000..d394d944ac --- /dev/null +++ b/docs/website/video-transcripts/IxVUX0s2Nms.json @@ -0,0 +1,8 @@ +{ + "line_count": 136, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 700, + "youtube_id": "IxVUX0s2Nms" +} diff --git a/docs/website/video-transcripts/IxVUX0s2Nms.txt b/docs/website/video-transcripts/IxVUX0s2Nms.txt new file mode 100644 index 0000000000..30fe074daa --- /dev/null +++ b/docs/website/video-transcripts/IxVUX0s2Nms.txt @@ -0,0 +1,136 @@ +in this video we cover some of the +additional forms in the app and how +we'll address them +starting with the details form +Details Form +the details form becomes far simpler +once most of the data moves to the title +area +the remaining details are mostly billing +which has its own form +and miscellaneous information such as +email url etc +Billing Form +the billing form is important not just +because we wanted the details to feel +less cluttered but also conceptually +billing usually needs to stand +on its own as people seek it out and +expect help specifically for that +feature +it's another relatively simple form that +could probably be created with instant +ui +the build form is pretty much a copy and +paste with slightly different buttons +i think consistency is a virtue in this +case +here's where things get interesting +the details form allows us to navigate +to a styling form +this styling form allows us to click on +an area within the ui of the restaurant +app so we can customize that area +Color/Font Settings +when a user touches the restaurant ui +below you should see a pop-up dialog +with a set of options for customization +based on the option picked +a different ui +will be shown +if foreground or background is selected +we'll open a color picker that allows us +to either type in an html color or +fiddle with intensities to set the right +foreground or background color +Set Font +the font ui should have a very limited +set of options +mostly around basic styling +unfortunately including +files as problematic as we can't open +such a file dynamically on all oss +Set Image (icon, background, Dish) Set Dish Image +when image background or icon is +customized we should get a ui that +allows us to crop the icon to the right +size +appropriately so this image sizes will +always appear correctly +on the device when the final app is +built +this is important +images are too large that are too large +might also burden the server +so we limit image size in kilobytes on +the server +when we adapt the image size locally +we can pretty much guarantee it's not a +huge image +that will cross a problematic threshold +Address/Location Settings Address/Location +the details form will contain a lot of +details and including +all the fields required for restaurant +address +would be overwhelming +so it might make sense to separate it to +a different form +that includes all of that +one important detail +we need +is the geographic location of the +restaurant so we can launch +accurate navigation +to the location +the buttons in this ui +allow us to request location from the +gps instead of typing in +latitude longitude coordinates +the about the tails form should allow +setting the html +for the about form in the original +design i wanted both the ability to set +a url +for an about page and the ability to +embed html but eventually i settled on +setting the url only +Skipped +these are features that should probably +be a part of the app in the future +to keep the app simple i decided to +focus on the core +and remove as much of the things that +aren't essential +removing features is probably one of the +most important things you can do +when designing an app +Implementation Plan +a plan can be something you write down +or have in your head +but without when you are constantly +putting out +fires +and you never get anywhere +an organized plan with process +with a process prevents you from +wavering back and forth and keeps you +dealing with the priorities +i decided on a depth first +implementation where we will get a +specific set of forms working first and +then slowly add features +breadth first is also a good direction +but i prefer depth first as it allows me +to validate the whole process from end +to end relatively early and i think +that's crucial +this is especially important in this +relatively complex app where we might +run into conceptual issues that will +require +re-evaluation of some early designs and +plans +thanks for watching +next we'll start implementing the core +ui elements diff --git a/docs/website/video-transcripts/JWLsSbbo4N4.json b/docs/website/video-transcripts/JWLsSbbo4N4.json new file mode 100644 index 0000000000..954916797b --- /dev/null +++ b/docs/website/video-transcripts/JWLsSbbo4N4.json @@ -0,0 +1,8 @@ +{ + "line_count": 83, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 458, + "youtube_id": "JWLsSbbo4N4" +} diff --git a/docs/website/video-transcripts/JWLsSbbo4N4.txt b/docs/website/video-transcripts/JWLsSbbo4N4.txt new file mode 100644 index 0000000000..b9c9e09a37 --- /dev/null +++ b/docs/website/video-transcripts/JWLsSbbo4N4.txt @@ -0,0 +1,83 @@ +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/video-transcripts/JZvrsZzdays.json b/docs/website/video-transcripts/JZvrsZzdays.json new file mode 100644 index 0000000000..d713c85e6e --- /dev/null +++ b/docs/website/video-transcripts/JZvrsZzdays.json @@ -0,0 +1,8 @@ +{ + "line_count": 93, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 578, + "youtube_id": "JZvrsZzdays" +} diff --git a/docs/website/video-transcripts/JZvrsZzdays.txt b/docs/website/video-transcripts/JZvrsZzdays.txt new file mode 100644 index 0000000000..85297f945e --- /dev/null +++ b/docs/website/video-transcripts/JZvrsZzdays.txt @@ -0,0 +1,93 @@ +user service is a big class and it makes +sense to cover it in two parts +next on the agenda is get avatar it +returns the avatar picture of the user +as a byte array +notice we need an id and not a token to +get the avatar as anyone who is familiar +with the user id should be able to see +the avatar +if the avatar reference is null we +return null instantly +the set avatar method works in a similar +way +notice that unlike get avatar with set +avatar we need a token as this is a set +operation this prevents other users from +changing our avatar +when we map this to the web service we +need to first create a media entry and +only then set the avatar value +the friend request includes a few moving +parts +first we need to send a request +we need an auth token +as this is a secure operation +we obviously don't have the auth token +of the person we are friending +this is a special case where we modify +his entry without an auth token that's a +bit risky +the notification service should notify +the user that he got a new friend or +request +we'll dig into the notification service +soon +but first let's check out the other side +of the equation +the accept friend request method +this time me and him are flipped +this checks against a potential weakness +where a hacker could potentially call +accept friend request for a person that +didn't request it +when a request is accepted we need to +update the friend list on both sides +i didn't add a remove friend request +method which i probably should have +added i think it's trivial to add so i'm +leaving it as an exercise +the last method for user service is the +upload contacts method +it's invoked when the user uploads the +contacts from his phone +if we upload contacts in the past +it's a good idea to try and merge with +what we have +so we can check that by running a +count query +for every contact we find we check to +see if there is a user in the system and +if so we add that user to the people you +may know list +when adding a lot of elements using save +all is much faster than save +for simplicity i chose to merge based on +full name in retrospect it might have +been wiser to use a device unique id for +every contact but that might breed +duplicates too +i oversimplified a very complex process +of handling shadow users +if you take the time to build something +like this then make sure to analyze data +more thoroughly and create a more more +robust social graph +notice that we still need to declare the +update people you may know method we +mentioned before +we search for the shadow user in the +table of users using the find him method +which we'll cover soon +if a friend was found and isn't already +a friend or in the people you may know +list +then we can add that user to the people +you may know +the find him method is simple +boilerplate that searches based on all +the metadata in the shadow user we use +find by email phone etc to find the +shadow user in our regular user database +with that we finished the user service +class which is the largest service class diff --git a/docs/website/video-transcripts/Jk3tTyZroP0.json b/docs/website/video-transcripts/Jk3tTyZroP0.json new file mode 100644 index 0000000000..5737bd7d43 --- /dev/null +++ b/docs/website/video-transcripts/Jk3tTyZroP0.json @@ -0,0 +1,8 @@ +{ + "line_count": 162, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 905, + "youtube_id": "Jk3tTyZroP0" +} diff --git a/docs/website/video-transcripts/Jk3tTyZroP0.txt b/docs/website/video-transcripts/Jk3tTyZroP0.txt new file mode 100644 index 0000000000..e33af4c618 --- /dev/null +++ b/docs/website/video-transcripts/Jk3tTyZroP0.txt @@ -0,0 +1,162 @@ +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/video-transcripts/KckMUmmSzd4.json b/docs/website/video-transcripts/KckMUmmSzd4.json new file mode 100644 index 0000000000..e8c1b69335 --- /dev/null +++ b/docs/website/video-transcripts/KckMUmmSzd4.json @@ -0,0 +1,8 @@ +{ + "line_count": 182, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1035, + "youtube_id": "KckMUmmSzd4" +} diff --git a/docs/website/video-transcripts/KckMUmmSzd4.txt b/docs/website/video-transcripts/KckMUmmSzd4.txt new file mode 100644 index 0000000000..e91761a073 --- /dev/null +++ b/docs/website/video-transcripts/KckMUmmSzd4.txt @@ -0,0 +1,182 @@ +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/video-transcripts/Ke8bjFdD1bc.json b/docs/website/video-transcripts/Ke8bjFdD1bc.json new file mode 100644 index 0000000000..575bd33b0b --- /dev/null +++ b/docs/website/video-transcripts/Ke8bjFdD1bc.json @@ -0,0 +1,8 @@ +{ + "line_count": 95, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 584, + "youtube_id": "Ke8bjFdD1bc" +} diff --git a/docs/website/video-transcripts/Ke8bjFdD1bc.txt b/docs/website/video-transcripts/Ke8bjFdD1bc.txt new file mode 100644 index 0000000000..38fb8f5a15 --- /dev/null +++ b/docs/website/video-transcripts/Ke8bjFdD1bc.txt @@ -0,0 +1,95 @@ +next on the agenda is integrating the +model that we developed with the UI that +we built +first I didn't mention the format +currency call +initially when I started working on the +code I used the L10 manager to format +the code which made sense at a glance +however currency is something we get +from the restaurant itself +and it should handle the currency type +adding the dishes code +is code we already have however this +second block of code is pretty cool +first I'll bind a listener to the dish +quantity which allows me to track +changes to the map of dishes +to the quantities of each +that means that every time a dish is +added or removed in the map of dishes +regardless of the source of the change +we can get an event +and recalculate the amount in the order +updating the amount is literally as +trivial as looping over the dishes and +adding up the amounts when then setting +it to the label +notice that we call revalidate at the +bottom since the label might have a +longer string as a result and we might +need to enlarge the space allocated for +it +we have a very similar code in the +checkout form it updates the total value +based on the dish map +in this case we don't need listeners +since the checkout form only provides +one way to edit the data the dish +container hasn't changed much so I +snapped out most of the code +however the animation portion of the +removal now needs to update the price +after the animation completed +notice we call update price only after +the animation so the repaint of the +price won't interfere with the Running +Animation +the contact test form is practically the +same as before and the only major +difference is that the formerly +hard-coded data is now entirely from the +restaurant class +the main menu form has three interesting +pieces of code I'd like to focus on +first the code to create the categories +list is no longer hard-coded +the second part is the content container +which Loops over the dishes in the main +menu and places them into the UI unlike +the previous code this now uses the +restaurant model to show the data +the final part is the code which adds a +dish +or order is the button you see here it +adds the item to the order total +notice that we just put the dish into +the order and update the quantity +nothing else +the event handling code updates the +subtotal of the order seamlessly +I don't always use the top to bottom +approach of development +but I like it more +when I take that approach I can quickly +bring non-coders into the circle of +discussion as the UI aspect is instantly +communicatable +and everything else becomes far easier +there are always exceptions to the rule +for instance sometimes we build apps for +companies that have extensive back-ends +and no front ends in those cases it's +sometimes easier to just build the +mapping to the back end and then build a +user interface around that +in both cases I'm firmly in the camp of +running quickly through the design +you can't debug a design unless you are +building a mission critical app your +time might be better spent coding +than overthinking a design +thanks for watching I hope you found +this educational please join the +discussion in the comments section and +let me know what you think diff --git a/docs/website/video-transcripts/KhSWyE6rAN8.json b/docs/website/video-transcripts/KhSWyE6rAN8.json new file mode 100644 index 0000000000..3fe47664af --- /dev/null +++ b/docs/website/video-transcripts/KhSWyE6rAN8.json @@ -0,0 +1,8 @@ +{ + "line_count": 95, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 527, + "youtube_id": "KhSWyE6rAN8" +} diff --git a/docs/website/video-transcripts/KhSWyE6rAN8.txt b/docs/website/video-transcripts/KhSWyE6rAN8.txt new file mode 100644 index 0000000000..a238dd0b7c --- /dev/null +++ b/docs/website/video-transcripts/KhSWyE6rAN8.txt @@ -0,0 +1,95 @@ +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/video-transcripts/L7ulPCUxIsw.json b/docs/website/video-transcripts/L7ulPCUxIsw.json new file mode 100644 index 0000000000..3c4450df58 --- /dev/null +++ b/docs/website/video-transcripts/L7ulPCUxIsw.json @@ -0,0 +1,8 @@ +{ + "line_count": 121, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 611, + "youtube_id": "L7ulPCUxIsw" +} diff --git a/docs/website/video-transcripts/L7ulPCUxIsw.txt b/docs/website/video-transcripts/L7ulPCUxIsw.txt new file mode 100644 index 0000000000..0bd41ef7c8 --- /dev/null +++ b/docs/website/video-transcripts/L7ulPCUxIsw.txt @@ -0,0 +1,121 @@ +when we build an app with the server +side we still need to think about the +rest api even if it's strictly for our +app +there are still implications such as +security and future enhancement that we +need to consider even for in-house apis +Assumptions +i first start with +general assumptions about the api +it will have several methods to update +create delete elements in the cloud +we will then send a build request +to build the content +this might sound obvious but originally +i thought we'd have an api where +everything is managed locally +and a build accepts all the data from +the client in one request +the main reason i have that design in my +head +is because the design makes sense for +codename one itself +but it doesn't make sense for this +specific api +API Update +this is the first draft of my plan for +the api +update restaurant would allow typical +crude operations on the restaurant +object +the same would be true for dish +files would let us get a specific file +from the server it won't include listing +or anything like that +we'll use this to push out the result +do build will perform the actual +asynchronous build and return +immediately +this is +far from a complete list +but it's a starting point +REST Request +this is the basic implementation of the +update restaurant rest request +the post method accepts image uploads +notice it can accept an icon a logo or a +background image and can set them into +the restaurant object +also notice we use +secret as an argument to find the right +restaurant instance +we return the secret value even though +it's not necessary +the main reason for returning the value +has more to do with spring boot behavior +than anything needed by the client side +the second method in the restaurant +service is the put method that accepts +the json request containing the +restaurant data +this is all pretty straightforward +if we have an existing secret we just +update the restaurant otherwise we +create a new one +the build service accepts three +arguments +the secret +safeguards this process +the push key provides us +the device id to which we can send push +notification +and the target type indicates the type +of build requirement required +for example android source etc +in order for the asynchronous call to +work we need to enable +asynchronous calls and spring boot which +we can do by enhancing the main +application +the build app method +is marked as asynchronous and will run +in a thread queue +right now i implemented a step that just +takes the restaurant app +zip and creates a file with the same +name +in a fixed directory in the file system +File Upload +for upload to work +we need to define the right limits for +file uploads and multipart +we can do that in the application +properties by adding these two entries +to define +maximum file sizes +that's important for stability of the +servers as they shouldn't get overly +large files +the files web service just returns a +file from the given directory +notice we verify the file name doesn't +contain any illegal characters +then verify that the file is within the +destination directory and has a zip +extension +this seems harder +than just returning a file +and it is +the reason for that is that we are +effectively exposing part of our file +system here +and +this is a security risk +for example +someone could include a url like this +that points to the root directory +and fetches the password list +if we don't verify every character in a +url +this would be possible diff --git a/docs/website/video-transcripts/LFoSl_a2rs8.json b/docs/website/video-transcripts/LFoSl_a2rs8.json new file mode 100644 index 0000000000..3d481caf8c --- /dev/null +++ b/docs/website/video-transcripts/LFoSl_a2rs8.json @@ -0,0 +1,8 @@ +{ + "line_count": 283, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1559, + "youtube_id": "LFoSl_a2rs8" +} diff --git a/docs/website/video-transcripts/LFoSl_a2rs8.txt b/docs/website/video-transcripts/LFoSl_a2rs8.txt new file mode 100644 index 0000000000..839683b5b5 --- /dev/null +++ b/docs/website/video-transcripts/LFoSl_a2rs8.txt @@ -0,0 +1,283 @@ +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/video-transcripts/LUHb5fuWzJE.json b/docs/website/video-transcripts/LUHb5fuWzJE.json new file mode 100644 index 0000000000..3fa2b1f133 --- /dev/null +++ b/docs/website/video-transcripts/LUHb5fuWzJE.json @@ -0,0 +1,8 @@ +{ + "line_count": 171, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 965, + "youtube_id": "LUHb5fuWzJE" +} diff --git a/docs/website/video-transcripts/LUHb5fuWzJE.txt b/docs/website/video-transcripts/LUHb5fuWzJE.txt new file mode 100644 index 0000000000..ec0a2999dd --- /dev/null +++ b/docs/website/video-transcripts/LUHb5fuWzJE.txt @@ -0,0 +1,171 @@ +in this section we'll finish the +restaurant app +while skipping some big pieces such as +the implementation of the server +i'm attaching the server project to the +module so you would be able to reference +and probably run it +but the goal of this course is to teach +codename one and not to teach server +development +in the next course we'll cover server +development more in depth and touch on +some of the work we did for this server +the goal of this module +is to finish the app +it's not a goal to create a perfect app +but rather to reach a milestone that we +can use +one of the hardest things for developers +to do is to finish +we constantly find something new that +needs doing and it becomes harder to +reach a point of release +that's why it's crucial to define strict +limits and curb your ambition +so you can get something into the hands +of users +restricting the feature set to a strict +list +only works if you maintain discipline +and don't embellish the list +don't add to the entries and don't add +new entries +this is the current list i came up with +for this for this app +it includes many missing pieces and some +of them are pretty big +but generally it represents the things +we need to add or fix +i intend to implement all of these +within this module which is relatively +quick +normally a list like this should take +longer to implement +let's get started with the first item on +the agenda +the network layer +and i'll do this by creating an +abstraction of the server communication +despite skipping the server code most of +this code should be pretty clear +as we go over it +the dish service implements caching +for the dish list +we fetch from the server to make sure +the app loads instantly +if we fetch new data +we will show it later as it's more +important that the app loads quickly +here we check whether cache even exists +this is important for +first time activation +assuming cache exists +we can just load the data from the cache +into the global +restaurant object +the cache has the +json as it was returned from the server +so we can pause it using the standard +parsing code +this is convenient for debugging as we +can inspect the json file in the file +system +for the simulator and see what's saved +locally +the json file contains a map object +which contains two arrays +dishes and categories +the dishes array contains all the +information in a dish and every entry +within this map has the same key value +pair as the dish +object properties +one of the features of property objects +is the ability to populate a property +from a map +or from json directly +and this is exactly the case for which +this is useful +the categories are just string names of +all the available categories we could +probably just go over the dishes and +extract the categories from there +from there +once we add the categories we set the +boolean flag indicating the download was +finished +this is important as external code might +have a listener bound to the property +so it can refresh the ui once the data +finished loading +moving on we have two separate methods +to update the dishes they are both +identical and just use the add to queue +and wait or add to queue +the reason we need both is simple we use +add to queue when the app launches as we +want to launch +right away and don't want to wait for +the data however pull to refresh needs +to block until the data is here +and in that case we need to use add to +queue and wait +the aptly named +update dishes from server +is the common code for both update +methods +it creates a connection request to the +dish url that performs a get http method +on the +server the response is passed mostly +so we can check the timestamp and update +the local timestamp +the timestamp tick +trick allows us to +send a request with the last received +timestamp +and initially we just send zero +this means the server is responsible for +updating that value and if no change was +made since the last time the server +changed that value we don't need to +receive new data +you will notice that if the data is +changed we save it to cache then load it +from cache later +this is pretty important +we call the loading code from the post +response call which runs on the event +dispatch thread +the other callbacks and the connection +request occur on a network thread but +the post response call is on the event +dispatch thread and thus it can change +the ui +or ui related elements +as i mentioned before we send the +timestamp and also an authorization key +to the server the latter is hard-coded +for the app but the former gets updated +by the server every time the data +changes let's move on to the main menu +form where we now fetch dishes from +cache if they aren't loaded yet +you will also notice that the port +refresh call makes use of the update +sync method to fetch the updated dishes +synchronously +this block represents a very special +case of the first invocation when we +have no dishes available and are still +waiting +in this case we place an infinite +progress in place and bind the listener +to the finished download so we can +replace the progress +notice we aren't concerned about dishes +suddenly appearing in the background +thread because everything is modified +via the event dispatch grid so we can +rely on this being in sync diff --git a/docs/website/video-transcripts/LZ_ydua__gY.json b/docs/website/video-transcripts/LZ_ydua__gY.json new file mode 100644 index 0000000000..288706f24a --- /dev/null +++ b/docs/website/video-transcripts/LZ_ydua__gY.json @@ -0,0 +1,8 @@ +{ + "line_count": 74, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 454, + "youtube_id": "LZ_ydua__gY" +} diff --git a/docs/website/video-transcripts/LZ_ydua__gY.txt b/docs/website/video-transcripts/LZ_ydua__gY.txt new file mode 100644 index 0000000000..67dd555acd --- /dev/null +++ b/docs/website/video-transcripts/LZ_ydua__gY.txt @@ -0,0 +1,74 @@ +the hardest part in the mock-up is +behind us the next stage is the French +container which lists friends requests +and potential friends suggestions +the friends container is a simple UI and +simple class we have a few UI IDs within +this class that I'll cover soon +when I get to the CSS related changes +let's look at the code +we extend container instead of infinite +container as we don't need it in this +case +technically I could have gone with an +infinite approach but I wanted to keep +things simple +the container arranges elements +vertically using box layout since this +isn't a form it's not scrollable by +default so we need to enable +scrollability because the parent form +isn't scrollable +add pull to refresh removes the elements +and recreates the UI +init fills up the container it's invoked +when the class is created or refreshed +the Avatar and the side of the friend +appears as an 18 millimeter image +we use a blank image as the placeholder +for the URL image so the Avatar will +download dynamically to the local cache +if we have friend suggestions we Loop +over friend requests and add them with +the avatar for each one notice we used +the URL image create cached image API to +fetch the Avatar URL instead of get +Avatar Facebook used square images here +so it made sense to use something else +for this functionality +we do the same for the friend suggestion +list under a different title +next we have the friend request entry +method +we create the button labels based on the +type of request +we place the name above the buttons +using a box layout on the Y Asus then +place the two buttons within a grid +layout giving them the same size +the Avatar is placed in the west since +its size is constant we place the +content in the center which gives it +space to grow +the titles above the listing are created +in the init method with this code block +there this handles the red circle with +the number next to the title +in order to complete this we need a few +CSS changes +the subtitle is just a gray slightly +larger font +the red circle has padding that is large +enough to make it visible the size is +close enough so it will align reasonably +with the friend subtitle +friend name looks smaller than friend +subtitle but it's really because friends +subtitle is written in uppercase +the confirm button is a standard button +with rounded corners and a specific +background +the delete button removes the background +and adds a pixel wide border around it +with that the friend suggestion UI +should work and run as expected diff --git a/docs/website/video-transcripts/Lai--eYYJTw.json b/docs/website/video-transcripts/Lai--eYYJTw.json new file mode 100644 index 0000000000..5bbf0ad539 --- /dev/null +++ b/docs/website/video-transcripts/Lai--eYYJTw.json @@ -0,0 +1,8 @@ +{ + "line_count": 148, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 831, + "youtube_id": "Lai--eYYJTw" +} diff --git a/docs/website/video-transcripts/Lai--eYYJTw.txt b/docs/website/video-transcripts/Lai--eYYJTw.txt new file mode 100644 index 0000000000..231fb54f4b --- /dev/null +++ b/docs/website/video-transcripts/Lai--eYYJTw.txt @@ -0,0 +1,148 @@ +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/video-transcripts/M518p4_Horg.json b/docs/website/video-transcripts/M518p4_Horg.json new file mode 100644 index 0000000000..5cb8a2cef8 --- /dev/null +++ b/docs/website/video-transcripts/M518p4_Horg.json @@ -0,0 +1,8 @@ +{ + "line_count": 104, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 589, + "youtube_id": "M518p4_Horg" +} diff --git a/docs/website/video-transcripts/M518p4_Horg.txt b/docs/website/video-transcripts/M518p4_Horg.txt new file mode 100644 index 0000000000..3ae467ccee --- /dev/null +++ b/docs/website/video-transcripts/M518p4_Horg.txt @@ -0,0 +1,104 @@ +the changes to newsfeed container +include the final pieces to post a media +object and the ability to view a media +post +while i was here i also fixed the code +for displaying a styled post which is +something i never implemented in the +code itself +we'll start at the bottom +with create post bar +which is where we reference the new apis +to post an image slash video file +this was changed from using the +constructor to the new factory method +the gallery picker launches in an all +mode which will show both images and +videos +we invoke the right type of post based +on the user selection +that's a simple change +and now we can actually do a media post +it would actually work +but won't show the image in the ui as +the code within this class still can't +render that +this is a simple fix +first we need a new method that will +create a component to match the given +media id +we'll use the image as a placeholder +image for url image we initialize it +lazily to a size that makes sense for +this device +since the image is scaled in the +download process +there is no need for the calc preferred +size trick here +we can just use a regular button or +label +a media stream can be played directly +from our server url which is pretty darn +cool +we need to override preferred size here +because the media +can be any size +this is a bit oversimplified +proper server media handling will +transcode the +video to multiple form factors and +deliver the right video type for every +device this saves bandwidth but also +makes sure the media is playable +avoiding multiple device related issues +now that this is in place we can tie it +in to the post rendering logic in create +news item +if we have an attachment we add a media +component to the bottom of the post +otherwise we use the same postcode as +before +with the media post +with that media post will work and show +up in your feed +there is however one additional +enhancement i added to the create news +item +method to support the styled posts which +up until now were ignored +i save the result of the if statement +here to make the following code shorter +and simpler +rich text view now accepts a ui id for +the content i'll go into that code soon +the body is given +the background style ui id +the same is true for span label version +of the code +this leads us directly to the changes in +rich text view +because of its nature rich text view is +a bit of a hack +without much support for styling +i wanted to give it some generic styling +support for the rich posts so this isn't +a huge change but it helps +first i had to make font size non-final +as this will change due to styling +the next step is simply a change to the +constructors and edit method init now +accepts an optional ui id which defaults +to null +we set this ui id +in the new constructor but leave it as +null +in the other cases +the ui id is isn't set +if the ui id isn't set everything acts +like it did before +otherwise we get the default font from +the style then resize the bold italic +font to match its size +that's it we can now style the default +font +of the rich text view diff --git a/docs/website/video-transcripts/MLbOdCt67Rw.json b/docs/website/video-transcripts/MLbOdCt67Rw.json new file mode 100644 index 0000000000..0fd7a57839 --- /dev/null +++ b/docs/website/video-transcripts/MLbOdCt67Rw.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 63, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/013-css-changes.md", + "status": "transcript-fetched", + "word_count": 350, + "youtube_id": "MLbOdCt67Rw" +} diff --git a/docs/website/video-transcripts/MLbOdCt67Rw.txt b/docs/website/video-transcripts/MLbOdCt67Rw.txt new file mode 100644 index 0000000000..ab7f77620d --- /dev/null +++ b/docs/website/video-transcripts/MLbOdCt67Rw.txt @@ -0,0 +1,63 @@ +now that we have a general picture +of our final destination +let's figure out how to get there +the transparent gradient at the bottom +of the dish view is almost entirely css +which is pretty cool +this css generates an image which is +then overlaid with transparency +i also chose an italic font for this +case which might be more appropriate for +the dish description +the toolbar +also uses the gradient tint +it derives from container to get the +zero padding and margin +the map address +is similar to the dish overview +description +with a bit of padding +the text of the map address +is a separate component unlike the dish +overview +which means these ui ids +are effectively different +i set the background of the side menu +to be white +besides fitting with the general design +and separating it from the form color +this has another important use +one of the important things to notice +about the side command +is that margin is zero except for the +bottom where we have a one pixel margin +if you will look at the entries you will +see a one pixel white line between the +command entries +that's the white color of the side +navigation panel coming through between +the entries +it's not a separation line it's +literally the background +site commands are styled differently +in the native themes +so +we need to override a lot of behaviors +such as text decoration which is set to +3d text in ios +3d text draws a subtle shadow under +every letter +normally there is a status bar component +on top of the side menu in ios +so the clock or battery indicators don't +draw on top of the side menu +since we have an image here +this isn't a problem +and we can set its size to zero +i thought about using the exact same ui +id +as i used for the shopping cart button +but that button has a bit of a margin to +push it away from the side of the form +i probably should have used inheritance +here but i didn't diff --git a/docs/website/video-transcripts/MbTEz-K8iwY.json b/docs/website/video-transcripts/MbTEz-K8iwY.json new file mode 100644 index 0000000000..78adbf1b02 --- /dev/null +++ b/docs/website/video-transcripts/MbTEz-K8iwY.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 193, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/024-capture-and-callbacks-in-ios.md", + "status": "transcript-fetched", + "word_count": 1073, + "youtube_id": "MbTEz-K8iwY" +} diff --git a/docs/website/video-transcripts/MbTEz-K8iwY.txt b/docs/website/video-transcripts/MbTEz-K8iwY.txt new file mode 100644 index 0000000000..c91d6c13eb --- /dev/null +++ b/docs/website/video-transcripts/MbTEz-K8iwY.txt @@ -0,0 +1,193 @@ +in this final part we'll finish the ios +portion implementation +there are some methods that have skipped +entirely so i won't discuss them here +but there are a lot of simple methods +like we had in the android port which +just delegate onwards +the getters are the simplest they are +they don't even need to enter the ios +thread +this returns the camera view which +should be initialized once start is +invoked +this method is a standard method in +every native interface everything in +between is trivial +the setters are a bit more verbose but +not by much they are all practically +identical so i'll only focus on the +first +i don't want to call update if the +update didn't actually change +we update on the main thread by invoking +the update methods from before +notice i use the async core as a rule of +thumb always use the async call unless +you must use the sync core +it's faster and has a lower chance of a +deadlock +in this case i don't need the action to +happen within this millisecond so async +will work just fine +i also have two special getters +that need to run on the event dispatch +thread +normally this wouldn't be a problem but +when we need to do +to return a value that's a bit +challenging +the block keyword allows us to mark the +field as a field +we can change from the lambda expression +here is a case where we must use +dispatch sync if we used async the +return statement +would have happened before we assign the +value +now all that is left is the actual +capture method +the capture methods are a bit more +complicated +surprisingly capturing a still image is +a bit harder than capturing a video +since +the capture image method is a bit long +i've split it into two lock before +this is a similar case +of ios 10 requiring a new api +all capture methods must run on the +native ui threads as they deal with the +native ui +failing to do so +leads to weird crashes +this is the new ios 10 api +if it isn't here +we'll execute the ios 9 compatible code +up to this point everything is pretty +trivial +delegate is a special concept in ios +similar to java's interfaces +this means the current class implements +the delegate +before i go to the ios 9 code within +this method let's look at the delegate +in order to implement the delegate we +need to make a small change to the +header file +the delegates are declared in a syntax +that's reminiscent of the java generics +syntax +i also added the delegate needed for +video recording while i'm here already +so +that's two delegates +now that these are in place +let's look at the delegate code +delegate method declarations are huge +this goes all the way to the curly brake +brackets +this makes java's verbosity seem quaint +this is all just one delegate callback +method declaration +this is an error or the delegate was +invoked because of the vid of a video +event +we don't want to step into image +processing code +ns data handles almost all i o and ios +it's similar to bytebuffer in javasc +and allows us to map files from slash +into storage +i use nsdata to byte array to convert +the nsdata object to a java byte array +notice that in the native code all java +objects are effectively java underscore +object +i can now invoke the callback method +with the given byte array notice the +callback method includes the full name +and argument types +the vm passes thread local context on +the stack for every method +this makes things like stack traces work +for the code to compile we need to add +these declarations below the hashtag +import statements +the vm generates c code so we can import +it with include +the ns +data2byter method +is a +method from the ios port i could have +just included a whole header but there +is no need in this case it makes the +process of converting an nsdata +much simpler +now that all this is out of the way +let's go back to the capture image class +and the ios 9 compatibility block +this is effectively code i got from +stack overflow +i needed this since most samples are for +ios 10 plus now and ignore the legacy +but i still have quite a few ios 9 +devices that can't upgrade to ios 10 so +i'm assuming there is still some market +for compatibility +of this code is boilerplate code for +detecting the camera +no wonder it was removed +the captcha image process is +asynchronous and invokes the lambda +expression below to process the +resulting image +the rest of the code should be very +familiar as it's almost identical to the +one we used and the ios 9 version +we just call back into java +with this image capture should now work +for both old and new devices +all the basics should work with the +exception of video +capturing video is surprisingly easier +than capturing still images +first the simplest method +is capture video +the no arguments version of this method +saves to temporary file +file name we invoke the version that +accepts the file +with a temp.mob file path +the version that accepts a path +isn't much harder +the av capture movie file output class +represents the recording process +we lazily initialize it +we bind the output value to the capture +session +now we can provide a url from the file +argument and start recording again +we use self as the delegate +which obviously leads us to the delegate +method +most of this should be very familiar now +that we went through the image capture +code +the callback accepts a path string which +we can translate from in a string using +the from in a string api call +the last remaining piece +is the stop video method +this is a trivial method if you've been +keeping up +we just stop the recording and clean up +and with that last bit of code we are +done +i hope you found this useful +there is a lot of code to go through but +most of it is +damn trivial once you look at it +you can create native interfaces that do +just about anything if you have the +patience to debug and google native apis diff --git a/docs/website/video-transcripts/Mcw8z_uP3BA.json b/docs/website/video-transcripts/Mcw8z_uP3BA.json new file mode 100644 index 0000000000..41ee055c39 --- /dev/null +++ b/docs/website/video-transcripts/Mcw8z_uP3BA.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 273, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/020-introduction-and-generic-code.md", + "status": "transcript-fetched", + "word_count": 1479, + "youtube_id": "Mcw8z_uP3BA" +} diff --git a/docs/website/video-transcripts/Mcw8z_uP3BA.txt b/docs/website/video-transcripts/Mcw8z_uP3BA.txt new file mode 100644 index 0000000000..52d410ca5b --- /dev/null +++ b/docs/website/video-transcripts/Mcw8z_uP3BA.txt @@ -0,0 +1,273 @@ +this module covers low level apis and +some +ios slash android specific details +i don't aim to teach objective c or +some core concepts of android +development +you don't necessarily need to know these +things but they would help +it's important to notice that the code +in this module is fresh and complete +it's here as a porting reference +since this is pre-alpha level code it +should be treated as such +i've discussed low-level native +interfaces and third-party library +integrations before +but i never did it for an api as complex +as the camera api +arguably the maps support is harder so +developers could have used it as a +reference +my cheap motivation in doing this module +is building the cn1 lib itself +low-level camera access has been a wish +list of hours since we introduced said +ordering of components +as such +the tutorial is an afterthought +the first step of implementing a cn1 lib +is looking for available apis on both +sides +and sometimes surveying the work done by +other cross-platform tools for +inspiration +in this case other tools don't offer +something as comprehensive +so i don't spend too much time on that +android's native camera api is a mess +and at some point google just threw the +whole thing out and replaced it with +camera 2. +if you want to support older devices +you need to support both apis and +there's a lot of nuance with permissions +etc +i didn't want to use the native android +api as is +and started checking for options +two open source camera apis stood out +and camera kit android looked like the +best option +normally this is the place where i would +go over the android api and go over the +ios api and try to find common ground +instead i chose to skip that +and just write the android +implementation +then think about the ios implementation +after the fact +my reasoning was that camera kit already +spent a lot of time designing an +abstract api +it makes sense to follow their lead +i'm not a camera expert but they already +dealt with +user requirements and design issues +this would make the android +implementation trivial +and if something doesn't work on ios or +elsewhere we can create special cases +for that +i didn't build a cn1 lib +i started with an app that uses +native interfaces +apps can i can debug unlike a cn1 lib +once the app was done i just copied the +sources into a new cn1 lib project and +created that +so without further ado let's dive into +the portable portion of the code +as i mentioned before i decided to model +the api +based on the android camera kit api +which is mostly exposed in the camera +view class +you can see in the camera view project +camera view is a big class with a lot of +ui code so i wanted to distill the api +calls within +i searched within the class for public +methods and isolated them +i then created a new native interface +within com.codename1.camera +the one method that's a special case +that didn't exist in the original +is peer component +get view +this is a method that returns the +preview for viewing the camera +in the native code it's the actual +camera view class but in our case the +native interface hides that +this method returns the camera view +in the native code +camera view has a method +get camera properties which returned an +object called camera properties +that object contained two fields +horizontal and vertical +viewing angles which i just mapped +directly +in retrospect some of these apis like +toggle facing flash +is facing back front should have been +implemented in the java side +the api already used ins for constants +so this was pretty easy to translate +into a native interface +these two rely on event listener code +i'll elaborate more on that soon +these were originally preview and +capture size +they returned a size object which was +just width and height in a class +as you can see the translation was +mostly easy and other than a few methods +that returned objects everything was +really smooth +i did gloss over some apis i chose not +to implement within the native interface +you will notice all of these methods +have one thing in common callbacks +we can't pass a callback into the native +interface +so all of these apis can't be +implemented in the native interface +we can implement them in the high level +abstraction though and i'll go into and +i'll get into that soon +but before we go there i did mention the +constants from the native api +they are implemented in the camera kit +class in the native project i had to +implement our own constants which i did +using an interface +you'll notice that this is copied from +the android version and so values +returned from there should automatically +work for us on android +which is pretty cool +never expose +a native interface to a user it's a +recipe for disaster as things might not +work as expected +always +wrap the native interface +in a regular class as that allows for +fine grained control +it lets you change the native api and +move forward while keeping compatibility +and simplicity +so the next order of business was the +decision of +how to encapsulate this functionality +for that i created the camera kit class +which abstracts this native interface +we implement the constants interface +so we'll have easy access to them +the native interface instance is shared +between the methods of the class +this is a singleton +it's not how the native api works but +it's much simpler and we don't need +anything better +use this list +to send camera events to the user of the +api +i'll discuss that further soon +ios strips out unused code which can +cause callbacks to fail +we need to trick the ios vm into +thinking that some code will execute +ios needs a build hint we can just +inject it into the build hint list if +the user didn't declare it +we review the build hints if the ios dot +ns camera usage description isn't +declared +we add it +notice that this will work only +on the simulator +i called this create even though it's +more like a get instance method +while the camera kit won't work on the +simulator i can still bind functionality +to it +in this case we do +two things in the simulator +we registered the required build hint if +it isn't there already +we invoke all the callback methods +notice that this code will never execute +but our ios vm can't detect that because +this is for ios +is in private +without these calls +the ios vm would +strip out these methods and the c code +that invokes them +wouldn't compile +if the native interface is supported on +this device +then we can return the instance of this +class +otherwise we return null +there is a lot to digest here luckily +this was the hardest part the rest is +verbose +but it's far simpler than this +most of the class looks like this +we delegate the calls to the native +interface to keep that code hidden +nothing interesting +there are a few interesting methods +within the class so i'll address them +here and skip these boring calls +toggle facing +is in the native interface but instead +of implementing it in ios i chose to +implement it +in the camera kit and ignore the native +interface +the same is true for toggle flash i'm +sure i could do this for several other +methods and simplify the native +interface layer +the add remove listener code +just modifies the list of listeners +notice i trimmed the code a bit so it +would all fit in one slide +the fire methods are invoked internally +to send events to the listeners +since the native code invokes them +they might not be on the edt and so they +recurse via call serially +to move the context into the edt +the event dispatch logic is identical +for all the fire methods +it's just a loop over the listeners and +a method invocation +the rest of the listeners follow the +same semantic for updating a video +result or an image result +so +who invokes the fire methods +i deprecated this and the native +interface so developers won't use them +by mistake +this class is for internal usage only +all three events are handled in the same +way +through the fire method +notice the different types of arguments +to the constructors +we'll discuss the complexities of them +in the ios port +the final two missing pieces are the +listener interface +and event class +both should be pretty obvious +so i won't list them here +you can check out the project source +code if you are curious diff --git a/docs/website/video-transcripts/NVPIXnkoxv0.json b/docs/website/video-transcripts/NVPIXnkoxv0.json new file mode 100644 index 0000000000..015b96e64b --- /dev/null +++ b/docs/website/video-transcripts/NVPIXnkoxv0.json @@ -0,0 +1,9 @@ +{ + "line_count": 15, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-use-cloud-connect.md", + "status": "transcript-fetched", + "word_count": 421, + "youtube_id": "NVPIXnkoxv0" +} diff --git a/docs/website/video-transcripts/NVPIXnkoxv0.txt b/docs/website/video-transcripts/NVPIXnkoxv0.txt new file mode 100644 index 0000000000..89c74b15c6 --- /dev/null +++ b/docs/website/video-transcripts/NVPIXnkoxv0.txt @@ -0,0 +1,29 @@ +_Transcript source: embedded._ + +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. + +We’ll start by defining "cloud connect". Cloud connect synchronizes the GUI builder with devices seamlessly. + +When you make changes within the GUI builder those changes are instantly reflected on devices connected to your account via the cloud. + +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 diff --git a/docs/website/video-transcripts/Nv_0NVgCbSk.json b/docs/website/video-transcripts/Nv_0NVgCbSk.json new file mode 100644 index 0000000000..73ee3d3ead --- /dev/null +++ b/docs/website/video-transcripts/Nv_0NVgCbSk.json @@ -0,0 +1,8 @@ +{ + "line_count": 139, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 804, + "youtube_id": "Nv_0NVgCbSk" +} diff --git a/docs/website/video-transcripts/Nv_0NVgCbSk.txt b/docs/website/video-transcripts/Nv_0NVgCbSk.txt new file mode 100644 index 0000000000..49e1db0f31 --- /dev/null +++ b/docs/website/video-transcripts/Nv_0NVgCbSk.txt @@ -0,0 +1,139 @@ +next we have the name entry stage in the +sign up wizard +the name entry stage in the signup +wizard +is pretty trivial there's just one +interesting aspect here +the text +input notice that the text input looks +different in android when we enter text +and on ios it looks completely different +this is part of the magic behind the +text component class on android we +animate hint labels in the style of +material design +and in ios we show a grouped pair +let's go right into the create name +method +text component +is a text input component that combines +the label and text field into one +component and automatically adapts +it to the platform +text mode layout is a layout manager +designed for text component +it uses table layout on android and box +layout on ios +this will act like adding to a table +layout when running on android thus +placing the components side by side +on ios it would ignore the percentage +value and stack the components +vertically using a box layout on the y +axis +the text mode layout isn't really a +layout as much as it is a delegate +when running in the android mode which +we refer to as the on top mode +the layout is almost an exact synonym +of table layout and in fact delegates to +an underlying table layout +in fact there is a public final table +layout instance within the layout that +you can refer to directly +there is one small difference between +the text mode layout and the underlying +table layout and that's our choice to +default or to default to align entries +to top with this mode +it's important for error handling which +i didn't use here +when working in the non-android +environment we use a box layout on the +y-axis +as the basis +there is one thing we do have that's +different from a default box layout and +that's grouping +grouping allows the labels to align by +setting them to the same width +internally it just invokes component dot +set same width +since text components hide the labels +there is a special group method there +that can be used +however this is implicit with the text +mode layout which is pretty cool +this listens to the done button +in the virtual keyboard and +automatically treats it as a click on +the next button +for this form i need to add one change +to the css +this is the text that appears on top of +the android field when we have input +there +it's a really small label that uses +regular font for visibility instead of +light +with that we finished the name form and +it should just work +birthday is the form i'm currently least +happy with +we are in the process of rewriting the +picker component which would hopefully +allow a much better looking ui for this +form +for now i compromise on this +when the ui is clicked on the device you +will see a nice looking date picker ui +though +the flip side is that the code is super +simple +we set the picker type to date +20 years ago is a good place to start +for an age picker by default so that's +what i did +that's trivial and includes no changes +to the css +the gender form is pretty simple +we have a couple of ui ids for the +gender toggle and icons +before we can go into it i created a +small helper method to make the two +gender buttons +toggle buttons and codename one can be +radio buttons that means that we can +pick only one of them +when we click on female the mail entry +is deselected +we create the font icon based on a +separate ui id because we want it to be +much bigger than the text on the button +so we can't use set material icon +here we create the radio button and set +the text positioning +a radio button belongs to a group and +only one member of the group can be +selected +that's mostly simple boilerplate code +the form code is even simpler +we use button group to tie two or more +radio buttons together +only one of the radio buttons in a group +can be selected +grid layout gives both buttons the exact +same size +the last piece for this form is the css +for the ui ids we defined +the gender icon uses the same icon +settings as the logo with a different +color +we have a special case for the selected +pressed versions of the ui id to make +the icon white +the gender toggle ui id +uses a round wrecked border with a gray +line surrounding it once we did all that +we can run the app and we would be +halfway through the wizard diff --git a/docs/website/video-transcripts/OWHizrNyizQ.json b/docs/website/video-transcripts/OWHizrNyizQ.json new file mode 100644 index 0000000000..35fe14b362 --- /dev/null +++ b/docs/website/video-transcripts/OWHizrNyizQ.json @@ -0,0 +1,8 @@ +{ + "line_count": 29, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 235, + "youtube_id": "OWHizrNyizQ" +} diff --git a/docs/website/video-transcripts/OWHizrNyizQ.txt b/docs/website/video-transcripts/OWHizrNyizQ.txt new file mode 100644 index 0000000000..8532879517 --- /dev/null +++ b/docs/website/video-transcripts/OWHizrNyizQ.txt @@ -0,0 +1,29 @@ +In this tutorial we will go over the process of signing an iOS application +We start by logging in to the iOS provisioning portal +In the certificates section you can download your development and distribution certificates. +Devices +In the devices section add device id's for the development devices you want to support. +Notice no more than 100 devices are supported! +Application ID +Create an application id, it should match the package identifier of your application +perfectly! +Development +Create a provisioning profile for development, make sure to select the right app and make +sure to add the devices you want to use during debug. +Download +Refresh the screen to see the profile you just created and press the download button +to download your development provisioning profile. +Create a distribution provisioning profile, it will be used when uploading to the app +store. +There is no need to specify devices here. +Download the distribution provisioning profile. +Import +We can now import the cer files into the key chain tool on a Mac by double clicking the +file, on Windows the process is slightly more elaborate +We can export the p12 files for the distribution and development profiles through the keychain +tool +Export +In the IDE we enter the project settings, configure our provisioning profile, the password +we typed when exporting and the p12 certificates. +It is now possible to send the build to the server. +Thanks for watching. diff --git a/docs/website/video-transcripts/OnZIaGXDZSo.json b/docs/website/video-transcripts/OnZIaGXDZSo.json new file mode 100644 index 0000000000..8065eb666d --- /dev/null +++ b/docs/website/video-transcripts/OnZIaGXDZSo.json @@ -0,0 +1,8 @@ +{ + "line_count": 154, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 854, + "youtube_id": "OnZIaGXDZSo" +} diff --git a/docs/website/video-transcripts/OnZIaGXDZSo.txt b/docs/website/video-transcripts/OnZIaGXDZSo.txt new file mode 100644 index 0000000000..4458ebf095 --- /dev/null +++ b/docs/website/video-transcripts/OnZIaGXDZSo.txt @@ -0,0 +1,154 @@ +next we'll explore the comments ui +i've simplified the comments ui a bit +mostly to keep the code short and +understandable +i could support arbitrary depth threaded +comments and various other capabilities +but this creates some usability issues +eventually as space narrows and it +becomes harder to follow the discussion +thread +so i limited the replies to top level +comments only and try to simplify the +user experience a bit +i also used pearl shaped comments +instead of round rectangles they just +look nice and i left it at that +Text Field +let's check out the code +this is the text field where the user +can type +in his own comments to submit +comments are placed in a scrollable box +layout container +in the center of the form +when we press reply to a specific +comment its id is placed here +the border layout is needed so we can +place the text field input in the bottom +of the form +we don't add the text field directly +though we also add a send button with it +by wrapping them both into a border +layout together +i used static import of font image so +this line will work correctly +if the send button or the done button in +the virtual keyboard is pressed +we post the comment currently within the +text field +this adds the existing comment to the ui +Post Comment +there were a few methods mentioned in +this code that we need to go over +specifically post comment add comment +let's start with the former +we connect to the server so we need to +show a blocking ui dialog +we construct a comment object and set +the values to for it +notice we don't need the comment id as +that's generated in the server +the server api does the heavy lifting of +submitting the comment +when we are done we need to explicitly +clear the text field otherwise the +submitted comment would still be there +we animate the ui to valid state after +the addition of the comment +Add Comment +this naturally leads to the add comment +method +create comment creates the visual +representation of a comment we'll +discuss it next +the comment doesn't have a parent if the +comment doesn't have a parent +we'll just add directly to the list of +comments +we search through the hierarchy to find +the parent comment component +assuming we find it +we can nest the component +every parent +component comment component has a +container for replies children +that's nested into it +if the child container isn't there yet +we create it dynamically and add it +notice +that we add to a specific offset within +the container so the comments appear +below the parent +if the child container already exists +we can add to it directly notice that +since comments are already sorted by +time +adding to the end will always work +correctly and consistently +with that we effectively implemented one +level of comment nesting we can further +generalize the nesting logic to allow +additional or even infinite levels of +nesting but as i said earlier this might +have usability issues +Create Comment +finally we need a few additional methods +to implement the creation of the comment +etc +create comment generates the +component that represents this given +comment and potentially the reply button +the comment itself is just a text area +with a comment ui id +we make that text uneditable +fetch an avatar image for the chat +i could have used rounding and similar +effects but i chose to keep the code +simple +if we reply to a specific comment we +just change the id of the parent comment +the comment instance is stored in the +components client properties so we can +later connect them in the find +find parent comment method +Find Parent Comment +find parent comment loops through the +container and finds the component +matching this component id +if the comment we are looking at +has the same id +then we can return +this component +in this case the component matching this +id doesn't exist or isn't there yet +if this meth in this method we return an +image matching the user's avatar which +is exposed via slash user slash avatar +slash ur user id +on the server +Comment Form +with that comments form is nearly done +we need a css style for comment +it's +black text over light gray background in +perl rounded shape +the one thing to notice +is the padding +we need a lot of it to keep the text +away from the curve of the border +now commons form should just work once +we plug it in +to do that we need to go to create news +item in news feed container while we are +here +we might as well implement the like +functionality too +we set the like toggle buttons +selected state if the user already liked +this post we invoke like on a click +this is already implemented in the +server api so this is just one call +we add the action listener that invokes +the comments from a form and that's it +like and comments should work diff --git a/docs/website/video-transcripts/Otujb_KyofA.json b/docs/website/video-transcripts/Otujb_KyofA.json new file mode 100644 index 0000000000..f4f7c6ef0b --- /dev/null +++ b/docs/website/video-transcripts/Otujb_KyofA.json @@ -0,0 +1,8 @@ +{ + "line_count": 176, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 991, + "youtube_id": "Otujb_KyofA" +} diff --git a/docs/website/video-transcripts/Otujb_KyofA.txt b/docs/website/video-transcripts/Otujb_KyofA.txt new file mode 100644 index 0000000000..5fcb33d1d8 --- /dev/null +++ b/docs/website/video-transcripts/Otujb_KyofA.txt @@ -0,0 +1,176 @@ +notification is the last client data +class that maps to an entity +in that sense it will get more +interesting after this point +the class is pretty standard +again these map directly to the +client-side data +notification might relate to a post or a +comment so having its id here +might be useful to launch said post +when a notification is clicked +the constructor and dao match the +conversion of +the other classes +the rest is again getters and setters +the next step is the dao which again +maps very closely +there is literally nothing special here +we might as well have used the +notification entity to some degree +as you can see the rest of the code is +classic boilerplate +this is getting a bit more interesting +newsfeed represents the feed for the +user +i could have just created a really +complex join query on post and called it +today +but that's problematic +the news feed isn't just a list of posts +based on date +it's something that includes complex +a complex algorithm behind it +running that algorithm every time the +use of pages through data +might be very expensive +furthermore changes to the algorithm +shouldn't impact +past placements +we'd want consistency and we'd also like +to have a log +of what did the user see +in that sense constructing the news feed +as an entity in its own right makes a +lot of sense +we can place the articles into the news +feed using a sort order that makes sense +for a specific user without impacting +everyone we can tune and experiment with +article placement algorithms easily +because our processing can be done +offline on the table itself +we use an auto increment numeric id +since +this object isn't exposed to the client +side +at any stage the id doesn't matter as +much and this id type is faster +every entry within the news feed has a +user and post +notice that the user is probably a +different user +from the one who submitted the post +post day is the number of days since +epoch +this is useful for sorting i want +today's posts to always come before +yesterday's posts +even if they are higher quality +timestamp is also used for the sort +but to a lesser degree +rank can be manipulated by the ranking +algorithm to push good posts up +within the day +we have no doubt we don't need it this +is a server-side object +we do need the getters setters though +for the entity +the news feed also requires a crude +repository class +we need to use the auth and not the id +to prevent a case where another user +gets a peek at my feed +which might include friend only posts +with that we only have one more item +the shadow user is a potential user +of whom we have knowledge +but he might not be in the social +network +you ever wonder how facebook seems to +know so much about you before you even +signed up +they keep suggesting people that they +can't possibly know about +well +here's one of the tricks they use +when you agree to synchronize your +contacts to facebook and they nag a lot +about this +they upload the whole thing and store it +if you have friends that are already in +facebook you'll get suggestions +instantly but they keep everything +anyway +when one of your contacts signs up for +facebook they notice that you know and +instantly add a suggestion +this works in reverse too someone has +your email or phone number so you'd see +them as a suggestion +they go even further by associations for +instance +you might have a connection like that +through a third party so even if you +never synchronized contacts they'd still +know +i chose to implement a relatively simple +version of this and didn't include all +the metadata that facebook probably +stores +this is mostly a simple proof of concept +that should be enough for us to provide +friends suggestions +we again use auto increment for +an internal entity +this is the metadata we gather for a +user there is obviously a lot more we +can gather +this is the user who uploaded the data +so if the data in this record matches me +then i probably know the owner +we have a dow object but this dial is +right only +so the entity isn't exposed to the +outside world but the outside world is +exposed to the entity +that's why we don't have a get dial +method +we do have a special constructor type +that makes adding a new shadow user +easier +with the as with the other entities the +rest is just setters and getters +in a sense the shadow user dao is a bit +different from other dows +this is pretty standard now with two +bigger missions +id and owner both of which are missing +the idea is redundant as it's auto +generated the user is also redundant as +it's always the user that uploaded the +contacts there is no point of including +either one of those +values in the dao as they won't arrive +through through it from the client +the rest of the dao is just getters and +setters +in this class in this case the crude +repository is actually pretty +interesting we need it for the case +where we create a new user or update +data +we need to find all the related users +given a user's phone we look through the +database for users who might have that +phone in their contacts the same is true +for email +we check if a user has uploaded contacts +in the past +by counting his entries assuming he has +we will try to merge the data +we use the full name of the user when +merging contact data +with that we finish the entities and we +are finally ready to move up the food +chain to the service layer diff --git a/docs/website/video-transcripts/PERdJq3I-As.json b/docs/website/video-transcripts/PERdJq3I-As.json new file mode 100644 index 0000000000..be719d64e1 --- /dev/null +++ b/docs/website/video-transcripts/PERdJq3I-As.json @@ -0,0 +1,8 @@ +{ + "line_count": 77, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 429, + "youtube_id": "PERdJq3I-As" +} diff --git a/docs/website/video-transcripts/PERdJq3I-As.txt b/docs/website/video-transcripts/PERdJq3I-As.txt new file mode 100644 index 0000000000..63fd8f60c3 --- /dev/null +++ b/docs/website/video-transcripts/PERdJq3I-As.txt @@ -0,0 +1,77 @@ +it will take a bit of work to implement +the server and proper abstraction +however it's a good idea to put things +in the right place to begin with +using a server api class will help us +organize things correctly +piece by piece +we can then replace mock-ups with +implementations and see things working +almost instantaneously +currently the server api class +serves as a mock-up it's a +static class that hides the server +connection +i'm +blocking api calls within this class as +they are easier to integrate into the +infinite container which i'll introduce +soon the currently logged in user is a +global state in the application which is +a pretty standard practice +i've set up a few images from the +diverse ui project so we can have fake +users with avatars in the app +check out the diverse ui project at +diverseur.com +its goal is to feature more diversity in +app mockups +we need times in the timeline etc to +start from a reasonable enough date so i +can use +now as the baseline +we'll set up four user objects +that we can use soon +this method returns the current user +after lazy initializing it +this is the shorthand builder +constructor where i set a hard-coded +user +i add friends and people i may know +which are used later in the friends view +this method fetches the posts in the +current timeline +the method accepts time of the last post +so we can fetch the posts +since that time +we add a comment to the comment count +so the comment count wouldn't be zero +there is still no comment view though +the post itself doesn't include much +either just some html +for the post content +when we return null when there is no +further information that's it we are +almost ready to implement the news feed +before i start with the news feed itself +i'd like a couple of utility methods +for that i'll add a ui utils class as +such +i use these to present time logic more +efficiently +these create a gray spacer that's used +in several places in the facebook ui +this method formats time as statements +such as just now 15 minutes ago etc +then falls back to standard date and +time values +we use this when displaying time on +posts we'll use these in the newsfeed +container class +but before we get there +we have +the missing css statements for the ui +ids we used in the listing +these separators have a gray background +and right amount of padding to match the +ui diff --git a/docs/website/video-transcripts/PHb5YO77OQk.json b/docs/website/video-transcripts/PHb5YO77OQk.json new file mode 100644 index 0000000000..9e48403180 --- /dev/null +++ b/docs/website/video-transcripts/PHb5YO77OQk.json @@ -0,0 +1,8 @@ +{ + "line_count": 120, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 666, + "youtube_id": "PHb5YO77OQk" +} diff --git a/docs/website/video-transcripts/PHb5YO77OQk.txt b/docs/website/video-transcripts/PHb5YO77OQk.txt new file mode 100644 index 0000000000..5639d769ab --- /dev/null +++ b/docs/website/video-transcripts/PHb5YO77OQk.txt @@ -0,0 +1,120 @@ +there is a fine line between doing a +mock-up and implementing a data model +once we have a ui in place +designing the underlying data model +becomes a puzzle of filling in the +missing pieces +before we go into the news feed i'd like +to create the object that represents +users and posts having these in place +will make things easier when we want to +implement the server +we'll use property objects which make +server communication and caching trivial +first we'll start with the user object +we will need a new package to separate +the data model i went with com +codename1.fbclone.data +so what do we need for user a lot of +these things are obvious from a sign up +the signup ui +first name +family name +email +phone +gender and birthday +we also need a unique id since security +is crucial +i will go with a string based unique id +we also need a picture of the user to +show next to his posts so an avatar +entry will also help +this is all easy to express in a +business object class +these properties match the things we +said we needed to know about the user +dates are easy to store as long as value +has long values since epoch +getting the full name is a pretty common +thing so it makes sense to provide this +helper method +everything about this class is really +simple except for the avatar +it should point at an image url but how +would we use it +to simplify that i'll add a get avatar +method to user that will include the +functionality of fetching slash caching +slash resizing and shaping the avatar +image +the method accepts the image size in +millimeters and returns an image that +matches that size +we have an image in cache for this +for this user we'll return that image +we load the image from storage if +loading failed due to corrupt image +we'll delete the image file so the next +call to this method will recreate it +we create a mask image that's a +white on black circle that we'll use to +crop the image so it will appear +circular +we use the material design font image of +a person to create an avatar placeholder +image +masking doesn't work with font image +so we convert the font image to a +regular image and mask it +we have an avatar url +we fetch the data from the url into a +file and mask using the given image +automatically +this isn't trivial +but isn't hard either it will produce +the round avatar image we see in +facebook +but we aren't done yet +we need to +do the post object +for the news feed which lists posts on +the wall +again we can refer to the ui to decide +on the content of this class +user is the person who wrote the post +date +title +content +visibility is it public friend only etc +styling we can configure the background +image color of the post +comments +and likes +we'd also need an id for the post as +before likes can't be a numeric count +since each user can only like once +we need a list of users who liked the +post +comments should be represented as a +comment object as well +i'll get to that soon +this is the post class +again the class matches the ui almost to +the letter +comments and likes use list property +to handle multiple entries under a post +this raises the obvious question about a +comment since we aren't close to +implementing threaded comments yet i'll +have to guess about the content of this +entry +comments are bound to a parent post +they can be posted by any user notice i +use ids instead of objects +this will be simpler during passing of +the data +the idea of the parent comment or null +this allows us to implement nested +comment threads +we'll need one final piece and the data +model for now diff --git a/docs/website/video-transcripts/PRk7EhXQRqs.json b/docs/website/video-transcripts/PRk7EhXQRqs.json new file mode 100644 index 0000000000..d965be55df --- /dev/null +++ b/docs/website/video-transcripts/PRk7EhXQRqs.json @@ -0,0 +1,8 @@ +{ + "line_count": 213, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1134, + "youtube_id": "PRk7EhXQRqs" +} diff --git a/docs/website/video-transcripts/PRk7EhXQRqs.txt b/docs/website/video-transcripts/PRk7EhXQRqs.txt new file mode 100644 index 0000000000..7dea04cff7 --- /dev/null +++ b/docs/website/video-transcripts/PRk7EhXQRqs.txt @@ -0,0 +1,213 @@ +setting up a server in the cloud is +probably one of the most tedious tasks +we need to do +even if you are a corporate developer +who never has to do this i think there +is a lot of value in understanding what +goes into this +this module is aimed at a quick and +dirty deployment the goal of this +tutorial isn't to teach you scaling or +complex ideas +just just to follow through all the +before we begin we need to make some +changes to the pom file +this is one of the coolest features in +spring boot and that says a lot +you can generate an executable binary +script that's installed in front of the +jar +that means you can literally type the +name of the jar file on linux and it +will run +no java minus jar or anything like that +this might seem trivial but the truly +amazing thing is that you can use it as +a service on linux +which means you can bind it to common +system services in etc in a d +if you are not deeply familiar with +linux that might not seem like much +but it makes setting up the server much +easier +App Engine +i'm a big believer in sustainability +if running your startup's backend is +five dollars per month +that gives you a pretty decent chance of +running without breaking the bank +a lot of startups including ourselves +make the mistake of over scaling +we used app engine which was very +alluring at the time +the problem is that costs quickly +ballooned to thousands of dollars per +month +which was totally unjustified +using a vps is safe and scales with the +need of your business +but let's go on and talk about the +server itself +Server +i used mariadb instead of mysql in +production +on the desktop i preferred mysql since +it had a better installer for mac os 10. +mariadb is a fork of mysql that is +pretty much compatible +i normally prefer to have a similar +environment on production and +development +but in this case i use a mac instead of +linux anyway +so i just went with it +SSH +i use ssh to log into the server +if you use linux or mac this is just a +command line +for windows users there are a lot a lot +of free ssh clients in the wild and you +can use them +Linode +we'll start on the linode site +i'm skipping some of the points related +to signer billing etc +i just select the vps type which in this +case is the cheapest option +i create a new server instance +Operating System +once the server is active we need to +install an operating system +Deploy +i choose to deploy a new image which in +this case refers to the disk image +effectively this means operating system +install +i choose centos because i'm used to it +and it's well supported +i type in the root password +which is something you need to remember +so don't lose that +then deploy +once i do that the server will take some +time to set up +after it's done +press boot to start up +the new vps +User vs Root +once the server is booted you can ssh to +it with your root password notice that +you should normally use a user rather +than root as usage of the root user +should be restricted to a minimum +root is all powerful +you want to reduce usage of root for a +few reasons +the obvious one is that mistakes and +root can be costly +but another reason can be that if your +system was compromised with lower +privileges +a hacker has a good chance of elevating +these privileges +every time you use root +a few years back +apache was hacked +and +the hacker +relied on a vulnerability in a script on +the site to gain access +the thing is the script was running +under a regular user that isn't rude so +he couldn't really do much +he uploaded an exploit that would give +him access to root +only if a root user +executed a specific command +and bingo he got rude had someone not +used root +the hacker would have been stuck in +purgatory +for a long +time +Create a new user +so +the thing we need to do when we get to +the server +is create a new user called builder +we'll use that user to run everything +you will notice the sharp character +that is the command prompt of +a root +user on unix +Passwd command +when root invokes the passwd command it +doesn't ask for the existing password +as +root can override can override that +just type in the new password and verify +it +in linux we install packages and +dependencies are resolved automatically +the underlying system in centos is rpm +which stands for red hat package manager +centos is a fork of red hat +yum takes that to the next level by +connecting to servers and searching for +the rpm packages +and all the dependencies +and then +fetching everything you need to install +wget is a tool that can perform http +requests to download a file from command +line +since we don't have a +display opening a browser isn't really +practical and this is reasonably +convenient +jdk hack +this is a bit of a dirty trick +if you try to download the jdk you will +notice there is no way to get the +download link +oracle hides it until you check a box +which means you need to set +a cookie to get it +that's really annoying and the only way +i found to download the jdk directly to +the box +was this hack +the alternative which i've used in the +past is to download the jdk onto my mac +and then use the scp command to copy it +to the server +that's really slow as i need to download +and then upload the file +scp is secure copy and it allows copying +files over ssh +i'll discuss it a bit later +yum jdk +you remember i said yum fetches things +automatically +so you would expect to do something like +yum jdk and have the jdk installed +technically that would work and might +even work here +all linux distributions ship with +openjdk +which is the open source version of java +it's pretty similar but not identical to +the oracle jdk +that we had to download +one of the main reasons i did that this +was support for javafx +which doesn't exist in the jdk +we need it +for +the css compiler +and i wanted it to work +so i'm still using yum instead of rpm +directly to install a package +using yum is important as it can update +its installed database and resolve +dependencies diff --git a/docs/website/video-transcripts/PbRbzCGQCQE.json b/docs/website/video-transcripts/PbRbzCGQCQE.json new file mode 100644 index 0000000000..874d25b7c9 --- /dev/null +++ b/docs/website/video-transcripts/PbRbzCGQCQE.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 151, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/035-building-a-desktop-version-of-the-kitchen-sink.md", + "status": "transcript-fetched", + "word_count": 881, + "youtube_id": "PbRbzCGQCQE" +} diff --git a/docs/website/video-transcripts/PbRbzCGQCQE.txt b/docs/website/video-transcripts/PbRbzCGQCQE.txt new file mode 100644 index 0000000000..7dbb66b01a --- /dev/null +++ b/docs/website/video-transcripts/PbRbzCGQCQE.txt @@ -0,0 +1,151 @@ +so we ran the kitchen sink as a mobile +app using the simulator +and that's great +before going to android and ios let's +try running the app as a desktop app +this will be a relatively simple +exercise that can help us understand the +more nuanced android and ios processors +as i mentioned before +we don't have a real main class we only +have the bootstrap codename one main +class +which we can't run as a standalone app +so we will need to create a new main +class +which i will place under +a directory called stubs +the term sub in codename one +is +one we use internally to refer to the +ios bootstrap glue code +in this case i place the stub under +stubs slash desktop slash src com +codename one demos kitchen +there is a bit of code here in the stub +so i'm going to go over the various +lines +it's basically a swing app that embeds +codename one so you can enhance the stub +with everything available under java se +i included import statements because +they refer to classes in java sc +and that might be confusing in the stub +it makes sense to import the +implementation code since this code has +no portability to other platforms so i'm +importing the javasc port directly +for simplicity i hard-coded the app +dimensions but it's pretty easy to +create more elaborate heuristics and +window positioning using swing +if your theme doesn't derive from native +this doesn't matter however if it does i +made it use the ios 7 theme which is the +default ios theme +this will make the app feel a bit more +like an ipad app +the javasc port is used for the +simulator too +so things like network monitor and other +simulator features might still be on if +this is a developer machine +so here we disable all of those flags +this is the location for the storage +file under the user home directory +in the simulator this is always.cn1 +but it makes more sense to use your app +name for real world apps +the simulator hides the full file system +and shows a fake one under the dot +cn1.hierarchy +this disables that behavior so getroots +will return the file systems in your +machine +the simulator does that by default to +make it feel more like a real device +when running on a desktop you would +prefer showing a tablet ui rather than a +phone ui +this means that his tablet and his +desktop would both return true +swing is also single threaded +this invokes the run method +on the swing thread so we can construct +the swing ui +we create a swing jframe and then +initialize codename one with the content +pane of that frame +these are display properties we rely on +in code +they aren't essential but it's good +practice to have them +the frame size and position is sized and +positioned +we bind a window listener for the app +close event +we make sure to call stop and destroy on +the main class before exiting +notice we invoke these methods on the +codename one thread +we go into the codename one thread to +invoke start and init once those are +done we call the method again on the +swing thread and show the frame itself +i took a bit of a shortcut by reusing +the same runnable instance for both +callbacks +now that this is out of the way +we are nearly done +we just need to compile that code and +link it with the javasc port code since +we already have an ant build xml file +this is probably the easiest way to do +that +i added a new target to the build.xml +file that looks like this +i want to make sure +that we are working with a clean slate +when running this build +there are two important things to notice +in this statement i don't use the boot +class path since the app is now a +standard javasc app +the classpath points at javasc.jar +because i'm compiling against an +implementation +not an api +notice that the stubs slash desktop +directory is added to the compilation +process so that would get bundled too +we copy the theme from the codename one +repo so the native theme will look like +ios +the final step is the executable jar +generation +for a jar to be executable we need the +main class in the manifest which i +define here +we need three things in the jar +first we need the concept contents of +javasc port which is already available +in the cn1 project notice i exclude the +skin file from there as we don't need +the simulator and will just increase the +jar size +the next thing we need are the compiled +sources of the kitchen sink project and +kitchen sink stub +and finally we need the resources +defined within the kitchen sink project +so i package them directly from the src +directory making sure to exclude the +java source files +after going through all of this i now +have a desktop app that i can run +using this +notice that this is a self-contained fat +jar +with no external dependencies which +means it should work well for tools such +as java packager etc diff --git a/docs/website/video-transcripts/Q0fkApAGMZA.json b/docs/website/video-transcripts/Q0fkApAGMZA.json new file mode 100644 index 0000000000..3ec70196e1 --- /dev/null +++ b/docs/website/video-transcripts/Q0fkApAGMZA.json @@ -0,0 +1,8 @@ +{ + "line_count": 125, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 697, + "youtube_id": "Q0fkApAGMZA" +} diff --git a/docs/website/video-transcripts/Q0fkApAGMZA.txt b/docs/website/video-transcripts/Q0fkApAGMZA.txt new file mode 100644 index 0000000000..470159f3f8 --- /dev/null +++ b/docs/website/video-transcripts/Q0fkApAGMZA.txt @@ -0,0 +1,125 @@ +we'll move on to some additional forms +starting with the billing form +the billing form like the details form +is a relatively simple cookie cutter +form +today i might have made it with the +instant ui +and would have been done with it +but when i wrote this code we didn't +have that class yet +despite the fact that the ui looks +simple the layout is non-trivial +first we have a border layout that +encloses the form +we use that to keep the green button at +the bottom of the form regardless of +size or scrolling +next we have the content container which +is a box y container that includes the +whole user interface except for the +button +we mark it as y scrollable +further down in the code so we can +scroll through elements within its +within +it's important that text fields always +reside in a y scrollable container +so the virtual keyboard will have space +to work with +and finally we have the table +the numeric values are too small to give +them each a line so i packed them in a +2x2 table +the entire table is added to the content +later on +notice that the table container isn't +scrollable as nesting scrollable +scrollables is a +mobile +bad idea or bad practice +we don't have scroll bars so when we +swipe the implementation needs to guess +which scrollable we actually mean +and that might not end well +as i mentioned before +this code predated the binding api +and didn't use it +so we have some helper code such as the +adds as double method which +automatically parses the text value and +if it's not a double returns the +existing value +this allows us to automatically update +the property +binding would have made this code +simpler and shorter +next we add the elements to the table +i'm not really sure why i chose to do a +2x2 table with box layout instead of a +2x4 layout +so we can place everything directly into +the table +possibly it was force of habit +both approaches work though +the separator line allows us to +differentiate between the brain three +arguments and the rest of the billing +details +i've mentioned this before but it bears +repeating a separator hair is just a +component with one pixel padding and a +grayish color +normally a label without text is hidden +by default +but if we invoke +set show even if blank +it will be rendered even if there is no +text or icon +there isn't all that much here in the +final part of the billing form +it's just the things i've mentioned +above like the scrollable y of the +content pane and its placement +in the center +now let's look at this at sending this +data to the server side +up until now when we sent data it didn't +include all the information we needed so +we created a new version of the app +settings call with the restaurant +details this allows us to still reuse +the existing object from the restaurant +model +this is a bit of a hack so +let's get go over what i did here +in the app settings class i get the +value of the object as a map object i +then add all the properties from the +restaurant object to create one large +json object +containing everything i need in the +server which doesn't have a +distinction between the two +then i'll remove the cart and menu +entries these entries are big and i +don't need them for the specific api as +they have their own model +i also remove the logo +and title background images as +they have separate web service +you might recall from before +that basic things like +title tagline etc weren't persisted +anyway well +now we have a bind to preferences method +that just implicitly saves and restores +from preferences +that way restaurant data is implicitly +saved locally in preferences +it's not sqlite but it works and saves +some of the effort today after the +improvements we have in the sql map +i might have used that +or i might have used the seamless +preferences persistence we have built in diff --git a/docs/website/video-transcripts/QY7josfVbdg.json b/docs/website/video-transcripts/QY7josfVbdg.json new file mode 100644 index 0000000000..04d6a6a658 --- /dev/null +++ b/docs/website/video-transcripts/QY7josfVbdg.json @@ -0,0 +1,8 @@ +{ + "line_count": 246, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1403, + "youtube_id": "QY7josfVbdg" +} diff --git a/docs/website/video-transcripts/QY7josfVbdg.txt b/docs/website/video-transcripts/QY7josfVbdg.txt new file mode 100644 index 0000000000..406c9c0ee7 --- /dev/null +++ b/docs/website/video-transcripts/QY7josfVbdg.txt @@ -0,0 +1,246 @@ +the entities section was long +boilerplate heavy and a bit boring +so while the server is never as +interesting as the client +the services layer is still far more +interesting +services encapsulate the generic +business logic of the application +this is the part of the code that +includes the intelligence of the +application +the entity layer is about storage only +the web service layer is about +connection to the client and this layer +is about things the app needs to do +we'll start with the obvious service +user service it encapsulates all the +things a user will need +let's start with the fields of this +class first +we include the user repository shadow +user repository and media repository +as we manipulate these data stores +directly +for notifications we work with the +service instead of +writing to storage notifications can be +sent via push later and using this +approach can make that seamless +the password encoder is used to hash the +password values in the database +we'll store property values here so we +can keep api keys outside of the source +code +the rest of the code has many methods +i'll break them down one by one +the login method returns an instance of +the user object after validating the +user it accepts an email or phone as +identification and the password for +authorization +we query either by email or by phone +either way it should result in a user +list +assuming a user was found in the query +we verify there is only one this is just +a to be sure scenario +we use the encoder tool to check the +password +since the passwords are hashed in the +database this is the only way assuming +the password and hash match we return +the login dao which should also include +the token +for the other cases we throw the login +exception this obviously raises the +question of the login exception it's +just the simple exception subclass we +just used this exception to mark the +type of error for the web service +up is probably the most challenging +method in this class if only because of +all the external dependencies it brings +to the table +let's go over the method and then go +into the dependencies +as we create a user we want to see if we +have shadow user listings so we can +suggest friends to him immediately +if the user is registering by email we +try to find that email +and fail with the right exception +assuming all is well we search the +shadow user list for the email +we do the exact same thing with a user +that logs in with a phone +notice that only one of the two should +have a value +since a user can be updated via sign up +or update the logic for updating from +the dao object is common in set props +which i'll cover soon +we hash the password into the database +this will generate a large random +looking secure sorted hash string notice +that password and token are updated +manually outside of set props +the auth token is generated here right +now i keep it simple but a secure app +will also invalidate the token +occasionally +verification code is saved to the +database here we'll discuss this method +soon +if we have shadow user entries we build +up a potential friend list +the new user object is stored into the +database +the email or sms for the activation code +is sent out right now right here +notice we return the login dial instance +and not the dao that was a lot but there +is a lot more +the methods set props create +verification code send activation email +and send activation sms were mentioned +in the code let's go over them +first in line is setprops it serves as a +generic setter that accepts a dow object +and copies its values into the user +entity +this might have made sense in the user +class +i placed it here because it's the only +place i need this method +notice that only basic features are +updated nothing secure or risky set +props might be invoked in a case of an +update and we don't want to accidentally +change a value that we shouldn't so the +code is trivial to +prevent accidental modification of +something important +next we have create verification code +which generates the random verification +code +we accept the number of digits to +generate +a random digit between 0 and 9 is +generated for every length and appended +to the strength +to the string +this is trivial so we can move to the +more interesting methods +this method uses the twillow web service +to send an sms message to a given number +we need the api keys to invoke the api +i'll go into those soon +technically we can do this just once but +for convenience i stuck it here +this is the api call to twillow's sdk it +sends the sms from the given phone +number to the device with the given text +the api key for twillow and mailgun are +stored in a file on disk so we don't +have to include them in the code this +method loads that file and caches it +we lazily initialize the variables we +saw earlier +we load a file called +fbclonekeys.properties into the api keys +this file should be in the user's home +directory +since this is pretty basic i'll convert +the exception to runtime exception as i +don't want to deal with checked +exceptions here +the next step is filling up this +properties file +we sign up to twillow.com and +mailgun.com to get send credentials for +the properties file i won't go too deep +into the whole process as it's a +standard signup process and the +respective documentation support will +probably serve you better +this is the properties file containing +the attributes we need from twillow and +milgan +all of these values should be available +from their website once you sign up +notice that the values in the slide are +random +and +won't work for you +the last remaining piece is the email +activation option +we could use a tool like +javax.mail which is available in spring +but that might be a problem getting an +email into people's inboxes is far +harder than just the basic mail api +luckily there are several companies that +specialize in email delivery +deliverability ranging from sendgrid mel +gun and even aws +i've explained working with sendgrid in +the past so i thought i'd give milgan a +try +pretty much all of these tools are the +same you need to sign up and configure +your domain that's the hardest part and +it's pretty hairy +i suggest following the instructions on +the site all sites includes instruction +for working with java and all of them +work out of the box with spring boot +this code was literally copied from the +mail gun introduction tutorial +the one change is the properties object +pointing at the api keys +we return a result from the mail gun +servers but for now we don't really use +it at at this time +now that this is done signup will +automatically send an email or an sms +based on the data submitted in the user +signup +when we make a change that might impact +the user object such as uploading +contacts etc +we would like to update the locally +cached user object on the device +that's exactly the purpose of refresh +user +you might think +why not log in again +that's simple we don't cache the +password as that would be insecure +but we can refresh the user object with +a token which is probably more secure +the method itself is trivial we just +find the user and return the login dial +which includes all the extra private +details +after signup is complete the user needs +to verify that he got the right +activation code this is done with the +verify method +verification succeeded +and the value is in the database matches +the code +verification can only be used once +otherwise a user can change the phone +email +and reuse the same code without +validating +if verification failed we need to notify +the client-side code +update is another trivial method we +reuse this code so it's common between +update and sign up +updating the password is optional +if it's null +we don't want to override the existing +password diff --git a/docs/website/video-transcripts/Qw7moLv-dE4.json b/docs/website/video-transcripts/Qw7moLv-dE4.json new file mode 100644 index 0000000000..71650a5d06 --- /dev/null +++ b/docs/website/video-transcripts/Qw7moLv-dE4.json @@ -0,0 +1,8 @@ +{ + "line_count": 152, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 801, + "youtube_id": "Qw7moLv-dE4" +} diff --git a/docs/website/video-transcripts/Qw7moLv-dE4.txt b/docs/website/video-transcripts/Qw7moLv-dE4.txt new file mode 100644 index 0000000000..08e2cdff67 --- /dev/null +++ b/docs/website/video-transcripts/Qw7moLv-dE4.txt @@ -0,0 +1,152 @@ +in this module we will persist data +locally +so we don't have to go to the server to +fetch data every time +we'll use sqlite +not because it's the best storage medium +but because it's something i want to +teach +i'll use the automated properties +binding api +to map objects to sqlite +which makes working with sqlite far +easier if i didn't have that api i might +have just skipped over sqlite altogether +as working with sql +is unpleasant and error prone +in order to get the mapping right +i had to move some properties to getter +and setters +as at the time i originally did this +code +there was no option to exclude an entry +from storage +this is no longer the case and we can +now mark a property for exclusion +but changing that would mean changing +all the following modules and that's not +necessarily something i want to do right +now +i used a local abstraction +for all the persistence code to make it +more domain specific +which is a good practice in general +app storage abstracts the process of +saving the objects +so +right now it's implemented using sql map +but in theory we could replace that +let's review this class +the class is implemented as a singleton +and they initialized on first use +initialization creates the db instance +that is kept within the application +we create the sql mapping instance +to the database +notice +that the sqlite api doesn't have +separate methods for create or open +and does the former implicitly which is +pretty convenient +next we create the tables if they don't +exist +we create blank object instances +for the objects that we need +then define the primary key field +i also set the column type for price to +be double +this is no longer necessary as newer +versions of the sql map api detect the +double property and automatically set +the right type +create table +literally issues a create statement +notice that we don't support alter at +this time +although this might be introduced in the +future +insert and update are pretty much direct +wrappers to the built-in methods that +handle the exceptions locally instead of +propagating the i o exception +from the sqlite api +the reason for the delete listener +is that we want the dish list to +automatically remove the dish +when it's deleted by the edit form +this allows us to decouple that code and +get an event from the underlying data +model +we use the event dispatcher class to +subscribe and fire an event here +this is a really convenient class when +you are building an api +delete serves pretty much the same +purpose as update and insert with the +exception that it also fires the +dispatch event appropriately +app settings is a special object +as there is only one of it so when we +issue a select query +we should never expect more than one +entry +however it's still a select query +by default when we invoke the select +method +if a property value isn't null it's +added to the where clause +both name and tagline default to +non-null values +and so we need to set them to null +once that's done +we can just do the select query as usual +if there are no entries we just invoke +insert +for a new settings object +fetching the list of dishes is similar +but more standard as we return the full +list of dishes +the main point of interest is the use of +the name property to indicate the order +by option +for the select statement +some dishes might not have an image +within them +as we create them +and here we force a placeholder image to +prevent such a case +i would recommend that you always use +that strategy and have a good looking +placeholder image +now that we have the basic persistence +object in place +let's proceed to integration +but first let's discuss a conceptual +issue +why do we need persistence to begin with +technically +we shouldn't have a local database as +all the data is in the server and local +data is meaningless +in theory we could enable working +offline but that's not a current goal +and would create many complexities i +want to avoid +there is only one goal for this code and +its performance +users and developers often conflate +performance with something that has to +do with bits and bytes +but it's really about decisions we make +as developers +do we go to the server or do we use +local data do we cache +in this case it can create +a conflict between the local and remote +database +we need to guard against that obviously +but once +data duplication exists +we open up a risk +this risk is worth it for this level of +performance diff --git a/docs/website/video-transcripts/RBYGdCllnww.json b/docs/website/video-transcripts/RBYGdCllnww.json new file mode 100644 index 0000000000..8f2e7078ea --- /dev/null +++ b/docs/website/video-transcripts/RBYGdCllnww.json @@ -0,0 +1,8 @@ +{ + "line_count": 61, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 322, + "youtube_id": "RBYGdCllnww" +} diff --git a/docs/website/video-transcripts/RBYGdCllnww.txt b/docs/website/video-transcripts/RBYGdCllnww.txt new file mode 100644 index 0000000000..69901b2782 --- /dev/null +++ b/docs/website/video-transcripts/RBYGdCllnww.txt @@ -0,0 +1,61 @@ +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/video-transcripts/Rat-FDqDCTU.json b/docs/website/video-transcripts/Rat-FDqDCTU.json new file mode 100644 index 0000000000..fbd1425229 --- /dev/null +++ b/docs/website/video-transcripts/Rat-FDqDCTU.json @@ -0,0 +1,8 @@ +{ + "line_count": 103, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 624, + "youtube_id": "Rat-FDqDCTU" +} diff --git a/docs/website/video-transcripts/Rat-FDqDCTU.txt b/docs/website/video-transcripts/Rat-FDqDCTU.txt new file mode 100644 index 0000000000..70ab8eefc0 --- /dev/null +++ b/docs/website/video-transcripts/Rat-FDqDCTU.txt @@ -0,0 +1,103 @@ +Facebook has a splash screen which takes +a short time to load the login UI I'm +assuming Facebook tries to figure out as +much as possible about the device and +its owner before showing the login UI +since we don't have such capabilities +which I personally find disturbing we +might not need a splash screen at all +we can go directly to the login UI +however this isn't ideal and we should +show a splash screen the first time the +app is launched +iOS applications take a while to load +they feel much faster because they show +a splash screen when they first load +this means the image of the UI appears +almost instantly and is replaced by the +actual UI once the loading finished +to the use of this seems seamless in a +well-crafted app +codename 1 runs the application on the +Mac Cloud servers 16 times as of this +writing to produce 16 separate +screenshots matching the various device +configurations +some device configurations include both +landscape and portrait mode for instance +iPads high resolution phones +Etc +as a result the numbers add up to 16 +separate screenshots +notice that this is subject to change as +we are moving toward an automatically +generated native splash screen in iOS +but since that isn't there yet at this +time this is still relevant +for a typical application that doesn't +require login this isn't a problem it +can show the UI of the first form +for instance in the case of Facebook we +show the feed without any data +this would give a sense of loading data +which would pop in once loading finishes +unfortunately Facebook's login UI looks +very different from its feed UI +so the first activation would seem odd +if the feed UI was replaced with the +login UI +in that case we'd rather have a splash +screen a pair for the screenshot process +since the splash screen is pretty much +identical in the native iOS slash +Android implementation +there isn't much we need to do in terms +of design +first we need to add a new UI controller +class that will handle the UI navigation +and hide the underlying forms +here we can implement the splash screen +and later the navigation to additional +forms +this can be static as it happens once +and we can control it +using absolute Center the logo will be +in the middle of the screen for all +devices slash resolutions +we defined this UI ID earlier it uses +the Facebook icon font that means that +the f308 character will render as the +Facebook logo +this is important for the morph +transition we use that to animate to the +first form +when departing this form we will move +the component named logo in this form +to assume the position slash appearance +of the logo component in the destination +form +the duration of the transition is 1200 +milliseconds +this animation controls an effect where +the logo appears translucent and +gradually becomes opaque +every 20 milliseconds we update the +opacity of the logo until we finish +which should take 1 000 milliseconds +this will create an effect where the +logo fades in and then moves from the +center of the screen to the top or side +in landscape mode +when we are done we show the login form +which I currently implemented as a stub +or we now need +is one additional CSS entry to support +this +we set the background color to the blue +of the splash screen +we now need to bind this to the main UI +so we need one change to the main class +Facebook clone I replace the default +High World form with this call +pressing play should result in a splash +screen fading in diff --git a/docs/website/video-transcripts/Rok0Ubr4xes.json b/docs/website/video-transcripts/Rok0Ubr4xes.json new file mode 100644 index 0000000000..8099d0a06d --- /dev/null +++ b/docs/website/video-transcripts/Rok0Ubr4xes.json @@ -0,0 +1,8 @@ +{ + "line_count": 116, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 693, + "youtube_id": "Rok0Ubr4xes" +} diff --git a/docs/website/video-transcripts/Rok0Ubr4xes.txt b/docs/website/video-transcripts/Rok0Ubr4xes.txt new file mode 100644 index 0000000000..43d7b92f0d --- /dev/null +++ b/docs/website/video-transcripts/Rok0Ubr4xes.txt @@ -0,0 +1,116 @@ +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/video-transcripts/RsVOanGYjqo.json b/docs/website/video-transcripts/RsVOanGYjqo.json new file mode 100644 index 0000000000..c076ac19dd --- /dev/null +++ b/docs/website/video-transcripts/RsVOanGYjqo.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 156, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/037-build-an-ios-native-version-of-the-kitchen-sink.md", + "status": "transcript-fetched", + "word_count": 845, + "youtube_id": "RsVOanGYjqo" +} diff --git a/docs/website/video-transcripts/RsVOanGYjqo.txt b/docs/website/video-transcripts/RsVOanGYjqo.txt new file mode 100644 index 0000000000..b6eaec2660 --- /dev/null +++ b/docs/website/video-transcripts/RsVOanGYjqo.txt @@ -0,0 +1,156 @@ +the ios app is surprisingly easy when +compared to the android app +there are some nuances but overall the +process is pretty simple +first we need to compile the vm and port +so we need to go back to the cn1 +directory and execute these commands +we also need retro lambda which we can +compile from +github.com +orf jackal retro lambda or download the +binary from +oss sonotype dot org slash contacts +groups slash public slash net slash off +jackal slash retro lambda slash retro +lambda +i won't go into the compilation process +of retro lambda as i just downloaded the +binary +i created a new kitchen sync ios folder +and placed the binary there +you can run retro lambda with the +following command +once this command is complete a version +of kitchen sync classes targeting jdk 5 +resides under the kitchen sync classes +directory +i won't go into details about this +command as i think you would be better +served by the documentation in the retro +lambda project +the next step is to create and compile +the step class +which we will use to launch the +implementation +in the kitchen sync ios folder i created +a step directory i then added +the kitchensync stub dot java to that +directory +the one interesting thing in the stub is +the fact that the implementation invokes +the runnable callback when it's ready +everything else is +really trivial +this can be compiled using this command +notice that i used source +1.5 level when compiling this is crucial +as the vm doesn't support java 8 +directly +we use retro lambda to add support for +java 8 language features but don't do +that on the stub +once we do this we can create the output +folder +xcode proj and run the vm translation +code +there is a lot to digest in this command +so i'll break it down to one argument at +a time +this should be pretty easy we are +running the vm translator it's just an +executable jar +this represents the vm target os this is +currently the only supported option +in the future we might support +additional targets for the vm +next we have the path bash understands +this as multiple commands so we have to +put the entire path in quotes +these are the individual elements within +the first element is +the in the path is the stubs directory +where i compiled the kitchen sink stub +before +the second element is the classes of the +vm +this covers the java core classes +supported by codename one +these are the actual classes of the java +app after they went through the retro +lambda process +these are the classes of the ios port +implementation +these are the core implementation +classes of codename one itself +these are objective c sources that +implement the native code referenced +from the ios port +this is the target output directory we +created above +this will include the output project +this is the name of the stub class we +created +this is the package name of the step +class and the application itself +these are the title and version numbers +of the application +this is the target type ios indicates a +universal ipad iphone app but you can +specify iphone or ipad here +and finally these are the included +native libraries +frameworks codename one requires these +two by default +now that this is all done we have an +xcode project under xcode proj +dist +we can double click the project to +launch xcode this instantly opens a +ready to run file but first we need to +fix a few things +the first step is picking the main +project area +and select build settings then click +architectures entry and select standard +architectures +the next step is font registration +we need two fonts +one is handly which is used in the demo +the second is the material design font +which is used for common icons in +codename one +to do this we need to select the build +settings tab +and in it add the attributes for fonts +by clicking the small plus button next +to one of the attributes +in ios you need to declare ttf files +included in the project and can't use +them otherwise the build servers do that +automatically +you should be able to type fonts +provided by application and xcode +will offer that string to you as you +type +once you select that you should have one +entry called item0 and you should be +able to add another one +type in the font file names +for the project specifically +materialdesignfont.ttf +and handleyregular +dot ttf +notice that these are case sensitive +notice that this step is currently +necessary but it might be unnecessary in +the future +we need to update the two sib files in +the application +and select the deployment target as 8. +notice that xcode sometimes fails to +accept the selection +and i personally had to restart it twice +now that all of this is done you should +be able to press play in xcode and run +the codename one application diff --git a/docs/website/video-transcripts/Rt68rA2c6A8.json b/docs/website/video-transcripts/Rt68rA2c6A8.json new file mode 100644 index 0000000000..41e6f455f8 --- /dev/null +++ b/docs/website/video-transcripts/Rt68rA2c6A8.json @@ -0,0 +1,8 @@ +{ + "line_count": 40, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 227, + "youtube_id": "Rt68rA2c6A8" +} diff --git a/docs/website/video-transcripts/Rt68rA2c6A8.txt b/docs/website/video-transcripts/Rt68rA2c6A8.txt new file mode 100644 index 0000000000..ab5f93f308 --- /dev/null +++ b/docs/website/video-transcripts/Rt68rA2c6A8.txt @@ -0,0 +1,40 @@ +there is still one small missing piece +for search +what happens when we click one of the +search results +when a person is clicked we want to show +his posts +when a post is clicked we want to show +the post itself +the user form ui is relatively spartan +when compared to facebook's ui +i didn't want to go too deep into the +full title area ui description and +everything else because those are +relatively trivial technically yet time +consuming +instead i chose to focus on the +functionality of showing the ui +this is the user shown in the form which +we found in the search results +all this is standard paging boilerplate +like we had in the other infinite +container instances +we request the posts of the specific +user with his user id +which we already implemented in the +server +this method should be made static +so we can use it here +this is trivial to do +so i won't go into that code +the infinite container is placed in the +center and this is pretty much the +entire functional ui +the same should be done for the post +search code but it's much simpler we +don't even need to save the post +instance since there isn't much here +this is pretty simple but it should work +fine and function correctly +with that searches fully working diff --git a/docs/website/video-transcripts/S1SHZLXQxSI.json b/docs/website/video-transcripts/S1SHZLXQxSI.json new file mode 100644 index 0000000000..c0215d4b3e --- /dev/null +++ b/docs/website/video-transcripts/S1SHZLXQxSI.json @@ -0,0 +1,8 @@ +{ + "line_count": 148, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 863, + "youtube_id": "S1SHZLXQxSI" +} diff --git a/docs/website/video-transcripts/S1SHZLXQxSI.txt b/docs/website/video-transcripts/S1SHZLXQxSI.txt new file mode 100644 index 0000000000..18f39e6acc --- /dev/null +++ b/docs/website/video-transcripts/S1SHZLXQxSI.txt @@ -0,0 +1,148 @@ +the native uber app is a bit weird when +it comes to account editing +when you want to edit any entry within +the account you click on it and it sends +you to a different form where you can +edit save +i'm assuming there is some logic in that +flow as it makes it harder to edit +details by mistake +but it's not exactly intuitive +i chose to just use text fields and map +them automatically to the user object +with the binding api +this makes everything really simple +one caveat is that i didn't want to send +user details for every minor change and +only wanted to send that detail +when going out of the form to save on +bandwidth noise +i needed some additional changes to make +the functionality work +but let's start with edit account form +itself +when we click on the avatar button in +this form we can open the camera to +capture a new picture +and set that as our avatar notice that i +set the image to be 512 pixels wide +the -1 argument +indicates i want to maintain aspect +ratio for the height +we need this code as the server limits +the size of files it can receive +you can customize that in spring boot +settings +but it's not a good idea to overdo the +size as it could be an attack vector +i'll discuss the two set avatar methods +soon +the edit tag +is the small circle on top of the image +indicating that it can be clicked +it i used a label and styled it in code +i could have done all of this in the +designer tour as well the padding makes +the circle bigger and the margin +position positions it in the right +position on top of the image +to actually position it on top +i use the layered layout where i place +the image below +and enclose the edit label in a flow +layout to push it down +i use the ui binding code to bind the +user object to the text fields +this will automatically update the +preferences whenever the user types in a +text field as the properties of the user +object will change +notice that the text fields get their +values automatically from the user +object too as the binding works both +ways +this is a bit of a hack +but it works great +i only want to save a user if the data +within the user changed so i generate a +tostring value of the user object which +i can later compare to the current value +if the values differ then the user +object was changed +i only want to save data when leaving +this form +and currently there is only one way out +back +i can detect the return to the previous +form and save the data to the server +there +it's important to unbind the object +since user is a global object and if we +keep the binding it can cause a memory +leak +the user object will reference the +binding which references the text fields +the text fields include the entire form +hierarchy indirectly +here we check that the user was modified +as i explained before +and sent the data to the server +asynchronously +that was a lot of code but it wasn't +that complicated relatively to the level +of functionality it delivered +let's start with the easiest missing +piece edit user +on the server we have a single method +for add slash edit user named ad add +edit user +it's mapped to slash user slash add +but it does edit to +we already have an add new user method +in the client but that method is +synchronous +and we don't want a blocking call that's +why we need this version that works +asynchronously +we have two separate methods for saving +the avatar the first one is in common +code it saves the avatar image to the +local storage +this is the exact masking code we have +in get avatar +it might have made sense to extract it +to a common method but the gist of it is +simple +we need to mask the image +we mask and set the image +to the variable instance that is used +later by get avatar +i return this value so it can be updated +into the ui instantly +notice that this is all entirely local +to my phone +so we need to update the user account in +the spring boot server +again this relies on a method that we +added a while back to the spring booth +server +multi-part request is a standard for +file upload and it's used when you +submit an html form tag over http with a +file within it +it takes care of base64 encoding +and related complexities +almost seamlessly +i need the token to write data +notice i encoded it in the url instead +of as an argument +adding arguments to multiple requests is +a bit challenging in spring boot +and i thought this was easier +since capture returned a file name +passing it +is literally the simplest approach at +this point +and that's it +avatar upload and download should +now work diff --git a/docs/website/video-transcripts/S5pAGkV4h4w.json b/docs/website/video-transcripts/S5pAGkV4h4w.json new file mode 100644 index 0000000000..b8741d9c21 --- /dev/null +++ b/docs/website/video-transcripts/S5pAGkV4h4w.json @@ -0,0 +1,8 @@ +{ + "line_count": 341, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1914, + "youtube_id": "S5pAGkV4h4w" +} diff --git a/docs/website/video-transcripts/S5pAGkV4h4w.txt b/docs/website/video-transcripts/S5pAGkV4h4w.txt new file mode 100644 index 0000000000..9f3d43e375 --- /dev/null +++ b/docs/website/video-transcripts/S5pAGkV4h4w.txt @@ -0,0 +1,341 @@ +i almost always start apps by building a +mock-up of the front end first +we are driven by visual feedback +the ability to see and interact with the +app clarifies the problem space and +motivates you to move forward back-end +and business logic become easier as you +can associate them with requirements +from the gui +one of the benefits of having markup +code is that it's easy to experiment +with the markup +for example +we can comment code in out +and see how it impacts everything move +your items around to judge impact etc +before we jump into the implementation +of the markup +we first need to understand what we are +building +and study the reference +in this case the reference is the +facebook app +so we'll start by deconstructing it +i'll begin with the login process +which is the first thing you see when +you install the facebook app +there are a few things i'd like to +discuss about the login process +deep versus flat also known as shallow +ui structures +forms with no functionality +plain text password used in the ui +delayed verification process +device orientation support in facebook +phone and tablet ui +and finally the adherence to platform +conventions in ios versus android +i'll go all +over all of these one by one +but first let's take a brief bird's eye +view of the sign in ui +this is the sign-in ui in the native +facebook app on android +don't panic i'll go over all the steps +i will also simplify it a lot because +this is a mess as you can tell +this is what i came up with +as you can see it's much simpler but +includes a lot of the features we saw +before +i'll go over all of this soon but first +let's get back to our agenda +there are two approaches we can take for +user input +with deep we ask simple questions in +each form and dig deep through the forms +until we have all the information +with flat we show one form with all the +information we need +the previous graphic showed a deep +approach whereas the edit details form +on the right takes the flat approach +facebook clearly picked deep for the +signup process and then did so for +various other concepts but within the +app it often picked up the flat approach +for instance for editing user +preferences +why was facebook inconsistent and why +pick one approach or the next +there are several reasons to pick a deep +it's intuitive +even those who have aversion to +technology can understand the flow of +the ui +it's portable +more white space on the screen allows +the ui to resize and adapt a to a wide +range of display sizes +it goes through a process as +humans we are used to this approach +so +if deep is so great why doesn't facebook +use it everywhere +because it has some limits +the biggest is expressiveness it's very +hard to communicate complex data and +ideas like we see above +where gender has complex options or a +person might not want to advertise their +birthday +annoyance is also an important limit +going through a seven step wizard to +change a single detail +is annoying to most users +picking deep or shallow approaches is a +matter for a ux expert +as developers are concerned as code +views +how do we express two distinct ui +paradigms and map to the same data model +since the facebook ui is so darn simple +i'd like to automate a lot of it and one +of the core goals is automating this +logic +let's go back to the flow graphic +we looked at before +the splash screen is shown when we start +this makes sense for platforms like ios +but it's there on android 2. +the logo blinks a bit +when we move to the login form it lets +you input the email or phone +notice a few interesting things about +this form +it doesn't use the material design style +of labels on top of the field +when elements gain focus +but it does try to comply with some +android paradigms for instance the +buttons use uppercase text +which is the convention on android +the create account ui +is +one form that literally has no purpose +when i looked over the ui i was +perplexed by this +i'm guessing there is some ux reason for +this form but i have no idea +i didn't include anything like that in +the clone and there is no equivalent of +this in the ios version version +of the native facebook app +before i discuss the name form notice a +few interesting things about this wizard +the word next is spelled in the style of +ios with case sensitive text instead of +uppercase +also notice that here the first and last +name fields use material design +convention for placement +and when you select them the hint +animates above the field unlike the +first form of the application +i think this inconsistency is due to +different teams building different parts +it contributes to an app that feels +unrefined in some regards +this is a long wizard with quite a few +stages +and notes +there are small inconsistencies with the +ui for birthday and the gender prompt +doesn't look great +during verification we can either enter +either a phone number +of an +or an email in the case of the latter we +can click the sign up with email address +link at the bottom of the form which +sends us to the email entry form +the email entry form is practically +identical to the phone number form +and you can toggle back +the final stage is where interesting +things happen typing a password on a +mobile phone is one of my biggest pet +peeves +there is literally no reason for doing +that +facebook solved that badly by making the +password visible by default +their old ui would show a regular +password field and offer to replace it +with a plain text field if you got the +password wrong +the current approach is problematic and +shows the lacks approach to security at +facebook +i will still clone that approach even +though i don't like it because it's +common practice +while we are on the subject of +verification facebook doesn't verify +during sign up +i could have given a completely fake +email +and the process would continue until the +end +facebook nags after the fact to make +sure the email phone +is correct but you can complete sign up +with a wrong email or phone +i'm assuming this relates to the +challenge of delivery to email or phones +on a global scale i won't do that though +although you can probably adapt the code +to copy that behavior +facebook supports device orientation +changes everywhere it often achieves +this ui by removing content +surprisingly this isn't consistent for +other apps from facebook for instance +instagram is locked to portrait +orientation +at first when i looked at how facebook +implemented landscape support my first +thought was lazy +to be fair a lot of apps lock +orientation change all together for +instance instagram uber etc +so landscape support in facebook is +refreshing +it still looks bad +it might be related to the universal +nature of facebook +the app should work everywhere and might +be under constraints we can't imagine +but i find that hard to believe +it just looks like something they didn't +care enough about to invest in +this makes some sense i doubt most users +rotate their device during the signup +process signing up on an ipad is +radically different to the signup +process in android +it's clear that there are distinct +groups developing the application the +entire ui is shown within a dialog that +updates with the progression of the +wizard +let's zoom in a bit on the stages +facebook clearly has two separate themes +for ios slash android the apps are +inspired by some common design language +but take very different approaches for +some basic ui features +the android version uses some aspects of +material design and the ios version +looks +somewhat like an ios app but neither one +stands out as an example of great design +in the given native platform +so what's the point +you will notice that the flow of the +stages in sign up is different between +ios and android terms are in the +beginning instead of the end and there +are no redundant forms +notice i magnified the ui so it will be +more visible as the ipad resolution is +so big it would make the entire text +unreadable +the stages are pretty much the same +conceptual stages as we saw in the +android app +but slightly different +again the password is plain text +which shows that this is a clear policy +decision +the gender question is probably the most +telling +in the ios version it's expressed as a +toggle button whereas an android that's +expressed as a radio button the former +is clearly superior but it's available +in ios only +the app itself doesn't use the ipad form +factor effectively +for the most part it feels like a +blown-up phone version of the app +which is stunning for an app like this +it does show a search ui on the side +when running in landscape mode but +that's pretty much it +how would this ui adapt better to tablet +form factor +the feed is about posts +right now they are just blown up to the +size to a size that doesn't help if i +use an ipad it's not for seeing larger +posts +we could fit two or three columns of +posts in the feed and significantly +increase information density +there are some other considerations for +facebook such as ad placement which +might be impacted +this ui choice is also reflected in the +web version of facebook so i'm guessing +it's a +conscious +choice +there is a lot to say about the main ui +of facebook +let's start by reviewing the android +version of the ui so we are all roughly +on the same page +i trimmed a lot of the hidden nuance and +details from this chart +after looking at this i developed +several insights into the facebook ui +but one of those was specifically +interesting +they removed the hamburger back in the +day facebook was one of the first apps +to feature the hamburger side menu +this is a ui element signified by three +horizontal lines +one on top of the other +hamburger it allows you to open or swipe +in a drawer with options +this menu is now a standard in android +however facebook chose to discard it in +favor of swipeable tabs at the top of +android and on the bottom in ios +on a personal level i'm conflicted about +this +it's cool that facebook goes against the +trend especially +since it's a trend they helped start +tabs have +the user experience advantage of +visibility +which is great +however they have three major +disadvantages +first they take up extra stream screen +space +second they show only an icon +new users might be confused about what +these icon icons mean +third it's step backwards tab based uis +were popular in 2009 +despite these reservations i'll copy +their style in the ui we built a lot of +demos with side menus so i'll use this +opportunity to build something different +i'll come back to this diagram later +when we go over the implementation diff --git a/docs/website/video-transcripts/SBqYZSdIdec.json b/docs/website/video-transcripts/SBqYZSdIdec.json new file mode 100644 index 0000000000..ed763a1355 --- /dev/null +++ b/docs/website/video-transcripts/SBqYZSdIdec.json @@ -0,0 +1,8 @@ +{ + "line_count": 118, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 679, + "youtube_id": "SBqYZSdIdec" +} diff --git a/docs/website/video-transcripts/SBqYZSdIdec.txt b/docs/website/video-transcripts/SBqYZSdIdec.txt new file mode 100644 index 0000000000..1ee4e3c12d --- /dev/null +++ b/docs/website/video-transcripts/SBqYZSdIdec.txt @@ -0,0 +1,118 @@ +push is one of the more challenging +tasks on mobile the first time around +it's an uphill battle because it's just +so darn confusing and alien +however once you understand the +underlying logic and platform oddities +it becomes simple +we'll start with +gcm by going to developers.google.com +mobile slash ad +where we need to be logged in with our +google account +here start by clicking pick a platform +for which we have selected 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 google's cloud +and 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 +we can either share or not share service +data that's up to you +and we can proceed to the final stage of +creation of the app +which also entails some +waiting +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 codename1 settings +application and go to the build hints +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 +the registration is done +we'll still need some of these values +and the code but we finish the +configuration portion of 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 hire +within the preferences ui +otherwise you won't be prompted for push +details +once we log in to the apple developer +account +we can move to the next step +we pick or add devices i tend to 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 dialogue +but if you already have a certificate +and it's working for you +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 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 check box that we must +activate if this check box isn't here +then you aren't logged in with a pro +account +once we finish and press save the +certificate should be generated we +should also receive an email with the +instructions and url explaining how we +should integrate push the email should +contain urls in the cloud for two push +certificates which are also generated to +your local file system under ios certs +these urls are important when we need to +send the push message diff --git a/docs/website/video-transcripts/T8l7k2OeGpo.json b/docs/website/video-transcripts/T8l7k2OeGpo.json new file mode 100644 index 0000000000..999fe7e73a --- /dev/null +++ b/docs/website/video-transcripts/T8l7k2OeGpo.json @@ -0,0 +1,8 @@ +{ + "line_count": 75, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 407, + "youtube_id": "T8l7k2OeGpo" +} diff --git a/docs/website/video-transcripts/T8l7k2OeGpo.txt b/docs/website/video-transcripts/T8l7k2OeGpo.txt new file mode 100644 index 0000000000..4cfca4aa17 --- /dev/null +++ b/docs/website/video-transcripts/T8l7k2OeGpo.txt @@ -0,0 +1,75 @@ +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/video-transcripts/TOWhbEhiRe4.json b/docs/website/video-transcripts/TOWhbEhiRe4.json new file mode 100644 index 0000000000..eb6957f484 --- /dev/null +++ b/docs/website/video-transcripts/TOWhbEhiRe4.json @@ -0,0 +1,8 @@ +{ + "line_count": 100, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 545, + "youtube_id": "TOWhbEhiRe4" +} diff --git a/docs/website/video-transcripts/TOWhbEhiRe4.txt b/docs/website/video-transcripts/TOWhbEhiRe4.txt new file mode 100644 index 0000000000..b202e8c954 --- /dev/null +++ b/docs/website/video-transcripts/TOWhbEhiRe4.txt @@ -0,0 +1,100 @@ +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/video-transcripts/TRF76P1Dwwc.json b/docs/website/video-transcripts/TRF76P1Dwwc.json new file mode 100644 index 0000000000..ea1eb7b7fd --- /dev/null +++ b/docs/website/video-transcripts/TRF76P1Dwwc.json @@ -0,0 +1,8 @@ +{ + "line_count": 105, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 615, + "youtube_id": "TRF76P1Dwwc" +} diff --git a/docs/website/video-transcripts/TRF76P1Dwwc.txt b/docs/website/video-transcripts/TRF76P1Dwwc.txt new file mode 100644 index 0000000000..5e8cb33af8 --- /dev/null +++ b/docs/website/video-transcripts/TRF76P1Dwwc.txt @@ -0,0 +1,105 @@ +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/video-transcripts/U0Ms65vcVr4.json b/docs/website/video-transcripts/U0Ms65vcVr4.json new file mode 100644 index 0000000000..46f30d087e --- /dev/null +++ b/docs/website/video-transcripts/U0Ms65vcVr4.json @@ -0,0 +1,8 @@ +{ + "line_count": 145, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 800, + "youtube_id": "U0Ms65vcVr4" +} diff --git a/docs/website/video-transcripts/U0Ms65vcVr4.txt b/docs/website/video-transcripts/U0Ms65vcVr4.txt new file mode 100644 index 0000000000..91f8d32603 --- /dev/null +++ b/docs/website/video-transcripts/U0Ms65vcVr4.txt @@ -0,0 +1,145 @@ +we are now ready for the new post form +changes +up until now the changes were simple but +in this class we have a bit of work to +do +let's start with the top level changes +currently we support one attachment this +field represents the attachment id +this is the attachment mime type if +applicable +this is the button for sending a post +we need to disable this button until the +attachment is uploaded +the constructor is now private as the +class is instantiated via factory +methods +also notice we don't invoke +inner ui in the constructor anymore +the first big change is the init ui +method which up until now created the +same ui and was bound in the constructor +this will no longer work as the ui with +an attachment will be slightly different +this code is still there it just moved +to the factory method +here +the post styles code happened at the end +of the internet ui +originally but due to logistics we need +to tear +this allows the image and video versions +to avoid the styles bar at the bottom of +the ui +notice that those don't exist in the +native facebook app either +which brings us back to the init ui +method +this is the button for posting from the +toolbar +we can later disable this button +we can find the button +for toolbar commands +to get low level control +this code will behave exactly the same +as it did before if post styles had a +value and we'll just add the post to the +center if it doesn't +those are somewhat nuanced changes that +expose components +we can work with +let's see how the salau's image uploads +in the main method +this is a factory method +for an image post +star bar +in the bottom isn't included +we set the mime type here but notice we +don't know the media id yet +so the attachment value isn't set +the image will take up half the screen +height +this is better than using an api like +set preferred size because it will still +work if the device is rotated +here we upload the media +to the server and return a slider +component to track the upload progress +we place the image +on the bottom of the form +and the slider progress on top of it +further down +before we go to the logical next step in +the upload method i'd like to point out +a big missing piece +delete +if we close this post now the image +would still be in the servers and no one +would know +that's a flaw i left in place due to +time constraints +there are several potential fixes +probably the best one would be a +back-end server process that deletes +media +that has no references +we can also add a delete web service but +that wouldn't work perfectly for cases +of app crashes etc +i'll come back to the video post soon +for now i'll skip ahead to the upload +method +if we're uploading a media file +the post button should be disabled until +we are done +once upload finished +we enable the post button +notice we save the attachment value to +which we will need later +you can bind a connection request to a +slider using this utility class built +into codename one +every stream and codename one is +observable which means we can get update +events on any i o operation seamlessly +utilities like slider bridge take this +to the next logical step by binding the +progress event from network manager to a +slider with this +let's go directly to the changes +in post to support attachments +this code +had to move outside of the if statement +even though it hadn't changed +if we have an attachment +we add it to the post object +after this line the rest of the code is +identical +with that +image posting should work although we +still need to update the callers to use +the factory methods instead of the +constructor +we have one final change to new post +form +the create video post +most of this looks like the create image +post method with +minor changes such as mp4 instead of +jpeg etc +we can't +override the performed size of the video +component but we can replace it in the +container where we do override the size +we need +we that container anyway so we can place +a program progress indicator on top of +that to show the upload progress +this shows a frame of the video one +second into playback +notice i invoke play and only then try +seeking without doing that the behavior +is undefined for some features such as +well seek +with that the new post form is done +we just need to wire it in diff --git a/docs/website/video-transcripts/UU6HCbenVAA.json b/docs/website/video-transcripts/UU6HCbenVAA.json new file mode 100644 index 0000000000..55f71ca9c6 --- /dev/null +++ b/docs/website/video-transcripts/UU6HCbenVAA.json @@ -0,0 +1,8 @@ +{ + "line_count": 153, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 893, + "youtube_id": "UU6HCbenVAA" +} diff --git a/docs/website/video-transcripts/UU6HCbenVAA.txt b/docs/website/video-transcripts/UU6HCbenVAA.txt new file mode 100644 index 0000000000..5741ce1a30 --- /dev/null +++ b/docs/website/video-transcripts/UU6HCbenVAA.txt @@ -0,0 +1,153 @@ +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/video-transcripts/UhLFHJj8qnM.json b/docs/website/video-transcripts/UhLFHJj8qnM.json new file mode 100644 index 0000000000..0d34e6a3e9 --- /dev/null +++ b/docs/website/video-transcripts/UhLFHJj8qnM.json @@ -0,0 +1,8 @@ +{ + "line_count": 143, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 820, + "youtube_id": "UhLFHJj8qnM" +} diff --git a/docs/website/video-transcripts/UhLFHJj8qnM.txt b/docs/website/video-transcripts/UhLFHJj8qnM.txt new file mode 100644 index 0000000000..712289e0ee --- /dev/null +++ b/docs/website/video-transcripts/UhLFHJj8qnM.txt @@ -0,0 +1,143 @@ +we need to make a decision on the ui +design +should we use the ios design the android +design or both +android and ios differ greatly in the +login process for facebook +i'm assuming the reason for this is +apple's review process +that places restrictions on what +facebook can do +i think starting with terms and +conditions makes more sense +you get that out of the way and then +proceed directly to use information +other than that for the most part i +chose to go with a design that looks +more like the android design +for the signup wizard +before i go into the code let's look at +the first form in the signup wizard so +we understand what we are up against +we need to adapt the title area to +landscape mode +we need to have different back command +behavior for ios we need a rich text +component to provide multi-line text +with links +the first two are common to all the +forms +to facilitate that i decided to create a +generic form that will handle all the +stages of the sign up +sign up form +i'll discuss that shortly +but first let's discuss the rich text +support +a very common request is support for +rich text and codename one +this is hard to do in a generic +performant cross-platform way +but can be done easily for simple tasks +like this +notice that for a lot of use cases i +would just use browser component +here that won't be ideal because i want +deep integration with the ui and deep +control +since we already have an xml parser +that's perfectly capable of passing html +i decided to use a very simple html +based syntax +i also added support for bold and italic +while demonstrating that simple things +like line breaks still work +let's go over the code step by step +this is a container as we pass we add +elements into it +this is the html text we set +it's useful for the get text method +font size is hard coded in millimeters +for simplicity +the event dispatcher lets us broadcast +link click +events as action event +the following variables are used by the +parser to store temporary pass estate +as it builds the ui +these are the fonts and constants we +will use when the parser asks us to +create a component +the constructors delegate to the init +method which in turn initializes the +default fonts +notice that the default layout is flow +layout +we rely on that to allow aligning the +text to the center or right +this method is invoked by the parser to +add a component +if the string has a space we split it to +multiple separate string strings so each +individual component can break a line in +the flow +layout we add either a label or a button +that looks like a label +the parser updates current link so if we +have a link +we are within an a tag and need to use a +button style to look like a link +current link value is the content of +ahref +we just fire the listener event +dispatcher with the ahref content as the +source +if this isn't a link it's a label +we remove the spaces +padding and margin +we then use the width of the space to +re-add a space in the form of padding +this allows line breaks on word +boundaries +the parser might need to remove a space +for some special cases for instance +if we close an a tag and place a dot +right next to it +the set text method +is the api that sets the html to this +component +we wrap the html so it's well formed and +pass the text using the parser inner +clause +these add listeners so when a user +clicks on a link these listeners will +receive the event +from the event dispatcher +the next stage is the parser in a class +it's a bit big +we derive xml parser and override +callback methods to tokenize the string +all the methods here are callbacks in a +sac star parser +we also support a dom style but i chose +a callback approach for performance +if the block element starts with a space +we add a space to the last block element +we remove the space from the previous +component if the block doesn't end with +a space +here we handle the individual html tags +we want to support by changing the +current font or color +when a tag ends we clear the current +font color and restore the default we +don't handle complexity like tag nesting +etc +when we're in an a tag we save the value +of the href attribute for use in the +event handler code +that's it +we now have a poor man's html renderer +that we can use for simple blocks of +highlighted text +we'll make use of it soon diff --git a/docs/website/video-transcripts/UwdI_tMqYgU.json b/docs/website/video-transcripts/UwdI_tMqYgU.json new file mode 100644 index 0000000000..55b4dc782c --- /dev/null +++ b/docs/website/video-transcripts/UwdI_tMqYgU.json @@ -0,0 +1,8 @@ +{ + "line_count": 88, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 479, + "youtube_id": "UwdI_tMqYgU" +} diff --git a/docs/website/video-transcripts/UwdI_tMqYgU.txt b/docs/website/video-transcripts/UwdI_tMqYgU.txt new file mode 100644 index 0000000000..fba421a231 --- /dev/null +++ b/docs/website/video-transcripts/UwdI_tMqYgU.txt @@ -0,0 +1,88 @@ +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/video-transcripts/UxATbrfWveU.json b/docs/website/video-transcripts/UxATbrfWveU.json new file mode 100644 index 0000000000..6396f3a50d --- /dev/null +++ b/docs/website/video-transcripts/UxATbrfWveU.json @@ -0,0 +1,8 @@ +{ + "line_count": 117, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 739, + "youtube_id": "UxATbrfWveU" +} diff --git a/docs/website/video-transcripts/UxATbrfWveU.txt b/docs/website/video-transcripts/UxATbrfWveU.txt new file mode 100644 index 0000000000..2f1dcf2a9e --- /dev/null +++ b/docs/website/video-transcripts/UxATbrfWveU.txt @@ -0,0 +1,117 @@ +we are finally down to the last service +and nearly finished the server +the post service handles everything +related to posting commenting and liking +it's not as complex as user service but +it still has a lot of logic and +dependencies +this is a common constant that's used in +the code we use a similar constant in +the client side +we reference multiple repositories here +notice the news feed and comments have +no public facing service class so their +data is encapsulated here +again we send the notifications via the +notification service and not by writing +to the repo +this method returns the posts for the +given user +if that user is us or a friend it shows +everything if the user is a stranger it +will only find the public posts +we need the ought user user so we can +check if the requested user is a friend +if the user is a friend or it's my own +posts then we look for all the posts +sorted by date +otherwise we only want the public posts +but with the same sort order of date +once we have the data we can just add +the whole thing into a list and return +that this is pretty simple despite the +verbosity it allows us to implement +client-side ui such as clicking on a +user to see his posts +newsfeed isn't just the list of posts +for user it includes friends mixed in +and can be organized in a unique way +that's why we separated it to a special +entity in the storage +most of the work in the news feed is +done when we insert it into it so there +is not not much left to do at this point +that's a good thing as this method +should be fast +the query does most of the work +we sort the results based on three +criteria day means all the posts from +today +will come before posts from yesterday no +matter the rank +rank lets us float the best posts posts +to the top and timestamp sorts within +that +we build dow responses like we did +before +the next method is the actual post +method that adds a new entry to the news +feeds posts +this is all pretty standard stuff we +create a new post entity and fill it up +based on the dao we then save this post +to the database +since the news feed is a separate entity +we now take the new post and add it +there +when we go over all our friends and add +the post to their feed +we return the id of the newly posted +article +this leads us to the implementation of +add posts to news feed which performs +the addition to the news feed entity +this is again pretty trivial we set the +values and save the news feed entry +this is the day since epoch it allows us +to differentiate easily between posts +made today and those made yesterday +we can probably do this using jpaquer +ajpa query but sometimes saving the data +we need is simpler +we can place a comment into a post using +the comment method +a user can only post a comment on a post +if it's public or by a friend +we need to protect that +we save the comment then add it into the +post and save that +we send out a notification of the new +comment to the user whose post this is +i oversimplified again a notification +would go out to people watching the post +and to other commenters as well but for +the sake of simplicity i left those out +currently i limited like to posts only +it should be something that we can apply +to comments and other features it should +also be pretty easy to create the more +nuanced like +emotions +that facebook introduced a couple of +years ago +i wanted to keep things simple so i vote +avoided those +i also didn't provide a way to unlike +which in this case is taking back a +previous like operation +not a negative like +this should be pretty easy to implement +once you have like working so i didn't +bother going there +we just add the user to the list of +likes if he isn't in the list already +we send a notification that the post was +liked to the author +with that we finally finished the +service layer entirely diff --git a/docs/website/video-transcripts/UxZ1HeheGwU.json b/docs/website/video-transcripts/UxZ1HeheGwU.json new file mode 100644 index 0000000000..0b2c0d1b07 --- /dev/null +++ b/docs/website/video-transcripts/UxZ1HeheGwU.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 335, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css.md", + "status": "transcript-fetched", + "word_count": 1768, + "youtube_id": "UxZ1HeheGwU" +} diff --git a/docs/website/video-transcripts/UxZ1HeheGwU.txt b/docs/website/video-transcripts/UxZ1HeheGwU.txt new file mode 100644 index 0000000000..d89153edd6 --- /dev/null +++ b/docs/website/video-transcripts/UxZ1HeheGwU.txt @@ -0,0 +1,335 @@ +the css plugin is one of the more +elaborate plugins for codename one +and while it was developed by steve +hanna +who is a codename one employee he +developed it sort of as a separate +stand-alone personal hobby project +and it stayed that way +until this moment specifically +mostly because it's a very very +complex idea +and we're still not exactly sure +how well it will fit into codename one +it's something that we're evaluating +whether to +bring it into codename one maybe to +build something a bit different but so +far it looks like it's gaining +some traction and might become an +official part of codename one moving +forward +but +at this moment no decision has been made +regardless it's still a very powerful +tool and because it's based on the +existing codename one capabilities +it should be supported forever +in terms of the functionality +the workings with working with css is +very familiar uh for better and for +worse i personally am not a big fan of +css because of its +uh +overly obtuse syntax but a lot of people +are used to it +and are familiar with it +and that can be good it can also be +sometimes bad because for codename one +we need a lot of custom attributes which +aren't necessarily things that you can +just use from by copying and pasting +from the internet and that's another +problem with css you'd expect to be able +to copy and paste css from the internet +which isn't exactly something you can +fully do +with uh this plugin only +to a partial extent +so +to install the plugin just go to +the url mentioned here +the npm installation process is the +easiest +if you have npm if not it might be more +practical to just install it manually +and +maybe even base your installation on +the project that i provided here with a +test that includes the source code of +everything just copying the +the +the jars and everything +the +the usage is also +explained in that website +at the github project +but i'll give you a couple of examples +so you will have a good context to begin +with css +so a very basic hello world css would be +just creating a button +and in this case you'll notice what i +was referring to when i said custom code +in one attribute notice the cn1 pill +border is a special case attribute and +there's a lot of documentation with a +lot of these and the website +it's essentially the round border from +codename one +only the cn1 round border is the round +version and the pill border is the +version that's +defined as rectangle in codename one +rounded with a +rectangle +and +here i define obviously the background +color foreground color those are +identical and notice the special case +for the font family which is native main +thin which is uh the native thin fonts +that are supported built-in barcode name +one +so +that's again another special case in css +here but ultimately this is actually +really really nice and it hides a lot of +complexity +because the button +will be applied to the pressed and +selected mode +and +will essentially just work as you pretty +much expect +the only other change i need to make to +the code is change the high world to use +a button instead of using a label by +default +and once i do that +i get this which is what i would expect +to happen and it worked the first time i +tried it which is really really really +impressive +uh in terms of this particular plug-in +it works really well and if you're +comfortable with css +it's a very interesting approach +so +what exactly happened here because you +know +i defined the theme do we still have a +theme how does it exactly work +if you understand the concepts of +codename one and the theme +res file +well +i'll try to explain it in a bit more +detail +so the css support uses a preprocessor +that parses the css and generates a +theme.res file actually +theme.css.js file by default technically +you can name it anything if you name +your css file x dot css it will call it +x.css.res +but one of the things that you need to +do is define +layering +and layering +essentially places +the +the base theme dot res and +places the css theme on top of that +and that means the +when our inner code loads it loads the +theme.s file initially and sets it as +the theme for the application +and +within that theme +we have a theme constant i'm not +sure if i don't think i discussed uh the +concept of theme content constants +before but there's essentially a tab +within the designer where you can +when you can see constants that you can +define to define all sorts of behaviors +in codename one and one of them is an +overlay where you can say okay in this +theme that this is just the base +after you finish loading this theme load +these other themes +and +essentially the theme.s file uh at least +when you use the npm install +uh adds another uh +overlay +theme +and points to the css uh file creator +and that way it's essentially +loaded automatically +for you and you get the css in place the +nice thing about that is that you can +still use the designer if you're more +comfortable doing some things in the +designer and that's just changing +stuff there in the theme the default +theme and then go to the css and do the +things that you're more comfortable +doing there so you have essentially two +theme two res files and uh +because of the way it's built the +the css one will take precedence because +it comes +after the +the base theme so +if there's a conflict there css always +takes it in this case +you can flip that as well but that would +require a bit of code +and +that's generally it +now +two things i i can go on about css and +the selectors there's this remarkably +huge list of attributes that steve +implemented there and obviously going +even over a fraction of them it includes +support for nine piece borders and other +capabilities of code name one but i +don't want to dig into all of that +because it's a bottomless pit of +of +subjects +the +two things i would like to touch is +images and basic selectors so +uh +i'm giving her two examples of how you +can customize various behaviors +and one of those is the button pressed +i can just do that and essentially this +is a bit of a darker color now the next +image won't show that because it shows +the regular button but +if you press it it would have the darker +color and that's all i need i don't need +to define all the other things i don't +need to define the border again i don't +need to define anything +it will just +work and that's +that's pretty nice in its own right +there's no need to do any extra work or +anything like that just works with the +darker color +and +by the way part of that is also one of +the nice things about that is that it +uses the round border in the past uh the +css plugin had support for round borders +but it used an image +and that was inferior in some regards +because it would deteriorate with the +various resolutions +in this case the round border sort of +solves that problem and allows us to +create very +uh compelling +user interface elements +and +all i just have another selector here +for form +and in the type place an image reference +now the nice thing about it is that i +can i created up directory images under +the css directory and placed duke.png +there which is a standard image of duke +but +one of the nice things about that is +that you can put any url you can put a +url off the internet literally an http +or well +and +it will work it will +get in there +and +the next thing that might not be +intuitive is the source dpi value +normally an image would just be included +but once we include that attribute it +automatically assumes that the image is +a multi-image and it will import it as a +multi-image and automatically scale it +to the various resolutions +as needed +and that's really really nice +because it allows you to add multi +images with essentially no real work +there's other ways that the css plugin +allows you to import multi images +including by define including all of the +various resolutions and all sorts of +other approaches +it's really nice +and +in this case i gave a 260 ppi uh +resolution or dpi resolution +which +essentially is close to very high in +terms of the resolutions +so the image would be scaled and that +that indicates the size of the source +image so if the resolution is very hard +we'll just use the source image as is if +it's trying to create a high +resolution image it will scale it down +by a specific factor to that determines +the difference between high and very +high +um +i can go into the semantics but +generally +if an image matches uh it was designed +for instance for an iphone +uh +what is it um iphone +5 +so it has a +dp uh dpi of +400 i think or 360 then you define it +you define that version that size of the +dpi here and the scaling +algorithm will automatically scale it to +the right resolutions +the don't +catch me on my word about the dpr of +iphones i don't remember my +uh by heart at +1am +anyway +the +background type uh essentially says we +want to align the image to the bottom +right and that's again a codename one +style +uh +specific style in css you can do +anything you can tile you can all of the +things that you can do in the designer +you can do to the css here and that's +really nice and this essentially places +duke at the bottom right corner of the +form +like this +and i can't press the high world here +because it's an image but it would +change the color based on that +so +i hope this +brief +presentation helped explain css +i think there's huge potential there +and +i hope you enjoy working with it +thank you diff --git a/docs/website/video-transcripts/VCNFKMeud-w.json b/docs/website/video-transcripts/VCNFKMeud-w.json new file mode 100644 index 0000000000..064430b60e --- /dev/null +++ b/docs/website/video-transcripts/VCNFKMeud-w.json @@ -0,0 +1,8 @@ +{ + "line_count": 117, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 621, + "youtube_id": "VCNFKMeud-w" +} diff --git a/docs/website/video-transcripts/VCNFKMeud-w.txt b/docs/website/video-transcripts/VCNFKMeud-w.txt new file mode 100644 index 0000000000..7aa2cb56ae --- /dev/null +++ b/docs/website/video-transcripts/VCNFKMeud-w.txt @@ -0,0 +1,117 @@ +in this section we'll cover a lot of +minor features mostly related to user +interface +that are part of the missing pieces on +the road to complete the app +let's start with the categories that we +can see at the top of the restaurant +application +when we fetch a dish we can check if a +category is already added and if not +we can just add it into the list of +categories +this list is used later on when we build +the list model for the categories +in the dish edit form we can provide an +autocomplete text field +which allows us to type in new +categories or select an existing +category +in that same form when a user presses ok +we check if the category already exists +and if not +we add it to the category list +this makes the whole process automatic +and effectively removes the edit add +delete user interface +for the categories +that simplifies our work by removing +forms +we would have built +and also simplifies the user experience +laziness pays off +here we can see the server code for the +categories +and you will notice that it is handled +as part of the dish update +there is no separate web service for +categories further simplifying this +logic +we implicitly create a category if it's +missing +and save it to the mysql database +we have a separate call to call the +category which will go into +now +the +category call +just hops +just loops over the dishes to see if a +category is unused +if it is missing the method deletes that +category +the delete method also calls the +category +since the deleted dish +might be the last one using a specific +category so we need the call there too +back in the client we use the validator +api to place a constraint +on the category so we'll always have a +category for a dish +we also set a numeric constraint on the +price +notice that a validator is essential +even if you set the text field as a +numeric text field the definition only +indicates the type of virtual keyboard +used but doesn't enforce input since +mobile os's don't do that +next we'll handle prices on dishes +as you can see in this code +we can just add a price label with the +right price badge ui id +we use a property listener to +automatically update the price +as it changes in the underlying object +we align the price to the right using +the flow layout if we'd have used just +text alignment the purple background +would have stretched through the entire +width of the space +the price badge ui id has most of the +settings you would expect for +color alignment padding +and even a bit of +translucency the thing that pushes it +from the top is a four millimeter margin +on the top side which places it at just +the right place +next we'll address the details form +which is mostly pretty trivial +let's go over it step by step +i've enclosed common code for text field +and label creation +in the add text and label method +this ended up being reducing only a +small part of the clutter and wasn't as +worthwhile as i had initially +hoped +address colors and other elements are +represented by a button +which will navigate to a deeper editor +form +this is important as it allows us to +keep the user interface in this form +relatively simple +without too much clutter +i considered using something like an +accordion ui +but using navigation was +simpler +as i mentioned before +the add text field and label +is relatively simple +it sets +the hint and just adds the components +into place diff --git a/docs/website/video-transcripts/VDyltmk_Hu8.json b/docs/website/video-transcripts/VDyltmk_Hu8.json new file mode 100644 index 0000000000..d9966f24d2 --- /dev/null +++ b/docs/website/video-transcripts/VDyltmk_Hu8.json @@ -0,0 +1,8 @@ +{ + "line_count": 58, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 328, + "youtube_id": "VDyltmk_Hu8" +} diff --git a/docs/website/video-transcripts/VDyltmk_Hu8.txt b/docs/website/video-transcripts/VDyltmk_Hu8.txt new file mode 100644 index 0000000000..b3ce9a4080 --- /dev/null +++ b/docs/website/video-transcripts/VDyltmk_Hu8.txt @@ -0,0 +1,58 @@ +push is fragile and can be blocked by +the user +in order for the app to work properly we +need to properly handle the situation +where push isn't available +and we can do that with a fallback +implementation +i have two such implementations i will +start with the http based implementation +the async build code +needs a place where it can deliver the +message sent back to the client +the restaurant entity is pretty +convenient for that +we can store the result from the build +process +in the last result entry within the +restaurant entity +you might recall the build app service +class where we send the build async call +using a post method +we can just return the response by using +a get method to fetch the entity +database transactions take care of the +rest +and all we need to do now is pull from +the client for this update +in the client side the build app method +sends a build to the server +here we have a polling fallback +code to see how the connection is +working +if we have a push key +we don't need polling as push will take +care of this but if we don't have it +then polling needs to kick in +the polling code is pretty simple +let's review what we have here +first we have a request to to the build +app url again +this time however we pass false instead +of true to the second argument +that means we are making a get request +instead of a post request +we wait for the response and check if +it's valid +then schedule a timer that will keep +polling as long as the value hasn't +changed +once that's done we cancel the time +and return the value with on build +result this isn't ideal +and normally we try to avoid http +polling +check out a better approach demonstrated +in the websocket version +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/VnYaxvVn6OA.json b/docs/website/video-transcripts/VnYaxvVn6OA.json new file mode 100644 index 0000000000..a17b4c293c --- /dev/null +++ b/docs/website/video-transcripts/VnYaxvVn6OA.json @@ -0,0 +1,8 @@ +{ + "line_count": 49, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 257, + "youtube_id": "VnYaxvVn6OA" +} diff --git a/docs/website/video-transcripts/VnYaxvVn6OA.txt b/docs/website/video-transcripts/VnYaxvVn6OA.txt new file mode 100644 index 0000000000..d2931de1e4 --- /dev/null +++ b/docs/website/video-transcripts/VnYaxvVn6OA.txt @@ -0,0 +1,49 @@ +a server is incomplete without the +client +let's go back to the client side and see +how to proceed from there +the builder class allows us to build a +new restaurant app +but the first thing we need is to obtain +a secret matching this device +in order to do that we have an api +called create restaurant if necessary +if we already have the secret it will +just return the secret and we can move +on +if we are already in the process of +requesting the secret it will wait for +that and if not it will actually connect +to the web service +to get a new secret +the connection to the web service code +is pretty standard +put operation which works asynchronously +without interrupting the user +notice the response is invoked in post +response to keep things on the edt +we use create restaurant if necessary +like this +which +effectively uses the closure code to get +the reference to the secret +it isn't as convenient as synchronous +code +but it still allows us to send a +connection request call +i've designed the client api and the ui +so +they don't get in the way +we send a request and trust it to work +if it fails we'll undo the change +after or retry +but the idea is to +not bother the user with details of +network reliability or +synchronicity +the api on the client and the server is +designed to use the network manager +queue +effectively +thanks for watching +i hope you found this informative diff --git a/docs/website/video-transcripts/WT4cFceBWRA.json b/docs/website/video-transcripts/WT4cFceBWRA.json new file mode 100644 index 0000000000..b6b7763436 --- /dev/null +++ b/docs/website/video-transcripts/WT4cFceBWRA.json @@ -0,0 +1,8 @@ +{ + "line_count": 581, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 3705, + "youtube_id": "WT4cFceBWRA" +} diff --git a/docs/website/video-transcripts/WT4cFceBWRA.txt b/docs/website/video-transcripts/WT4cFceBWRA.txt new file mode 100644 index 0000000000..d4150c6852 --- /dev/null +++ b/docs/website/video-transcripts/WT4cFceBWRA.txt @@ -0,0 +1,581 @@ +in this session I'll talk to you about +adapting a UI design +specifically from PSD a Photoshop file +and it's going to be a bit challenging +Adapting a UI Design +technically +but really cool because we're going to +do something as amazing as the image you +see on your right here +and this is the original that's the +Photoshop design that's not what we'll +end up with we'll end up with something +that looks very similar although not +pixel perfect because I'm not aiming at +that +I'm aiming at something that looks +uh more device like +and I'm not going to copy the fonts or +the icons I'm going to use material +icons because they're available +and I don't want to go into that if I +wanted to do it Pixel Perfect I could +but you know getting 95 percent is much +easier than getting that last 99 percent +and every bit you add beyond that +complicates things further so I want to +keep this relatively simple it's still +going to be a bit difficult +this design is something I got from that +envisionapp.com do URL you see at the +bottom here now check it out over there +you can also download uh the Photoshop +file the specific Photoshop file took it +from that includes several designs I +picked just one specific screen to +make things simple for the specific demo +Approach +so let's look a bit deeper +this specific image was first break it +down into the images that we need and +the first steps that we need to do is to +cut out +pieces out of the design that we'll need +to use +in this specific design there there +isn't that much to cut out because it's +a relatively simple design and which is +what I like and also because a lot of +these things are standard so we can use +the standard elements like I said before +we won't need the icons in this case if +we were building something that's Pixel +Perfect I'd also cut them out +but we won't be using them +we'll just use standard icons that we +have +and we'll need the background image +we'll cut it out and we'll put it in the +title area of course +we'll use the standard Fab a floating +action button from codename one +and it's uh +and we'll just configure the colors to +look like that that's the only change +that we need because it's already +implemented in codename one and will +just work +we will need another single image so +that makes two images in total that +we'll need from that photoshop file and +that is the image pointed at here the +green line at the left including the +selection itself +and we'll cut these as a nine piece +border I'll discuss non-piece borders a +bit later in case you're not familiar +with that concept but generally it just +allows us to +scale uh design or specific types of +designs like this one in a way that's +very efficient +and doesn't degrade the image quality in +any way +so they're pretty useful for things like +like this in terms of design elements +and those are the only two images that +we'll need +now as I said we'll ignore the icons for +the check boxes as well so I'll use the +standard material checkbox which looks +slightly different than this but very +close obviously the magnifier and the +back look a bit different as well +uh still it will end up looking really +nice in my not so humble opinion in this +particular case +so in the first part we'll open +Photoshop +now I don't know if you have a Photoshop +license or not you would probably want +to have Photoshop although I provide the +cut images if you don't have it I think +it's definitely worth to have it uh you +can do most of these things with +gameport are the three tools but as a +fan of from the Past who eventually +took the plunge into Photoshop +it's much easier when you're working +with a designer that produces a +Photoshop file similarly sketch the same +thing although I have less experience +with that so I don't want to get into +that at the moment +so this is the PSD it includes a lot of +images as you can see I'm zooming in and +I'll pick the right one back yeah +so this is it I'll just try and crop it +so we'll just have that because I don't +care about everything else +and I want to cut out the relevant image +files the the shop at the top and +without all the text and the Fab and +everything else just the image of the +sandwich +just the same sandwich that's that's +good enough as it is actually does look +good +uh and the trick to cutting it is I'm +converting it to a Smart object that's +an option Photoshop here you need to +click on on the right area there and it +converts then double click that and it +opens it and that's it that's the image +and that's really convenient now notice +I'm using export +to export as a JPEG and reducing the +quality a bit and that removes all sorts +of metadata that photoshop might include +inside there which is really annoying +and took me ages to find out so instead +of doing save as like most people do +like I used to do do export big tip huge +difference in terms of file size +and the reason I'm using JPEG and and +saving it is because we'll use the image +as is and we won't use all sorts of um +uh how should I put it uh +uh multi images or anything we'll use +the image just as is because it's it +fits and that since we'll scale it +anywhere on the device and it will work +in that way better it's at all keep the +size of the resulting app smaller now +I'm hiding the elements the text and the +check box and now I can just select the +area that matters +and I'll do a copy merged +now copy merged is uh copies just the +visible pixels unlike a regular copy +Ctrl C or command C won't work in +Photoshop you need copy merged and then +when I create new it automatically sees +the size of the clipboard and uses that +so I don't even need to set the size +that's pretty great ux there +so I just type in the name although I +didn't need to and I can just paste it +in and that's the image that I need and +I can just save it in this case it's +okay to save although I I could do an +export there as well it doesn't matter +though because I'm going to cut it into +an eyepiece border +and code name one will regenerate the +image anyway so so the way I save it +doesn't really matter in terms of +metadata it's discarded anyway +so that's Photoshop and these are just +the two images we need now other things +that we'll do here is picking colors and +things like that I won't show them to +you it's just using the the Color Picker +tool and things like that +I'll just type in the the values for the +specific colors when we'll get there +uh and let's skip right ahead to the +interesting part which is the code +FAB Location +so the one of the important things in +the code and I want to talk about that a +bit more in depth is the floating action +button +now the area at the top that you see +here that's a toolbar +and I could cheat by essentially not +having one of the tricks that I did in +the past is just not having a title at +all and then drawing the entire area on +the top using just components that I add +into that area and that would work and +that would work great and I could I have +shipping apps that work like that but I +want to use the toolbar at least to some +degree +uh but I'm not using it in the same way +as most apps use the toolbar and the +reason is the specific Floating Action +button +so it resides in the area between the +toolbar and the content +now uh The Floating Action button is +positioned in a special area in codename +one called +the layout pane +now the layout pane is a layer that +resides on top of the content pane which +is the content pane is where all of the +content of the form the whole body +resides +and the layered pane resides on top of +that visually so whatever you put in the +layout pane will appear on top of +whatever's in the form +for the title bar or the toolbar in in +that sense they're separate and the +reason we decided to put the layered +pane on top of the content Pane and not +on top of everything is partially +historic and in retrospect retrospect +possibly wrong +although it does have advantages it does +have the advantage of the common use +case like things like validation things +like that are mostly relevant for the +content pane +but uh in retrospect it would have been +useful to have the ability to place +things also on top of the toolbar +but we don't have that other than the +gloss pane +and specifically before I digress too +much the layered pain doesn't have that +so we literally can't +put a component like that between the +toolbar and the form +so there's a trick +and the trick is that the toolbar can be +drawn in essentially on top of the +content +and the use case for that is in some +cases content needs to slide below the +toolbar for various effects including +this specific effect that's exactly one +of the use cases that we built this +feature for +it's kind of a hidden feature you need +to construct the toolbar with a true +Constructor and you need to install it +manually instead of the global toolbar +settings so we'll use that in code and +we'll show you +how to +disable the global toolbar which you can +disable temporarily or permanently +whichever or just not set it to true +in the init method and then just install +a +the type of toolbar that you need +the thing is when you have an overlay +toolbar of this type +the content will hide underneath that +toolbar +so we'll need a special component to +push the content downwards so so the the +portion with the image so it won't hide +the Apple the the +what is the tab bananas and everything +that we need to buy so it won't hide +that uh we'll add another invisible +component that's exactly the size of +that image +and it's going to reside below that +image and that will push the rest of the +things downwards and that sort of +uh solves that problem and The Floating +Action button can just be positioned +with because the toolbar is now part of +the parent form and +it all works as expected +and that's why some of the things that +I'm doing in terms of the code will look +uh how shall we put it weird +there's another reason and I'm keeping +that to the end of the specific design +choices I've made I'm +made the code +not necessarily in the standard way and +I'm keeping that as a secret to to the +finishing slides +CheckBoxes vs. Toggle Buttons +so the next thing I want to talk about +is uh the check boxes now you can +customize checkboxes and codename one to +look exactly like that you can do that +I don't like you it's um +it's possible but the moment you start +customizing checkboxes you do that in a +global API because there's lots of +assumptions inside the checkbox code +that expected to behave in a particular +way and this is a really really really +simple thing +so the thing is in codename one we have +special modes for checkbox and radio +buttons that allow them to render +without the actual check sign now that +doesn't sound like what we're doing here +but that's exactly what I'm doing here +I'm turning on the togglebot button mode +which effectively hides the check mark +from the checkbox and then this icon +that you will see here is really just an +icon of a check +and these are really toggle buttons +they're not check boxes so +it's it's really easy to to do a +checkbox with the toggle button +which is technically a checkbox uh +perhaps I didn't explain that in the +best way so check boxes can essentially +be toggle buttons and toggle buttons are +just buttons that are either pressed or +released and they don't have a check +next to them +uh so in this case these are literally +buttons that are checked or released and +the difference that the V sign next to +them is really just an icon it's not +really uh the standard checkbox +component +and the reason I chose to work in this +particular way is it's just a bit +simpler to customize that's pretty much +the only reason to do that +uh otherwise you know the customizing +the checkbox it's +um some apis that aren't convenient +so let's start by creating a new project +and I'll create a simple Bare Bones +application almost every project that I +create is just the standard Bare Bones +application with uh nothing in it so I +can edit myself I use the native theme +and it's important to define a correct +package name at this stage if you don't +know that by now don't use codename one +for your names please I see lots of +people do that please please don't do +that that's not a good idea uh +because this package names must be +unique and must be owned by you the +domain must be owned by you that's +crucial because that's something that +you will use to ship your app later +so don't screw that +piece up because that's really bad +and it's creating a new project and uh +it's just the standard typical Bare +Bones codename one project nothing in +there +and here we go regular form +so now let's paste the shopping list in +here +and move on +to actually creating the right form with +uh the right name for the form and the +right title for the form +so it's a shopping list application +and I'll start with the building the +individual check boxes and separators +from that string array I pasted above +but this is currently everything is +hard-coded for simplicity's sake it's +not an actual application yet +so I need to import obviously these uh +component and checkbox and all of that +and netbeans is a bit acting up in terms +of uh everything I have running +concurrently on my machine +uh so forgive me about that uh the check +box uh I'm creating it with a string +Constructor and I need to do several +things in order for the checkbox to +appear as it is in the design +and one of those things is to set the +Gap the Gap determines the distance +between the icon and the text +so I need to set the Gap to three +millimeters so that it will look uh +similar to the design where everything +is spaced out +and uh notice I I will I also need to +place the text to the right of the icon +using the set text position +and that will +place it so so it looks that way +otherwise the text will be below the +icon and it's not the design UI that we +actually want +now notice that the design uses a lot of +spacing one of the things I see +designers do developers do incorrectly +is sort of ignore the spacing or +automatically set the margin and padding +to zero everywhere +and that's not good you need to use +white space and notice it really really +well in the designs white space is super +important when it comes to design +so pay attention to that and +try to use it effectively so designs +will look good +and of course last but not least we're +adding the icon itself that's a font +image so it's literally a font icon from +the built-in material design fonts +so I'm just using a material font with a +UI ID +and the UI ID will be used to get the +design the colors and the sign +sorry in this case not the size because +I'm passing the size as the last +argument but uh the colors foreground +background opacity of uh actually +transparency but +you know Nuance uh I'm giving it that UI +ID that's where I can customize that UI +ID later on in the designer tool which +will move to right after this +and set the correct colors I'm using +separate icons for the icon and the +Pressed icon so each one would look a +bit different +I'm obviously giving uh different UI IDs +although it's technically the same icon +if you look at it +and that's essentially the checkbox the +next stage is a separator the separator +is actually a really cool uh thing it's +just a label +that literally does nothing other than +have a specific uh UI ID +of separator +the thing is that I need to invoke a +special method here +and that method indicates that I want +the label to render +despite the fact that it's empty +because there's a special case for empty +labels that hides them +and this sort of disables that special +case because it's very common to use +labels and have them disappear if I +didn't want them so that's the default +so you need to call so show even if +blank to do that +and I will just do a for Loop over the +elements within uh the shopping list and +just add them one by one +and I'm just adding them all to the +shopping list add create check and then +add the separator to separator to the +next line +and um doing it as a single statement +mostly +to signify that it is a signals a single +statement +logically +if not physically +and next we'll move to the designer and +in the designer tool I'll +uh add elements first for the check +boxes uh the checked icon so it will +have the right color +and I have it in my clipboard here +that's specific the specifically the +selected color and I'll keep it +transparent +then I'll copy that +and I'll create an unchecked icon +and the color there is +pretty easy to remember +it's just a form of Gray +and the transparency again same thing we +don't want a background for that +and last but not least is separator +separator is pretty easy +because separate sorry a toggle button +first +so in the case of the toggle button +uh we have again the specific background +colors +not as easy to remember but not too hard +and one of the things that uh +we need to do with the toggle button +besides Define the transparency to a +pack in this particular case is to +disable the Border because toggle button +is a standard component and has a border +by default so I need to select an empty +border to override the built-in default +border for toggle button next I need to +define a lot of padding so it will feel +um +spacious again back to the the original +design and the importance of using +padding to make user interface field +wide and I'm setting the margin to zero +because they need to literally touch one +another I'm also explicitly setting the +alignment to left because the default +alignment for buttons is often Center +and I'm setting the font to be a light +font uh sorry a thin font at a specific +millimeter size and that will +um experimented with it with it and +looked reasonably good now I'm using the +native fonts which don't look identical +everywhere +and I'm pasting it to the different +stages +now and here the last stage and this is +really important +I pasted the toggle button to uh +the selected now lifted as is it's not +necessarily what I'd want to do but I +did it right now I might change it in +the future +but the Press state which is the one +we're looking at is when when a toggle +button is physically pressed down +and in this case I want the nine piece +border that I mentioned earlier +so before we go into doing that I want +to have a word about what nine piece +9-Piece Border +borders are in case you +aren't familiar with them enough +haven't played with them didn't read +that developer guide section about them +which I worked really hard on writing so +take that passive aggressive and use +that to study that that section against +uh and if it's not great or excellent +please edit it on the wiki so a nine +piece border essentially takes it's +great for symmetric shapes although it +works with some asymmetric shapes you +will notice the shapes on the left are +colored in green those are great shapes +or good shapes in the case of the +rightmost one +four nine piece borders they will work +with nine piece of borders the shapes on +the right will not +and the reason will become apparent when +I explain uh more accurately why it's +also harder to work with some types of +gradients with nine piece borders +the advantages that nine piece borders +have is that they're efficient and +they're low memory and they're they +don't degrade when you uh +scale them to Infinity they look +essentially the same +and the reason is the way that they work +so a nine piece border Cuts an image +into these nine pieces that you see +before View the four corners +four sides and a center +and what diff --git a/docs/website/video-transcripts/WYlM2utu4Ps.json b/docs/website/video-transcripts/WYlM2utu4Ps.json new file mode 100644 index 0000000000..905a716f36 --- /dev/null +++ b/docs/website/video-transcripts/WYlM2utu4Ps.json @@ -0,0 +1,8 @@ +{ + "line_count": 32, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 171, + "youtube_id": "WYlM2utu4Ps" +} diff --git a/docs/website/video-transcripts/WYlM2utu4Ps.txt b/docs/website/video-transcripts/WYlM2utu4Ps.txt new file mode 100644 index 0000000000..3042494d69 --- /dev/null +++ b/docs/website/video-transcripts/WYlM2utu4Ps.txt @@ -0,0 +1,32 @@ +right now we have only one subclass of +the base form +the main menu form which represents the +main ui +the list of elements on top +is a standard code codename one list +normally selection won't appear in the +list once our finger is lifted +so we had to explicitly invoke some +methods to force that behavior +we also set the list to horizontal mode +and fixed center mode +the dish container uses a text area for +the content +since text areas can grow +we need to disable that behavior +as we prefer cropping of the text in +this case +we're adding the two buttons using this +code which includes the pricing in the +label +notice these are enclosed in a grid +layout to the south portion of the +container +we discussed masking before +and here is where we apply it to the +image object +so it has the rounded corner appearance +the mask with round rect method uses +this image as a mask +and applies it +to the given image diff --git a/docs/website/video-transcripts/W_1S2Rzgff8.json b/docs/website/video-transcripts/W_1S2Rzgff8.json new file mode 100644 index 0000000000..eaa690f6ee --- /dev/null +++ b/docs/website/video-transcripts/W_1S2Rzgff8.json @@ -0,0 +1,8 @@ +{ + "line_count": 99, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 583, + "youtube_id": "W_1S2Rzgff8" +} diff --git a/docs/website/video-transcripts/W_1S2Rzgff8.txt b/docs/website/video-transcripts/W_1S2Rzgff8.txt new file mode 100644 index 0000000000..b234625557 --- /dev/null +++ b/docs/website/video-transcripts/W_1S2Rzgff8.txt @@ -0,0 +1,99 @@ +all media objects for instance images +videos etc +are stored in the media entity +this is pretty convenient as it lets us +store media files in the database under +a separate database table +which would be better for performance +there are pros and cons for storing +media in a database +a pro would be that it works with +clustering as data in the day is in the +database and not on a specific server +file +however it might make the database too +large and harder to back up +performance is more complicated it +should be reasonable as long as table is +separate +historically performance of blobs in the +database was pretty bad and there might +be issues in some deployments or some +database types so this might be +challenging +both approaches would be simple unless +scaling is involved in which case we'd +need to store files somewhere +there is a common alternative of using a +service dedicated to hosting files +either homegrown or in the cloud for +instance s3 +since the media object is a is separate +it makes sense to have it anyway and if +we choose to use such a service we can +include the file key for the cloud +object instead of the actual media file +the media object itself is pretty simple +just like the user object we use a ui +uuid to represent a media object +this is the file name used when +uploading which might be relevant if we +let the user browse the files he +uploaded in the future +the timestamp when the media was +uploaded using long makes this simpler +to work with although +date might be more convenient in a +database +role can be used to indicate the purpose +of the image for instance if it can be +an avatar to indicate the image serves +as an avatar +visibility is the privacy scope of the +image for instance friends would +indicate only friends can see the image +whereas public would indicate it's +visible to everyone +this is the user that uploaded this +media object +the data is marked as a lob which lets +us store relatively large objects notice +that there is still a limit on file +upload size and forced by the web server +support in spring boot +the media object has a dow as well it +lets us pass the media data back and +forth through the service layer +as you can see the rest of the code is +only getters and setters +nothing much +the object is relatively simple and only +contains +one small dependency media dao +we could potentially enhance it with +additional data such as description +tagging and even auto generated content +based on image recognition +but for now that's enough +before we go into the media dao we also +need to mention the crude repository +interface for this class +it's simple as we don't need any queries +at this point +now that we got this out of the way we +can look at the dao it's trivial too +the fields map directly to the fields of +the media class +in this case +the obvious exception is the user dial +object which uses the dial type instead +of the user type +as we did with the user object we have a +default constructor and a convenience +constructor both of these were generated +by the ids completion suggestions +the rest of the code is just +auto-generated getters and setters +nothing else +and with that we conclude the media +object entity diff --git a/docs/website/video-transcripts/Wiy2goFwfQA.json b/docs/website/video-transcripts/Wiy2goFwfQA.json new file mode 100644 index 0000000000..6952633750 --- /dev/null +++ b/docs/website/video-transcripts/Wiy2goFwfQA.json @@ -0,0 +1,8 @@ +{ + "line_count": 30, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 150, + "youtube_id": "Wiy2goFwfQA" +} diff --git a/docs/website/video-transcripts/Wiy2goFwfQA.txt b/docs/website/video-transcripts/Wiy2goFwfQA.txt new file mode 100644 index 0000000000..126ed8c49d --- /dev/null +++ b/docs/website/video-transcripts/Wiy2goFwfQA.txt @@ -0,0 +1,30 @@ +i've discussed the star settings as part +of the star form +here is the class itself +the style setting +class +is a standard property business object +class which means it can be persisted +implicitly into the database and +serialized to the server using +json web services +to support this in the app storage i've +added an insert or update method which +implicitly checks if a style exists in +the database +and if not it adds it +the fetch style method works in a very +similar way to the api i showed for +fetching the app storage +but a bit simpler +in that sense fetch styles +is similar as it fetches the list of all +the styles +the last bit of changes just consists of +creating the tables +and setting the primary key +so this class can be +properly saved +into sql +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/XSztvFzQAr0.json b/docs/website/video-transcripts/XSztvFzQAr0.json new file mode 100644 index 0000000000..0dac158859 --- /dev/null +++ b/docs/website/video-transcripts/XSztvFzQAr0.json @@ -0,0 +1,8 @@ +{ + "line_count": 392, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 5507, + "youtube_id": "XSztvFzQAr0" +} diff --git a/docs/website/video-transcripts/XSztvFzQAr0.txt b/docs/website/video-transcripts/XSztvFzQAr0.txt new file mode 100644 index 0000000000..3737c0c777 --- /dev/null +++ b/docs/website/video-transcripts/XSztvFzQAr0.txt @@ -0,0 +1,392 @@ +hi everyone today i'd like to talk about the basic one of the first questions we +get asked on the chat in the website and that's what is code name one +now my two second answers obviously +it's java for mobile application developers that's sort of the quick answer +but it kind of hides a lot of the complexity and the history so this is +really the long version of that our answer which includes +everything that describes what codename one is now even if you've worked with +codename one built an app and shipped it and did everything +i'm sure there's some thing within this presentation that you didn't know +or didn't remember and even i had to look up a couple of things like how many +releases we've had since we launched which is something oh that literally surprised me +so first let's start with a bit of history so +the origin of codename one is when by the way it's not chen +if you only saw his name online uh ken fishbein uh +started the luit project at uh sun microsystems back in 2006 2007 +and he started it with the goal of ending device fragmentation +back then it was uh the mobile device fragmentation it was before android existed was before +ios obviously uh existed so device fragmentation was mostly j2me +and rim devices back then and he built something it was used +internally the project for which it was built actually eventually closed down but the library sort of +became very popular and uh we saw the potential and +eventually we started working with customers and eventually we open sourced it in 2008 as part of sun microsystems it was +a big announcement java 1 main stage and everything and +a really a really big deal and it gained quite a bit of community traction +the problem is it was standing on top of j20 which was declining fast +and some support for rim the community +came came through and actually did a port to android which we were prohibited +from doing because of all of the complexity related to that and we had some proof of concept uh port +to ios that worked but again not something we could release as part of sun microsystems +and not as part of oracle later on so eventually uh +in 2012 we left and formed cone name one both myself and hen +and there we took that open source project as +the bit of the beginning and built the whole thing around it the +whole thing that is code name one today is much much bigger and you'll see in the +following slide of what it actually is so and since then we've +gone from literally zero to millions of installs hundreds of millions of installs +10 releases and added lots of additional platform support uh besides the ios and android +with which we launched we also added things we never imagined that will add like javascript support +and desktop support and windows support we built three ports for windows +nightmare but it was a huge journey to get to this +point from that beginning but +let's take a step back now and look at what that is so +from the list these are essentially the five things that are code name one +and it's really a combination of all of those together so +i'll talk about each one of these sentences in the next slide in the next few slides because for each +one of them i want to talk uh more in depth and +the thing is that one of the reasons we came up with the +the name code name one was really by accident we literally had a code name written on uh slides and sort of yeah it +sounds nice and the url is available let's take that but the thing is that it really fits +we take these five completely separate things that are supposedly unrelated to one another +and we ship them as a single product you just install the id plug-in and that's it codename one is installed +and that's why in retrospect that's why we call it codename one it sort of works +and uh and we decided that we like that so anyway the first thing is a virtual +machine now on most platforms we don't actually ship a virtual machine +so for instance uh on android we just use dalvik or art +that exists it works there rather well and we just work with that for the desktop port we can just embed uh +the jre from oracle into the port and essentially just ship that +but some of the platforms that we target don't have java built in and the best example for that is ios +which doesn't have java support initially when we launched we used an existing product called xmlvm which is +an open source project that translates uh by code to to c code essentially +and uh it worked reasonably well but it wasn't really well maintained maintained it was huge it had a lot of complexity +that we didn't really need and eventually when we saw the project +winding down and had lots of issues that we've constantly struggled with +we decided that the right thing to do was build something of our own +and in retrospect that was very good decision to make because we wanted to keep the simplicity +of xml vm and wanted to simplify it further by having the bytecode translated to c +and not directly to arm code and this is really important because when we translate you see +we can we can compile using xcode using the official tool that apple ships +and that's crucial because that means we'll be future compatible to any version +of ios that apple ships because they won't stop supporting c because all the games +are built with it everything is built with it so codename one will always work uh +with apple's uh products with minimal changes at best for +instance they came up with uh arm64 a while back +and a lot of competitors scrambled to try and support that +and they have bitcode now which again people are scrambling to support and don't support +we support both without much of a problem just flipping a switch and the reason for that is a very +conservative approach of trans uh transpiling to c code instead of directly to +arm assembly which would be problematic this has another advantage that we can +actually take the compiled code essentially the c project +and with the include sources feature we can literally run the compiled application on apple's +tools and debug and find issues and it's easy for us when we find an issue in +codename one itself but a lot of developers also use it to debug and profile their projects which +i'll talk about in the boot camp as well +windows works a bit differently we had a version that transpiled to c-sharp but that was problematic because +of small really painful nuances between c-sharp and java that make it +almost impossible to do something like that in an efficient way +so instead we chose to adapt a port of ikvm which is a virtual +machine for that takes java bytecode and runs it on.net +and we took that and adapted it to work with the universal windows platform and +it's uh we got some community code and we forked it and we did a lot of work there and +it's on our repository just like uh our vm jet which does uh sorry vm +that does uh the bytecode to c translation all of that is open source obviously +and it's a pretty huge project that we did just recently +last year and it's uh working reasonably well +and the last vm that we have essentially isn't is one that we didn't write at all +it's tvm and it works beautifully and it transpires +java bytecode to javascript and it works in a very similar way to gwt +but doesn't require the source code it works with a bytecode and it's more efficient more modern it works with +threads which is really impressive and it's a pretty great solution so +that's one of the few solutions that we're able to take as is and just use it +so it's pretty a pretty nice solution so that's the virtual machines that we +use the second piece of the puzzle is the api for devices and that's what +essentially luit evolved into so a huge part of that is the user +interface aspect which includes a lot of the legacy of luit so sometimes if you +see something in codename one that doesn't make sense so looks like it's old it's because it is old +we built on that infrastructure that took us years and years and years to build and +we kept on building on top of it because essentially codename one is to some degree an operating system of +its own and that takes years to build and we've had these years to build that with a big +open source community buckets and microsystems also did a lot of that work +and we've got this huge api for mostly user interface because that's the +biggest piece of pretty much any mobile application but we also abstract everything else from +file system to storage to databases to media to pretty much anything you'd +expect there's some legacy there but that's actually part of the good +because it provides stability and continuity the porting layer is +the co-name one implementation and that's something that allows us to separate the api +that's portable to all the platforms from our specific implementation now +that's a mostly hidden piece of codename one we hide it behind package protected fields and things like that you're not +supposed to touch it because it's uh an implementation detail and might +change without warning but essentially that's the piece that allows us to port codename one anywhere +so for instance the javascript port the vm is open source but the the port +itself is something that we actually had to sit down and write this huge port where we draw on the canvas and do all +of that stuff behind the scenes to get everything to work in the browser the same thing is +true for ios where we spent a lot of time building writing a lot of opengl code +and all sorts of other 2d graphic code and you name it all of that is written in +native cn objective c to to map everything to +the requirements of the api and that's how we maintain portability +for all the various platforms now the lightweight ui approach i'll +talk about it more in depth after i finish this segment because it deserves +its own discussion the whole ui is uses that approach and +that's probably uh the biggest differentiator between codename one and pretty much +everything else that is out there although there's quite a few it's not a new invention that we +made it's it's a very old idea that originates from small talk +and and it's pretty much the only way to build a completely portable platform +now the api itself is really simple it's designed for portability and to be +really easy to use so some things are sometimes oversimplified to keep them +sensible but it's designed designed to still be flexible enough so you could do anything +you can control any pixel you can do anything and the last thing and this is really +important the api on the device is statically linked now +that dynamic linking is a very powerful tool if you're building an operating system if you're building an app +static linking has huge benefits because it provides you the stability and consistency +that you'd often want for your apps so for instance if i build an app and ship +it i'd like it to be as stable as possible and i wouldn't want something to change +suddenly with the running app but if you depend on an external library and the operating system replaces that +library you might your app might break because it depends depended on something that +might be minor in theory but could be devastating for your app so +codename one codeman's library is statically linked in and +one of the advantages it gives us as the developers of codename one is that we can move fast and sometimes break things +in terms of compatibility because we know that +shipping apps won't break if your app has already shipped changes that we make right now +won't break that app because that app is oblivious to the changes that we've made to codename one because it's statically +linked if you will try to rebuild the app you will find that something broke +but then you will find it at that point and fix it or notify us and we'll fix it +and that allows us to move faster than say google so for instance google if +they change something even minor with an android it might break shipping apps +and that's a huge problem for them so they need to move very carefully and +they often break things and it's a desire disaster in our case if we break something it +isn't as big a deal for our users because you can see it and you can fix it and +there's also versioned builds that allow you to build against a stable release +within codename one and that way even if we do break something you can still target a specific version and won't +fail on that so the ide plugins are often +given more credit than they're worth the ide plugins are relatively simple +containers that contain the whole shebang so you install them and they include the +pieces of the api and project templates and they include the tooling which i'll talk about in the next slide +but they themselves aren't that clever they don't include that much logic +they generally serve as a sort of glorified installer for codename +one's uh tools and for our [Music] +templates they don't really include that much logic within them +but they're useful as as an installer essentially +now the tools are the are the brains behind everything so we've got build +tools which are essentially uh based on ant and i can go into a +long discussion on why we chose and mostly it was legacy +but we've evaluated other build approaches like maven and gradle and +we constantly looked at options like that and they don't provide an advantage for our +very very narrow use case and only disadvantages so +i'll probably write about it at some point but we we don't just don't have an advantage of +leaving ant and and it makes a lot of sense because it's very familiar and is supported by all +ids except for android studio +but it works on intellij it works on the clips it works on netbeans so that's great +and we also have the device simulator and that's crucial for the device simulator to work we need lightweight +architecture which i'll talk about later otherwise this would be impossible we've got +two gui builders essentially the new gui builder and the older one +which is inside the resource editor slash designer the resource center slash designer +serves as a sort of universal tool a sort of swiss army knife that provides theming +localization image management and obviously the old gui build and all of these tools combined +are essentially uh the ways you interface with codename one besides the api +and they're really important for that and the last piece of the puzzle and possibly one of the more controversial +and important pieces is the cloud build now with recent versions we also support +offline build but if you'd actually use that you'd understand why we have the cloud build +offline build is painful you need to have a mac if you want to build for ios +you need to configure it in a very specific way and this is very error prone and things +can fail and lots of annoying ways and that's just for mac os if you want +windows built and you need a windows machine and different type of configuration and that's a pain in its own right +and android also the installation of the right versions of the sdks and +everything it's it's hell on wheels to to get a build setup working +so we essentially have machines in the cloud that are set up properly to build +uh our current versions of the applications and by the way these are things that we +need to constantly update because apple releases a new version google releases something new and and so forth so we +constantly need to update these machines with uh additional uh version tools versions of +the sdks and things like that so this is a really +something that's much simpler thanks to the central uh management of this +the build tools essentially communicate with our cloud and essentially send tasks that are later on picked by the +build server compiled with native tooling and that way you don't need a mac thanks to the +cloud build and the build is actually faster i personally use the cloud build even though i can use the offline builder +because it's faster and it's easier so when i use offline build it's +it's much slower so because the machines we have in the cloud are more powerful it's actually +faster to send my jar into the cloud and get the binary back then to build everything +locally that's uh that surprised me when i first found found that out and i have a relatively +powerful laptop so it was weird um +and it's it's a bit controversial because people sometimes have issue with sending things +to the cloud and uh i don't think that's as much a problem +in 2017 because we use dropbox for everything and we store important files +everywhere and it's not as if our computers are +halos of impenetrable security so the idea our cloud is secure and we do +spend a lot of time there but uh source code theft existed in +the 90s and our cloud never gets your source it only gets your bart code +so in that sense it's a very secure solution and i think most people who +have an issue with that haven't really gone through the +various ways in which offline build can fail +and all of these ways that it can fail +are applicable in the same way anyway +the thing i wanted to focus about most here is the lightweight architecture because i think that's +probably uh even more controversial than uh the +cloud build approach if if that's possible and that +it's one of those things that uh is sometimes hard to convey +to developers both in terms of how it's the advantages and the disadvantages +so first let's uh talk about what frameworks you might be familiar +with that are lightweight versus the ones that are heavyweight that you might be familiar with +now uh notice broadway lightweight architecture obviously a ui aspect not +so it's irrelevant to the other aspects of codename one so codename one is lightweight +and swing and java fx and qt are all lightweight architectures i didn't mention flash and lots of other +uh frameworks are lightweight frameworks and heavyweight frameworks +there's several examples swt awt xamarin uh accelerator all heavyweight +uh frameworks to one degree or another some frameworks mix heavyweight and lightweight +to some degree mostly if they mix it heavily to one side then they're considered heavy weight if +we we allow some heavy weight widgets but i digress so +the the word lightweight is sometimes when i used it to refer to codename one sometimes people hey you're +making fun of other frameworks as if they're heavyweight well it's not a term that we made up it's a term that comes +from a swing and originally uh +it was just used to differentiate between swing and awt now in a lightweight framework uh the +framework draws its own components in the heavyweight framework it uses native widgets for everything +and that is sounds more efficient but it isn't because you need to constantly +communicate with the native system and that has uh carries a weight +and in that sense it's heavyweight uh so that term was uh given by the swing team +to indicate that uh that it uses lightweight architecture as opposed to the peer architecture of +heavyweight frameworks now +the best way to describe them the you can't really say that one approach +is better than another well i can okay i'll i'll go with it i'll go +on a limb and say lightweight is better but obviously that's an opinion there's facts that +work for both sides both sides have reasonable claims i think that lightweight has better claims +objectively but obviously i'm biased so it's hard to to tell one of the things +that i've noticed over the years +well before codename one when people would argue between swt and swing +this is a religious debate you either buy into lightweight or you buy into heavyweight +if you bought into the religion there's little that i can say that will sway you one way or another +if you are one of the swinging voice voice votes in the middle or if you're on the lightweight side +then that can work otherwise if you're so gang-ho on heavyweight +well not much i can say so in lightweight frameworks we draw our +own widgets that means literally every pixel in the screen is under our control +and that's a huge advantage so for instance i can just overwrite paint on a painter and just draw in a specific +location and be able to do a very complex and yet still portable +code and we handle the events and +user input for pretty much every widget so when we draw a button we literally +draw the lines of the button or all the images that represent the button then the text and when a user touches the +screen we detect oh we touched in the area of the button and do all of the things like the button +press animation and everything we literally do all of that we lay out the widgets we arrange them we do everything +that's why it took us so long to build codename to build luit essentially and then codename one +because we needed to do all of that and that's essentially replicate what the operating system does +and we also provide all the tooling that's related to that from the gui builders +and uh apis and everything heavyweight goes uh +the other way around in some regards it's simpler to implement for the guys building the framework +uh they just wrap the native platform you know i'm saying just it's not +necessarily as simple as that but often it is because some of the heavyweight +frameworks are just scripts that take the native documentation and map it directly +the api is generally thinner and simpler because +they can't go much beyond the native widgets if they try to be portable they need to +restrict themselves to the lowest common denominator we in lightweight frameworks i often refer +to it as the highest common denominator because we can implement things that aren't available +in a platform in a way that's portable whereas with heavyweights +this isn't usually done and when they do that it's often harder because they're kind of +working against the platform and it's uh all of the things like the layouts and +everything might flunk if you use them in a way that they're not supposed to be used +so and one of the more important things about heavyweight is that you need to use the platform native tools so for +instance if you're using a heavyweight framework like xamarin you'd need a mac to debug +an ios app otherwise it just won't work because you'd um +you really need apple's simulator to run and +run your application uh you you can't run it with uh with +your own simulator because they didn't implement the functionality they only implemented wrappers around the +functionality built built by apple so with a lightweight framework we can sort +of hide apple doesn't really exist for us we can debug on windows and +application and it will work and with xamarin and this is really where +we're getting to the advantages with xamarin or with a tool like that you'd end up having a +layer on top of the native tool and in that case you +you take all of the complexities all of the bugs and all of the issues of the native platform +and add to them the framework issues so i've heard some people complain about +some of the native frameworks of the heavyweight ones that they're not as +stable as they should be the problem is when people make that complaint +you never really know if the problem is within the framework or within the user's installation on his machine +where he might have installed it on an unsupported version of xcode or something like that and +something in the connection there broke with us because the implementation is +based on uh on a lightweight framework we literally implemented everything so when something +breaks it's totally our fault but that's part of the beauty of it we +can actually fix everything and we're not as dependent on +apple or google for uh anything other than the ability to draw +which is mostly consistent and doesn't break as often and +that provides us a huge level of portability and there's a good reason uh +that lightweight frameworks existed through history and that's for portability they were invented during +the age of small talk and later on advocated by people who needed extreme levels of portability +and that's a huge advantage and disputed of lightweight frameworks +on the other side os conventions are sometimes harder to get +from lightweight frameworks so native frameworks automatically get the +os conventions without a problem we resor we use theming to give +os style behavior but obviously it's sometimes a race to +keep up with the way an operating system behaves so nuances might exist +that's always a trade-off between portability ease of use that we provide +and customizability and the other hand it's os conventions +now the ability to be consistent is another advantage of +lightweight frameworks where we can customize the application to be pixel perfect to the way your +designer envisioned it and that's something that's much harder to do +with uh heavyweight framework because sometimes uh one operating system will behave in +one way and another even from the same vendor so an android version +x from samsung might behave differently from android version y from htc +and it's really hard to get these things to match up perfectly with a heavyweight +framework but we can get pixel perfect behavior and one of the nice things here is +performance so a lot of times performance is +very portable so when you look at a specific +use case where performance isn't as great the solution to performances +in at least 90 percent of the cases is to cache cache data keep it in memory +or keep it in the gpu and the graphic processing unit and to do that +we we need to load the right data in advance put it in the right place and make sure it act +it works fast enough and when we do an optimization like that +it's usually very portable and because everything is most of the +code is written in java in our case and its performance will be very consistent +across platforms so if we optimize something in caching or in some other strategy for +android this optimization will work similarly to ios etc +so this is a huge advantage of lightweight +solutions with heavyweight solutions performance is sometimes +good because heavyweight widgets themselves are optimized by the operating system but their behavior isn't consistent so +something that might uh make a heavyweight weight widget like +a list behave quickly on ios might make it behave more slowly on android and so +you end up with a lot of nuance when it comes to performance that's really hard to get right across +operating systems and some of the strategies might not translate as well from ios to android +and so forth so this is a +very debatable and complex issue the issue of performance +the heavyweight frameworks are easier to build for the framework designer +so it's if someone wants to build a framework it's much easier to build a heavyweight one than lightweight one +that's what i meant by the easier to build it was probably +what i was thinking and the last bit about heavyweight and +by the way i really struggled trying to find entries that would make heavyweights +seem uh good to find the advantages uh column +because honestly that's a bit of confirmation bias +but there isn't that much other than matches os conventions and potentially +performant so access to native os features essentially when you want to do uh +to include a feature like google maps for instance so in codename one you can add google maps but that's because we +uh added a peer component and that's a bit challenging you need to know how to do +that and you need to uh to add a peer and it's not as bad today +it actually works relatively well today to do that but um +it's not as easy as some of the native frameworks allow you to +of the heavyweight frameworks uh that allow you to do that and uh +in that sense it's a bit harder sometimes so it's possible in both but +in codename one because of the lightweight architecture we draw things and then if you add something like a map +which is drawn by the system we need to know +when to draw on top of it and when to draw below it and that is sometimes a bit complex and we +also lay it out so so there's various nuances that you need to be familiar with when it comes to +dealing with the peer components and with a heavyweight framework it +works a bit better so +in codename one as i mentioned earlier native peers uh are supported which is +really important some of the lightweight frameworks don't allow so for instance qut doesn't allow its swing has issues with +it uh actually not that far from our issues that we had with it uh +but it's it's a pretty big deal because usually lightweight frameworks don't support native peers and we +think that's really important that we do +we also support z ordering and native components which is also very important +being able to put something on top of that now the simulator +which is a point that i didn't mention earlier is enabled thanks to the lightweight +architecture because we draw everything we can simulate everything on our own and we don't need a mac for that in +order to debug the moment that exists we don't need a mac at all +so we can use uh the cloud build to actually build the native application go +directly to device because we can debug locally and then go directly to the device we +don't need um that problem and that thing and uh with native frameworks that's not an +option so cloud build would be redundant because you'd need a mac anyway +so with heavyweight widgets uh it's it's redundant in that sense +um now codename one as i said is very performant because we use opengl to do +all the drawing and everything so it has native level of performance just like games are fast code name one is fast +and uh it was optimized during the days of uh feature phones so if it ran on a two +megabyte nokia it should run fine on a on a gigabyte worth of ram on an iphone +so it's pretty it's a very efficient framework in that sense +and it's remarkably customizable we control literally every pixel and so do you so you can literally change +everything in the screen and that's unparalleled by heavyweight widget +solutions so i hope this presentation +gave you the tools to understand what a part of codename one +where it came from uh how big it is in terms of the functionality and understand the pieces +that comprise of it and also how they all compete with one another +for better and for worse thank you diff --git a/docs/website/video-transcripts/Xrolmjg8Dr0.json b/docs/website/video-transcripts/Xrolmjg8Dr0.json new file mode 100644 index 0000000000..436f7650c5 --- /dev/null +++ b/docs/website/video-transcripts/Xrolmjg8Dr0.json @@ -0,0 +1,8 @@ +{ + "line_count": 26, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 712, + "youtube_id": "Xrolmjg8Dr0" +} diff --git a/docs/website/video-transcripts/Xrolmjg8Dr0.txt b/docs/website/video-transcripts/Xrolmjg8Dr0.txt new file mode 100644 index 0000000000..ab0fc9038a --- /dev/null +++ b/docs/website/video-transcripts/Xrolmjg8Dr0.txt @@ -0,0 +1,43 @@ +We are nearing the end and it's time the last two big features: media & push. + +Here I'll focus on the former. Media includes all the aspects of the media from acquisition whether from the camera or gallery, to the upload & then viewing said media. +For the sake of simplicity I'll support only one media file and not the full breadth of ways Facebook allows you to submit/upload and manage media files. +We'll start with the changes in the server then move through the steps in that sequence. + +The changes in the server for media support are minimal. Most of the work is already done we just need to wire and tune a few things. One thing I did need to change was the packet size in the database. + +I had to add the argument --max_allowed_packet=256M to mySQL's launch arguments so it would work with large video files. + +The most obvious change is to Post & its related DAO/Service classes. We'll start with Post itself. + +I've added a relation to media files that lets us reference a specific media object. Notice you can add multiple media files. I won't use that in the code because that makes layout of these files a bit harder and I don't want to get into that. I also added a getter & setter but I won't show them as it's pretty obvious. + +The next change in this class is to the DAO method of Post. Notice attachments are represented as a Map of Strings and not as MediaDAO objects, we don't want to expose the full DAO to the client + +A map is enough as all we need in the client is the media ID & mime type + +This map is now accepted in the PostDAO constructor + +This leads me directly to the PostDAO class which has the predictable change within it. This map includes the set of attachments we created in Post + +it's reflected in the constructor & in the getters/setters that we won’t detail. This will actually work seamlessly for return values + +but for upload we will need an additional set of changes in PostService. +First we need media access which is the one repository we didn't include in the PostService. We need this so we can translate client request to server side media objects. + +The work in the service is done in the post() method, it's a relatively simple change of translating media id's to media objects. + +Since we are creating a new Post it's safe to create a set from scratch + +We just use findById to set a media object into place. That's a trivial change but with it everything will work seamlessly on the server side as the DAO will handle the new JSON requests and server API doesn't need to change! + +There is one tiny change we need to make to media service. It's very nuanced, see if you can spot it… I’ll give you a hint + +See it? If you don’t I can give you a bigger hint + +Notice it yet? We switched @RequestHeader to @RequestParam so the auth can be passed as an argument. +This is a security risk, if you share that URL from your app it will essentially hand out the key to your app! +So why do something like this if there is a risk in it? +In the client side when I want to show an image I can just submit a URL to the URLImage API. However, if I have a required header the only way to pass it is via global headers which would be a mistake here as it might leak that key to a 3rd party website by mistake. I could have left the API as it was and just written more elaborate code on the client... +I chose to take this shortcut both because it's simple and also to explain the nuances of security risks. This isn't a vulnerability, but it can become one if we aren't careful. When you are the only person working on the project this isn't a big deal but with more engineers coming on board details like this might be ignored/missed by latecomers. +With that the changes to support media on the server are done and we can move to the client side. diff --git a/docs/website/video-transcripts/YE7OQFQE0yA.json b/docs/website/video-transcripts/YE7OQFQE0yA.json new file mode 100644 index 0000000000..976bc90c61 --- /dev/null +++ b/docs/website/video-transcripts/YE7OQFQE0yA.json @@ -0,0 +1,8 @@ +{ + "line_count": 86, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 458, + "youtube_id": "YE7OQFQE0yA" +} diff --git a/docs/website/video-transcripts/YE7OQFQE0yA.txt b/docs/website/video-transcripts/YE7OQFQE0yA.txt new file mode 100644 index 0000000000..8d6ed607d6 --- /dev/null +++ b/docs/website/video-transcripts/YE7OQFQE0yA.txt @@ -0,0 +1,86 @@ +animations often evolve in close +proximity to the final application +i don't want to spend too much time +building up things that are +overly specific to uber's design +as you would have to throw them away +anyway +one uber specific animation that i liked +a lot +is a blue highlight circle around the +floating action button +this is android specific for uber on ios +they have a circle but it's inside the +floating action button +i'm guessing this has more to do with +the implementation +than a deliberate design choice +this is pretty easy to implement the +round border allows us to stroke the +border with any color +and to any angle +that allowed this animation with almost +no code +the class has a private constructor +associated with the floating action +button +we create instances of this class by +using the bind method +we need the existing ui id of the +floating action button +which might be different from the +default +we manipulate the style of the floating +action button directly +so we will use the ui id to restore the +default settings +when we are done +the animation stroke and speed are hard +coded to 0.5 millimeter stroke +and 1.5 second per progress rotation +this can be manipulated to create more +deterministic +progress indicator +i use a timer every 30 milliseconds to +update the ui +notice that if a timer runs too fast it +will be throttled and invoked much later +this won't make much of a difference +since we use motion to determine the +actual speed of progress +every time the motion finishes a cycle +we start again from scratch to give the +feel of +infinite progress +i update all three styles individually +instead of using +get all styles +the main reason is preserving the +uniqueness of each style +which get all styles would override +i just get the existing border +while relying on the fact that it's a +round border +and customize the angle properly +bind starts the timer and sets the +animation on +we store the instance of this class in a +client property so we can stop the +animation +later +when stopping the animation i also set +the ui id again to reset all the changes +that were made to the style object +unlike the other animations we've had +i took a radically different approach +of using the ui timer and the style +i used ui timer instead of timer as it +makes sure to run the callbacks directly +on the event dispatch thread +so there is no need for call serially +i've added this to the code in enter +password form so the progress indication +appears there +i essentially replaced all of the code +that referred to infinite progress +with fab progress diff --git a/docs/website/video-transcripts/YwD35TG6WAg.json b/docs/website/video-transcripts/YwD35TG6WAg.json new file mode 100644 index 0000000000..9930510c72 --- /dev/null +++ b/docs/website/video-transcripts/YwD35TG6WAg.json @@ -0,0 +1,8 @@ +{ + "line_count": 92, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 453, + "youtube_id": "YwD35TG6WAg" +} diff --git a/docs/website/video-transcripts/YwD35TG6WAg.txt b/docs/website/video-transcripts/YwD35TG6WAg.txt new file mode 100644 index 0000000000..1708b7ff65 --- /dev/null +++ b/docs/website/video-transcripts/YwD35TG6WAg.txt @@ -0,0 +1,92 @@ +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/video-transcripts/YwkwqXkHGwg.json b/docs/website/video-transcripts/YwkwqXkHGwg.json new file mode 100644 index 0000000000..98cfc360bf --- /dev/null +++ b/docs/website/video-transcripts/YwkwqXkHGwg.json @@ -0,0 +1,8 @@ +{ + "line_count": 103, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 584, + "youtube_id": "YwkwqXkHGwg" +} diff --git a/docs/website/video-transcripts/YwkwqXkHGwg.txt b/docs/website/video-transcripts/YwkwqXkHGwg.txt new file mode 100644 index 0000000000..424f338e9e --- /dev/null +++ b/docs/website/video-transcripts/YwkwqXkHGwg.txt @@ -0,0 +1,103 @@ +there is only so far we can go with the +simulator +sometimes we run into issues we can't +trace in the simulator and can figure +out by trial and error +in those cases if the device +causing an issue is an android device we +can generate an android native project +using include source and run the +built-in android profiler tool +there isn't that much to discuss about +the android profiler it improved a great +deal in recent years and now provide +something close to the level of the +profiler we have on the desktop it's +still not remotely remotely as thorough +so we can use it only as a basic +guideline +when we run the profiler we see some +information at the bottom of the android +ide +when we expand it and dig in we can +study the cpu overhead +and memory overhead of a specific +operation so we can narrow down where +the cpu is spending its time +one of the cool features in android +devices is the developer options menu +this menu is hidden by default in +android and you need to activate it in +the settings by clicking the about phone +option then scrolling all the way down +and tapping the build number entry seven +times +once you do that you +will have a new developers option +menu option +in the above menu +there are many interesting features you +can enable and play with to help test +your android app but my personal +favorite is the debug gpu overduel +when you click this option you are +presented with three options we can +enable the show overdraw areas option +which will give us the results on the +right +as a side note +deuteronomy +the +utronomy which i hope i pronounced +correctly refers to the green color +blindness +and is unrelated to performance it's +related to accessibility of your color +choices +the colors look weird at first +but here's what they say +true color +no overdraw you are perfect and even +google doesn't hit that much +blue +overdrawn one time that's probably the +normal for most of your ui if your +background isn't blue or true color you +might have a problem +green +overdrawn two times +green is reasonable for nested elements +pink is overdrawn three times +this is okay for text and special cases +red is over drawn four or more times +this is something you should inspect and +check +if it's acting as it should +it's normal to see some pink in an app +but if you have red it might be a +problem +notice that the kitchen sink has red in +the clock which indeed animates the tick +effect so +overdraw in that section makes a lot of +sense +the ios version of the profiler has some +advantages over the android version +it's in fact based on the dtrace tool +that originated at sun microsystems +when you activate the profiling in io in +ios the app is recompiled and you are +presented with a set of options +there are many interesting options but +most of them are relevant for low level +development +most of us can use something relatively +simple such as time profiler +this is the output of the time profiler +after you play around with the app a bit +you can dig in and get a lot of +additional information from the ui +once you expand the hierarchy you can +look into the cpu time of every method +within the thread and isolate potential +bottlenecks on the device diff --git a/docs/website/video-transcripts/ZEe_hb1Lz6Y.json b/docs/website/video-transcripts/ZEe_hb1Lz6Y.json new file mode 100644 index 0000000000..b4308eb4da --- /dev/null +++ b/docs/website/video-transcripts/ZEe_hb1Lz6Y.json @@ -0,0 +1,8 @@ +{ + "line_count": 224, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1364, + "youtube_id": "ZEe_hb1Lz6Y" +} diff --git a/docs/website/video-transcripts/ZEe_hb1Lz6Y.txt b/docs/website/video-transcripts/ZEe_hb1Lz6Y.txt new file mode 100644 index 0000000000..ca08a01c06 --- /dev/null +++ b/docs/website/video-transcripts/ZEe_hb1Lz6Y.txt @@ -0,0 +1,224 @@ +in this section we continue with other +performance tips and pitfalls starting +with lists +when we initially created the list API +we were heavily inspired by swing's +architecture the ability to create an +infinitely sized list without +performance penalty was attractive and +seemed like a good direction for our +original two megabyte Ram targeted +devices back in 2006 seven +we knew the renderer model approach was +hard for developers to perceive but we +also assumed a lot of Swing developers +would find it instantly familiar +we made attempts to improve list in the +years since for instance multi-less +generic less cell renderer container +list Etc +these hoped but the core problem of list +remain +I won't go into all of the problems with +list and only focus on the fact that +performance benefits of list are dubious +list can perform well but writing +performance list code is a challenge +anything under 5000 entries would +perform better with alternative +Solutions if you need more than 5000 +rows reconsider +scrolling Beyond 1000 rows on a mobile +device is challenging +it made sense for a swing application +but it doesn't make sense for a mobile +phone or even a tablet +most developers will be better served by +an infinite container +if you do choose to go with list the +performance of the render and model +methods is crucial as they are invoked +multiple times per page over and over +again +in fact this is such a problem that for +anything under 200 elements a container +would perform much better than this +overdraw is the process of drawing the +same pixel more than once so if I paint +the background in white then draw text +on top of the white background I've +effectively overdrawn every pixel where +the text is drawn +this is perfectly normal for an +application for example I can fill the +background in white then draw text on +top of that +however if we over draw pixel four times +there might be a problem that might mean +the logic of the application is drawing +a background for the root component then +drawing again on top of that and again +that might still be okay but we need to +be aware of this and minimize it as +overdraw can be expensive +reducing overdraw can boost rendering +performance and animation smoothness +to reduce overdraw we need to look at +the hierarchy in the component inspector +and minimize opaque slash translucent +elements +we want no more than two backgrounds per +hierarchy and the ideal circumstances +I will discuss the process of debugging +overdraw later +networking is usually conducted on its +own thread and for the most part +shouldn't impact performance too much as +the network thread yields CPU seamlessly +when you use the network manager however +there are some nuances that should still +be followed by default code name one +allocates one thread for networking so +developers can rely on simple +consistency when making a request +otherwise you might write code like the +code above and request 2 would finish +before request 1. that's perfectly fine +for some applications but you need to be +aware of that +assuming you understand this we +recommend two Network threads for most +applications +you can add more but that might impact +overall performance without providing a +huge benefit to network speed +you can set the thread count in the init +object method in the main class using +update Network thread count method +the network threads are often the best +place to do long running Network related +tasks such as parsing +for instance the code on top might seem +simple but it carries a price it runs on +the EDT +the code above will work correctly since +the UI change will run on the ADT but +the parse data invocation might be slow +and might slow the entire UI a better +approach will be the usage of the code +below which runs the parsing on the +network thread +this has the added benefit where the +response stream isn't read to a byte +array first before parsing notice that +the rest API and codename 1 implicitly +does the parsing of Json on the network +thread to protect the EDT +it's easy to lose track of size slash +performance when you are working with +the within the Comforts of a visual tool +like the codename one designer +when optimizing resource files you need +to keep in mind one thing it's all about +image sizes +images will take up 95 to 99 percent of +the resource file size everything else +pales in comparison +large resource files can both harm your +build quota and hurt your performance by +taking up too much RAM in runtime they +increase the distribution size which +means users might uninstall your app or +even forget they installed it by the +time it was downloaded +like every optimization the first rule +is to reduce the size of the biggest +images which will provide your biggest +Improvement for this purpose we +introduced the ability to see image +sizes and kilobytes to launch that +feature use the menu item image sizes in +the designer make sure to backup your +resource file on a regular basis +especially before doing this stuff +unless you've found a specific image +that takes up most of the space it's +probable that space is taken up by many +multi images +multi images are essentially a single +image in multiple resolutions for +different device densities +these images can grow to a very large +size as we store a lot of resolutions +for every such image +if there are densities you don't need +such as low very low both of which don't +exist in small devices or 4K you can +just delete all of these image from +existing multi images through the menu +item image Advanced remove DPI +image delete unused images presents you +with a dialog containing the images that +are unused in the theme +it allows you to select the images you +wish to delete +all are selected by default +notice that this method doesn't scan the +code for image usage so an image which +you refer to from code will be marked as +unused and you would need to unmark it +manually +if you have a very large image that is +opaque you might want to consider +converting it to JPEG and replacing the +built-in pngs notice that jpegs work on +all supported devices and are typically +smaller +you can use the excellent Opti PNG tool +from HTTP +optipng.sourceforge.net to optimize +image files right from the codename one +designer +to use this feature you will need to +install Opti PNG then select images +launch optipng from the menu once you do +that the tool will automatically +optimize all your pngs +when faced with size issues make sure to +check the size of your res file if your +jar file is large open it with a tool +such as 7-Zip and sort Elements by size +start reviewing which element justifies +the size overhead +some apis might impact performance +negatively in some cases let's start +with round border and round rect border +since their low-level rendering code can +be expensive we use a mutable image to +Cache the data into the parent component +object this trades off performance for +Ram and might cause an upfront +performance penalty normally it +shouldn't matter if you have a couple of +round buttons however this could be very +costly if you have a list containing +many elements with such borders +gradients used to be very slow in +codename one as they relied on software +rendering for portability +newer versions are much faster for the +most part but I would strongly advise +benchmarking to make sure +using the gradient as a background +creates an image object background which +can result in a huge memory overhead +you would often be served with a better +served with solid colors or image +backgrounds +gaussian blur is an inherently slow +algorithm +there is no getting around it if you +need to use this API you need to use a +trick like call Sierra Leone idle or a +thread notice that apis such as drop +shadow use gaussian blur under the hood diff --git a/docs/website/video-transcripts/ZzSCPcnFkxs.json b/docs/website/video-transcripts/ZzSCPcnFkxs.json new file mode 100644 index 0000000000..50009a3c64 --- /dev/null +++ b/docs/website/video-transcripts/ZzSCPcnFkxs.json @@ -0,0 +1,8 @@ +{ + "line_count": 105, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 559, + "youtube_id": "ZzSCPcnFkxs" +} diff --git a/docs/website/video-transcripts/ZzSCPcnFkxs.txt b/docs/website/video-transcripts/ZzSCPcnFkxs.txt new file mode 100644 index 0000000000..dab303bf09 --- /dev/null +++ b/docs/website/video-transcripts/ZzSCPcnFkxs.txt @@ -0,0 +1,105 @@ +continuing with the style form for the +final two pieces +the color +and font picker +moving on to the color picker the ui for +this component is relatively simple +we accept a color value and provide a +callback whenever the color value +changed +these are the pieces that make up the +component +three sliders for picking a color +channel +the text field and a label whose +background color is used to preview the +selected color +let's take a quick peek +at color customizer before we proceed +i can spend a while discussing the color +customizer but there is really very +little to discuss +we create a round thumb image and place +it on an editable slider component +honestly this code is a bit of a mess +and should have been in the theme +but because of the special nature of +this form +i couldn't place this stuff +in the theme +so i had to do it here +that's a major reason for this method +so the code would be a bit more bearable +every time a channel color changes +we update the color with a new value and +shift the channel color +into place for integration with the +other current channels in the current +color +let's take a second and check out update +color +update color just sets the color into +place and calls on success +for us +it also updates the text field with the +right hex color +an important piece is that update color +uses +acquire lock and release lock +which we need to guard against +recursive access +when we invoke set text to update the +hex value +when the user drags +the slider +it triggers a text change event +but since lock isn't acquired by the +text event it won't do the change +the same is true +the other way around +if it wasn't we'd run into an infinite +loop where changing the text updates the +slider +and vice versa to infinity +speaking about the hex text field +we acquire the lock here too +and update the various channels as +needed +the method is relatively +simple +let's move on to the font editor which +is in some regards even more elaborate +than the color editor +this starts with code that tries to +calculate the font size +once a font is loaded its size is +determined in pixels not in millimeters +but we want +to size it in millimeters as that's more +portable +so we need some way to infer that +here we loop over the values and try to +infer the millimeter value for a given +pixel value +from the list +appropriately we will pick the right +string from the list +in the picker +moving on we have the buttons within the +button group +that allow us to pick the right style +for the resulting font +we have the text preview obviously +but that's relatively simple +when we pick a size we need to update +the size all around appropriately +and delivered to the callback method +and finally we have one huge action +listener that handles all of the buttons +to give give each one its right style so +a button for bold will apply the right +font and so forth +with italic etc +there are probably more elegant ways to +do this but honestly fonts are always a +huge pain regardless of what you choose diff --git a/docs/website/video-transcripts/_EXEN52wQvs.json b/docs/website/video-transcripts/_EXEN52wQvs.json new file mode 100644 index 0000000000..67c26d9b29 --- /dev/null +++ b/docs/website/video-transcripts/_EXEN52wQvs.json @@ -0,0 +1,9 @@ +{ + "line_count": 20, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-use-storage-file-system-sql.md", + "status": "transcript-fetched", + "word_count": 833, + "youtube_id": "_EXEN52wQvs" +} diff --git a/docs/website/video-transcripts/_EXEN52wQvs.txt b/docs/website/video-transcripts/_EXEN52wQvs.txt new file mode 100644 index 0000000000..534f03c373 --- /dev/null +++ b/docs/website/video-transcripts/_EXEN52wQvs.txt @@ -0,0 +1,31 @@ +_Transcript source: embedded._ + +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. + +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. + +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 + +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. + +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 query API is pretty simple we can iterate over rows in a query and pull out the column values. + +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 diff --git a/docs/website/video-transcripts/_JCsyyIH02E.json b/docs/website/video-transcripts/_JCsyyIH02E.json new file mode 100644 index 0000000000..b317ebf630 --- /dev/null +++ b/docs/website/video-transcripts/_JCsyyIH02E.json @@ -0,0 +1,8 @@ +{ + "line_count": 126, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 650, + "youtube_id": "_JCsyyIH02E" +} diff --git a/docs/website/video-transcripts/_JCsyyIH02E.txt b/docs/website/video-transcripts/_JCsyyIH02E.txt new file mode 100644 index 0000000000..7e90770a91 --- /dev/null +++ b/docs/website/video-transcripts/_JCsyyIH02E.txt @@ -0,0 +1,126 @@ +a big missing piece is the settings code +one of the important things we need to +expose +is the ability to customize information +about the user +ideally this should be as flexible as +possible so when we add additional +attributes to track +they can be incorporated right into the +ui +i simplified a lot of the facebook +ui +had and tried to focus on two big ticket +items +the image pickers and generic user +settings +i've encapsulated the image picking in +the image picker class +this is convenient as we might want to +make it more elaborate in the future +at the moment it simply picks from the +device gallery +we don't need a class in terms of +implementation it's there to encapsulate +the metadata of the image in a single +operation +the file name is useful for the upload +process as we maintain that metadata +data in the server for +reference open gallery +launches the os native code in this case +using image mode +we keep the file name and the image +object +this class encapsulates the upload +feature too +so the file name can remain encapsulated +that's pretty simple +we could +possibly do more by offering an option +to launch the camera etc +the settings ui is encapsulated in the +settings form class +cover and avatar represents the two +images in the settings ui +the background cover image and the +avatar image +the two +change fields represent the camera +change buttons that allow us to replace +the avatar images +next let's explore the constructor code +it's mostly standard and creates the +main settings ui for replacing the +images +here we bind the back button behavior +the current avatar is set into the +avatar label +if a cover image is set we fetch the +image asynchronously +both image buttons delegate to picker +methods so we can replace that logic in +the future +the cover label places the change button +on top +using a layered layout and wraps the +button in a flow layout so it will align +to the bottom right +we do the exact same thing with the +avatar image and its picker button +we then wrap both of those layered +layouts and another layered layout and +the avatar +in a flow layout to align to the bottom +center region +the layer layout usage raises the +obvious question +why not use one layer layout instead of +three +that would be hard to accomplish with +the picker button +we would have a hard time aligning the +picker button to the avatar +of the avatar to the bottom right corner +since the parent layout would be too big +next up +are the buttons we create at the bottom +of the ui +as mentioned at the end of the +constructor +the button bar +are just four buttons in a horizontal +grid +we only map the settings to +functionality +of full editing +in show user edit form +image picker does most of the heavy +lifting here we launch it and get an +image object back +and if +we get an image object back we proceed +we upload the image and get a callback +with the media id value +the avatar is set both locally and +remotely so we don't need a server +refresh call +we do refresh the icon though and make +sure to shape it as it was before +cover picking is very similar but has +simpler code as we don't need to shape +the resulting image and have a simpler +api +next we override this method +to have a custom toolbar +a toolbar is created using the layer +layered mode +the true argument +which makes the content run under it +we use the container ui id for the +toolbar to make it transparent +finally the last piece of code is the +show user edit form since there is still +a lot to cover here i'll continue in the +next +lesson diff --git a/docs/website/video-transcripts/_WKRuO0Izxg.json b/docs/website/video-transcripts/_WKRuO0Izxg.json new file mode 100644 index 0000000000..32093300fa --- /dev/null +++ b/docs/website/video-transcripts/_WKRuO0Izxg.json @@ -0,0 +1,8 @@ +{ + "line_count": 76, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 409, + "youtube_id": "_WKRuO0Izxg" +} diff --git a/docs/website/video-transcripts/_WKRuO0Izxg.txt b/docs/website/video-transcripts/_WKRuO0Izxg.txt new file mode 100644 index 0000000000..4c5e86ed4e --- /dev/null +++ b/docs/website/video-transcripts/_WKRuO0Izxg.txt @@ -0,0 +1,76 @@ +we proceed with search into the web +service and client layers +once we have the service layer the web +service layer is trivial +with one small caveat that i'll +highlight soon +first let's review the clause +the base url maps to slash search +which makes sense +people search +maps to the slash people url it takes +the query slash paging arguments and +auth header +for simplicity i didn't incorporate +oauth +but it should be incorporated +searching for posts is practically +identical to searching for people +this method implements +one-time initialization for search data +the search database +it's the one caveat i mentioned earlier +this is a bit of an ugly hack i open a +browser url with search slash rebuild +search db +question mark key equals +this huge string u s h i u y etc +this allows me to initialize the search +database when i set up a server on a new +machine +once the app goes into production i can +remove this method to prevent malicious +usage +i could have used other tricks such as +checking a special is initialized flag +whenever search is invoked +finally we have one last part the +client-side code +first we need to expose the server +search api via the server api class +since the code to search people and +posts is almost identical +i delegated it to a generic method +the method returns a varying type but +also accepts a type for the business +object so we can instantiate the right +class +we can't rely on the value of +the t generic +and runtime due to generic erasure +and java generic argument is syntax +sugar that is stripped out by the +compiler +when codename one compiles to the device +we remove everything related to that +since it's only relevant with reflection +anyway +and would balloon the code size +significantly +in both cases search is a get operation +that accepts page and query data +assuming there are results we loop over +them and add the business object to the +response list +keep the code generic we use the new +instance method +to create the business object +the populate from json code +is still the same +this can be thrown by a new instance +but we should be okay here since this +method is only used internally with +known class types +with that search is now mapped to the +client side the last piece of the puzzle +is tying it into the ui diff --git a/docs/website/video-transcripts/a6q1M_wqEYY.json b/docs/website/video-transcripts/a6q1M_wqEYY.json new file mode 100644 index 0000000000..da25ff4a3f --- /dev/null +++ b/docs/website/video-transcripts/a6q1M_wqEYY.json @@ -0,0 +1,8 @@ +{ + "line_count": 171, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 959, + "youtube_id": "a6q1M_wqEYY" +} diff --git a/docs/website/video-transcripts/a6q1M_wqEYY.txt b/docs/website/video-transcripts/a6q1M_wqEYY.txt new file mode 100644 index 0000000000..f3c7c5a1d1 --- /dev/null +++ b/docs/website/video-transcripts/a6q1M_wqEYY.txt @@ -0,0 +1,171 @@ +after going over the css it's time to go +into the code changes we made for this +design +first we need some common business logic +to represent a dish +i use the properties api to represent +the dish object +a dish contains a unique id +price name description and image in two +sizes thumbnail and four size +the about form is pretty trivial +we store the full html hierarchy under +the html directory which acts as a +special case and allows us to view html +with all the bills and whistles on the +device +we use enclosed layout to position the +cart icon on the top right +this encloses the button in a flow +layout to position it +in the right place +the top area is big enough to contain +the image +we see here +and its size is determined by the size +of the top bar +which is +the top of the main form +however +because we use and close right +the cart button is positioned correctly +the pickup component is just a +hard-coded set of values from 1 to 100 +with a special case for 0. +we use a picker class to represent that +but the layout of the main widget +is interesting here +we have a box layout x +for the elements which arrange them +in a row +it's important not to use flow layout +for the entire row as it might break a +line which isn't something we want at +this point +however if we use flow layout for a +single element +breaking a line isn't an option +so this won't be a problem +we wrap the quantity button +in a flow layout so it will be in the +middle +we do the same for the two labels since +both comprise of one item +the flakiness that is sometimes +associated with full layout layout +isn't applicable +and +we can just use it as +a trick to center align elements +we don't need to center align the image +because it is physically larger than the +other elements +the labels are arranged as a box layout +y +notice they are in the middle because of +the flow layout surrounding the box y +the quantity button allows us to edit +the number of elements +and also remove an element from the list +if you look at the removal effect +looping to the right here +you will notice that +this animation has several stages +first we set the dish to an x position +outside of the form +and call animate unlayout +to show the float floating +out +animate unlayout moves a layout from a +valid laid out state to an invalid state +and is designed exactly for this use +case next we physically remove the +element which is now outside of the form +and we animate the ui layout +this creates the scroll layout effect +the contact us form is relatively simple +it has a lot of data in it +but all of that is pretty standard +first we have the map container class +which is a part of the codename one +google maps cn1 lib +that we installed +on into the application +this can be installed via the extensions +menu +and allows us to place a native map on +the form the coordinates of the map are +all hard-coded at this point +the address is just a text area that +isn't editable +styled to appear in this specific way +the phone and navigate buttons are just +standard codename one buttons +with the shopping cart style +they map to the appropriate native +functionality in the display class +we overlay the text and buttons +container on top of the map +using the layered layout since the map +is a native component there are +implications to placing an overlay on +top +but +there +are a bit too much for this discussion +if you are interested +in learning more about that check out +the developer guide section on peer +component +the dish view +is very similar to the map view in +regard to the bottom description +and the overlay +notice +that i went with image viewer instead of +using an image +this allows pinch to zoom that will +even work with the overlay on top +and will allow us to look more closely +at the dishes +the price label is essentially the same +your order component that appears +in the top of the standard forms +however if we tried to draw it +cut off +the round border wouldn't let us +the layout manager prevents us from +placing a component off screen as well +there are other hacks we could do but i +chose to just paint the component onto +an image that is too small to hold the +full size of the component then place +that image on the screen using a glass +pane +a glass pane is drawn as an overlay on +top of the entire screen +and as you can +draw anything there +there are surprising parallels between +best practices in almost every +discipline i ever learned design is no +different +you can take a lot of the coordinates of +programming and apply them to design +once you understand the basic philosophy +if you look at the previous ui elements +it's pretty obvious which ones were +designed by a designer and which ones +weren't +but if we showed them to someone who +isn't sensitive to design nuance +it might take some guesses +so we can get by +but not much more than that +when in doubt i take the lazy approach +i just use images or other elements to +cover up the lack of design skills +thanks for watching i hope you found +this educational please join the +discussion in the comments section +and let me know what you think diff --git a/docs/website/video-transcripts/a7MF3oSCASE.json b/docs/website/video-transcripts/a7MF3oSCASE.json new file mode 100644 index 0000000000..05e69b2275 --- /dev/null +++ b/docs/website/video-transcripts/a7MF3oSCASE.json @@ -0,0 +1,8 @@ +{ + "line_count": 70, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 391, + "youtube_id": "a7MF3oSCASE" +} diff --git a/docs/website/video-transcripts/a7MF3oSCASE.txt b/docs/website/video-transcripts/a7MF3oSCASE.txt new file mode 100644 index 0000000000..127f2ba17f --- /dev/null +++ b/docs/website/video-transcripts/a7MF3oSCASE.txt @@ -0,0 +1,70 @@ +there are two about forms we need to +discuss +the first one is a part of the app and +describes the app builder +the second one is a form that designs +the about form that will appear in the +end result restaurant app +in both cases about is an important page +to have as we can stick a lot of +important information there without +impacting the flow or usability of the +final app +About Us Form +the about form of the application is +probably the simplest form we have +it just displays the placeholder html +i'm not a big fan of html +based uis but one thing html does +relatively well is about forms and it's +really easy to just move this out +notice i'm using set url hierarchy which +allows us to package the entire html +source tree with css javascript etc +under the html package this will display +implicitly in a portable way for the +devices +Placeholder +this is the placeholder html it doesn't +really matter as you can see it's just a +placeholder i'm showing it only for +completeness sake +Validation +the about form also has validation for +the url value +which won't allow us to accept the form +until we have a valid url +that disables the checkbox for ok until +we enter a valid url +i'll just enter the url for +codenameone's homepage so we can see +that this doesn't just take the url it +also shows the preview +originally i thought about embedding +html within the final app +but that is painful to implement in a +generic way +i'm assuming that every restaurant would +have +would already have a website in place +notice the flickering is an issue in the +simulator and not on the actual device +UI Binding +the implementation of this form is also +pretty trivial +we use ui binding to update the about +page url but it's such a simple form +that there isn't much need for anything +as we scroll down we see that the page +has okay and back commands +that +either release or commit the single +binding object +we can also see the validation binding +that disables the ok command +but the one interesting thing is +that we load the page url +only when the user interface is finally +valid +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/aNFKBDeky2s.json b/docs/website/video-transcripts/aNFKBDeky2s.json new file mode 100644 index 0000000000..eed2cc2d7e --- /dev/null +++ b/docs/website/video-transcripts/aNFKBDeky2s.json @@ -0,0 +1,8 @@ +{ + "line_count": 127, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 649, + "youtube_id": "aNFKBDeky2s" +} diff --git a/docs/website/video-transcripts/aNFKBDeky2s.txt b/docs/website/video-transcripts/aNFKBDeky2s.txt new file mode 100644 index 0000000000..f7562184c9 --- /dev/null +++ b/docs/website/video-transcripts/aNFKBDeky2s.txt @@ -0,0 +1,127 @@ +now that we reviewed the basic design +elements let's apply this to the code +we will start with the base form which +is a base class from which the top level +forms will derive in the future +Base Form +the init method of the base form +initializes the common pieces including +the search entry which is currently just +a search command +it doesn't do anything right now and is +there mostly for the icon +the psd didn't include a design for the +side menu +it included only the icon indicating the +side menu is there +i added a side menu entry to make the +icon appear +but it doesn't have any functionality or +design +Filler Label +i've mentioned the usage of a layered +toolbar before +a layered toolbar floats on top which +would mean the list of filters etc would +appear underneath it in normal +circumstances +to avoid this i added a filler label +that is as tall as the toolbar area +and placed it +in the top portion of the form +it pushes all the other elements +downwards so they don't overlap with the +title +the background image is a part of the +style +and will stay in place behind everything +Order Entry +we discussed the your order entry in the +css +but i would like to call out the fact +that i used the localization manager to +format the currency price here +that's why the price is listed as ils +instead of usd +in this particular case +this is probably wrong +as prices should be delivered +from the server with the currency type +we discussed the shopping cart style in +the css 2. +notice we apply the shopping cart style +before calling the material icon +which effectively used the styles from +the css for the icon +Label +this label +is placed into a grid layout +with a transparent container +so the top portion shows the background +image and this label abstracts it +creating the effect of a line crossing +that way it looks like the shopping cart +is peaking from the bottom of the ui +this almost works +Grid Layout +remember when i mentioned going +overboard i meant this part +the label and the grid layout trick +almost worked +and this is the workaround to get it to +work +a grid layout takes two components and +give them gives them the exact same size +to fill up available space +this works great except for cases where +the size isn't divisible by two +in which case one pixel at the bottom +remains empty +the workaround was simple +make sure the size of the order bar is +always even +and that way the two components get +exactly the same size +otherwise in some weird cases +we'd have a line of the image +peaking in the bottom part +Category List +the category list +is just a list +it's implemented +in the actual form +and not within the base form which is +common to all forms +so this method might return null as not +all forms will need that list +i mentioned the separator line +in the css section +but here i'd like to draw your attention +to set show +even if blank on the label by default an +empty label is completely removed in +codename one +this helps achieve several design +aesthetics +in this case we don't want that behavior +and we can disable that by using this +method +Top Bar +you might recall the top bar from the +css +it includes the title image +fill style +again notice that the portion that +supposedly doesn't have the title image +showing +still +has it +it's just obscured +the whole title area is just placed in +the north section of a border layout +the content differs from form to form +so +this is the content of the main form and +isn't implemented here +we are just invoking the method of the +subclass diff --git a/docs/website/video-transcripts/aVkeOIx-Is8.json b/docs/website/video-transcripts/aVkeOIx-Is8.json new file mode 100644 index 0000000000..f78df68c27 --- /dev/null +++ b/docs/website/video-transcripts/aVkeOIx-Is8.json @@ -0,0 +1,8 @@ +{ + "line_count": 178, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 912, + "youtube_id": "aVkeOIx-Is8" +} diff --git a/docs/website/video-transcripts/aVkeOIx-Is8.txt b/docs/website/video-transcripts/aVkeOIx-Is8.txt new file mode 100644 index 0000000000..ad49ac4402 --- /dev/null +++ b/docs/website/video-transcripts/aVkeOIx-Is8.txt @@ -0,0 +1,178 @@ +in this module we'll discuss animations +first a word of caution +don't overdo it +animations aren't about making things +flashy or good looking +when you read apples or google's +documentation on animations they are +always explained +the first animation type exists to draw +attention to something +a good example is when we enter a new +form and want the user to notice a +specific important feature +we can animate it +if we have more than one animation going +on the user will be distracted and will +miss out on our highlight +an example for this is an animation on +an important button so the user will +notice it +the second common use case is +confirmation +one example of this is a form transition +in ios forms move from left +from right to left +when we dig deeper into the app +when we go back they move in the +opposite direction +thus confirming that we are going back +we also have a cover transition that is +used to signify global functionality +that resides outside of the navigation +stack +but this sort of animation is important +in many such cases +elements animate outside +when we delete them so we will get a +sense of confirmation that deletion did +occur +we have a lot of animation types that +allow you to deliver these use cases +as well as many frivolous effects if you +so +desire we'll start with transitions +these are pretty much the first +animation types people notice when +running an app +here i'll demonstrate transitions on a +form +but we can apply a transition to a +dialog or an arbitrary component by +using the +container.replace method +using transitions is trivial we just +apply them to a form a dialogue or use +them and replace +the rest just works +we have several built-in transitions +common transitions include things such +as slide cover fade etc +flip transition is a card like 3d effect +that relies on device native perspective +transform +it will look pretty different on devices +and the simulator +morph +converts one component to another and is +aimed at the material design paradigm +where a ui element in one form +transforms to another when moving to a +new form +bubble transition is also inspired by +material design +and represents a dialogue growing into +place using a bubbling effect +for most the most part we use common +transition for almost all transitions +i mentioned slide fade cover etc +slide is the default transition codename +one except for ios where the transition +is slide fade +by default +this is a special case transition that +slides the body +while fading the and sliding the title +elements at a different pace +the theme has common constants to define +the default transition to the various +types of elements +so you can define the default transition +for form dialog etc +directly from the theme without changing +the code +when we apply a transition to a form +we have both an in +and out transition +this is often confusing +for dialogues the transition in is what +we show when the dialogue is shown and +the transition out is played when the +dialog is disposed +so we usually need both +for forms we usually only use transition +out by convention +there are some +edge cases where both would make sense +but they are pretty rare +and unless you build your own custom +transition you will probably never need +to transition in +on form +these are the uncover and cover +transitions running on the cart action +when i pick this transition i mostly try +to be consistent with other apps but in +retrospect using cover for this feature +makes perfect sense +as it's outside of the regular +navigation stack +i use the morph transition to move from +a thumbnail to the full screen image +the effect has its kinks +but it works +exiting from the morph isn't as +attractive +so i chose to use the uncover +in the reverse direction +going into the code we can see the show +checkout method +which shows how the transition is +applied +to the form +notice we apply both cover and uncover +respectively so the effect of going in +and going out of the form +remains +another important thing to notice +is that we save the old transition out +of the parent form +the reason for this is that we'd like to +restore the old transition when we go +out so cover won't be used for all +future navigation +this is restored here when we press the +x button +that's handled in the constructor of the +checkout +form +the dish effect is a bit more nuanced +morph transition works by converting one +component to another component +in this case we convert the thumb +component matching the dish id +to the background image +notice that these map to the name +properties of the respective components +the reason for this +is that a transition can be defined and +cloned +before a form exists +or while a form changes +by binding to a component name we +effectively +disconnect the transition from a +specific component instance +notice that every thumb also has the id +appended to it +this allows us to distinguish between +different entries within the form +we use the show listener to restore the +original transition out +since a new form instance of dish view +form +is created every time this shouldn't be +a problem +if this wasn't the case we would have +needed a remove show listener +too diff --git a/docs/website/video-transcripts/aW3OlGTmasw.json b/docs/website/video-transcripts/aW3OlGTmasw.json new file mode 100644 index 0000000000..25d7d7de3b --- /dev/null +++ b/docs/website/video-transcripts/aW3OlGTmasw.json @@ -0,0 +1,8 @@ +{ + "line_count": 116, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 617, + "youtube_id": "aW3OlGTmasw" +} diff --git a/docs/website/video-transcripts/aW3OlGTmasw.txt b/docs/website/video-transcripts/aW3OlGTmasw.txt new file mode 100644 index 0000000000..044c75e1a0 --- /dev/null +++ b/docs/website/video-transcripts/aW3OlGTmasw.txt @@ -0,0 +1,116 @@ +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/video-transcripts/aZ2Y74xPj_0.json b/docs/website/video-transcripts/aZ2Y74xPj_0.json new file mode 100644 index 0000000000..c7127edac6 --- /dev/null +++ b/docs/website/video-transcripts/aZ2Y74xPj_0.json @@ -0,0 +1,8 @@ +{ + "line_count": 87, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 397, + "youtube_id": "aZ2Y74xPj_0" +} diff --git a/docs/website/video-transcripts/aZ2Y74xPj_0.txt b/docs/website/video-transcripts/aZ2Y74xPj_0.txt new file mode 100644 index 0000000000..c6e8286661 --- /dev/null +++ b/docs/website/video-transcripts/aZ2Y74xPj_0.txt @@ -0,0 +1,87 @@ +in the previous module +we built a mock-up for restaurant +application from pre-existing psd files +but it had +a lot of missing features and some +design +issues are several missing pieces in the +app design most importantly missing form +designs +we need a dish view that we will see +when clicking the more info button for a +dish we need an about form +this isn't mentioned in the design but +it's crucial in every app +we can use it for disclaimers licenses +marketing and much +more contact us is important for any +restaurant +so this is also an essential ui element +the side menu should allow us to reach +those additional forms +and as we stand right now we don't have +a design for that element +before we continue it's important to +understand the motivation +this isn't a design class it's still a +programming class +in fact the designs i came up with are +subpar +understanding design is hugely +beneficial for us as developers +the specific example +is +excellent +where +we only got parts of the designs but +still need to deliver something +but a better example is that we develop +a sense of +what designers need and see +this helps us develop +taste and perspective +it allows us to distinguish between good +and bad design it also teaches us to +demand the right thing from our +designers +and communicate with them more +effectively +a blank +page is both liberating and frightening +for designer +and a developer alike +we don't have that here we have an +existing imperfect design that we can +build on +top one of the mistakes developers make +is +overly focusing on the external +appearance of a design instead of +understanding its intentions and +motivations +i think +laziness is +one of the great qualities of a good +developer it encourages us to build +great reusable software +the same principle applies to design +reuse +sentiment bears repeating +good design +is consistent and follows +common conventions +another tip that bears repeating +is avoiding the core factor +back in the lure days +we added a cube transition effect +between forms +this instantly became the only way +developers transitioned between forms +and that looked awful +apple's design rarely does things for +the sake of pretty +every animation conveys a meaning +when things swoosh in +an apple ui +it's always to convey direction deletion +or some other deeper purpose diff --git a/docs/website/video-transcripts/anfVMvBXXX0.json b/docs/website/video-transcripts/anfVMvBXXX0.json new file mode 100644 index 0000000000..b2a0732ade --- /dev/null +++ b/docs/website/video-transcripts/anfVMvBXXX0.json @@ -0,0 +1,8 @@ +{ + "line_count": 150, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 763, + "youtube_id": "anfVMvBXXX0" +} diff --git a/docs/website/video-transcripts/anfVMvBXXX0.txt b/docs/website/video-transcripts/anfVMvBXXX0.txt new file mode 100644 index 0000000000..47ae1b4b23 --- /dev/null +++ b/docs/website/video-transcripts/anfVMvBXXX0.txt @@ -0,0 +1,150 @@ +layout animations are some of the most +important yet easy to use +animations in codename one +they make the application flow feel +reactive and responsive +at the core of layout animations +is the concept of explicit reflow +in codename one layouts determine the +position of components +that means that if you add a new +component +it will be positioned by the layout +however there is a big caveat +if you add the component after the form +was shown +it won't position itself +unless you do something like rotate the +device which implicitly lays out the +component +you need to ask for layout explicitly +you can invoke revalidate to force +layout +this is valuable for cases where you can +add multiple components +in those cases the layout logic can +happen only once +and can be much +simpler +layout animation works by animating the +validate effect +it takes the components from their +previously invalid or incorrect +positions +and moves them into their real final +position +normally when you add a component +you won't set a size or position to it +as those will be determined by the +layout manager +but with layout animations this can be +useful +as you can position a component +explicitly +and then it will animate from that +position +this allows you to create a flow +in the ui so components settle into +place +this serves to draw attention to changes +in the ui in most cases +animate unlayout is +the exact opposite or +evil twin +of the animate layout method +animate layout takes layout in an +invalid state and returns it to normal +layout with an animation +animate unlayout takes layout in an +invalid state +returns it to normal instantly +then animates it into the invalid state +so if we want to move a component out of +the form +with an animation +we can set its x position +to the screen width +if we call animate layout we will get an +effect of the component sliding into the +screen +from right to left +if we invoke animate and layout we get +the effect of the component sliding out +of the screen +when we run animate layout +we do something that's effectively the +equivalent of revalidate so the ui is +valid once we finished +with animate unlayout the situation is +the exact opposite +the ui becomes invalid +when we are done +which means a typical animate and layout +will be followed by an animate layout or +revalidate +the purpose of animate and layout is +strictly as a visual effect unlike its +animate layout counterpart +there are a few varieties and a bit of +noise +but layout animations generally fall +into into these two categories i +mentioned +and weight versions of the methods use +invoke and block to help you serialize +calls to layout and unlayout +this is useful when you want to create +elaborate effect +with one piece relying on the completion +of the next piece +fade versions of the layout methods can +fade components in or out +for unlayout components are faded out +and for layout they are faded in +this allows components to materialize or +de-materialize gracefully +the hierarchy related animate methods +such as animate hierarchy +are very problematic +they recurse into a hierarchy and try to +lay out all of the elements +within +but this is tricky as you can run into a +lot of edge cases such as +a component that moves from outside the +clipping bounds +to of its parents container +in this video i activated slow motion +mode which is very useful for debugging +animations but for some reason the +unlayout effect wasn't slowed down +so you can only see the deletion briefly +and then the animate layout that follows +is slow +let's go over the code that creates +this animation +the entire animation is done in five +lines of code +let's go over them one by one +first we set the deleted dish container +to the display width which pushes it out +of the visible range +with set x core +we now perform animate and layout and +wait +we maintain full opacity which +corresponds to the second argument +this animates the removal of the dish +from the screen by sliding it to the +right +we next remove the component that we +animated out +this is important as we are currently in +an invalid state +but the component is still in the +hierarchy +now we can animate the other element +into their new place since the dish was +removed there is more available space +and we can use it by shifting everything +into the new position diff --git a/docs/website/video-transcripts/bH7cS5ENNlw.json b/docs/website/video-transcripts/bH7cS5ENNlw.json new file mode 100644 index 0000000000..1bc1d67518 --- /dev/null +++ b/docs/website/video-transcripts/bH7cS5ENNlw.json @@ -0,0 +1,8 @@ +{ + "line_count": 75, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 406, + "youtube_id": "bH7cS5ENNlw" +} diff --git a/docs/website/video-transcripts/bH7cS5ENNlw.txt b/docs/website/video-transcripts/bH7cS5ENNlw.txt new file mode 100644 index 0000000000..2146a839a2 --- /dev/null +++ b/docs/website/video-transcripts/bH7cS5ENNlw.txt @@ -0,0 +1,75 @@ +in this part we'll review the designs +for the additional forms and the process +are used to produce them +design is based on common principles +so the place to start is by looking at +what other people did +when faced with a similar ui design +requirement +it goes without saying that we are +looking for inspiration only +a basic principle exists in all of these +designs +the dish is the most important element +so +how +do we combine that design with the +design language of +the guy who designed this +they don't fit very well together +so the best thing to do +is to probably minimize the impact of +the design by letting the dish +take up the available space +of the new form +the main challenge is in displaying the +text +and the price +and keeping consistency with this design +language +so how do you combine these two +i came up with this +which isn't my best work +but we'll do for now +the pricing is listed on the right in a +label inspired by the price in the main +screen +and the close +add to cart is inspired by the checkout +form +html has its issues but it's a great +tool for building an about form +it doesn't look great on the simulator +because we need to rely on the browser +component in javasc +but in the device +it looks great +i use the template for the restaurant +as the html in this form +so we can get a decent markup showing +how this should appear in production +i looked at a few contact us forms and +noticed that the map was often very +dominant +this is really important for a +restaurant which has a physical location +i made the map take over the whole form +and that way the ui looks great thanks +to the implementation of the map +i used the existing cart design to +implement +the call and navigate buttons and some +of the ui +fade look from the dish form +i'm a bit frustrated that i couldn't get +the map to stretch all the way up as +it's restricted to the content area +i didn't spend too much time on the side +menu +i just took the colors of the theme +and the top image to create this design +since the side menu +are pretty standard ui elements +i +leaned on my experience with other apps +when designing this interface diff --git a/docs/website/video-transcripts/bU6pZPs3uto.json b/docs/website/video-transcripts/bU6pZPs3uto.json new file mode 100644 index 0000000000..44370c4079 --- /dev/null +++ b/docs/website/video-transcripts/bU6pZPs3uto.json @@ -0,0 +1,8 @@ +{ + "line_count": 110, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 683, + "youtube_id": "bU6pZPs3uto" +} diff --git a/docs/website/video-transcripts/bU6pZPs3uto.txt b/docs/website/video-transcripts/bU6pZPs3uto.txt new file mode 100644 index 0000000000..9e2bb7d3c4 --- /dev/null +++ b/docs/website/video-transcripts/bU6pZPs3uto.txt @@ -0,0 +1,110 @@ +in this part we dig deeper into the UI +abstraction clause +moving on we reach the fields of the +class and the start of the UI let's step +over those +first we have the form delegate instance +this is only relevant if we are in a +phone mode and not in the tablet mode +where we have a different class in place +the container is the current UI since +form is a subclass of container this +will work for both the form and the +tablet UI mode +elements such as the OK cancel buttons +are added to a container and then the +main container is wrapped up to include +these additional buttons +this allows us to keep the code mostly +seamless +but we sometimes need access to the +actual decorated container for example +when we need to replace it +don't worry if you don't understand this +it will be clearer as I go through the +actual code +this is essentially the equivalent of +get current form +only in this case we get the current UI +abstraction which will work correctly +let's move on to the creation code and +specifically the code that occurs when +working with a tablet notice that is +tablet will return true for desktops as +well so there isn't as much of a need +for is desktop unless you need it to be +truly distinctive from tablet +by default in codename one the content +pane of a form is scrollable on the Y +Asus so I tried to replace this Behavior +here so the code will feel seamless +the UI abstraction client property +allows us to get access to the UI +abstraction class from the component +this is useful for methods that need +access to navigation logic but run from +the UI code +for instance a button in the UI that +triggers form navigation +the decorated container defaults to this +container +unless we have a back slash OK button +pair in which case we create a new +wrapper of the container and use the +decorated container for that +we just created okay and cancel buttons +which we add into the south of the +container using a grid layout notice the +usage of grid layout ensures both +buttons will have exactly the same size +which contributes to nice Aesthetics +if you recall the previous code for ok +at cancel we had in the form delegate +this will seem almost identical +but then you might notice the current UI +is really the UI abstraction class +and we use showback from there and not +from the form +this is the else statement that runs in +case of regular forms or in phone mode +it just defers everything to the form +delegate +moving on we can see the common methods +for show are implemented normally as one +would expect by calling show or showback +respectively +but there is something interesting here +tablet UI which we use to implement the +show logic +tablet UI is the equivalent for form +Delegate for tablets but unlike that +class it's a single form that never +changes +I'll go to that class soon +other delegates work seamlessly +with the container notice that add on a +form will work well and so we'll add on +a container +so in this case we can just seamlessly +delegate +bind Fab Works exactly as we would +expect for a form but with the tablet we +need to re-wrap the decorated container +and use that as the new decorated +container to understand why you need to +understand that Fab wraps the content of +a container and adds a layered layout +where it resides +the parent is a form already then we +have a layered Pane and it just uses +that +but if it's not then we need to use the +container that the Fab returns which is +where the delegated container comes in +really handy +finally all of these methods are just +callbacks that we invoke in the code +the submit button validation is handled +here so it will work with the internal +OK button without exposing a button +component that might be fragile diff --git a/docs/website/video-transcripts/cRqvkIpJlkg.json b/docs/website/video-transcripts/cRqvkIpJlkg.json new file mode 100644 index 0000000000..0aaf54adbd --- /dev/null +++ b/docs/website/video-transcripts/cRqvkIpJlkg.json @@ -0,0 +1,8 @@ +{ + "line_count": 206, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1158, + "youtube_id": "cRqvkIpJlkg" +} diff --git a/docs/website/video-transcripts/cRqvkIpJlkg.txt b/docs/website/video-transcripts/cRqvkIpJlkg.txt new file mode 100644 index 0000000000..fc4c81f1e6 --- /dev/null +++ b/docs/website/video-transcripts/cRqvkIpJlkg.txt @@ -0,0 +1,206 @@ +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/video-transcripts/cZiHmpw2FVI.json b/docs/website/video-transcripts/cZiHmpw2FVI.json new file mode 100644 index 0000000000..a7a3ce4be1 --- /dev/null +++ b/docs/website/video-transcripts/cZiHmpw2FVI.json @@ -0,0 +1,8 @@ +{ + "line_count": 62, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 385, + "youtube_id": "cZiHmpw2FVI" +} diff --git a/docs/website/video-transcripts/cZiHmpw2FVI.txt b/docs/website/video-transcripts/cZiHmpw2FVI.txt new file mode 100644 index 0000000000..3dc86e54e7 --- /dev/null +++ b/docs/website/video-transcripts/cZiHmpw2FVI.txt @@ -0,0 +1,62 @@ +camera support can be a book in its own +right +so i'm keeping this to the bare minimum +we'll use the camera kit cn1 lib to +implement camera support in the +application +since that isn't supported everywhere +for instance the simulator +will implement a fallback to use the +capture api in those cases +before we proceed we need to install the +camera kit cn1 lib from the codename one +extension manager +following that we need to refresh libs +so we can use that api +the main form defines the camera button +on the top left portion of the form +we need to bind our ui to that call we +need to add a new camera method +this leads us to the new camera method +if camera kit is null which will happen +in the simulator +we need to fall back to use the capture +api +captcha is trivial to use it just +returns a file path for the image or +null if the user canceled the operation +here we use one of the new create +methods we added to image picker then +launch the new post form as usual +the new camera form class provides a +custom camera ui +this leads us to the camera form class +which implements a camera viewport we +use a layered layout so we can place +widgets on top of the camera view +scrolling should be disabled as we can't +scroll a camera layered layout doesn't +implicitly disable scrolling like border +layout +events from the camera notify us of +things such as image capture which is +the only thing we handle here at this +time +this adds the camera viewport to the ui +so we can see what we are capturing +this button allows us to capture a photo +of what we are seeing +this is +really basic i didn't handle video flash +zoom or any other important feature we +can handle all of those things but that +would mean building a camera app which +is a task in its own right +so i've left it at that for now +however once you do this you can see how +this leads directly into +the post ui and everything comes +together for media posting +media uploading has a lot of nuances and +complexities are then touched due to +time and breadth diff --git a/docs/website/video-transcripts/csTtSj6TqRE.json b/docs/website/video-transcripts/csTtSj6TqRE.json new file mode 100644 index 0000000000..0d3f3b150b --- /dev/null +++ b/docs/website/video-transcripts/csTtSj6TqRE.json @@ -0,0 +1,8 @@ +{ + "line_count": 290, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1710, + "youtube_id": "csTtSj6TqRE" +} diff --git a/docs/website/video-transcripts/csTtSj6TqRE.txt b/docs/website/video-transcripts/csTtSj6TqRE.txt new file mode 100644 index 0000000000..1858fa069f --- /dev/null +++ b/docs/website/video-transcripts/csTtSj6TqRE.txt @@ -0,0 +1,290 @@ +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/video-transcripts/cxllJwt10VU.json b/docs/website/video-transcripts/cxllJwt10VU.json new file mode 100644 index 0000000000..385e73d453 --- /dev/null +++ b/docs/website/video-transcripts/cxllJwt10VU.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 141, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-01-java-for-mobile-devices/008-theme-basics.md", + "status": "transcript-fetched", + "word_count": 992, + "youtube_id": "cxllJwt10VU" +} diff --git a/docs/website/video-transcripts/cxllJwt10VU.txt b/docs/website/video-transcripts/cxllJwt10VU.txt new file mode 100644 index 0000000000..a1e0d77db5 --- /dev/null +++ b/docs/website/video-transcripts/cxllJwt10VU.txt @@ -0,0 +1,141 @@ +In this short video we’ll discuss theming +in Codename One +We’ll start with a bare bones native style +application to keep things simple. +We’ll start in the designer tool +In the designer tool we can use the native +theme option to see how our theme will look +in iOS and Android. +We can thus test the native feel of our theme +We can add a new entry to the theme by pressing +the add button. +We can pick a theme entry from the combo box +or type a custom entry. +The derive flag indicates if we want to inherit +the appearance from the native theme. +We can define many background image and gradient +types but now we’ll focus on simpler things +I will create a green button with red foreground +because my goal is to teach not create something +“pretty”. +I can set the color by typing the color hex +values into the respective entries or by using +the color picker widget. +I will also need to set the background to +opaque which is 255, 0 is transparent +I can pick the alignment of the text, center +is pretty common for buttons so I’ll use +that but I can align to the left & right too +Padding and margin can be defined in pixels, +percentage units of the screen size or millimeters. +We always recommend using millimeters as it +is consistently portable. +This allows me to customize the size and spacing +of the button, don’t be stingy with padding +as elements need to be big enough for touch +interaction +There is a lot of confusion over padding and +margin so let’s clarify this. +Margin represents the space outside of the +component so you use that to push components +away from one another or bring them closer +together. +Padding makes the component larger or smaller, +it’s the additional space within the component +beyond what is normally needed to draw the +component. +I’ll discuss border & derive soon. +For now we’ll move directly to font +Font’s can be pretty complex. +I recommend only using one of these native +fonts which will look decent on Android & iOS. +You should usually size the native font in +millimeters to avoid platform inconsistency +This looks great on iOS, lets see how it looks +on Android… +Uh oh, that was totally surprising and unexpected. +This happens because on Android the button +has a border. +Lets edit the button entry +Border always takes precedence over other +background types so we should derive it and +use an empty border if we don’t want a border +That seems to have fixed it but if we click +the button we see there is still a border +for the selected style +We solve this by adding a new selected style +for the button that derives from the unselected +button and also overrides the border style +with an empty option. +The selected style is effectively the focused +state of the button +I use command-c or control-c on windows to +copy and paste the selected style into the +pressed style. +When I click the button the problem is gone +but I don’t see any effect either +I can edit the selected style of the button +and pick a dark green color I then do the +same for the pressed style of the button. +Once all that is done pressing the button +shows the correct highlighting and all is +good in the world once more +The code to show this is trivial, just a button +added to a form +And this is how it looks on the simulator +Lets say we want to add a red button to the +app, we’ll just add a new theme entry and +call it RedButton. +We will then derive from Button so we will +get a similar look +We will set the background to red and foreground +to white while leaving everything else in +the default state +We’ll copy and paste the red button style +into the selected state, we’ll leave everything +as is and only change the background color +to be a shade darker than the current red +color by tuning it in the picker +Now we’ll just paste this into pressed and +go back to the IDE +We add the red button entry by creating a +new button and adding it into the form. +We use set UIID to set the theme entry ID +to red button. +We can also pass this to the button constructor +as the second argument but I wanted the code +to be clearer +As you can see the red button looks and acts +like the green button +The cool thing is that you can apply a UIID +to damn near anything so I can apply the red +button style to a text field thru set UIID +and get almost the same effect +As you can see this is pretty close to the +button appearance applied to a component that +has completely different behavior +The theme can be manipulated directly from +code in roughly the same way. +We’ll set the button color to blue by setting +the background color of the red buttons unselected +style. +Notice the pressed/selected behavior for the +new blue button is missing +We can fix that by changing the selected & pressed +styles as well directly from code +I set them to the same color so we don’t +see much but you hopefully get my point… +There is a better way though, by using get +all styles we can set the property on all +the styles in one swoop. +One last subject is theme constants, with +constants we can manipulate many aspects of +the theme. +You can see them in the theme UI and look +at the help text below each constant to understand +what they are about. +I suggest reading in the developer guide about +the various options in the theme constants +and theming in general. +Thanks for watching, I hope you enjoyed this +tutorial and found it helpful diff --git a/docs/website/video-transcripts/d1T25sbFUKE.json b/docs/website/video-transcripts/d1T25sbFUKE.json new file mode 100644 index 0000000000..1a0e52f83f --- /dev/null +++ b/docs/website/video-transcripts/d1T25sbFUKE.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 102, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/018-dependencies-gradle-and-cocoapods.md", + "status": "transcript-fetched", + "word_count": 531, + "youtube_id": "d1T25sbFUKE" +} diff --git a/docs/website/video-transcripts/d1T25sbFUKE.txt b/docs/website/video-transcripts/d1T25sbFUKE.txt new file mode 100644 index 0000000000..3197525cca --- /dev/null +++ b/docs/website/video-transcripts/d1T25sbFUKE.txt @@ -0,0 +1,102 @@ +in this section we'll focus on the +native dependencies before reviewing the +implementation +most native code requires that we link +against specific libraries and this is +often the most challenging part of +implementing the actual native interface +if we review the braintree setup guide +for android we'll see that the +instructions include a gradle dependency +change +this is a common tool for integrating +third-party libraries to android native +apps +and it usually follows syntax like the +one we see above +normally this is pretty easy to +integrate +the build hint android gradle depth +should solve the problem in no time +but if we try to build the app we'll get +this build error which is not just +unreadable it's also snipped because it +overflows the screen +so let's zoom in a bit but this doesn't +tell us more one of the mistakes people +make when looking at the gradle errors +is +the focus on the gradle exceptions which +are confusing and verbus +the error is usually printed above +that +and here it is in bold +the minus decay version is the actual +error we are experiencing the problem is +that the suggestion entry is wrong +we can just fix our men sdk version and +codename one +instead +we have a build hint for setting the +android men sdk version to 16 +which will align it with the min sdk +version required by braintree +the problem is that even after that +change +i still got a compilation error which +was just about the most unclear error +message possible +i googled this +which is pretty much the best way to +solve issues like this +and i found a stack overflow entry that +explained +that this is caused by the wrong sdk +version setting in +android +at this time codename one defaults to +sdk 23. +notice we update that value occasionally +so this might be incorrect when you see +this +however +in this particular case the fix was +simple +set the sdk version to 25. +once this was set build went through as +expected and we were able to get a +working project for android +on the ios side things were also a bit +tricky +ios has a tool called cocoapods +which allows us to define dependencies +since we already use maps in the contact +us form we already have a dependency on +that +we can just add a brain tree drop-in +dependency as explained in the +integration guide in the braintree site +and this should work +however +it seems that drop-in requires ios 9 or +newer +as highlighted in their site +so we can define the build hint +pods platform +to force the minimal +platform +but this creates a conflict with the +google maps cn1 lib +which defined the pods platform to 7.0 +this is something that should be +resolved by now +but if you happen to run into something +like this and need to fix it +at this exact moment +then what i did is simple +i edited the required properties in the +cn1 lib file +and just removed the offending line +notice that the cn1 lib is just a zip +file so i was able to just edit the +properties file diff --git a/docs/website/video-transcripts/exL7bS0StP4.json b/docs/website/video-transcripts/exL7bS0StP4.json new file mode 100644 index 0000000000..8e3272e551 --- /dev/null +++ b/docs/website/video-transcripts/exL7bS0StP4.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 174, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-03-build-real-world-full-stack-mobile-apps-java/001-server.md", + "status": "transcript-fetched", + "word_count": 963, + "youtube_id": "exL7bS0StP4" +} diff --git a/docs/website/video-transcripts/exL7bS0StP4.txt b/docs/website/video-transcripts/exL7bS0StP4.txt new file mode 100644 index 0000000000..b1678ff408 --- /dev/null +++ b/docs/website/video-transcripts/exL7bS0StP4.txt @@ -0,0 +1,174 @@ +i skipped the server creation tutorial +before +as i felt it left the domain of codename +one +too much +and ventured into server programming +in +this level we don't skip such things +so i'm going to review the server code +related to the restaurant app and why it +was built like that +i find server code to be boring that's +not a bad thing boring can be great when +you need something you can rely on which +is exactly what a good server should be +i'll try to focus on the interesting +pieces only and i will mention some of +the entities or the relationships as +they are just more of the same +there are some great java ee and spring +boot courses out there and i don't think +it's our place to displace them +because we did the client first +and the server portion is mostly filling +in the blanks there are still some +hidden complex nuggets +but it's mostly simple +security is one of the more challenging +things about server programming i'll try +to explain as much as i can +but once you go into security there is +always more to know +it's a specialty in its own right +our starting point will be the same +spring boot project i did in the base +course +i'll use mysql again and create a blank +database we'll use app backend server as +the project name +the same database and project will be +used for the app maker +as well as the app itself +there are some nuances in spring such as +service components which are similar to +session beans and java +i'm not using any of those things which +would make sense in an architectural +standpoint but aren't really useful for +our narrow use case +let's start right away +assuming the project was created adding +braintree support to the server side +starts with +editing +the +pom file +with the write dependency +this will allow us to compile the code +that supports braintree +the braintree service is pretty simple +it maps to the braintree url +the first thing we do is provide access +to the braintree +gateway +notice that +the normal braintree api hard codes all +of these values but we load them +dynamically and then place them in a +cache for reuse +the reason for this is that the +braintree credentials are specific to +restaurant and we will have multiple +restaurants running on a single server +the service methods themselves are +pretty trivial +the token is generated using the +standard braintree api there isn't much +here it just returns the string which we +use in the client-side code +the interesting method is the purchase +method where we create a purchase +request and finally +fill in the amount we want to pay +this isn't obvious from the code here +but we have a small security issue in +this code +right here +calculate amount runs on the order which +we received from the client +this means that the prices of the +elements in the order were also received +from the client +our client is theoretically trustworthy +but if someone reverse engineers the +communication protocol for the client +they could just change the prices of the +dishes to give themselves a discount +however if we just +take the dish id and calculate the price +on the server +this will make no difference +this doesn't even have to be malicious +if a price was updated and a local copy +of the app still has the old price +because the user hasn't launched the app +in a year +it's important to make these +calculations in the server +always +in the final version of the server this +is fixed but i kept it for now in the +interest of simplicity and also to +demonstrate how nuanced security issues +can be +this is the basic restaurant entity it +allows us to save the details we need of +the restaurant +we'll add a lot more +into this class but for now +all we need are these things +notice that instead of using a numeric +auto-generated id +i chose to use a string +which i've set to a universal unique +identifier in the constructor +numeric ids are sometimes problematic as +one can potentially guess the id and +scan your database +for example if someone notices that we +have an api called +list dishes that accepts the restaurant +id +this might be a problem he could scrape +the api for all the dishes of all the +restaurants by scanning integer values +scanning your universal unique +identifier values will take centuries or +even eons +when looking at the code you will see +some duplication +in this example we can see the order +entity next to the order data access +object or dao +you will notice that these values are +very similar to one another a dao +transfers data from the client or within +the server +and an entity stores the data +there are often mirror images of one +another but not always +in this case we can see that the dao +contains things that aren't part +of the order +also the architecture is different since +the entity needs to map to storage +structure and the dao is designed for +our convenience +this is a common practice that saves the +need to pass entities around and break +the separation of the tiers within the +application +it allows us to have some flexibility +with our data +the versioning service allows us to +detect if a new version of the app is +available and effectively allows us to +disable outdated versions +this is important as we might be +stuck with an out of date protocol that +we wish to retire +we might need to change the server +address +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/exQ8xXAxtoU.json b/docs/website/video-transcripts/exQ8xXAxtoU.json new file mode 100644 index 0000000000..f479ba3dd0 --- /dev/null +++ b/docs/website/video-transcripts/exQ8xXAxtoU.json @@ -0,0 +1,8 @@ +{ + "line_count": 103, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 511, + "youtube_id": "exQ8xXAxtoU" +} diff --git a/docs/website/video-transcripts/exQ8xXAxtoU.txt b/docs/website/video-transcripts/exQ8xXAxtoU.txt new file mode 100644 index 0000000000..145a5dd0a6 --- /dev/null +++ b/docs/website/video-transcripts/exQ8xXAxtoU.txt @@ -0,0 +1,103 @@ +in this part we will discuss the process +of cutting a psd file +into the images that we need using +photoshop +we'll start with the title image i click +on it to find it within the list of +layers +after i identify the main layer +i see there is another blend +layer next to it +i select them and merge them +in the right-click menu +i convert this new layer +to a smart object +and then i open that +in a new tab +now +we can use file export +to save this layer as a jpeg +i'm using jpeg as the quality of the +background +isn't crucial and doesn't require +translucency +so it can benefit +from the smaller file size +next up +are the items in the list of dishes +we'd like the white rounded square +background +and to do this we'll start hiding +everything that we don't need +now that everything is hidden +we can trim the empty pixels +and leave only the part that matters +we can now remove all the elements from +within the square +so we'll have a completely blank square +this time we'll export png as we need +the transparency +we will then undo everything +so we can keep cutting +the other ui elements +the dish images +have rounded corners around them +and we'd like to apply that uniformly +the trick for that is masking +where we will extract the shape of the +round corner as a black and white image +i isolate the responsible layer +then paint the layer in white +and everything else in black +using the bucket fill tool +once that is done +i try to crop that image +to the smallest size reasonable +i will use it later to shape the dish +image +in the final app +now we can save this image +even though it doesn't have any +transparency in it +we will still use png as we don't want +lossy encoding for this image +now we need the buttons from the items +first we select the button +to find the right layer group +which we then convert to a smart object +we can now edit the smart object +directly +and hide the text on the button +again we export the file to a png +as we need the image to have +transparency +and then we go back +to get the second button in the exact +same way +we go through the exact same process of +selecting the layer and converting it to +a smart object +then removing the text +we export as png +the last piece +is the border of the checkout +we can just hide everything else that +exists in the file so +only the border background and separator +line +remain +once we have that +we can trim the pixels so we'll have an +image +of the right +size +we can first cut out the top portion +above the line +i'll skip the save part as it's pretty +standard +and last we can cut out the bottom +portion +leaving the top line +as part of the bottom +border +you diff --git a/docs/website/video-transcripts/fJD45Mz8SZM.json b/docs/website/video-transcripts/fJD45Mz8SZM.json new file mode 100644 index 0000000000..8825cf4447 --- /dev/null +++ b/docs/website/video-transcripts/fJD45Mz8SZM.json @@ -0,0 +1,8 @@ +{ + "line_count": 98, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 566, + "youtube_id": "fJD45Mz8SZM" +} diff --git a/docs/website/video-transcripts/fJD45Mz8SZM.txt b/docs/website/video-transcripts/fJD45Mz8SZM.txt new file mode 100644 index 0000000000..fe79844735 --- /dev/null +++ b/docs/website/video-transcripts/fJD45Mz8SZM.txt @@ -0,0 +1,98 @@ +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/video-transcripts/fmNpMFLwABA.json b/docs/website/video-transcripts/fmNpMFLwABA.json new file mode 100644 index 0000000000..8e460e6b32 --- /dev/null +++ b/docs/website/video-transcripts/fmNpMFLwABA.json @@ -0,0 +1,8 @@ +{ + "line_count": 54, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 596, + "youtube_id": "fmNpMFLwABA" +} diff --git a/docs/website/video-transcripts/fmNpMFLwABA.txt b/docs/website/video-transcripts/fmNpMFLwABA.txt new file mode 100644 index 0000000000..3f87b7eba0 --- /dev/null +++ b/docs/website/video-transcripts/fmNpMFLwABA.txt @@ -0,0 +1,54 @@ +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. +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. +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. +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 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. diff --git a/docs/website/video-transcripts/gJoJQST5jyM.json b/docs/website/video-transcripts/gJoJQST5jyM.json new file mode 100644 index 0000000000..8f25fce87e --- /dev/null +++ b/docs/website/video-transcripts/gJoJQST5jyM.json @@ -0,0 +1,8 @@ +{ + "line_count": 426, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 2607, + "youtube_id": "gJoJQST5jyM" +} diff --git a/docs/website/video-transcripts/gJoJQST5jyM.txt b/docs/website/video-transcripts/gJoJQST5jyM.txt new file mode 100644 index 0000000000..774d36128e --- /dev/null +++ b/docs/website/video-transcripts/gJoJQST5jyM.txt @@ -0,0 +1,426 @@ +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/video-transcripts/gM-InTtdhVE.json b/docs/website/video-transcripts/gM-InTtdhVE.json new file mode 100644 index 0000000000..9eed596f52 --- /dev/null +++ b/docs/website/video-transcripts/gM-InTtdhVE.json @@ -0,0 +1,8 @@ +{ + "line_count": 177, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 995, + "youtube_id": "gM-InTtdhVE" +} diff --git a/docs/website/video-transcripts/gM-InTtdhVE.txt b/docs/website/video-transcripts/gM-InTtdhVE.txt new file mode 100644 index 0000000000..a837ca4baf --- /dev/null +++ b/docs/website/video-transcripts/gM-InTtdhVE.txt @@ -0,0 +1,177 @@ +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/video-transcripts/gmqFd2bU_fM.json b/docs/website/video-transcripts/gmqFd2bU_fM.json new file mode 100644 index 0000000000..05e29b1a45 --- /dev/null +++ b/docs/website/video-transcripts/gmqFd2bU_fM.json @@ -0,0 +1,8 @@ +{ + "line_count": 109, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 623, + "youtube_id": "gmqFd2bU_fM" +} diff --git a/docs/website/video-transcripts/gmqFd2bU_fM.txt b/docs/website/video-transcripts/gmqFd2bU_fM.txt new file mode 100644 index 0000000000..38ae5fae0a --- /dev/null +++ b/docs/website/video-transcripts/gmqFd2bU_fM.txt @@ -0,0 +1,109 @@ +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/video-transcripts/h-I62aFYBNA.json b/docs/website/video-transcripts/h-I62aFYBNA.json new file mode 100644 index 0000000000..4b44441b1e --- /dev/null +++ b/docs/website/video-transcripts/h-I62aFYBNA.json @@ -0,0 +1,8 @@ +{ + "line_count": 78, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 465, + "youtube_id": "h-I62aFYBNA" +} diff --git a/docs/website/video-transcripts/h-I62aFYBNA.txt b/docs/website/video-transcripts/h-I62aFYBNA.txt new file mode 100644 index 0000000000..44bdccaf09 --- /dev/null +++ b/docs/website/video-transcripts/h-I62aFYBNA.txt @@ -0,0 +1,78 @@ +with this last part we'll finally finish +the server code +post web service is even simpler than +user web service as it delegates to the +post service class which is smaller +the per service prefixes all calls with +post as opposed to the user web service +which used slash user +this class carries a one-to-one +relationship to the post-service class +we make no use of other services +the methods here can throw a permission +exception which as usual what will +translate to an error dao +the methods of the class translate the +business logic to uh web service as +before they are trivial for the most +part +list and feed just return pageable data +for the given user notice that every +method here accepts the auth header +when making a new post or comment the +dao object is passed along with the +header this allows for clean client-side +code that can be unaware of the +authorization for the most part +that's it the class itself is a trivial +wrapper around the service +class this brings us to the last class +we will cover in the server before going +back to the client the media web service +the media web service is as simple as +the post web service but has a few +gotchas due to the complexity of mime +types and file upload +the media is mapped to the slash media +base url +again the class can throw a permission +exception which will translate to an +error dial +now that we got the main boilerplate out +of the way let's look at the methods +this lets us fetch a public image or +media file it assumes the media is +public and so we need no authorization +the idea of the media is passed in the +url path +itself +we get the mime type from the media +entry in the database since media can be +literally anything +an all request will also return friends +only media entry +and is thus marked as all +other than the alt entry it's identical +to the public request +upload is a multi-part upload request +that accepts a file with additional +metadata as a multi-part request +multipart is a part of the http +specification that includes specific +structure +for submitting a file in a web-based +form +codename one has built-in support for +uploading files using multipart request +so this is pretty convenient solution +for file upload with that we are done +with the server +this has been a lot of work for the +server but most of it was simple +boilerplate +the server isn't hard technically in +fact it's simple and that's a bit boring +but we had to get through that in order +to get the more interesting pieces the +following lessons would be far more +interesting as a result diff --git a/docs/website/video-transcripts/hCjmHoktlrU.json b/docs/website/video-transcripts/hCjmHoktlrU.json new file mode 100644 index 0000000000..a69150318d --- /dev/null +++ b/docs/website/video-transcripts/hCjmHoktlrU.json @@ -0,0 +1,9 @@ +{ + "line_count": 22, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-use-desktop-javascript-ports.md", + "status": "transcript-fetched", + "word_count": 960, + "youtube_id": "hCjmHoktlrU" +} diff --git a/docs/website/video-transcripts/hCjmHoktlrU.txt b/docs/website/video-transcripts/hCjmHoktlrU.txt new file mode 100644 index 0000000000..06003d9554 --- /dev/null +++ b/docs/website/video-transcripts/hCjmHoktlrU.txt @@ -0,0 +1,43 @@ +_Transcript source: embedded._ + +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. + +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. + +On Windows an EXE or an MSI file can be generated as a result, this is determined by build hints. + +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. + +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. + +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. + +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 diff --git a/docs/website/video-transcripts/hwsWdhJHl8Q.json b/docs/website/video-transcripts/hwsWdhJHl8Q.json new file mode 100644 index 0000000000..3063b2a4b3 --- /dev/null +++ b/docs/website/video-transcripts/hwsWdhJHl8Q.json @@ -0,0 +1,8 @@ +{ + "line_count": 377, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 5258, + "youtube_id": "hwsWdhJHl8Q" +} diff --git a/docs/website/video-transcripts/hwsWdhJHl8Q.txt b/docs/website/video-transcripts/hwsWdhJHl8Q.txt new file mode 100644 index 0000000000..9d089ff1a9 --- /dev/null +++ b/docs/website/video-transcripts/hwsWdhJHl8Q.txt @@ -0,0 +1,377 @@ +hello everyone in this session i want to talk about core concepts of mobile development +some of them are related to codename one and i'll go into that but most of them +are very generic and the reason for that is that sometimes +i see developers coming into codename one and into the mobile world and +suddenly hit with a realization that oh you can't do that in +mobile or you can't do that in a cross-platform way or this is only applicable to android and lots of other +things of that nature because they're not familiar enough with the restrictions and limitations and the +mentality that exists within this particular niche and here i'd like to talk about +some of these things i don't know if everything will sync in but to sort of give a sense of why +mobile is so different from desktop programming or server programming uh if those were the experiences you had +before and also if you just started out in mobile it gives you a better sense of +where the limitations are so the first thing i +want to talk about is density because density and device resolution +Density vs. Resolution +are well most of us know about them to some degree or another but even people who +know what density is i sometimes run into +the unintuitive nature of of this particular problem +so first density is the dots per inch or the points per inch +or pixels per inch it's essentially how close the pixels are to one another so if you take an +inch for inch instance uh from an iphone 7 and you look at it you you can count uh +literally 401 pixels if if you'll have a big magnifying glass or something +and you'll literally be able to count lots of pixels there because it has a relatively high uh +pixel ratio although nowhere near some of the android devices which go +above 600 and even more and +all the devices and even relatively new devices sometimes are as low as 100 +ppi and that initially doesn't seem like a big deal most of us are +obviously usually focused on resolution but here i give a relatively simple +example of two relatively new devices iphone 7 plus and the ipad 4. both of them are +relatively new the ipad 4 is almost twice the size of the iphone 7 +but they have almost the same number of pixels within them +and there is a difference there's a few hundred pixels difference obviously but +it's not as much as you'd expect there to be and the reason is the iphone is far more +dense than the ipad and the thing is that when you take a +device that's a tablet and you take a a device that's a phone usually people +expect the tablet to have far more pixels and that's not always true +now i didn't find a good graphic to represent density but i did find a great +graphic from open signal that you can see here that represents the number of resolutions that we have for common +android devices this is only android devices iphone devices are much simpler but +still the variety is astounding and +you really need to be resolution independent when you're working in mobile +and unlike web designs where you can say okay i'll design to a specific resolution and +people will just have lots of white space and a phone people won't accept that they want the ui to fit +so a lot of the cheats that we had on the web we can't do them here we need to +actually use the available space so +lots of the differences as i said uh can be very extreme +the if you take +a relatively low resolution image and try to show it on a very high +density device it will look very pixelated and that's something you just can't fix so you need high resolution +images the problem is that usually low dpi devices have less memory because +usually when we want to build a device we don't want to stick in too much memory because that brings up the price +that brings up the battery usage that causes a lot of cascading sort of effect +so uh these devices usually have less memory so they will really be hit if we have +too many high resolution images and also scaling down a high resolution image +doesn't look as good it doesn't look as bad as scaling up but you still get artifacts that are not +as bad but but still artifacts and that's not an option either +so there's two solutions essentially for +the density problem the first solution is the one that we use most often +because it's essentially the easiest all operating systems have a solution of this type all mobile operating systems +that have a solution of this type and that's to include images for +the various dpis and the way that it works is you give an +image for a high resolution dpi for high density dpi and +lower and lower and lower and each one is adapted in this and smaller and that way in runtime you request an +image and you get the right resolution image in codename one we have that we call it +multi images and we do it sort of seamlessly you just give us a high resolution image and we scale it +now on the device scaling we'll have artifacts but when we do it on the desktop we can run a very +uh refined algorithm that scales things with a very high quality +and you can't really do that in runtime on a device it will be too expensive +but on the desktop we can and uh and that when you get a multi-image you +can literally get very accurate +size accurately sized image and that's probably the simplest approach and the +one we usually take because we get resources that are images and it's really easy to work with that +there is another approach and that's vector images vector graphics +and those contain two big uh genres within +Vector Graphics +the first which we support relatively well is icon fonts and that allows you to essentially +if you think about it a lot of the icons are just a drawing +that's very similar to text but just with a specific shape +so we can there's this trend that's been going on for several +years of creating a font file that contains within it uh various +shapes logos of social network companies the share icon and all sorts of pretty +much any icon you can imagine and there's lots of great uh +repositories that contain many icon types like that and we have built in +the material design icon set from google which is free for any sort of use and that's why we +bundled it in so we can use it within uh our code to represent things uh +and have them appear beautifully with icons and everything by default +and that way it's actually pretty small one of the nice things about icon fonts is +that they are the file the entire material design icon font is 100k +and it contains quite a lot of icons and that's pretty cool we can offer +a relatively large amount of icons for literally no cost at all and just use the drawstring uh logic to draw it +and uh and it looks beautiful and it's anti-aliased and it's just perfect you can color it +based on the system colors which is one of the best uh aspects of icon fonts because +if we style a button and we say we want the foreground to be in this color when it's unselected and +that color when it's pressed and the icon font will automatically change the color uh +based on that state and that's really really really convenient so i love this approach and +we use it a lot the second approach is vector files now we don't support it at this time because +it's not as popular or wasn't as popular over the years the starting started +getting some traction in recent years as google added some support for +for svg files into their ide now no mobile device supports svg natively +they support it within the web browser but not for actual drawings so if you +you can open a png file you can open a jpeg file when drawing in the native code but an svg file won't work and the +reason for that is that it's kind of hard to render an svg file properly +because it contains a lot of information it's kind of bound into the +dom structure of a web browser and when you you start looking into all of the +complexities of the standard you your head explodes and you say okay let's just leave it for the web browser +so one of the things that google did which was something very smart and we wanted to do it too +is an ability to import an svg file and convert it into java source essentially +that you can compile as part of your application and then it draws everything and that's something +that we actually we have an issue on that and maybe by the time you view this in the future iteration it might already +be implemented but it's something that we don't have at +this particular time there is a disadvantage sometimes and the performance of +vector files because when we have an image file +it's just pixels and the device draws it really really quickly it just copies +pixels from one place to another and everything is already rendered and a vector file or +a future vector file of this type would be harder to draw at the same +performance level the benefit is that it's obviously smaller and it's very sharp for every +resolution so it's something that we we are still interested because the +price might be good for some use cases +uh the next thing i want to talk about is certificates in provisioning one of the harder concepts in coding one and in +mobile development in general and uh uh +it will be discussed more in the development process of +the boot camp but i want to go into details about +about this in big breaststrokes +because there's a lot to discuss here even without going into every single detail +or into the process itself of making them so to understand all mobile os's require +some form of signing by default there are sort of exceptions +but not really in in production +basically to to actually sell something to a store or pass it onwards or debug +it or anything you need to sign code to run on the device and uh +apple and google and microsoft have very different approaches to what +signing is why and why is it there so +google and microsoft look at signing as a way to protect your +app after it's been installed on a device so for instance +say you wrote an app and you upload it to google play +and you have a google play account you paid for it it's yours and tomorrow someone hacks your google play account +and he's in some foreign country you don't know who he is but he now controls your +google play account so great you can report him to google but what if during that time he uploaded +fake versions of your apps and essentially distributed them to all your users to the automatic update +and now all your users have their data wiped well +google isn't so much concerned about us as developers as much as it is concerned about all of our users +and they don't want that so what they they did here is that they did +a double solution here so if someone hacks your account that's he broke stage +one but to upload an app you'll need your certificate and your certificate essentially +is meaningless it doesn't say for real who you are it just says that +yeah i'm the same guy that uploaded the application initially so i can create my +own certificate and i'm just saying hey i can call myself bob it doesn't matter +but the thing is that once i upload an app to the store with a bob certificate +from this point on only the bob certificate can provide an update for that app +otherwise that app will not update so if you lose your google uh +certificate it's gone the app can't be updated anymore +and this is sometimes it can makes things complicated because +you know sometimes if you build a debug version and install it on your phone then a release version won't work +because they have different certificates so that's with the android version +so it's kind of a pain but it's a very +convenient approach because you don't need to go to google and ask them for a certificate +apple took a very different approach so with apple +they're giving you a certificate to indicate that you paid them to uh build +uh an app and the logic here isn't just a greed +it's also a verification process that way the the moment you pay them +money they can verify who you are to some degree and +in that sense they want to weed out the potential that you're a malicious player +at least not of a low level if you're a government operative they don't really have the ability to block that +but at least it will stop a script kitty who wants to distribute his malware through +the app store so it's powerful in that sense at least in +stopping the basic abuse but the thing is that if +with ios if you lose the certificate it's not a problem you know if +you can just wipe it the you can just essentially revoke it and +create a new certificate and you can submit every time now how does that work because an apple if you try it during +debug and you revoke a certificate and you create a new one and then you try to install on top of that it won't work +either well the thing is that when you submit a certificate to +itunes you have two certificates one of them you use during debug you use +the debug certificate to install applications on your phone during development but when you submit an app to apple you +use a distribution certificate now the moment you use a distribution certificate you can't install it on your +phone you can only submit that app to apple and +that app is reviewed by apple engineers and once they reviewed it they sign it +with their certificate and that's the one the device checks so when you install your own app from the +store it will be signed by apple as well as you and the thing is that when you want to +say replace it with your own copy during debug you won't be able to because the +certificates will collide so they have a similar approach to security as google has where +they check their certificates during install but the thing is that they take the responsibility +of what's the allowed certificate and i'm sorry i might have made it more +complicated than it is actually no it's already too complicated +to begin with it's not my fault i'm blaming apple completely +but generally you have two certificates you can use the debug version to debug +you can use only the release version only to submit to the app store uh +or for test flight which is similar to app store submission but i won't go there in this slide +in this session at the moment because that's too much uh +now the next uh aspect of this +Provisioning (iOS) +is the provisioning so we have two certificates we have a debug certificate +and a distribution certificate so but the thing is that those two aren't +enough we need two more files that represent provisioning +and they're essentially kind of the document that comes with the +app that says how that app may be used so +when during development apple has a special restriction where they say you can't install on more than +100 phones during development and the reason for that restriction is don't circumvent the app store +if you can install an unlimited amount of devices then you could theoretically just put +the app on your website and that's it and people will just go out and install it +so by limiting the install number to a hundred devices and not just that these +hundred devices you can't just add and remove devices willy-nilly every device you removed is sort of +ignored it still counts to your 100 and only once a year when you renew your certificate uh do they allow you to wipe +the slate and start in a clean situation +so the provisioning profile during debug includes that list of devices where +you're allowed to install and without your device being listed there you can't install onto your own +device so that's really important and the other +things that it includes all sorts of you know are you using a specific api like icloud like push like things like that +all of that is itemized within that file so you still need it even for distribu for distribution +and it includes all sorts of information that apple needs when they install when they test +when they deploy that application so and +the provisioning is also directly tied to the certificate so if you revoke the certificate +you need to re download a new version of the provisioning with a new certificate +uh the other way doesn't apply so if you make change to the provisioning you can just download the new version and the +same certificate will still work and that's useful +one thing that i didn't mention in the slides not sure if i want to go into it too +much but you can use the same certificate for lots of projects with +ios and also with google if you want to that that although that's a completely different debate +whether you should uh but in the case of ios +you can use one you should usually use one certificate for debug and one certificate for distribution for all +your projects and just generate provisioning for each +one because the provisioning would usually be different and that's more convenient obviously the +moment you you revoke or need to update the certificate because once a year you need to update the certificate from +apple uh then you need to go over all your projects and update that of +all the active projects with the new certificate and your provisioning and it's it's tedious +and the last portion i want to talk about is the push certificate and +Push Certificates (iOS) +well technically i don't really want to talk about it because i didn't mention push here which is a big complex subject +i want to talk about it separately but generally push allows you to send +data to the device the thing the reason i'm talking about that is that if we're talking about +certificates there's also these and ios also has certificates for push +now this is a huge i mean if we thought push was confusing on ios it just got worse +and i've seen so many people fail on on this particular +nuance uh so we have the debug push we have the distribution push but we also have two +uh uh push certificates uh now say debug push +debug certificate and distribution certificate we also have two push certificates one +of them for the sandbox and one of them for production and the reason we have those is +that when your server needs to send a push to a device and the way this works +is the server says i want to send a message to device x that's out there so +it just connects to usually with us to our server which is the codename one server but our server +goes and connects to uh the itunes server and tells it notify device x that such +and such happened so that server needs to authenticate me and for that it needs +a certificate that indicates who we are and that is also a certificate that we need +we need two of them because there's two push environments one of them is during the use during the bug which is the +sandbox and another one is used during a +run time with the production and +these certificates are completely different from build and during build we don't even know they exist +so don't confuse those two groups of certificates they're +completely different although the push certificates are mentioned in the provisioning so +yeah it's it's as complicated as that but once we actually go through that it's not as bad +once you do that a few times although it's error prone +so i just noticed that i mentioned sandbox there and i'm mentioning it here and here i'm mentioning it in a +completely different context and here i'm mentioning it as the application sandbox +Application Sandbox +so application sandbox is the way your app is sort of restricted +so when we install an application on a regular pc or at least a 1990s pc +that application could do whatever the hell it wanted because it was running on our system and it could +change files anyway and everywhere so in pcs and macs we kind of restricted +things a bit oh you can't go to this directory if you're a regular user application if you're not an admin if +you're not root you can't access this you can't access that well apps take it to a whole different level +apps are really really really restricted they're installed through randomly generated +directory names in some cases and +they can't access folders that aren't theirs essentially +so on ios there's literally no real shared space or ability to +normally communicate between apps or things like that it's it's very restricted so things like uh +that we'd expect to to exist when we're a desktop developer often just don't mean +and we need permissions to do all sorts of things +like access photos and cameras and things like that which i'll discuss soon +so one common thing that people constantly ask about is you know file +pickers or just go to their image folder and these sort of things just don't physically exist on devices +one of the things that does exist is for instance we have the open image gallery api +and you can essentially ask the system to pick a file an image file for you +and then it opens the native image picker uh gallery you pick an image and that image is sent back to your app +and initially it sounds really an awkward way to do it +but then you start thinking about it what if uh an app just goes into your image gallery and starts uploading all +of your images to to someone's private cloud and uh you start realizing that uh oh that's a +huge privacy violation so that's just impossible our apps are +blocked from even accessing the the image area +unless they have a very explicit permission to do so +and inter-app communication is very very limited one app can't access a folder of +a different app because then essentially if you have a banking app installed +it would be very heavily attacked by pretty much everyone trying to crack it +to get into that sweet sweet cache so obviously +there's a very uh strict sandbox between applications and +things like communicating between one app and another app is usually done through urls for intents +to other means it's always awkward we support some of that so for instance +you can just use a url to open a different application that works in ios and in android and often +is reasonably portable in in some cases +and you can do a lot of things uh with those sort of apis +Permission Systems +and as i mentioned uh the permission system allows you to request uh abilities and essentially get +them so for instance uh uh when we open a camera or +contacts the operating system asks the user and that's the operating system that needs to ask it not +not something that we're asking whether the user wants to allow the app to do it +and that is a very transparent way to do it historically android didn't have that it +worked in a uh by declarative permissions so essentially uh we'd write into the +manifest uh the set of permissions an app expects and uh during install you were prompted +with the list of permissions the app needed which was usually very unintuitive because you're looking at +this huge list of all sorts of things that an app uses and you have no idea +how that actually relates so now apps and android install instantly and ask you for permission and +runtime which is far more convenient you still need to list the permissions +in the manifest to support all the devices and one of the nice things in codename +one is that we automatically scan your code and automatically insert permissions into place +where it's needed so this is almost seamless to you and know that +it's a huge pain in standard android programming uh if +something you didn't ask for the right permission or something like that it's painful +so this works like that today anyway uh +background uh i think this is the last uh section +uh background is another thing where people expect things to work like they do on +Background Processes +on desktop and in this particular case also expect them to work the way it works in android +so this is one of those things that that changed a lot and that's +both os is sort of gravitated to more common middle ground and this is +something by the way that i've noticed over the years that ios and android started from very +different locations and both of them moved a lot toward one +another and converged on almost anything and you know ios fans say oh android +stole our ideas and vice versa both stole a lot of ideas from one +another extensively and uh +it's a good thing they're both they've both come a long way and improved as a result +so one of the examples here is that ios didn't have multitasking at all when it launched +steve jobs literally said it will drain the battery we don't want to do that and technically he was right +android had background processors and multitasking and everything and initially it didn't seem that bad +because the initial android phone had decent battery life but then once you started installing apps and +installing apps you quickly found out that oh well it doesn't +and the reason it doesn't is that often you don't really notice that an application is +still running in the background and if you kill it you actually create a worse situation in some cases because the +service isn't necessarily dead and the relaunch later might occur +and it's kind of problematic +so ios started by gravitating towards android by adding background processes +and unlike uh android that sort of allowed anything to run as a service in the background so +you could just write any bit of java code and it will run in the background and all is +good in ios they required you to define a set of uh one of +one use case out of a set of allowed use case use cases so for instance if you want to +track location gear location download the file play music or do all sorts of +background tasks like that you literally need to declare that about your application +and a guy at apple during review time would actually look at that and say okay +the particular app in question warrants that so for instance if you +have a game that's completely unrelated to background +location and you say oh wait i want to track background location and +just don't tell don't tell anyone why +that might raise a flag during review because they want an actual explanation +of why you are tracking background location and +that's useful otherwise all apps would just +use that and waste battery obviously but also potentially violate our privacy +so there's good in that to some degree android 6 +made a lot of improvements to battery life and one of those improvements is that it curtailed some of the usage of +these background services so if you're using android 6 you will +notice that some things sometimes when you get back to your phone it kind of takes a while to +get going again and the reason for that is exactly this it sort of stops background process in +the track and it has a system that's closer to the way that ios works with +uh specific use cases and things like that so it can +not waste as much battery when it's off +so we support a lot of these use cases and we work in a way that's +more similar to the way ios works which makes more sense for them from the portability standpoint +and uh well but but still background processes are +harder to get uh to work properly across platforms +because obviously the vm isn't fully running and all sorts of other things like that +it's complex so if you're thinking about app functionality that relates to things +running in the background uh do your research uh because it's a very +uh tricky uh part of mobile development +so there's a lot i didn't cover i sort of uh went over the basics here +What did we learn? +i don't know if you knew the things i covered or didn't +but i hope i did give you a bit of sense of +some of the less intuitive things that we have here +it's i think app development when you come to it from the server side from desktop +initially it seems very familiar especially with codename one which is a very familiar program framework for +desktop developers and sometimes that's misleading and i hope +you are less misled by that and you get a better sense that +uh how different this is from desktop development +and uh if you didn't get by my awkward explanation of ios certificates and +signing just how painful ios signing is then trust me you'll get it when you +actually try to go through it and seriously people +guard your certificate keys for android because if they're gone they're gone +and i've heard the stories there is no work around google itself can't fix it +commit these certificates to your versioning system +and make sure you have them backed up before you submit your first application to google store otherwise +i don't want to talk about otherwise that he shouldn't be gone ever +so keep it safe that's the final word on that +and thank you i hope you've enjoyed that uh this presentation diff --git a/docs/website/video-transcripts/hys6Rpkru50.json b/docs/website/video-transcripts/hys6Rpkru50.json new file mode 100644 index 0000000000..8e976a7954 --- /dev/null +++ b/docs/website/video-transcripts/hys6Rpkru50.json @@ -0,0 +1,8 @@ +{ + "line_count": 95, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 517, + "youtube_id": "hys6Rpkru50" +} diff --git a/docs/website/video-transcripts/hys6Rpkru50.txt b/docs/website/video-transcripts/hys6Rpkru50.txt new file mode 100644 index 0000000000..f5ad681a1a --- /dev/null +++ b/docs/website/video-transcripts/hys6Rpkru50.txt @@ -0,0 +1,95 @@ +i'm continuing where i left off with the +show user edit form method +a quick reminder this is the final +output of the show user edit form +i want this form to be low maintenance +as user properties would be added +frequently +and this might become a bottleneck +the solution for that is instant ui +which automatically creates user +interfaces from property objects +there are obviously constraints we need +to adhere to +so the ui would be usable +we'll discuss those +these properties don't fit into the ui +notice birthday uses a long value +instead of date +and so it's hard to map to the ui +when i implemented this i thought about +converting the birthday from long to a +date object but eventually decided +against that +dates are very flaky as java implicitly +includes time zone +and we naturally think of them as +calendar dates +birthday is a perfect example of this +serialization via json with date objects +is painful so i wanted to avoid that +the solution is a bit convoluted +but it works +i added a metadata entry and hid it from +the serialization code +i'll discuss that later when we go over +the changes to the user object +the gender string is a multiple choice +option +so we have the labels and values for the +choices so the right ui is shown +this creates a container that lets us +edit the content of the me +object +i add some padding and make the +container scrollable +since it contains text components and +they must have a scrollable parent +instant ui implicitly binds the ui to +the user object +so we unbind when leaving the form +when we save the changes asynchronously +this creates a dynamic editable form +one of the cheap chief benefits here is +that the generated ui can be improved +seamlessly with no change to your code +this won't create the image from before +though +since the property names don't have +spaces +and +right +casing this won't look like that we also +need to add a birthday property to match +that ui +this is a meta property that looks like +a date field but delegates everything to +the actual birthday property +we also add the birthday date property +to the index +the +display labels for every property are +set here +if we didn't do this we'd see the +property names instead +the birthday date would be ignored from +json map code so it won't mess +with the other logic +there is a bit of css we need to +implement for the entire settings form +this is the white border surrounding the +avatar +we need some padding to make the border +visible +the margin on top pushes it down so it +will peek below the cover image +the camera circle is black over gray +with a small enough size so it won't +intrude +we want the title area to have margin at +the bottom so the avatar image will peak +out +this is a big enough label for the name +of the user below the +the avatar image +with that settings are done diff --git a/docs/website/video-transcripts/iCY64ThCleI.json b/docs/website/video-transcripts/iCY64ThCleI.json new file mode 100644 index 0000000000..bd9c6f3b8c --- /dev/null +++ b/docs/website/video-transcripts/iCY64ThCleI.json @@ -0,0 +1,8 @@ +{ + "line_count": 403, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 2311, + "youtube_id": "iCY64ThCleI" +} diff --git a/docs/website/video-transcripts/iCY64ThCleI.txt b/docs/website/video-transcripts/iCY64ThCleI.txt new file mode 100644 index 0000000000..31b92fd9fa --- /dev/null +++ b/docs/website/video-transcripts/iCY64ThCleI.txt @@ -0,0 +1,403 @@ +brace yourselves a bit +server programming isn't as exciting as +client-side code +it includes lots of boilerplate code and +some complex concepts +please bear with me +facebook back and server logic is +remarkably complex +what i built took roughly a day of work +maybe a bit longer with debugging +i cut a lot of corners to get this +working especially in the complex data +driven aspects some features such as +search will be discussed later +this seems like a lot to swallow but +this architecture is remarkably simple +once you start looking at it +we have four big pieces +web services +these are thin layers that provide a +standard rest for api +they encapsulated +in the rest controller classes that +expose the json web api +services provide a generic +implementation of the underlying logic +so it isn't mixed with web service +specific adaptations +service beans abstract the business +logic +and jpa from the web service +this means +in the future we can port everything to +use something like web sockets +and we use 100 of the code +within the service class +dao +data transfer objects +are used to move data from the storage +layer +all the way to the client +spring boot can translate a dow object +to json +in a response and create a dao from json +jpa entities +we use jpa to access the database and +query slash right into it +this includes both the entities and the +crude repositories +this architecture lets us mix and match +pieces as we evolve +i chose to use web services for this app +but tomorrow we could migrate the code +to websockets and reuse most of the code +as all the pieces from the services +layer onwards wouldn't need to change +the same is true for the database +replacing mysql +should be trivial as jpa can work with +any sql database +however you could go even further and +replace the entire way data is stored +for instance with a nosql database or +with direct sql access +since the data is hidden from the user +this can be accomplished while leaving +major portions of the code intact +to understand how this works let's +review a small sample of the new user +creation logic +request arrives to the server as a web +service +it then passes between the layers +initially the request comes in as a user +dao object containing the attributes of +the user +the response is a new user dao object +that includes additional attributes +specifically the user id +and security token +Spring Boot +i'm assuming you have mysql installed +and ready to use +so the next step is the new spring boot +project +we will need the following features from +spring boot jpa jersey security web +services and mysql +for completeness this is the palm xml +file dependency section +notice i'm using spring boot 2. +we need jpa +jersey allows us to automatically +marshal objects to slash from json +security allows us to hash our passwords +we obviously need web services support +we also need the mysql jdbc plugin +we might as well add two additional +dependencies this is a simple rest api +which we'll use to communicate with +mailgun to send emails +twilio allows us to send sms messages +for device activation +now that all of that is in place let's +proceed to the code +my preferred place to start when working +on server code is the database +i might start with the web service if i +have an api in mind but i usually pick +the database as it's easier to quantify +but before we go there we need to add +some boilerplate +assuming you've created your app with +the initializer you would have this +class +if you don't have it then you need this +class as it's the entry point for your +application +next to it we can place the security +configuration class +which handles basic security boilerplate +as such +here we disable some security +protections in spring boot +these make sense for browser clients but +not so much for native mobile clients +where we might need to jump through +hoops to get things working +this is the password encoder that we +will use later to hash the user +passwords +as i mentioned before +the best place to start a server +implementation +is the data structure i will skip the +database schema and instead discuss the +object entities that we need and how +they can serve us +the nice thing about jpa is that it can +automatically create the schema for us +which is really useful for fast +prototyping +the most obvious object to start with is +user +the user class is an entity that handles +the data of a person using the app +facebook obviously holds far more data +over each user but this should be pretty +simple to extend +before i go into the code there is one +concept i'd like to discuss first +unique ids also known as primary keys +a very common concept in databases is +the usage of an auto increment unique id +this means a database would +automatically generate a numeric primary +key for us matching every user +this is a really cool but also +problematic notion +if our ids are numeric and sequential +that means we can't really use them +outside of the server +if we expose them to the outside world +someone can just scan all our users +numerically +a far better approach is a string based +long random id +which we can generate with the +uuid api in java +the chief value of using this as a +primary key is performance +since queries based on primary key are +practically free +it makes a lot of sense to expose the +primary key to the client side +another advantage is database +flexibility +it would be possible to migrate to a +different database type in the future if +we use this approach an auto-generating +strategy can cause a problem as we try +to move live data from one database to +another +User Object +now that we got that out of the way +let's go to the code you will notice +that the user object in the server is +very similar to the user object in the +client +there are some differences but +essentially they are they correlate to +one another +we'll start with the field declarations +this is a string id as i mentioned +before notice it lacks the auto-generate +annotation you might see in jpa entities +most of the other fields are simple +persistent fields with the exception of +email and phone where we demand +uniqueness from the sql database +to verify an email or phone we send out +a code and store it here +if we had a memory db such as redis or +memcached we'd use that +but it's not a big deal to use the +database for this at first +a user might change his email or phone +after the fact so we need to maintain a +reference to the value +we verified +since dates and java are technically +timestamps we need to explicitly state +the sql data we need here +we store media files such as pictures in +a separate media entity +we'll discuss that entity soon +we have three relations to other users +for friends +friend requests and people you may know +the field stores a hashed version of the +password which is encrypted +it's never exposed to the user passwords +in the database are hashed and salted +this is handled automatically by spring +as we'll see soon enough +hashing is a form of encryption that +only goes one way for instance if my +password is x y zed +i can hash it and generate a value that +looks completely random +i can't decrypt it ever again +however if i know the password is xyz +i can verify it against the hash +salting means random data is inserted +into the hash to make it even harder to +break the hash +the token is +a special field that allows us to edit a +user we expose it only to the logged in +user and he can use that token to edit +the +data we have a unique id for every user +but we don't use it for write operations +our id is public knowledge so if a user +needs to refer to my user object he'd +use my unique id +this is efficient and accurate since ids +never change they are primary keys +when a user logs in we provide the token +so only the user can update his own data +this means the password isn't stored on +the device and a token can be updated +slash revoked +it's also long enough and random enough +which isn't always the case for +passwords +naturally tokens can't be primary keys +since tokens might need resetting in +case of a vulnerability and primary keys +are forever +if i was super concerned about security +to +a paranoid level i'd encrypt the tokens +in the database in the same way we +encrypt passwords that would mean we +would need to give different token a +different token to every device since +hashing is a one-way street +naturally that's a pain to handle so i +avoided it here +we initialize the primary key in the +constructor +this will be overridden when loading +from database but makes sure we have a +unique id when saving a new user +these methods check the list of friends +to see if a person with the given id or +token is in our friend list we'll use it +later +codename one on the client side doesn't +support java 8 streams at this time +they help writing some complex ideas a +bit more concisely but we find them hard +to compile to efficient code for ios +on the server this isn't a problem +here we have another case of a stream +with a for each method +this is pretty easy to explain with the +block above +the stream code is roughly identical to +the standard java for loop +this method handles conversion of lists +of users to list of user dao +this is a common practice as we get a +lot of those +we'll use the full name a lot in the +code so it makes sense to have this as a +helper method +the birthday can be null so we need to +check before converting to a long value +here we create a dao object matching +this user i'll discuss the dao in more +detail soon +but as you recall from before +we use it to transfer data to the client +notice that no private information is +passed when the dial is created not even +the friend list no password token etc +we pass the +auth token here but not the password +this method is invoked when a user logs +in and returns +to the user his own data +looking over the rest of the code you +will notice +that the rest is just a lot of +boilerplate setters and getters +there is nothing interesting here +but we need this for jpa to function +properly +these getters and setters are made +through id refactorings so i didn't +write them +DAO +before we move to the methods i'd like +to discuss the concept of a dao +a dao stands for data access object this +is a conceptual idea there is no dow api +or requirement +you can skip it entirely +however it's a +very common best practice +when working with back-end systems for +instance in our application we have +three layers +web services the user-facing code +services the back-end logic +entities jpa the database +the roles are clearly separate +that's important as it means we can +replace or change one layer +significantly without impacting the +others +for instance we can move to websockets +replacing the web services layer +or we can move to nosqldb and throw away +the entity layer +so how do we transfer data between the +layers while keeping them logically +separate +enter the dow objects they aren't +entities entities are too close to the +data and are hard to modify those are in +place simply to pass along the data +the cool part about daos is that +springboot can automatically convert +them to json when sending a response +from the web service and automatically +create a new instance from json when +receiving a call +we could just pass the entity itself but +that would break the separation of +layers and might inadvertently expose +private data to the client side +so for the user object we have a similar +user dial equivalent +the fields are almost identical to the +fields of the user object +you will notice that even the relations +are dao objects +notice that the class includes private +data such as password and oauth +as you might recall from the user object +we never pass password into the dial and +it's hashed anyway so why do we need the +password in the dial +when the user is created or updated the +password value can be set the dow is +sent from the client side too +and that value may come from there +the token is returned in the dao once +after login or create +the dao must include a no arg construct +constructor so it can be instantiated by +spring boot +we also have a convenience constructor +for our use +the rest of the code is all +automatically generated getters and +setters +the one last missing piece for the user +object is the user repository interface +which allows us to query the user +objects +the implementation of these queries is +generated automatically by spring boot +based on the names of the jpa fields +ignore case is a special case keyword +for spring which works exactly as one +would think +with that we have the first entity and +the data area +and the basic spring boot server diff --git a/docs/website/video-transcripts/jR7_OTg-aG0.json b/docs/website/video-transcripts/jR7_OTg-aG0.json new file mode 100644 index 0000000000..f65b631d11 --- /dev/null +++ b/docs/website/video-transcripts/jR7_OTg-aG0.json @@ -0,0 +1,8 @@ +{ + "line_count": 27, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 157, + "youtube_id": "jR7_OTg-aG0" +} diff --git a/docs/website/video-transcripts/jR7_OTg-aG0.txt b/docs/website/video-transcripts/jR7_OTg-aG0.txt new file mode 100644 index 0000000000..4e4728187d --- /dev/null +++ b/docs/website/video-transcripts/jR7_OTg-aG0.txt @@ -0,0 +1,27 @@ +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/video-transcripts/jVuNrLw4e-A.json b/docs/website/video-transcripts/jVuNrLw4e-A.json new file mode 100644 index 0000000000..65df906081 --- /dev/null +++ b/docs/website/video-transcripts/jVuNrLw4e-A.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 199, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/017-the-native-interface-callback.md", + "status": "transcript-fetched", + "word_count": 1011, + "youtube_id": "jVuNrLw4e-A" +} diff --git a/docs/website/video-transcripts/jVuNrLw4e-A.txt b/docs/website/video-transcripts/jVuNrLw4e-A.txt new file mode 100644 index 0000000000..c94f5f4af9 --- /dev/null +++ b/docs/website/video-transcripts/jVuNrLw4e-A.txt @@ -0,0 +1,199 @@ +as i built this app +i took the code i used within it +and created a cn1 lib +that implements the native interface +described here +so a better way to implement braintree +support would be to use the official +braintree cn1 lib +however this is still useful +as an educational tool explaining both +braintree and how to map a native +interface +braintree is a pretty simple api on the +client side +as most of the +charging process is handled on the +server side of the api +the reason for this is that the client +side is far more hackable +even in a native app +in order to work with the api we start +with a token +which we can get from the server code +and then perform the charge process +which is handled most mostly seamlessly +by the library api +if you go to the braintree online docs +and look at the integration instructions +for native android code you will find +this code +this is native android code for making a +request +the token is something we need to get +from the server although conveniently +the braintree site provides a helpful +debugging token we can work with +this can fit right into our native +interface +but first i want to review the native +api to decide what sort of native calls +we would need +notice that even though a view object is +passed +it isn't really used and that a token +value is needed even though it isn't +passed +moving to the ios side the objective c +code isn't as readable for java +developers +so let's review this in pieces +this is a method declaration that +accepts +one string argument +technically in objective c +these are messages not methods but i +won't go into subtle language +differences +here we allocate a request +notice that this is the equivalent of a +new call in java +the init entry is a method invocation of +init +methods are invoked using a square +bracket syntax +init is a special case method in +objective c +and is usually used as a constructor +this is really an interesting block +so i'll split the block itself into +several +pieces that's simple it's just code that +allocates the drop-in controller not +much to it +this is like java's new however you will +notice that the brackets aren't closed +because the init method +or the constructor +should be invoked +the init method here is special +it accepts the token as an argument +as well as the request +notice two interesting things +the client token is just passed +but request is written twice +in objective c arguments are named that +means that you need to type the name of +every argument +to a method +and you can have two methods with the +same name but different argument names +to make matters more confusing the first +argument is a special case that doesn't +need a name +so +we have one argument +followed by the other arguments each +containing the name of the argument +and then the value +so the request argument name and value +make sense +but the last argument might seem odd +handler is an argument that accepts a +lambda expression with a callback +that callback +accepts several argument types +and will be invoked asynchronously +when a purchase +produces a result +the lambda expression itself +checks if the arguments represent an +error cancellation +or success +this code should be pretty familiar to +java programmers +the one weird thing is that objective c +strings start with the at character +which i won't go into here +the final piece of code is the present +view controller +which +should be +which shows the ui +notice it happens before the lambda +expression is invoked +as that is a callback +also notice we use the objective-c +keyword +self +which is the objective-c equivalent +of java's this keyword +in this particular case self is assumed +to be a view controller +so now that we've reviewed everything in +the method +it's pretty clear that the big thing +that's needed from the java side of the +api +is the token and not much else +this is something we can represent with +this simple method +once we implement that in the java side +we can use generate native stubs to +generate stub code for the various +native platforms and fill up that stub +code +one important piece that's missing from +the native interface is the callback +code +we need to monitor callback events so we +can know if a purchase succeeded +or failed +a more common approach would be to pass +the callback object instance +to the call +but here i use static callback code +the reason for that is simple +native interfaces have a lot of +restrictions on them and one of those is +that we can't pass arbitrary object +types +the reason for those restrictions is +simplicity and portability how would you +represent an object instance and the +objective c mapping +to implement callbacks +we use static methods +and some platform-specific code +calling the native interface directly is +often a bad practice +native interfaces are often fragile and +should be wrapped in an abstraction that +hides potential platform issues for +instance +if the ios and android native interfaces +have different features we might be able +to hide or abstract it in the java side +which is normally much easier to do +than implementing native code +in this code +we even have an attempt at fallback code +based on the javascript port of +braintree +it doesn't work all that well but that's +mostly because i didn't pay that much +attention to it +this class is mostly trivial sense the +native interface is trivial too +notice the code at the bottom that +invokes the static callbacks +this is a special case for ios +where the vm strips away unused code +by writing this com +this the compiler can't tell that the +code will never be invoked and will keep +it in place +notice that even making a seemingly +small change like making flag into a +private variable +might break this diff --git a/docs/website/video-transcripts/jhPep9vHv_U.json b/docs/website/video-transcripts/jhPep9vHv_U.json new file mode 100644 index 0000000000..2bff7f7bad --- /dev/null +++ b/docs/website/video-transcripts/jhPep9vHv_U.json @@ -0,0 +1,8 @@ +{ + "line_count": 109, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 558, + "youtube_id": "jhPep9vHv_U" +} diff --git a/docs/website/video-transcripts/jhPep9vHv_U.txt b/docs/website/video-transcripts/jhPep9vHv_U.txt new file mode 100644 index 0000000000..7506f20c9d --- /dev/null +++ b/docs/website/video-transcripts/jhPep9vHv_U.txt @@ -0,0 +1,109 @@ +finally we'll discuss the side menu and +preview functionality +two relatively big pieces of code +getting side menu navigation ui to look +good +has +many nuances and pitfalls +the specific ui includes some small +details including a different color to +the selected entry representing the +current form +a couple of points i'd like to highlight +are first +the large amount of padding +three millimeters on every side +that is very important for the feel of +an application don't be stingy with +padding the one pixel +bottom margin creates the effect of a +thin white line +separating the side command entries +and looks like a border between the +entries +going over the code we can see the image +at the top of the side menu is the same +one +in the main form +it wouldn't matter if it was a different +image +might actually look better +i use the logo icon mostly to give the +ui the right feel +otherwise the sign menu feels bare +the same is true for the tagline i use a +special ui id with some padding to space +it a bit from the bottom +without this tag line the side menu +would look empty and weird +here is an interesting trick normally +side menu commands have the side command +style +however i wanted the current form +command to be darker +so i +check what's the current form +and use the uiid attribute of the +command to determine the ui id +to +selected side command which happens to +look exactly like the side command's +pressed +style +but there is a problem +since the commands use +a material design command +icon it's determined before the ui id +has time to take effect and the icon +would have the lighter background color +the hack +is to use +the pressed icon for the icon as it will +look exactly like the selected side +command in this case +the last feature in this module +is preview +we can preview the app by pressing the +play button +and the trick is that we literally run +the restaurant app in place +we only modified it slightly +by adding an exit button to the side +menu +i copied the source of the restaurant +app +instead of making it more generic +it's a lazy solution +but i am lazy and proud of it +if the need arises i'll refactor +everything and build a +shared code base +but only when the effort justifies +itself and not a moment +sooner +to get this to work i had to rename the +theme.res file and the builder app so we +won't conflict with the theme of the +restaurant app +i did copy the sources +and did a few minor hacks in the code +but most of that could be generalized in +the future +i stepped out purchase.java as the +native interface portion would have been +hard to replicate +and it's not something i'd want in the +preview +showing the preview literally means +creating the main menu form from the +restaurant app and setting the theme +from there +then showing the first form +exiting means the reverse of that +with the now renamed builder menu +there are more efficient ways of doing +this but this is the most simple and +consistently reliable way +i found to accomplish this functionality +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/kfKsVAx659s.json b/docs/website/video-transcripts/kfKsVAx659s.json new file mode 100644 index 0000000000..4ba18b4737 --- /dev/null +++ b/docs/website/video-transcripts/kfKsVAx659s.json @@ -0,0 +1,8 @@ +{ + "line_count": 48, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 273, + "youtube_id": "kfKsVAx659s" +} diff --git a/docs/website/video-transcripts/kfKsVAx659s.txt b/docs/website/video-transcripts/kfKsVAx659s.txt new file mode 100644 index 0000000000..5def3f20c1 --- /dev/null +++ b/docs/website/video-transcripts/kfKsVAx659s.txt @@ -0,0 +1,48 @@ +the more tab is effectively all the +missing functionality facebook removed +when it eliminated the side menu ui +we won't implement most of this stuff +with the exception of user settings +which i'll do later +to keep the code short i added a static +import of the font image class so i can +reference material icon names directly +the code itself is trivial +we use container again and not infinite +container which is necessary in this +case we also have some padding all +around and activate scrolling which is +important the me button will lead us +later to the settings ui +right now the css api +doesn't support line borders +yet +so code handles that +every button icon has a different color +slash icon with roughly the same ui +so a generic method to create the button +can solve +that generic method just generates a +multi button +with the proper border color to create +the circle effect on the icon +there are quite a few ui ids here but +most of them aren't custom ui ids they +are built-in ui ids from multi-button +multi-line one represents the first line +of a multi-button +the avatar of the user we want +uh +for for the avatar of the user we want +more margin +so the text will be better positioned +the second line of the multi button is +gray and noticeably smaller +the circle icon itself has noticeable +padding and a large font size for the +icon +with this the mock-up of the main ui +should work and run +there is one last piece we need to go +through before going into the server +code diff --git a/docs/website/video-transcripts/kjUFJro0yUQ.json b/docs/website/video-transcripts/kjUFJro0yUQ.json new file mode 100644 index 0000000000..40ce68f6a2 --- /dev/null +++ b/docs/website/video-transcripts/kjUFJro0yUQ.json @@ -0,0 +1,8 @@ +{ + "line_count": 283, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1452, + "youtube_id": "kjUFJro0yUQ" +} diff --git a/docs/website/video-transcripts/kjUFJro0yUQ.txt b/docs/website/video-transcripts/kjUFJro0yUQ.txt new file mode 100644 index 0000000000..06ffbc6909 --- /dev/null +++ b/docs/website/video-transcripts/kjUFJro0yUQ.txt @@ -0,0 +1,283 @@ +security is a huge topic +that we can probably talk about for ages +for now i'll try to limit myself to the +basics +in this first part we'll discuss the +basics of security and certificate +pinning +Security Basics +before we begin i'd like to make a short +disclaimer +security slows stuff down and sometimes +makes your app more cumbersome the +question of why we didn't solve security +is a question of usability +a lot of core concepts and programming +would become harder and less efficient +this would also impact usability to the +user who often is the biggest +vulnerability there is +throwing permission prompts at users is +problematic +it triggers permission fatigue and users +tend to ignore these prompts at some +point +for programmers things as basic as +mutable state +can often cause vulnerability and code +removing security weaknesses +is pretty difficult +if you are still with me +let's start with the basics and common +terms +a term you should all be familiar with +is vulnerability +we hear that term a lot when discussing +security but it's a bit confusing +sometimes +a vulnerability is a security bug +but that doesn't mean anything all +software has vulnerabilities because +it's so hard to avoid for instance if +you have a method that returns a java +array +your code is probably vulnerable +a caller can change the element in the +returned array and impact internal state +working around this vulnerability by +creating a new copy of the array will be +expensive +most of us are willing to risk security +for extra ram and cpu power an exploit +converts a vulnerability or a few +different vulnerabilities into a hack +so if you offer a banking sdk and return +internal state in an array i can then +change that internal state and hack the +sdk +that's an exploit +there are different grades of exploit +and +sometimes you will need to chain them +the worst kind of exploit is a remote +root exploit +a remote root exploit is a vulnerability +you can use for full control of a system +without physical access to the machine +that's the worst kind of exploit and +it's pretty rare +usually security works in layers with +penetration of one layer +isn't enough you would still face +another layer below it +so let's take the example of returning +internal state data +and continue with that +let's say one of the objects in the +array has an os native method +let's say that method +has a vulnerability that allows us to +step outside of the virtual machine +the os +should have restrictions too +so even if we can step outside of the vm +we still be in a sandbox +let's say the os has a user escalation +vulnerability where the attacker can +elevate his privileges to root +so something small like returning an +array can become the missing piece in a +big attack +this isn't common as such +vulnerabilities are usually fixed by +vendors +our chief concern as developers is the +safety of our app data +and access +more than device root exploits +codename one tries to be as secure as it +can be by default +some of our design choices make codename +one more secure by default +even when compared to native coding and +especially when compared to other +approaches +the first is obfuscation by default +codename one obfuscates its code +this isn't a complete +roadblock to a hacker a diligent hacker +can get around obfuscation +still it makes reverse engineering +an application +harder +obfuscation is especially hard to deal +with in codename one +even compared to native code +both android and ios +store ui data in readable formats +reverse engineering tools can decode +these uis by default +a hacker can point at a button and find +the code that binds to that button +this undermines obfuscation and native +applications +in codename one even if you use the gui +builder the ui compiles to bytecode +and thus to native +so the ui gets obfuscated with the rest +of the business logic code +this means you will have more code +but no readable layout files +reverse engineering tools will have +nothing to go on with codename one +even when tools adapt to codename one +this is unlikely to change due to code +obfuscation +in that sense codename one is even more +secure than a typical native app +think about the way a typical hacker +will crack your application +let's say you have a login button +and the hacker wants to grab the +username and password +the hacker can upload a hacked version +of your app to the store and grab the +credentials from a fake app +users of the fake app will be oblivious +to the steel +as the app will look and behave the same +way +the first step to hacking an app this +way +is to find the button and fields code +in a native app this would be doable +as both would be in the xml +so +the hacker's job is trivial +for a codename one app this would be a +tedious process of digging through +obfuscated code +finally release builds +add a few layers of default security for +instance blocking remote debugging etc +this is especially true for android +Certificate Pinning +as i mentioned before we try to be +secure by default however some potential +vulnerabilities +would impact usability too much the +first protection system i'd like to +discuss is certificate pinning +certificate pinning thwarts a man and +middle attack on ssl +to understand this +we need to understand +ssl and man in the middle attacks +when we connect +to a server +there are many servers along the way +for instance if your user is in an +internet cafe +and a hacker hacked the wi-fi server +when he connects to your server the hack +can spy +and even change all of the communication +we use ssl or https to encrypt all the +data +and this works by sending an encryption +key from the server to the client +what if the hacker sends his own +encryption key +that's why we have a certificate +authority +you can create a certificate for +yourself +you need an authority to verify your +identity +for instance i can buy a certificate +from very sign who will verify my +identity +that way the client knows the +certificate can be trusted +the problem is that certificate +authorities got hacked in the past +so it's possible a hacker would get a +valid trusted certificate +in that case he can execute a +man-in-the-middle attack +this scenario is pretty challenging +a hacker would need to get in the middle +he would also also need a valid fake +certificate +this is a ca a case that would be +possible in the age of government +sponsored hacking +if you are building an app for the +government +or financial institutions +you should be aware of this +in those cases you should use +certificate pinning as an extra layer of +security on top +certificate pinning verifies a +certificate signature of the server +we can thus verify that the certificate +we are communicating with is a specific +one +the downside for this is updates if we +update the certificate on the server we +need to update the installed +applications they might stop working or +start warning users +in valid server update situations +Connection Request +the connection request class contains a +check ssl certificates method +that accepts an array of the +certificates supported by a secure +connection +you can verify that a signature of a +server +matches a specific signature +that you expect +and that way prevent a potential exploit +you can loop over the array of +certificates and verify that a proper +certificate is in place +the certificate +unique key you get in the simulator +should match the one you have in +production +the drawback of the security layer +is in flexibility +normally we can just replace the server +and set up a new certificate +as long as the certificate is valid this +is pretty seamless +but with pinning we need to jump through +a few hoops +that might mean that if you update the +server apps +that are installed on a user device will +stop working +another important bit +is the algorithm used +most servers support multiple signing +algorithms for maximum compatibility +however +algorithms such as +sha-1 are already long in the tooth +sha-1 +can be defeated with modern hardware +and it might be better to pin a stronger +signature +such as sha 256 etc +every certificate has an algorithm +associated with it +and you should pick the best option +notice that you shouldn't depend on the +number of certificates +or their offsets +as this will block the flexibility of +the server team +to make changes diff --git a/docs/website/video-transcripts/l1TPKuHYr9c.json b/docs/website/video-transcripts/l1TPKuHYr9c.json new file mode 100644 index 0000000000..3ceae8c3bb --- /dev/null +++ b/docs/website/video-transcripts/l1TPKuHYr9c.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 76, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/034-running-the-kitchen-sink-in-the-simulator.md", + "status": "transcript-fetched", + "word_count": 444, + "youtube_id": "l1TPKuHYr9c" +} diff --git a/docs/website/video-transcripts/l1TPKuHYr9c.txt b/docs/website/video-transcripts/l1TPKuHYr9c.txt new file mode 100644 index 0000000000..02d01a7014 --- /dev/null +++ b/docs/website/video-transcripts/l1TPKuHYr9c.txt @@ -0,0 +1,76 @@ +now that we have the layer of the land +let's start building things specifically +the kitchen sink demo +first we'll build cldc11 as it's pretty +trivial +after that we should have +this slash cldc11 cldc11.jar +next we'll build the library itself +we now have codename one compiled notice +a pattern here +it's relatively simple at this stage +next we want the simulator so we will re +repeat the same process for the javasc +port notice i invoked and jar twice this +isn't a mistake the first time copies a +skin file into place +but does that a bit too late a second +time generates a proper jar +we should now have codename1.jar +from the codename1project this directory +and the javasc jar from the javasc +project this should allow us to build a +hello codename one project +the catch and sync is a simple yet +comprehensive demo that reviews a wide +range of the codename one functionality +it's a good candidate to compile so we +can start by pulling it from git +alternatively you can download the +zipped url from github +a codename one project is a standard and +project which we can build and run +similarly to other and projects +notice that we are missing the codename +one build client.jar +unless we use the codename one ide +plugin +this is the jar that effectively +implements the build server +functionality in codename one +if you use the codename one plugin you +can just copy it from another codename +one project +however if you don't intend to use the +build servers at all you don't really +need it +since ant is sensitive to missing +dependencies you will need to remove the +codename one ant tasks from the file +notice that this is only necessary if +you don't have codename one build +client.jarrow +the file is really simple and doesn't +include many targets most of the targets +included in the file +are included in the file nb project +slash build example.xml +which netbeans defines +this is just a bit of guarding against +compilation issues i included the full +file in the resources for this module +finally in order to compile the project +we need to copy the following files into +the kitchen sync directory and run and +after this is done you can now run the +project in the simulator using either +and or the manual java command +you will notice we are running the +simulator class not the main class +which in the case of the kitchensync +project is called com.codnam1 +the simulator loads the class +dynamically and handles the device skin +that class and package are defined in +the codename one settings +dot properties file diff --git a/docs/website/video-transcripts/l97sDefhHMM.json b/docs/website/video-transcripts/l97sDefhHMM.json new file mode 100644 index 0000000000..54f8001781 --- /dev/null +++ b/docs/website/video-transcripts/l97sDefhHMM.json @@ -0,0 +1,8 @@ +{ + "line_count": 143, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 800, + "youtube_id": "l97sDefhHMM" +} diff --git a/docs/website/video-transcripts/l97sDefhHMM.txt b/docs/website/video-transcripts/l97sDefhHMM.txt new file mode 100644 index 0000000000..32a2270db8 --- /dev/null +++ b/docs/website/video-transcripts/l97sDefhHMM.txt @@ -0,0 +1,143 @@ +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/video-transcripts/m43A68sNcvg.json b/docs/website/video-transcripts/m43A68sNcvg.json new file mode 100644 index 0000000000..971e261714 --- /dev/null +++ b/docs/website/video-transcripts/m43A68sNcvg.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 82, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/031-hello-world-and-devices.md", + "status": "transcript-fetched", + "word_count": 435, + "youtube_id": "m43A68sNcvg" +} diff --git a/docs/website/video-transcripts/m43A68sNcvg.txt b/docs/website/video-transcripts/m43A68sNcvg.txt new file mode 100644 index 0000000000..6e2ed0bb65 --- /dev/null +++ b/docs/website/video-transcripts/m43A68sNcvg.txt @@ -0,0 +1,82 @@ +now that we have maps installed and have +the keys let's proceed to the hello +world stage +the obvious hello world is +this thing +we just create map container and show it +this looks awful +that is the map component with +openstreetmap +it takes a while to load and isn't +exactly attractive +so let's add the javascript key to the +constructor so the fullback will be the +javascript maps instead of open street +maps +this looks more familiar and modern +now we could use google map provider +with map component +which might +improve the look of the component a bit +but i don't see a reason to dwell on +that when we can just use the javascript +fallback +in the past before z-order appears that +made some sense but it no longer does +now let's configure the build hints for +the individual os settings +first the android native map +api key needs to be specified using this +exact syntax +you can copy and paste it from the maps +github page where all of these keys are +listed +don't forget to replace your android api +key +with the key generated in the urls i +mentioned in the previous module +next we need to do that same thing for +ios and specify the ios key +in this build hint +and last but not least we need the +javascript key2 +normally this isn't essential since we +pass the javascript key in the +constructor +we need this for the javascript port +where the key +needs to be specified in another +location +building this to native android produces +this result on the device +however +the ios build fails +looking at the build error it's clear +there was a null pointer exception +when running the application +but this is an error from the build +server +as you might recall ios native +applications need a splash screen +the codename one build server solves +this by running the application several +times in the servers +where it generates splash screens for us +however the build servers don't support +the web browser component +the reason for this is simple +the web browser component will look +different on the device and so a +screenshot won't work well +the solution is to handle a case where a +web browser isn't supported +this fix is relatively trivial +if web browser isn't supported +it has to be the ios screenshot mode +and in this case we can just mention +that we are loading +a nice graphic might be relevant here +if we want to refine this further +and now +that this was built it looks like this +in ios diff --git a/docs/website/video-transcripts/mFFjxs9EDW8.json b/docs/website/video-transcripts/mFFjxs9EDW8.json new file mode 100644 index 0000000000..779eb3a752 --- /dev/null +++ b/docs/website/video-transcripts/mFFjxs9EDW8.json @@ -0,0 +1,8 @@ +{ + "line_count": 93, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 498, + "youtube_id": "mFFjxs9EDW8" +} diff --git a/docs/website/video-transcripts/mFFjxs9EDW8.txt b/docs/website/video-transcripts/mFFjxs9EDW8.txt new file mode 100644 index 0000000000..6847b4fc20 --- /dev/null +++ b/docs/website/video-transcripts/mFFjxs9EDW8.txt @@ -0,0 +1,93 @@ +up until now we focused on the visual +aspect of the application +without giving much thought to the +architecture +sometimes we start by designing the +model or underlying logic and sometimes +we take a more top-down approach +in mobile i like starting with the ui +design +as this is such an important part of the +product experience +we'll start by breaking the ui and +business logic apart +the first step is understanding the +pieces that are involved in the backend +model +we then need to integrate that model +data into the ui code and decide on the +right point of separation between the +model and the ui +i don't spend much time in front of a +whiteboard drawing architectural +diagrams +architecture is usually an iterative +process +where i can adapt the design to match +the evolving need +good architecture starts with necessity +i needed the dish class to represent the +data model +before +and now it's a great point to begin with +our ui already communicated some of the +necessity from the model +dish is obvious +but the main ui maps perfectly to a +restaurant menu +the checkout ui maps to a specific order +which we should also represent within +the model +we need a global state that contains the +menu +current order etc +this object can be represented by a +restaurant object +to which we can push a few other +variables +into that object +the menu object is simple it includes a +list of dishes and categories there +isn't much here +order is also pretty self-explanatory +notice that the menu object didn't have +an id +while the order has an id +we have one menu for the app so an id +isn't really necessary but we can have +multiple orders and might want to keep +a record of each order +also notice i used a string +for an id rather than a numeric id which +allows more flexibility in id generation +dishes are stored in a map to quantity +which might not be the best data model +but it works +this necessitates +an equals and hashcode method +implementation on dish +the restaurant object is a singleton +for simplicity's sake at least at this +point most of the details i need in the +app from the name of the restaurant to +its location +are all within this object +this is important as it provides us with +the necessary flexibility and allows us +to adapt the app to fit any restaurant +we want without code changes +we initialize the restaurant +properties in the constructor +i've snipped this a bit to keep it +narrow +but generally we set the values from a +properties file so we can customize this +easily +i hard coded the menu and dishes +at this point so we don't need to deal +with the complexities related to this +set of features +as i mentioned before the restaurant is +a singleton i load the values for it +from a properties file +within the app at this point diff --git a/docs/website/video-transcripts/mqNtfN2C3fM.json b/docs/website/video-transcripts/mqNtfN2C3fM.json new file mode 100644 index 0000000000..62890a9926 --- /dev/null +++ b/docs/website/video-transcripts/mqNtfN2C3fM.json @@ -0,0 +1,8 @@ +{ + "line_count": 117, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 618, + "youtube_id": "mqNtfN2C3fM" +} diff --git a/docs/website/video-transcripts/mqNtfN2C3fM.txt b/docs/website/video-transcripts/mqNtfN2C3fM.txt new file mode 100644 index 0000000000..0004088c66 --- /dev/null +++ b/docs/website/video-transcripts/mqNtfN2C3fM.txt @@ -0,0 +1,117 @@ +let's go to the client side for a moment +and do some operations on your local +machine +specifically file copy +scp is secure copy +it allows us to copy a file from our +desktop to the server or vice versa +on windows there are some visual scp +tools +but on mac i like using the command line +which i find more convenient +the syntax might seem odd +if you are familiar with the unix cp +command this is roughly the same syntax +only through the network this code is +executed on my local mac +so the first argument copies my instance +of +restbuilder.properties +which includes server configuration +settings that's the file we will be +copying +the second argument +is the destination +since it's remote it starts with the +username on the remote machine +followed by the at sign +and the ip address of the remote machine +then a colon is used to give the +destination path on the remote machine +you will be prompted for the password +of builder at which point copy will +proceed +this is very useful +command +now that we got that out of the way i +can just copy the actual jar to the +server +same exact thing +and now back on the server +we can open a text editor and set the +value of +the server url to the current ip address +nano +is a reasonably easy to use text editor +at least when compared to vi +you can use control x to exit +you will be prompted to save +next +let's log in to mysql +we'll be prompted for the password we +need to run the create database command +notice that the tables will be created +implicitly by jpa +the next step is to download apache and +which we need in order to build the +resulting application +we can unzip it +in the user's home directory thanks to +the unzip command +we installed previously +since the version number for ant is a +bit problematic i'm renaming the +directory and +so the build process can find it +the mv command represents move +and works as rename +i also rename +the backend server it shouldn't include +the version +as we'll update it in the future and we +don't want it to carry an outdated +version number +technically it might have been more +correct to use a sim link +but i want to keep things simple +you might recall we did a su builder +so i'm just exiting from that su command +and returning to the root account if i +use exit from there i will log off from +the server +this command might be confusing to +windows users +since there is nothing like this on one +on windows +the unix file system supports a feature +called symbolic links +essentially it creates what looks and +acts like a file or a directory +but it's really a file system pointer at +a file that resides elsewhere +in this case i created a sim link to etc +in a d +named app backend server +notice that it doesn't have the jar +extension +this points to the jar file we have in +the builder home directory +if you know linux or unix this might +look +really weird +if you know linux or unix you might be +thinking +what the hell just happened +well +the jar is really a linux shell script +too +so by placing a sim link in the nfd +directory it starts working as a service +just like mysql +i can't stress enough how amazing this +is +everything works exactly like you would +expect from any other service on linux +for instance you can see the output logs +for the service +under the var log directory diff --git a/docs/website/video-transcripts/nF4eqzVcsic.json b/docs/website/video-transcripts/nF4eqzVcsic.json new file mode 100644 index 0000000000..548e93123e --- /dev/null +++ b/docs/website/video-transcripts/nF4eqzVcsic.json @@ -0,0 +1,8 @@ +{ + "line_count": 92, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 635, + "youtube_id": "nF4eqzVcsic" +} diff --git a/docs/website/video-transcripts/nF4eqzVcsic.txt b/docs/website/video-transcripts/nF4eqzVcsic.txt new file mode 100644 index 0000000000..255802b0fe --- /dev/null +++ b/docs/website/video-transcripts/nF4eqzVcsic.txt @@ -0,0 +1,92 @@ +hello guys today I want to talk to you +about uh image capture and grabbing a +photo with your camera so as you can see +I have a UI here with a capture button +and just a photo label label right below +it so to get started all I need to do is +actually uh click the capture and add an +action event to that capture entry so +we'll go to net beans as you can and as +you can see we have this uh right here +Capture Methods +and what I can do is essentially uh just +use the capture class which allows me to +capture all sorts of things from audio +to video and obviously photos of +different +kinds so to capture a photo I can just +use one of these three methods the +simplest one just captures a photo and +places it in a in a file and gives me +the name of the file or null if the the +file doesn't exist the second one is +interesting it uh returns immediately as +you can see it returns void and it will +call the action listener later with the +string of the file this is uh useful but +more complicated to invoke and use in +general it allows you to do things +asynchronously which is sometimes more +convenient to some people uh I think +personally this and this method are much +simpler +uh and the last one uh is an interesting +one it captures a photo just like the +first method but it also scales it after +the fact so you effectively get uh a +photo that is of the given width and +given height uh within uh your +Resort so that that is really useful uh +because cameras might capture really +really large images that might take a +lot of memory so we recommend you don't +uh use or load such images and usually +we recommend using just this method and +scaling to something that makes sense so +in this case I'm going to uh use the +display width as the Target and notice +Create Image +I'm giving minus one as the height that +means that we'll maintain aspect ratio +based on the width of the image and here +I can get the image back and if the +image isn't +null which it can be if the user cancels +or something like that then I'll just uh +find the photo and set the IM the icon +to my image and to create the image all +I'll do is image create image and give +it +this and I'll need to wrap this up with +a try +catch +block like +this +no I said +like this in this particular case this +is obviously an oversimplification but +it should work now uh one more thing I +need to do is get the component form and +revalidate because if I don't revalidate +then this line will effectively do +nothing it will set the icon but I won't +see anything because it will be too +small so to see this in action I can +just +play and we'll see the UI that I've +created earlier with the capture button +I can press the capture button you will +notice that it just gives me uh the file +dialogue and I can pick the image that I +want this is how it behaves in the +simulator on the device obviously it +will literally capture an image and I +can pick the image I want in this +particular case I have screenshots from +applications right here and as you can +see it captured the image on the device +it will literally capture uh from the +camera so thanks for watching I hope +you've enjoyed this tutorial and learned +how to uh capture images in code name +one thank you diff --git a/docs/website/video-transcripts/nImSppBdgkY.json b/docs/website/video-transcripts/nImSppBdgkY.json new file mode 100644 index 0000000000..3a0cb34c22 --- /dev/null +++ b/docs/website/video-transcripts/nImSppBdgkY.json @@ -0,0 +1,8 @@ +{ + "line_count": 186, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1121, + "youtube_id": "nImSppBdgkY" +} diff --git a/docs/website/video-transcripts/nImSppBdgkY.txt b/docs/website/video-transcripts/nImSppBdgkY.txt new file mode 100644 index 0000000000..43d6e3d526 --- /dev/null +++ b/docs/website/video-transcripts/nImSppBdgkY.txt @@ -0,0 +1,186 @@ +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/video-transcripts/oR3KHYf5OrY.json b/docs/website/video-transcripts/oR3KHYf5OrY.json new file mode 100644 index 0000000000..63ea63e3f7 --- /dev/null +++ b/docs/website/video-transcripts/oR3KHYf5OrY.json @@ -0,0 +1,9 @@ +{ + "line_count": 23, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-intellij-idea.md", + "status": "transcript-fetched", + "word_count": 607, + "youtube_id": "oR3KHYf5OrY" +} diff --git a/docs/website/video-transcripts/oR3KHYf5OrY.txt b/docs/website/video-transcripts/oR3KHYf5OrY.txt new file mode 100644 index 0000000000..666454bf86 --- /dev/null +++ b/docs/website/video-transcripts/oR3KHYf5OrY.txt @@ -0,0 +1,37 @@ +_Transcript source: embedded._ + +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. + +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. + +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. + +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/). diff --git a/docs/website/video-transcripts/owhInk5YAtg.json b/docs/website/video-transcripts/owhInk5YAtg.json new file mode 100644 index 0000000000..43e58ea8fc --- /dev/null +++ b/docs/website/video-transcripts/owhInk5YAtg.json @@ -0,0 +1,8 @@ +{ + "line_count": 184, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 990, + "youtube_id": "owhInk5YAtg" +} diff --git a/docs/website/video-transcripts/owhInk5YAtg.txt b/docs/website/video-transcripts/owhInk5YAtg.txt new file mode 100644 index 0000000000..55a1d25999 --- /dev/null +++ b/docs/website/video-transcripts/owhInk5YAtg.txt @@ -0,0 +1,184 @@ +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/video-transcripts/p05yMCleSLo.json b/docs/website/video-transcripts/p05yMCleSLo.json new file mode 100644 index 0000000000..3ece953998 --- /dev/null +++ b/docs/website/video-transcripts/p05yMCleSLo.json @@ -0,0 +1,8 @@ +{ + "line_count": 139, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 749, + "youtube_id": "p05yMCleSLo" +} diff --git a/docs/website/video-transcripts/p05yMCleSLo.txt b/docs/website/video-transcripts/p05yMCleSLo.txt new file mode 100644 index 0000000000..f8e7f91f1b --- /dev/null +++ b/docs/website/video-transcripts/p05yMCleSLo.txt @@ -0,0 +1,139 @@ +in this section we'll discuss the star +form +which allows us to determine the style +of elements within the application +the style form allows us to view the +main ui of the application +and touch various elements +so we can customize their foreground +color background color and font +there are a lot of limitations i imposed +on this ui to keep things relatively +simple +fonts can't be customized on something +that doesn't actually have a font +such as an icon +we only allow a very small selection of +font options +the reason for that is +difficulty of +getting this right across platform +where there are so many options +available +the color picker is also relatively +a relatively simple widget +that just allows us to drag the rgb +channels or set the hex value of the +color +i wanted to keep this ui as trivial as +possible +you can reset the changes in two ways +use the back button on the top left +without saving or use the restore style +option +within the popup dialog +the style form allows us to define +that ui +notice that it derives from form +but is otherwise a pretty standard class +we derive from form instead of the base +form class as we don't want the side +navigation menu and other thrills that +come +with a custom base class +there is another reason +we want to apply styling aggressively +and if we derive from the main form +we might run into some issues +the style setting class contains +information about changes to a specific +style +we use this to store the changes defined +by the user +and send this data to the user +i'll +cover that class soon enough +the toolbar styling is something we need +to do in code +otherwise when we start shifting styles +around things can get messy +this is the back command and right below +we can see the ok command +with the check next to it +both do exactly the same thing +they reload the builder theme +so we can restore it just like we did in +the preview code in the preview code +then show the previous form +one thing i do want to highlight here is +the insert or update call +which loops over the styles and saves +them +when we make a change +as you can see +the theme for the restaurant tab +is set when we first launched this form +so it controls +most of the ui here +moving ahead we can see the code that +deals with applying the styles saved in +storage +we load the styles from storage and then +loop over them +for each type font foreground or +background we create a theme entry +a theme in codename one is a hash table +that has keys and values +with the format you see in the code +for instance +button dot fg color represents the +foreground color for the theme +and so forth +we can then use add theme props to layer +this theme on top of the existing theme +and thus load the style settings from +the database +moving on we place the main menu form +from the restaurant app in the center of +the user interface +so we can click on the actual restaurant +app +notice we make the toolbar focusable +we need that so the tapping on the +toolbar will allow us to customize it +otherwise some special case logic of the +form class +might kick in +we override the pointer events in the +form such as pointer dragged pressed etc +if the event is +in the content pane +we discard it +if not +we send it on +this allows events in the toolbar but +blocks events everywhere +this works because events in codename +one propagate through the parent form +and so if we override the pointer events +and don't call super it's as if the +event never happened +that can be useful if we want to provide +complete custom functionality +the pointer released +override is below the get start setting +and we'll discuss that more in depth +soon enough +but first i want to talk a bit about +getstar settings method +this method caches styles from sql and +loads them lazily you might think why +would we need that +the reason is cancel +we need the ability to cancel the +changes so we can't persist every star +change +we make +we need to save them in the list and +only update the database when the ok +button is pressed diff --git a/docs/website/video-transcripts/p6UFNw0nGik.json b/docs/website/video-transcripts/p6UFNw0nGik.json new file mode 100644 index 0000000000..2ac7585f74 --- /dev/null +++ b/docs/website/video-transcripts/p6UFNw0nGik.json @@ -0,0 +1,8 @@ +{ + "line_count": 166, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 887, + "youtube_id": "p6UFNw0nGik" +} diff --git a/docs/website/video-transcripts/p6UFNw0nGik.txt b/docs/website/video-transcripts/p6UFNw0nGik.txt new file mode 100644 index 0000000000..9db10ee6c1 --- /dev/null +++ b/docs/website/video-transcripts/p6UFNw0nGik.txt @@ -0,0 +1,166 @@ +in this session i'm going to talk about +one of the more +complicated subjects and that's +threading and the edt +threading in general is one of the +harder things in +programming not because of +the immediate things but rather the +less obvious implications of +some behaviors when it comes to threads +and there's a lot of nuance when it +comes to threading i'm not going to go +into that nuance and mostly leave that +to the more +advanced thread theory books and things +related to that +i'm going to talk mostly about threading +in a higher level +which is +one that we mostly apply in codename one +but notice that a lot of things when +people +rely on behaviors of the java virtual +machine memory model like double locking +semantics and things like that if you're +familiar with that +don't try to be adventurous when it +comes to codename one because +it's about +portability and +one vm will behave in one way and +another will behave in another way when +it comes to the memory model +we can't keep up with the same level of +accuracy that uh standard jvms keep up +because we translate to native code +and this is sometimes very challenging +to do +so +even experts sometimes have an issue +with uh doing the nuances +uh +try to keep threading as simple as +possible and use it just uh as much as +needed to know as needed there's no and +no more than that +so +about reading uh +the most important thing in coding one +is the event dispatch thread +and that's essentially codename one is +single threaded in the sense that you +have that thread and that thread alone +cannot access the user interface or +should access the user interface +actually +as the correct term +you can open other threads and the +network sits on a different thread and +there are other complexities related to +threads but generally +as a whole codename one is +considered a single threaded environment +that means it's not +thread safe +there aren't +many apis in the modern area era that do +thread safe +apis to developers and that's because +it's just +almost impossible to do that correctly +awt tried to do that uh ads and +microsystems and to this day there are +open bugs that are just unfixable in awt +in terms of threading because it's so +damn hard to get thread safe to work +correctly +so +the approach there is to go the exact +opposite direction and try not to be +thread safe at all +and then also optimize the code as a +result because locking is one of the +most expensive things you can do in +terms of +low-level apis and when you need to +render things to draw things on the +screen you don't want to waste time with +one of the most expensive apis you can +use +so you don't want to do it often or much +you need to do it to some degree because +there is absolutely no choice +uh +actually we need to do it +so you don't have to the adt uses +locking inside it but you and your code +don't need to as a result of of the +existence of the edt +so +uh +the os's themselves have their own event +dispatch to it this is pretty much +for all +and it doesn't actu necessarily be uh +act as one native underlying os thread +but usually whenever an application is +instantiated it gets one thread that's +allowed to access the the native system +user interface and that's it +and that's per application usually +and +we +with our edt essentially hide the native +event dispatch grid +and that sounds redundant you know why +have two edts why have a native one and +why not just expose that directly to the +developer well +that's very unportable +and the native edts behave very very +very differently between os's +and they don't have all sorts of +abilities +not all of them some of them do but not +all of them have something like invoke +and block +and +when we want one of the things in +codename one is that +the secret to its portability is first +lightweight components but the second +secret which is +arguably bigger +is the fact that we have the edt and the +edt makes sure that events arrive at a +consistent way +and at a consistent +behavior +and with the native thread you wouldn't +be able to get the same level of +consistency +throughout +so +that's really important for for the +overall portability +and +i normally try to avoid uh +charts in my +videos because i'm not a very visual +person in terms of structural thinking +but in the case of ddt it's sometimes +hard to explain what is actually going +on so i did a really really trivial +chart that i later enhanced to explain +another another idea +and the basic edt is just a while loop +and this is something that people need +to understand it's it's an infinite +while loop that just runs as long as the +code in one application runs +and it paints and then it sleeps and it +paints and it sleeps and the sleep by +the way is a very important part of it +because +when you do threads you need to diff --git a/docs/website/video-transcripts/q4YjUClRHBk.json b/docs/website/video-transcripts/q4YjUClRHBk.json new file mode 100644 index 0000000000..58af7b872b --- /dev/null +++ b/docs/website/video-transcripts/q4YjUClRHBk.json @@ -0,0 +1,8 @@ +{ + "line_count": 88, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 477, + "youtube_id": "q4YjUClRHBk" +} diff --git a/docs/website/video-transcripts/q4YjUClRHBk.txt b/docs/website/video-transcripts/q4YjUClRHBk.txt new file mode 100644 index 0000000000..83d98714bf --- /dev/null +++ b/docs/website/video-transcripts/q4YjUClRHBk.txt @@ -0,0 +1,88 @@ +since the facebook app has many ui +elements the mock-up stage for this app +stretches on longer than i would have +hoped +the nice thing about this is that the +logic of the app is relatively simple +so once the mock-up would be done +it should be pretty easy to implement +the basic business logic +notice that the smart portion in +facebook +is the back-end analysis which are +mostly skip +it's a subject for a big data course not +a mobile development course +i discussed the main ui before so i +won't go into a recap of the of its +purpose +we'll mark up the basics so we can get +started with implementation and server +code for now i'll skip camera and video +as we'll address them later +the big piece is the main ui which +includes the four tabs for news feed +friends notifications and more features +let's jump right into the main ui +the main form includes four containers +that are arranged as tabs you will +notice that the tabs default to top +placement on android and bottom +placement on ios +this matches how the native facebook app +behaves +to support this ui we need container +classes representing each tab +we need to add four classes named +newsfeed container friends container +notifications container and more +container +we'll get to those four containers one +by one +but first i'll explain the main form +itself +it's important to place the tabs in the +center +they never scroll off the screen and +take up the available +space +we add each tab and give it a five +millimeter icon +from the material icon set +the camera and chat icons aren't +implemented at this time +search looks like a text field but it's +a button that leads to the search form +i'll implement that form later +this is +relatively simple +we also need some css to support that +this the image includes a few ui ids +we'll cover a bit later when discussing +the news feed +let's look at the css code behind that +i chose to implement the search ui in +the ios style with a round pill border +shape +we create an opaque white tab and give +it a small margin and pixels on top and +bottom +the tab container below +the tab is gray +it lets us form a gray line above and +below the tab +the selected tab has a border on some +native platforms so it's important to +disable that +here we just want a different color for +the selected and pressed styles +once this is done +we have the basic tab ui +we can wire it into the main ui by +implementing show main ui in ui +controller +once this is done pressing login will +show the main ui form +now we can proceed to implement the tabs +but first we need +something else diff --git a/docs/website/video-transcripts/qEgDZHZJEYo.json b/docs/website/video-transcripts/qEgDZHZJEYo.json new file mode 100644 index 0000000000..52b221d488 --- /dev/null +++ b/docs/website/video-transcripts/qEgDZHZJEYo.json @@ -0,0 +1,8 @@ +{ + "line_count": 69, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 386, + "youtube_id": "qEgDZHZJEYo" +} diff --git a/docs/website/video-transcripts/qEgDZHZJEYo.txt b/docs/website/video-transcripts/qEgDZHZJEYo.txt new file mode 100644 index 0000000000..c7bc738e08 --- /dev/null +++ b/docs/website/video-transcripts/qEgDZHZJEYo.txt @@ -0,0 +1,69 @@ +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/video-transcripts/qGJaVaQ_MNY.json b/docs/website/video-transcripts/qGJaVaQ_MNY.json new file mode 100644 index 0000000000..718d75c8ad --- /dev/null +++ b/docs/website/video-transcripts/qGJaVaQ_MNY.json @@ -0,0 +1,8 @@ +{ + "line_count": 115, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 617, + "youtube_id": "qGJaVaQ_MNY" +} diff --git a/docs/website/video-transcripts/qGJaVaQ_MNY.txt b/docs/website/video-transcripts/qGJaVaQ_MNY.txt new file mode 100644 index 0000000000..76a2a6ea2b --- /dev/null +++ b/docs/website/video-transcripts/qGJaVaQ_MNY.txt @@ -0,0 +1,115 @@ +performance is one of those vague +subjects that is often taught by example +in this section i want to discuss some +of the performance optimization +strategies we took when working on the +second version of the kitchen sync demo +during our debugging of the contacts +demo that is a part of the new kitchen +sync demo we noticed its performance was +subpar +we assumed this was due to the +implementation of get all contacts and +there is nothing to do +while debugging another issue we noticed +an anomaly during the loading of the +contacts +this led to the discovery that we are +loading the same resource file over and +over again for every single contact in +the list +conclusion +don't assume +Dont Assume +in the contacts portion of the demo +we have a share button for each contact +the code for constructing a share button +looks like this +this seems reasonable +until you realize that the constructors +for sms share email share and facebook +share +load the icons for each of those +these icons are in a shared resource +file that we load and don't properly +cache +the initial workaround was to cache this +resource but a better solution +was to convert this code +this way the resource uses +lazy loading as needed +this small change boosted the loading +performance and is probably the general +performance due to the less memory +fragmentation +the lesson that we should learn every +day is never assume about performance +Image Loading +another performance pitfall in this same +demo came during scrolling scrolling was +janky uneven and smooth right after +loading finished +would and would recover after a couple +of minutes +this relates to the images of the +contacts +to hasten the loading of contacts we +load them all without images +we then launch a thread that iterates +the contacts and loads an individual +image for a contact +then sets that image to the contact and +replaces the placeholder image +this performed well in the simulator +but didn't do too well even on +a powerful mobile phones +we assumed this wouldn't be a problem +because we used util dot sleep to yield +cpu time but that wasn't enough +often we +when we see performance penalty the +responses move it to a separate thread +the problem is that +this separate thread +needs to compete for the same system +resources and merge its changes back +onto the edt +when we perform something intensive we +need to make sure that the cpu isn't +needed right +now we added a variable called last +Scrolling +scroll which updates when the user tries +to scroll the screen +with the current time +then we did this within the background +loading thread this effectively sleeps +when the user interacts with the ui +and only loads the images if the user +hasn't touched the ui in a while +notice that we also check if the scroll +changes this allows us to notice cases +like the animation +of scroll winding down +all we need to do now is update the last +scroll variable whenever user +interaction is in place +the code above works for user touches +the code below works for general +scrolling +due to technical constraints we can't +use a lambda in this specific case +once this was done scrolling became +smooth and filled +out the entries when we were idle +we wrote this code before call serially +on idle existed i wonder if it would +have worked as well for this particular +use case +it's really easy to optimize in a +cross-platform way +i optimized by moving stuff to a +separate thread and yielding for the +main thread +this would impact all platforms and +provide a smoother experience all around diff --git a/docs/website/video-transcripts/qKc1hZyw360.json b/docs/website/video-transcripts/qKc1hZyw360.json new file mode 100644 index 0000000000..9cb7115ba7 --- /dev/null +++ b/docs/website/video-transcripts/qKc1hZyw360.json @@ -0,0 +1,8 @@ +{ + "line_count": 159, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 941, + "youtube_id": "qKc1hZyw360" +} diff --git a/docs/website/video-transcripts/qKc1hZyw360.txt b/docs/website/video-transcripts/qKc1hZyw360.txt new file mode 100644 index 0000000000..57790226b6 --- /dev/null +++ b/docs/website/video-transcripts/qKc1hZyw360.txt @@ -0,0 +1,159 @@ +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/video-transcripts/qUJ4FwZMEQY.json b/docs/website/video-transcripts/qUJ4FwZMEQY.json new file mode 100644 index 0000000000..0fcd2b80cf --- /dev/null +++ b/docs/website/video-transcripts/qUJ4FwZMEQY.json @@ -0,0 +1,8 @@ +{ + "line_count": 150, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 758, + "youtube_id": "qUJ4FwZMEQY" +} diff --git a/docs/website/video-transcripts/qUJ4FwZMEQY.txt b/docs/website/video-transcripts/qUJ4FwZMEQY.txt new file mode 100644 index 0000000000..4e46ca902c --- /dev/null +++ b/docs/website/video-transcripts/qUJ4FwZMEQY.txt @@ -0,0 +1,150 @@ +in this part we will go over the +remaining big subjects and animations +we start with style animation which we +discussed before +we used that to animate the title bar +but we can do pretty much anything a +style can do +although +some things work better than others +for instance animating a color works +really well +but animating alignment will not +as it only has +three hard states +most of the other animation types have +simple methods to implement them +such as animate layout +but the style animation is only +accessible via the animation manager +class +the reason for this is that it's a newer +animation type the older animations +predated the animation manager and were +adapted to work with it +the style animation was created with the +animation manager and so it relies on it +this code animates a label uiid to a +text field uiid +you will notice we created a generic +component animation class +and use animation manager to set it +these component animations are pretty +powerful +you can sequence the and run them in +parallel +you can do quite a lot of powerful +things with them +the animation manager is a late addition +to codename one it came after we already +had layout animations and a lot of +infrastructure in place so we had to +upload everything and stick it below +this created interesting situations and +made the api awkward at some points +the main reason we had to do that is +orchestration +the animation manager takes care of all +the animations within the form +from one point in the code +before we had this it was really +difficult to run an animation without +collision +so if an animation was running and a +user triggered a button that removed an +unrelated component +the animation would crash +because the number of components was +suddenly different +so the animation manager came along and +it handles animations but also handles +serialization of operations +that means +that if you try to remove or add a +component while an animation is in +progress +this operation will be postponed until +the animation completes +you can also tie together component +animation objects and effectively create +elaborate animations +i would discourage that for most cases +as it can lead to overly complex +animations +that serve +form instead of function +and finally i'll cover +the exact opposite +of these high-level animations +the low-level animation infrastructure +animations ui timers and many other +features in codename one +rely on the animate method of component +which is core to codename one but so few +people understand it +when the edt runs a cycle +we call it a tick +and the edt aims +for up to 60 ticks per second +although the speed varies +with each tick +the +form is tossed with handling animations +and it invokes the animate method +if the animate method returns true the +animation will be painted +notice that animation in this case +refers to the animation interface but +all components implement that so you can +use the component instead +now +this would be very costly if we had to +go over every component 60 times per +second +so only components that use register +animated will receive the animate call +the thing is that while there are +registered animations the edt cannot +truly sleep +but that's probably okay +the real problem is when animate methods +return true too often or perform long +complex operations +this can become a serious battery drain +that will slowly seep power from the +device +so it's very important to deregister +unnecessary animations +we try to do it with our components +but my point here is that this is pretty +low level stuff +and you should be very careful when +dealing with it as the performance +implications can be significant +all our components use this feature +text fields use it to blink their +cursors url images use animations to +update the image +when we receive an up-to-date image from +the server +it's pretty powerful +the clock demo in the kitchen sync uses +it too let's have a look +notice we register animated and +deregister it based on the need +in the initialize and the initialize +callbacks +this triggers a call to animate method +notice we only refresh the clock once a +second this is important otherwise +excessive repaints could drain the +battery +and finally we draw the animation in the +background +callback +notice that this is pretty low level +these types of animations are designed +for graphics +not for components +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/qaYExTzcCVY.json b/docs/website/video-transcripts/qaYExTzcCVY.json new file mode 100644 index 0000000000..2ac522eff9 --- /dev/null +++ b/docs/website/video-transcripts/qaYExTzcCVY.json @@ -0,0 +1,8 @@ +{ + "line_count": 128, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 739, + "youtube_id": "qaYExTzcCVY" +} diff --git a/docs/website/video-transcripts/qaYExTzcCVY.txt b/docs/website/video-transcripts/qaYExTzcCVY.txt new file mode 100644 index 0000000000..d805183a68 --- /dev/null +++ b/docs/website/video-transcripts/qaYExTzcCVY.txt @@ -0,0 +1,128 @@ +in this part we'll cover the server side +of the push +you might recall the async class from a +previous review of a server code +here it is more fleshed out and we'll +also send out a push when the build is +true +Variables +we'll start by reviewing these variables +we have two IRS certificate URLs and +passwords for these two URLs we also +have a flag indicating whether this is a +production or debug push when we send a +push to iOS we need a special +certificate to authenticate us to Apple +it's not the signing certificate it's a +special Cloud certificate +Apple has two separate server +deployments +one you use while developing and one +during production they don't mix +each has its own certificate +an annoying bit is that if you use a +phone during development it will +sometimes take up for a day for it to +start working for the production +deployment +notice that the production deployment +will only work once you install through +the App Store and doesn't work with +debug builds +Push Token +the push token is taken from the +codename One account settings in the +build server and is used to authenticate +with the codename one server +the GCM server key is the other value +from the initial Android registration +process we need it for the Android push +Rest Builder +we initialized these fields from a file +in the user home directory called rest +Builder the reason for this is both easy +maintenance but mostly convenience so I +won't have to include my keys and the +source code and constantly remember to +remove them I can just set the values in +a properties file and forget about this +Build App +I'll take a short detour through the +build app method you might recall where +a lot of customization is now happening +on the general generated application you +will notice we generate the right +package name and Main class we also set +the right restaurant ID +Etc so the generated app will work as +expected +CSS +a bit lower we can see the CSS generated +by the style objects we configured in +the client this will allow customizing +the look of the final restaurant app +Android Target +further down the source Target hasn't +changed much but you will notice the +Android Target is now implemented +it sets the automated flag to True which +allows sending a blocking build which +returns a result synchronously +this is an Enterprise feature and it's +useful for continuous integration or +cases such as this where you want the +build servers to do a build for you and +return a result +I execute the r and command using the +process Builder and wait for it to +complete +scrolling a bit down we can see the loop +over the result zip from the synchronous +ant build +where we look for the result APK and +extract it to send the result to the +client +but that's not what I'm looking at +Push Notification +if we have an error we invoke the method +send push notification with the push key +if we have it +Send Push Notification +going further down we can see +push being sent and here are the +arguments are far clearer for the +success +the app build completed successfully +string is pretty clear but we also have +a semicolon followed by a URL and number +three +if you recall that means a Type 3 push +with a string message and a result URL +so it's effectively two separate +messages the first visible the second +hidden +send push notification is actually +really simple +since we do it from the server we need +to post to the codename one per servers +at +https push dot codename1.com slash push +slash push +with a post method +we need to provide all the arguments in +the post body from token to the device +key and certificates +the push server returns a response which +we should should log for our records in +case of failure but we should normally +keep track of in case of key a key that +expires +in this particular case this isn't +likely since the key is sent with the +request so it should always be 100 valid +there are some additional details but +the basic gist of sending a push message +response for this case should be +reasonably clear with this code +thanks for watching +I hope you found this informative diff --git a/docs/website/video-transcripts/r_ti2QAhm9s.json b/docs/website/video-transcripts/r_ti2QAhm9s.json new file mode 100644 index 0000000000..e84fdb0774 --- /dev/null +++ b/docs/website/video-transcripts/r_ti2QAhm9s.json @@ -0,0 +1,8 @@ +{ + "line_count": 134, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 762, + "youtube_id": "r_ti2QAhm9s" +} diff --git a/docs/website/video-transcripts/r_ti2QAhm9s.txt b/docs/website/video-transcripts/r_ti2QAhm9s.txt new file mode 100644 index 0000000000..d8c36eadf6 --- /dev/null +++ b/docs/website/video-transcripts/r_ti2QAhm9s.txt @@ -0,0 +1,134 @@ +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/video-transcripts/rjIQqfrFvbI.json b/docs/website/video-transcripts/rjIQqfrFvbI.json new file mode 100644 index 0000000000..3d4c5719ca --- /dev/null +++ b/docs/website/video-transcripts/rjIQqfrFvbI.json @@ -0,0 +1,8 @@ +{ + "line_count": 106, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 625, + "youtube_id": "rjIQqfrFvbI" +} diff --git a/docs/website/video-transcripts/rjIQqfrFvbI.txt b/docs/website/video-transcripts/rjIQqfrFvbI.txt new file mode 100644 index 0000000000..b07201d452 --- /dev/null +++ b/docs/website/video-transcripts/rjIQqfrFvbI.txt @@ -0,0 +1,106 @@ +the last piece of the server code is the +web service layer which again is again +relatively boilerplate driven +the web services mostly invoke the +service layer without much imagination +that's a good thing in engineering terms +boring is good +but it doesn't make a thrilling story so +please bear with me as this will pick up +pace a bit when we connect to the client +side +we have three web service classes user +web service media web service and post +web service +just like the user service this class is +pretty big +and also includes some additional +capabilities from the notification +service despite that most of the classes +boilerplate there is still a lot of it +so i'll try to keep it manageable by +splitting it down a bit +the web service is located at the slash +user base which means the ur any url +would be below that +we map two services in this single +service +since notifications has just one method +it made sense bringing it here +the exception handler grabs one of the +three exception types that can be thrown +in one of the methods and in this class +and convert them to an error response +object +notice that the error response is the +error dao which generates json with the +error message this is the error da class +the error dial object is just a simple +dao that includes the error message and +an optional error code +the user login url +returns the response as json in the body +and accepts a json request in the body +of the post +the exception will be converted to an +error dial if it's thrown +the use the slash user slash refresh +method +is a get operation +notice that it doesn't accept an +argument it uses an http header named +auth for the +authorization token +notice i used the auth header almost +everywhere +as it's a standard part of the request +you will notice the code is very similar +simple it's declarative code that +defines how the web service will look +before delegating to the underlying +service class +next we have the operations that map an +avatar this means the urls user slash +avatar slash user id would return the +avatar jpeg directly and would work even +if the browser with the browser so we +can share this url +response entity lets us control the http +response including the mime type and +media content +in this case we send a 404 response when +the user doesn't have an avatar +i use the get method to make a change in +the set avatar request +this simplifies the code as it already +requires a separate operation to upload +the media object +the friend request methods are trivial +and nearly identical they both accept +the same arguments and pass them on to +the underlying web service call service +call +i use a get operation because i don't +want to add a dial and get into the +complexities of post for this case +normally a get operation is a bad idea +for an operation that triggers a change +i wouldn't do this on a on the web since +it can lead to accidental resubmission +of data +however since my client here is an app +the use of get operation simplifies some +of the syntax +finally the last piece of the user web +service code is the notification and +contacts support +this method allows us to page through +the list of notifications fetching a new +page dynamically as needed +we can submit a list of contacts for the +shadow user through this api notice that +a list from json will implicitly +translate to the given contact list +with that we can finish the relatively +simple +user web service diff --git a/docs/website/video-transcripts/sBAiPOnjX6o.json b/docs/website/video-transcripts/sBAiPOnjX6o.json new file mode 100644 index 0000000000..1c368907aa --- /dev/null +++ b/docs/website/video-transcripts/sBAiPOnjX6o.json @@ -0,0 +1,8 @@ +{ + "line_count": 190, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1031, + "youtube_id": "sBAiPOnjX6o" +} diff --git a/docs/website/video-transcripts/sBAiPOnjX6o.txt b/docs/website/video-transcripts/sBAiPOnjX6o.txt new file mode 100644 index 0000000000..09db5d860d --- /dev/null +++ b/docs/website/video-transcripts/sBAiPOnjX6o.txt @@ -0,0 +1,190 @@ +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/video-transcripts/sH5GY816sRc.json b/docs/website/video-transcripts/sH5GY816sRc.json new file mode 100644 index 0000000000..70141aba0f --- /dev/null +++ b/docs/website/video-transcripts/sH5GY816sRc.json @@ -0,0 +1,8 @@ +{ + "line_count": 205, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1060, + "youtube_id": "sH5GY816sRc" +} diff --git a/docs/website/video-transcripts/sH5GY816sRc.txt b/docs/website/video-transcripts/sH5GY816sRc.txt new file mode 100644 index 0000000000..9187e820c3 --- /dev/null +++ b/docs/website/video-transcripts/sH5GY816sRc.txt @@ -0,0 +1,205 @@ +now comes the world of pain +ssl support +this is actually not too horrible +usually +but i chose to go with the +free let's encrypt option +that might have been a mistake in +retrospect +in the ide on our development machine +let's open +application.properties and set +several important +variables notice that i will need to +comment out these values when running +locally since i can't get ssl when +running locally +the server must use ssl for everything +as ios won't allow regular http +connections +to do this i'll define the server port +to +8443 +and require ssl +the next few lines point at the keystore +file +password and alias +we'll configure all of these values soon +i'll need to redeploy the updated server +with ssl setting +by copying it to the server +and obviously i'll need to do this line +again +to +to move the uploaded file to replace the +existing backend +server +first we need to set up dns for the +server +assuming you already registered a domain +setting the a record to point at the ip +address of your server gives it a name +you can't apply https to an ip address +and it's not a good idea in the long run +as it might pose a problem with scaling +or migrating +let's +for a second stop and explain how https +works +the server sends a certificate which +verifies the identity of the server +for example +this is build.magimob.com +and to prove that certificate is signed +by a signing authority +this signing authority is a company we +trust to verify that identity +in the past signing authorities +literally looked you up through duns +numbers +which is a horrible thing i don't want +to go into right now +that way they could verify your identity +let's encrypt was formed to allow +everyone to get a valid ssl certificate +for free +it does that by automating the process +completely +and the trick is composed of two pieces +the first is an application you run on +your server to verify that this is the +right server +the second trick +is +the short lifetime of a certificate +only 90 days +even if an exploit exists +it will expire in 90 days naturally +normally certificates last a year or two +and it's really painful to replace them +the 90 day time is horribly short +and the reason it's so short is so we +would be forced to automate the process +completely +that means our server should request a +new certificate on its own without any +action from us +that's actually pretty cool +it would mean the certificate will +always be renewed +and +current without any work from us +once we do the actual work +one caveat for java developers is +this +in order to recognize a certificate +authority +the root certificate should be embedded +into browsers and the jdk itself +most devices browsers etc already +recognize let's encrypt +but +the jdk only added it in update 8 u101 +101 +which is pretty late in the game +that might mean you will need to update +your jdk to access the secure site +so let's get this off the bat +i really regretted picking let's encrypt +as i walked through the process it sucks +on spring boot +the automation doesn't work out for the +box and i needed to do so many so much +wizardry +it's amazing +i don't understand why the guys at +spring or tomcat didn't implement +built-in support for this out of the box +i hope they will add it in an update +but i haven't seen anything so far i +seriously hope the rest of this module +becomes out of date +we start by installing some packages +required by let's encrypt +this is pretty much +the instructions you can find online to +do that this isn't a smooth experience +and part because of the newness of let's +encrypt +the first thing we install isn't so much +a thing as it is a third-party +repository to look in for additional +packages +next we install some utilities that +allow us to use the config manager in +the next step +i +honestly have no idea why the hell this +is needed +but it's part of the setup instructions +so i went along with it +the search bot is a command line tool +that allows us to renew certificates +automatically +certificate robot +got it +we need to stop the server if it's +running +the reason for this is clear when we +understand how let's encrypt works +it can integrate with some servers +but not with spring boot +so the alternative is to let it spin its +own server on port 443 which we would +normally need so we need to shut down +the server while we renew the +certificate +now that the server is off +we can launch the cert bot command which +fetches a certificate from let's encrypt +service automatically +the command includes several arguments +so let's review the important ones +dash dash standalone means we'll be +using the standalone server and can't +use integrations +to one of the existing supported servers +dash d +passes the domain name that we want the +certificate for +which needs to be the same one +associated with the dns from before +the next two arguments represent the +actual ports used for the servers notice +that port 8 +and 443 will be used internally but +since we redirect those to different +local ports with the iptable settings +we need to indicate the real local ports +to use +running this one command should generate +the certificate +but it doesn't end there +this is another big obtuse command let's +encrypt generates a certificate and +saves it using a pem format +which we can't use from java +we can use this openssl command to +convert the pem file saved in the slash +edc slash let's encrypt directory +to the keystore format we need +we ran that command as root so i'm using +the change own or shown command to +change the ownership of the file +from root to builder this is generally +good practice for files within the user +directory +it would have probably been been a +better idea to just run the open ssl +command +as builder +now that this is done i can relaunch the +backend server and https should work as +expected +but there is still one +thing missing diff --git a/docs/website/video-transcripts/sK-u1TBWFX8.json b/docs/website/video-transcripts/sK-u1TBWFX8.json new file mode 100644 index 0000000000..60e99911bd --- /dev/null +++ b/docs/website/video-transcripts/sK-u1TBWFX8.json @@ -0,0 +1,9 @@ +{ + "line_count": 27, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage.md", + "status": "transcript-fetched", + "word_count": 1319, + "youtube_id": "sK-u1TBWFX8" +} diff --git a/docs/website/video-transcripts/sK-u1TBWFX8.txt b/docs/website/video-transcripts/sK-u1TBWFX8.txt new file mode 100644 index 0000000000..8feeae4ef6 --- /dev/null +++ b/docs/website/video-transcripts/sK-u1TBWFX8.txt @@ -0,0 +1,46 @@ +_Transcript source: embedded._ + +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?" + +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! + +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 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. + +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. + +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. diff --git a/docs/website/video-transcripts/sUhpCwd0YJg.json b/docs/website/video-transcripts/sUhpCwd0YJg.json new file mode 100644 index 0000000000..73c8331cc8 --- /dev/null +++ b/docs/website/video-transcripts/sUhpCwd0YJg.json @@ -0,0 +1,8 @@ +{ + "line_count": 11, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 439, + "youtube_id": "sUhpCwd0YJg" +} diff --git a/docs/website/video-transcripts/sUhpCwd0YJg.txt b/docs/website/video-transcripts/sUhpCwd0YJg.txt new file mode 100644 index 0000000000..eee27a8272 --- /dev/null +++ b/docs/website/video-transcripts/sUhpCwd0YJg.txt @@ -0,0 +1,11 @@ +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. +WebService Wizard +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. +WebService servlet +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. diff --git a/docs/website/video-transcripts/sed5OPQSfe0.json b/docs/website/video-transcripts/sed5OPQSfe0.json new file mode 100644 index 0000000000..cf876e6006 --- /dev/null +++ b/docs/website/video-transcripts/sed5OPQSfe0.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 165, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/032-markers-lightweight-overlays-and-map-layout.md", + "status": "transcript-fetched", + "word_count": 815, + "youtube_id": "sed5OPQSfe0" +} diff --git a/docs/website/video-transcripts/sed5OPQSfe0.txt b/docs/website/video-transcripts/sed5OPQSfe0.txt new file mode 100644 index 0000000000..8ed73d31b8 --- /dev/null +++ b/docs/website/video-transcripts/sed5OPQSfe0.txt @@ -0,0 +1,165 @@ +we can spend a lot of time talking about +maps +since there are just so many features in +the map api +but the most basic one is markers +we can add a marker to a map using this +api +notice that we can pass an encoded image +that will be used instead of the default +image +in this case i chose to pass null +the resulting map looks like this +pretty familiar +but a bit limited +we don't have a lot of control over the +marker +a marker is great +but sometimes we want more +why not place a component on the map in +the location that we want +this used to be a problem historically +all native components +commonly referred to as pair or +heavyweight components +we're always on top +we solve this problem by going through a +lot of hoops and it's now possible to +place +a component on top of the map +however it's still not trivial +how do we know where to place the +component +we could use gloss paint to draw on top +of the map +but that's a bit limiting and requires a +lot of low level +code +we need a simple layout manager that +allows us to place components based on +their longitude and latitude values +it sounds hard to create a layout +manager but it really isn't +in this layout manager i make some +assumptions such as +an assumption that the layout manager is +placed exactly on top of a map container +within a layered layout +this means that when we position a +component it will be exactly above the +map +[Music] +the first step in implementing the +layout manager is extending the layout +class +notice i also implement the map listener +interface +so the layout can update positions when +the map changes +in the constructor i break encapsulation +a bit +by binding the map listener +this isn't ideal code but it can work +for our current purposes +in the future +i hope to create a more generic version +of this class in the map cn1 lib +even when we do that this code would +still be useful to explain how things +like this work +the map layout +is a constraint based layout +similar to border layout +that means +you need to add a component to the map +layout with a constraint +in this case a coordinate object +notice +i do a cast +so if someone adds a component with a +different object type as a constraint +you will get a class cast +exception +this is the entire layout manager class +notice it's only two pages +in large font +the area surrounded in red is the entire +layout +code +it's trivial +first we iterate over the components +within the parent container +we get the current coordinate +that we added +with the constraint +we can then +use the map api to convert the +coordinate to a point in the screen +normally we shouldn't call set size set +x or set y +but a layout manager is a special case +it's the one place where we are supposed +to set +the component position and size +we do several things here +we give all components their natural +preferred size +some layout managers do that others +don't +in our case using the preferred size +makes a lot of sense +the next thing we do is set the x and y +based on the screen point we got from +the map +notice we position the coordinate +in the center below the component which +turns out nicely for the component type +i tried +but it might make sense to make this +more configurable +and that's it +this is effectively the entire layout +manager logic +most layout managers should define a +preferred size +but since we will always reside on top +of a map +we can rely on its preferred size to +define +the amount of space +required +this is the event callback for map +modification +every time the map is modified we +revalidate the parent and trigger +repositioning of the components +this breaks the separation between +layout manager and the parent component +so this isn't necessarily a best +practice in terms of design +but for the purposes of a simple +tutorial +it works +adapting our previous code i can just +create a button to represent moscone +instead of a marker +notice i wrap the map container +with a marker container +markers container that uses the map +layout +i can now do whatever i want with the +button +when i add it i need to pass the +coordinate object of moscone +for this thing to work +and +this is the result +it doesn't look much different on the +surface +but for us this is a huge difference +instead of markers i can just place +anything i want +anywhere on top of the map +that's huge +thanks for watching +i hope you found this informative diff --git a/docs/website/video-transcripts/set12jVQl_A.json b/docs/website/video-transcripts/set12jVQl_A.json new file mode 100644 index 0000000000..2024896d95 --- /dev/null +++ b/docs/website/video-transcripts/set12jVQl_A.json @@ -0,0 +1,8 @@ +{ + "line_count": 335, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 2203, + "youtube_id": "set12jVQl_A" +} diff --git a/docs/website/video-transcripts/set12jVQl_A.txt b/docs/website/video-transcripts/set12jVQl_A.txt new file mode 100644 index 0000000000..aaa050bcf6 --- /dev/null +++ b/docs/website/video-transcripts/set12jVQl_A.txt @@ -0,0 +1,335 @@ +Hello guys, welcome to another +installment of how do I and today I want +to talk to you about something that is +immensely important to all of us +performance. Now this is a big subject +so I'll try to cover it as much as I can +but still be relatively brief. So go +into the if you don't experience good +performance with code name one then +please talk to us in the forums check +the developer guide and uh go over other +things. I'll provide as many tips as I +can but obviously the platform is huge +and contains a lot of hidden information +in it and things sometimes don't act as +you would expect. So let's go over the +Lists +basics here of what +uh the pitfalls people usually run into +in +performance. So let's start with lists. +If you used a list and you made made use +of your own custom list model or list +renderer, then there are several +pitfalls within these two tools. So to +explain that, let's look at the API +itself. The API for the list model is +here under list. And normally if you +don't actually create your own model, +then you shouldn't have a problem. The +only problem you would have is if you +have a list that's already showing on +the screen. +that if you have a list that's already +showing on the screen, you might have a +problem if you repeatedly call uh the +default list models add item entry or +remove item +entry. And the reason behind +this is that both of these methods are +rel uh they every time you add an item +or remove an item, the list model needs +to fire an event and notify the list. So +if you do that repeatedly, add lots of +items, remove lots of items all the +time, then the data change event is +fired to the parent list and you're +effectively causing lots of events. All +of them trigger repaints and are very +expensive and they're very hard to pull. +So to change a lot of data list, you're +better off replacing the entire model +than firing this event cons constantly. +So that's really important if you're +using the default list. If you implement +your own list, however, you should make +the methods like get and get item at be +very very very fast. If you can't make +them, if you connect to the internet or +do any other heavy lifting sort of +things, then what you should do is +return immediately with fake data or any +data something and then update them +later on. It's crucial because these +methods are called all the time and very +frequently and we rely on them being +cached locally. This is hugely important +and you should observe that when making +a network uh bound list or something +like that and using the list model +properly. The same holds true for the +renderer. A common mistake people make +is when they call the renderer +component, they return a new instance of +a component. Never ever do that. That +sort of uh blocks your performance +instantly, thrushes the garbage +collector and creates uh a lot of +garbage because this method is called a +lot. It's not called per the number of +the items because we have animations +within the list and things like that. It +can be called repeatedly o repeatedly +over and over and over and over again. +So take that into consideration. +whenever you're building any form of uh +interface on top of the list +component. So that those two things are +important and I urge you to pay +attention to them. +Uh the next issue is if you're doing any +sort of drawing of strings or things +like that, the string width method +uh which is supported by fonts and +allows you to essentially measure the +width of a string is really really +really slow on some platforms, not on +all of them. And that's a feature of the +platform because uh actually measuring +the width of a string is much more +complicated than it might seem. you need +to go through the glyphing process and +everything. So, uh that's one of the +reasons why our label implementation +isn't multi-line by default +automatically +uh and why text area is much slower than +label. So if you want to build something +that's efficient, I suggest you use +labels and not try to have line breaks +automatically because you would get far +worse performance +uh because of the repeated calls to +string width. Now we have internal +optimizations for that that sort of make +lots of assumptions and try to uh reduce +the cost of string width. So it's not a +log in of uh of the cost because you +essentially need to measure every +characters with it's I I +digress. But still automatic line +breaking is far more expensive than you +deciding in advance. Okay, I want to +break a line here. So don't thrush +string with w with you'd be surprised +how how much of the performance costs we +pay in codeame one is related to that +particular method that's uh that's +difficult to optimize +uh bitmap fonts are deprecated in +codeame one we recommend you use true +type fonts for everything they perform +as fast as any font but unfortunately +they're unsupported in the low-end +devices ES uh in J2ME and stuff like +that. So we are aware of some people +still choosing to use bitmap fonts +despite their many disadvantages. One of +which is their performance. +Surprisingly, they actually work faster +than uh system fonts in some +cases, but uh they work slower on other +platforms and it's it's a matter of uh +uh it's really difficult to assess their +performance on real world devices. So, +we recommend people don't use them uh at +all. They're more expensive in terms of +memory. They're they're not +internationalizable. +they have many problems. So, we just +don't recommend people use them at all +and we have deprecated +them. Uh, small images doesn't seem like +such a performance issue and it isn't. +If you're drawing one small image, then +no big deal. It doesn't cost you almost +anything to draw an image small or +large. The problem is when you draw +small images and you draw lots of small +images. So for instance using a one +pixel image and tiling it in the +background that's really really really +slow and the reason for that is how the +GPU works. Essentially every time you +draw to the screen you need to go back +and forth between the CPU and uh the +graphic processing unit and going back +and forth there is really expensive for +doing lots of small operations. you +you're better off uh doing larger +operations. So in that same coin, it is +much faster to draw an image than draw +lots of lines or draw lots of graphic +primitives. So you're better off just +drawing a bit map image that's as large +as reasonably possible. Keep in mind +that larger images might take more +memory and might not look good on +smaller device. And my next point about +uh scaling is also very important. So +anyway, small images are generally +problematic if you draw if you choose to +make lots of small images uh small image +drawings. That's that's a big cost. +That's why when we cut borders, we +recommend you cut relatively larger im +portions of the border for the nine +piece borders. See my tutorial on that. +It's just much much faster. And we find +that this is a great piece of uh cost in +ter terms of performance for some of our +users. Now image +scale is an issue uh where images uh if +you invoke the method image scale scaled +sorry I missed that here. It should be +scaled really not image scale. Uh that +method creates a new image that is +essentially scaled to the given +resolution. The thing is that this +method while it works it +produces not the highest quality image +because it doesn't do any interpolation +or anything it cost you in terms of +performance still because we do all of +this in Java code and but it's although +it's relatively fast the problem is uh +twofold the first there's a problem in +memory here uh once you invoke image +scaled you effectively uh get an image +that is much larger in terms of RAM. Uh +the default images you get in code name +one and you I suggest you read the image +section in the developer guide. It's +it's really complex but interesting I +think +fascinating. Uh the image section in uh +and that discusses it. Images don't +actually take their full size. they take +the size of uh the compressed image, the +PNG or the JPEG that you chose, which +isn't much. However, when you invoke +scale, the image will automatically take +up uh the width, the scale width you've +selected times the height* 4 and +possibly times two again for iPhone and +other +platforms. So, that is a pretty huge +size. So for instance, if you want to +set a background image for an iPad 3, +which +is48x536, then you'd have to do +* +uh 1536 * 4 * 2 because we keep the +image twice both in RAM and and on GPU +on the OpenGL uh section. So don't use +that method whenever possible. The +substitute is to use uh the draw +version. The if we will go into the +graphics section here +uh then you have a method which isn't +ideal. Normally you'd uh the best +approach when working with icons and +things like that is to use a method like +uh +uh when working with graphics like that +is to use a method uh uh like a +multi-image or something like that when +working with icons. But if you need +something like scaling, we suggest use a +method like this which is automatically +used when uh when we set a scaled +background or things like that. This +doesn't have the cost of uh uh the RAM +overhead and it's better in general +because it's usually handled by the GPU +on devices like J2ME. Unfortunately, it +isn't supported. So this works only with +uh the smarter devices out +there. Uh the two last things uh so the +Issues +problem is in our developer guide that +this is not always uh immediately +obvious that the roundrect issue is u +problematic. Uh the roundrect issue is +related to um the fact that when we draw +round rect that is really simp easy and +simple just drawing a roundre wctck. The +problem is when we combine it with a +background image and +transparency and that's where things +start to +fail because there is uh we don't have +roundrect clipping or the ability to +construct things in such a way. So the +performance of this is remarkably +remarkably slow and the memory overhead +is use uh nine piece images. Don't use +this. This is really really really +really really really +really +slow. Uh we have it there mostly for +historical reasons. Uh we'll probably +deprecate it because it is so slow. Uh I +would strongly recommend you don't use +it and use instead uh ninepiece images. +So don't use the roundrect border uh +unless you you're doing something really +trivial and even then you're better off +using a npiece image which is properly +multi anti-aliased and etc. So don't use +that uh at least not in the current +version of code name +one. Uh last but not not least is uh +translucency. Translucency is really +fast on modern devices. You shouldn't +worry about that. it should work okay. +Obviously, if you do lots of layers of +translucency, you will pay for that. So, +take that into consideration. However, +if you're targeting older devices, +specifically Symbian, Symbian sucks in +terms of translucency. This is a +well-known issue with uh the Symbian +devices. So, just don't use translucent +images or any form of +translucency if you want good +performance on a Symbian device. they +are just really slow with it and there +isn't much we can do about that. That's +uh a known issue with that device family +and the problem is that some of our +themes use translucency quite heavily. +So they won't perform as well on that on +those particular +devices. Uh so you might want to +optimize uh those themes. Now to track +performance issues is relatively simple. +just create a blank theme or a theme +without that much information in it and +try to reproduce the issue there. This +often helps you in finding whether the +performance is related to a theme +element or to something within your +application and work through a process +of elimination uh trying to get uh an +insight into which portion is causing uh +the performance effect that you are +seeing. Obviously, this isn't always a +trivial case because without the theme, +it is often harder to to truly perceive +performance. But uh but sometimes that +helps. So, thank you again for watching +this tutorial and I hope you've enjoyed +it and uh uh learned something from it. +Thanks. diff --git a/docs/website/video-transcripts/swgT_aDsv3U.json b/docs/website/video-transcripts/swgT_aDsv3U.json new file mode 100644 index 0000000000..dd4f9997f6 --- /dev/null +++ b/docs/website/video-transcripts/swgT_aDsv3U.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 164, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/019-the-native-code.md", + "status": "transcript-fetched", + "word_count": 947, + "youtube_id": "swgT_aDsv3U" +} diff --git a/docs/website/video-transcripts/swgT_aDsv3U.txt b/docs/website/video-transcripts/swgT_aDsv3U.txt new file mode 100644 index 0000000000..a6ea3ba140 --- /dev/null +++ b/docs/website/video-transcripts/swgT_aDsv3U.txt @@ -0,0 +1,164 @@ +the implementation code for android and +ios is pretty trivial and is almost +identical to the code we already saw +from braintree +the best way to implement a native +interface is to send a build with the +include source option +we can then open the source code in +xcode or android studio and implement +the native interface there +then we can copy the native interface +code back into codename one +project and continue working like before +i don't always do that with android +since i find android studio to be slow +and painful to work with but if the +native interface is complicated it's +sometimes better than the alternative +another important point to notice is the +different threads +when we call the native code we usually +do so from our edt which isn't the +native os thread +if the edt is blocked or waiting on the +native thread things can get ugly +so it's important to understand which +thread you are using and for what +purpose +this is the android implementation of +the native interface +let's +go over the various pieces +pretty much every sample code from any +third-party library will assume you +derived the activity class which is +pretty common +so most will just use this to indicate +your activity class in codename one you +will need to get the current activity +luckily this is pretty easy as we have a +class called android native util +which provides a few helper method for +android native code including get +activity +as i mentioned before this method is +invoked on the codename one thread and +so it might +cause an issue since android has its own +native thread where things should +execute +run on ui thread is android's equivalent +of the edt and this means the rest of +the code will run in the android native +thread +unless stated otherwise code is expected +to run on the native os thread so this +is a good move for most cases +this might beg the question +why not do it for everything +why aren't native interface calls +implicitly invoked on the native os +thread +the answer is that it's not always true +for for instance in i o methods +we wouldn't need the dispatch to it +it's also unclear if call is synchronous +or asynchronous and dispatching includes +an overhead +moving on we can see that +the start activity for result call +that we had in the original code +however in this case we invoke it on +android native util +since we don't derive from activity +we also pass activity as an argument +start activity for result is a special +method that triggers callbacks +calls within the activity +to allow this common use case the +android native util version includes an +intent result listener class that has +passed to that method +and receives callback invocations +the callback from the braintree code can +have several results +such as ok error or cancel +just like in the ios version +here we just invoke the static callback +method on result and pass the data +the ios version of the code looks a bit +like a mess +the main reason is the long method names +that are part of our vm +they might look intimidating at first +but as we go over the code you will see +that this isn't as hard as it looks +the dispatch async call is identical to +call serially or +run on ui thread it executes the lambda +block on the native ios event dispatch +thread +the next two lines are practically +identical to the braintree code +we'll discuss the lambda callback soon +but first let's look at the present view +controller you will recall this was +invoked on self and the original code +which was the view controller in +codename one we have a global view +controller which you can use with that +syntax instead of using +self for most such code +once we get into the callback code the +logic is a bit different +it looks a bit long but you will notice +that a method invocation is just a +really long c function call +it starts with a package name +followed by the class name followed by +the argument types and possibly by the +return type +all of which are required to make a +method unique and see +the arguments we pass +start with a constant that allows us to +pass additional arguments +there are several argument types like +this i suggest checking out the +developer guide to fully understand this +argument but it generally represents the +thread context +the next argument is the string +for which we use the special from ns +string method to generate a javalang +string +since the method is static +we don't need to pass an object context +or deal with the v table +this makes the call far simpler +a lot of developers shy away from native +interfaces which is a shame +they are slightly challenging but they +are +surmountable challenges +some things are cryptic but as you saw +in the example we practically copied and +pasted the native code from braintree +with very few changes and this was a +relatively complex api to support +a huge pain is in the error logs on both +platforms +android is actually regressed in this +sense and became far worse +i would suggest asking our team on the +forum or stack overflow when you are +stuck with such errors google is +hugely helpful in finding solution for +these sort of errors +working with the native ide to implement +the native interface stubs is a great +time saver especially if you are a +relative novice +thanks for watching i hope you found +this educational diff --git a/docs/website/video-transcripts/sxCADu-SjpQ.json b/docs/website/video-transcripts/sxCADu-SjpQ.json new file mode 100644 index 0000000000..e83548057c --- /dev/null +++ b/docs/website/video-transcripts/sxCADu-SjpQ.json @@ -0,0 +1,8 @@ +{ + "line_count": 111, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 652, + "youtube_id": "sxCADu-SjpQ" +} diff --git a/docs/website/video-transcripts/sxCADu-SjpQ.txt b/docs/website/video-transcripts/sxCADu-SjpQ.txt new file mode 100644 index 0000000000..a9f91cd5cc --- /dev/null +++ b/docs/website/video-transcripts/sxCADu-SjpQ.txt @@ -0,0 +1,111 @@ +we are finally ready to tie this into +the ui +of the client +the search ui +is in a dedicated form +it works as you type and lets you toggle +between people slash post search mode by +pressing the buttons on the top right +corner +let's dive into the code starting with +the fields and general types +this is set to true when we are in +people search mode +we use this flag to detect if the search +text actually changed +the timer waits to make sure the user +isn't still typing so we won't send too +many search queries at once +the time of the last search helps us +decide how long we should wait for the +upcoming search query so we don't send +too many queries +this is the search text field where the +user can type +search results add up +into the infinite container for paging +slash refresh capabilities +if search text is less than two +characters we shouldn't start searching +this is the standard page calculation +logic that we have in other infinite +container classes +we build results for people or posts in +the same way +there are two create entry methods that +cover the logic for both cases +the resulting components are returned on +null in the case of an empty set +this is pretty direct and should be +relatively obvious +the two big pieces that are obviously +missing from this code are the create +entry methods so let's get to them +the version that accepts the user object +just create a creates a multi button for +that +the same is true for the post entry +i'll get to the event handling code +later +the user form and post form mentioned +here don't exist yet so i'll discuss +them after finishing the search class +we use border layout so the infinite +container can fit the cen in the center +there is no other reason +the title ui id makes the search field +fit into the title area +in ios the title is centered by default +and that doesn't look good while editing +so we left a line explicitly for this +case +this is the search operation with every +change to the text field we can we call +the update search method which performs +the actual search +set title component places the component +in the title area instead of the label +that's there by default +this closes the search form by going to +the previous form +there are two buttons in the title area +that let us toggle people and post +search modes we invoke refresh when +there is a change which triggers a call +to fetch components in the infinite +container for the first page +when we show the form +the +search field will instantly launch into +editing mode +this is important with a virtual +keyboard so it won't just have focus it +will actually be in edit mode +this leads us to the update search +method which performs the actual search +logic +if the text doesn't change +we don't do anything +if we have a search pending we kill that +search request since this +all runs on the adt this is a search +that didn't start yet +if we already started the search we make +sure it wasn't too soon otherwise we'll +postpone the search for later +when the timer elapses we update the +search time and refresh +notice that since this is a ui timer +elapsing happens on the edt +if there is nothing pending we just do +the search right now and refresh the +infinite container +with this the search form should work +and show search results instantly +the one last piece of the puzzle +is plugging it into the ui which is +actually trivial +we add this line to the main form +constructor with that you can now click +the search button and it will actually +work with the data from the database diff --git a/docs/website/video-transcripts/tBBtpdz-JJg.json b/docs/website/video-transcripts/tBBtpdz-JJg.json new file mode 100644 index 0000000000..6d4b741a46 --- /dev/null +++ b/docs/website/video-transcripts/tBBtpdz-JJg.json @@ -0,0 +1,8 @@ +{ + "line_count": 113, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 687, + "youtube_id": "tBBtpdz-JJg" +} diff --git a/docs/website/video-transcripts/tBBtpdz-JJg.txt b/docs/website/video-transcripts/tBBtpdz-JJg.txt new file mode 100644 index 0000000000..2447e188a9 --- /dev/null +++ b/docs/website/video-transcripts/tBBtpdz-JJg.txt @@ -0,0 +1,113 @@ +up until now the app focused on a very +narrow use case +and the side menu was just for show +i won't fill up the entire app as the +for uber app is very wide and nuanced +but i would like to add a few essential +ui elements to show just how easy it is +and how much work we already did in the +server to prepare ourselves for this +the uber signup process prompts the user +for details which is a nuance we skipped +early on +i won't rectify it but i will map the +menu item for settings to show the +settings form +and i'll map a click on the user's +avatar to the edit account form +this means we need to add two new forms +settings form and edit account form +i didn't take the full ui of the +settings form and i didn't implement +everything +however the basic design is there and it +should provide a good framework for +additional forms +in this application +the settings form is encapsulated in +this code +this is the standard header for forms +with the black title +it initializes the ui and adds the +scroll down collapse effect +we defined this method before and it +hasn't changed +i changed get avatar to return an image +as a callback +this can happen if the image wasn't +downloaded yet from the server +the rest of the code is mostly ui mockup +meant to replicate some of the ui +elements in the uber app +most of these things aren't challenging +notice that i have new create entry +methods +in common code +i refactored them from the completion +container as is +i added a relatively trivial create +separator method which i reused later in +edit account form as well +for completeness this is the create +separator method from common code +there isn't much to discuss about that +method so let's move on to the next step +the next step is a review of the changes +to the get avatar method most of the +code is the same as it was before +all of this didn't change at all +we still need the mask object regardless +of the outcome as we will need to mask +an image avatar from the server to in +order to give it the route effect +the reason we need to do it +all of that in the client is +that only the client knows the size of +the image it needs +this will also give give the server +flexibility in the future to increase +image resolution slash size +the fetch avatar method might fetch the +avatar image from local storage or +download it from the internet +when it's done it will invoke the +callback method with the image object +the image was fetched from local storage +the avatar might already be there so we +don't need to do anything else +otherwise we can continue with the same +code we had before in the get avatar +method +now let's look at the fetch avatar +method in user service +which we used in that code +i made two variants of the method +one accepts the id of the user whose +avatar we are fetching +and the other one uses our id +right now i only use the one that +doesn't take the id however it should be +trivial to show the image of the driver +scheduled to pick us up next to his name +this is a web service i already mapped +in the server code a while back +the image is returned for the user with +the given id +in the path +a new user won't have an avatar and we +will get a +full response +which is 100 valid for this case +without this line +we would get a default error prompt +which we don't want +the built-in method in connection +requests +checks if an image already exists in +storage under the given name +and if so invokes the callback with that +name +otherwise it adds the request to the +queue downloads the image storage +file name loads it and invokes the +callback with that image object diff --git a/docs/website/video-transcripts/tWMVUDScyfQ.json b/docs/website/video-transcripts/tWMVUDScyfQ.json new file mode 100644 index 0000000000..b291f82cd9 --- /dev/null +++ b/docs/website/video-transcripts/tWMVUDScyfQ.json @@ -0,0 +1,8 @@ +{ + "line_count": 109, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 594, + "youtube_id": "tWMVUDScyfQ" +} diff --git a/docs/website/video-transcripts/tWMVUDScyfQ.txt b/docs/website/video-transcripts/tWMVUDScyfQ.txt new file mode 100644 index 0000000000..a686fc0ef7 --- /dev/null +++ b/docs/website/video-transcripts/tWMVUDScyfQ.txt @@ -0,0 +1,109 @@ +the next big step is making this into a +real app by implementing a lot of +the details such as comments search and +settings +each one of these seems like a huge task +but surprisingly they aren't as hard as +one would have expected +for search we need to leap back into the +server code to implement search +on the surface search seems like a +complex problem but thankfully spring +boot and hibernate magically solve that +for us +hibernate is the default jpa +implementation in spring boot +one of its features is integration with +lucine elasticsearch +which you can read about at the +hibernate.org +search url +springboot makes that even simpler +we can support search by making a small +change +to the palm.xml file +then we need to configure search in the +application.properties file as such +these just mean the search index is +saved into the file system which is the +simplest approach for implementing this +there is a lot of power behind this tool +and you can learn more about it in their +website +users can't search everything that would +be bad as we don't want the ability to +search through passwords etc +we can add search support by marking an +entity with the at indexed annotation +then marking each field that should be +indexed with the at field annotation +notice that we should use the +org.hibernate.search.annotations package +for these annotations as there is +another indexed class in spring boot +we only index the name of the user as we +don't necessarily want to expose private +data through search +we'll do a similar change to post which +contains fields worth indexing +we only index the text content as other +content might produce weird results in +search +i go further with searching comments etc +but for simplicity i stopped here +with that the entities should be +completely searchable +we now need to expose the search +functionality in the service layer +this warrants a brand new search service +clause +the entity manager provides direct +access to the underlying jpa +implementation +normally spring boot hides that but for +search we need direct access +this is a small helper method that +shortens the boilerplate syntax of f to +ft +instead of that code +we first need to build a search +search database this is a one-time +operation once the database is built +hibernate will keep the database up to +date +we have two types of searches so i +combine their common logic into this +method +the query builder searches a specific +entity +for instance post user etc once we have +it we can construct a complex search +query +the search for keywords is marked as +fuzzy which means it will find answers +that don't match complex completely for +the given text +we create the query and issue it +we give it a range of results so we can +page through the query query results +like we do with other pageable content +searching people or posts becomes a +matter of passing the entity type field +names and paging numbers +we then convert the entity result to a +dow object +as return types +post searches are exactly the same same +only with post objects instead of user +objects +that was a bit of code but the premise +is relatively simple and heavy lifting +is done by hibernate +the interesting part is the query +builder portion +you can customize the query builder with +any fields you wish to search and +complex constraints +related to the search but for the simple +implementation we have right now this +will do just fine diff --git a/docs/website/video-transcripts/tiY5ofnJop8.json b/docs/website/video-transcripts/tiY5ofnJop8.json new file mode 100644 index 0000000000..eca1ea4a71 --- /dev/null +++ b/docs/website/video-transcripts/tiY5ofnJop8.json @@ -0,0 +1,8 @@ +{ + "line_count": 120, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 629, + "youtube_id": "tiY5ofnJop8" +} diff --git a/docs/website/video-transcripts/tiY5ofnJop8.txt b/docs/website/video-transcripts/tiY5ofnJop8.txt new file mode 100644 index 0000000000..0c63be12ac --- /dev/null +++ b/docs/website/video-transcripts/tiY5ofnJop8.txt @@ -0,0 +1,120 @@ +the restaurant app maker +is an application that we will build +together based on the restaurant app i +introduced earlier +it generates a custom application +for a specific restaurant +unlike the restaurant app which is +pretty standard +and for which we could derive +inspiration from existing apps +this is a relatively fresh idea there +aren't many app makers out there +and the few that are in the market are +web-based +so we need to design something without +any psd +and still have something that looks +reasonably good +when when we are faced with a task like +this the solution is the construction +i break the problem into smaller +manageable problems and attack each one +individually +the best place to start is the user +interface even though we don't have a +design +it helps to visualize a problem before +we get to the implementation details +and ui is a great place to start +this is the first page of details that +the restaurant app will need within it +so we'll need to build an app that +accepts all of this information and some +of the information from the next page +notice that this is only a partial list +and some of the things here like color +scheme or fonts +can be pretty challenging +this is a mock-up design i created after +looking at several app maker sites +i understood most of them use a +tabbed-based ui so +that's where i started +i took the information that we need from +the list of items and placed it into the +app +the thing is +that this was a bad direction +once i drew that it became relatively +clear that this isn't what i want +i left it because i think it's useful +for you to see the path i took to reach +the end result +the other issue is the boring details +stuff +one of the most important things for any +app +is a first impression +it's crucial that the first screen of +the app +looks good by default and keeps users +engaged otherwise they might skip the +app entirely +opening with the details +makes sense for a developer but doesn't +make sense from a user interface +design standpoint +this is my second attempt +the elements on the left are the side +menu elements +the core design is inspired by material +design with the floating action button +and toolbar elements +dishes use the material design grid +design +and focus on the food image +this is important as pictures sell +a lot +of the more complex details have default +values +so +a +user can launch the app and instantly +see his custom app in action +with almost no work +to see the app you can just press the +play button which will preview the app +locally +a lot of the elements in the title are +editable +you can edit the title background using +the edit background +and this will impact the final app +both the title and tagline are text +fields +and can be edited in place +i considered making the dish editable in +place +but eventually decided to have a +separate form +to edit it +as we can go into a lot of details +for the new dish +edit dish form i drew inspiration from +material design again +i used a gradient so the title will be +readable on top of of the dish image and +use the material design floating action +button +that is placed on the border of the +title +the reason for that placement was so we +don't click delete dish +or the description +by accident +the camera button is meant as a generic +action to pick an image not necessarily +through a camera +i chose to enable delete through here +as it removes clutter from the main form diff --git a/docs/website/video-transcripts/tjgyigzxo5U.json b/docs/website/video-transcripts/tjgyigzxo5U.json new file mode 100644 index 0000000000..7ad43ab21b --- /dev/null +++ b/docs/website/video-transcripts/tjgyigzxo5U.json @@ -0,0 +1,8 @@ +{ + "line_count": 108, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 613, + "youtube_id": "tjgyigzxo5U" +} diff --git a/docs/website/video-transcripts/tjgyigzxo5U.txt b/docs/website/video-transcripts/tjgyigzxo5U.txt new file mode 100644 index 0000000000..35c00d2ea4 --- /dev/null +++ b/docs/website/video-transcripts/tjgyigzxo5U.txt @@ -0,0 +1,108 @@ +the next obvious entity would be post +a user posts to facebook so it makes +sense to have an entity for that +the post object on the server is again +very similar to the post object in the +mobile client +as they represent the same logical data +we still need the layer of abstraction +as the server might require additional +information that might not be applicable +to the client +a post is made by a user this allows us +to query and maintain relational +integrity on the posts +the date and title and content of the +post are pretty self-explanatory they +contain everything that's important +about the post +visibility represents +whether the post is public or for +friends only similarly to the media +object +styling maps directly to the client-side +object of the same name representing the +style of the post such as background +type etc +we need to add a comments entity as well +just like we have in the client side +notice that they are ordered based on +date within the relation +likes can't be counter a counter +we need to track the individual users +who liked +we need dao objects both for the post +and the comments of the post +as it was with the other entities +the rest of the code is just getters and +setters +i'll get to the dao object soon +but i'd like to first start with a +comment object +the user who made the comment isn't +necessarily the guy who made the post +the parent comment can be null +but can point at a parent +comment for nesting discussion +a comment has a date and text notice it +doesn't have a parent post as we don't +need to +we get the comments through the post so +we don't need the back reference +the constructor and dial are pretty +standard for this type of object +the rest of the code is getters and +setters +there are four additional blocks of +boilerplate we need to add to get this +to work +first we need the post dow +the fields are pretty similar to the +entity again with the dow object +objects replaced +the constructors are similar to the +other dows +the rest are getters and setters as +usual +comment dao is more of the same +there is one tiny difference here +instead of sending the comment dial +object we pass the primary key of the +parent if applicable +again the constructors are as usual +the getters and setters take up the rest +of the space +finally we need the crude repository +objects +for both entities i'll start with post +repository +this finder method finds a post by +specific user with a specific visibility +for instance public or friend +this finder method finds all the posts +by a user before invoking it we need to +make sure the user has permission to do +so +notice that the find query returns a +page object instead of a list and +accepts a pageable value +this is built in support in spring for +the process of paging +paging allows us to run a query and go +through the results one page at a time +we can request a specific page from the +result and specify the amount of items +we expect to receive +i'll go over all of these when dealing +with the service classes +notice i did this for elements where we +should expect a large volume of +responses +that should be sent to the end user +the last piece of information is the +comment repository which has nothing +interesting +with that +post and comment entities are done we +are left with one last entity that has a +client counterpart diff --git a/docs/website/video-transcripts/ts-Q1zBGXco.json b/docs/website/video-transcripts/ts-Q1zBGXco.json new file mode 100644 index 0000000000..cb236dc6b2 --- /dev/null +++ b/docs/website/video-transcripts/ts-Q1zBGXco.json @@ -0,0 +1,8 @@ +{ + "line_count": 86, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 424, + "youtube_id": "ts-Q1zBGXco" +} diff --git a/docs/website/video-transcripts/ts-Q1zBGXco.txt b/docs/website/video-transcripts/ts-Q1zBGXco.txt new file mode 100644 index 0000000000..f3fdc98d8f --- /dev/null +++ b/docs/website/video-transcripts/ts-Q1zBGXco.txt @@ -0,0 +1,86 @@ +in this final part we'll discuss the +checkout form +yes i said form +i know it looks like a dialogue but i +chose to implement it as a form and if +you look at it closely you will see why +the functionality of the checkout ui +is outside of the receipt section +which we would normally think of as the +dialog +both the x button and +the order button ours are outside of +that ui +hence this should appear as a form +to get this appearance +we grab a screenshot of the existing +form +by drawing it onto an image +we then blur and tint the screenshot +and set it as the background of our form +we also +use cover transition which provides that +cool ios style transition +for the checkout ui +the close button is just a button placed +in the north of the form +i considered using a toolbar and +eventually chose to just place +components in the north area +both +would have worked +out to +identical results +so there was no real reason to use +components +here i construct the title and close +button +it's mostly one long line of code +if we ignore the code required to +implement the x +the checkout button is placed in the +south +again +not a lot of functionality for that +the dish container code is a bit +simplistic +i extracted it to a separate method +that just constructs a border layout +entry with the image title and the price +you might recall the dialogue top uid +from css +and cutting tutorial +this is effectively the background of +the receipt +the same would be true for the dialog +bottom +which is also from css +i can go on a bit +but most of the functionality of the app +should be straightforward by now +one of the main things i hope you +learned from this module is the +attention to detail and compromises +required in building a mobile app +when building an app i always start with +the mockup +it lets me gain perspective of the +functionality and communicate with +non-technical people +about the effort +the original psd +looked trivial +but some tricks +like the even sized container for the +grid layout or even +the layout i chose +weren't as simple +other effects that might have seemed +hard like the receipt ui +were +much easier +just a nine piece border +thanks for finishing this module i hope +everything was clear to you and please +feel free to ask questions and make +comments so we can improve diff --git a/docs/website/video-transcripts/tugJ_7xMdzs.json b/docs/website/video-transcripts/tugJ_7xMdzs.json new file mode 100644 index 0000000000..9f7889f19a --- /dev/null +++ b/docs/website/video-transcripts/tugJ_7xMdzs.json @@ -0,0 +1,8 @@ +{ + "line_count": 331, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 5048, + "youtube_id": "tugJ_7xMdzs" +} diff --git a/docs/website/video-transcripts/tugJ_7xMdzs.txt b/docs/website/video-transcripts/tugJ_7xMdzs.txt new file mode 100644 index 0000000000..70f71166b5 --- /dev/null +++ b/docs/website/video-transcripts/tugJ_7xMdzs.txt @@ -0,0 +1,331 @@ +in this tutorial i'm going to talk about spring boot and introduce you and i don't expect +that you know java e or anything like that and or servlets or anything +really because the and that's part of the reason why i +chose spring boot i'm assuming most of you know servlets but i don't know that for sure +and it's uh teaching that if you don't know that is much much harder spring +boot is easy it's modern it's popular pretty much everyone is picking it up +and for good reason it's really really really easy to build it it's as easy as building a command line application and +practice frankly it is a command line application in the most basic sense +it's trivial to install and it integrates with all the web servers services the databases +everything that you basically need to build an app and it's java it's completely java in +even a more fundamental way than enterprise java which is a bit uh +overly cluttered and spring strips away a lot of these complexities +but still includes all the power and it's integrated with all the major +ides so uh i'm using netbeans because of force of habit mostly +uh and it's a good idea also it's a very good ide +and there's also intellij obviously an eclipse eclipse is actually supported officially by spring so +it's got its own dedicated ide for that and they all work really well it works with great old works with maven it's +a really really nice approach to building apps but i didn't answer exactly what so this is the official +explanation from the spring website you can pause and look at what it says in terms of the +text but the gist of it is really simple +it's it tries to mimic a lot of the ideas from other uh languages and frameworks by making +everything by convention so spring just creates the application because we all created the same +applications and we generally when we build an app with java e 90 of us just +go on stack overflow and copy and paste this huge block of code that's boilerplate that's pretty much what +everyone does because everyone does the same exact application over and over and over again +and they just copy all that boilerplate and start building an app spring essentially says +forget that you don't the spring boot it just says forget that we'll do the boilerplate for you because +you're doing the same we'll let you customize it if you don't need this or if you need that to work a +bit differently but generally you're doing the same you're just replacing the database you're blessing placing this address you're doing that +and it just gets you up and running with nothing practically +and it's instantly instantly includes all the services that you need +so that's really convenient so generally the output of spring boot is just one +jar one fat fat fat jar that includes tomcat or jetty or +whatever you chose inside of it literally inside of the jar so +when you run that jar using java minus jar and the name of your spring boot project literally tomcat +launches connects to databases does everything that you needed to do with that one jar and that's really +really really convenient when you want to deploy an update or anything like that you just +it's just a jar you don't need to do all that much to get an update up and running +and that's really really convenient and +it integrates with everything including uh any database i specifically chose to use mysql +now the reason i chose to use mysql is very uh it had a better installer for mac os +which i use so i i very much was in conflict between mysql or maria i didn't +think postgres because i have some experience with it but not enough and uh myself is more popular and +usually it's a question i get more frequently working with mysql rather than postgres +so i chose to go with mysql there's also maria db which looks really great i +haven't worked with it much but it just didn't have an installer for mac os that's the main reason why i +didn't pick that and picked mysql instead which is much easier to install here +and but they both should be mostly interchangeable so if you prefer mariadb +go for it and one of the nice things about +spring is that it works with jpa or hibernate and that's an object-relational mapping tool +that allows you to map an object to uh fields within the table and it +also creates the tables and the sql implicitly so this is really easy to do in spring +boot and it's even less boilerplate than you'd normally have with jpa +it's still jpa but the nice thing it's also a standard so if you're familiar with java e this +should be instantly familiar and it's even easier than it is in java +e to work with it so +it's mysql is as a database it's familiar it's simple and it performs +really really well and it's very powerful it scales to huge numbers +but the obvious question i i got when i said okay we'll go with mysql to some of my friends is uh +why not no sql because that's the big thing and people are building with +and with lots of other solutions based on nosql it's very popular in the startup world today and +for good reason it's really easy to do some of the more social type of applications with a document based +storage rather than a tabular based storage although +sometimes that's overblown by people who don't really who aren't used to building uh sql-based +applications so when we started codename one we started +it on app engine from google and we're sort of um didn't really have a choice the only +option to store back then this changed since but back then the only option to store was to use google's big table +and we thought okay that one shouldn't be a big deal to use an sql database to this day it's a remarkable pain that +we're stuck with that um i don't want to say a bad word in this +video so imagine me cursing to high heaven and that's pretty much how i feel about +google app engine and about their data store api which is one of the worst things i've ever worked +with by a pretty good margin and uh +the reason for that isn't so much um the implementation it's designed to +scale at google levels of scale and if you need just you know just tens +of millions or hundreds of millions and not trillions and billions of uh operations +and literally that's the scale of difference then you'd be served better by sql +because sql can do millions really really easily and even billions to some degree +and to show that to prove that google itself uses sql or at least used sql for +adsense for the things that make money because it's more reliable and easier to work with when +you need to actually have accountability nosql is really really good +at storing very abstract data that has no form and by definition +abstract data this has no form can't be queried easily and it's all +uh essentially you throw away the dba but you have to do the work of the dba now and you're throwing away all of the +tools for reporting and all of that stuff but you have to do that work now +so it doesn't really solve those problems it just lays them on you instead of laying it on +the dba the the person that manages the database and that's really really really painful +if you need to just you know a simple question how many users do you have that do x +and with nosql it's sometimes hard because you need to index things and do all sorts of +operations in order to actually do a query so there's solutions like bigquery and stuff like that but then you have to +share the data and don't get me started and the complexities related to that +and sql solved all of those things really really efficient efficiently and at scale and added reliability at a +level that's amazing so sql works +now the thing is that moving to sql from nosql is almost impossible because your data is all over the place now and just +shoving it into the really rigid structure of sql would be +almost impossible it's it's hard to do that but going the other way around if +you find that sql is restrictive moving to nosql is actually a very easy +move so i'd rather start with sql because it's easy because i can experiment i can +query it works it's reliable and not worry about the scale issue i mean +huge data operations run on mysql and work fine +and it's it can scale well uh if we ever need to go further that's +possible so i'd rather start from that always it's easier surprisingly +so let's start with a small mini mission it's not an actual mission and missions themselves won't be in this format but i +don't want this to be a video about installing stuff so i'm not going to +start showing you how to install mysql also because i use a mac and it's possible that probable that you are not +and just firing up windows and recording videos and screencasts for something that frankly will change +and all the time and probably has lots of videos on it on +the internet i just looked at the tutorials on the mysql website and +installed through there it worked really really easy for the most part the problem +i ran into two problems when installing mysql on my machine and currently and that's uh +the installer doesn't activate activate the database +so you actually need to go to the system settings and nicely it actually adds an entry to the mac os system settings for +mysql and you literally click there and activate the database and you can set it to +launch on startup and that's really really nice and once that exists you can just launch +mysql which is in a weird location for mac and not in the path but fine +and it will prompt you for a password that you got during installation +and once you do that you're effectively logged in to the database +and there you can actually create a database and we'll create a database called test and +in that database you we will essentially write stuff later on +now one of the things i ran into as well is that newer versions of mysql +uh as a security measure uh require that you change the password uh upon +installation even though the password is completely random you need to to change it before actually creating any database +so this is the line i had to use obviously you need to replace your new password with +the password you have and maybe the user root which uh in my case that was the user +during installation it might be different it's supposed to be the user of the user that installed +in this case root on a mac i'm guessing normally you'd want the user to be mysql +but that's linux where we will be deploying eventually +right now this is mostly around uh the local machine so +let's move on to spring boot which is probably more interesting than mysql which as i said covered quite a lot +and so is spring boot by the way you can google pretty much anything about it and there's tons of resources on the +internet this is just to show you the very basic +thing and this will serve also as a basis for following tutorials that i'm going to do +which is why i'm doing it and not just sending you off to read something on the internet +so i just used the id plug-in as is for netbeans i didn't test this on the other +ides i'm assuming it's as simple as that and +in the netbeans id when i do a new project i have once the plugin is installed +i have an option to select a spring boot initialized initializer project now +notice this is an initializer project and not not the basic one without anything +so it will actually offer you to select entries to add +into the project and by the way the netbeans project that i provided is just a maven project so you can open it in +any id that supports maven which is all ids and just work with it as is it it's not +physically netbeans project per se it's a generic project so it will work in eclipse it will work in intellij +so if you just want to take what i did and open it in your favorite id just do that +so the the new project uh staging the wizard +uh provides uh prompts you for lots of details about the +output project the one really important thing here is the package name because the package +name includes the default application and +all of the classes related need to be below that package to to be detected and injected by spring +and uh so you need to pay attention to that +the next stage in the the wizard is uh where we select the packages that we'd +like to support from spring boot so i picked uh +several important packages the first one is web obviously so we'll be able to do servlets and stuff like that +although we don't need it as much what we need is uh jersey which allows us to do +web services and we'll use that websockets obviously +jpa for storage and mysql for writing to the database which includes +jdbc driver and everything that you need to connect to the mysql database there's +lots of others including redness for replication and uh and +all sorts of things that would be really really useful i'm +ignoring most of them for simplicity i don't want to over burden here obviously +when it comes to scale we can add things later and i'm a big believer in doing the +minimum possible and no more than that because everything that you add effectively um +makes things more complicated later on so only add when you need it that's a +general philosophy when you're building stuff like this so the final stage is just you know the app +name as it will appear in the file system and uh and an id +uh after the creation the app doesn't have any um +jars in it so it would seem like it doesn't compile but maven has an option +the id at least in netbeans you can just right click the id and say resolve conf +missing libraries and it will offer you to update everything from maven and once you update the library everything will +run and just work as you'd expect which is actually nice it was one of the +very rare few times that maven worked for me properly so that's good +the next stage is we need to configure that's the only configuration i actually had to do to the basis app basic app in +order for it to work normally when you work with java e it's configuration hill +it's all tons of xmls or annotations which include all sorts of configuration +this took some work to discover but not much really +the main problem i had was with the database table dropping issue which was +uh it would clear the database uh tables every time that i built the app +and that was a bit painful other than that everything worked very seamlessly you can just use +these lines in the application properties which allow you to configure various behaviors of +of the spring application of the spring boot application and that just configures it to work with +the mysql database if you want to work with a different database you can very easily do so +by just using these configurations so it's it's pretty cool +the code that's generated when you create a new app is this that's the entire application and you'll notice +first of all that it's a main method which is kind of weird for a person +coming from java ee but this is literally that's what spring boot is it's a main application that +just runs spring which runs everything within it that means it's just literally a main file +and that's pretty nice so let's build something that's actually useful based on +this uh minimal minimal amount of effort and to +me just getting something running is hugely important so we'll first start with the jpa +because storage is something we'll obviously need we always need storage for something in the server +so uh we'll use jpa which is specifically the hibernate +implementation and it allows us to as i said before map objects directly to the database i'm not +going to mention sql at all here because i don't like sql i don't like +working with it all that much i know i said i like i like the databases i like the tabular structure writing sql is +a unpleasant at best so jpa really simplifies that for for me +and for my personal tastes i know i'm not necessarily not everyone agrees with that uh +approach uh but i prefer that i like the abstraction the jpa provides and i prefer working with that over +direct sql when reasonably possible and +normally you can build things that are very performant in that way it's debatable obviously or everything +is uh i'm not i'm not going to dig too much into a jpa i'm just going to sort of +show you uh what it is how it works and uh in the very basic sense so if you're not +familiar with jpa it's a standard java e api notice that all the imports here which i made sure to include +they're all javax persistence so they're all a standard api they're not something unique to +string and all of these fields essentially get persisted that means the database table +when you look at it will include an id column which will be able to generate an auto incremented which is really nice +because it's it's really hard to do that um in a portable way +uh the you'll have a column called name marked deleted etc and the owner +now this is a table that will contain a list of items and we'll use +this to provide a sort of item api later on +i trimmed some of the code uh so it will fit in a slide you know obviously there's lots of boilerplate +here still but it's mostly getters and setters and then the constructor values +that's mostly java boilerplate it's not something that spring can do much about +also because that's a java standard so it's not in their control now item repository is a very +interesting thing and that is a spin class now it extends crude repository now +uh and i included here a special method i could have left it blank you know just the interface itself with nothing but i +included a special method called find by owner which will return uh the items +based on their owner a list of items now crude if you're unfamiliar with that +term means essentially the operations you normally do on a database which is +create read update and delete and most applications that's what we need to do you know pretty much all +applications work as just taking entries in a database creating a +new entry reading it you know listing and entries and things like that updating for +changes and deleting when necessary and this uh interface is spring's +abstraction for the various crude operations that exist +and it's automatically created by spring that means even the find by owner method +that you see here that's implemented by spring i don't need to write that code at all it will +just implement that and that's really really nice that it just works +and if you'll look at the next slide you'll see that +we have literally uh that value and it's automatically injected and created when +we need it now this is the item service class uh the class declaration is sort of +on the top here as you can see and notice that above the class declaration there is a request mapping annotation +and it maps to slash item that means that in the server there will be a url called slash item +and it will map to the methods within this class +now the first method and by the way the names of the methods such as stuff i picked up i could have named the methods +uh zubavel or whatever and they would still work because they +were they map based on the uh the method type the request method so +for instance the first method is a get method notice it's mapped by request mapping to get the second one is mapped +by request mapping to put and that means the first method will be called when the http method with get is +invoked on uh slash item and the second one will again for slash +item but with the method put uh i hope you're familiar with http +methods where you can pass get put post delete uh etc so +these map directly to that now notice they don't have the typical of +servlet request response if you're used to java ee and the reason is that they're mapped directly to +json so uh which is the default here so when we request uh +a json url uh we when we send a get request and return an +array of items then literally that is translated to a json +representation and that json representation is returned +in the body of the response as you can see by the response body annotation +and when we do a get or or go in this particular case +uh i literally uh find the id of the entry if if a specific entry was asked for and +if not i use find by owner which was mentioned earlier +similarly if you look at the put when someone submits an item and as you can see +literally the item is submitted and created the submit would literally be a +json body and i'll show you later how this is used and remember +json is passed and automatically translated to a java object which is remarkable +and that's jersey by the way doing all of that it's not something that spring boot does spring boot simplifies some of +this this is actually the work of the jersey standard so this is this is part of java e +partially now you'll notice below that i just call repo dot save and that's again the crude +interface that we mentioned earlier it allows us to just call save and pass in the item and that item just gets stored +into the database which is spectacular if +it's just really easy i can do this with persist and lots of other things in the jpa but this is just easier +slightly less boilerplate code and that's it essentially i have a +simple get and put operations with almost no effort +again notice that the item repository here is injected +by spring i literally didn't implement that that interface and notice i don't just sign into it anywhere i just use it +as if it's already there magically so normally if this wasn't with spring +boot it would be uh null and this is really nice +uh i'm return one thing i want to highlight is that i'm returning the item and +you'll notice the item was an entity now that's really bad design +i'm doing it on purpose because i'm trying to teach something how it's +how you can get something out quickly and sometimes you know you do bad design choices +to get something out and i don't think that's necessarily bad because +if you'll build the best designed project in the world that never sees the light of day then that's the worst +design you can possibly build so it's important to be realistic to not +try to reach to the sky of some theoretical level of abstraction +and doing extra work because of theoretical value i'm generally against that +but returning the entity instance could be dangerous because +i'm right now all the data that's there isn't private so i can just return it and it's okay +but in theory if i wanted to build a really secure application i wouldn't necessarily want the user to +know the id of the line within the database that includes the entity the item +because if i have a weakness someplace else in the code he might be able to use his knowledge of the +database primary key to circumvent something and the fact that primary keys also sequential +provide some form of weakness so it's often you more useful to use +a sort of unguessable hash value i'm getting a bit carried away with a +complexity analogy here but generally returning uh an internal representation +is bad and you normally want to have something in between obviously +the design is very much um we can talk about that quite a lot the design is very much +a set of compromises because when you return if i would have created an item +dao something like that uh it would have been generally a copy and paste in the current situation and +that's not great design either so whatever i would have done the system is too simple +to do good design and too simple uh in that sense i prefer to to keep it +relatively simple as is i'm just highlighting this +so you don't think that's a good approach a better approach would be to work with data access objects that +are specifically designed to return the json correctly and that way you separate +the actual database data that's stored and the communication layer which is something that's logic that logically +should be separate both for security and for maintainability in the future into the future +so the next thing uh we want to do is actually show you how +this works because you know we did all of that now how does it does it map so +i'm not sure if you're familiar with curl i'm guessing not so much it's a really nice command +line utility that allows you to essentially send http requests now i'm not using codename one to do these +connections because i'm keeping it to the next presentation +where i'll show you how it's done in codename one and that's a lengthy presentation in its +own right so i'd rather use this approach and then if that's still unclear you can wait for +that presentation but generally the crow command just requests and the +url with given values and returns something in this particular case i wanted to to +do the put request so curl minus h adds a header in this case the header is +content type application json the next header is minus x which where we determine the type of http method in +this case put which means it goes to the method uh for adding an entry +the next thing represents the body of the entry in this case it's um +the literally the item fields you'll notice they map to the item fields name +marked deleted owner uh etc and the last entry is the url remember +the url is item in this case localhost item and the output for that because you +remember that method returned the instance of the item is exactly that it's the same value that i +sent because that's what was received in the server with one extra entry and that's the id +which was auto generated now you'll notice i didn't pass the id when you pass it which implicitly set it to null +now the server when it gets another id understands it needs to create it and then uh +works with that similarly we can fetch an items items +with just accrual request this will set use cruel in the default settings which is that just uh send a get request +in this particular case i passed the owner value as shy and because i made that previous request three times you'll +notice i have three entities uh from the database that all have +these uh values so that that's essentially +shows you how this works and that you can add pretty much anything into that uh +database so i hope this was educational and helpful +for you there's lots of resources out there on spring boot i did this video so you +don't go all over the place because these resources are often over ambitious and try to teach you lots +of things and these are the things that i need you to know and i'll try to teach you the things +that i need you to know for the successful completion of the boot camp +but obviously there's a huge sea of valuable spring and spring boot +resources that you can delve into if you want to go deeper i'm not going to talk about all of the +complexities and the security elements and how to set up mysql and because this +never ends and frankly people did better than me on the internet +i do hope you understood how the rest4 service works and how easy it is to build it i didn't yet go into websockets +which i will go probably later uh thank you for watching this and i hope +again that you enjoyed it and wasn't too heavy diff --git a/docs/website/video-transcripts/uYCCbE70kE0.json b/docs/website/video-transcripts/uYCCbE70kE0.json new file mode 100644 index 0000000000..8c261ebf61 --- /dev/null +++ b/docs/website/video-transcripts/uYCCbE70kE0.json @@ -0,0 +1,8 @@ +{ + "line_count": 128, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 646, + "youtube_id": "uYCCbE70kE0" +} diff --git a/docs/website/video-transcripts/uYCCbE70kE0.txt b/docs/website/video-transcripts/uYCCbE70kE0.txt new file mode 100644 index 0000000000..8b261b9c8b --- /dev/null +++ b/docs/website/video-transcripts/uYCCbE70kE0.txt @@ -0,0 +1,128 @@ +the star form is a pretty large class +and as such i've split the segment into +multiple parts +let's move on to the pointer released +method +this method is a special case as it +seems to behave +like the other methods +but then it binds functionality to the +else +clause +first though +why pointer released and not pointer +pressed +by convention all events +in all operating systems that might +trigger a ui change are based on pointer +or key release +this is a convention that's used +everywhere +and the reason is simple +say i have a pointer event and it +triggers showing a dialogue if i use +trust then the released event +might be sent to the dialogue +this can breed ui inconsistency +if someone relies on this behavior and +might trigger a race condition between +pressed and released events +so we always use release events for +everything that isn't scrolling or input +where reaction speed matters +having covered that let's look at what +this method actually does +first we get the component at the right +position +of the pointer event +and gets get its ui id +there is nothing we can do with a +container +so it's a special case that we don't +customize +scrolling down a bit +if there is a border we can't customize +the background since border will +override that +so we don't need to take that into +account +so +if +this is a text area without text +then we can't customize the font either +and we might as well return +the same is true for a label +if it's a label without text +then font doesn't make sense either +if it's not a text area or label +customizing the font is also meaningless +so we can return in that case too +scrolling further down we can see the +customization dialog +which is just a simple dialogue with +four buttons to customize the various +elements +change foreground color seems a bit +intimidating +but it isn't as intimidating as it might +seem +first we dispose of the existing dialog +then we create a new one to which we add +a color picker +we'll discuss the color picker in depth +later on +however +notice that this method accepts a +callback closure +that calls set fg color and repaint to +update the user interface +live with every change +made +we then use show popup dialog to show +the pop-up dialog on the component that +we clicked +the pop-up dialog is a special type of +dialogue with an arrow which we used +here +that's it +the background color +has nearly the same code with one minor +difference +the background +should be opaque +for the background to show properly +and right now we don't provide a way to +determine opacity +in the color dialog +font customization also uses a custom +call +to a font editor which we'll discuss +later but the basic logic is pretty +similar to the rest +the default value button is just one +line of code which is a common trick +calling set ui id with the same ui id +just resets the value to the ui id +default so all the changes we made +would be +implicitly discarded +when we construct the ui there are +several options to consider +if this is a label or text field then we +need to offer a way to customize the +font and foreground color +otherwise we only need the background +color +notice other options were already dealt +with at the top of the method +where we returned for cases where there +was nothing to do +if this is a label or text field then +the only question is whether we can also +customize the background in which case +we will add that button +too +finally we show the popup +i disable the transition as the default +fade transition for popups +doesn't look great in this case diff --git a/docs/website/video-transcripts/uvQKs_PdB64.json b/docs/website/video-transcripts/uvQKs_PdB64.json new file mode 100644 index 0000000000..420e425e1a --- /dev/null +++ b/docs/website/video-transcripts/uvQKs_PdB64.json @@ -0,0 +1,8 @@ +{ + "line_count": 155, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 846, + "youtube_id": "uvQKs_PdB64" +} diff --git a/docs/website/video-transcripts/uvQKs_PdB64.txt b/docs/website/video-transcripts/uvQKs_PdB64.txt new file mode 100644 index 0000000000..652f456944 --- /dev/null +++ b/docs/website/video-transcripts/uvQKs_PdB64.txt @@ -0,0 +1,155 @@ +in-app purchase is a relatively simple +feature +with a bit of a hassle from the stores +who handle the product management +the api itself is trivial +before we begin let's review the basics +of what in-app purchase +is +in-app purchase allows us to use the +same billing support +apple and google use for buying apps +in order to sell items within the app +you can use this to sell virtual goods +which means things that are part of the +application such as digital content +upgrades +in-app features +game elements etc +using in-app purchase is much easier +than credit card billing or any other +form of payment as the whole process is +managed by google or apple +however it's also a requirement +if you have elements sold within your +app +you might get banned if you don't use +in-app purchase +as an example we recently updated the +kitchen sync demo in the apple app store +and got a rejection +the tester reviewed the link in the app +to codenameone.com +and complained that we sell items within +the site the subscription +and don't use in-app purchase +we explained that the app is completely +free and the subscription has nothing to +do with the app +and the app past verification +this should obviously teach you that +communication in these cases is +bidirectional +and also how serious apple is about the +in-app purchase requirement +so +why don't we sell codename one +subscriptions via in-app purchase +this is something we are actually +considering +but the main the motivation is the 30 +commission to apple +they now have a special case for +subscriptions that might make them more +financially viable +so we might add that in the future as an +option +but overall in-app purchase is expensive +if you are having thoughts about +charging extra for in-app purchase +for purchases made through in-app +purchase +that would be a problem as it's +prohibited by apple's terms of service +let's start by looking at the code for +in-app purchase +just like +push requires the push callback in the +main class +in-app purchase requires the callback in +the main class too +i'll skip ahead to the callback methods +for the purchase +item purchased is the actual callback +method +and it includes the sku representing the +item that was purchased +an sku is a constant value that you +define when you set up the items in +google play or itunes connect +the sku lets you determine the item that +the user purchased +when you have more than one item +we are calling into the pending build +method in this case to proceed with the +purchase flow we started there +i'll go into that soon +we have an error callback +as well +which we can use for error logging +the refund callback might be interesting +if +this is a feature you can disable or +refund +but in this case if a user asks for a +refund there isn't much we can do as the +app was already built +the rest of the callbacks relate to +subscription and manual purchase +callback +which is no longer used +subscription is a subject in its own +right that i won't get into here +but it's a very good direction as it +allows a constant and steady stream of +revenue +let's move into the app form code for +purchase +the purchase ui allows shows a dialog +with commands for purchasing an ios +android or source code build +for now we don't support the ios build +but might provide this in a future +update +based on the command selected by the +user we can select the right target and +perform the right purchase type +once pending build is invoked +it just invokes build app as usual +since this is invoked by the purchase +callback this is effectively seamless +we could have used the sku value from +the server and called the build app +callback with the sku which would have +worked exactly the same as this +as you can see the code is pretty +trivial the main hassle is handling +this in google's and apple's interfaces +in google play after you've uploaded +your initial apk you can select in-app +product we click add new product then +select managed product which allows us +to add a new product sku +i type in build for android as the sku i +can use an arbitrary string value to +represent the sku +i type in a name and a description +for the item we're selling +this will be displayed to the user +as part of the native +process by google play +the next +stage is scrolling down to the price and +clicking the add price button +so we can enter the price for the item +using the local currency +i set the price for 40 40ns which is +mostly a trial and error thing +of what google will generate in terms of +the pricing table +once this is set i can scroll to the top +and save the data +and this in a nutshell is an app +purchase +thanks for watching i hope you found +this informative diff --git a/docs/website/video-transcripts/v-kJ_nIYq3I.json b/docs/website/video-transcripts/v-kJ_nIYq3I.json new file mode 100644 index 0000000000..160ed0ce10 --- /dev/null +++ b/docs/website/video-transcripts/v-kJ_nIYq3I.json @@ -0,0 +1,8 @@ +{ + "line_count": 105, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 606, + "youtube_id": "v-kJ_nIYq3I" +} diff --git a/docs/website/video-transcripts/v-kJ_nIYq3I.txt b/docs/website/video-transcripts/v-kJ_nIYq3I.txt new file mode 100644 index 0000000000..fb6772c9aa --- /dev/null +++ b/docs/website/video-transcripts/v-kJ_nIYq3I.txt @@ -0,0 +1,105 @@ +we are nearing the end of the sign up +wizard ui +the next stage would be +your mobile number +but that's a bit of a special case +because it leads to the email address +and both of these are effectively the +same form +they both look pretty similar to one +another +and similar between ios and android with +the obvious platform differences +this is create number notice that it +delegates all of the work to the create +mobile or email +so let's start there first and come back +you can reach this form either from +gender or from email number based on +going back and forth so the title of the +back command can differ +the text entry is just one text field +so the ui is pretty trivial +notice i use constraint which will +determine the type of virtual keyboard +i'll use +we pass the action listener +on the link +it will navigate to the other form +eg if we are in a +phone entry it will go to email entry +we add a button to the south container +this is the sign up with email address +link +or the similar sign up with mobile +number +we pass through if this is a phone +number and false for email +we also pass the text of the phone email +we'll display that in the confirmation +form that was mostly standard stuff it +will be further clarified with the +actual calls to this method +these are the labels we show in the form +the text input constraint in this case +it's a phone number in other case in the +other case it will be an email address +constraint +navigation to the other form here we +navigate to the email entry form and +there we'll navigate to the number +as you can see the email version is +practically identical +that's pretty much it for these two +forms we will need one css entry though +it's the same as blue link only bold +once we do this email number toggle +should +work +there is nothing interesting how i +consider making a generic method that +will generalize this +and the phone number slash email form +but decided against that i think that +would have over generalized +uh it would have created an overly +confusing code +the code is pretty much textbook +boilerplate nothing much to talk about +the only thing worth noticing in this +form +is the phone and value variables that +are passed right through the method to +the confirmation form +we can now move to the last stage of +signup as there is no css or anything +the final stage of the sign up process +is the account confirmation form +this is a trivial form +as well but it does include minor things +to notice +the subtitle can span lines here +depending on the size of the phone +screen +we have a special center line message +that includes the content we passed from +the phone slash email stage of the +wizard +when done we show the main ui +i'll add a stub for this method soon +we just have one more tiny change to the +css +we need to add the new +center label style +finally we need to add some wiring to +show the signup process in ui controller +we'll add a new signup call +and we will also need a stub for show +main ui +so the sign up wizard will compile +and finally we bind the signup wizard to +the login form constructor using this +code +with that the sign up process mockup is +done and we can move to mocking up the +main ui diff --git a/docs/website/video-transcripts/vAiTW4Y8QuA.json b/docs/website/video-transcripts/vAiTW4Y8QuA.json new file mode 100644 index 0000000000..38411faf5d --- /dev/null +++ b/docs/website/video-transcripts/vAiTW4Y8QuA.json @@ -0,0 +1,8 @@ +{ + "line_count": 100, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 554, + "youtube_id": "vAiTW4Y8QuA" +} diff --git a/docs/website/video-transcripts/vAiTW4Y8QuA.txt b/docs/website/video-transcripts/vAiTW4Y8QuA.txt new file mode 100644 index 0000000000..cf4afc2a60 --- /dev/null +++ b/docs/website/video-transcripts/vAiTW4Y8QuA.txt @@ -0,0 +1,100 @@ +now that we covered the basic +persistence abstraction +let's proceed to integrate it into the +app +we fetch dishes as we need them +implicitly from storage +this is pretty simple code that just +adds all the dish elements to the dishes +list proper property +as i mentioned before the dish list form +needs to detect the deletion of a dish +so it can remove it from the ui +we do this by binding the delete +listener and removing the element +one of the really cool things about +properties is the ability to bind +property change listeners two properties +so we can decouple code that uses the +same data model to display a view in two +different places +i wrote this code before the ui binding +api code existed in codename one +but it's pretty trivial either way +i just update the text +when the property changes +it's better to beg for forgiveness than +to ask for permission +is a proverb sometimes attributed to +programming pioneer grace hopper +regardless of the source of that great +quote it's a wonderful example of +good mobile app design +in a mobile app you shouldn't bother +users with are you sure prompts +you should just do what the user does +and provide a way to undo which is +exactly what we do when we delete a dish +in this code +i'll skip a bit ahead because the code +above is asynchronous +when delete is pressed we show the +previous dish list form +since the edit form is no longer +applicable +then we delete the dish in the server +and in the storage you will notice that +there is no prompt +now we need to show a ui +that allows the user to undo the delete +operation +but here is the problem +we just navigated to the dish list +from the form +we were in +since the edit form is no longer +applicable +so +we use a listener on the parent form so +we will know when it is shown again +the problem is that if we do that +without the next line we'll get prompts +every time we enter that form +after the deletion +so the obvious question is +why don't we just call remove show +listener this +well +funny story +this is a lambda expression +and here this is always the parent class +so remove show listener this +won't have the desired effect +so the solution is a bit of a hack +but it was either this or use +inner classes so i chose this hack +this is relatively simple +we are now in the right form after the +deletion and i can now show the undo +toast message that will appear in the +bottom of the form +if a user taps this popup it will undo +the deletion instantly +the undo code doesn't really undo as +there is no meaningful way to undo +encode +we just re-add the dish +by comparison saving the dish is much +easier +and practically trivial +all of the logic to save a dish to the +server and update it to storage is just +a couple of lines of code +in the case of the +tagline and title +we currently only save them locally but +we will add the server update code as +well +thanks for watching i hope you found +this +informative diff --git a/docs/website/video-transcripts/vLBQhAJ6aTk.json b/docs/website/video-transcripts/vLBQhAJ6aTk.json new file mode 100644 index 0000000000..22560c315f --- /dev/null +++ b/docs/website/video-transcripts/vLBQhAJ6aTk.json @@ -0,0 +1,8 @@ +{ + "line_count": 153, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 836, + "youtube_id": "vLBQhAJ6aTk" +} diff --git a/docs/website/video-transcripts/vLBQhAJ6aTk.txt b/docs/website/video-transcripts/vLBQhAJ6aTk.txt new file mode 100644 index 0000000000..873e21f15c --- /dev/null +++ b/docs/website/video-transcripts/vLBQhAJ6aTk.txt @@ -0,0 +1,153 @@ +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/video-transcripts/vOcKbbW9HBM.json b/docs/website/video-transcripts/vOcKbbW9HBM.json new file mode 100644 index 0000000000..a53c1f7123 --- /dev/null +++ b/docs/website/video-transcripts/vOcKbbW9HBM.json @@ -0,0 +1,8 @@ +{ + "line_count": 56, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 324, + "youtube_id": "vOcKbbW9HBM" +} diff --git a/docs/website/video-transcripts/vOcKbbW9HBM.txt b/docs/website/video-transcripts/vOcKbbW9HBM.txt new file mode 100644 index 0000000000..2689cd42ea --- /dev/null +++ b/docs/website/video-transcripts/vOcKbbW9HBM.txt @@ -0,0 +1,56 @@ +in this section we'll discuss the +address input logic and validation +one of the things that i missed when i +wrote the section on the ui elements +that are missing is the address +collection ui the reason i missed it +was that i assumed incorrectly that the +billing api will do that for me +it doesn't do that and +so i needed to add some form of design +for address collection as you can see in +the video +it's relatively bare bones for now +but it will do +to collect the address i used this +business object i also added some simple +mapping into preferences +as you can see below +so the last entered address will be +cached for future orders +the rest of the logic is pretty easy +i just bind each text field to the +property +notice that this original code was +developed before we had property binding +and instant ui support +if i had to do it again i'd probably use +instant ui +for this feature as it's an ideal use +case +a lot of the logic here +can probably be simplified in the +current api but for now it works +this block +is really a form of validation +we have a shipping range property that +can +be set for restaurant +and here we use the device gps to make +sure the device is within +reasonable range of the restaurant +here we slide the components of the +shopping cart out to make room for the +address fields +which we add and animate into place here +this is mostly mostly simple code +for form input as the binding was done +before +the validation code is pretty simple +i mentioned the shipping range but we +also have a minimum order value +which is now enforced +i also added support for a delivery fee +that can be levied on top of the total +cost this is applied in the summary code +of the form diff --git a/docs/website/video-transcripts/vdFr815Dhpg.json b/docs/website/video-transcripts/vdFr815Dhpg.json new file mode 100644 index 0000000000..32398818f4 --- /dev/null +++ b/docs/website/video-transcripts/vdFr815Dhpg.json @@ -0,0 +1,8 @@ +{ + "line_count": 262, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1543, + "youtube_id": "vdFr815Dhpg" +} diff --git a/docs/website/video-transcripts/vdFr815Dhpg.txt b/docs/website/video-transcripts/vdFr815Dhpg.txt new file mode 100644 index 0000000000..517b9d93c7 --- /dev/null +++ b/docs/website/video-transcripts/vdFr815Dhpg.txt @@ -0,0 +1,262 @@ +the next step is the login form which +requires a bit more +the design of the login ui varies +dramatically and in both cases it wasn't +great +so i ended up taking a great deal of +creative liberty in my interpretation on +how this should look +there are a lot of things i chose to +change about the ui most of them are due +to taste and personal preference +i try to make the code as close as +possible +so i would reuse more of it +but i still wanted to show how i can +create +very different uis for very different +platforms +for instance +this is the ios version running on a +tablet you will notice i copied a lot of +the ui but you will also notice i took +some inspirations from android +i made sure to get everything +working in landscape notice i took a +very different approach to the ui and +landscape that i think looks much better +than the native version which frankly +looks awful +notice that in the ipad version in the +clone i don't move the title image +it doesn't move to the side like it does +in other platforms when shifting to +landscape +i also ignored the top area with more +functionality and languages +it doesn't exist on ios and didn't +strike me as too important +since +the login form is a complex form i chose +to create a new class +for that i also chose to create a new +package com.codename1.fbclone.forms +this class is a bit big +but before i get into it i'd like to +explain how i implemented this ui in the +ios version +what we see here are two text fields +grouped together so the first and the +last have rounded look while entries in +the middle which we don't have here +would not +this is a common look in ios although it +has gone somewhat out of fashion +in codename one we can apply that look +using the component group class +this class is a box layout container +that automatically assigns special ui +ids to components so the first component +would be group element first the second +would be group element and the last +would be group element lost +notice that there is also a special ui +id for buttons added to the group +we don't use it in this case so that's +just an fyi +the ios theme includes styling for these +elements they include rounding on the +top of the first element and rounding on +the bottom for the last element +on android this design paradigm is +uncommon +as a result there is no styling for +these elements on android +a group layout acts as a regular box +layout container on android and doesn't +change the natural +styling of the elements +this works seamlessly because the native +ios theme +has a theme constant component group +ball +if it is set to true that means the ui +id should be changed +component group ball is only defined on +ios and defaults to false +elsewhere +in the code i use that theme constant to +decide whether to show the android ui or +the ios ui +let's get into the code first the class +variables and method declarations +the logo is pretty much the same value +we saw for the splash screen it doesn't +have to be the same though +email and password are just standard +input fields +the following buttons exist in the ios +and android uis although the signup +button has different text in the android +version +the login form uses border layout so we +can place the content where we want +without the ui id the toolbar would +appear on the top of the screen +container is transparent with zero +padding margin which hides the toolbar +we set the name of the logo so the logo +from the splash screen will animate into +place due to the morph transition +we're placing the logo on the top of the +screen in the center notice i used the +login title to set the background around +the logo +we are on a phone +if we are on a phone i want the logo +container to shift to the right side of +the screen +when rotating to landscape this is +handled automatically by border layout +if we have component group support i'll +show the ios style ui +i prefer this to testing whether this is +ios since other platforms might choose +to apply that look +we place the ui +in the absolute center so it's in the +middle of the content we wrap the button +and component group in a box layout on +the y axis +so they stick together in the center +the login and password go into the +component group and the login button is +placed below +the padded container ui id allows us to +create some white space around the +components +so they don't stick to the edges when +running in a phone +i add the content pane to the center of +the ui so it will take up the available +space +we place the two up buttons on the +bottom of the ui +the android version is far simpler we +place the components in a box layout y +and tweak a few ui ids slash texts +and finally this hides the status bar +line on the top of the form +and lets the image occupy the space +the status bar appears on top of the +toolbar and grabs space to push down the +content to leave space for the iphone x +notch and ios clock etc it's a feature +of ios only +one item i skimmed over when reviewing +the ios look code is why we needed the +content container +we could have just used the form which +is already in border layout and just +changed it to use center behavior center +absolute +that would have worked for portrait +but not for landscape +since both +since south takes up the space below +east and west +it would have produced this image +which isn't what we want +the next big thing is setting the css +values +before i go into the new ui ids +we'll start with one change to the +existing css selector +this allows the buttons in android to +appear in all caps +there these are the ui ids that should +be considered as buttons +since the button class is very common we +don't apply the caps behavior to all +buttons by default if the ui id isn't +button we need to explicitly declare +that we are interested in the text +becoming uppercase +notice that this will only apply to +platforms where buttons are caps to +begin with +so if we use blue text on ios it won't +be capitalized +before we go into the full set of css +changes here is a map of the ui ids i'll +mention you can reference this map if +something isn't clear +when i review the code +we need to add the following to the css +file to support all the new ui ids there +is quite a lot of code here +this automatically adds the file to the +resources +the login banner is this jpeg which you +should place in css slash images in your +project +by default the plugin adds multi images +where +we can specify the dpi of the source +image +by setting the dpi to zero i state that +i want a regular image +multi images are great but for +background that would be scale to fill +anyway +the pixel perfect scaling of multi +images will probably be wasted +multi images take extra space in the +resource file and thus in ram +jpeg is +especially efficient in terms of size +and for relatively large background +images it can be the ideal option +the title image fills available space +which should look decent regardless of +the resolution of this type of image +i use a pretty big padding value to give +the title area space in both +orientations so the image background +will show +we shouldn't change the container ui id +so i added this generic ui id for +spacing +out the ui a bit +i use this as the base ui id for buttons +as they have a lot of properties in +common such as center alignment etc +i usually prefer to derive ui ids from +my own style and avoid deriving from +built-in ui ids +as those might change in unpredictable +ways +we derive the base button ui id +we customize the background color and +use a round rect for the border +this pattern repeats in almost all of +the following ui ids +the default text hint +looked bad +so we need to customize it to have +better font padding +etc +this is the text that appears before we +type in the text field +addition additional padding is needed in +the text field and group elements to +make them fit better into the ui +with these changes we are nearly done +there is just one last change to make +this all work +we just create and show the login form +for now no extra logic +with that we can run the code and see +the ui running +next i'll continue with the sign up +process diff --git a/docs/website/video-transcripts/vjTexRDCihA.json b/docs/website/video-transcripts/vjTexRDCihA.json new file mode 100644 index 0000000000..4645d10978 --- /dev/null +++ b/docs/website/video-transcripts/vjTexRDCihA.json @@ -0,0 +1,8 @@ +{ + "line_count": 127, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 701, + "youtube_id": "vjTexRDCihA" +} diff --git a/docs/website/video-transcripts/vjTexRDCihA.txt b/docs/website/video-transcripts/vjTexRDCihA.txt new file mode 100644 index 0000000000..a4812f91ac --- /dev/null +++ b/docs/website/video-transcripts/vjTexRDCihA.txt @@ -0,0 +1,127 @@ +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/video-transcripts/vxsUf3gw1zg.json b/docs/website/video-transcripts/vxsUf3gw1zg.json new file mode 100644 index 0000000000..ff0895decf --- /dev/null +++ b/docs/website/video-transcripts/vxsUf3gw1zg.json @@ -0,0 +1,8 @@ +{ + "line_count": 151, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 832, + "youtube_id": "vxsUf3gw1zg" +} diff --git a/docs/website/video-transcripts/vxsUf3gw1zg.txt b/docs/website/video-transcripts/vxsUf3gw1zg.txt new file mode 100644 index 0000000000..8607fff4fa --- /dev/null +++ b/docs/website/video-transcripts/vxsUf3gw1zg.txt @@ -0,0 +1,151 @@ +let's dive right into the code starting +with base navigation form +the main class for the top level forms +is the base navigation form +here we have the material commands in +the side menu that allow us to navigate +to the other top level forms +the title of the forms +is really a text field +as it's editable in a place +the trick in making a text field feel +like title is to style it as such +which is why we created navigation title +uiid +and that style has zero margin +one millimeter of padding +with five millimeter white font that's +aligned to the left +that looks and feels like a title +one important thing to notice is the +left align +which doesn't match the ios convention +center aligned text fields are flaky +across platforms so we don't support +them properly +that's why we use left alignment here +the tagline is similarly a text field +with the right styling +technically the styling derives +navigation title +so +we can be in sync +with changes to the title style +it just uses a smaller three millimeter +font +Edit Background UI +in case this wasn't clear +these ui ids map to the title and +subtitle of the form right +here moving on +the edit background ui on the bottom +right portion of the form maps to this +button +which allows us to edit the background +image in the title area +you will notice we use the constructor +that accepts the button label and ui id +the edit background ui id is styled to +match the navigation title but it also +adds padding to the bottom +so the text doesn't appear within the +white area +i'll talk about that more in the next +section +besides that the other interesting thing +about this ui id is the right alignment +which we use to make it sure it appears +to the right of the icon +the background image is just a scaled +image label that stretches all across +we added a four millimeter margin to the +bottom of this background image +and that allows the icon to peak exactly +four millimeters outside of the bottom +portion +this looks decent enough and works well +for all resolutions +since the size is in millimeters and the +icon is also scaled in millimeters +the logo image +uses a special rounded scaled logo +getter +which adapts the logo's look to the +design of the ui +let's go see how that method works +Rounded Logo +the rounded scaled logo is +just an image with no setter +so +where is this initialized +when we set the property for the logo we +automatically generate this image too +by drawing the image in the right place +let's go over these steps +we have two important sizes here +millimeter +or mm is the size of the arc +in the corner +and size is the size of the entire icon +i got these numbers through trial and +error until i got something that looked +satisfactory and the various device +densities +a general path is a shape object we can +fill stroke or clip based on a shape +notice that shape operations can +sometimes be expensive in terms of +graphics processing power so while they +are very powerful +you need to be very aware of what you +are trying to do +a general path allows us to define an +arbitrary shape we can then use in the +drawing primitives +this raises the question of why not use +the built-in round rect methods within +graphics those don't allow the same +level of customizability and power as +the shape api +they aren't as consistent across +platforms either +building the shape is +essential +uh essentially about moving a virtual +pen around and drawing lines arcs or +curves along the path +this is mostly straightforward +here we create a mask from the shape +a mask for the image +we create a solid black image +and we fill the shape area with a white +color +then we create a mask +a mask can be applied to an image to +remove the black pieces +and leave the white pieces +this begs the question why not use shape +clipping that i mentioned earlier +masking supports anti-aliasing which +renders the elements in the corners with +a slight alpha channel to make the +corner seem smooth +a clip is a binary operation where a +pixel can either be in the clip or it's +outside of the clip +so there is no room for interpretation +using shape clipping won't look as good +furthermore shape clipping isn't as +portable as masking which is +100 portable to all supported +codename one platforms +last line of this code +we do two separate things +the fill method scales the image but it +does that while cropping redundant areas +so +if we have an image that isn't a square +the area outside will be clipped +and the center will remain +we then apply the mask to the clipped +image to create that round effect diff --git a/docs/website/video-transcripts/w7xvlw3rI6Y.json b/docs/website/video-transcripts/w7xvlw3rI6Y.json new file mode 100644 index 0000000000..f7dcebc711 --- /dev/null +++ b/docs/website/video-transcripts/w7xvlw3rI6Y.json @@ -0,0 +1,9 @@ +{ + "line_count": 9, + "quality": "needs-review", + "source": "embedded", + "source_path": "content/howdoi/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature.md", + "status": "transcript-fetched", + "word_count": 264, + "youtube_id": "w7xvlw3rI6Y" +} diff --git a/docs/website/video-transcripts/w7xvlw3rI6Y.txt b/docs/website/video-transcripts/w7xvlw3rI6Y.txt new file mode 100644 index 0000000000..f8a78693eb --- /dev/null +++ b/docs/website/video-transcripts/w7xvlw3rI6Y.txt @@ -0,0 +1,16 @@ +_Transcript source: embedded._ + +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? + +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. + +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. + +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. + +The pro versions include 5 month support which typically maps to the last one or two releases. + +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. diff --git a/docs/website/video-transcripts/wp6gqYLD-XM.json b/docs/website/video-transcripts/wp6gqYLD-XM.json new file mode 100644 index 0000000000..1a5e598db5 --- /dev/null +++ b/docs/website/video-transcripts/wp6gqYLD-XM.json @@ -0,0 +1,8 @@ +{ + "line_count": 69, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 403, + "youtube_id": "wp6gqYLD-XM" +} diff --git a/docs/website/video-transcripts/wp6gqYLD-XM.txt b/docs/website/video-transcripts/wp6gqYLD-XM.txt new file mode 100644 index 0000000000..ff4856a564 --- /dev/null +++ b/docs/website/video-transcripts/wp6gqYLD-XM.txt @@ -0,0 +1,69 @@ +facebook allows you to attach an image +while you are editing a post +this includes some nuanced behaviors i +would like to skip +instead i'll use the gallery icon next +to the post to create a dedicated image +video post +i've changed a lot in image picker +so much so that it literally doubled in +size +the first change includes support for +picking video or photo +the image picker method is refactored to +a more generic pick method +that works with both images and videos +this is the same code as before +notice that if the mode +is image only the new video pick +functionality would be irrelevant +assuming the callback isn't null which +can happen on cancellation +this +points to a file system storage path +to the image or video file +we check the extension to see if this is +an image otherwise we assume video +since sl is +lowercase we don't need to ignore case +the encoded image is created and a +callback is invoked for the case of an +image +in the case of a video +we don't need to save the file as it's +very transient when compared to image +files +when uploading we need to check if this +is a video right now i oversimplified +all uploads are marked as jpeg or mp4 +that isn't correct for most cases +the mime type should defer in some cases +but for the sake of simplicity i chose +to keep it this way +since we create the image in this class +it makes sense to provide a get image +method for users of the new create +methods which i'll address now +there are a few additional changes we +need in the class to support custom +capturing +when we capture an image using a low +level api we don't need the pick method +and we'll just submit the data directly +in these cases we we need a different +api +but want to retain the picker as we use +it later in the code +the factory method creates a picker with +a pre-existing file +we read the file into an encoded image +to create a picker instance +in this factory method we already have +the battery data of the image +we make +up a fake file name +for the update process +obviously an event would be redundant +for these two use cases +this will be clearer when we reach the +camera portion later diff --git a/docs/website/video-transcripts/wqcM8pSOGTY.json b/docs/website/video-transcripts/wqcM8pSOGTY.json new file mode 100644 index 0000000000..bd54044ee2 --- /dev/null +++ b/docs/website/video-transcripts/wqcM8pSOGTY.json @@ -0,0 +1,10 @@ +{ + "fetch_method": "youtube_transcript_api", + "line_count": 270, + "quality": "needs-review", + "source": "fetched-automated", + "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/033-introduction-and-setup.md", + "status": "transcript-fetched", + "word_count": 1468, + "youtube_id": "wqcM8pSOGTY" +} diff --git a/docs/website/video-transcripts/wqcM8pSOGTY.txt b/docs/website/video-transcripts/wqcM8pSOGTY.txt new file mode 100644 index 0000000000..cb30dc62e7 --- /dev/null +++ b/docs/website/video-transcripts/wqcM8pSOGTY.txt @@ -0,0 +1,270 @@ +working with the codename one sources +is not for the faint of heart +you can learn a lot from going through +the process +however if your only goal is to avoid +the build servers you might find it +harder to work with +in fact i personally use the build +servers when building apps and testing +them +i almost never use offline build or the +sources directly +instead i hack and test things via the +include source option +however +learning this is still valuable and i'm +aware of a few people who don't share my +opinion on this matter +still +why would i write a guide for something +like this +there are three individuals i think of +who might benefit from this guide +if you are the type of person who needs +to do everything yourself +then this is pretty much it +if you want to understand the +underpinnings of codename one at a +deeper level than +more the more abstract descriptions +then this is a good first step +if you want to feel secure that you can +hack codename one manually if our +service changes or becomes unavailable +in the future then the mere existence of +this guide should help calm some of +those concerns +to understand how to work with codename +one code +we first need to understand what +codename one does +for this case i'll remove the build +servers from the equation and focus on +the portability aspect codename one +includes three distinct pieces of four +portability +vm +for some platforms such as android the +vm is natively included +however we work on top of rvm in ios +kvm in windows +and tvm for javascript +api represents the actual calls to the +com.codename1 +classes +ports are the implementations of the api +for the various platforms +this is all pretty trivial the api is +always the same we change the vm and +port to support all the platforms so if +you understand this +then the simulator is simple too +in the simulator the vm is just the +standard java vm +and the port is just the port of +codename one to java se +there is a bit more to that though +the port javasc includes a lot of +specialized glue code to show the +simulator and other features such as the +network monitor etc +but let's start with this and move on +all of the instructions that follow will +work regardless of whether you have the +plugin installed +i will focus on using the command line +and assume you can translate that to +your ide of choice +technically you don't really need the +plugin if you are working from the +source code +however it's convenient to work with +it means that selecting new project and +picking codename one just works +it means that double clicking a resource +file launches the designer tool +but technically you don't really need it +what you do need is jdk8 +notice that newer versions might not +work +we generally use the oracle jdk because +it comes bundled with javafx +and that makes everything simpler as the +simulator needs javafx for some features +specifically media and browser +if you choose to use openjdk it might +work if you install +it with opengfx +but it's not something we tried +apache aunt can be downloaded from the +apache website +make sure ant is in your system path and +that the command runs +note that you will need to define the +java home environment variable properly +git is optional you can download the zip +from github but if you'd like to +contribute pull requests back +into the project this can be useful +for android development we'll use +android studio 3 which you can download +from google +for ios development we will use xcode 9 +point x or 0.2 +from apple +we need to clone these repositories or +just download the source zips +of these repositories and expand them +these repository repositories should +reside in the same hierarchy and i will +rely on that later on when giving out +instructions you will notice we have +three repositories to start with +codename one is the main repo the skins +make sense as we use them to show the +phone skins but why have binaries +the immediate question would be why do +we need binaries for an open source +project +the answer is that they are harder to +build on their own in some cases and in +others we need stubs or nate of native +platforms so we can compile against them +without downloading the full sdk +let's go over the files and directories +and the binaries project and explain +them one by one +the svg directory +is from the open source batik project +it's unmodified we need it for the +designer tool for svg support +the ikvm project is hard to compile +without windows so we have a +pre-compiled version here +these are legacy stubs of the blackberry +os +i won't cover blackberry here so these +aren't really needed +we need stubs to compile javafx code we +don't embed this +this is the jh labs project for javasc +image filters we use this to implement +gaussian blur in the simulator +these are used by the sqlite +implementation +in the simulator +these are part of the open source assemb +project which we use in powerpower vm +everything here is stubs which we use to +compile the android native +implementation without downloading the +full android sdk +we don't run any of this just use it for +compilation +these are legacy stubs for j2me which is +no longer supported +these are libraries used by the codename +one designer tool +there are a lot of swing libraries used +here +all of them are open source +this is an open source library we +developed to improve javadoc generation +check out the repos for the project +this is a pre-compiled binary from the +cldc directory in the project +it's here for convenience +this is technically a set of stubs of +the java api supported by codename one +this is a customized version of the +cldc.jar file that exposes +classes.getresources +string which we normally don't want to +expose +the entire directory contains binaries +for the uwp port +i won't go into that because i won't +cover the offline building of the uwp +version here +before we go into building the project +let's inspect the separate pieces +most of the pieces we fetched in the +codename one repository are netbeans +projects +notice that netbeans projects are just +specific folder structures with an ant +build xml file +that means you don't need netbeans in +order to build or run them +the codename one folder includes the +codename one apis +this is just a standard netbeans and +project that contains 100 portable code +notice that at this time this project +still uses java 5 syntax due to some +technical constraints +codename one designer contains the +designer tool which is a swing +application built with a swing app +framework +it's a standard netbeans project too +with major pieces written using the +netbeans matisse gui builder +the vm directory hosts the power vm +source code +it includes two subfolders each of which +is a standalone netbeans and project +the batco translator is a java netbeans +project that reads java bytecode and +generates c code it includes a couple of +built-in c files but most of the c code +is generated +the portion of the vm that isn't a part +of the codename one api +this is a netbeans project that includes +implementation of all the java packages +needed by parker vm +factory is a simple project that +contains one class +com.coname1.impo +dot implementation factory +that lets us decouple the implementation +of codename one from the ports +themes are the native os theme files +which we load when a theme derives from +native +these are embedded into the ports and +into the skins +tests contain unit tests for the +platform +ports are the various open source os +native ports of codename one +and some platform-specific tools +this is where most of the +platform-specific code of codename one +resides +the android directory obviously includes +the android port +the same is true for the ios port +javasc +includes both the desktop port and the +simulator +uwp includes port to universal windows +platform +notice that this also includes our fork +of ikvm which is the vm implementation +for that platform +cldc11 +is not a port but rather a set of +supported stub apis this isn't an actual +vm but rather stubs we use when +compiling codename one apps to make sure +they don't use unsupported apis +retro is an adaptation to the +retroweaver that added support for java +5 code to run on old blackberry j2me +devices +this code is no longer used +the same is true for the other ports +within this directory diff --git a/docs/website/video-transcripts/x-mUTz23Cd4.json b/docs/website/video-transcripts/x-mUTz23Cd4.json new file mode 100644 index 0000000000..9e7756b506 --- /dev/null +++ b/docs/website/video-transcripts/x-mUTz23Cd4.json @@ -0,0 +1,8 @@ +{ + "line_count": 180, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 965, + "youtube_id": "x-mUTz23Cd4" +} diff --git a/docs/website/video-transcripts/x-mUTz23Cd4.txt b/docs/website/video-transcripts/x-mUTz23Cd4.txt new file mode 100644 index 0000000000..397a464bb5 --- /dev/null +++ b/docs/website/video-transcripts/x-mUTz23Cd4.txt @@ -0,0 +1,180 @@ +performance is a pretty complex subject +arguably one of the more complex +subjects i've covered here +so this module requires some patience +How This Module Is Structured +this guide is divided into two distinct +parts +tips on app performance +and how to find the cause of performance +issues by using profiling tools both in +the simulator +and on devices +within that i will also discuss two +separate aspects of performance +speed and ram +those are often conflicting forces +where an optimization of one is the +terminal to the other +i will finish the module with an example +of a performance issue i debugged in the +kitchen sink a while back +but before we go into these +we need to discuss a common theme of +misclassified performance problems +we would on occasion get bug reports or +complaints about performance from +developers +who nested two scrollable containers or +placed a focusable component within +another one +these are ui bugs where the api is told +to perform two conflicting tasks and +makes the wrong choice +the result can feel like a ui that isn't +responsive +but this clearly isn't a performance +problem +it's crucial to check against these +things and verify before going into +performance tuning +What is Performance? +we all know performance when we see it +but it boils down to a few separate +types of performance issues that have +radically different solutions +before we get started i'd like to review +these five big concepts related to +performance +Rendering Performance +this is often defined as frame rate +although i think that term makes more +sense in the gaming industry +this is a very visible +and much discussed issue even though +it's probably responsible for a much +smaller percentage of issues in apps +the performance issue is often expressed +in the feel of the app which seems off +the app will run reasonably well and +perform things fast enough +but animations and scrolling might seem +jumpy usually rendering performance is +impacted uniformly which means you would +see the performance degraded in a +consistent way and it won't improve or +degrade further as you go on rendering +performance issues are usually really +small issues +in order to reach 60 frames per second +we need to render a frame within 16 +milliseconds +that means that a delay of 5 to 10 +milliseconds in code can trigger a +rendering performance problem +Logic Performance +this is a far more common issue +that sometimes triggers rendering issues +it generally means the business logic of +the application +is slowing performance +this issue occurs +if you are doing too many things and +don't leave enough cpu for rendering +this is +generally expressed in a delay or a +bounce within the app +unlike rendering performance +this won't be uniform +a specific thing is taking a long time +and this can usually be isolated +reasonably well +logic performance issues can be solved +by proper use of caching threading +usually delays for these sort of issues +can be in the seconds region and impact +would be very noticeable +Memory Thrashing & Crashes +an app that allocates and discards +memory can trigger a performance penalty +with gc thrashing +it can also cause cache clearance which +will impact cpu performance further +cache clearance means that elements that +we store in cache to increase +performance +are discarded due to low memory +in the zeal to over optimize developers +often overuse memory and as a result end +up with crashes due to out of memory or +with cache that is constantly cleared +a huge piece of the performance puzzle +is perception +do this experiment +create an app with a button that shows a +dialog after a one second delay +when the user taps the button he will +get no indication of the delay +then do the same thing with a two second +delay but replace the button with a +loading +three dots after the tap +the user will perceive the second +version to be more responsive +this is easy to improve in the ui +instead of using tools like infinite +progress show a ui and fill it +dynamically +ideally have cached data from the last +run +ready and then update that data into +place +if a user sees a spinning animation with +nothing happening they will consider the +app slow +if the app loads the data and refreshes +as new data arrives they will consider +the app +fast +i find that when we dig into a lot of +user complaints about performance this +is probably the most common complaint +Pareto Distribution: the 80-20 Rule +i love this quote of donald knuth who +wrote the art of computer programming +books +what was true when he started developing +is still 100 true in this day and age +the real problem is that programmers +have spent +far too much time worrying about +efficiency in the wrong places +and at the wrong times +premature optimization is the root of +all evil +or at least most of it in programming +in other words donald is saying +only optimize when you actually need to +and based on measurements +the first rule of performance is to do +what's right for your app +if you spend time optimizing at an early +stage you will have an unmaintainable +app worse it would be harder to optimize +the app properly +after the fact +in almost all cases of optimization that +i worked through in my career +spanning mobile desktop and servers +optimizations generally boil down to +caching +if you cash in the wrong place or cash +the wrong thing you can cause logic +problems and pay +a performance penalty +eighty percent +of your performance gains will come from +changes to twenty percent of the code +i'd argue the number in the real world +is closer to ninety eight percent to two +percent diff --git a/docs/website/video-transcripts/xCEOuV43Uqw.json b/docs/website/video-transcripts/xCEOuV43Uqw.json new file mode 100644 index 0000000000..0de792ce92 --- /dev/null +++ b/docs/website/video-transcripts/xCEOuV43Uqw.json @@ -0,0 +1,8 @@ +{ + "line_count": 224, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1348, + "youtube_id": "xCEOuV43Uqw" +} diff --git a/docs/website/video-transcripts/xCEOuV43Uqw.txt b/docs/website/video-transcripts/xCEOuV43Uqw.txt new file mode 100644 index 0000000000..7e70591cde --- /dev/null +++ b/docs/website/video-transcripts/xCEOuV43Uqw.txt @@ -0,0 +1,224 @@ +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/video-transcripts/xLa5yeeVsE8.json b/docs/website/video-transcripts/xLa5yeeVsE8.json new file mode 100644 index 0000000000..063e01f476 --- /dev/null +++ b/docs/website/video-transcripts/xLa5yeeVsE8.json @@ -0,0 +1,8 @@ +{ + "line_count": 130, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 816, + "youtube_id": "xLa5yeeVsE8" +} diff --git a/docs/website/video-transcripts/xLa5yeeVsE8.txt b/docs/website/video-transcripts/xLa5yeeVsE8.txt new file mode 100644 index 0000000000..9208fa6bc6 --- /dev/null +++ b/docs/website/video-transcripts/xLa5yeeVsE8.txt @@ -0,0 +1,130 @@ +the sign of form abstracts the whole +process and generalizes most of the +common features in the sign up stages +all of these forms are are handled by +one class sign up form let's start by +reviewing the generic aspect of that +class and then dig into every stage +the sign up form is a form that +encapsulates common functionality in all +of these forms we use the content +container to place the main UI and the +South container for the links on the +bottom +The Constructor is protected as it's +used only within this class +only the content pane should be +scrollable everything else should be +fixed in place +this supports the back behavior when +arrow is shown on Android and back label +is shown in iOS +we give the title a different UI ID when +it's in landscape mode so it can shrink +when we rotate the device +the problems link is added to the bottom +of the UI inside a container so we can +add additional elements there +this is the next button that appears in +each stage the static methods below +create each stage of the wizard +now we have all the infrastructure in +place to build the individual stages of +sign up +the first stage is the terms and +conditions form which we create in the +create terms method +in this form we have some highlighted +text for this we will need the rich text +view control that provides Rich Text +viewing capabilities +we could use a browser component but +that might lead us down a problematic +path of matching fonts behaviors with +the HTML renderer +for this case I'd rather have something +simple +the code for this form is simple +the sign in label appears in the iOS +back button +we Center align the HTML since it +renders into flow layout this is pretty +easy +we need a different label for the next +button +we show the create name form on the next +operation +I left this as a mock-up +clicking the links isn't used but you +can Implement these links in any way you +want +the content is in the center of the form +and uses box layout on the Y Asus we add +elements directly to it simple +next we need to style a lot of elements +for this form +a lot of these will apply to the +following sign up form methods so this +CSS listing will be a bit longer +first let's look at the UI IDs involved +next let's look at the CSS needed for +this +first we need to change two things in +the constants entry +we need to add the next button UI ID to +the list of caps buttons +landscape title UI ID ball indicates +that we should use landscape suffixes +suffixed UI IDs for the title elements +notice that Facebook didn't uppercase +the next button on Android I chose to do +that as it fits better to the platform I +didn't do that for the problems link or +other links as that's not the convention +on Android +let's go over the new additions to the +CSS now the toolbar has a bit of padding +and right the right background color +the title is four millimeters with a +large font this doesn't look like the +more understated Facebook font but I +think it looks better +the title command is smaller than the +title so it won't take up too much space +back command is only used in iOS so we +keep it even smaller so it will fit with +the title which can be a problem with +long text +next we'll discuss the landscape UI IDs +toolbar landscape reduces the padding +further in the toolbar +it also reduces the size of the title +font +if we don't reduce the padding in the +command and title area the title area as +a hole won't shrink +when I initially implemented this I +didn't reduce the sizes of the title +command and back command Styles the font +shrank but the toolbar maintained its +height +I use the component inspector tool in +the simulator to look at the differences +between the preferred height and actual +height of all the elements in order to +figure out which was at fault +we use a bold Center aligned font for +the subheading in the forms +the sign-up process forms have an +off-white color which I took from the +Android version of The Wizard +initially I used a blue button for this +but eventually I decided to create a UI +ID for it as I wanted more spacing from +the top I might change next button in +the future to work better in landscape +by reducing margin in landscape mode +the link at the bottom of the form is +relatively small and blue with this you +can run and see the first signup form +the rest becomes trivial by comparison diff --git a/docs/website/video-transcripts/xzwq4P9ogoU.json b/docs/website/video-transcripts/xzwq4P9ogoU.json new file mode 100644 index 0000000000..76b8157e94 --- /dev/null +++ b/docs/website/video-transcripts/xzwq4P9ogoU.json @@ -0,0 +1,8 @@ +{ + "line_count": 65, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 364, + "youtube_id": "xzwq4P9ogoU" +} diff --git a/docs/website/video-transcripts/xzwq4P9ogoU.txt b/docs/website/video-transcripts/xzwq4P9ogoU.txt new file mode 100644 index 0000000000..913a42530c --- /dev/null +++ b/docs/website/video-transcripts/xzwq4P9ogoU.txt @@ -0,0 +1,65 @@ +the restaurant app maker is an +application that we will build together +based on the restaurant app i introduced +earlier +it will generate a custom application +for a specific restaurant +as i mentioned before we are taking a +depth first strategy where we implement +a narrow user interface and logic +all the way to the server code +so we can test the full process before +we fill in the pieces across the stack +so +we are starting with a relatively simple +baseline where we only include a part of +the ui +due to that i removed almost every +complex detail form and chose to focus +only on the very basic features +i +chose to copy the data model from the +restaurant app +i copied which is the lazy approach +i think it often makes sense to copy at +first and if you find yourself copying +repeatedly +then spend the time to refactor +i see developers trying to over +generalize things that don't need to be +generic +and this often ends up taking +more time than most of us expect +for code specific for the app i added a +new app settings class +this is a class mostly for data related +to the code generation +i did the data model now because it was +already implemented for the most part +just like i did the ui first for the +restaurant app as it was already shown +in the psd +in this case the data model is a good +foundation to build on top +let's try to draw how this will look +we start with the standard codename one +form class as the base class +which i will demonstrate here +base navigation form derives from form +to provide common capabilities such as +the +the common side menu +and other common top level form +abstractions +it will have several subclasses matching +the top level forms but right now we'll +focus on the two common uis +one of them is +the dish list +and the other is the build form +the dish edit form derives from form +directly +as it doesn't include the side menu or +related logic so the base navigation +form doesn't fit +in this case diff --git a/docs/website/video-transcripts/yamsuV5Airc.json b/docs/website/video-transcripts/yamsuV5Airc.json new file mode 100644 index 0000000000..de1d6eb7f2 --- /dev/null +++ b/docs/website/video-transcripts/yamsuV5Airc.json @@ -0,0 +1,8 @@ +{ + "line_count": 227, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 1328, + "youtube_id": "yamsuV5Airc" +} diff --git a/docs/website/video-transcripts/yamsuV5Airc.txt b/docs/website/video-transcripts/yamsuV5Airc.txt new file mode 100644 index 0000000000..a3b6aa2a95 --- /dev/null +++ b/docs/website/video-transcripts/yamsuV5Airc.txt @@ -0,0 +1,227 @@ +now that the server is working we need +to connect the app mockup to it and get +all the pieces working in cohort +since we did most of the work this is +actually simpler than it sounds +i completely rewrote the server api +which was a mock-up beforehand i +considered building an abstraction +similar to the one on the server side +with separate classes for user media and +post but eventually decided this doesn't +warrant that +it might +as the api grows but right now the api +is still simple enough the code is +generally just glue code that doesn't +contain any real logic +the code doesn't change up to here +everything else is different +we have two new variables the url of the +server and the token we use for +authentication +the get method is a shorthand for rest +get methods that already inserts the +right base url token and json headers +for spring boot +post works in the same way and saves us +from a lot of boilerplate code +we can check if a user is logged in by +checking the token value this is useful +as +a key password scenario +the rest of the code is mostly mirror of +the server code +first we have login and signup which are +practically identical so their common +code is encapsulated by one method +both methods are post operations that +submit data to the respective url +a login failure might still invoke the +success callback method +this automatically populates the user +object with the json data from the +server +the token is also saved separately for +convenience +we save the user object in storage as a +json file this means we can just read +that file the next time we launch +saving as json is very convenient as we +can literally read the content of the +file during development and also produce +test cases using this approach +notice we save the json data to storage +not far system storage +storage is generally private to the app +and while it's not secure enough for +your bank account details it should be +reasonably safe for most apps +as i mentioned before signup and login +use the same method with different data +and url +refresh is invoked when we need to fetch +updated user data after making changes +the operation is synchronous so we can +just read the data and store it into +json immediately +the verification step just sends the +code using a get request and expects an +ok response notice this isn't a json +string just the word +ok +update works in a similar way but posts +the user data +notice how much simpler this is than the +signup method +partially due to the synchronous api +notice we save the changes into the +cached local file +when we use this method we would need to +already have a media object id +so set avatar will point at a valid +image +most of these are pretty uniform and +don't bring anything new or interesting +now to do the next two methods for +friend request +a friend request is sent as a get method +as we only need the user id and not much +more +accepting a friend request is +practically the same operation +if there were more than two methods it +would have made sense to +extract the common logic to one method +but for this amount of code it's +probably not worth it +the next stage is a bit more interesting +we need an api to upload the user +contacts +all the heavy lifting is done in the +contacts to json method +this call just posts the data returned +from that method to the url +so let's look at the context to json +method +we need to translate the codename one +contact object +two json strings matching the shadow +user dao object in the server side +the first step is looping over all the +elements to generate the json i chose to +generate it manually as a string as a +quick and dirty solution +we create a map and place the properties +into it we translate the nuanced +contacts object into a simplified object +the logic used here is a bit too +simplistic but it will do +the a more realistic approach would +upload all of the data +the advantage of using this api rather +than just writing the json +is that special characters are escaped +correctly so this is far simpler +the two json api only supports +outputting maps not arrays slash lists +as we need +so this portion of the hud is a +hard-coded string +that's just a bit verbose and slightly +hacky but it works +the next method continues the direction +of upload with media +upload api +for media upload we use the uniform the +four multipart w3c standard which is +built into codename one in the multipart +request class +upload media submits a multipart request +which is a special type of post +operation that encodes binary data as +base64 for upload +this is a callback from connection +request that allows us to read the +response directly to a string notice +this method is invoked on the network +thread +this callback is invoked on the event +dispatch thread +when the connection request finishes +it's important to call the success +method on the edt to avoid threading +issues +add to queue is asynchronous so the +media will upload in the background and +invoke the success callback when ready +uploading an image is a bit challenging +but most of the complexity is hidden by +codename one +the me method returns the logged in user +instance if we are logged in we lazily +initialize the user instance +we load the json file saved during login +signup from the storage +the next step is the list of +notifications which we fetch +synchronously page bar page +you may recall that in the mock-up code +i used timestamps to define the paging +process in the infinite container that +approach works great for some paging +strategies but for the strategy we +picked on the server it's not ideal +in fact the approach on the server is +even simpler than what we did +we just need a page and the size of the +page +we use synchronous api calls +since the infinite container api expects +us to return values +the json passing always returns a map +but the server return returns a list in +this case +for those cases a map with a single +entry called root is implicitly created +we create and populate a notification +object for every entry in the list +if there are no entries or if there was +an error we return null to end the +listing +this method is replicated by other +services that return pageable data +specifically following the following +methods deal with posts +we +have two lists of post objects this +method concentrates the common code from +both of them it's roughly identical to +the notifications method +this is pretty much the only difference +we instantiate post instead of +notification +here we list the post for a specific +user we construct and launch the query +so map the map response returns to the +post method +the newsfeed call is identical with +different urls and it doesn't +need the user id +with that we are nearing the end of the +server api class the last few methods +are again pretty simple methods covering +the actions one can take to submit data +we submit a post object in the body of +the web service and return true if the +post succeeds +a comment object is submitted in much +the same way as a post but it sets the +id of the comment entry added into the +comment object +even though a like receives a post +we only send the post id and use the +post object to keep this type safe +with that we mirrored the current server +changes to the client and can move to +the integration aspect diff --git a/docs/website/video-transcripts/ymocxBIQn0o.json b/docs/website/video-transcripts/ymocxBIQn0o.json new file mode 100644 index 0000000000..63cb46b66e --- /dev/null +++ b/docs/website/video-transcripts/ymocxBIQn0o.json @@ -0,0 +1,8 @@ +{ + "line_count": 61, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 352, + "youtube_id": "ymocxBIQn0o" +} diff --git a/docs/website/video-transcripts/ymocxBIQn0o.txt b/docs/website/video-transcripts/ymocxBIQn0o.txt new file mode 100644 index 0000000000..5239ee2be1 --- /dev/null +++ b/docs/website/video-transcripts/ymocxBIQn0o.txt @@ -0,0 +1,61 @@ +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/video-transcripts/yv3WXt8o88k.json b/docs/website/video-transcripts/yv3WXt8o88k.json new file mode 100644 index 0000000000..347f74f565 --- /dev/null +++ b/docs/website/video-transcripts/yv3WXt8o88k.json @@ -0,0 +1,8 @@ +{ + "line_count": 133, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 697, + "youtube_id": "yv3WXt8o88k" +} diff --git a/docs/website/video-transcripts/yv3WXt8o88k.txt b/docs/website/video-transcripts/yv3WXt8o88k.txt new file mode 100644 index 0000000000..ab5a08ac41 --- /dev/null +++ b/docs/website/video-transcripts/yv3WXt8o88k.txt @@ -0,0 +1,133 @@ +we'll continue with the code for the +dish list +the dishless form is the form that +contains the set of dishes we can select +from +the elements within this form are stored +in a grid layout +you will notice that the number of rows +is hard coded +that is because rows are added +implicitly +as we add more elements +but columns +won't change by default +this means that if we only have one or +two elements +the rest of the places will remain blank +and +the one element we have won't take up +all the space +the floating action button at the bottom +allows us to add a new dish to the set +of existing dishes +notice that we instantly add a dish +and show +the edit ui forcing the user to delete +the dish +if you want to cancel this edition +that's a common practice on mobile +we're dealing with the ok cancel process +is sometimes harder than just doing +something like this +the +add dish method adds a ui element to the +list of dishes +that element includes a black gradient +overlay which makes the white text on +top +readable even with a +white image below +the entire component is a lead component +which means all events within the +container are delegated to the scale +image button +that means that the listener on the +scale image button in the last line will +receive all of the events in the +container hierarchy +we show the dish edit form when editing +a dish +since the dish is instantly created and +edited there is no form for a new dish +and this form doesn't have +two modes like you normally would +or +for edit or create +in this app there is only one mode +edit +title area of +dish edit form is +built of components +because it is large and relatively +custom +i prefer to handle all of the commands +and functionality and code +so the title itself +is a +text field again just like in the main +form +as the digital title can be edited in +place +the back and ok buttons are just +standard buttons with the right ui id +and the icon +this is where it gets interesting +the title component is now really tall +because of the background image +so if i had used a standard add material +command it would have been placed +somewhere along the middle of the layout +instead of +at the top +so to get around this i created a border +layout which effectively occupies +this area +but again +if i'd add the components directly +they will be placed in the middle +vertically instead of at the top as i +would like +so i wrap the two commands with a flow +layout to push them to the top +i wrap the text field with a box layout +though and the reason is a bit more +complicated +flow layout doesn't deal well with +resizable components like text fields +since it tries to break a line when we +run out of space +if the title is too long it will try to +break a line instead of letting the text +field overflow which is what i want +box layout doesn't have that problem +code right here positions the floating +action button +on the border between the title area and +the body +notice that this component is positioned +on top of both and doesn't require a +special empty region +like the icon in the previous form +we bind the floating action button +directly to the layered layout on top +on the top right +which should position it really high in +the form +but we then use this code to set the +margin of the floating action button +so it will match the toolbar height +minus half the height of the floating +back button +this positions it perfectly +last but not least we can see the +component representing the delete button +at the bottom +followed by +stub action handling code for the +various buttons +delete is placed in the south so it will +always appear at the very bottom of the +form regardless of +scrolling thanks for watching +i hope you found this informative diff --git a/docs/website/video-transcripts/zJ7L5fi60H8.json b/docs/website/video-transcripts/zJ7L5fi60H8.json new file mode 100644 index 0000000000..6a56a8f9fc --- /dev/null +++ b/docs/website/video-transcripts/zJ7L5fi60H8.json @@ -0,0 +1,8 @@ +{ + "line_count": 75, + "quality": "needs-review", + "source": "fetched-manual", + "status": "transcript-fetched", + "word_count": 450, + "youtube_id": "zJ7L5fi60H8" +} diff --git a/docs/website/video-transcripts/zJ7L5fi60H8.txt b/docs/website/video-transcripts/zJ7L5fi60H8.txt new file mode 100644 index 0000000000..a11b27e7d1 --- /dev/null +++ b/docs/website/video-transcripts/zJ7L5fi60H8.txt @@ -0,0 +1,75 @@ +the notification service is arguably one +of the simplest services +we have right now +its main reason for existence is push +notification that will integrate into it +later on +as i said the class is trivial and +contains just two methods +the +only repository is the notification +repository as we don't need access to +anything else for the service +send notification is important as this +would be a great location to send push +notification or trigger a +websocket-based push in the future +right now we just save the data to the +database which we would do anyway as we +want to keep track of notifications +this is an api for the client side so it +can list the notifications pending to +the user it accepts a page number so we +can go through the pages of results +when we go through the notification +pages we also pass the sort value to +indicate we want notifications arranged +by date +the return value is the dows +for the current page carrying the +notification data +the send notification api is an api +designed for the server side +notifications only originate within the +server to prevent abuse by malicious +clients +we continue the trend of simple services +with the media service which is almost +as simple as the notification service +the media service works with the media +repository only but it needs to +authenticate some actions against the +user repository +when adding a new media we accept almost +all the values from the client with the +exception of time which we store in the +server to prevent abuse +we need an auth token to make sure the +media is saved under the correct user +we return the media id when we are done +so it can be referenced by the user +public media can be requested via the +idea of the media and doesn't require +any authentication +we need to check that the user is indeed +requesting a public media object and +isn't spoofing a request +this obviously brings up the visibility +constance enum which is pretty trivial +right now we just have two visibility +types for public and friend as you can +see there is public method tests if the +field is public or now +the permission exception is thrown +otherwise +that exception is pretty simple and +again just a marker exception for the +web service layer +finally for media that could potentially +be private we need this method it +accepts the user token and media id +if the media is public then there is no +problem we can just return it +if the media is for friends only we need +to verify that the user requesting the +media is indeed a friend From c4f58d8ce247e30660d1465cb9d30235d880a542 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Tue, 17 Mar 2026 19:10:39 +0200 Subject: [PATCH 2/2] Remove transcript artifacts and cleanup website refresh tooling --- .../001-introduction.md | 6 - .../002-creating-a-hello-world-app.md | 6 - ...003-core-concepts-of-mobile-development.md | 6 - .../004-what-is-codename-one.md | 6 - ...5-anatomy-of-a-codename-one-application.md | 6 - ...6-internationalization-and-localization.md | 6 - .../007-layout-basics.md | 6 - .../008-theme-basics.md | 6 - .../009-adapting-a-ui-design.md | 6 - .../010-storage-filesystem-and-sql.md | 6 - .../011-threading-and-the-edt.md | 6 - .../012-understanding-properties.md | 6 - .../013-push-notification.md | 6 - ...nterfaces-access-native-device-features.md | 6 - .../001-working-with-css.md | 6 - .../002-introduction-to-spring-boot.md | 6 - .../003-connecting-to-a-web-service.md | 6 - .../004-introduction.md | 6 - .../005-cutting-images-in-photoshop.md | 6 - .../006-css.md | 6 - .../007-baseform.md | 6 - .../008-mainmenuform.md | 6 - .../009-checkoutform.md | 6 - .../010-introduction.md | 6 - .../011-the-new-forms.md | 6 - .../012-fixing-the-checkout-experience.md | 6 - .../013-css-changes.md | 6 - .../014-code-changes-and-summary.md | 6 - .../015-overview-and-basic-model.md | 6 - .../016-integration-and-summary.md | 6 - .../017-the-native-interface-callback.md | 6 - .../018-dependencies-gradle-and-cocoapods.md | 6 - .../019-the-native-code.md | 6 - .../020-introduction-and-generic-code.md | 6 - ...plementing-the-native-camera-on-android.md | 6 - .../022-camera-ios-port-basics.md | 6 - .../023-arc-and-view-on-ios.md | 6 - .../024-capture-and-callbacks-in-ios.md | 6 - .../025-communicating-with-the-server.md | 6 - .../026-address-and-validation.md | 6 - .../027-categories-and-search.md | 6 - ...security-basics-and-certificate-pinning.md | 6 - ...e-encryption-and-misc-security-features.md | 6 - .../030-introduction-and-installation.md | 6 - .../031-hello-world-and-devices.md | 6 - ...ers-lightweight-overlays-and-map-layout.md | 6 - .../033-introduction-and-setup.md | 6 - ...nning-the-kitchen-sink-in-the-simulator.md | 6 - ...g-a-desktop-version-of-the-kitchen-sink.md | 6 - ...roid-native-version-of-the-kitchen-sink.md | 6 - ...-ios-native-version-of-the-kitchen-sink.md | 6 - ...s-performance-breaking-down-the-problem.md | 6 - ...t-threading-caching-and-soft-references.md | 6 - ...pes-and-their-impact-on-performance-ram.md | 6 - ...-network-parsing-and-resource-file-size.md | 6 - ...ktop-using-the-performance-monitor-tool.md | 6 - ...43-profiling-on-devices-ios-and-android.md | 6 - ...erformance-problems-in-the-kitchen-sink.md | 6 - .../001-server.md | 6 - .../002-scope-and-basic-ui-design.md | 6 - .../003-fleshing-out-the-ui-design.md | 6 - .../004-architecture-of-mockup.md | 6 - ...-base-navigation-form-and-shape-effects.md | 6 - .../006-dish-list-and-edit.md | 6 - ...oduction-architecture-and-authorization.md | 6 - .../008-rest-api-design.md | 6 - .../009-communicating-from-the-client.md | 6 - ...traction-with-object-relational-mapping.md | 6 - .../011-integrating-sqlite-into-the-code.md | 6 - .../012-details-categories-and-validation.md | 6 - .../013-billing-and-global-server.md | 6 - .../014-sidemenu-and-preview.md | 6 - .../015-about-forms.md | 6 - ...customization-1-introduction-and-basics.md | 6 - ...customization-2-the-customization-popup.md | 6 - ...-customization-3-font-and-color-pickers.md | 6 - ...e-customization-4-saving-style-settings.md | 6 - ...020-push-1-initial-registration-process.md | 6 - .../021-push-2-client-side-code.md | 6 - ...-push-3-the-server-side-and-build-logic.md | 6 - .../023-push-http-fallback.md | 6 - .../024-push-websockets-fallback.md | 6 - .../025-in-app-purchase.md | 6 - .../026-setting-up-the-vps-server.md | 6 - .../027-yum-mariadb-security-and-iptables.md | 6 - .../028-starting-the-server-on-boot.md | 6 - ...let-s-encrypt-https-certificate-support.md | 6 - ...automating-lets-encrypt-renewal-process.md | 6 - .../031-abstraction-and-architecture.md | 6 - .../032-the-uiabstraction-class.md | 6 - .../033-the-tabletui-class.md | 6 - .../034-putting-it-all-together.md | 6 - .../035-transitions.md | 6 - .../036-layout-animations.md | 6 - ...yle-animations-and-low-level-animations.md | 6 - .../038-1-introduction.md | 2 - .../039-2-basic-setup.md | 2 - .../040-3-login-and-country-code.md | 2 - .../041-4-login-shadow-and-rotation.md | 2 - .../042-5-social-login-and-country-picker.md | 2 - .../043-6-sms-activation-flow.md | 2 - .../044-7-map-form.md | 2 - .../045-8-where-to-ui.md | 2 - .../046-9-where-to-ui-part-ii.md | 2 - .../047-10-side-menu.md | 2 - .../048-11-the-spring-boot-server.md | 2 - .../049-12-server-websocket-handler.md | 2 - .../050-13-client-side-userservice.md | 2 - .../051-14-sms-activation-and-interception.md | 2 - .../052-15-location-service-client-side.md | 2 - ...ng-the-location-service-to-the-map-form.md | 2 - ...4-17-reverse-gecoding-google-webservice.md | 2 - ...rections-and-places-google-web-services.md | 2 - ...056-19-auto-complete-location-search-ui.md | 2 - .../057-20-search-completion-container.md | 2 - ...-21-plotting-the-route-on-the-map-setup.md | 2 - ...tting-the-route-on-the-map-to-from-tags.md | 2 - ...lotting-the-route-on-the-map-completion.md | 2 - ...-hailing-in-the-client-showing-a-beacon.md | 2 - ...nt-networking-and-sending-push-messages.md | 2 - .../063-26-driver-app-server.md | 2 - ...-27-driver-app-server-websocket-portion.md | 2 - ...28-the-driver-app-2-apps-in-one-project.md | 2 - ...ng-the-driver-app-and-push-notification.md | 2 - .../067-30-driver-and-user-hailing-process.md | 2 - ...raintree-flow-explained-and-server-side.md | 2 - ...69-32-braintree-client-side-integration.md | 2 - ...33-social-login-basics-and-facebook-app.md | 2 - .../071-34-facebook-and-google-login-code.md | 2 - .../072-35-google-login-process.md | 2 - ...sition-animating-elements-between-forms.md | 2 - ...conditionally-showing-a-form-transition.md | 2 - ...rcular-floating-action-button-animation.md | 6 - ...ings-form-and-fetching-the-avatar-image.md | 6 - ...r-ui-binding-and-multipart-image-upload.md | 6 - .../078-1-introduction.md | 6 - .../079-2-creating-the-project-and-css.md | 6 - .../080-3-splash-screen.md | 6 - .../081-4-login-form.md | 6 - .../082-5-rich-text-view-and-signup-form.md | 6 - .../083-6-signup-form-terms-and-conditions.md | 6 - ...-7-signup-form-name-birthday-and-gender.md | 6 - ...m-phone-email-password-and-confirmation.md | 6 - .../086-9-the-main-form.md | 6 - ...client-data-model-user-post-and-comment.md | 6 - .../088-11-serverapi-abstraction-mockup.md | 6 - .../089-12-the-newsfeed-container.md | 6 - .../090-13-friends-container.md | 6 - .../091-14-notifications-container.md | 6 - .../092-15-the-more-container.md | 6 - .../093-16-the-new-post-form.md | 6 - ...server-architecture-and-the-user-entity.md | 6 - .../095-18-media-entity.md | 6 - .../096-19-post-and-comment-entities.md | 6 - ...cation-newsfeed-and-shadowuser-entities.md | 6 - .../098-21-service-layer-and-userservice.md | 6 - .../099-22-userservice-part-ii.md | 6 - ...23-notificationservice-and-mediaservice.md | 6 - .../101-24-postservice.md | 6 - ...-25-webservice-layer-and-userwebservice.md | 6 - ...3-26-postwebservice-and-mediawebservice.md | 6 - .../104-27-client-side-serverapi.md | 6 - .../105-28-client-server-signup-process.md | 6 - .../106-29-newsfeed-and-posts-from-server.md | 6 - ...-synchronization-accept-reject-requests.md | 6 - ...ver-side-with-spring-boot-and-hibernate.md | 6 - ...09-32-search-webservice-and-client-code.md | 6 - ...110-33-search-client-side-ui-searchform.md | 6 - ...search-results-ui-userform-and-postform.md | 6 - ...12-35-threaded-comments-ui-commentsform.md | 6 - .../113-36-settingsform-cover-and-avatar.md | 6 - ...stantui-automatic-dynamic-ui-generation.md | 6 - ...15-38-server-side-post-media-attachment.md | 6 - ...39-imagepicker-video-and-custom-support.md | 6 - ...-attachments-client-side-business-logic.md | 6 - ...1-post-image-and-video-from-newpostform.md | 6 - ...videos-and-styled-posts-in-the-newsfeed.md | 6 - .../120-43-low-level-camera-integration.md | 6 - ...cation-theory-entity-and-service-layers.md | 2 - ...push-notification-server-implementation.md | 2 - .../123-46-push-client-side-integration.md | 2 - .../124-1-getting-started.md | 2 - .../125-2-client-to-server-abstraction.md | 2 - .../126-3-the-model-package.md | 2 - .../127-4-the-main-class.md | 2 - .../128-5-main-form.md | 2 - .../129-6-theme-css.md | 2 - .../130-7-bubble-border.md | 2 - .../131-8-chat-form.md | 2 - .../132-9-the-new-message-form.md | 2 - .../133-10-server-entities.md | 2 - .../134-11-server-dao-and-entities.md | 2 - .../135-12-user-service.md | 2 - .../136-13-user-web-service.md | 2 - .../137-14-web-socket.md | 2 - .../138-introduction.md | 2 - .../139-server-part-i.md | 2 - .../140-server-part-ii.md | 2 - .../141-server-part-iii.md | 2 - .../142-client-model.md | 2 - .../143-client-ui.md | 2 - ...-functionality-invoke-native-interfaces.md | 6 - ...rvices-perform-operations-on-the-server.md | 6 - .../how-do-i-create-a-9-piece-image-border.md | 6 - ...tion-send-it-to-my-device-using-eclipse.md | 6 - ...end-it-to-my-device-using-intellij-idea.md | 6 - ...ion-send-it-to-my-device-using-netbeans.md | 6 - ...o-i-create-a-list-of-items-the-easy-way.md | 6 - .../howdoi/how-do-i-create-a-simple-theme.md | 6 - ...do-i-create-an-ios-provisioning-profile.md | 6 - .../how-do-i-create-gorgeous-sidemenu.md | 6 - ...one-source-modify-it-contribute-it-back.md | 6 - .../how-do-i-debug-on-an-android-device.md | 6 - ...from-the-resource-file-add-a-multiimage.md | 6 - ...me-one-tools-and-the-standard-ide-tools.md | 6 - ...codename-one-use-the-versioning-feature.md | 6 - ...gui-builder-populate-the-form-from-code.md | 6 - ...rmance-or-track-down-performance-issues.md | 6 - ...ernationalizationlocalization-to-my-app.md | 6 - ...ioning-components-using-layout-managers.md | 6 - ...how-do-i-take-a-picture-with-the-camera.md | 6 - .../howdoi/how-do-i-use-cloud-connect.md | 6 - ...-i-use-crash-protection-get-device-logs.md | 6 - .../how-do-i-use-desktop-javascript-ports.md | 6 - ...use-http-sockets-webservices-websockets.md | 6 - .../howdoi/how-do-i-use-offline-build.md | 6 - ...o-i-use-properties-to-speed-development.md | 6 - ...-notification-send-server-push-messages.md | 6 - .../how-do-i-use-storage-file-system-sql.md | 6 - ...debug-the-native-code-on-iosandroid-etc.md | 6 - .../__pycache__/split_courses.cpython-313.pyc | Bin 11795 -> 0 bytes .../bootstrap_video_update_sections.py | 113 ---- .../scripts/build_video_refresh_inventory.py | 221 ------- .../scripts/download_youtube_captions.py | 272 -------- .../scripts/fetch_missing_transcripts.py | 243 -------- docs/website/scripts/inject_transcripts.py | 104 ---- .../scripts/normalize_transcript_cache.py | 43 -- .../scripts/report_video_refresh_progress.py | 43 -- docs/website/scripts/video_refresh_lib.py | 283 --------- .../video-transcripts/-7XHkBMK4NY.json | 8 - .../website/video-transcripts/-7XHkBMK4NY.txt | 120 ---- .../video-transcripts/-M957AAi-vk.json | 9 - .../website/video-transcripts/-M957AAi-vk.txt | 48 -- .../video-transcripts/-aTpqxDa1Ag.json | 8 - .../website/video-transcripts/-aTpqxDa1Ag.txt | 117 ---- .../video-transcripts/-brmZYVWb0Y.json | 8 - .../website/video-transcripts/-brmZYVWb0Y.txt | 194 ------ .../video-transcripts/-f1yue3hDEk.json | 10 - .../website/video-transcripts/-f1yue3hDEk.txt | 162 ----- .../video-transcripts/008AK1GfHA8.json | 9 - .../website/video-transcripts/008AK1GfHA8.txt | 18 - .../video-transcripts/0ETli_N__ZY.json | 8 - .../website/video-transcripts/0ETli_N__ZY.txt | 46 -- .../video-transcripts/0a6mPI412C4.json | 8 - .../website/video-transcripts/0a6mPI412C4.txt | 114 ---- .../video-transcripts/0l4R049tSOY.json | 8 - .../website/video-transcripts/0l4R049tSOY.txt | 52 -- .../video-transcripts/0m7Bay4g93k.json | 9 - .../website/video-transcripts/0m7Bay4g93k.txt | 50 -- .../video-transcripts/16-Vkgcx2kg.json | 10 - .../website/video-transcripts/16-Vkgcx2kg.txt | 147 ----- .../video-transcripts/17ISIksjcPM.json | 10 - .../website/video-transcripts/17ISIksjcPM.txt | 266 -------- .../video-transcripts/1wHGnmO-vtE.json | 8 - .../website/video-transcripts/1wHGnmO-vtE.txt | 155 ----- .../video-transcripts/2nD75pODPWk.json | 8 - .../website/video-transcripts/2nD75pODPWk.txt | 139 ----- .../video-transcripts/2xKvvv7XoVQ.json | 9 - .../website/video-transcripts/2xKvvv7XoVQ.txt | 55 -- .../video-transcripts/3-ZH2IFIIMY.json | 8 - .../website/video-transcripts/3-ZH2IFIIMY.txt | 187 ------ .../video-transcripts/32mkZymqa6E.json | 9 - .../website/video-transcripts/32mkZymqa6E.txt | 49 -- .../video-transcripts/3B7C0ZbV8Bc.json | 8 - .../website/video-transcripts/3B7C0ZbV8Bc.txt | 338 ---------- .../video-transcripts/3IC2qZ3wUO4.json | 8 - .../website/video-transcripts/3IC2qZ3wUO4.txt | 132 ---- .../video-transcripts/3rLf9A9RYPY.json | 8 - .../website/video-transcripts/3rLf9A9RYPY.txt | 206 ------- .../video-transcripts/46TpE-Cgtw8.json | 8 - .../website/video-transcripts/46TpE-Cgtw8.txt | 143 ----- .../video-transcripts/47WhIiLxv78.json | 8 - .../website/video-transcripts/47WhIiLxv78.txt | 189 ------ .../video-transcripts/4D_KUa2qv2o.json | 9 - .../website/video-transcripts/4D_KUa2qv2o.txt | 55 -- .../video-transcripts/4jIqplr19HA.json | 8 - .../website/video-transcripts/4jIqplr19HA.txt | 117 ---- .../video-transcripts/54POZ4PFFBw.json | 8 - .../website/video-transcripts/54POZ4PFFBw.txt | 126 ---- .../video-transcripts/5EyrMpQqR-k.json | 8 - .../website/video-transcripts/5EyrMpQqR-k.txt | 162 ----- .../video-transcripts/5WpZmIkdUfs.json | 8 - .../website/video-transcripts/5WpZmIkdUfs.txt | 142 ----- .../video-transcripts/5eMvwRcDcug.json | 10 - .../website/video-transcripts/5eMvwRcDcug.txt | 209 ------- .../video-transcripts/65LciCzyRNQ.json | 8 - .../website/video-transcripts/65LciCzyRNQ.txt | 160 ----- .../video-transcripts/65jD9oGw61w.json | 8 - .../website/video-transcripts/65jD9oGw61w.txt | 404 ------------ .../video-transcripts/6oTy-LcTm0s.json | 9 - .../website/video-transcripts/6oTy-LcTm0s.txt | 55 -- .../video-transcripts/73d65cvyQv4.json | 9 - .../website/video-transcripts/73d65cvyQv4.txt | 35 -- .../video-transcripts/77N2t2n8rbQ.json | 9 - .../website/video-transcripts/77N2t2n8rbQ.txt | 27 - .../video-transcripts/7CoD9u6KM2Q.json | 8 - .../website/video-transcripts/7CoD9u6KM2Q.txt | 289 --------- .../video-transcripts/7G4OkjgTWIQ.json | 10 - .../website/video-transcripts/7G4OkjgTWIQ.txt | 159 ----- .../website/video-transcripts/7XHkBMK4NY.json | 8 - docs/website/video-transcripts/7XHkBMK4NY.txt | 120 ---- .../video-transcripts/85MyytvMS9I.json | 8 - .../website/video-transcripts/85MyytvMS9I.txt | 226 ------- .../video-transcripts/8WER-8R0WqA.json | 8 - .../website/video-transcripts/8WER-8R0WqA.txt | 126 ---- .../video-transcripts/8fxZVc1hw6Q.json | 8 - .../website/video-transcripts/8fxZVc1hw6Q.txt | 343 ----------- .../video-transcripts/8wzBpEp81Kc.json | 9 - .../website/video-transcripts/8wzBpEp81Kc.txt | 81 --- .../video-transcripts/91BrBoia4nM.json | 8 - .../website/video-transcripts/91BrBoia4nM.txt | 161 ----- .../video-transcripts/99DAeP9LG6c.json | 9 - .../website/video-transcripts/99DAeP9LG6c.txt | 44 -- .../video-transcripts/9T8MBBuWBDs.json | 8 - .../website/video-transcripts/9T8MBBuWBDs.txt | 110 ---- .../video-transcripts/A72rY4rU7E0.json | 8 - .../website/video-transcripts/A72rY4rU7E0.txt | 215 ------- .../video-transcripts/ACsZ8qiwR8Q.json | 9 - .../website/video-transcripts/ACsZ8qiwR8Q.txt | 76 --- .../video-transcripts/AFvuY7Ev-XA.json | 8 - .../website/video-transcripts/AFvuY7Ev-XA.txt | 66 -- .../video-transcripts/AIsD5MXEK-c.json | 8 - .../website/video-transcripts/AIsD5MXEK-c.txt | 80 --- .../video-transcripts/AMlnslUn1bA.json | 8 - .../website/video-transcripts/AMlnslUn1bA.txt | 78 --- .../video-transcripts/B4QCJRFpG-k.json | 8 - .../website/video-transcripts/B4QCJRFpG-k.txt | 126 ---- .../video-transcripts/BSj3KRM5Sj0.json | 8 - .../website/video-transcripts/BSj3KRM5Sj0.txt | 75 --- .../video-transcripts/BSrupbUahRM.json | 8 - .../website/video-transcripts/BSrupbUahRM.txt | 43 -- .../video-transcripts/BXwh2T7wvfc.json | 8 - .../website/video-transcripts/BXwh2T7wvfc.txt | 92 --- .../video-transcripts/BY1lMQz873g.json | 8 - .../website/video-transcripts/BY1lMQz873g.txt | 193 ------ .../video-transcripts/BbVoa3vw7OM.json | 8 - .../website/video-transcripts/BbVoa3vw7OM.txt | 157 ----- .../video-transcripts/BwWhFcHc6LM.json | 8 - .../website/video-transcripts/BwWhFcHc6LM.txt | 157 ----- .../video-transcripts/C3PLjAWQ-XA.json | 9 - .../website/video-transcripts/C3PLjAWQ-XA.txt | 20 - .../video-transcripts/Cc8YG-_M02M.json | 8 - .../website/video-transcripts/Cc8YG-_M02M.txt | 172 ------ .../video-transcripts/CvbcWC4hLYk.json | 8 - .../website/video-transcripts/CvbcWC4hLYk.txt | 64 -- .../video-transcripts/DWifLQZyPDE.json | 8 - .../website/video-transcripts/DWifLQZyPDE.txt | 362 ----------- .../video-transcripts/Dh1bYqiKBQo.json | 8 - .../website/video-transcripts/Dh1bYqiKBQo.txt | 116 ---- .../video-transcripts/DyzEgAGyRcA.json | 8 - .../website/video-transcripts/DyzEgAGyRcA.txt | 84 --- .../video-transcripts/EkiTDQn9Cpg.json | 8 - .../website/video-transcripts/EkiTDQn9Cpg.txt | 388 ------------ .../video-transcripts/FKTM8jAepJs.json | 8 - .../website/video-transcripts/FKTM8jAepJs.txt | 98 --- .../video-transcripts/FSek5IOQ0IE.json | 8 - .../website/video-transcripts/FSek5IOQ0IE.txt | 245 -------- .../video-transcripts/FcHVK9ObONg.json | 8 - .../website/video-transcripts/FcHVK9ObONg.txt | 105 ---- .../video-transcripts/FczBAgUIb1c.json | 8 - .../website/video-transcripts/FczBAgUIb1c.txt | 64 -- .../video-transcripts/GEG_S-dyxaM.json | 8 - .../website/video-transcripts/GEG_S-dyxaM.txt | 65 -- .../video-transcripts/GEzM1MXkqnk.json | 8 - .../website/video-transcripts/GEzM1MXkqnk.txt | 93 --- .../video-transcripts/GMnWMB_JfaQ.json | 8 - .../website/video-transcripts/GMnWMB_JfaQ.txt | 130 ---- .../video-transcripts/H7WW4GwXK8o.json | 8 - .../website/video-transcripts/H7WW4GwXK8o.txt | 45 -- .../video-transcripts/HJvMQKM5FPY.json | 8 - .../website/video-transcripts/HJvMQKM5FPY.txt | 49 -- .../video-transcripts/HRTLnLecoMA.json | 10 - .../website/video-transcripts/HRTLnLecoMA.txt | 110 ---- .../video-transcripts/IaalGP1UDeU.json | 8 - .../website/video-transcripts/IaalGP1UDeU.txt | 44 -- .../video-transcripts/IqsUSCgSVTo.json | 9 - .../website/video-transcripts/IqsUSCgSVTo.txt | 62 -- .../video-transcripts/IxVUX0s2Nms.json | 8 - .../website/video-transcripts/IxVUX0s2Nms.txt | 136 ---- .../video-transcripts/JWLsSbbo4N4.json | 8 - .../website/video-transcripts/JWLsSbbo4N4.txt | 83 --- .../video-transcripts/JZvrsZzdays.json | 8 - .../website/video-transcripts/JZvrsZzdays.txt | 93 --- .../video-transcripts/Jk3tTyZroP0.json | 8 - .../website/video-transcripts/Jk3tTyZroP0.txt | 162 ----- .../video-transcripts/KckMUmmSzd4.json | 8 - .../website/video-transcripts/KckMUmmSzd4.txt | 182 ------ .../video-transcripts/Ke8bjFdD1bc.json | 8 - .../website/video-transcripts/Ke8bjFdD1bc.txt | 95 --- .../video-transcripts/KhSWyE6rAN8.json | 8 - .../website/video-transcripts/KhSWyE6rAN8.txt | 95 --- .../video-transcripts/L7ulPCUxIsw.json | 8 - .../website/video-transcripts/L7ulPCUxIsw.txt | 121 ---- .../video-transcripts/LFoSl_a2rs8.json | 8 - .../website/video-transcripts/LFoSl_a2rs8.txt | 283 --------- .../video-transcripts/LUHb5fuWzJE.json | 8 - .../website/video-transcripts/LUHb5fuWzJE.txt | 171 ------ .../video-transcripts/LZ_ydua__gY.json | 8 - .../website/video-transcripts/LZ_ydua__gY.txt | 74 --- .../video-transcripts/Lai--eYYJTw.json | 8 - .../website/video-transcripts/Lai--eYYJTw.txt | 148 ----- .../video-transcripts/M518p4_Horg.json | 8 - .../website/video-transcripts/M518p4_Horg.txt | 104 ---- .../video-transcripts/MLbOdCt67Rw.json | 10 - .../website/video-transcripts/MLbOdCt67Rw.txt | 63 -- .../video-transcripts/MbTEz-K8iwY.json | 10 - .../website/video-transcripts/MbTEz-K8iwY.txt | 193 ------ .../video-transcripts/Mcw8z_uP3BA.json | 10 - .../website/video-transcripts/Mcw8z_uP3BA.txt | 273 -------- .../video-transcripts/NVPIXnkoxv0.json | 9 - .../website/video-transcripts/NVPIXnkoxv0.txt | 29 - .../video-transcripts/Nv_0NVgCbSk.json | 8 - .../website/video-transcripts/Nv_0NVgCbSk.txt | 139 ----- .../video-transcripts/OWHizrNyizQ.json | 8 - .../website/video-transcripts/OWHizrNyizQ.txt | 29 - .../video-transcripts/OnZIaGXDZSo.json | 8 - .../website/video-transcripts/OnZIaGXDZSo.txt | 154 ----- .../video-transcripts/Otujb_KyofA.json | 8 - .../website/video-transcripts/Otujb_KyofA.txt | 176 ------ .../video-transcripts/PERdJq3I-As.json | 8 - .../website/video-transcripts/PERdJq3I-As.txt | 77 --- .../video-transcripts/PHb5YO77OQk.json | 8 - .../website/video-transcripts/PHb5YO77OQk.txt | 120 ---- .../video-transcripts/PRk7EhXQRqs.json | 8 - .../website/video-transcripts/PRk7EhXQRqs.txt | 213 ------- .../video-transcripts/PbRbzCGQCQE.json | 10 - .../website/video-transcripts/PbRbzCGQCQE.txt | 151 ----- .../video-transcripts/Q0fkApAGMZA.json | 8 - .../website/video-transcripts/Q0fkApAGMZA.txt | 125 ---- .../video-transcripts/QY7josfVbdg.json | 8 - .../website/video-transcripts/QY7josfVbdg.txt | 246 -------- .../video-transcripts/Qw7moLv-dE4.json | 8 - .../website/video-transcripts/Qw7moLv-dE4.txt | 152 ----- .../video-transcripts/RBYGdCllnww.json | 8 - .../website/video-transcripts/RBYGdCllnww.txt | 61 -- .../video-transcripts/Rat-FDqDCTU.json | 8 - .../website/video-transcripts/Rat-FDqDCTU.txt | 103 ---- .../video-transcripts/Rok0Ubr4xes.json | 8 - .../website/video-transcripts/Rok0Ubr4xes.txt | 116 ---- .../video-transcripts/RsVOanGYjqo.json | 10 - .../website/video-transcripts/RsVOanGYjqo.txt | 156 ----- .../video-transcripts/Rt68rA2c6A8.json | 8 - .../website/video-transcripts/Rt68rA2c6A8.txt | 40 -- .../video-transcripts/S1SHZLXQxSI.json | 8 - .../website/video-transcripts/S1SHZLXQxSI.txt | 148 ----- .../video-transcripts/S5pAGkV4h4w.json | 8 - .../website/video-transcripts/S5pAGkV4h4w.txt | 341 ---------- .../video-transcripts/SBqYZSdIdec.json | 8 - .../website/video-transcripts/SBqYZSdIdec.txt | 118 ---- .../video-transcripts/T8l7k2OeGpo.json | 8 - .../website/video-transcripts/T8l7k2OeGpo.txt | 75 --- .../video-transcripts/TOWhbEhiRe4.json | 8 - .../website/video-transcripts/TOWhbEhiRe4.txt | 100 --- .../video-transcripts/TRF76P1Dwwc.json | 8 - .../website/video-transcripts/TRF76P1Dwwc.txt | 105 ---- .../video-transcripts/U0Ms65vcVr4.json | 8 - .../website/video-transcripts/U0Ms65vcVr4.txt | 145 ----- .../video-transcripts/UU6HCbenVAA.json | 8 - .../website/video-transcripts/UU6HCbenVAA.txt | 153 ----- .../video-transcripts/UhLFHJj8qnM.json | 8 - .../website/video-transcripts/UhLFHJj8qnM.txt | 143 ----- .../video-transcripts/UwdI_tMqYgU.json | 8 - .../website/video-transcripts/UwdI_tMqYgU.txt | 88 --- .../video-transcripts/UxATbrfWveU.json | 8 - .../website/video-transcripts/UxATbrfWveU.txt | 117 ---- .../video-transcripts/UxZ1HeheGwU.json | 10 - .../website/video-transcripts/UxZ1HeheGwU.txt | 335 ---------- .../video-transcripts/VCNFKMeud-w.json | 8 - .../website/video-transcripts/VCNFKMeud-w.txt | 117 ---- .../video-transcripts/VDyltmk_Hu8.json | 8 - .../website/video-transcripts/VDyltmk_Hu8.txt | 58 -- .../video-transcripts/VnYaxvVn6OA.json | 8 - .../website/video-transcripts/VnYaxvVn6OA.txt | 49 -- .../video-transcripts/WT4cFceBWRA.json | 8 - .../website/video-transcripts/WT4cFceBWRA.txt | 581 ------------------ .../video-transcripts/WYlM2utu4Ps.json | 8 - .../website/video-transcripts/WYlM2utu4Ps.txt | 32 - .../video-transcripts/W_1S2Rzgff8.json | 8 - .../website/video-transcripts/W_1S2Rzgff8.txt | 99 --- .../video-transcripts/Wiy2goFwfQA.json | 8 - .../website/video-transcripts/Wiy2goFwfQA.txt | 30 - .../video-transcripts/XSztvFzQAr0.json | 8 - .../website/video-transcripts/XSztvFzQAr0.txt | 392 ------------ .../video-transcripts/Xrolmjg8Dr0.json | 8 - .../website/video-transcripts/Xrolmjg8Dr0.txt | 43 -- .../video-transcripts/YE7OQFQE0yA.json | 8 - .../website/video-transcripts/YE7OQFQE0yA.txt | 86 --- .../video-transcripts/YwD35TG6WAg.json | 8 - .../website/video-transcripts/YwD35TG6WAg.txt | 92 --- .../video-transcripts/YwkwqXkHGwg.json | 8 - .../website/video-transcripts/YwkwqXkHGwg.txt | 103 ---- .../video-transcripts/ZEe_hb1Lz6Y.json | 8 - .../website/video-transcripts/ZEe_hb1Lz6Y.txt | 224 ------- .../video-transcripts/ZzSCPcnFkxs.json | 8 - .../website/video-transcripts/ZzSCPcnFkxs.txt | 105 ---- .../video-transcripts/_EXEN52wQvs.json | 9 - .../website/video-transcripts/_EXEN52wQvs.txt | 31 - .../video-transcripts/_JCsyyIH02E.json | 8 - .../website/video-transcripts/_JCsyyIH02E.txt | 126 ---- .../video-transcripts/_WKRuO0Izxg.json | 8 - .../website/video-transcripts/_WKRuO0Izxg.txt | 76 --- .../video-transcripts/a6q1M_wqEYY.json | 8 - .../website/video-transcripts/a6q1M_wqEYY.txt | 171 ------ .../video-transcripts/a7MF3oSCASE.json | 8 - .../website/video-transcripts/a7MF3oSCASE.txt | 70 --- .../video-transcripts/aNFKBDeky2s.json | 8 - .../website/video-transcripts/aNFKBDeky2s.txt | 127 ---- .../video-transcripts/aVkeOIx-Is8.json | 8 - .../website/video-transcripts/aVkeOIx-Is8.txt | 178 ------ .../video-transcripts/aW3OlGTmasw.json | 8 - .../website/video-transcripts/aW3OlGTmasw.txt | 116 ---- .../video-transcripts/aZ2Y74xPj_0.json | 8 - .../website/video-transcripts/aZ2Y74xPj_0.txt | 87 --- .../video-transcripts/anfVMvBXXX0.json | 8 - .../website/video-transcripts/anfVMvBXXX0.txt | 150 ----- .../video-transcripts/bH7cS5ENNlw.json | 8 - .../website/video-transcripts/bH7cS5ENNlw.txt | 75 --- .../video-transcripts/bU6pZPs3uto.json | 8 - .../website/video-transcripts/bU6pZPs3uto.txt | 110 ---- .../video-transcripts/cRqvkIpJlkg.json | 8 - .../website/video-transcripts/cRqvkIpJlkg.txt | 206 ------- .../video-transcripts/cZiHmpw2FVI.json | 8 - .../website/video-transcripts/cZiHmpw2FVI.txt | 62 -- .../video-transcripts/csTtSj6TqRE.json | 8 - .../website/video-transcripts/csTtSj6TqRE.txt | 290 --------- .../video-transcripts/cxllJwt10VU.json | 10 - .../website/video-transcripts/cxllJwt10VU.txt | 141 ----- .../video-transcripts/d1T25sbFUKE.json | 10 - .../website/video-transcripts/d1T25sbFUKE.txt | 102 --- .../video-transcripts/exL7bS0StP4.json | 10 - .../website/video-transcripts/exL7bS0StP4.txt | 174 ------ .../video-transcripts/exQ8xXAxtoU.json | 8 - .../website/video-transcripts/exQ8xXAxtoU.txt | 103 ---- .../video-transcripts/fJD45Mz8SZM.json | 8 - .../website/video-transcripts/fJD45Mz8SZM.txt | 98 --- .../video-transcripts/fmNpMFLwABA.json | 8 - .../website/video-transcripts/fmNpMFLwABA.txt | 54 -- .../video-transcripts/gJoJQST5jyM.json | 8 - .../website/video-transcripts/gJoJQST5jyM.txt | 426 ------------- .../video-transcripts/gM-InTtdhVE.json | 8 - .../website/video-transcripts/gM-InTtdhVE.txt | 177 ------ .../video-transcripts/gmqFd2bU_fM.json | 8 - .../website/video-transcripts/gmqFd2bU_fM.txt | 109 ---- .../video-transcripts/h-I62aFYBNA.json | 8 - .../website/video-transcripts/h-I62aFYBNA.txt | 78 --- .../video-transcripts/hCjmHoktlrU.json | 9 - .../website/video-transcripts/hCjmHoktlrU.txt | 43 -- .../video-transcripts/hwsWdhJHl8Q.json | 8 - .../website/video-transcripts/hwsWdhJHl8Q.txt | 377 ------------ .../video-transcripts/hys6Rpkru50.json | 8 - .../website/video-transcripts/hys6Rpkru50.txt | 95 --- .../video-transcripts/iCY64ThCleI.json | 8 - .../website/video-transcripts/iCY64ThCleI.txt | 403 ------------ .../video-transcripts/jR7_OTg-aG0.json | 8 - .../website/video-transcripts/jR7_OTg-aG0.txt | 27 - .../video-transcripts/jVuNrLw4e-A.json | 10 - .../website/video-transcripts/jVuNrLw4e-A.txt | 199 ------ .../video-transcripts/jhPep9vHv_U.json | 8 - .../website/video-transcripts/jhPep9vHv_U.txt | 109 ---- .../video-transcripts/kfKsVAx659s.json | 8 - .../website/video-transcripts/kfKsVAx659s.txt | 48 -- .../video-transcripts/kjUFJro0yUQ.json | 8 - .../website/video-transcripts/kjUFJro0yUQ.txt | 283 --------- .../video-transcripts/l1TPKuHYr9c.json | 10 - .../website/video-transcripts/l1TPKuHYr9c.txt | 76 --- .../video-transcripts/l97sDefhHMM.json | 8 - .../website/video-transcripts/l97sDefhHMM.txt | 143 ----- .../video-transcripts/m43A68sNcvg.json | 10 - .../website/video-transcripts/m43A68sNcvg.txt | 82 --- .../video-transcripts/mFFjxs9EDW8.json | 8 - .../website/video-transcripts/mFFjxs9EDW8.txt | 93 --- .../video-transcripts/mqNtfN2C3fM.json | 8 - .../website/video-transcripts/mqNtfN2C3fM.txt | 117 ---- .../video-transcripts/nF4eqzVcsic.json | 8 - .../website/video-transcripts/nF4eqzVcsic.txt | 92 --- .../video-transcripts/nImSppBdgkY.json | 8 - .../website/video-transcripts/nImSppBdgkY.txt | 186 ------ .../video-transcripts/oR3KHYf5OrY.json | 9 - .../website/video-transcripts/oR3KHYf5OrY.txt | 37 -- .../video-transcripts/owhInk5YAtg.json | 8 - .../website/video-transcripts/owhInk5YAtg.txt | 184 ------ .../video-transcripts/p05yMCleSLo.json | 8 - .../website/video-transcripts/p05yMCleSLo.txt | 139 ----- .../video-transcripts/p6UFNw0nGik.json | 8 - .../website/video-transcripts/p6UFNw0nGik.txt | 166 ----- .../video-transcripts/q4YjUClRHBk.json | 8 - .../website/video-transcripts/q4YjUClRHBk.txt | 88 --- .../video-transcripts/qEgDZHZJEYo.json | 8 - .../website/video-transcripts/qEgDZHZJEYo.txt | 69 --- .../video-transcripts/qGJaVaQ_MNY.json | 8 - .../website/video-transcripts/qGJaVaQ_MNY.txt | 115 ---- .../video-transcripts/qKc1hZyw360.json | 8 - .../website/video-transcripts/qKc1hZyw360.txt | 159 ----- .../video-transcripts/qUJ4FwZMEQY.json | 8 - .../website/video-transcripts/qUJ4FwZMEQY.txt | 150 ----- .../video-transcripts/qaYExTzcCVY.json | 8 - .../website/video-transcripts/qaYExTzcCVY.txt | 128 ---- .../video-transcripts/r_ti2QAhm9s.json | 8 - .../website/video-transcripts/r_ti2QAhm9s.txt | 134 ---- .../video-transcripts/rjIQqfrFvbI.json | 8 - .../website/video-transcripts/rjIQqfrFvbI.txt | 106 ---- .../video-transcripts/sBAiPOnjX6o.json | 8 - .../website/video-transcripts/sBAiPOnjX6o.txt | 190 ------ .../video-transcripts/sH5GY816sRc.json | 8 - .../website/video-transcripts/sH5GY816sRc.txt | 205 ------ .../video-transcripts/sK-u1TBWFX8.json | 9 - .../website/video-transcripts/sK-u1TBWFX8.txt | 46 -- .../video-transcripts/sUhpCwd0YJg.json | 8 - .../website/video-transcripts/sUhpCwd0YJg.txt | 11 - .../video-transcripts/sed5OPQSfe0.json | 10 - .../website/video-transcripts/sed5OPQSfe0.txt | 165 ----- .../video-transcripts/set12jVQl_A.json | 8 - .../website/video-transcripts/set12jVQl_A.txt | 335 ---------- .../video-transcripts/swgT_aDsv3U.json | 10 - .../website/video-transcripts/swgT_aDsv3U.txt | 164 ----- .../video-transcripts/sxCADu-SjpQ.json | 8 - .../website/video-transcripts/sxCADu-SjpQ.txt | 111 ---- .../video-transcripts/tBBtpdz-JJg.json | 8 - .../website/video-transcripts/tBBtpdz-JJg.txt | 113 ---- .../video-transcripts/tWMVUDScyfQ.json | 8 - .../website/video-transcripts/tWMVUDScyfQ.txt | 109 ---- .../video-transcripts/tiY5ofnJop8.json | 8 - .../website/video-transcripts/tiY5ofnJop8.txt | 120 ---- .../video-transcripts/tjgyigzxo5U.json | 8 - .../website/video-transcripts/tjgyigzxo5U.txt | 108 ---- .../video-transcripts/ts-Q1zBGXco.json | 8 - .../website/video-transcripts/ts-Q1zBGXco.txt | 86 --- .../video-transcripts/tugJ_7xMdzs.json | 8 - .../website/video-transcripts/tugJ_7xMdzs.txt | 331 ---------- .../video-transcripts/uYCCbE70kE0.json | 8 - .../website/video-transcripts/uYCCbE70kE0.txt | 128 ---- .../video-transcripts/uvQKs_PdB64.json | 8 - .../website/video-transcripts/uvQKs_PdB64.txt | 155 ----- .../video-transcripts/v-kJ_nIYq3I.json | 8 - .../website/video-transcripts/v-kJ_nIYq3I.txt | 105 ---- .../video-transcripts/vAiTW4Y8QuA.json | 8 - .../website/video-transcripts/vAiTW4Y8QuA.txt | 100 --- .../video-transcripts/vLBQhAJ6aTk.json | 8 - .../website/video-transcripts/vLBQhAJ6aTk.txt | 153 ----- .../video-transcripts/vOcKbbW9HBM.json | 8 - .../website/video-transcripts/vOcKbbW9HBM.txt | 56 -- .../video-transcripts/vdFr815Dhpg.json | 8 - .../website/video-transcripts/vdFr815Dhpg.txt | 262 -------- .../video-transcripts/vjTexRDCihA.json | 8 - .../website/video-transcripts/vjTexRDCihA.txt | 127 ---- .../video-transcripts/vxsUf3gw1zg.json | 8 - .../website/video-transcripts/vxsUf3gw1zg.txt | 151 ----- .../video-transcripts/w7xvlw3rI6Y.json | 9 - .../website/video-transcripts/w7xvlw3rI6Y.txt | 16 - .../video-transcripts/wp6gqYLD-XM.json | 8 - .../website/video-transcripts/wp6gqYLD-XM.txt | 69 --- .../video-transcripts/wqcM8pSOGTY.json | 10 - .../website/video-transcripts/wqcM8pSOGTY.txt | 270 -------- .../video-transcripts/x-mUTz23Cd4.json | 8 - .../website/video-transcripts/x-mUTz23Cd4.txt | 180 ------ .../video-transcripts/xCEOuV43Uqw.json | 8 - .../website/video-transcripts/xCEOuV43Uqw.txt | 224 ------- .../video-transcripts/xLa5yeeVsE8.json | 8 - .../website/video-transcripts/xLa5yeeVsE8.txt | 130 ---- .../video-transcripts/xzwq4P9ogoU.json | 8 - .../website/video-transcripts/xzwq4P9ogoU.txt | 65 -- .../video-transcripts/yamsuV5Airc.json | 8 - .../website/video-transcripts/yamsuV5Airc.txt | 227 ------- .../video-transcripts/ymocxBIQn0o.json | 8 - .../website/video-transcripts/ymocxBIQn0o.txt | 61 -- .../video-transcripts/yv3WXt8o88k.json | 8 - .../website/video-transcripts/yv3WXt8o88k.txt | 133 ---- .../video-transcripts/zJ7L5fi60H8.json | 8 - .../website/video-transcripts/zJ7L5fi60H8.txt | 75 --- 679 files changed, 34244 deletions(-) delete mode 100644 docs/website/scripts/__pycache__/split_courses.cpython-313.pyc delete mode 100644 docs/website/scripts/bootstrap_video_update_sections.py delete mode 100644 docs/website/scripts/build_video_refresh_inventory.py delete mode 100644 docs/website/scripts/download_youtube_captions.py delete mode 100644 docs/website/scripts/fetch_missing_transcripts.py delete mode 100644 docs/website/scripts/inject_transcripts.py delete mode 100644 docs/website/scripts/normalize_transcript_cache.py delete mode 100644 docs/website/scripts/report_video_refresh_progress.py delete mode 100644 docs/website/scripts/video_refresh_lib.py delete mode 100644 docs/website/video-transcripts/-7XHkBMK4NY.json delete mode 100644 docs/website/video-transcripts/-7XHkBMK4NY.txt delete mode 100644 docs/website/video-transcripts/-M957AAi-vk.json delete mode 100644 docs/website/video-transcripts/-M957AAi-vk.txt delete mode 100644 docs/website/video-transcripts/-aTpqxDa1Ag.json delete mode 100644 docs/website/video-transcripts/-aTpqxDa1Ag.txt delete mode 100644 docs/website/video-transcripts/-brmZYVWb0Y.json delete mode 100644 docs/website/video-transcripts/-brmZYVWb0Y.txt delete mode 100644 docs/website/video-transcripts/-f1yue3hDEk.json delete mode 100644 docs/website/video-transcripts/-f1yue3hDEk.txt delete mode 100644 docs/website/video-transcripts/008AK1GfHA8.json delete mode 100644 docs/website/video-transcripts/008AK1GfHA8.txt delete mode 100644 docs/website/video-transcripts/0ETli_N__ZY.json delete mode 100644 docs/website/video-transcripts/0ETli_N__ZY.txt delete mode 100644 docs/website/video-transcripts/0a6mPI412C4.json delete mode 100644 docs/website/video-transcripts/0a6mPI412C4.txt delete mode 100644 docs/website/video-transcripts/0l4R049tSOY.json delete mode 100644 docs/website/video-transcripts/0l4R049tSOY.txt delete mode 100644 docs/website/video-transcripts/0m7Bay4g93k.json delete mode 100644 docs/website/video-transcripts/0m7Bay4g93k.txt delete mode 100644 docs/website/video-transcripts/16-Vkgcx2kg.json delete mode 100644 docs/website/video-transcripts/16-Vkgcx2kg.txt delete mode 100644 docs/website/video-transcripts/17ISIksjcPM.json delete mode 100644 docs/website/video-transcripts/17ISIksjcPM.txt delete mode 100644 docs/website/video-transcripts/1wHGnmO-vtE.json delete mode 100644 docs/website/video-transcripts/1wHGnmO-vtE.txt delete mode 100644 docs/website/video-transcripts/2nD75pODPWk.json delete mode 100644 docs/website/video-transcripts/2nD75pODPWk.txt delete mode 100644 docs/website/video-transcripts/2xKvvv7XoVQ.json delete mode 100644 docs/website/video-transcripts/2xKvvv7XoVQ.txt delete mode 100644 docs/website/video-transcripts/3-ZH2IFIIMY.json delete mode 100644 docs/website/video-transcripts/3-ZH2IFIIMY.txt delete mode 100644 docs/website/video-transcripts/32mkZymqa6E.json delete mode 100644 docs/website/video-transcripts/32mkZymqa6E.txt delete mode 100644 docs/website/video-transcripts/3B7C0ZbV8Bc.json delete mode 100644 docs/website/video-transcripts/3B7C0ZbV8Bc.txt delete mode 100644 docs/website/video-transcripts/3IC2qZ3wUO4.json delete mode 100644 docs/website/video-transcripts/3IC2qZ3wUO4.txt delete mode 100644 docs/website/video-transcripts/3rLf9A9RYPY.json delete mode 100644 docs/website/video-transcripts/3rLf9A9RYPY.txt delete mode 100644 docs/website/video-transcripts/46TpE-Cgtw8.json delete mode 100644 docs/website/video-transcripts/46TpE-Cgtw8.txt delete mode 100644 docs/website/video-transcripts/47WhIiLxv78.json delete mode 100644 docs/website/video-transcripts/47WhIiLxv78.txt delete mode 100644 docs/website/video-transcripts/4D_KUa2qv2o.json delete mode 100644 docs/website/video-transcripts/4D_KUa2qv2o.txt delete mode 100644 docs/website/video-transcripts/4jIqplr19HA.json delete mode 100644 docs/website/video-transcripts/4jIqplr19HA.txt delete mode 100644 docs/website/video-transcripts/54POZ4PFFBw.json delete mode 100644 docs/website/video-transcripts/54POZ4PFFBw.txt delete mode 100644 docs/website/video-transcripts/5EyrMpQqR-k.json delete mode 100644 docs/website/video-transcripts/5EyrMpQqR-k.txt delete mode 100644 docs/website/video-transcripts/5WpZmIkdUfs.json delete mode 100644 docs/website/video-transcripts/5WpZmIkdUfs.txt delete mode 100644 docs/website/video-transcripts/5eMvwRcDcug.json delete mode 100644 docs/website/video-transcripts/5eMvwRcDcug.txt delete mode 100644 docs/website/video-transcripts/65LciCzyRNQ.json delete mode 100644 docs/website/video-transcripts/65LciCzyRNQ.txt delete mode 100644 docs/website/video-transcripts/65jD9oGw61w.json delete mode 100644 docs/website/video-transcripts/65jD9oGw61w.txt delete mode 100644 docs/website/video-transcripts/6oTy-LcTm0s.json delete mode 100644 docs/website/video-transcripts/6oTy-LcTm0s.txt delete mode 100644 docs/website/video-transcripts/73d65cvyQv4.json delete mode 100644 docs/website/video-transcripts/73d65cvyQv4.txt delete mode 100644 docs/website/video-transcripts/77N2t2n8rbQ.json delete mode 100644 docs/website/video-transcripts/77N2t2n8rbQ.txt delete mode 100644 docs/website/video-transcripts/7CoD9u6KM2Q.json delete mode 100644 docs/website/video-transcripts/7CoD9u6KM2Q.txt delete mode 100644 docs/website/video-transcripts/7G4OkjgTWIQ.json delete mode 100644 docs/website/video-transcripts/7G4OkjgTWIQ.txt delete mode 100644 docs/website/video-transcripts/7XHkBMK4NY.json delete mode 100644 docs/website/video-transcripts/7XHkBMK4NY.txt delete mode 100644 docs/website/video-transcripts/85MyytvMS9I.json delete mode 100644 docs/website/video-transcripts/85MyytvMS9I.txt delete mode 100644 docs/website/video-transcripts/8WER-8R0WqA.json delete mode 100644 docs/website/video-transcripts/8WER-8R0WqA.txt delete mode 100644 docs/website/video-transcripts/8fxZVc1hw6Q.json delete mode 100644 docs/website/video-transcripts/8fxZVc1hw6Q.txt delete mode 100644 docs/website/video-transcripts/8wzBpEp81Kc.json delete mode 100644 docs/website/video-transcripts/8wzBpEp81Kc.txt delete mode 100644 docs/website/video-transcripts/91BrBoia4nM.json delete mode 100644 docs/website/video-transcripts/91BrBoia4nM.txt delete mode 100644 docs/website/video-transcripts/99DAeP9LG6c.json delete mode 100644 docs/website/video-transcripts/99DAeP9LG6c.txt delete mode 100644 docs/website/video-transcripts/9T8MBBuWBDs.json delete mode 100644 docs/website/video-transcripts/9T8MBBuWBDs.txt delete mode 100644 docs/website/video-transcripts/A72rY4rU7E0.json delete mode 100644 docs/website/video-transcripts/A72rY4rU7E0.txt delete mode 100644 docs/website/video-transcripts/ACsZ8qiwR8Q.json delete mode 100644 docs/website/video-transcripts/ACsZ8qiwR8Q.txt delete mode 100644 docs/website/video-transcripts/AFvuY7Ev-XA.json delete mode 100644 docs/website/video-transcripts/AFvuY7Ev-XA.txt delete mode 100644 docs/website/video-transcripts/AIsD5MXEK-c.json delete mode 100644 docs/website/video-transcripts/AIsD5MXEK-c.txt delete mode 100644 docs/website/video-transcripts/AMlnslUn1bA.json delete mode 100644 docs/website/video-transcripts/AMlnslUn1bA.txt delete mode 100644 docs/website/video-transcripts/B4QCJRFpG-k.json delete mode 100644 docs/website/video-transcripts/B4QCJRFpG-k.txt delete mode 100644 docs/website/video-transcripts/BSj3KRM5Sj0.json delete mode 100644 docs/website/video-transcripts/BSj3KRM5Sj0.txt delete mode 100644 docs/website/video-transcripts/BSrupbUahRM.json delete mode 100644 docs/website/video-transcripts/BSrupbUahRM.txt delete mode 100644 docs/website/video-transcripts/BXwh2T7wvfc.json delete mode 100644 docs/website/video-transcripts/BXwh2T7wvfc.txt delete mode 100644 docs/website/video-transcripts/BY1lMQz873g.json delete mode 100644 docs/website/video-transcripts/BY1lMQz873g.txt delete mode 100644 docs/website/video-transcripts/BbVoa3vw7OM.json delete mode 100644 docs/website/video-transcripts/BbVoa3vw7OM.txt delete mode 100644 docs/website/video-transcripts/BwWhFcHc6LM.json delete mode 100644 docs/website/video-transcripts/BwWhFcHc6LM.txt delete mode 100644 docs/website/video-transcripts/C3PLjAWQ-XA.json delete mode 100644 docs/website/video-transcripts/C3PLjAWQ-XA.txt delete mode 100644 docs/website/video-transcripts/Cc8YG-_M02M.json delete mode 100644 docs/website/video-transcripts/Cc8YG-_M02M.txt delete mode 100644 docs/website/video-transcripts/CvbcWC4hLYk.json delete mode 100644 docs/website/video-transcripts/CvbcWC4hLYk.txt delete mode 100644 docs/website/video-transcripts/DWifLQZyPDE.json delete mode 100644 docs/website/video-transcripts/DWifLQZyPDE.txt delete mode 100644 docs/website/video-transcripts/Dh1bYqiKBQo.json delete mode 100644 docs/website/video-transcripts/Dh1bYqiKBQo.txt delete mode 100644 docs/website/video-transcripts/DyzEgAGyRcA.json delete mode 100644 docs/website/video-transcripts/DyzEgAGyRcA.txt delete mode 100644 docs/website/video-transcripts/EkiTDQn9Cpg.json delete mode 100644 docs/website/video-transcripts/EkiTDQn9Cpg.txt delete mode 100644 docs/website/video-transcripts/FKTM8jAepJs.json delete mode 100644 docs/website/video-transcripts/FKTM8jAepJs.txt delete mode 100644 docs/website/video-transcripts/FSek5IOQ0IE.json delete mode 100644 docs/website/video-transcripts/FSek5IOQ0IE.txt delete mode 100644 docs/website/video-transcripts/FcHVK9ObONg.json delete mode 100644 docs/website/video-transcripts/FcHVK9ObONg.txt delete mode 100644 docs/website/video-transcripts/FczBAgUIb1c.json delete mode 100644 docs/website/video-transcripts/FczBAgUIb1c.txt delete mode 100644 docs/website/video-transcripts/GEG_S-dyxaM.json delete mode 100644 docs/website/video-transcripts/GEG_S-dyxaM.txt delete mode 100644 docs/website/video-transcripts/GEzM1MXkqnk.json delete mode 100644 docs/website/video-transcripts/GEzM1MXkqnk.txt delete mode 100644 docs/website/video-transcripts/GMnWMB_JfaQ.json delete mode 100644 docs/website/video-transcripts/GMnWMB_JfaQ.txt delete mode 100644 docs/website/video-transcripts/H7WW4GwXK8o.json delete mode 100644 docs/website/video-transcripts/H7WW4GwXK8o.txt delete mode 100644 docs/website/video-transcripts/HJvMQKM5FPY.json delete mode 100644 docs/website/video-transcripts/HJvMQKM5FPY.txt delete mode 100644 docs/website/video-transcripts/HRTLnLecoMA.json delete mode 100644 docs/website/video-transcripts/HRTLnLecoMA.txt delete mode 100644 docs/website/video-transcripts/IaalGP1UDeU.json delete mode 100644 docs/website/video-transcripts/IaalGP1UDeU.txt delete mode 100644 docs/website/video-transcripts/IqsUSCgSVTo.json delete mode 100644 docs/website/video-transcripts/IqsUSCgSVTo.txt delete mode 100644 docs/website/video-transcripts/IxVUX0s2Nms.json delete mode 100644 docs/website/video-transcripts/IxVUX0s2Nms.txt delete mode 100644 docs/website/video-transcripts/JWLsSbbo4N4.json delete mode 100644 docs/website/video-transcripts/JWLsSbbo4N4.txt delete mode 100644 docs/website/video-transcripts/JZvrsZzdays.json delete mode 100644 docs/website/video-transcripts/JZvrsZzdays.txt delete mode 100644 docs/website/video-transcripts/Jk3tTyZroP0.json delete mode 100644 docs/website/video-transcripts/Jk3tTyZroP0.txt delete mode 100644 docs/website/video-transcripts/KckMUmmSzd4.json delete mode 100644 docs/website/video-transcripts/KckMUmmSzd4.txt delete mode 100644 docs/website/video-transcripts/Ke8bjFdD1bc.json delete mode 100644 docs/website/video-transcripts/Ke8bjFdD1bc.txt delete mode 100644 docs/website/video-transcripts/KhSWyE6rAN8.json delete mode 100644 docs/website/video-transcripts/KhSWyE6rAN8.txt delete mode 100644 docs/website/video-transcripts/L7ulPCUxIsw.json delete mode 100644 docs/website/video-transcripts/L7ulPCUxIsw.txt delete mode 100644 docs/website/video-transcripts/LFoSl_a2rs8.json delete mode 100644 docs/website/video-transcripts/LFoSl_a2rs8.txt delete mode 100644 docs/website/video-transcripts/LUHb5fuWzJE.json delete mode 100644 docs/website/video-transcripts/LUHb5fuWzJE.txt delete mode 100644 docs/website/video-transcripts/LZ_ydua__gY.json delete mode 100644 docs/website/video-transcripts/LZ_ydua__gY.txt delete mode 100644 docs/website/video-transcripts/Lai--eYYJTw.json delete mode 100644 docs/website/video-transcripts/Lai--eYYJTw.txt delete mode 100644 docs/website/video-transcripts/M518p4_Horg.json delete mode 100644 docs/website/video-transcripts/M518p4_Horg.txt delete mode 100644 docs/website/video-transcripts/MLbOdCt67Rw.json delete mode 100644 docs/website/video-transcripts/MLbOdCt67Rw.txt delete mode 100644 docs/website/video-transcripts/MbTEz-K8iwY.json delete mode 100644 docs/website/video-transcripts/MbTEz-K8iwY.txt delete mode 100644 docs/website/video-transcripts/Mcw8z_uP3BA.json delete mode 100644 docs/website/video-transcripts/Mcw8z_uP3BA.txt delete mode 100644 docs/website/video-transcripts/NVPIXnkoxv0.json delete mode 100644 docs/website/video-transcripts/NVPIXnkoxv0.txt delete mode 100644 docs/website/video-transcripts/Nv_0NVgCbSk.json delete mode 100644 docs/website/video-transcripts/Nv_0NVgCbSk.txt delete mode 100644 docs/website/video-transcripts/OWHizrNyizQ.json delete mode 100644 docs/website/video-transcripts/OWHizrNyizQ.txt delete mode 100644 docs/website/video-transcripts/OnZIaGXDZSo.json delete mode 100644 docs/website/video-transcripts/OnZIaGXDZSo.txt delete mode 100644 docs/website/video-transcripts/Otujb_KyofA.json delete mode 100644 docs/website/video-transcripts/Otujb_KyofA.txt delete mode 100644 docs/website/video-transcripts/PERdJq3I-As.json delete mode 100644 docs/website/video-transcripts/PERdJq3I-As.txt delete mode 100644 docs/website/video-transcripts/PHb5YO77OQk.json delete mode 100644 docs/website/video-transcripts/PHb5YO77OQk.txt delete mode 100644 docs/website/video-transcripts/PRk7EhXQRqs.json delete mode 100644 docs/website/video-transcripts/PRk7EhXQRqs.txt delete mode 100644 docs/website/video-transcripts/PbRbzCGQCQE.json delete mode 100644 docs/website/video-transcripts/PbRbzCGQCQE.txt delete mode 100644 docs/website/video-transcripts/Q0fkApAGMZA.json delete mode 100644 docs/website/video-transcripts/Q0fkApAGMZA.txt delete mode 100644 docs/website/video-transcripts/QY7josfVbdg.json delete mode 100644 docs/website/video-transcripts/QY7josfVbdg.txt delete mode 100644 docs/website/video-transcripts/Qw7moLv-dE4.json delete mode 100644 docs/website/video-transcripts/Qw7moLv-dE4.txt delete mode 100644 docs/website/video-transcripts/RBYGdCllnww.json delete mode 100644 docs/website/video-transcripts/RBYGdCllnww.txt delete mode 100644 docs/website/video-transcripts/Rat-FDqDCTU.json delete mode 100644 docs/website/video-transcripts/Rat-FDqDCTU.txt delete mode 100644 docs/website/video-transcripts/Rok0Ubr4xes.json delete mode 100644 docs/website/video-transcripts/Rok0Ubr4xes.txt delete mode 100644 docs/website/video-transcripts/RsVOanGYjqo.json delete mode 100644 docs/website/video-transcripts/RsVOanGYjqo.txt delete mode 100644 docs/website/video-transcripts/Rt68rA2c6A8.json delete mode 100644 docs/website/video-transcripts/Rt68rA2c6A8.txt delete mode 100644 docs/website/video-transcripts/S1SHZLXQxSI.json delete mode 100644 docs/website/video-transcripts/S1SHZLXQxSI.txt delete mode 100644 docs/website/video-transcripts/S5pAGkV4h4w.json delete mode 100644 docs/website/video-transcripts/S5pAGkV4h4w.txt delete mode 100644 docs/website/video-transcripts/SBqYZSdIdec.json delete mode 100644 docs/website/video-transcripts/SBqYZSdIdec.txt delete mode 100644 docs/website/video-transcripts/T8l7k2OeGpo.json delete mode 100644 docs/website/video-transcripts/T8l7k2OeGpo.txt delete mode 100644 docs/website/video-transcripts/TOWhbEhiRe4.json delete mode 100644 docs/website/video-transcripts/TOWhbEhiRe4.txt delete mode 100644 docs/website/video-transcripts/TRF76P1Dwwc.json delete mode 100644 docs/website/video-transcripts/TRF76P1Dwwc.txt delete mode 100644 docs/website/video-transcripts/U0Ms65vcVr4.json delete mode 100644 docs/website/video-transcripts/U0Ms65vcVr4.txt delete mode 100644 docs/website/video-transcripts/UU6HCbenVAA.json delete mode 100644 docs/website/video-transcripts/UU6HCbenVAA.txt delete mode 100644 docs/website/video-transcripts/UhLFHJj8qnM.json delete mode 100644 docs/website/video-transcripts/UhLFHJj8qnM.txt delete mode 100644 docs/website/video-transcripts/UwdI_tMqYgU.json delete mode 100644 docs/website/video-transcripts/UwdI_tMqYgU.txt delete mode 100644 docs/website/video-transcripts/UxATbrfWveU.json delete mode 100644 docs/website/video-transcripts/UxATbrfWveU.txt delete mode 100644 docs/website/video-transcripts/UxZ1HeheGwU.json delete mode 100644 docs/website/video-transcripts/UxZ1HeheGwU.txt delete mode 100644 docs/website/video-transcripts/VCNFKMeud-w.json delete mode 100644 docs/website/video-transcripts/VCNFKMeud-w.txt delete mode 100644 docs/website/video-transcripts/VDyltmk_Hu8.json delete mode 100644 docs/website/video-transcripts/VDyltmk_Hu8.txt delete mode 100644 docs/website/video-transcripts/VnYaxvVn6OA.json delete mode 100644 docs/website/video-transcripts/VnYaxvVn6OA.txt delete mode 100644 docs/website/video-transcripts/WT4cFceBWRA.json delete mode 100644 docs/website/video-transcripts/WT4cFceBWRA.txt delete mode 100644 docs/website/video-transcripts/WYlM2utu4Ps.json delete mode 100644 docs/website/video-transcripts/WYlM2utu4Ps.txt delete mode 100644 docs/website/video-transcripts/W_1S2Rzgff8.json delete mode 100644 docs/website/video-transcripts/W_1S2Rzgff8.txt delete mode 100644 docs/website/video-transcripts/Wiy2goFwfQA.json delete mode 100644 docs/website/video-transcripts/Wiy2goFwfQA.txt delete mode 100644 docs/website/video-transcripts/XSztvFzQAr0.json delete mode 100644 docs/website/video-transcripts/XSztvFzQAr0.txt delete mode 100644 docs/website/video-transcripts/Xrolmjg8Dr0.json delete mode 100644 docs/website/video-transcripts/Xrolmjg8Dr0.txt delete mode 100644 docs/website/video-transcripts/YE7OQFQE0yA.json delete mode 100644 docs/website/video-transcripts/YE7OQFQE0yA.txt delete mode 100644 docs/website/video-transcripts/YwD35TG6WAg.json delete mode 100644 docs/website/video-transcripts/YwD35TG6WAg.txt delete mode 100644 docs/website/video-transcripts/YwkwqXkHGwg.json delete mode 100644 docs/website/video-transcripts/YwkwqXkHGwg.txt delete mode 100644 docs/website/video-transcripts/ZEe_hb1Lz6Y.json delete mode 100644 docs/website/video-transcripts/ZEe_hb1Lz6Y.txt delete mode 100644 docs/website/video-transcripts/ZzSCPcnFkxs.json delete mode 100644 docs/website/video-transcripts/ZzSCPcnFkxs.txt delete mode 100644 docs/website/video-transcripts/_EXEN52wQvs.json delete mode 100644 docs/website/video-transcripts/_EXEN52wQvs.txt delete mode 100644 docs/website/video-transcripts/_JCsyyIH02E.json delete mode 100644 docs/website/video-transcripts/_JCsyyIH02E.txt delete mode 100644 docs/website/video-transcripts/_WKRuO0Izxg.json delete mode 100644 docs/website/video-transcripts/_WKRuO0Izxg.txt delete mode 100644 docs/website/video-transcripts/a6q1M_wqEYY.json delete mode 100644 docs/website/video-transcripts/a6q1M_wqEYY.txt delete mode 100644 docs/website/video-transcripts/a7MF3oSCASE.json delete mode 100644 docs/website/video-transcripts/a7MF3oSCASE.txt delete mode 100644 docs/website/video-transcripts/aNFKBDeky2s.json delete mode 100644 docs/website/video-transcripts/aNFKBDeky2s.txt delete mode 100644 docs/website/video-transcripts/aVkeOIx-Is8.json delete mode 100644 docs/website/video-transcripts/aVkeOIx-Is8.txt delete mode 100644 docs/website/video-transcripts/aW3OlGTmasw.json delete mode 100644 docs/website/video-transcripts/aW3OlGTmasw.txt delete mode 100644 docs/website/video-transcripts/aZ2Y74xPj_0.json delete mode 100644 docs/website/video-transcripts/aZ2Y74xPj_0.txt delete mode 100644 docs/website/video-transcripts/anfVMvBXXX0.json delete mode 100644 docs/website/video-transcripts/anfVMvBXXX0.txt delete mode 100644 docs/website/video-transcripts/bH7cS5ENNlw.json delete mode 100644 docs/website/video-transcripts/bH7cS5ENNlw.txt delete mode 100644 docs/website/video-transcripts/bU6pZPs3uto.json delete mode 100644 docs/website/video-transcripts/bU6pZPs3uto.txt delete mode 100644 docs/website/video-transcripts/cRqvkIpJlkg.json delete mode 100644 docs/website/video-transcripts/cRqvkIpJlkg.txt delete mode 100644 docs/website/video-transcripts/cZiHmpw2FVI.json delete mode 100644 docs/website/video-transcripts/cZiHmpw2FVI.txt delete mode 100644 docs/website/video-transcripts/csTtSj6TqRE.json delete mode 100644 docs/website/video-transcripts/csTtSj6TqRE.txt delete mode 100644 docs/website/video-transcripts/cxllJwt10VU.json delete mode 100644 docs/website/video-transcripts/cxllJwt10VU.txt delete mode 100644 docs/website/video-transcripts/d1T25sbFUKE.json delete mode 100644 docs/website/video-transcripts/d1T25sbFUKE.txt delete mode 100644 docs/website/video-transcripts/exL7bS0StP4.json delete mode 100644 docs/website/video-transcripts/exL7bS0StP4.txt delete mode 100644 docs/website/video-transcripts/exQ8xXAxtoU.json delete mode 100644 docs/website/video-transcripts/exQ8xXAxtoU.txt delete mode 100644 docs/website/video-transcripts/fJD45Mz8SZM.json delete mode 100644 docs/website/video-transcripts/fJD45Mz8SZM.txt delete mode 100644 docs/website/video-transcripts/fmNpMFLwABA.json delete mode 100644 docs/website/video-transcripts/fmNpMFLwABA.txt delete mode 100644 docs/website/video-transcripts/gJoJQST5jyM.json delete mode 100644 docs/website/video-transcripts/gJoJQST5jyM.txt delete mode 100644 docs/website/video-transcripts/gM-InTtdhVE.json delete mode 100644 docs/website/video-transcripts/gM-InTtdhVE.txt delete mode 100644 docs/website/video-transcripts/gmqFd2bU_fM.json delete mode 100644 docs/website/video-transcripts/gmqFd2bU_fM.txt delete mode 100644 docs/website/video-transcripts/h-I62aFYBNA.json delete mode 100644 docs/website/video-transcripts/h-I62aFYBNA.txt delete mode 100644 docs/website/video-transcripts/hCjmHoktlrU.json delete mode 100644 docs/website/video-transcripts/hCjmHoktlrU.txt delete mode 100644 docs/website/video-transcripts/hwsWdhJHl8Q.json delete mode 100644 docs/website/video-transcripts/hwsWdhJHl8Q.txt delete mode 100644 docs/website/video-transcripts/hys6Rpkru50.json delete mode 100644 docs/website/video-transcripts/hys6Rpkru50.txt delete mode 100644 docs/website/video-transcripts/iCY64ThCleI.json delete mode 100644 docs/website/video-transcripts/iCY64ThCleI.txt delete mode 100644 docs/website/video-transcripts/jR7_OTg-aG0.json delete mode 100644 docs/website/video-transcripts/jR7_OTg-aG0.txt delete mode 100644 docs/website/video-transcripts/jVuNrLw4e-A.json delete mode 100644 docs/website/video-transcripts/jVuNrLw4e-A.txt delete mode 100644 docs/website/video-transcripts/jhPep9vHv_U.json delete mode 100644 docs/website/video-transcripts/jhPep9vHv_U.txt delete mode 100644 docs/website/video-transcripts/kfKsVAx659s.json delete mode 100644 docs/website/video-transcripts/kfKsVAx659s.txt delete mode 100644 docs/website/video-transcripts/kjUFJro0yUQ.json delete mode 100644 docs/website/video-transcripts/kjUFJro0yUQ.txt delete mode 100644 docs/website/video-transcripts/l1TPKuHYr9c.json delete mode 100644 docs/website/video-transcripts/l1TPKuHYr9c.txt delete mode 100644 docs/website/video-transcripts/l97sDefhHMM.json delete mode 100644 docs/website/video-transcripts/l97sDefhHMM.txt delete mode 100644 docs/website/video-transcripts/m43A68sNcvg.json delete mode 100644 docs/website/video-transcripts/m43A68sNcvg.txt delete mode 100644 docs/website/video-transcripts/mFFjxs9EDW8.json delete mode 100644 docs/website/video-transcripts/mFFjxs9EDW8.txt delete mode 100644 docs/website/video-transcripts/mqNtfN2C3fM.json delete mode 100644 docs/website/video-transcripts/mqNtfN2C3fM.txt delete mode 100644 docs/website/video-transcripts/nF4eqzVcsic.json delete mode 100644 docs/website/video-transcripts/nF4eqzVcsic.txt delete mode 100644 docs/website/video-transcripts/nImSppBdgkY.json delete mode 100644 docs/website/video-transcripts/nImSppBdgkY.txt delete mode 100644 docs/website/video-transcripts/oR3KHYf5OrY.json delete mode 100644 docs/website/video-transcripts/oR3KHYf5OrY.txt delete mode 100644 docs/website/video-transcripts/owhInk5YAtg.json delete mode 100644 docs/website/video-transcripts/owhInk5YAtg.txt delete mode 100644 docs/website/video-transcripts/p05yMCleSLo.json delete mode 100644 docs/website/video-transcripts/p05yMCleSLo.txt delete mode 100644 docs/website/video-transcripts/p6UFNw0nGik.json delete mode 100644 docs/website/video-transcripts/p6UFNw0nGik.txt delete mode 100644 docs/website/video-transcripts/q4YjUClRHBk.json delete mode 100644 docs/website/video-transcripts/q4YjUClRHBk.txt delete mode 100644 docs/website/video-transcripts/qEgDZHZJEYo.json delete mode 100644 docs/website/video-transcripts/qEgDZHZJEYo.txt delete mode 100644 docs/website/video-transcripts/qGJaVaQ_MNY.json delete mode 100644 docs/website/video-transcripts/qGJaVaQ_MNY.txt delete mode 100644 docs/website/video-transcripts/qKc1hZyw360.json delete mode 100644 docs/website/video-transcripts/qKc1hZyw360.txt delete mode 100644 docs/website/video-transcripts/qUJ4FwZMEQY.json delete mode 100644 docs/website/video-transcripts/qUJ4FwZMEQY.txt delete mode 100644 docs/website/video-transcripts/qaYExTzcCVY.json delete mode 100644 docs/website/video-transcripts/qaYExTzcCVY.txt delete mode 100644 docs/website/video-transcripts/r_ti2QAhm9s.json delete mode 100644 docs/website/video-transcripts/r_ti2QAhm9s.txt delete mode 100644 docs/website/video-transcripts/rjIQqfrFvbI.json delete mode 100644 docs/website/video-transcripts/rjIQqfrFvbI.txt delete mode 100644 docs/website/video-transcripts/sBAiPOnjX6o.json delete mode 100644 docs/website/video-transcripts/sBAiPOnjX6o.txt delete mode 100644 docs/website/video-transcripts/sH5GY816sRc.json delete mode 100644 docs/website/video-transcripts/sH5GY816sRc.txt delete mode 100644 docs/website/video-transcripts/sK-u1TBWFX8.json delete mode 100644 docs/website/video-transcripts/sK-u1TBWFX8.txt delete mode 100644 docs/website/video-transcripts/sUhpCwd0YJg.json delete mode 100644 docs/website/video-transcripts/sUhpCwd0YJg.txt delete mode 100644 docs/website/video-transcripts/sed5OPQSfe0.json delete mode 100644 docs/website/video-transcripts/sed5OPQSfe0.txt delete mode 100644 docs/website/video-transcripts/set12jVQl_A.json delete mode 100644 docs/website/video-transcripts/set12jVQl_A.txt delete mode 100644 docs/website/video-transcripts/swgT_aDsv3U.json delete mode 100644 docs/website/video-transcripts/swgT_aDsv3U.txt delete mode 100644 docs/website/video-transcripts/sxCADu-SjpQ.json delete mode 100644 docs/website/video-transcripts/sxCADu-SjpQ.txt delete mode 100644 docs/website/video-transcripts/tBBtpdz-JJg.json delete mode 100644 docs/website/video-transcripts/tBBtpdz-JJg.txt delete mode 100644 docs/website/video-transcripts/tWMVUDScyfQ.json delete mode 100644 docs/website/video-transcripts/tWMVUDScyfQ.txt delete mode 100644 docs/website/video-transcripts/tiY5ofnJop8.json delete mode 100644 docs/website/video-transcripts/tiY5ofnJop8.txt delete mode 100644 docs/website/video-transcripts/tjgyigzxo5U.json delete mode 100644 docs/website/video-transcripts/tjgyigzxo5U.txt delete mode 100644 docs/website/video-transcripts/ts-Q1zBGXco.json delete mode 100644 docs/website/video-transcripts/ts-Q1zBGXco.txt delete mode 100644 docs/website/video-transcripts/tugJ_7xMdzs.json delete mode 100644 docs/website/video-transcripts/tugJ_7xMdzs.txt delete mode 100644 docs/website/video-transcripts/uYCCbE70kE0.json delete mode 100644 docs/website/video-transcripts/uYCCbE70kE0.txt delete mode 100644 docs/website/video-transcripts/uvQKs_PdB64.json delete mode 100644 docs/website/video-transcripts/uvQKs_PdB64.txt delete mode 100644 docs/website/video-transcripts/v-kJ_nIYq3I.json delete mode 100644 docs/website/video-transcripts/v-kJ_nIYq3I.txt delete mode 100644 docs/website/video-transcripts/vAiTW4Y8QuA.json delete mode 100644 docs/website/video-transcripts/vAiTW4Y8QuA.txt delete mode 100644 docs/website/video-transcripts/vLBQhAJ6aTk.json delete mode 100644 docs/website/video-transcripts/vLBQhAJ6aTk.txt delete mode 100644 docs/website/video-transcripts/vOcKbbW9HBM.json delete mode 100644 docs/website/video-transcripts/vOcKbbW9HBM.txt delete mode 100644 docs/website/video-transcripts/vdFr815Dhpg.json delete mode 100644 docs/website/video-transcripts/vdFr815Dhpg.txt delete mode 100644 docs/website/video-transcripts/vjTexRDCihA.json delete mode 100644 docs/website/video-transcripts/vjTexRDCihA.txt delete mode 100644 docs/website/video-transcripts/vxsUf3gw1zg.json delete mode 100644 docs/website/video-transcripts/vxsUf3gw1zg.txt delete mode 100644 docs/website/video-transcripts/w7xvlw3rI6Y.json delete mode 100644 docs/website/video-transcripts/w7xvlw3rI6Y.txt delete mode 100644 docs/website/video-transcripts/wp6gqYLD-XM.json delete mode 100644 docs/website/video-transcripts/wp6gqYLD-XM.txt delete mode 100644 docs/website/video-transcripts/wqcM8pSOGTY.json delete mode 100644 docs/website/video-transcripts/wqcM8pSOGTY.txt delete mode 100644 docs/website/video-transcripts/x-mUTz23Cd4.json delete mode 100644 docs/website/video-transcripts/x-mUTz23Cd4.txt delete mode 100644 docs/website/video-transcripts/xCEOuV43Uqw.json delete mode 100644 docs/website/video-transcripts/xCEOuV43Uqw.txt delete mode 100644 docs/website/video-transcripts/xLa5yeeVsE8.json delete mode 100644 docs/website/video-transcripts/xLa5yeeVsE8.txt delete mode 100644 docs/website/video-transcripts/xzwq4P9ogoU.json delete mode 100644 docs/website/video-transcripts/xzwq4P9ogoU.txt delete mode 100644 docs/website/video-transcripts/yamsuV5Airc.json delete mode 100644 docs/website/video-transcripts/yamsuV5Airc.txt delete mode 100644 docs/website/video-transcripts/ymocxBIQn0o.json delete mode 100644 docs/website/video-transcripts/ymocxBIQn0o.txt delete mode 100644 docs/website/video-transcripts/yv3WXt8o88k.json delete mode 100644 docs/website/video-transcripts/yv3WXt8o88k.txt delete mode 100644 docs/website/video-transcripts/zJ7L5fi60H8.json delete mode 100644 docs/website/video-transcripts/zJ7L5fi60H8.txt 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 c8b30d3e3a..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 @@ -30,9 +30,3 @@ The best way to use this course is to keep a project open while you read. Run th - [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 2f3afcb176..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 @@ -39,9 +39,3 @@ By the time you have a form on screen, a button responding to taps, and a simula - [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 2620b0afd6..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 @@ -36,9 +36,3 @@ The broader lesson is that mobile work rewards adaptability. You do not control - [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 6e85dc3133..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 @@ -35,9 +35,3 @@ If you are trying to decide whether Codename One is "just Java for mobile", the - [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 4d465ee363..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 @@ -36,9 +36,3 @@ So the real anatomy of a Codename One application is simpler than it first appea - [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 3cbb91fc69..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 @@ -35,9 +35,3 @@ The video uses the older designer workflow to define bundles and RTL markers. Th - [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 fdc2b2cc6f..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 @@ -35,9 +35,3 @@ The modern adjustment is not in the layout system itself so much as in how you s - [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 f5c304cd47..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 @@ -35,9 +35,3 @@ Theme constants and other lower-level theme settings still matter, especially wh - [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 203072369b..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 @@ -38,9 +38,3 @@ The best way to adapt a design in Codename One today is to treat the mockup as a - [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 d8581954de..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 @@ -34,9 +34,3 @@ The modern recommendation is mostly about choosing the simplest layer that fits - [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 281b852cb9..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 @@ -35,9 +35,3 @@ Once you adopt that model, threading stops being an abstract theory lesson and b - [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 81b7db255a..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 @@ -32,9 +32,3 @@ So the best way to think about properties is as a productivity tool for model-dr - [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 cc3773668a..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 @@ -35,9 +35,3 @@ Push types also need to be chosen deliberately. Visible notification types are u - [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 e2e12df278..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 @@ -37,9 +37,3 @@ The hard part of native interfaces is often not the code itself but the configur - [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 9ac5a6a8a8..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 @@ -40,9 +40,3 @@ Once you approach the topic that way, the rest falls into place. Layouts define - [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 1da96fb44d..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 @@ -32,9 +32,3 @@ The key outcome of this lesson is not "install Spring Boot." It is understanding - [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 444d0f2091..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 @@ -33,9 +33,3 @@ This lesson also highlights a broader truth about mobile networking: the app is - [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 05444bc52b..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 @@ -31,9 +31,3 @@ In the lessons that follow, the design gets broken down into manageable parts: a - [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 77627a2677..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 @@ -33,9 +33,3 @@ So while the tooling in the video is older and Photoshop-specific, the principle - [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 c75fb9821c..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 @@ -35,9 +35,3 @@ So the practical workflow is straightforward. Build the screen structure in Java - [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 5c0660d3cf..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 @@ -33,9 +33,3 @@ So the practical lesson is simple: if several screens share the same top bar, ba - [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 79bfca6584..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 @@ -33,9 +33,3 @@ By the end of this lesson, the main screen should no longer feel like a set of d - [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 65d14bc4f4..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 @@ -31,9 +31,3 @@ So the checkout form is valuable not only because it looks good, but because it - [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 6b997aca5d..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 @@ -31,9 +31,3 @@ So this module is really about design judgment in a programmer's context. You al - [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 4b880d6578..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 @@ -33,9 +33,3 @@ This lesson also reinforces something developers often underweight: taste is par - [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 915488612a..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 @@ -32,9 +32,3 @@ So if a checkout UI feels elegant but does not let the user edit the order natur - [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 d6c2a241cc..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 @@ -33,9 +33,3 @@ So the right way to think about these CSS changes is not "decorate the new scree - [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 f7803e6c9d..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 @@ -33,9 +33,3 @@ So the summary of this lesson is that extending a UI from mockup to working app - [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 f3dff01449..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 @@ -32,9 +32,3 @@ The result of this first architecture pass is not a grand framework. It is a cle - [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 87df40370e..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 @@ -31,9 +31,3 @@ The wider lesson here is that good architecture does not need to arrive all at o - [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 2e23604224..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 @@ -32,9 +32,3 @@ So the real takeaway from this lesson is not "copy this payment bridge." It is " - [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 4a3be3b0ed..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 @@ -30,9 +30,3 @@ Native dependency management is not glamorous, but it is part of the cost of cro - [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 47ded0e267..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 @@ -33,9 +33,3 @@ So the modern version of this advice is: prototype in the native IDE when needed - [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 7f3157ac31..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 @@ -30,9 +30,3 @@ One modern point worth stating explicitly: if there is already a maintained Code - [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 5585ec3c34..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 @@ -32,9 +32,3 @@ So the Android side is not just "translate methods one by one." It is "translate - [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 458b85e800..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 @@ -32,9 +32,3 @@ So the lesson here is not about becoming an iOS media expert overnight. It is ab - [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 5d63b91bde..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 @@ -32,9 +32,3 @@ So the point of this lesson is not really ARC trivia for its own sake. It is und - [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 8ab020f9bd..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 @@ -30,9 +30,3 @@ This lesson also reinforces why patience matters more than heroics in native wor - [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 fc32a60ba0..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 @@ -32,9 +32,3 @@ So the practical outcome here is a network layer with sane caching, timestamp-ba - [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 a040c5650d..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 @@ -32,9 +32,3 @@ So the key lesson is that checkout forms are not just a place to gather input. T - [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 689f40f63f..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 @@ -30,9 +30,3 @@ The important product lesson here is that finishing the app does not mean ignori - [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 67be61d146..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 @@ -32,9 +32,3 @@ The practical takeaway is that mobile security should be layered. Protect transp - [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 728f1e5bee..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 @@ -30,9 +30,3 @@ So the practical rule for these features is simple: use them when they protect s - [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 bb081d903f..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 @@ -30,9 +30,3 @@ So the right starting point for maps in Codename One is not "drop in a map widge - [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 ccf02c632e..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 @@ -30,9 +30,3 @@ So the real goal here is not just to get a map on screen. It is to make sure the - [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 933e1da6e9..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 @@ -30,9 +30,3 @@ So the lesson here is that built-in markers are fine when they are enough, but d - [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 3c2a89ca65..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 @@ -30,9 +30,3 @@ So this lesson is best read as an anatomy-of-the-platform walkthrough for advanc - [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 c7eb2a6575..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 @@ -28,9 +28,3 @@ This is another reason direct-source work should be treated as an advanced mode. - [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 db70147a56..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 @@ -30,9 +30,3 @@ So the main value here is architectural. A desktop wrapper is just one more exam - [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 dd8df88aa2..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 @@ -28,9 +28,3 @@ This is one more reason the normal hosted or standard build flow remains the rig - [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 b192a99bbb..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 @@ -28,9 +28,3 @@ So the lesson here is the same as the Android one, but even more visible. Workin - [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 e4641c1ce6..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 @@ -32,9 +32,3 @@ So the best way to start performance work is to classify the problem. Is it rend - [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 08fdb61940..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 @@ -34,9 +34,3 @@ So the practical takeaway is this: keep the EDT lean, use threads deliberately, - [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 6795c06f33..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 @@ -32,9 +32,3 @@ For modern projects, this lesson pairs well with the general advice to keep styl - [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 ddbd25bf9b..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 @@ -32,9 +32,3 @@ This lesson is really a collection of reminders that performance is often about - [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 5749f45593..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 @@ -32,9 +32,3 @@ The most practical habit from this lesson is the back-of-the-envelope check. If - [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 0a0b71aa7c..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 @@ -30,9 +30,3 @@ The broader lesson is that performance work should move from general to specific - [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 e167d852ad..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 @@ -30,9 +30,3 @@ So the real lesson from the case study is methodological. Do not assume. Measure - [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 594c0aa587..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 @@ -32,9 +32,3 @@ So the first server lesson is really about responsibility boundaries. The mobile - [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 4ee01e3cdd..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 @@ -32,9 +32,3 @@ So the real takeaway here is not just one UI sketch over another. It is the idea - [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 9dc31a15b1..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 @@ -32,9 +32,3 @@ So the broader lesson is this: fleshing out the UI is not only about drawing mor - [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 baf60ccc7d..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 @@ -30,9 +30,3 @@ So the architectural lesson here is not complicated, but it is important. Establ - [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 79193edafb..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 @@ -30,9 +30,3 @@ So the navigation form in this lesson is important for two reasons. It gives the - [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 2886317910..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 @@ -32,9 +32,3 @@ So this lesson is really the first end-to-end content-management interaction in - [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 f13ae8aa78..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 @@ -32,9 +32,3 @@ So the broader takeaway is that once the app maker becomes real, the server is n - [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 90e20aaa79..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 @@ -30,9 +30,3 @@ This lesson also reinforces a broader design rule: REST is not about worshipping - [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 609f419133..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 @@ -30,9 +30,3 @@ So the key takeaway here is not a specific request helper. It is the product sta - [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 8c5d374ef1..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 @@ -30,9 +30,3 @@ So the main lesson here is to persist locally on purpose, behind a small abstrac - [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 f9309d8202..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 @@ -30,9 +30,3 @@ So the integration lesson is really about coupling and user experience at the sa - [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 f1d891fc5e..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 @@ -30,9 +30,3 @@ So the common thread in this lesson is simplification through structure. Remove - [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 b19cb3e6fb..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 @@ -30,9 +30,3 @@ So this section is less about billing forms in isolation and more about how buil - [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 2cdc12d7d1..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 @@ -30,9 +30,3 @@ So the key lesson is that preview should be treated as a product feature, not as - [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 1b61362986..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 @@ -32,9 +32,3 @@ So the real lesson here is not “how to show HTML.” It is how to give both th - [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 98be6eceda..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 @@ -32,9 +32,3 @@ So the purpose of the style form is not just to expose colors and fonts. It is t - [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 ea57b5ea99..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 @@ -30,9 +30,3 @@ So this lesson is really about matching the editing surface to the structure of - [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 e4f806ae1a..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 @@ -30,9 +30,3 @@ So the real product lesson here is that builder controls should be expressive en - [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 60d985dd6a..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 @@ -30,9 +30,3 @@ So the final lesson in this style-customization block is straightforward but imp - [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 3a969cb625..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 @@ -37,9 +37,3 @@ At this stage, the goal is not to send a notification yet. The goal is to leave - [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 2ebd48fde2..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 @@ -39,9 +39,3 @@ The lesson also touches on registration failures, and that part is worth updatin - [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 4d125621e8..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 @@ -37,9 +37,3 @@ Finally, log the push response on the server. Even in a small project, delivery - [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 25904625dc..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 @@ -35,9 +35,3 @@ The video correctly presents polling as a fallback rather than the ideal design. - [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 2e59a9bc5c..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 @@ -39,9 +39,3 @@ The video also shows WebSocket support being installed through the old settings - [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 bbbb6f5682..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 @@ -37,9 +37,3 @@ The exact screens shown in the video are dated now, but the overall store workfl - [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 b1996b9ed5..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 @@ -35,9 +35,3 @@ This setup step is intentionally unglamorous, but it matters. A backend deployme - [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 2e17781a7d..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 @@ -33,9 +33,3 @@ That combination of service hardening and port mapping is the difference between - [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 859420a8ef..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 @@ -31,9 +31,3 @@ The lesson also includes supporting pieces such as copying environment-specific - [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 68dce80da0..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 @@ -33,9 +33,3 @@ The warning about Java trust stores is also still relevant in spirit. When certi - [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 bda6514646..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 @@ -31,9 +31,3 @@ The main thing to preserve from this lesson is the deployment mindset. Security- - [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 df67e91879..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 @@ -33,9 +33,3 @@ This is the real benefit of the module. It is not just about making one screen w - [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 f640e3c419..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 @@ -35,9 +35,3 @@ This kind of abstraction can become overengineered very quickly, so the best thi - [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 b20a256999..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 @@ -33,9 +33,3 @@ The video also shows some duplication between phone-specific and tablet-specific - [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 62dfe73cc3..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 @@ -33,9 +33,3 @@ So the right takeaway here is not that every app needs exactly this abstraction - [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 42b3f40571..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 @@ -33,9 +33,3 @@ Another good pattern from the lesson is restoring the previous transition after - [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 316a911313..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 @@ -35,9 +35,3 @@ The lesson is also right to be cautious about the more recursive hierarchy-wide - [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 09bcc60d92..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 @@ -35,9 +35,3 @@ So the real lesson here is not “here is one more animation trick.” It is tha - [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 21ede3e388..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - Uber is a big influence on the mobile market and a lot of us try to replicate their 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 825650970c..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - 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 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 c472759877..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - in this part we'll create a mock-up for some of the forms starting with the login process 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 0d89b0cdb1..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - in this part we'll refine the login form and go into low-level graphics for animation and background 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 3637f50ec7..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - in this part we'll finally leave the confines of the login form and move to the simple forms of social 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 9d162d551a..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - in this part we'll go into the sms activation flow the first form in the sms activation 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 92a2838283..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - in this section we finally get to the map ui i hope you followed the instructions 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 8a4491caba..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - 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 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 14f5faddbc..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the where2 ui is a pretty big piece of the puzzle and it also includes the navigation ui 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 7fc9fb7573..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - we are nearing the end of the mock-up code the next thing on the agenda is the side 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 ea1f6a91d4..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - now that we got the mock-up running let's jump to the other side of the fence and set up the server 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 e1522a4d20..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - continuing the server side code will now delve into the location logic which maps to the websocket support in 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 915dc0fd1e..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - now that we have a server and a mock client we need to connect them together so we have a working prototype 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 60180f92cf..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the next step is binding this to the ui for a fully working sms activation process 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 f43f0bc79b..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - next we'll bind the websocket logic and map UI to bring this all together we can get started with a location 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 4e8df10bee..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - 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 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 f130bbbbb9..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - we finally have a client server application that works but we don't have any real functionality 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 af7f998095..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - we can now move forward to the last two pieces of the search service class if the last request is this request 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 e59d8822de..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - now that the basic infrastructure is out of the way we'll start wiring it into the ui 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 5b89a081e6..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - we'll continue the search ui implementation as we step into the completion container class 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 754af0c5e9..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - next we'll bring these changes into the map form class which handles the actual heavy lifting of search 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 5cdd6c6974..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - with that out of the way search should work but how does it display the result 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 25185c4f73..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - finally after all the buildup let's draw the path on the map form we've had 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 588abdc5a0..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - now that we have a path on the map we can move forward to the hailing process 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 343cf5d2e0..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - next we'll go into the underlying business logic portion of the hailing process on the client 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 8002c8cbdf..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - before we proceed into the actual driver app work and push notification we need to implement all the 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 9872c5c4f0..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the code for morph transitions broke another thing it broke the facebook or google login 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 f1013a2b22..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - when i started on this road i didn't want to create a driver app and wanted to focus only on the client 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 a550f36f83..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - before we go into the big ui changes let's go over some of the networking level level changes in the code 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 5e30906720..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the last two big ticket items are billing and social login i won't implement them with adherence to 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 92145895c4..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the last two big ticket items are billing and social login i won't implement them with adherence to 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 e3dc2a4ac2..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - next we'll address the client side of the payment process before we begin we need to download the 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 5d5197fad1..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - we already prepared a lot of the groundwork for social login on the server 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 80877efaf0..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - next we'll integrate the facebook login process into the code to get the native login working we only 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 54a12fd19e..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the last piece is the google login support which we almost finished as the code we did for facebook is nearly 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 dd657ed1fe..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - i kept most of the default transitions and did a few animations along the way but i didn't spend too much time on 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 6865fc401f..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the code for morph transitions broke another thing it broke the facebook or google login 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 2c4e1351ef..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 @@ -33,9 +33,3 @@ This lesson sits in a good middle ground between high-level animation APIs and f - [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 da88e42701..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 @@ -33,9 +33,3 @@ This is one of those small service-layer improvements that pays off all over the - [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 c9b3bbe991..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 @@ -33,9 +33,3 @@ The warning about unbinding is also important. Once bindings are attached to a g - [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 cdeed1be98..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 @@ -33,9 +33,3 @@ The architectural hint near the end is also important: a social app carries a hu - [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 e45c7716e2..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 @@ -31,9 +31,3 @@ The CSS details in the lesson are the beginning of a theme system, not just a fe - [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 f6f3984a89..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 @@ -31,9 +31,3 @@ The morph from the splash logo into the next screen’s logo is also a good exam - [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 c811f6ce5a..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 @@ -33,9 +33,3 @@ The CSS work in this lesson is more than ornament. It defines which buttons shou - [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 a5523e6d8f..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 @@ -31,9 +31,3 @@ This is the kind of reusable UI infrastructure that is worth writing. It is clos - [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 dd54e495f3..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 @@ -31,9 +31,3 @@ This is one of those lessons where the code is not flashy, but the payoff is lar - [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 2167a56574..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 @@ -31,9 +31,3 @@ Taken together, these screens show a good principle for signup flows: keep the s - [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 bc473e5ab7..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 @@ -31,9 +31,3 @@ The final wiring into the UI controller is also important. A wizard is not compl - [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 81220c7725..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 @@ -31,9 +31,3 @@ At this stage, the individual containers behind the tabs are still mostly placeh - [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 33e13be374..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 @@ -33,9 +33,3 @@ The nested-comment support hinted at in the `Comment` model is also a good sign - [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 f68af1588b..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 @@ -33,9 +33,3 @@ This is exactly the right stage to introduce a server boundary. The app is compl - [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 b38939f30a..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 @@ -31,9 +31,3 @@ The feed item assembly code also shows good layout judgment. Buttons that should - [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 2cec120bed..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 @@ -31,9 +31,3 @@ The confirm/delete controls then finish the pattern. The screen is not just deco - [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 be327c9100..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 @@ -31,9 +31,3 @@ This lesson also demonstrates that not every feed-style screen needs a long chai - [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 1ce0743b1c..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 @@ -31,9 +31,3 @@ What matters most is that the app now has a place for user-adjacent functions su - [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 3246730bbb..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 @@ -31,9 +31,3 @@ This lesson also shows a useful boundary between content and presentation. The p - [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 9779b655a3..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 @@ -33,9 +33,3 @@ The client and server user models are deliberately similar, and that is good. Th - [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 02204d2375..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 @@ -31,9 +31,3 @@ The DAO stays intentionally simple, which is also a good sign. Media transport s - [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 1ff3c30adb..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 @@ -33,9 +33,3 @@ The repository discussion is also useful because this is where paging starts to - [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 f09158488d..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 @@ -33,9 +33,3 @@ Even though the implementation here is intentionally simplified, the lesson is u - [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 33a460b5f6..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 @@ -33,9 +33,3 @@ The sections on Twilio and Mailgun are partly about tooling, but the more durabl - [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 a5a45a76c0..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 @@ -33,9 +33,3 @@ This lesson also quietly reinforces a pattern that matters across the whole back - [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 2b12decebe..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 @@ -33,9 +33,3 @@ The little permission and visibility helpers in this lesson may feel unremarkabl - [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 b1a1b96078..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 @@ -33,9 +33,3 @@ The simplifications are also sensible. There is no full reactions model yet, and - [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 c176e0799b..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 @@ -33,9 +33,3 @@ So the right takeaway is not “copy these HTTP verbs literally.” It is “kee - [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 d24db73b93..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 @@ -31,9 +31,3 @@ The main thing to notice about this lesson is how uneventful it feels. That is e - [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 5e17484ec7..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 @@ -33,9 +33,3 @@ The contact-upload logic is also a good reminder that “client API” sometimes - [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 bb65815917..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 @@ -33,9 +33,3 @@ The last change to the UI controller is small but important: startup now depends - [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 69428d242d..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 @@ -31,9 +31,3 @@ The friend-list refresh change follows the same pattern. Once user state is cach - [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 dae79ede2d..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 @@ -33,9 +33,3 @@ The lesson’s mention of notifications is important too. Once friendship action - [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 784c123553..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 @@ -33,9 +33,3 @@ The one-time index build is also a useful operational reminder. Search is not ju - [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 90a318a921..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 @@ -31,9 +31,3 @@ The note about generic erasure is also useful context here. Java generics feel e - [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 1ae40ac7ae..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 @@ -33,9 +33,3 @@ The final touch is small but important: putting the editable search field direct - [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 c26aaf3794..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 @@ -31,9 +31,3 @@ This is a good example of prioritizing functional completeness over ornamental c - [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 7f6208cafe..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 @@ -31,9 +31,3 @@ This is another good example of the course finding the right level of product re - [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 97faed2ea0..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 @@ -31,9 +31,3 @@ This is one of the better examples in the course of progressive feature design. - [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 0bf4a52fc1..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 @@ -31,9 +31,3 @@ The unbind-on-exit behavior is another good reminder that generated forms still - [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 adb6852d83..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 @@ -31,9 +31,3 @@ The more durable takeaway is that media delivery often forces you to choose betw - [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 c4d7bd6f43..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 @@ -31,9 +31,3 @@ This is one of those small infrastructure lessons that pays off disproportionall - [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 17767d97f1..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 @@ -31,9 +31,3 @@ The lesson also highlights a very practical detail: sometimes the app has raw by - [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 4fa5c66a4f..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 @@ -31,9 +31,3 @@ The lesson also calls out the orphan-media problem when a user abandons the form - [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 f5a8a2b3b4..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 @@ -31,9 +31,3 @@ The styled-post fixes are just as important. Earlier in the module, style choice - [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 d85fac47c2..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 @@ -31,9 +31,3 @@ That is a strong finish to this feature arc. Gallery selection, media upload, fe - [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 4d55558b86..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - this is probably the easiest section in terms of materials but since push is always such a hassle it could possibly 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 816472c9b6..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the changes to the notification service are significant and in effect that's the class that implements push notification 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 0bb7b5a905..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - it's now time to map the push support into the client side to do that we need to implement the push 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 aa6819a5fa..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - in this module we'll build a rudimentary whatsapp clone i'll take a very different path when 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 0ccfaae93a..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - we'll jump into the client functionality from the server connectivity class I won't start with the UI and build 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 e1dc7df0d1..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the next step are the other classes within the model package the rest is relatively trivial after the 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 3ae4f6d6f9..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - there is still one other class within the model package once we finish that 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 d866e6b9d5..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - now that we implemented the model code and the lifecycle code we are almost finished we are down to ui code and css 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 1d519da41c..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - now that we looked at the first ui form let's take a short detour via the css for the app 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 deb3ddbca5..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - while we are on the subject of theming there is one missing piece we neglected in the theme css 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 9c8874584a..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the next step is the actual chat form the chat form is a form like the main form we discussed earlier 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 7f9d5c0913..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the final part discussing the client-side code covers the new message form class 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 979f024506..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - we are finally back to the spring boot server we set up initially the code here is pretty simple as well 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 39075c6b15..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - 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 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 34996a4a26..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - the next step is the services layer which implements the relatively simple business layer of 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 638213b2cf..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - next we'll jump to the web service package this is pretty much identical to the 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 915a6e945a..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - next we'll jump to the websocket package which is the last package first we need to configure the websocket 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 173224d8ce..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - welcome to the first lesson of creating a netflix clone with codename one 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 f4a84f28c5..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - in this lesson we'll go over the basic server architecture and project lombok which we use in the server 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 9fb18dcd55..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - in the third part we'll dive right into the model objects representing the server and ultimately the front end code 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 9c0d39db97..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - in this final installment covering the server we'll go over the service classes and the final entity representing the 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 6206e70462..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - we're finally at the client side which is also pretty simple this time we'll start with the model 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 03c9b3b31e..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 @@ -18,8 +18,6 @@ description: "Watch the lesson and follow the accompanying resources." ## Transcript -_Transcript source: fetched-manual._ - 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 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 4af35e69e9..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 @@ -40,12 +40,6 @@ For deeper debugging, the include-sources workflow is still useful. When native - [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 _Join the conversation via GitHub Discussions._ 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 571fa7e757..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 @@ -32,12 +32,6 @@ For a current Codename One project, the practical advice is to design a normal s - [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 _Join the conversation via GitHub Discussions._ 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 6fef00fb42..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 @@ -34,12 +34,6 @@ The broader lesson is that visual styling should be deliberate. Use the right bo - [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 _Join the conversation via GitHub Discussions._ 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 5bbb6c7861..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 @@ -36,12 +36,6 @@ That leaves a straightforward modern workflow: generate a Maven project, choose - [Themeing](/themeing/) - [Moving To Maven](/blog/moving-to-maven/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 da0d826df2..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 @@ -37,12 +37,6 @@ So the practical workflow is straightforward: start with a Maven project, unders - [Create an iOS Provisioning Profile](/create-an-ios-provisioning-profile/) - [Moving To Maven](/blog/moving-to-maven/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 7105715970..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 @@ -37,12 +37,6 @@ So the modern NetBeans version of this lesson is straightforward: start from the - [Themeing](/themeing/) - [Moving To Maven](/blog/moving-to-maven/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 62899d375c..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 @@ -34,12 +34,6 @@ The main thing that changed since the video is not the underlying idea but the b - [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 _Join the conversation via GitHub Discussions._ 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 e31d624589..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 @@ -39,12 +39,6 @@ Theme constants and other lower-level theme settings still matter, especially wh - [Designer](/designer/) - [Hello World](/hello-world/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 826ed98e3e..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 @@ -33,12 +33,6 @@ The video is useful as a picture of the overall flow, but the modern thing to re - [Hello World](/hello-world/) - [Developer Guide](/developer-guide/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 48adbbf429..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 @@ -33,12 +33,6 @@ The main modern caution is not to over-invest in ornament if the menu is no long - [Layout Basics](/layout-basics/) - [How Do I Create A Simple Theme](/how-do-i/how-do-i-create-a-simple-theme/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 5d2816facb..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 @@ -35,12 +35,6 @@ The real educational value here is that the framework source is not off-limits. - [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 _Join the conversation via GitHub Discussions._ 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 94895d4c41..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 @@ -33,12 +33,6 @@ The video is also a bit out of date in how manual the Android Studio setup is. T - [Development Environment](/development-environment/) - [Moving To Maven](/blog/moving-to-maven/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 c62f43af03..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 @@ -32,12 +32,6 @@ The older page talks about fetching images from the resource file through the re - [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 _Join the conversation via GitHub Discussions._ 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 826c7dfe89..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 @@ -36,12 +36,6 @@ The practical lesson is to debug with evidence. Use the debugger for logic, the - [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 _Join the conversation via GitHub Discussions._ 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 45ffbb6e05..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 @@ -34,12 +34,6 @@ In practice, the healthiest workflow is to use the current Codename One version - [Developer Guide](/developer-guide/) - [How Do I Use Offline Build](/how-do-i/how-do-i-use-offline-build/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 a3c968df48..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 @@ -33,12 +33,6 @@ The modern caveat is that new Codename One projects are usually less GUI-builder - [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 _Join the conversation via GitHub Discussions._ 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 b0ccd28843..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 @@ -34,12 +34,6 @@ The right workflow is to measure first, then optimize. Use the performance tools - [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 _Join the conversation via GitHub Discussions._ 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 49d1f6f793..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 @@ -37,12 +37,6 @@ The practical goal is not just "translated text". It is an application that feel - [Hello World](/hello-world/) - [Properties Are Amazing](/blog/properties-are-amazing/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 cbbb927a6b..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 @@ -38,12 +38,6 @@ The modern adjustment is not in the layout system itself so much as in how you s - [Themeing](/themeing/) - [Getting Started](/getting-started/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 3cafc4a048..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 @@ -30,12 +30,6 @@ The modern advice here is mostly about restraint. Capture the image you need, no - [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 _Join the conversation via GitHub Discussions._ 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 b2c6c36749..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 @@ -27,12 +27,6 @@ So while the original Cloud Connect workflow is mostly historical now, the reaso - [Themeing](/themeing/) - [Hello World](/hello-world/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 d540aeaa22..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 @@ -33,12 +33,6 @@ The key point is that crash reporting works best when it is part of a broader lo - [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 _Join the conversation via GitHub Discussions._ 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 90ba2d54b4..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 @@ -32,12 +32,6 @@ The best way to choose between these ports is not to ask which one is more power - [Introduction For Android Developers](/introduction-for-android-developers/) - [Hello World](/hello-world/) - - ## Discussion _Join the conversation via GitHub Discussions._ 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 05f4b97f83..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 @@ -36,12 +36,6 @@ The modern decision tree is fairly simple. If you are calling a normal backend A - [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 _Join the conversation via GitHub Discussions._ 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 91d19fe9b0..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 @@ -34,12 +34,6 @@ The main reason to choose offline build is policy, not convenience. It is usuall - [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 _Join the conversation via GitHub Discussions._ 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 e24fc844cf..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 @@ -32,12 +32,6 @@ So the best way to think about properties is as a productivity tool for model-dr - [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 _Join the conversation via GitHub Discussions._ 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 d5edd85cec..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 @@ -37,12 +37,6 @@ Testing needs a layered approach. First make sure registration succeeds and the - [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 _Join the conversation via GitHub Discussions._ 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 d79c19677c..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 @@ -33,12 +33,6 @@ The modern recommendation is mostly about choosing the simplest layer that fits - [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 _Join the conversation via GitHub Discussions._ 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 5179b2f3c0..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 @@ -37,12 +37,6 @@ The modern takeaway is simple: use include-sources when the normal simulator or - [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 _Join the conversation via GitHub Discussions._ 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 fd245294c831a70650f7e8cc6cd8105479bec6a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11795 zcmbt4X>c3ob-TdgUK}Jq@Fu}q5Dij1WF3|j>lR6oa=3(!5D^LjAPEax(7T{yA&w0< zZ4KI;5E{o7G@e#elZokRr=!kvrplyklxcscGwBo?s0O@M9JTReM*k>NlQf=o+V^&` zAO(Z6lYRqy=librz3*M$ZkWwR0>b~i_g3VuS_tA7SWtqgnA!N4B#3tjhG57sg2Plb zMvkgDl?(IgG4&|LQKK48BbBK!?Wm5^jp{l5sDU%M2%oB*@ELc*7#JpN<#cPjyI7d4 z+gdB<8I1-qoY`k)v>sRyXYpAW9p)g_7gGbK{c6q%bw&xw#+ZCn4DG8LrWkVr@e;*V zGZv}F;H!dK6^;sRR;kU-*rYZG)Kp0|H9o42XeXHJ27^Bo*2y`Gtqy3d`CMx) z4yIA*!89p-m}Z~S%gD4y z@C{5W%;AE68$daWW3@|djX2h@is_I_O-yG4!8Q9@n6CZj2qK_|wKTUA%3fT=963k2 zyC32fC)}h+y%>nkyH%ng6o?0c(E!hjn%PJ=8gi>e>Qsb}i~7@x@kopfL`BWi(qc6H z5RX>W1!HVH%*GR)!PpYVhdsLnJTC<<1w6Ac&a)7kiA2MmQ20_L80PyHLW$=Ts6nU= zFM2|eOW`eG;b?4e0Y>v&j>P9Z!B{BF1{T7e7#qgU`;^W@o|&acH00sJfvD$ljDzg# zQZ(w}Vd)o@;R1_`ytEYPip#{+@gm8KS}q)4;@Du3G!X*?Vp0wNjh{mFF5x96x3)qZ z1_gPO2w4`CHkO#Cl%`3olG{vyxJX9jC3Y9b_!98yt?-rRW>m^GV`1gblRJvjg@AzJ za#EdyTQiX`PG1Ok61zP6zTPX6o`lNND{7-*o{zC^O4LMSm&2T>;p1FnQB-kZQOz&S zxJizNv3SIVOWfD*<-;7`&(8-U{cxuJL3W@&6bthGm%}rBBp&YPgU~w;5R1`B++Rd_ z-{KWf$48guBC}UGCyZ2uLykj~Bno=Nif&o=wqeD(Y|ZKGlZWo>jVs!>w5e}qf~x~L zeb;8qQaYG{n&!N|L(q4u9?9uf_0iwLX7QG}zoFf^H2WDdkiWpXP5{%X> zrEc9sg7SDg#)QU*$wOS*!~ccw5a~}O;QkqbhPgP8^eVnCVZhw1U!CspoGac#YR;jgtn`c+guUfPA{w&>JFqu>F)ZXN24w<)T+A`A<7%ttN z{|2ykiCqL><*E0_PMi!A1gr9@D&es(yMw7C@}y?7;gt`t0r;gvVgZs zIh`edTpadNPn)Gu8=NmvJcM)hO6|%DdjMj5jv!2gi|~?g%cu#F+|L=HS|rq0ml5GiaI$%`U+iN_7OFvv%2Tnqry@*^1c3^u`^pM?m{ z)?`i|E!Z0Kwr;`J{h@6~iYi#`D~aXA+pngmkFEAJzuNiE>mSjMVs)k?^K#DGwoIkS zLPO(?p6fmDxo`Ae?_cZ4HT0#Zm9xud-#-7g^^u9Fa<9__ZA*?x=N^RVN&yKL!$tz4 zcL^WCkqp797?M+W5kAVNVN{3|pB8dzDTj5Gl+%Gepl3AuNY3Ch`Y7Bgv}v)8(QziJ zkG@!5xMT+f*S z^a=V$q(~g51*k^iFi=q*fabpmG!OjZX%vNbLZNx&-s4T%)44*UC3xVi+uVtgd{AJe zI|NK(8~76aEAXN1$EM2HA&vbCjty#}h9Y>Ci|CZ@$eb2Ap!yr%IFL}aO^f7C&I)LH z_zw^z_M8pGgY&NVeAp$Qf-As=T(hw#2qvjE5Ddl^76a^+2s`KE!aT@x)G6F*4zHth zy*N;13Dj5kaCDY)LqR0tJRVKyM(Oyag@phdzD1^=zG8V8fOrlfxLJ<6Yp1_+It91O zam{$u_zs;q^s(LXk*%&^wXM9m{A%V{-r6o$+gAhcM{h=N4d=V}3f+6Rv>(Y^U4qrM zdg%RAH&5NN=R0=`ox8E!?nqCigXukQO+2b1YC6}e3EK!M-Lelr3A2iNzaRA-zh5-^ zi^KpbB*5*A}1aR`ay+0Y5+~yNFozM|H+}@7H;D(;H94xL zAjc-lw?;o|YP;1bH0?=_rl~Y@)sUn16xutoR72)Sj_N4X)hAD-J91S0I%U+1kQw{0 z2~6c#aO}#gW(Hcm6swUL3{}QpG+UStwwZwW&}GcWJg0XXMD4i5MIIi43AsUtM2*A= zxFO7&5TP-dU5GlFMtKQM6K)Il0``$Q9>&}ejE-V-45Q;1;hfwFj8OOCq_dd7oP^tx zm@7j%N^uS&9GJ%y0n!oCAh`kC&F!~_h337##2~BcCs;N7DTZJ* z46$H`fAw@}%t=LRC^JBcYOumMSnU_WmKYeT^HzK9AP>|a5A>|wtFjX($df+<)@tw? zCUsk;C}x=wgJO+dO%SNxYn((8T^5wxOoB~W9vF?XR)rx3K%75)99A<9r|Z>DqP!>z z-jZF+n!LI%fS|pm(yZ^oi2AKooYypoJhUt@T4lXY+bL`ImQ01fL4ugF2O9}u8r5?t zFgixd==Z4>?4Ux~mD{BmPpYGY5)oY%H zRmj0xIy>+JK-HIB%?WZG)p5FnH&DT3Wn9AE z--5HOo&jMvRXqs`M0u!mmYWpmg8MZ77f21#2=+-7s%7CzjPMh{vHDBEJt(TrPf|4n z>Jq4!ti@}Ybd^mG#nQO~j~NGQ+r%}%SD-$qtwep>!B*OF$LCj}a16${k7BKrS6UGR z*3Em0jMRcWx7k5yYd`xOHKQL51yI6zO#K*?( zkpyv2^)4~BNk!iSYTx99e+ngHvTPSvEUo_K?I0Kuh5XYLd}WN#>Fvg-WYlir@C#Pf zx!tJ0#mW%ZxH__u`>GNxOc9&(43eLi13$(DhfEv{U0k|s`>GdXiySIEq840p;LQMU zhb6egap3PODk7U@+47S79m%9@4AZmGC4OFkY?n=A*~ip(AQ}q>qWrlChDfjDkQvu+ZG&l;4YGp?c}-< z9;qDtoePWLdj)5w2t10C+XFj`y5ez)hIzn(q(h4u&_Kb> zsF@#vggc91YA$eDHrrGSgQ99-NY=u&a&OLtW24~$#!cuI4v&{R0}+p$4J^E}@WKm4 zy4gx8Toho@eBu8U==?We?a{WC)61uGbaV1pp}93VR%mKTo+z|-Bu5_f^d=_?7=1!p zRz}|%&DOee^v>k5`$}O*f3!jqlN^5pdExFjuckFHdJ2Ide-&Rd}NGOOyE{ zy6%rB3J&MBlUGk>zWUBs-q9sEx^j-5)Ul7P?FDdg)wzS|~rok|02`L+8sjfLip8@}s4C2=-gi(ZWu8r$-X9-+~bZyXdF2Xl?X zj|>{8C3PH}VP^Zv#PUR*ZWHLX)$V(=yU^B=I{qQu^2iQ@uh$Wdn&hc}udYvN?>Ba? zo_YVm%?q~&?lnHYJeE3=b{3xLPaVfj?R}WG7U;UnA%Sjw>(qvhXgc@^I84K@Xu{;o zTwH6v9?Q232`xkUmfb?j?t>A-v z)QB3%YUNNAav_L*}hYZTajk_Qd!O9~mJ$iuKm6(LPQXUj1E34V2-&Qxdb{iP# z3&z%Ybu9P{!GW%^fR}Jc&FH;a3o)c(3|?Ka#>kkw`eI(g(EWO_1Sv(%2Ec|r_Z_HJ zNg5B@h+8pG+?2OuUxJomDD{Pm*T9%1nO?!NRHQOPOAi252wh1&GW4c=g#A{sFD(^q zK!eh+l1)&W-6B~=R>rnZ1+RL9yJ|van-sj{g%ifXU4z4(fkDr}5ctL573MQFJUx7- zm%O6;UF}>K;2@zz*!`}A^iBh{2}d+=CAJiYtSn(Y^Gh@BpV4x`9|>X4X6PwN9_~Ek zKGQ74XFYqt(W(!#@Nx%kI=5a_&xPYKZ#=vpYT;!Dq`oYPP~wQj%N)EK`SHm{q+W_e zSk4F4ZX>sVBjYh}EJh{jl_eyYZv+QA7$F);>)~dg%o`!vlmasQH^`H~`nlJj`TGd> z5F(lNpIknfKAV}naq;>^iM1C@)|I`>d(+1=r*bAYc-Twj<2S~xkL64|(Ch9joYmJC^eqLWGkqm}aTQ=X3VL%vZ(Of3 z)M-I%I0$_e)MO^pt%BZ_(iAAeo2TA5m9Du*)qbp_(=}=CYW=m=tF5`J_SKnO)eZn& zp_l3ON=7{Xvc@Fw$yqZaUX+fn7Ls`McJYBj{00oABc~GtqFbVAt#cIzz zEA2-I(3Wyoe2nq4=4RacSZJ8L_OBD0qb}8m8(5J%K#a64t z*08s&@=7H}SOsn1j4BUOTQ#&*z?#%%XB^N9m@W?rW0^$nZ&_fBn;$+DSE*;5Ozn2- zW9p!#;tq!>#@TO^oOWQgmIvqsCSrL|a1NBFn?&wY7MS|U*0L%ndNR$L5U!J?&Dl)TfxwJq{+ z^7|B&^_Rnuxp`pT31@^aKEoI7rv0usw-ioTLSgx>9$pJ@M3qlw=Ly3>*SO>bcO_K& zT@qvGmT(`JAaYm1MEjmOSMUC`V>$?PwNr z1HkTW4J)rNzn;1Dp|ulXPpJ-55_`^Sa-A5U@K$1PUbrv!B4!u1QeW>3C(x6r)%R_ONeT=S7^)6qMypyLI)sz6%`mfDO~ z-cq*qnYF`fm-79Gh5p0Yz9U)sC;+UtRoRRwYQ2*%m{&~8ru1>yjJc=pF6e30b2HQp z!*#=I=X=(CU7t|bm#fK}& z)z0lv+LvJUi_7qkVXzZtOlN2wo?Uo!{Q<9z{jiek0|SLPNkQvRUQ~~RGAz0Kfa7Ay znpqP|PnX<}=gxS|%6CcN(g%fB4(F(I%itg`;s;t#Q;-7e2958s0FpsFU6LL!CY%cf zs0Wz~v3{uZK!GYNz7X?jmS#<}gxfH&$#5hsPhtd#Hkk)J<@2%xz27w#W+lf&$d#y9 z-jNr<<-)rntVRTZ7fz>n}t9N7c11BXBrRpw$ZVy;Y;eF^JOG)lAZsEBN;u9%b6 zSKT5PgLiJ8y9NON34i`yAj0oIs`92r!PK~VHfw6knmoy)_p3WrkLIe~Z;XM9!c>zE z=S)r8Xt3b)_?+h3(2{LGCNvz&QnjDBz~auiP6!luMbaZ5b@tw~jQshDO!tra{=V;) z_N_}l=v%&Y^F;E5U>W&@0(CcY`5x7CpE9RrGso6O?orQvLK#wL(4&zW&oq56xYqOC z=<0>+(5bsqcaLW$nXGZ@-ze{IHcW(dl;nXdi68H6IntmFLar2$-te|=pjEI{AAPzO zy7~PbP(Gj)zOXB4oABLTkrqMA1mxo_012<2nBv}mNx;qaN04xDLL};roc2yK#~2Co z;A&IzTu}ZJPCglGApjQ3djPrtf4&1E5F!-)=ENHlnc=n0?KsqgEpY4x}1 zPeD}F%Gx+Q^kZ+o-`!gD*P#@PMrJtNuxKiMgb5!G!R3`7k40_#$|9Im^3yTyi9=R` zUk%KHs}COiLALU-=%q0C`&ds+o<2Q=+nAU-1{=JN9i;6Y#hfIi+a$P9Bp8?JzYklK z$KqI2z6EGD$+rM$P}(u~0_MJg5#DndA^HQd5I)F#ALjWgL$JRR5 z__ZB3Pd~=ux=l~ounCgYqmMCJcM>YouT|CL2)RyRyrG7Qb-7}v)JBneas0i-7A#u6 zMv}d0m=@Dp4mrKub%(mc{Mc}JIQ!z6e?E{sAIQ#xh4Zu77iNY1bB`t1^@Pevj*{un zI)UkGr<|_EZyovIik!bQd{_J919Cp=3uVLef-mw|0$(3=kz-^A=EZbvNKS7%ZqNRp r>9JI|ew?(BXUH^c4%1KD)npAgvnV>1$f@f;qV#`w9K73 - -## Current Recommended Approach - - - -## Step-by-Step - - - -## Common Pitfalls - - - -## Current Codename One Notes - - - -## Further Reading - - - -## Video Update Script - -### Recommended New Video Angle - - - -### Target Audience - - - -### Runtime Goal - - - -### Script Outline - -1. Problem statement -2. Current Codename One approach -3. Key concepts -4. Demo sequence -5. Common mistakes -6. Summary and next steps - -### Slide Outline - -1. Title / objective -2. Why this matters -3. Current workflow -4. Key APIs, tools, or files -5. Demo checkpoints -6. Pitfalls / compatibility notes -7. Further reading / CTA - -### Demo Outline - - -""".strip() - - -def load_inventory(path: Path): - return json.loads(path.read_text(encoding="utf-8"))["items"] - - -def insert_after_first_youtube(body: str, block: str) -> str: - match = re.search(r"\{\{<\s*youtube.*?>\}\}", body) - if not match: - return body.rstrip() + "\n\n" + block + "\n" - return body[: match.end()].rstrip() + "\n\n" + block + "\n\n" + body[match.end() :].lstrip() - - -def main(): - parser = argparse.ArgumentParser(description="Bootstrap guide/script sections into selected video pages.") - parser.add_argument("--inventory", default=str(INVENTORY_PATH)) - parser.add_argument("--slug", action="append", required=True) - args = parser.parse_args() - - wanted_slugs = set(args.slug) - inventory_path = Path(args.inventory) - items = load_inventory(inventory_path) - updated = 0 - - for item in items: - if item["slug"] not in wanted_slugs or item["page_kind"] == "course-hub": - continue - path = inventory_path.parents[1] / item["source_path"] - text = path.read_text(encoding="utf-8") - if "## What This Covers" in text: - continue - frontmatter, body = split_frontmatter(text) - new_body = insert_after_first_youtube(body, TEMPLATE) - path.write_text(f"---\n{frontmatter}\n---\n{new_body}", encoding="utf-8") - updated += 1 - print(f"bootstrapped: {path}") - - print(f"completed: updated={updated}") - - -if __name__ == "__main__": - main() diff --git a/docs/website/scripts/build_video_refresh_inventory.py b/docs/website/scripts/build_video_refresh_inventory.py deleted file mode 100644 index 6f529a96b4..0000000000 --- a/docs/website/scripts/build_video_refresh_inventory.py +++ /dev/null @@ -1,221 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import json -from collections import Counter, defaultdict -from pathlib import Path - -from video_refresh_lib import ( - COURSE_HUBS, - COURSES_DIR, - HOWDOI_DIR, - INVENTORY_PATH, - determine_priority, - derive_public_url, - extract_section, - extract_transcript, - find_youtube_id, - inventory_sort_key, - load_howdoi_index, - load_markdown_page, - load_transcript_meta, - load_transcript_text, - normalize_slug_from_lesson_filename, - stable_relpath, -) - - -def build_items(): - howdoi_index = load_howdoi_index() - items = [] - - for path in sorted(HOWDOI_DIR.glob("*.md")): - page = load_markdown_page(path) - slug = str(page.meta.get("slug", path.stem)) - youtube_id = str(page.meta.get("youtube_id", find_youtube_id(page.body) or "")) or None - embedded_transcript = extract_transcript(page.body) - transcript_meta = load_transcript_meta(youtube_id) if youtube_id else {} - transcript_text = load_transcript_text(youtube_id) if youtube_id else None - anomalies = [] - if slug not in howdoi_index: - anomalies.append("missing-from-howdoi-index") - if not youtube_id: - anomalies.append("missing-youtube-embed") - if page.meta.get("type") != "howdoi": - anomalies.append("unexpected-type") - if extract_section(page.body, "transcript") and not embedded_transcript: - anomalies.append("transcript-heading-without-usable-content") - if not page.meta.get("title"): - anomalies.append("missing-title") - if not page.meta.get("description"): - anomalies.append("missing-description") - - transcript_source = transcript_meta.get("source") - transcript_status = transcript_meta.get("status") - if not transcript_source and embedded_transcript: - transcript_source = "embedded" - if not transcript_status: - transcript_status = "transcript-fetched" if (embedded_transcript or transcript_text) else "transcript-missing" - - item = { - "content_type": "howdoi", - "page_kind": "howdoi", - "course_id": "", - "module_title": "", - "lesson_title": str(page.meta.get("title", slug)), - "slug": slug, - "source_path": stable_relpath(path), - "public_url": derive_public_url(page, "howdoi"), - "youtube_id": youtube_id, - "page_has_youtube": bool(youtube_id), - "page_has_transcript": bool(embedded_transcript or transcript_text), - "transcript_source": transcript_source or "", - "transcript_status": transcript_status, - "guide_status": "guide-in-progress" if extract_section(page.body, "what this covers") else "guide-not-started", - "script_status": "script-drafted" if extract_section(page.body, "video update script") else "script-not-started", - "priority": 0, - "owner": "", - "notes": "", - "anomalies": anomalies, - } - item["priority"] = determine_priority(item) - items.append(item) - - for path in sorted(COURSE_HUBS): - page = load_markdown_page(path) - slug = str(page.meta.get("slug", path.stem)) - anomalies = [] - if not page.meta.get("course_id"): - anomalies.append("missing-course-id") - if not page.meta.get("layout"): - anomalies.append("missing-layout") - item = { - "content_type": "course", - "page_kind": "course-hub", - "course_id": str(page.meta.get("course_id", slug)), - "module_title": "", - "lesson_title": str(page.meta.get("title", slug)), - "slug": slug, - "source_path": stable_relpath(path), - "public_url": derive_public_url(page, "course-hub"), - "youtube_id": None, - "page_has_youtube": False, - "page_has_transcript": False, - "transcript_source": "", - "transcript_status": "transcript-missing", - "guide_status": "guide-in-progress" if extract_section(page.body, "what this covers") else "guide-not-started", - "script_status": "script-drafted" if extract_section(page.body, "video update script") else "script-not-started", - "priority": 1, - "owner": "", - "notes": "", - "anomalies": anomalies, - } - item["priority"] = determine_priority(item) - items.append(item) - - for path in sorted(COURSES_DIR.rglob("*.md")): - page = load_markdown_page(path) - youtube_id = find_youtube_id(page.body) - embedded_transcript = extract_transcript(page.body) - transcript_meta = load_transcript_meta(youtube_id) if youtube_id else {} - transcript_text = load_transcript_text(youtube_id) if youtube_id else None - anomalies = [] - if not youtube_id: - anomalies.append("missing-youtube-embed") - if not page.meta.get("course_id"): - anomalies.append("missing-course-id") - if not page.meta.get("module_title"): - anomalies.append("missing-module-title") - if not page.meta.get("title"): - anomalies.append("missing-title") - if extract_section(page.body, "transcript") and not embedded_transcript: - anomalies.append("transcript-heading-without-usable-content") - - transcript_source = transcript_meta.get("source") - transcript_status = transcript_meta.get("status") - if not transcript_source and embedded_transcript: - transcript_source = "embedded" - if not transcript_status: - transcript_status = "transcript-fetched" if (embedded_transcript or transcript_text) else "transcript-missing" - - item = { - "content_type": "course", - "page_kind": "course-lesson", - "course_id": str(page.meta.get("course_id", "")), - "module_title": str(page.meta.get("module_title", "")), - "lesson_title": str(page.meta.get("title", normalize_slug_from_lesson_filename(path.stem))), - "slug": normalize_slug_from_lesson_filename(path.stem), - "source_path": stable_relpath(path), - "public_url": derive_public_url(page, "course-lesson"), - "youtube_id": youtube_id, - "page_has_youtube": bool(youtube_id), - "page_has_transcript": bool(embedded_transcript or transcript_text), - "transcript_source": transcript_source or "", - "transcript_status": transcript_status, - "guide_status": "guide-in-progress" if extract_section(page.body, "what this covers") else "guide-not-started", - "script_status": "script-drafted" if extract_section(page.body, "video update script") else "script-not-started", - "priority": 2, - "owner": "", - "notes": "", - "anomalies": anomalies, - } - item["priority"] = determine_priority(item) - items.append(item) - - by_youtube = defaultdict(list) - for item in items: - youtube_id = item.get("youtube_id") - if youtube_id: - by_youtube[str(youtube_id)].append(item) - for youtube_id, grouped in by_youtube.items(): - if len(grouped) > 1: - for item in grouped: - item["anomalies"].append(f"duplicate-youtube-id:{youtube_id}") - - return sorted(items, key=inventory_sort_key) - - -def build_summary(items): - counts = Counter() - anomalies = Counter() - for item in items: - counts[f'content_type:{item["content_type"]}'] += 1 - counts[f'page_kind:{item["page_kind"]}'] += 1 - counts[f'priority:{item["priority"]}'] += 1 - counts[f'transcript_status:{item["transcript_status"]}'] += 1 - counts[f'guide_status:{item["guide_status"]}'] += 1 - counts[f'script_status:{item["script_status"]}'] += 1 - if item["page_has_youtube"]: - counts["video_backed_pages"] += 1 - if item["page_has_transcript"]: - counts["pages_with_transcripts"] += 1 - for anomaly in item["anomalies"]: - anomalies[anomaly] += 1 - return { - "counts": dict(sorted(counts.items())), - "anomalies": dict(sorted(anomalies.items())), - } - - -def main(): - parser = argparse.ArgumentParser(description="Build video refresh inventory.") - parser.add_argument("--output", default=str(INVENTORY_PATH), help="Output JSON path") - parser.add_argument("--stdout", action="store_true", help="Print inventory to stdout") - args = parser.parse_args() - - items = build_items() - payload = { - "summary": build_summary(items), - "items": items, - } - output = Path(args.output) - output.parent.mkdir(parents=True, exist_ok=True) - output.write_text(json.dumps(payload, indent=2, sort_keys=False) + "\n", encoding="utf-8") - - if args.stdout: - print(json.dumps(payload, indent=2, sort_keys=False)) - else: - print(f"Wrote {len(items)} inventory entries to {output}") - - -if __name__ == "__main__": - main() diff --git a/docs/website/scripts/download_youtube_captions.py b/docs/website/scripts/download_youtube_captions.py deleted file mode 100644 index 97333fcee0..0000000000 --- a/docs/website/scripts/download_youtube_captions.py +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/env python3 -""" -Download caption tracks for owned YouTube videos via the YouTube Data API. - -This script uses OAuth for an installed application. It requires a Google OAuth -client secrets JSON file created in Google Cloud Console with the YouTube Data -API enabled. - -Recommended venv packages: - pip install google-auth google-auth-oauthlib - -Typical usage from docs/website: - .venv-video-refresh/bin/python3 scripts/download_youtube_captions.py \ - --client-secrets client_secret.json \ - --limit 10 -""" - -import argparse -import json -import sys -import urllib.error -import urllib.parse -import urllib.request -from pathlib import Path -from typing import Dict, Iterable, List, Optional, Tuple - -from video_refresh_lib import ( - INVENTORY_PATH, - load_transcript_meta, - normalize_transcript_text, - save_transcript, - transcript_has_meaningful_content, - transcript_paths, -) - -API_BASE = "https://www.googleapis.com/youtube/v3" -OAUTH_SCOPES = ["https://www.googleapis.com/auth/youtube.force-ssl"] - - -def load_inventory(path: Path): - payload = json.loads(path.read_text(encoding="utf-8")) - return payload["items"] - - -def dedupe_items(items: Iterable[Dict[str, object]]) -> List[Dict[str, object]]: - seen = set() - deduped = [] - for item in items: - youtube_id = item.get("youtube_id") - if not youtube_id or youtube_id in seen: - continue - seen.add(youtube_id) - deduped.append(item) - return deduped - - -def select_items(items, args): - selected = [] - wanted_slugs = set(args.slug or []) - wanted_ids = set(args.youtube_id or []) - for item in items: - if item["page_kind"] == "course-hub": - continue - if wanted_slugs and item["slug"] not in wanted_slugs: - continue - if wanted_ids and item.get("youtube_id") not in wanted_ids: - continue - if args.only_missing and item["transcript_status"] != "transcript-missing": - continue - selected.append(item) - selected = dedupe_items(selected) - if args.limit: - selected = selected[: args.limit] - return selected - - -def get_credentials(client_secrets: Path, token_path: Path): - try: - from google.auth.transport.requests import Request - from google.oauth2.credentials import Credentials - from google_auth_oauthlib.flow import InstalledAppFlow - except ImportError as err: - raise RuntimeError( - "Missing OAuth dependencies. Install google-auth and google-auth-oauthlib in your venv." - ) from err - - credentials = None - if token_path.exists(): - credentials = Credentials.from_authorized_user_file(str(token_path), OAUTH_SCOPES) - if credentials and credentials.valid: - return credentials - if credentials and credentials.expired and credentials.refresh_token: - credentials.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file(str(client_secrets), OAUTH_SCOPES) - credentials = flow.run_local_server(port=0) - token_path.write_text(credentials.to_json(), encoding="utf-8") - return credentials - - -def api_json_get(path: str, access_token: str, params: Dict[str, str]) -> Dict[str, object]: - url = f"{API_BASE}/{path}?{urllib.parse.urlencode(params)}" - request = urllib.request.Request(url) - request.add_header("Authorization", f"Bearer {access_token}") - request.add_header("Accept", "application/json") - with urllib.request.urlopen(request) as response: - return json.loads(response.read().decode("utf-8")) - - -def api_download(path: str, access_token: str, params: Dict[str, str]) -> str: - query = dict(params) - query["alt"] = "media" - url = f"{API_BASE}/{path}?{urllib.parse.urlencode(query)}" - request = urllib.request.Request(url) - request.add_header("Authorization", f"Bearer {access_token}") - with urllib.request.urlopen(request) as response: - return response.read().decode("utf-8", errors="replace") - - -def format_http_error(err: urllib.error.HTTPError) -> str: - try: - body = err.read().decode("utf-8", errors="replace") - except Exception: - body = "" - if not body: - return f"http-{err.code}" - try: - payload = json.loads(body) - return f"http-{err.code}: {json.dumps(payload, sort_keys=True)}" - except Exception: - compact = " ".join(body.split()) - return f"http-{err.code}: {compact}" - - -def score_caption_track(item: Dict[str, object], preferred_languages: List[str]) -> Tuple[int, int]: - snippet = item.get("snippet", {}) - language = str(snippet.get("language", "")).lower() - track_kind = str(snippet.get("trackKind", "")).lower() - is_asr = track_kind == "asr" - language_rank = preferred_languages.index(language) if language in preferred_languages else len(preferred_languages) + 1 - kind_rank = 1 if is_asr else 0 - return (language_rank, kind_rank) - - -def choose_caption_track(items: List[Dict[str, object]], preferred_languages: List[str]) -> Optional[Dict[str, object]]: - if not items: - return None - ordered = sorted(items, key=lambda item: score_caption_track(item, preferred_languages)) - return ordered[0] - - -def vtt_to_text(vtt_text: str) -> str: - lines = vtt_text.replace("\r\n", "\n").split("\n") - text_lines: List[str] = [] - for raw in lines: - line = raw.strip() - if not line: - text_lines.append("") - continue - if line == "WEBVTT": - continue - if "-->" in line: - continue - if line.startswith("Kind:") or line.startswith("Language:"): - continue - if line.isdigit(): - continue - line = urllib.parse.unquote(line) - text_lines.append(line) - return normalize_transcript_text("\n".join(text_lines)) - - -def list_captions(access_token: str, youtube_id: str) -> List[Dict[str, object]]: - payload = api_json_get( - "captions", - access_token, - { - "part": "id,snippet", - "videoId": youtube_id, - "maxResults": "50", - }, - ) - return list(payload.get("items", [])) - - -def download_caption(access_token: str, caption_id: str, tfmt: str) -> str: - return api_download("captions/" + urllib.parse.quote(caption_id), access_token, {"tfmt": tfmt}) - - -def fetch_owned_caption(access_token: str, youtube_id: str, preferred_languages: List[str]) -> Tuple[Optional[str], str]: - try: - items = list_captions(access_token, youtube_id) - except urllib.error.HTTPError as err: - return None, format_http_error(err) - except Exception as err: # pragma: no cover - network/runtime dependent - return None, str(err) - if not items: - return None, "no-owned-captions" - chosen = choose_caption_track(items, preferred_languages) - if not chosen: - return None, "no-matching-caption-track" - caption_id = str(chosen["id"]) - try: - vtt = download_caption(access_token, caption_id, "vtt") - except urllib.error.HTTPError as err: - return None, format_http_error(err) - except Exception as err: # pragma: no cover - network/runtime dependent - return None, str(err) - text = vtt_to_text(vtt) - return (text if transcript_has_meaningful_content(text) else None), caption_id - - -def main(): - parser = argparse.ArgumentParser(description="Download owned YouTube captions via the YouTube Data API.") - parser.add_argument("--inventory", default=str(INVENTORY_PATH)) - parser.add_argument("--client-secrets", required=True, help="OAuth client secrets JSON path") - parser.add_argument("--token-file", default=".youtube-oauth-token.json", help="Cached OAuth token path") - parser.add_argument("--slug", action="append", help="Process only a specific slug") - parser.add_argument("--youtube-id", action="append", help="Process only a specific YouTube ID") - parser.add_argument("--limit", type=int, default=0) - parser.add_argument("--language", action="append", default=["en", "en-us", "en-gb"], help="Preferred caption language in priority order") - parser.add_argument("--only-missing", action="store_true", default=True) - parser.add_argument("--include-existing", dest="only_missing", action="store_false") - args = parser.parse_args() - - inventory_path = Path(args.inventory) - client_secrets = Path(args.client_secrets) - token_path = Path(args.token_file) - - credentials = get_credentials(client_secrets, token_path) - access_token = credentials.token - - items = select_items(load_inventory(inventory_path), args) - wrote = 0 - skipped = 0 - - for item in items: - youtube_id = str(item["youtube_id"]) - text_path, meta_path = transcript_paths(youtube_id) - if text_path.exists() and meta_path.exists() and args.only_missing: - print(f"cached transcript: {youtube_id}") - skipped += 1 - continue - text, detail = fetch_owned_caption(access_token, youtube_id, args.language) - if not text: - print(f"skipped transcript: {youtube_id} ({detail})") - skipped += 1 - continue - save_transcript( - youtube_id, - text, - { - "source": "fetched-owner-api", - "status": "transcript-fetched", - "quality": "needs-review", - "fetch_method": "youtube-data-api", - "caption_track_id": detail, - "source_path": item["source_path"], - }, - ) - print(f"fetched transcript: {youtube_id} via youtube-data-api") - wrote += 1 - - print(f"completed: wrote={wrote} skipped={skipped} processed={len(items)}") - - -if __name__ == "__main__": - try: - main() - except RuntimeError as err: - print(str(err), file=sys.stderr) - sys.exit(2) diff --git a/docs/website/scripts/fetch_missing_transcripts.py b/docs/website/scripts/fetch_missing_transcripts.py deleted file mode 100644 index 1c36d8c1a9..0000000000 --- a/docs/website/scripts/fetch_missing_transcripts.py +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import json -import subprocess -import time -from pathlib import Path - -from video_refresh_lib import ( - INVENTORY_PATH, - extract_transcript, - load_markdown_page, - normalize_transcript_text, - save_transcript, - transcript_has_meaningful_content, - transcript_paths, -) - - -def load_inventory(path: Path): - payload = json.loads(path.read_text(encoding="utf-8")) - return payload["items"] - - -def fetch_with_youtube_transcript_api(youtube_id: str): - try: - from youtube_transcript_api import YouTubeTranscriptApi - except ImportError: - return None, "youtube-transcript-api-not-installed" - - try: - if hasattr(YouTubeTranscriptApi, "get_transcript"): - transcript = YouTubeTranscriptApi.get_transcript(youtube_id, languages=["en"]) - text = "\n".join(chunk["text"].strip() for chunk in transcript if chunk.get("text")) - else: - transcript = YouTubeTranscriptApi().fetch(youtube_id, languages=["en"]) - text = "\n".join(getattr(chunk, "text", "").strip() for chunk in transcript if getattr(chunk, "text", "")) - except Exception as err: # pragma: no cover - network/runtime dependent - return None, str(err) - return text, "youtube_transcript_api" - - -def api_failure_is_authoritative(error_text: str) -> bool: - text = (error_text or "").lower() - return any( - token in text - for token in ( - "transcriptsdisabled", - "notranscriptfound", - "novideofound", - "video unavailable", - "the video is no longer available", - "subtitles are disabled", - "no transcripts were found", - ) - ) - - -def api_failure_is_ip_block(error_text: str) -> bool: - text = (error_text or "").lower() - return "requestblocked" in text or "ipblocked" in text or "youtube is blocking requests from your ip" in text - - -def fetch_with_yt_dlp(youtube_id: str): - command = [ - "yt-dlp", - "--skip-download", - "--write-auto-sub", - "--write-sub", - "--sub-langs", - "en.*", - "--sub-format", - "vtt", - "--output", - "-", - f"https://www.youtube.com/watch?v={youtube_id}", - ] - try: - result = subprocess.run(command, capture_output=True, text=True, check=False) - except FileNotFoundError: - return None, "yt-dlp-not-installed" - if result.returncode != 0: - return None, result.stderr.strip() or f"yt-dlp-exit-{result.returncode}" - text = normalize_transcript_text(result.stdout) - return text if transcript_has_meaningful_content(text) else None, "yt-dlp" - - -def select_items(items, args): - selected = [] - wanted_slugs = set(args.slug or []) - wanted_ids = set(args.youtube_id or []) - for item in items: - if item["page_kind"] == "course-hub": - continue - if wanted_slugs and item["slug"] not in wanted_slugs: - continue - if wanted_ids and item.get("youtube_id") not in wanted_ids: - continue - if args.only_missing: - allowed_statuses = {"transcript-missing"} - if args.retry_unavailable: - allowed_statuses.add("unavailable") - if item["transcript_status"] not in allowed_statuses: - continue - selected.append(item) - if args.limit: - selected = selected[: args.limit] - return selected - - -def should_mark_unavailable(args, fetch_error: str, used_fallback: bool) -> bool: - if not args.mark_unavailable: - return False - if used_fallback and not args.api_only: - return False - return api_failure_is_authoritative(fetch_error) - - -def should_stop_on_block(args, fetch_error: str) -> bool: - return args.stop_on_ip_block and api_failure_is_ip_block(fetch_error) - - -def remove_existing_transcript_artifacts(youtube_id: str): - text_path, meta_path = transcript_paths(youtube_id) - if text_path.exists(): - text_path.unlink() - if meta_path.exists(): - meta_path.unlink() - - -def update_unavailable_meta(meta_path: Path, youtube_id: str, source_path: str, fetch_error: str): - meta_path.parent.mkdir(parents=True, exist_ok=True) - meta = { - "youtube_id": youtube_id, - "source": "unavailable", - "status": "unavailable", - "quality": "unavailable", - "source_path": source_path, - "fetch_error": fetch_error or "authoritative-api-failure", - } - meta_path.write_text(json.dumps(meta, indent=2, sort_keys=True) + "\n", encoding="utf-8") - - -def main(): - parser = argparse.ArgumentParser(description="Fetch or extract transcripts for video pages.") - parser.add_argument("--inventory", default=str(INVENTORY_PATH)) - parser.add_argument("--slug", action="append", help="Process only a specific slug") - parser.add_argument("--youtube-id", action="append", help="Process only a specific YouTube ID") - parser.add_argument("--limit", type=int, default=0) - parser.add_argument("--only-missing", action="store_true", default=True) - parser.add_argument("--include-existing", dest="only_missing", action="store_false") - parser.add_argument("--retry-unavailable", action="store_true", help="Reprocess items currently marked unavailable") - parser.add_argument("--embedded-only", action="store_true", help="Only extract embedded transcripts") - parser.add_argument("--allow-fetch", action="store_true", help="Attempt network-based transcript fetching") - parser.add_argument("--api-only", action="store_true", help="Use youtube-transcript-api only and skip yt-dlp fallback") - parser.add_argument("--mark-unavailable", action="store_true", help="Write unavailable metadata only for authoritative API failures") - parser.add_argument("--delay-seconds", type=float, default=0.0, help="Sleep between network fetch attempts") - parser.add_argument("--stop-on-ip-block", action="store_true", help="Stop immediately if YouTube starts blocking transcript requests") - args = parser.parse_args() - - inventory_path = Path(args.inventory) - items = select_items(load_inventory(inventory_path), args) - written = 0 - unavailable = 0 - skipped = 0 - - for item in items: - youtube_id = item.get("youtube_id") - if not youtube_id: - continue - text_path, meta_path = transcript_paths(youtube_id) - if text_path.exists() and meta_path.exists() and args.only_missing: - continue - if args.retry_unavailable and item["transcript_status"] == "unavailable": - remove_existing_transcript_artifacts(youtube_id) - - page = load_markdown_page(Path(inventory_path.parents[1] / item["source_path"])) - embedded = extract_transcript(page.body) - if embedded: - save_transcript( - youtube_id, - embedded, - { - "source": "embedded", - "status": "transcript-fetched", - "quality": "needs-review", - "source_path": item["source_path"], - }, - ) - written += 1 - print(f"embedded transcript: {youtube_id} <- {item['source_path']}") - continue - - if args.embedded_only: - continue - - fetched = None - source = None - fetch_error = None - used_fallback = False - if args.allow_fetch: - if args.delay_seconds > 0: - time.sleep(args.delay_seconds) - fetched, source = fetch_with_youtube_transcript_api(youtube_id) - if not fetched: - fetch_error = source - if should_stop_on_block(args, fetch_error or ""): - print(f"stopping on ip block: {youtube_id} ({fetch_error})") - break - if not args.api_only: - used_fallback = True - if args.delay_seconds > 0: - time.sleep(args.delay_seconds) - fetched, source = fetch_with_yt_dlp(youtube_id) - if not fetched: - fetch_error = source - - if fetched: - save_transcript( - youtube_id, - fetched, - { - "source": "fetched-automated", - "status": "transcript-fetched", - "quality": "needs-review", - "fetch_method": source, - "source_path": item["source_path"], - }, - ) - written += 1 - print(f"fetched transcript: {youtube_id} via {source}") - elif should_mark_unavailable(args, fetch_error or "", used_fallback): - update_unavailable_meta(meta_path, youtube_id, item["source_path"], fetch_error or "") - unavailable += 1 - print(f"unavailable transcript: {youtube_id} ({fetch_error or 'authoritative-api-failure'})") - else: - skipped += 1 - print(f"skipped transcript: {youtube_id} ({fetch_error or 'not fetched'})") - - print(f"completed: wrote={written} unavailable={unavailable} skipped={skipped} processed={len(items)}") - - -if __name__ == "__main__": - main() diff --git a/docs/website/scripts/inject_transcripts.py b/docs/website/scripts/inject_transcripts.py deleted file mode 100644 index 09bc87265a..0000000000 --- a/docs/website/scripts/inject_transcripts.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import json -import re -from pathlib import Path - -from video_refresh_lib import ( - INVENTORY_PATH, - build_transcript_section, - extract_discussion, - extract_transcript, - heading_title, - load_markdown_page, - load_transcript_meta, - load_transcript_text, - split_frontmatter, -) - - -def remove_section(body: str, section_name: str) -> str: - lines = body.splitlines() - target_index = None - target_level = None - wanted = section_name.strip().lower() - for index, line in enumerate(lines): - heading = heading_title(line) - if not heading: - continue - level, title = heading - if title == wanted: - target_index = index - target_level = level - break - if target_index is None or target_level is None: - return body.strip() + "\n" - - kept = lines[:target_index] - skip = False - for line in lines[target_index + 1 :]: - heading = heading_title(line) - if heading and heading[0] <= target_level: - skip = True - if skip: - kept.append(line) - return "\n".join(kept).strip() + "\n" - - -def insert_before_discussion(body: str, block: str) -> str: - discussion_match = re.search(r"(?im)^##\s+discussion\s*$", body) - if discussion_match: - text = body[: discussion_match.start()].rstrip() + "\n\n" + block.strip() + "\n\n" + body[discussion_match.start() :].lstrip() - else: - text = body.rstrip() + "\n\n" + block.strip() + "\n" - text = re.sub(r"\n+---\s*\n*$", "\n", text.rstrip() + "\n") - return text.rstrip() + "\n" - - -def load_inventory(path: Path): - return json.loads(path.read_text(encoding="utf-8"))["items"] - - -def main(): - parser = argparse.ArgumentParser(description="Inject cached transcripts into markdown pages.") - parser.add_argument("--inventory", default=str(INVENTORY_PATH)) - parser.add_argument("--slug", action="append") - parser.add_argument("--youtube-id", action="append") - parser.add_argument("--include-unavailable-note", action="store_true") - args = parser.parse_args() - - wanted_slugs = set(args.slug or []) - wanted_ids = set(args.youtube_id or []) - inventory_path = Path(args.inventory) - items = load_inventory(inventory_path) - updated = 0 - - for item in items: - if item["page_kind"] == "course-hub": - continue - if wanted_slugs and item["slug"] not in wanted_slugs: - continue - if wanted_ids and item.get("youtube_id") not in wanted_ids: - continue - - youtube_id = item.get("youtube_id") - transcript = load_transcript_text(youtube_id) if youtube_id else None - meta = load_transcript_meta(youtube_id) if youtube_id else {} - if not transcript and not (args.include_unavailable_note and meta.get("status") == "unavailable"): - continue - - path = inventory_path.parents[1] / item["source_path"] - text = path.read_text(encoding="utf-8") - frontmatter, body = split_frontmatter(text) - cleaned_body = remove_section(body, "transcript") - block = build_transcript_section(transcript, meta) - new_body = insert_before_discussion(cleaned_body, block) - path.write_text(f"---\n{frontmatter}\n---\n{new_body}", encoding="utf-8") - updated += 1 - print(f"updated transcript section: {path}") - - print(f"completed: updated={updated}") - - -if __name__ == "__main__": - main() diff --git a/docs/website/scripts/normalize_transcript_cache.py b/docs/website/scripts/normalize_transcript_cache.py deleted file mode 100644 index a7715d47ee..0000000000 --- a/docs/website/scripts/normalize_transcript_cache.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -import argparse -from pathlib import Path - -from video_refresh_lib import TRANSCRIPTS_DIR, load_transcript_meta, save_transcript - - -def main(): - parser = argparse.ArgumentParser(description="Normalize transcript cache files and backfill metadata.") - parser.add_argument("--transcripts-dir", default=str(TRANSCRIPTS_DIR)) - parser.add_argument("--youtube-id", action="append", help="Normalize only specific YouTube IDs") - args = parser.parse_args() - - base = Path(args.transcripts_dir) - wanted_ids = set(args.youtube_id or []) - updated = 0 - - for text_path in sorted(base.glob("*.txt")): - youtube_id = text_path.stem - if wanted_ids and youtube_id not in wanted_ids: - continue - raw = text_path.read_text(encoding="utf-8", errors="replace") - meta = load_transcript_meta(youtube_id) - if not meta: - meta = { - "source": "fetched-manual", - "status": "transcript-fetched", - "quality": "needs-review", - } - else: - meta = dict(meta) - meta.setdefault("source", "fetched-manual") - meta.setdefault("status", "transcript-fetched") - meta.setdefault("quality", "needs-review") - save_transcript(youtube_id, raw, meta) - updated += 1 - print(f"normalized: {youtube_id}") - - print(f"completed: updated={updated}") - - -if __name__ == "__main__": - main() diff --git a/docs/website/scripts/report_video_refresh_progress.py b/docs/website/scripts/report_video_refresh_progress.py deleted file mode 100644 index 33cbce686a..0000000000 --- a/docs/website/scripts/report_video_refresh_progress.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import json -from collections import Counter, defaultdict -from pathlib import Path - -from video_refresh_lib import INVENTORY_PATH - - -def load_inventory(path: Path): - return json.loads(path.read_text(encoding="utf-8"))["items"] - - -def print_counter(title, counter): - print(title) - for key in sorted(counter): - print(f" {key}: {counter[key]}") - print() - - -def main(): - parser = argparse.ArgumentParser(description="Report video refresh progress.") - parser.add_argument("--inventory", default=str(INVENTORY_PATH)) - args = parser.parse_args() - - items = load_inventory(Path(args.inventory)) - by_kind = Counter(item["page_kind"] for item in items) - by_priority = Counter(item["priority"] for item in items) - by_transcript = Counter(item["transcript_status"] for item in items) - by_guide = Counter(item["guide_status"] for item in items) - by_script = Counter(item["script_status"] for item in items) - by_course = Counter(item["course_id"] or item["page_kind"] for item in items) - - print_counter("By Page Kind", by_kind) - print_counter("By Priority", by_priority) - print_counter("By Transcript Status", by_transcript) - print_counter("By Guide Status", by_guide) - print_counter("By Script Status", by_script) - print_counter("By Course", by_course) - - -if __name__ == "__main__": - main() diff --git a/docs/website/scripts/video_refresh_lib.py b/docs/website/scripts/video_refresh_lib.py deleted file mode 100644 index 30fc24f221..0000000000 --- a/docs/website/scripts/video_refresh_lib.py +++ /dev/null @@ -1,283 +0,0 @@ -#!/usr/bin/env python3 -import json -import re -from dataclasses import dataclass -from pathlib import Path -from typing import Dict, List, Optional, Tuple - - -ROOT = Path(__file__).resolve().parents[1] -CONTENT = ROOT / "content" -DATA = ROOT / "data" -HOWDOI_DIR = CONTENT / "howdoi" -COURSES_DIR = CONTENT / "courses" -TRANSCRIPTS_DIR = ROOT / "video-transcripts" -INVENTORY_PATH = DATA / "video_refresh_inventory.json" -HOWDOI_INDEX_PATH = DATA / "howdoi_index.json" -COURSE_HUBS = [ - CONTENT / "course-01-java-for-mobile-devices.md", - CONTENT / "course-02-deep-dive-mobile-development-with-codename-one.md", - CONTENT / "course-03-build-real-world-full-stack-mobile-apps-java.md", -] - -YOUTUBE_RE = re.compile(r"\{\{<\s*youtube\s+\"?([A-Za-z0-9_-]+)\"?\s*>\}\}") -HEADING_RE = re.compile(r"^(#{1,6})\s+(.*?)\s*$") - - -@dataclass -class MarkdownPage: - path: Path - meta: Dict[str, object] - body: str - - -def split_frontmatter(text: str) -> Tuple[str, str]: - if text.startswith("---\n"): - end = text.find("\n---\n", 4) - if end != -1: - return text[4:end], text[end + 5 :] - return "", text - - -def parse_frontmatter(frontmatter: str) -> Dict[str, object]: - meta: Dict[str, object] = {} - current_key: Optional[str] = None - for raw_line in frontmatter.splitlines(): - line = raw_line.rstrip() - if not line.strip(): - continue - if line.startswith("- ") and current_key: - meta.setdefault(current_key, []) - assert isinstance(meta[current_key], list) - meta[current_key].append(parse_scalar(line[2:].strip())) - continue - if ":" not in line: - current_key = None - continue - key, value = line.split(":", 1) - key = key.strip() - value = value.strip() - if value == "": - meta[key] = [] - current_key = key - continue - meta[key] = parse_scalar(value) - current_key = key - return meta - - -def parse_scalar(value: str): - if value.startswith('"') and value.endswith('"'): - return value[1:-1] - if value.startswith("'") and value.endswith("'"): - return value[1:-1] - if value.lower() == "true": - return True - if value.lower() == "false": - return False - if re.fullmatch(r"-?\d+", value): - try: - return int(value) - except ValueError: - return value - return value - - -def load_markdown_page(path: Path) -> MarkdownPage: - text = path.read_text(encoding="utf-8") - frontmatter, body = split_frontmatter(text) - return MarkdownPage(path=path, meta=parse_frontmatter(frontmatter), body=body) - - -def derive_public_url(page: MarkdownPage, page_kind: str) -> str: - url = str(page.meta.get("url", "")).strip() - if url: - return url - slug = str(page.meta.get("slug", page.path.stem)) - if page_kind == "howdoi": - return f"/how-do-i/{slug}/" - if page_kind == "course-hub": - return f"/{slug}/" - course_id = str(page.meta.get("course_id", "")) - return f"/courses/{course_id}/{page.path.stem}/" - - -def find_youtube_id(body: str) -> Optional[str]: - match = YOUTUBE_RE.search(body) - return match.group(1) if match else None - - -def heading_title(line: str) -> Optional[Tuple[int, str]]: - match = HEADING_RE.match(line) - if not match: - return None - return len(match.group(1)), match.group(2).strip().lower() - - -def extract_section(body: str, section_name: str) -> Optional[str]: - lines = body.splitlines() - target_index = None - target_level = None - wanted = section_name.strip().lower() - for index, line in enumerate(lines): - heading = heading_title(line) - if not heading: - continue - level, title = heading - if title == wanted: - target_index = index - target_level = level - break - if target_index is None or target_level is None: - return None - - collected: List[str] = [] - for line in lines[target_index + 1 :]: - heading = heading_title(line) - if heading and heading[0] <= target_level: - break - collected.append(line) - text = "\n".join(collected).strip() - return text or None - - -def extract_transcript(body: str) -> Optional[str]: - transcript = extract_section(body, "transcript") - if not transcript: - return None - cleaned = normalize_transcript_text(transcript) - return cleaned if transcript_has_meaningful_content(cleaned) else None - - -def extract_discussion(body: str) -> Optional[str]: - return extract_section(body, "discussion") - - -def transcript_has_meaningful_content(text: str) -> bool: - if not text: - return False - words = text.split() - if len(words) < 20: - return False - alnum = sum(ch.isalnum() for ch in text) - return alnum >= 80 - - -def normalize_transcript_text(text: str) -> str: - lines = [line.rstrip() for line in text.replace("\r\n", "\n").split("\n")] - normalized: List[str] = [] - last_non_empty = None - for raw in lines: - line = re.sub(r"\s+", " ", raw).strip() - if line == "---": - continue - if re.fullmatch(r"\d{1,2}:\d{2}(?::\d{2})?", line): - continue - if re.fullmatch(r"\d+\s*-->\s*\d+", line): - continue - if line == last_non_empty and line: - continue - if line: - normalized.append(line) - last_non_empty = line - elif normalized and normalized[-1] != "": - normalized.append("") - return "\n".join(normalized).strip() - - -def transcript_paths(youtube_id: str) -> Tuple[Path, Path]: - return TRANSCRIPTS_DIR / f"{youtube_id}.txt", TRANSCRIPTS_DIR / f"{youtube_id}.json" - - -def load_transcript_meta(youtube_id: str) -> Dict[str, object]: - _, meta_path = transcript_paths(youtube_id) - if not meta_path.exists(): - return {} - return json.loads(meta_path.read_text(encoding="utf-8")) - - -def load_transcript_text(youtube_id: str) -> Optional[str]: - text_path, _ = transcript_paths(youtube_id) - if not text_path.exists(): - return None - return text_path.read_text(encoding="utf-8").strip() or None - - -def save_transcript(youtube_id: str, text: str, meta: Dict[str, object]) -> None: - TRANSCRIPTS_DIR.mkdir(parents=True, exist_ok=True) - normalized = normalize_transcript_text(text) - text_path, meta_path = transcript_paths(youtube_id) - text_path.write_text(normalized.rstrip() + "\n", encoding="utf-8") - meta = dict(meta) - meta["youtube_id"] = youtube_id - meta["line_count"] = len([line for line in normalized.splitlines() if line.strip()]) - meta["word_count"] = len(normalized.split()) - meta_path.write_text(json.dumps(meta, indent=2, sort_keys=True) + "\n", encoding="utf-8") - - -def load_howdoi_index() -> Dict[str, Dict[str, object]]: - if not HOWDOI_INDEX_PATH.exists(): - return {} - items = json.loads(HOWDOI_INDEX_PATH.read_text(encoding="utf-8")) - return {str(item["slug"]): item for item in items} - - -def stable_relpath(path: Path) -> str: - return path.relative_to(ROOT).as_posix() - - -def normalize_slug_from_lesson_filename(name: str) -> str: - return re.sub(r"^\d+-", "", name) - - -def determine_priority(item: Dict[str, object]) -> int: - text = " ".join( - str(item.get(key, "")).lower() - for key in ("lesson_title", "module_title", "slug", "course_id") - ) - if any( - token in text - for token in ( - "hello-world", - "hello world", - "setup", - "installation", - "layout", - "theme", - "css", - "build", - "debug", - "native interface", - "native-code", - "native code", - "push", - ) - ): - return 0 - if str(item.get("content_type")) == "howdoi" or "course-01" in text: - return 1 - return 2 - - -def inventory_sort_key(item: Dict[str, object]): - return ( - int(item.get("priority", 99)), - str(item.get("content_type", "")), - str(item.get("course_id", "")), - str(item.get("source_path", "")), - ) - - -def build_transcript_section(transcript: Optional[str], meta: Dict[str, object]) -> str: - lines = ["## Transcript", ""] - if transcript: - source = str(meta.get("source", "unknown")) - lines.append(f"_Transcript source: {source}._") - lines.append("") - lines.append(transcript.strip()) - else: - status = str(meta.get("status", "transcript-missing")) - lines.append(f"_Transcript status: {status}._") - lines.append("") - lines.append("Transcript not yet available for this video.") - return "\n".join(lines).strip() diff --git a/docs/website/video-transcripts/-7XHkBMK4NY.json b/docs/website/video-transcripts/-7XHkBMK4NY.json deleted file mode 100644 index e3b4325f26..0000000000 --- a/docs/website/video-transcripts/-7XHkBMK4NY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 120, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 652, - "youtube_id": "-7XHkBMK4NY" -} diff --git a/docs/website/video-transcripts/-7XHkBMK4NY.txt b/docs/website/video-transcripts/-7XHkBMK4NY.txt deleted file mode 100644 index eda5b0b75c..0000000000 --- a/docs/website/video-transcripts/-7XHkBMK4NY.txt +++ /dev/null @@ -1,120 +0,0 @@ -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/video-transcripts/-M957AAi-vk.json b/docs/website/video-transcripts/-M957AAi-vk.json deleted file mode 100644 index 159c40962a..0000000000 --- a/docs/website/video-transcripts/-M957AAi-vk.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 28, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-use-http-sockets-webservices-websockets.md", - "status": "transcript-fetched", - "word_count": 1159, - "youtube_id": "-M957AAi-vk" -} diff --git a/docs/website/video-transcripts/-M957AAi-vk.txt b/docs/website/video-transcripts/-M957AAi-vk.txt deleted file mode 100644 index 859c711a81..0000000000 --- a/docs/website/video-transcripts/-M957AAi-vk.txt +++ /dev/null @@ -1,48 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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 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. - -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. - -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. - -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. - -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 diff --git a/docs/website/video-transcripts/-aTpqxDa1Ag.json b/docs/website/video-transcripts/-aTpqxDa1Ag.json deleted file mode 100644 index 1ed7f411af..0000000000 --- a/docs/website/video-transcripts/-aTpqxDa1Ag.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 117, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 707, - "youtube_id": "-aTpqxDa1Ag" -} diff --git a/docs/website/video-transcripts/-aTpqxDa1Ag.txt b/docs/website/video-transcripts/-aTpqxDa1Ag.txt deleted file mode 100644 index 07cf6af9c0..0000000000 --- a/docs/website/video-transcripts/-aTpqxDa1Ag.txt +++ /dev/null @@ -1,117 +0,0 @@ -how do we get friends -we already implemented contact upload -both in the server and the server api -class -we need to map that to the ui though -facebook nags about uploading contacts -all over the place which is probably -something you should do to if you are -building a social network i don't want -to get into that deep level of nagging -so i decided to add a floating action -button to the friends container -this is a bit tricky though -normally when we add a floating action -button we add it to a form -this hides some complexity it seems like -we are adding it to a form but in fact -it's hidden to a hidden layered pane -when we want to add it only to a -specific tab we need a better -understanding of the underlying -implementation -the floating action button wraps the -given container in a layered layout and -it places itself on top it then returns -a new container which you should use -instead of the original container -normally that's pretty easy to do but -it's obviously impossible to do within -friends container as it's the container -the solution is to implement this code -in the main form class -here we created a tab -for the contact upload -we find the fab and grab the return -value instead of the friends container -instance -pressing the fab will invoke the upload -contacts method -notice we add the returned friends -container that we wrapped with the fab -now that we have that we can implement -the upload contacts method -we need to fetch contacts from the -system it makes sense to do this on a -separate thread as this is a heavy task -when we fetch the contacts we specify -the fields we are interested in -if we specify fewer fields the fetch -operation will be faster -despite the name of the start thread -method we still need to invoke start on -the returned thread -once this is done pressing that button -should automatically prompt for -permission and upload the contacts from -your phone -in order to make the fab look good we -also need a couple of css changes -this mostly sets the fab to facebook -style blue color -each friend's suggestion has a button to -accept or remove the suggestion each -friend request has -similar button pairs -we added the bind confirm delete event -or bind add remove friends event call to -bind event listing to the buttons -when we remove or add a button we need -to remove the container of -the friend from the hierarchy but at the -stage where we bind the event we don't -have an instance of that container yet -a workaround is to locate that container -by traversing through the hierarchy of -the components -here we recursively look through parent -containers we stop when the parent is -the friend's container itself -this relies on the fact that friends -suggestion is added directly to the -parent friends container -once we have this implementing bind add -remove friend event -and bind confirm delete event becomes -trivial -we send a friend request -to the server when the button is pressed -after removing the container with the -friend's suggestion we animate the -remaining ui into place -we refresh the me object as it might be -stale after this change -the rest of the code is nearly identical -we invoke the server api code to trigger -the appropriate server changes -with this we can now send and accept -friend requests -the last piece are in the ui mapping is -notification support this is pretty -trivial now that we finished everything -just like before we no longer need the -last time value and can use the paging -support instead -we iterate over the notification objects -and create entry components for each one -new notifications will arrive via push -when we implement the push support later -on -with that we are effectively done with -the first part of the app -we have a fully working client server -mock-up there are still a lot of missing -pieces but this just became a process of -filling in the gaps it's much easier to -fill in the gaps once the foundation is -laid out in place diff --git a/docs/website/video-transcripts/-brmZYVWb0Y.json b/docs/website/video-transcripts/-brmZYVWb0Y.json deleted file mode 100644 index 57e4ae456e..0000000000 --- a/docs/website/video-transcripts/-brmZYVWb0Y.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 194, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1085, - "youtube_id": "-brmZYVWb0Y" -} diff --git a/docs/website/video-transcripts/-brmZYVWb0Y.txt b/docs/website/video-transcripts/-brmZYVWb0Y.txt deleted file mode 100644 index b3792bf469..0000000000 --- a/docs/website/video-transcripts/-brmZYVWb0Y.txt +++ /dev/null @@ -1,194 +0,0 @@ -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/video-transcripts/-f1yue3hDEk.json b/docs/website/video-transcripts/-f1yue3hDEk.json deleted file mode 100644 index e892255b87..0000000000 --- a/docs/website/video-transcripts/-f1yue3hDEk.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 162, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/006-css.md", - "status": "transcript-fetched", - "word_count": 857, - "youtube_id": "-f1yue3hDEk" -} diff --git a/docs/website/video-transcripts/-f1yue3hDEk.txt b/docs/website/video-transcripts/-f1yue3hDEk.txt deleted file mode 100644 index 849fde7c7f..0000000000 --- a/docs/website/video-transcripts/-f1yue3hDEk.txt +++ /dev/null @@ -1,162 +0,0 @@ -in this part we'll review the css -required to bind the application ui -if you haven't done so i'd recommend -checking out the video or material about -css integration in codename one as this -section assumes it's something that you -are familiar with -the constant selector is a special case -for theme constants -normally an include native ball must be -true -but in this case we have a layered theme -and setting this to force works around -some issues -the renderer show numbers ball flag -hides numbers in the list renderer -which you normally should define if you -use lists -for most themes container and label are -transparent but not in all themes -so it's important to define these -zero padding and margin are also very -important for a container -they should be there by default but this -makes sure -other than that -we just set a decent font for the label -the only entry for form -sets the background color of the form we -can leave the defaults in place -the title uid -makes sure the title is transparent -but most importantly -it disables a border that might be -applied to that ui id by the native -theme -it also enforces center alignment which -is the default in ios -but not on android -the command and menu color is set to -these two ui ids -i -i mentioned before that the area here is -a special component that abstracts the -view of the underlying image -this component is of the same color as -the form color with no margin so we will -get the visual effect -of the buttons peaking below the image -we place the title image that we cut out -before -in the background on top with a scale to -fill option -this allows us to fill up available -space -and possibly overflow -from the sides top or bottom -the order section -on top -uses a round rectangle border -which in css is defined as a pill -border notice the generous amount of -padding to push the border -so it won't seem cramped -also notice that the right padding isn't -even -the reason for that -is the shadow of the round border that -is technically a part of the border -since the shadow is cast to the bottom -right -we need more padding on those sides -the shopping cart icon -uses a regular round border -it's the same border type as the pull -border in codename one -but in css they are defined differently -notice the usage of margin to push the -border -from the right side -to distance it from the right edge -of the form -the list renderer is opaque by default -so we need to specify it as transparent -in the design -the text is slightly translucent -but that's a bit expensive and makes the -text too hard to read -instead i chose to just give it a -grayish color -which produces a similar effect without -performance implications and better -readability -i also -had to align the text to the center -as it's aligned to the left by the fault -and that looks weird in the carousel -mode -the selected version of the renderer is -white -and appears in the center -the positioning of the selected element -and its behavior are determined in the -code -the title -of each entry -is determined here -i use a decent font -and size although in a later revision i -made the one larger -which improved the design -i copied the color of the text from the -psd file -for the body -and used a thinner font to differentiate -from the title -the border of the buttons is a nine -piece image border -i defined it in the theme itself with -the gui tool as i found that to be -easier and more reliable -that might be due to personal habit as -i'm not used to css -i used some padding to space out the -content of the button -a common trick to implementing a -separator line is to use one pixel -padding with an opaque or translucent -color -that's what i did here -i also added three millimeter padding on -both sides to make sure the separator -spans correctly in this case -moving on to the checkout ui -most of the details in this page are -relatively simple -the one thing that's slightly different -but reasonably obvious -is the right alignment of the price -total entry -the checkout button derives from the add -to border button -and increases the size padding and -margin of everything -using inheritance in this way is -valuable as it allows us to reuse a -design element and thus save ram in -cases like this -where a nine piece border is used -the pieces we cut for the recipe -background we're cut into a nine piece -border in the theme designer -here -we define the padding and margin -the padding is required -so the text won't exceed the boundaries -of the nine piece image and the margin -helps us space the component from the -edges -notice the top bottom margins here -are zero -so the two separate pieces can touch diff --git a/docs/website/video-transcripts/008AK1GfHA8.json b/docs/website/video-transcripts/008AK1GfHA8.json deleted file mode 100644 index 74ae6469e1..0000000000 --- a/docs/website/video-transcripts/008AK1GfHA8.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 11, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-debug-on-an-android-device.md", - "status": "transcript-fetched", - "word_count": 284, - "youtube_id": "008AK1GfHA8" -} diff --git a/docs/website/video-transcripts/008AK1GfHA8.txt b/docs/website/video-transcripts/008AK1GfHA8.txt deleted file mode 100644 index 9516986323..0000000000 --- a/docs/website/video-transcripts/008AK1GfHA8.txt +++ /dev/null @@ -1,18 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -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. - -Some additional copying of gradle script snippets might be required based on your app! - -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/). diff --git a/docs/website/video-transcripts/0ETli_N__ZY.json b/docs/website/video-transcripts/0ETli_N__ZY.json deleted file mode 100644 index 2387b17bb4..0000000000 --- a/docs/website/video-transcripts/0ETli_N__ZY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 46, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 257, - "youtube_id": "0ETli_N__ZY" -} diff --git a/docs/website/video-transcripts/0ETli_N__ZY.txt b/docs/website/video-transcripts/0ETli_N__ZY.txt deleted file mode 100644 index 6afea75b1f..0000000000 --- a/docs/website/video-transcripts/0ETli_N__ZY.txt +++ /dev/null @@ -1,46 +0,0 @@ -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/video-transcripts/0a6mPI412C4.json b/docs/website/video-transcripts/0a6mPI412C4.json deleted file mode 100644 index d2e795f787..0000000000 --- a/docs/website/video-transcripts/0a6mPI412C4.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 114, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 670, - "youtube_id": "0a6mPI412C4" -} diff --git a/docs/website/video-transcripts/0a6mPI412C4.txt b/docs/website/video-transcripts/0a6mPI412C4.txt deleted file mode 100644 index 9f7b37a801..0000000000 --- a/docs/website/video-transcripts/0a6mPI412C4.txt +++ /dev/null @@ -1,114 +0,0 @@ -the one last form we'll work on in the -markup stage is the new post form -right now i'll only support simple -variations of the new post i'll deal -with image and video posting later -we will discuss some built-in styles and -use html to display formatted posts -this could allow for logic that -implements things such as hashtags etc -in a future implementation -unlock the tabs we had until now the new -post is a separate form -the new post form includes styles that -can be applied to the editing such as -colors background images etc -the form itself uses a border layout to -position title area -on the top and the rest of the ui over -the entire screen -we save the current form before showing -so we can go back to it -the avatar of the user -goes on the left side of the title area -followed by a button that should let us -pick the right visibility mode for the -post -the text of the post is just a standard -text area with 3 rows and 80 columns -they are just hints for the preferred -size of the component -if we input more than 3 rows the ui -shouldn't grow -it might go on top of the post style -container and it would be hard to -differentiate a tap -there from -text edit gesture -this method creates the bar at the -bottom of the screen -when we click an entry in the bar -the look changes and the post gets a new -style -the bar on the bottom of the form is an -x-axis box layout -that's scrollable on the x-axis -every icon is eight millimeters in size -we loop over the style types the first -style is label -which means an unstyled regular post -we create a radio toggle button for -every style and set the style to the -button -for the rounded corners we use the round -rect border class and apply it manually -as the style is designed for the entire -entry -the first entry is a special case -because text fields are sometimes -transparent and so are labels -this implements the selected line border -around an entry -notice stroke color can be black when -surrounding the white label -entry when the user clicks on one of -these radio buttons we invoke the change -style method -this brings us directly to the change -style method -we need a special case for the first -entry again as the behavior is slightly -different -separate ui ids are needed for the text -area and the foreground -before we go into the css we need one -more thing to get this working -we need the following line in the -newsfeed container class -in the create -post bar method -there is quite a bit of css listed here -let's start with the simple stuff the -friend combo -this is the gray round wrecked border -that's applied to the visibility button -this button determines if something is -seen only by friends or is -public this is the base ui id for every -one of the uids listed in the array it -isn't mentioned in the code but it's -used in the css itself -next we have the css definitions of the -text styles -for this code we'll need -hearts.jpg and hands.jpg that represent -two of the designs -notice that all the styles here derive -from post style bass -i fill the hearts pattern into place -even though -it might make sense to tile it in some -cases -i scale to fit the hands entry i'm not -sure if it's the best way as even in the -native app the hands disappear in some -cases -the rest of the styles are pretty -trivial and should be easy to understand -this is the style of the foreground text -when another style is applied to the -background -with that we are done -you should have a working mock-up with a -sign-up wizard and core functionality -the core functionality we will work on diff --git a/docs/website/video-transcripts/0l4R049tSOY.json b/docs/website/video-transcripts/0l4R049tSOY.json deleted file mode 100644 index 6d16ca6906..0000000000 --- a/docs/website/video-transcripts/0l4R049tSOY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 52, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 267, - "youtube_id": "0l4R049tSOY" -} diff --git a/docs/website/video-transcripts/0l4R049tSOY.txt b/docs/website/video-transcripts/0l4R049tSOY.txt deleted file mode 100644 index 2d5aeeb9f7..0000000000 --- a/docs/website/video-transcripts/0l4R049tSOY.txt +++ /dev/null @@ -1,52 +0,0 @@ -the last piece of the puzzle -is the automatic update -since certificates expire every 90 days -we need to automate their replacement -and luckily linux is pretty darn -powerful with these capabilities -first we need to create a new script -file -with the root user -we use the nano text editor to create a -new script -the script is pretty simple we do the -exact steps i mentioned previously -one by one -all of the commands -the one big problem is that the server -will effectively go down every two -months for a couple of minutes -to perform this task -we could probably come up with a better -approach for this -but this is what i came up with -after saving the script -i converted to an executable file using -the change mode command -this step is important for the next -command which edits the chrome tab by -default it would launch vim which i -don't really like so i set the editor -variable to nano this means the next -command will launch nano -the chrome tab tools -tool allows us to define -tasks that run at a specific schedule -it launches an editor -where we can define the syntax of the -chrome -command -this essentially defines that the script -will run -on the first of every other month -as we only list the odd number months -in the list -these cases -in these cases let's encrypt will be -invoked -this is pretty much it -the certificate should be renewed -implicitly on the first of every other -month -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/0m7Bay4g93k.json b/docs/website/video-transcripts/0m7Bay4g93k.json deleted file mode 100644 index e75076b7fa..0000000000 --- a/docs/website/video-transcripts/0m7Bay4g93k.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 29, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-create-a-list-of-items-the-easy-way.md", - "status": "transcript-fetched", - "word_count": 1200, - "youtube_id": "0m7Bay4g93k" -} diff --git a/docs/website/video-transcripts/0m7Bay4g93k.txt b/docs/website/video-transcripts/0m7Bay4g93k.txt deleted file mode 100644 index 1172cd5097..0000000000 --- a/docs/website/video-transcripts/0m7Bay4g93k.txt +++ /dev/null @@ -1,50 +0,0 @@ -_Transcript source: embedded._ - -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! - -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. - -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. - -Lets start with a hello world for a box layout list. This is pretty standard Codename One code, lets go over the different pieces. - -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. - -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. - -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. diff --git a/docs/website/video-transcripts/16-Vkgcx2kg.json b/docs/website/video-transcripts/16-Vkgcx2kg.json deleted file mode 100644 index e176cd6e59..0000000000 --- a/docs/website/video-transcripts/16-Vkgcx2kg.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 147, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/021-implementing-the-native-camera-on-android.md", - "status": "transcript-fetched", - "word_count": 873, - "youtube_id": "16-Vkgcx2kg" -} diff --git a/docs/website/video-transcripts/16-Vkgcx2kg.txt b/docs/website/video-transcripts/16-Vkgcx2kg.txt deleted file mode 100644 index cc5661725f..0000000000 --- a/docs/website/video-transcripts/16-Vkgcx2kg.txt +++ /dev/null @@ -1,147 +0,0 @@ -now that we have the generic code of the -library let's go into the Android port -if this is the first tutorial on -building native interfaces you are going -through then here's an important tip use -the include Source feature of codename -one and build an Android Studio project -then implement the native interface -within Android Studio you can then copy -and paste the code back into the -codename one project -this is far more convenient as you can -Implement and debug the code right -within the native IDE -the same is true for xcode when working -on a Mac in this case because the native -interface was so simple albeit large -I skipped that stage but I have a bit of -experience doing this -most of you would probably feel more -comfortable building the Hello World app -with the native interface and -implementing it in Android Studio -the Android port is Trivial once we have -the native interface we can right click -on the project and choose generate -native stubs -which produces the skeleton code for -this implementation class -unlike before I'll start with the -Imports as some of them are a bit -challenging -I just imported everything as it was -simpler -this is an important helper class when -writing native Android code you will see -me use it soon -I wanted one method from the -implementation run on UI thread and wait -it's really useful in some cases -let's get into the code itself -the view is the camera preview UI -I keep it as a member member to return -later -The Listener is bound later in the code -it just delegates all the calls into the -Callback -notice I have only one listener on the -native side -all calls are effectively the same they -just invoke the camera callbacks class -methods -Android native file paths should be -prepended with file URL to work and -codename ones file system storage -the next step is life cycle and view -methods -after the start method is invoked I'd -like the view object to be valid -otherwise we might have a problem -this code must run on Android's native -EDT hence the need for run on UI thread -and block -we initialize the view by The Listener -and stopped notice we can stop and start -more than once -we don't need to wait for stop to -complete but it still needs to run on -Android's dispatch to it -this method returns a peer component in -the Java side -the Android view is translated -automatically -there is supported method is a standard -method in Native interfaces which -defaults to false when you implement a -version in a platform set it to true -next let's do a quick recap of the -setter methods in the native interface -we can't use a Lambda yet because we are -still using Java 5 for Android native -code this might change by the time you -see this -we hope to upgrade Android native code -for newer builds to use Java 8 syntax -which will shorten the boilerplate here -significantly -all of the methods here work roughly in -the same way by delegating to the native -core via run on UI thread core I could -go for a couple of additional pages of -Setters but you get the drift it's more -of the same -next we can review the Getters which are -shorter notice we don't need to use -run-on UI thread as the Getters aren't -as sensitive to threading -as you may recall some of the methods -here mapped to properties of The -Returned objects as is the case here and -with the preview slash capture methods -below -like the Setters this goes on a bit -further so I'll spare you an additional -slide where I don't have anything -interesting to see -finally we have the capture method calls -we need run on UI thread as capture is -thread sensitive -files and codename one have file URL -prepended so they will act as urls -we remove this to translate the file -into an Android file -all the other methods are exactly the -same they just invoke the native Android -version -this is it for the code but it won't -work yet -we need three build hands to get this to -work -the camera kit page asks that we add the -Gradle bin build dependency -codename one the codename one equivalent -of that is the build hint Android dot -Gradle dep -th the page also mentions a pro God -entry a bit down in the page -this is handled by the android.progod -keep build hand -unfortunately if you go through all of -that things still won't compile because -of the old build tools version and -Target so we need to use the latest -version of build tools which camera kit -depends on -specifically Android dot build tools -version 27. -this line is a temporary fix -we will soon flip the default build -tools version 227 at which point it will -be redundant -once this is done we recommend removing -it so when we go to 28 it will use that -once all of that is done you should be -able to use camera kit on Android the -challenge is now -iOS diff --git a/docs/website/video-transcripts/17ISIksjcPM.json b/docs/website/video-transcripts/17ISIksjcPM.json deleted file mode 100644 index b4c0270076..0000000000 --- a/docs/website/video-transcripts/17ISIksjcPM.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 266, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/022-camera-ios-port-basics.md", - "status": "transcript-fetched", - "word_count": 1422, - "youtube_id": "17ISIksjcPM" -} diff --git a/docs/website/video-transcripts/17ISIksjcPM.txt b/docs/website/video-transcripts/17ISIksjcPM.txt deleted file mode 100644 index 03753d9794..0000000000 --- a/docs/website/video-transcripts/17ISIksjcPM.txt +++ /dev/null @@ -1,266 +0,0 @@ -the ios port is a steep climb unlike the -android version which maps almost -directly to the native code -still one of the advantages in ios -programming is the cleaner underlying -api that often simplifies common use -cases -due to this i chose to skip third-party -libraries and try to implement the -functionality of camera kit directly on -the native ios apis -when we use generate native stubs the -ios stubs include two files an h and an -m file -let's review the h file first i'll look -at what was generated in both -before we begin -this is a standard objective c -import statement that adds the basic -apple ios api -important objective c are more like c -includes than java imports -this is the class definition for the -native interface notice that ns object -is the common base class here -the method signatures should be pretty -readable as they map directly to the -java equivalent -getview expects a peer component on the -java side from this side -will return a ui view instance -once we get this -the implementation code is more of the -same -we import the header file we just -reviewed -just like includes in c -method implementations match their -declaration with curly braces for the -implementation -parameter names are important in ios -if you change the name of a parameter -you will break compilation as the name -is a part of the method signature -most types are familiar -but some like boolean -use a slightly different syntax with yes -and no instead of true and false -the same is true for nil instead of null -we'll start by bringing in constants -from the java constants interface -in the java implementation we could -ignore their values and the native side -because the implementation -already used the exact same values -we don't have that privilege -i'll also add an import to the native av -foundation -audio video foundation which is the ios -api for media -there are copies directly -from the java code with the public -static final portion replaced -to const -this will make coding the rest -easier -so now that we have the sense -of the scope let's start implementing -the important methods one by one -the natural place to start -is the start method -for this to work we first need to define -the first time camera kit launch -variable -the first time start is invoked we -initialize the default values of the -various constants to identify two -identical values we have in the android -version -this method initializes the camera view -the first time around notice that self -is the equivalent of this -dispatch sync is the ios equivalent of -call serially and wait -we want the block below to execute on -the native ios thread -and we want to wait until -it's defined it's finished -the capture session is stopped on the -stop call so if this isn't the first -time around we need to restart the -captcha session -before we proceed to the lazy init -method -let's look at the variables we added -into the header file -the direction the camera is facing front -or back -where the flash is on off or out of -flash mode -focus can be based on point or automatic -allows for several modes of capture i -didn't implement this for now -a set of constants indicating the -resolution for recorded video -current camera zoom value i saw yes here -if the app was given permission to -access the camera -since some callbacks for video and photo -might be similar i set this to indicate -what i'm capturing -this is the actual ui element we will -see on the screen -a ui view -is the ios parallel to component -i'll discuss this soon -notice that i imported the camera kit -view class here -it's a class i added -and i'll cover it soon -this is the native capture device -representing the camera -a different device instance is used when -we flip between back and front cameras -a session encapsulates the capture -process -we need to acquire access to the camera -with a session and relinquish it and -stop -the preview layer is where the video -from the camera is drawn this is a ca -layer which is a graphics surface that -we can assign to a ui view -i'll discuss this when covering camera -kit view -this class is responsible for capturing -a movie and saving it to a file -the last two entries handle photos the -former works on ios 10 -and newer devices -and the latter -works on older devices slash os -that's a lot to do to digest but we are -just getting started -let's move right into the lazy init -method -when i started with the code i thought -the camera will initialize lazily -but that didn't fit the -the rest of the api -so i abandoned that approach and -initialized onstart -i didn't bother -changing the name -since it isn't user visible anyway -the content of the following block runs -on the native ios thread synchronously -the method won't return until the code -is finished -this is the equivalent of -new object in objective c -we allocate the object and invoke its -init method which is sort of a -constructor -we're asking the av capture device -whether we have permission to use the -media device -this can result in one of four outcomes -not determined means we need to ask for -permission -so we ask which should prompt the user -with permission dialogue that he can -accept or reject -if he accepted we moved to the second -phase of -authorization -in lazy -init post authorization -if permission was denied in some way -there isn't much we can do the user will -see -a blank view -if authorization was already granted -previously we move on notice that for -this to work we need the ios ns camera -usage description constant i discussed -before -without that build hint permission is -denied automatically -let's move on to the lazy init post -authorization method -this is a complex method so i'll divide -it into two parts for simplicity -the first part of the method deals with -detection of the device -meaning picking the right camera -this checks if a specific class exists -ios 10 deprecated an api and introduced -a new one -if the new api doesn't exist we'll fall -back to the old api -if we reach this block we are in ios 10 -or newer -you will notice that getting a device in -ios 10 is one method with the only -difference being the position argument -value -notice objective c method invocations -use argument names -as part of the invocation -notice i referred to objective c -messages as methods -there is a difference between the two -but it's not something you need to -understand as a casual objective c user -this code is running on a device with an -ios os prior to ios 10. -here we loop over all the devices within -av capture device -if a device is in the right position we -update the device value and exit the -loop -the bottom portion of the method -is common to both ios 10 plus and prior -objective c often accepts pointers to -error variables which is it assigns -in case of an error -i don't check for error here -which i should -the input object can be created from the -device -we needed to start a session and donate -it after that at this time -we allocate a new -captcha session and set the input value -preview shows the video of the session -in our view -it's a ca layer -which we can't add directly to the -screen -set layer is a method i added to camera -kit view -i'll discuss that when covering -camera kit view -this is how you show a ca layer within a -ui view -these methods allow us to keep common -code between the initialization code and -the setter methods -that means a call to set flash will -trigger -update flash internally -i'll cover all four methods -soon -the final line starts the capture -session -this seems like a lot and it is a lot -we went through the heavy lifting -portion of the code and as you can see -it might not be trivial but it isn't -hard -i didn't know half of these methods when -i started out -but that's the great thing about being a -programmer in this day and age -we can google it diff --git a/docs/website/video-transcripts/1wHGnmO-vtE.json b/docs/website/video-transcripts/1wHGnmO-vtE.json deleted file mode 100644 index ec6981c547..0000000000 --- a/docs/website/video-transcripts/1wHGnmO-vtE.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 155, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1018, - "youtube_id": "1wHGnmO-vtE" -} diff --git a/docs/website/video-transcripts/1wHGnmO-vtE.txt b/docs/website/video-transcripts/1wHGnmO-vtE.txt deleted file mode 100644 index 0a3ba58396..0000000000 --- a/docs/website/video-transcripts/1wHGnmO-vtE.txt +++ /dev/null @@ -1,155 +0,0 @@ -hello guys uh some this time I want to -show you how to track performance in -your -application so to do this I'm going to -use the kitchen sync demo and not the -uhow world because I want to show off -some of the tools that perform much -better with a more elaborate application -like the kitchen sink so here I just ran -Debugger -the debugger the standard Java debugger -it works just as it would in any -standard Java project and you should use -it here I have an actual breako on -something but -that's something else uh you can use it -just like you would any uh debugger I -can just open uh say the kitchen sink -here or I can even just pause it and -instantly see where everything is for -instance where the EDT is where -everything is standing in case something -got stuck so -here I have uh the standard kitchen sink -say I want to break on the effects and -see what's going in there I can just go -into the -effects section right here place a break -point and debug it really convenient I -Debug -can just hover over everything inspect -everything and see where everything is I -hope you're familiar with these tools -they're really really useful and I'm -showing them just because Java -developers often don't look at them but -we offer a lot -more so for instance if you want to see -uh if you have violations in the EDT so -the EDT is one of the most important -things in COD name -one when -EDT Violations -uh when every event or every paint -operation happens in code name when it -happens on the event dispatch thread the -EDT and if you if if your code is too -slow and executes on the EDT well your -application will be slow if on the other -hand your code is -um uh accessing the EDT on a separate -thread then you might get uh crash or uh -behaviors that are just unexplainable -and really really hard to -track so the EDT um debugging tool here -which I've activated will show you when -EDT Debugging Tool -you have an EDT violation but the thing -is it's not always totally accurate -because of the way things are -implemented sometimes so for instance it -will fail on something like this uh see -it says it took too long to render a -frame which is probably true and then it -detects a violation of the EDT because -there are some issues there related to -the native components so that's what -essentially you'll see when you get a -violation the problem is you also see it -every time you edit text and you'll -notice that you actually see the -violation here as a stack pointing you -at the specific method that did -something supposedly bad in this -particular case we didn't do anything -bad it was completely fine it's -just uh text input doesn't really uh -work well with this tool but it's a -really really useful tool for when -things don't act as you would expect -them to and it also gives you warnings -about things like uh slow rendering for -certain things and and all sorts so -that's that's a cool tool the second one -Network Monitor -I've showed in previous uh session so I -won't spend too much time on it is the -network monitor so if I use something -like a web service and I send the web -service request I'll actually see the -request appear here I see the details of -the request the response length -everything and the full response data -including headers including everything -some headers are are trimmed just -because of uh some limitations uh of uh -the Java environment but it's pretty -complete for the most -part uh the next thing test recorder I -won't go into there's a separate -tutorial I'll talk about testing all all -Performance Monitor -as well uh but the last thing I want to -do is talk about the performance -Monitor and the performance monitor is -very interesting tool notice I'm playing -a bit with the UI and suddenly I see the -components that are in the UI now here -because the UI was hand coded most UI -components don't have a name because I -didn't actually name them but in -agreeable UI you will actually see names -and you can arrange uh the components by -the speed they take to render which -obviously the big container takes the -longest to render uh but it will tell -you what is taking up the most time and -Memory Monitor -what is drawn the most amount of times -in a particular screen and that is -useful information now you'll notice -that here we had some additional -information uh dumped into the screen -like images being created so this is -very interesting for you guys if uh you -have a user interface where memory is -running out for some reason well you can -use this tool to -monitor uh which image are being created -and images are usually the main reason -for memory issues and it tells you -pretty much how much memory and how much -uh in terms of size every image created -takes and that's again very useful -information to keep in mind when working -with a tool like that so I hope you've -enjoyed this short tutorial and I hope -you will make use of uh the tools we -have available for uh testing -performance and debugging and and I hope -you'll be able to resolve your issues -and if not talk to us in the discussion -forum where we'll try and help you uh -with these -things uh I didn't go into things like -logging and everything because that's -not so much the the a part of it but you -should be should be using the log apis -of cod name -one which provide a very useful way to -debugging on the devices at least in -terms of -logging so that's all for now and thanks -thank you for -watching diff --git a/docs/website/video-transcripts/2nD75pODPWk.json b/docs/website/video-transcripts/2nD75pODPWk.json deleted file mode 100644 index 23a507236b..0000000000 --- a/docs/website/video-transcripts/2nD75pODPWk.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 139, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 968, - "youtube_id": "2nD75pODPWk" -} diff --git a/docs/website/video-transcripts/2nD75pODPWk.txt b/docs/website/video-transcripts/2nD75pODPWk.txt deleted file mode 100644 index f51502b530..0000000000 --- a/docs/website/video-transcripts/2nD75pODPWk.txt +++ /dev/null @@ -1,139 +0,0 @@ -in this tutorial we're going to talk -about debugging code name 1's source -code and your application based on that -code as well as forking it and making -changes and contributing them back so I -start by watching and starring the C -name one project and also forking it -obviously and we'll do the same for the -Cod name one skins project which is also -an important project we will need uh -notice that I'm copying the htps uh URL -for the repository here we'll soon see -it being used when we clone these forked -projects -locally so now we need to actually copy -the code locally through -clone now that we have this URL we can -just use gits clone and our credentials -from GitHub in order to fetch the -repository for codame 1 this will take a -while I'm skipping some of that uh in -the -video uh but eventually when it's -finished it will uh offer net beans will -offer to open the projects you don't -need to do that you just need to open -the code name one project and the ports -Javas Port project notice that uh these -projects will include errors don't worry -we'll fix all of -that now we need to fetch uh the Skins -project and that's pretty similar we're -just using the urls the URL for the -Skins uh project and uh pretty much the -same process exactly notice that here -you don't need to actually open the -directory and there's no projects within -that directory it's just used internally -as part of running the -simulator the next thing to do is to -download the cn1 binaries project you -don't need to Fork it because it doesn't -contain anything of value mostly stubs -and things like that so we just download -uh directly using the download button at -that project unzip it in the same -hierarchy as the other projects and make -sure the name is cn1 binaries and not -something else so it will be found by -the build scripts when we try to -build now comes the fun part we can -actually run the project and to do that -we need to remove the existing jars -within the project that allow us to -debug it and replace them with the -projects themselves in the library -section just remove all the jars and -instead add the actual code name one -project do the same in the Run section -make sure by the way that everything is -at the top of the class path I removed -the Javas se. jar and replaced it with -the ports Javas project and moved it to -the top as well that's it all I need to -do now is run and once I run the uh the -actual sources will be used in order to -run the simulator and it's it's mostly -seamless so here we'll talk about both -debugging code name one itself changing -it and then also contributing it back so -to debug code name one itself we can -just open this project and go to any -piece of it and when you click control -click on it you can go to the actual C -name one source uh and not to a library -source that means you can change -anything there you can debug directly -into Cod name one you can inspect -variables you can change their values -you can change anything and use apply -code changes you can literally do -anything as if this was a standard -project of your own which it is in this -case so now I'm going to actually change -a file uh the UI timer file under this -different user which isn't my standard -user in -GitHub and here I'm adding a new Factory -method for UI time which is actually -something quite useful that I wanted to -do for quite a while instead of creating -uh UI timers using a Constructor and -then invoking schedule so this is really -a simple shortcut method which is very -trivial to add and very useful so I'm -just adding this method placing -documentation and everything related to -that and once I do that I can just uh -use git to commit the change and then -use the git menu to push it -and notice that this is seamless because -it's pushing it to my own forked -repository and not directly to code name -one's repository so there's no checks -and you can do any change that you think -is right obviously keep in mind if you -want to contribute it to us in the -future that you need to abide by our -coding standards and levels and also -where the code came -from so this is all pretty trivial and -you can follow this with every get -tutorial so I'd like to talk about -contributions first you need to make -sure that you own every line of code -that you are -contributing if you don't make sure that -you didn't remove copyright headers or -anything like that and I suggest -discussing these things in the -discussion forum First Once you want to -contribute go to your fork of code name -one in that fork just press uh pull -request and submit it through the wizard -it's that -simple now once it's done we -automatically receive an email and can -process that P request or discuss it -with -you so I'd like to thank you for -watching this and we really appreciate -that and we appreciate all our users and -developers those that contribute and -those that do not uh but I do hope that -you play a bit with the source code -regardless of your attention to -contribute because I think it's very -useful and helpful and a great teaching -tool to us all thank you diff --git a/docs/website/video-transcripts/2xKvvv7XoVQ.json b/docs/website/video-transcripts/2xKvvv7XoVQ.json deleted file mode 100644 index bfe1d1ef48..0000000000 --- a/docs/website/video-transcripts/2xKvvv7XoVQ.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 31, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-access-native-device-functionality-invoke-native-interfaces.md", - "status": "transcript-fetched", - "word_count": 1210, - "youtube_id": "2xKvvv7XoVQ" -} diff --git a/docs/website/video-transcripts/2xKvvv7XoVQ.txt b/docs/website/video-transcripts/2xKvvv7XoVQ.txt deleted file mode 100644 index 9baf3a0dc8..0000000000 --- a/docs/website/video-transcripts/2xKvvv7XoVQ.txt +++ /dev/null @@ -1,55 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -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. - -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. - -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? - -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. - -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. - -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. diff --git a/docs/website/video-transcripts/3-ZH2IFIIMY.json b/docs/website/video-transcripts/3-ZH2IFIIMY.json deleted file mode 100644 index 93be41ddef..0000000000 --- a/docs/website/video-transcripts/3-ZH2IFIIMY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 187, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 980, - "youtube_id": "3-ZH2IFIIMY" -} diff --git a/docs/website/video-transcripts/3-ZH2IFIIMY.txt b/docs/website/video-transcripts/3-ZH2IFIIMY.txt deleted file mode 100644 index 735fb385d0..0000000000 --- a/docs/website/video-transcripts/3-ZH2IFIIMY.txt +++ /dev/null @@ -1,187 +0,0 @@ -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/video-transcripts/32mkZymqa6E.json b/docs/website/video-transcripts/32mkZymqa6E.json deleted file mode 100644 index da9fb60874..0000000000 --- a/docs/website/video-transcripts/32mkZymqa6E.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 29, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-localizetranslate-my-application-apply-i18nl10n-internationalizationlocalization-to-my-app.md", - "status": "transcript-fetched", - "word_count": 1216, - "youtube_id": "32mkZymqa6E" -} diff --git a/docs/website/video-transcripts/32mkZymqa6E.txt b/docs/website/video-transcripts/32mkZymqa6E.txt deleted file mode 100644 index d5ead2a989..0000000000 --- a/docs/website/video-transcripts/32mkZymqa6E.txt +++ /dev/null @@ -1,49 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -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. - -You can add additional locales using this button. In this case I added the iw locale. - -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 - -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. - -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. - -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. diff --git a/docs/website/video-transcripts/3B7C0ZbV8Bc.json b/docs/website/video-transcripts/3B7C0ZbV8Bc.json deleted file mode 100644 index 90674c0b4d..0000000000 --- a/docs/website/video-transcripts/3B7C0ZbV8Bc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 338, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 5119, - "youtube_id": "3B7C0ZbV8Bc" -} diff --git a/docs/website/video-transcripts/3B7C0ZbV8Bc.txt b/docs/website/video-transcripts/3B7C0ZbV8Bc.txt deleted file mode 100644 index 3edd7dac6e..0000000000 --- a/docs/website/video-transcripts/3B7C0ZbV8Bc.txt +++ /dev/null @@ -1,338 +0,0 @@ -in this uh lesson i'm going to talk about connecting to a web service -a simple json web service using codename one -now there's lots of ways to do web service communication including a wizard that -i'm not going to use in this particular case uh for many reasons but mostly because i -want to show the actual json connection which is useful if you're connecting -to any third-party solution so it will work outside of java even if -you're not doing full stack and it's really easy to work uh with full stack with json anyway when you're using java -so the value of the wizard isn't as huge as it was -before spring boot and things like that existed -so i'm going to use just the plain json and web connection and i'm not going to use -any special tools like xpath and which we do have in the processing -package or uh any of the third-party libraries or libraries from other -developers in codename one because i want to keep it very very simple to the -apis that you'd use normally day to day -i might go deeper into some of these later on but uh the the basic apis are actually pretty -simple so i'd like to focus on those so -the web service that we'll use in your you'd be familiar with that if you went through the spring boot uh -video it just has this sort of sort of curl syntax which means essentially we're -to add an element to the web service and this is the the web service that i'm going to attack first -we just use a put request that places that element into the item -url and it responds by actually returning the actual item that was added which -would normally be the exact same json that we submitted plus an id value which is auto incremented -from the database with everything related to that so that's a really simple web service just a put -request that includes the json within the body -so to do that i'd like to have an item class if you recall from -uh that video if you've seen it the item class is just it's a business -object and here i need to actually redo it in the client in this case it's not an entity because obviously i can't copy -the database object locally and it's also slightly different -i added a couple of methods to convert the item into a map structure and the -reason to and out of a map structure and the reason i need that is so i can -convert it to and from json using the json parser -now uh i chose to use a way that's very similar to the way i worked in the server -there's another way and i'll discuss it a bit later with the properties api -but i won't demonstrate it at this time uh you'll also noticed i trimmed quite a -lot of code here including the constructors and the getters and the setters they're all -trimmed because i'm assuming they're trivial for everyone and because i just didn't have space i didn't want the -fonts to be tiny and completely unreadable -the to actually send an item or get an eye in this -case get send an item i've added a server class and in that server class i have -variables for the url and the owner and within that server class i have -several methods to send and respond and get items -so the first method to send an item to put it is add item sync and notice the sync in -the end because it's a synchronous method that means it sends an item and -waits to return the value and that's why we have the return statement here and this is really important this is a -blocking method you'll call it and then thread that calls into that method will stop its execution until the -method completes and returns the item value that's a synchronous method -now normally synchronous calls are very bad on the ui thread you don't want to -do too much i o on the ui thread or anything like that so because the ui thread the the main thread of code in -one the one that handles the events handles the painting handles all of these things and i'll talk about it extensively in -the edt uh lesson that thread is it must be -protected from blocking normally because if you block it for too long and too long can be -milliseconds literally if you block it for too long you -you might cause a delay in the painting in the drawing and the app will feel -unresponsive and it would be a problem so i'd highly recommend that you be -careful with these things but this particular case is fine because here we use -built-in code codename one method that's uh blocking in a legal way and -there's a special loophole called invoking block and the trick for that is used by -the add to queue and wait method that time that i call here and that's legal that will not block -painting it will not block the the events from going on and the application should still work -completely fine and i'll talk about it in the next slide but that's why it's called sync -the other things you should notice here is uh the way we generate json -so the request i have i'm requesting the addition of an item so as you saw in the previous slide i -added a to map method to my item class so -i use that in order to convert the map object representation into adjacent string -and that literally generates a standard valid json string that matches -the the structure i would expect to receive in the server so now the string json literally has -that item request next i'm creating a connection request object -in this particular case i don't subclass it often we subclass it in this case when the sync calls i often -don't uh and i just build it to the url in this case the url is just the localhost -and i add the item extension so the code would be more generic -essentially the last argument it's true that means that this is a post request -but notice that i'm using put as the set http method below -the reason for um connection request has two modes post -and and get it doesn't have put delete or any of those methods you literally have to use the http method -entry below and the reason for that difference is that post and get -embed arguments in a very different way get adds them to the url to the end of the url and post places them in the body -so there's generally just two ways to add arguments to a url in http and -that's one of them is post and one of them is get there's also multiparts and other complexities but those that's a -very different subject basically those are the two important ways to add entries -and everything falls into either one or the other and normally get is obviously -a get request put is similar to post in that regard so i'm selecting that this would be a post -request and i'm setting it to to put explicitly -so essentially put as a variance of post that means it adds arguments in a -similar way although in this particular case it doesn't really matter because we're overriding the body so -it mostly matters because uh post accepts uh -an output stream to write into and get doesn't so in this particular case the -instead of using the output stream approach we just use set request body and just place the body of the request -instead of adding arguments we just have the json as the body of the request and that's it -and that works rather nicely also set obviously the content type and everything related exactly like we do in -the uh in the curl request -now after i do that uh the next line after add to key and wait actually results -with a response and at that point i just parse the response -check uh that no exception was turned when uh parsing although most of that is uh -redundant because um this is the data is already fetched so there won't be an ir exception but i -need to catch it because it's a checked exception and that's it i have a map that i can -parse out of the json and generate an item from that and -that's relatively simple not all that much to understand here -but let's dig a bit a bit deeper into the sync versus async because the next method we'll look into is an async -method so i implemented both of the methods uh in -this class the add and the put uh the add and the get sorry -with um both async and sync versions so you'll be able to see the differences in the -implementation and get a feel to both approaches to the api -now that's the main reason i did that but -usually you pick one and go with it for most cases uh -for each api that you use although you don't have to be consistent because most developers and myself included -mix and we use sync for this and we use async for that and we use we use both uh when possible and that's -one of the advantages of code name one that we have the ability to do both most apis -don't have that flexibility and need need to use -usually async exclusively [Music] now -the the sync api has mostly advantages in terms of fluidity -it's really really easy to use because when you have things like -a transactional process so for instance if you need to request something from the server and if the server answers x then -you need to proceed to stage two then you need to ask the user and then the user answers something and then you need to again request the server to do -something rel based on the answer from the user and then again the response something and then again back -and forth so you have a process that's really really easy to do stage by stage -with uh ifs dialogue prompts and things like that in that case sync works -really simplifies a lot because you can literally write code that goes top to bottom with ifs and it's -a really simple very readable code when you use async everything is a callback so your code -starts leaping between methods and every stage becomes a map to -okay what's where's the next stage the code isn't readable from top to bottom unless you -and even if you organize it it's it becomes a mess very quickly -so sync is really really intuitive for some specific use cases it's much easier -especially when we do things like dialogues or things like that they are synced exactly because of that a -dialogue by default blocks and that makes it much much simpler to -to do some basic things like are you sure is a great example -of something i'm asking the user something i'm waiting for a response and until i get the response there's no -point in the application moving forward so using sync for things like that is really -really convenient unfortunately sync does have its -drawbacks one of the drawbacks is that it's slightly slower -and in some cases it it's usually not noticeable -but in some cases it might happen especially when you have a sync api and then with a call and then -another sync api call and then again and at some point you even reach a limit because you can't nest them too deeply -and that's where things start getting kind of wonky when you use sync things sometimes behave very uh badly -when you're using a sync so things like uh if a -method call is uh blocking and you're nested uh -in uh for instance if you're in a series of an event chain and you're blocking because -the sinkhole the event chain won't proceed now that's a bit of a heady discussion -so i'll leave it to the discussion on the adt but generally um if you're having weird problems try -switching some of your sinkholes to async calls and see if that works -but you could you should usually pick what's convenient and what's right in the particular case -it's hard to give an exact guideline here -other than very specific things and i'll show you at least one point later on -where sync doesn't make sense at all and where we -have to do async to to move ahead and i'll show that later on and highlight it -so the async version of the same method is a bit more complex -first of all we have to subclass the connection request with the sync version we could have done -it but we didn't have to because we could just rely on the read response working here there is no way for us to -know when read response will actually happen so we must override it -or use an action listener which is effectively the same thing we'd be binding a listener to -uh to a callback and it would still be async but it's roughly the same thing so -might as well subclass so in this case you'll notice that uh -another interesting thing is that we don't catch i o exceptions suddenly -and the reason for that is that the callbacks of the connection request uh throw ire exception to begin with -and have a global error handling uh section within uh -within the network manager where they handle all of these sort of things -so that's really convenient because you don't need to to deal with all of the error handling nuances and things like -that you can just rely on global code sort of solving and closing we don't need to close the input even you'll -notice it isn't closed or the output because the connection request again the network manager -handles that for you it opens the streams passes them to you and then cleans up after you so it's like uh -an ideal uh uh housemate and that does all -all of the dirty work for you and in that respect it's really really convenient no -cleanup no none of that uh but it is a bit more boilerplate so -it's it's a matter of choice though you'll notice there's a couple of -other big different big things here first of all i'm placing the json within the build request body -i could have used the same code that i used before of setting the json through the method but i prefer doing it this -way for no real reason other than to show you how how it can be done -otherwise if i'm subclassing the difference isn't that big so i use this approach -uh the json is generated by the way in the exact same way so -there is no difference there and then the response from the server i read within read response -now technically i could have done you'll notice there's a separation between read response -and post response within read response i have roughly the same code as i have in the sync method -only in this particular case it doesn't have the try catch or the close of the output stream input stream or -anything like that because it's all encapsulated but you'll notice that the on success -callback is called from post response now that method is -a mistake that i made because i named it that way and i've been hitting myself on the head -over it for a while uh it's unrelated to post it's kind of confusing it's unrelated to post it in -this case um i meant post as in after the response and -i probably probably should have said response on edt or something like that which is what it actually is i just -didn't like that name so i didn't use it which was stupid -and uh in this particular case that this method is invoked after read response completes -but it's invoked on the event dispatch thread that means that's the ui thread the read response and the build request -body happen on one of the network threads and they occurred there and -you essentially get that's why this is actually -slightly more efficient than the previous code because all of this happens in a separate thread from the ui -thread so it isn't blocking the ui in any way whatsoever -the previous code didn't block the ui -when it was reading the data but then the parsing code happened on the ui thread which might have caused the -stutter if you do that too much this code happens entirely off the ui in -terms of parsing because i do i also do the parsing within the read response section so this is -noticeably faster than uh well not noticeably actually you know if -if it's a large json it would be noticeable than the previous one both because it's -async and because uh some of the functionality happens off the main thread -and the post response happens on the main thread and it sends the result item back to -whoever called this now you'll notice that the method returns void because it returns immediately you will invoke this method -because it's asynchronous it will return instantly and will not have the result at that -point so if you need to add the item and know that it was added this isn't the method for you the other -method when you add the item you will know that the item was added because it will be returned to you a new instance -of it in this method you'll add the item and you won't yet know -whether the item was added actually or if a network suddenly went out -and the way to use it is you need to use the on success callback -to do the logic of what is done when the item was added -or failed to add so -that that's hopefully more understandable the next web service that we're going to map is a -very similar one based on get where we get the data for a specific owner in this particular -case i used shy but i made it generic for any type of owner so -again this is relatively simple stuff we just make a request get back json and -it's just a simple get request and we'll again see the sync version of that and the async version of that -so the sync version of that and again we're starting with sync again very simple we use a -request this time we pass force because it's a get request and we don't need to set the request method because get is -the default when we have a get request so a connection request to the item url -and that's it now the next line and this is interesting i could have just written -item question mark uh plus owner something like that -plus owner equals owner and it would have worked actually for for this particular owner -for instance that and would have worked i don't really need the ad argument and i would have saved the line of code -so why use the add argument at all well add argument -encodes the data and encodes the data in case the data uses an illegal character or something -like that then it will implicitly work also if you suddenly decide to change -this to a post request or something like that then add argument will implicitly work -as well because you wouldn't be relying on the fact that the argument goes in the url and not in the body -so using add argument makes your code more more generic and more safe in terms of -working with the characters that aren't necessarily one of the standard -uh characters you're allowed to use in the url so that's really useful -uh the next line again uh jason request and add your key and wait as usual -and this is pretty much the same boilerplate as we had before in this case we're just parsing -an array of items and building it uh into place so there's not all that much of a -difference with get items than the room there is with the add items -except for the basic end result -the async version is uh again -similarly structured to to that version you'll notice that i need to store the -item eraser well we'll return it in the post response again it's -again not a big method and very similar and practically every way -again add argument as used instead of building the url -this is very simple code and i hope it makes sense uh to everyone -so in this uh section i'm going to integrate it to the -ui that we actually had in uh the the one of the previous lessons -where we showed a to-do list so if we think about the list of items can -actually be a list of shopping items and i totally didn't plan that or even think about that this is completely -random just thinking about it right now and that list of items we can add an item to -the shopping list and then list the items so what luck we've got the web service -working we've got the ui for that working and now let's bind it all together so -the first thing we need is to add two members to uh the -uh to the main class that we had in the ui uh -one of them is the owner variable which represents the owner -previously in the web service i used shy as the owner in this case i'm just using owner so it won't show all of the -experiments i did in the database uh obviously uh create a server instance -with the owner and and notice the server is just the class that includes the async and the sync -methods that i showed you earlier so it's just that and i'm -i'll make the calls on top of those variables and we'll see soon um -just this so if you remember though there's a for -loop that just added uh the set of shopping elements into uh the ui -literally a for loop that iterated all over the ui very similar to the one here only it didn't include the -item variables it just went over strings without too many calculations or anything -related to that so in this case i'm changing a couple of -things i'm looping over item objects -and if an item isn't deleted i'll just add it just like before -only uh one by one based on the item value -and that's relatively simple now you'll notice i'm using the async -method and you might recall that i said before that i'll highlight a point where -this is essential that i use async and not sync well this is the case -and technically it's not a hundred percent accurate i could have used the sync method and wrapped it with a core serially but that's kind of like -um not using sync at all because essentially wrapping and sync -method and call serially sort of disables it uh as a sync but in this particular case -i'm uh uh i'm before the stage where the shopping -list form has already shown so we initially need to show the form -before we actually start doing anything and this is the very first form in the application -and we also want it to appear regardless if you're before show form you don't want to use a sync call -because it will block the form from showing and that's kind of an important thing you want the ui to appear first -and then you can block the the edt so this is a bad place to to do a sinkhole -sinkhole works best and uh action listeners and things like that and in this particular case it would uh -essentially block the form from showing and because this is the first form in the application that would be really bad -so that's one place with sync where you mustn't use sync -now the the next thing is i want to add -an actual new element so i want to call the add method uh the add method -should work through the the floating action button and so far i didn't bind any action to -it so now i'm binding an action to it and actually most of this is actually ui code and not so much web services -but it's still interesting so i'm biting an action listener to the -floating action button the plus button there and the first thing is i want to scroll -to the top because i want to be at the the top of the ui uh for doing that and i want to stick a -text field right there and start editing it which is exactly what i do i i literally open the virtual keyboard and start -editing the text field and once the user completed editing if he entered text -i remove the text field and i create a new -checkbox with a separator and animate it into place -and then i call the async version of the method to add the new item -with the owner and with everything related to that then when the callback is received i -notify the user that the item was added on the server and -this works really well and pops up a text entry where you can -literally type in the new entry and it appears into place you'll notice i used the async call -i could use the sinkhole in this particular case this is one of those cases where it -almost doesn't matter i used async mostly because i felt it fit here -but honestly there's no reason to have gone one way or the other with this particular call i -wouldn't have got an advantage noticeable otherwise from one choice or -another and this is how it looks on a simulator uh i didn't run it on the device for one -simple simple reason and that's because the server is on my local -machine so to connect your server on your local machine from a phone is kind of painful you need -to discover the ip and the lan and sort of make sure that you're -connected to the lan and do it that way it's unpleasant to say the least so i chose -to uh just do this in the simulator for now and uh -it still looks nice so -i did that this was relatively simple i didn't go too deep into that because there's an infinite amount of depth we -can go into but if you want to go further -uh there's a lot that we can do to practice this and -if you want to warm yourself up and get something uh -some learning done then at least think about how you would solve these um -missing functionalities if not actually go ahead and implement them and and see how it works so -the the first one is to implement the check functionality i didn't implement that because i didn't implement an edit -uh feature so one would need to build a web service for editing on the server to -actually modify an item and that would be the approach you take -there and obviously also listen to the events and do that -communicate all of the states in the ui and update the server synchronously asynchronously you name it -uh extra credit is if you actually do a delete as well because there's a feature -for delete there that are used as a flag and not as physically deleting by the way which is -a very common practice because sometimes you know you need history and things like that -but if you can implement delete as well i would do it with a swipe where you can actually swipe the entries to the side -and have a trash can icon or something like that and delete entries -i'd be interested knowing if any of you do that actually that -level and super triple extra credit as if you port everything -to use properties now properties is the thing that i mentioned earlier as more -uh advanced uh actually it's simpler in some regards uh that are sort of skipped -in this presentation and i'll talk about it about them later probably in the boot camp itself -properties allow you to essentially avoid getters and setters and just -write more concise code and one of the nice things about them is that they automatically export an import from json -and structures like that without you having to write almost any code -and which is pretty nice i find you say so myself so uh if you have that extra -energy to go into that i'd be interested in uh if someone gets all the way to doing -doing this with properties as well that would probably be cool -so i hope this was uh educational and don't forget to ask questions let me -know how we're doing so i can tailor everything to -include information that might be missing in these presentations so -i hope you understood how to use a json web service how to build something like -that how to communicate with uh service like we created the last time around -that you understand the difference between async and sync calls in the network manager i'm sure you don't -understand completely without a background on the edt which i will cover in a future lesson -uh and i hope you understand that uh add to uh q and block -doesn't uh disturb the ui thread that means it's -it's a legal way to block the ui thread unlike normally illegal methods of doing that -and i hope you understand how to wire a remote call to the ui and essentially -make it seamless thanks for watching -again i hope you enjoyed it and let me know what you think diff --git a/docs/website/video-transcripts/3IC2qZ3wUO4.json b/docs/website/video-transcripts/3IC2qZ3wUO4.json deleted file mode 100644 index eaca2386f4..0000000000 --- a/docs/website/video-transcripts/3IC2qZ3wUO4.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 132, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 985, - "youtube_id": "3IC2qZ3wUO4" -} diff --git a/docs/website/video-transcripts/3IC2qZ3wUO4.txt b/docs/website/video-transcripts/3IC2qZ3wUO4.txt deleted file mode 100644 index 529c3ef5c9..0000000000 --- a/docs/website/video-transcripts/3IC2qZ3wUO4.txt +++ /dev/null @@ -1,132 +0,0 @@ -hello guys and welcome to a tutorial -about creating events and navigation in -the codename one GUI builder so we -created a hello world application -following the wizard and here we have -the theme the stator of the application -the state machine classes and everything -so I can just double click the resource -file which I did a moment ago and open -it up I can click the theme and see left -this is just a plain hello world -application and I want to add an -additional form to this application so -how do I do that let's start by just -pressing the add new GUI element right -here and give it a name like second and -now I can give it a title I can go here -and select this and just give it a title -like second so we all know where we are -and I'm going to go back to the first -form and create functionality to -navigate to the second form form so the -easiest thing is to just add a button -right here and I'll just drag it into -place and I'll say call it go to second -form now the easiest way to do this is -to a command now once we assign a -command to a button that's it it's got -the command it won't handle other events -won't do anything it will handle the -command so we can have lots of actions -with the command lock exit execute which -opens a URL in the browser go backwards -or just go to the second form so we do -that and this is the easiest and -probably the best way to do that because -now we can actually see the navigation -happening right here and you'll notice -you get the back command automatically -so this just works so that's cool that's -the best way because you can actually -see the navigation happening so now -we're on our second form and we say ok -we want a button -here that will actually do something -like show a dialogue or anything like -that so okay so let's select this button -which we have right here and go to the -events tab now here I can handle lots of -events and I'll just press the action -event and go into NetBeans NetBeans -automatically opens up the state machine -cross for me when my machine actually -starts moving and goes to the right -method to perform the action yeah now -you'll notice the override is giving me -an error yeah that's because I didn't -save the resource so now that I've saved -the resource file this is actually fixed -this is something very important that -you need to keep track of because when -you save the resource file the state -machine base is generated and as you can -see this method is really defined in the -state machine base you're just -overriding it so that's a very important -thing that you should notice when you're -building events so anyway this is my -event handling code and I just want to -show a dialog that just says hi world -and something like this and I'm going to -give it an OK to dismiss and nothing -else so that's basically it if I run it -in the simulator then I'll actually see -the functionality completely this -feature because it's in code you can't -preview in the designer so how I can go -to the second form and the second form -has a button that the form opens a -dialog and I can go back so that's -really cool and that handles the events -now the last portion and maybe slightly -harder to grasp is how do I populate -this so for instance if I have a label -right here or any other component and I -want the string on this label to be -something that comes out of code well -let's call this label we need to give it -a name to reference a top leaf from code -so we can give it a name my code label -and my coat label should now be edited -from code so how do I do that -well we go to the before show math -section and this is specific to the form -it doesn't matter if you press it when -this is selected or when this is -selected you just press it and it opens -the event handling code for the before -the second form cross so this method -will get executed before this form is -shown and gives you a pointer to the -actual form so now all we need to do is -open the write finder method so let's -find what's the name find my code label -I didn't save again see that's really -important and that's a big stumbling -block -here we go find my code label I can give -us the form as an argument and I can set -the text of the label from code now if I -run this then I'll see that the label -was set from code now why did we need to -do that why couldn't I just do this -manually well the explanation is really -simple -the second form only exists before you -actually show it and once we leave a -form we discard it so when we went back -from this form so this is a new form and -this is again a new form and when we go -back this is also a new form because we -always create the forms on the fly to -save memory so we discard them -immediately that is why the only place -where this label actually exists is -right before you create that's the point -where it starts living and afterwards -it's instantly discarded so that's why -you need to initialize things at that -particular point -so thanks a lot for watching and I hope -you enjoyed this video diff --git a/docs/website/video-transcripts/3rLf9A9RYPY.json b/docs/website/video-transcripts/3rLf9A9RYPY.json deleted file mode 100644 index aaa2a1f1c0..0000000000 --- a/docs/website/video-transcripts/3rLf9A9RYPY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 206, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1131, - "youtube_id": "3rLf9A9RYPY" -} diff --git a/docs/website/video-transcripts/3rLf9A9RYPY.txt b/docs/website/video-transcripts/3rLf9A9RYPY.txt deleted file mode 100644 index 99a86c0023..0000000000 --- a/docs/website/video-transcripts/3rLf9A9RYPY.txt +++ /dev/null @@ -1,206 +0,0 @@ -with the facebook clone i ended up -constantly -contrasting uber and other apps with the -appearance of the facebook app -uber chose to use material design in the -ios version and the app looks great on -ios and android the flip side is that -uber is orientation locked and doesn't -support tablets properly -in that sense i don't want to make an -accurate facebook clone i want to make -something better -so the goal isn't an exact clone -but i will draw heavily heavy -inspiration i will copy the following -principles from facebook -native ui look -i will use native os widget look where -applicable -screenflow -i will keep a similar screenflow to -facebook -tablet and orientation i will support -orientation changes in the app and -tablet -mode plain text password i use this -behavior as facebook does even though i -don't like it -with that in mind i'll focus on the -following big ticket items -i won't go over each one right now but -i'll try to implement all of those in -the clone -before we proceed let's go over the -things i won't touch -the facebook app is huge -it contains a lot of nuanced hidden -functionality -most of that is simple but the breadth -of data makes it a huge undertaking -i've tried to address the complex -features of facebook but i'll skip some -big ticket items that i won't be able to -cover -events are complex they would require a -lot of time but won't necessarily teach -a new coding skill -camera special effects -this is probably possible but will also -probably take me an entire week to -implement -this is probably outside of the scope of -what we are trying to do -additional apps -the facebook app connects to instagram -messenger etc -this shouldn't be hard but i want to -focus on the app itself -find friends and complex searches -this is mostly server-side logic i'll -implement search but i won't go too deep -into this as there's a lot of server -side code -hashtags and complex posts -i won't go into all of the nuances of -post formatting supported by facebook -i'll include some common templates and -leave the rest for you to fill slash -enhance -i won't cover a lot of things such as -albums etc -privacy and visibility -while we will implement search in some -aspects of a social network we won't do -a lot of the important things -there are a lot of nuances to private -friends -friend only public postings that i won't -cover -these aren't hard to implement but they -mostly involve server side juggling -i prefer focusing on the client side -pretty much all the details business -pages ads promotions proper ranking -complex media detection etc facebook is -huge we will implement only a small -subset -supporting orientation tablets etc -requires a flexible architecture -furthermore facebook is filled with data -to a ridiculous extent there's so much -information about every attribute -we can support -that volume of data with properties -properties allow us to store data -transmit data -and automatically create uis from data -this is tremendous for a tool such as -facebook a facebook clone we can -eliminate a lot of the grind of ui -database passing overhead by leveraging -properties -because of the flexible nature of the -application i will use an approach of a -central controller class for the user -interface to keep things simple -now that we have a sense of what we are -trying to do -we can start with mocking up the initial -ui -we first need to start by creating the -facebook clone project -i created a standard codename one -project with the following settings -project name facebook loan package name -com dot codename one dot fb clone -template bare bones and theme native -i will use css for this project -the css plugin is getting -a complete rework right now and would be -integrated into codename one soon -check out the css project on github for -installation activation instruction as -an instructions as these will probably -change by the time you see this -we will need the facebook logo for the -markup and for that we will use an icon -font -icon fonts are fonts -that map a character glyph to an icon -that means that icon will have one color -but we can color it in any way we want -and size it draw it easily in every -platform -that's very convenient and there are -several providers -of such fonts -you can also convert svgs to fonts -but -the facebook logo is specifically widely -available -we'll make use of fontello which you can -find at fontello.com to generate a font -that contains the three facebook logos -from font awesome and the gender icons -pontello is a tool that can package -specific icons from multiple fonts into -a single file for your convenience -because of that we'll also use this -opportunity to bundle into icons -icon characters for male and female -gender -we'll use these during the sign up -these are icons from the mfg labs font -we need the gender icons as the material -design for icons don't have proper -gender representation -step one -type facebook into the search select the -three font awesome entries -step two type mail into the search and -select icons to match genders -step 3. type facebook into the name -field and download -step 4 copy the ttf file into the css -res directory of the facebook clone -project -you will need to create the rest -directory under the css directory -step 5 -open demo.html -in your browser -step six check the show codes box -step seven this is the code for each -icon we'll use -now that we have all of the pieces in -place we can create the initial css file -there are a few important things here so -let's review it one line at a time -this is a special css selector for theme -constants -you should have this in any theme by -default to derive the native theme -even if you don't think you are using -native theme features you probably are -this hides the scroll bars by default -sets the default gap between a label and -its icon -it applies to buttons and all label -subclasses -these -should already be transparent -this is here just to verify -the default font for the styles might be -ignored if a specific built-in style -already defines a font of its own -we define the ttf of the facebook icon -font -we downloaded before -using this we can draw a large facebook -logo with the font -while i'm here i'll define the main form -to be white -the app is a bit inconsistent with this -but white seems to be -the main background color diff --git a/docs/website/video-transcripts/46TpE-Cgtw8.json b/docs/website/video-transcripts/46TpE-Cgtw8.json deleted file mode 100644 index 1a1c243936..0000000000 --- a/docs/website/video-transcripts/46TpE-Cgtw8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 143, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 859, - "youtube_id": "46TpE-Cgtw8" -} diff --git a/docs/website/video-transcripts/46TpE-Cgtw8.txt b/docs/website/video-transcripts/46TpE-Cgtw8.txt deleted file mode 100644 index eecc6e4e7b..0000000000 --- a/docs/website/video-transcripts/46TpE-Cgtw8.txt +++ /dev/null @@ -1,143 +0,0 @@ -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/video-transcripts/47WhIiLxv78.json b/docs/website/video-transcripts/47WhIiLxv78.json deleted file mode 100644 index c8cda10094..0000000000 --- a/docs/website/video-transcripts/47WhIiLxv78.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 189, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1110, - "youtube_id": "47WhIiLxv78" -} diff --git a/docs/website/video-transcripts/47WhIiLxv78.txt b/docs/website/video-transcripts/47WhIiLxv78.txt deleted file mode 100644 index 2d1d852cd4..0000000000 --- a/docs/website/video-transcripts/47WhIiLxv78.txt +++ /dev/null @@ -1,189 +0,0 @@ -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/video-transcripts/4D_KUa2qv2o.json b/docs/website/video-transcripts/4D_KUa2qv2o.json deleted file mode 100644 index 6018b2c066..0000000000 --- a/docs/website/video-transcripts/4D_KUa2qv2o.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 28, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-positioning-components-using-layout-managers.md", - "status": "transcript-fetched", - "word_count": 1032, - "youtube_id": "4D_KUa2qv2o" -} diff --git a/docs/website/video-transcripts/4D_KUa2qv2o.txt b/docs/website/video-transcripts/4D_KUa2qv2o.txt deleted file mode 100644 index 28b39f4ff9..0000000000 --- a/docs/website/video-transcripts/4D_KUa2qv2o.txt +++ /dev/null @@ -1,55 +0,0 @@ -_Transcript source: embedded._ - -In this short video I’ll try to address one of the most challenging basic features of Codename One: Layouts - -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 - -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 - -This begs the question: Why? Why not place the components where we want them to be on the screen? - -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. - -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 - -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 diff --git a/docs/website/video-transcripts/4jIqplr19HA.json b/docs/website/video-transcripts/4jIqplr19HA.json deleted file mode 100644 index 59a0217d81..0000000000 --- a/docs/website/video-transcripts/4jIqplr19HA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 117, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 622, - "youtube_id": "4jIqplr19HA" -} diff --git a/docs/website/video-transcripts/4jIqplr19HA.txt b/docs/website/video-transcripts/4jIqplr19HA.txt deleted file mode 100644 index 4aa2418db3..0000000000 --- a/docs/website/video-transcripts/4jIqplr19HA.txt +++ /dev/null @@ -1,117 +0,0 @@ -we continue with installations of many -packages we need to run everything -our server is headless -that means it has no monitor or video -display -so -if we try to use an api like swing or -java fx -the app will fail as it won't be able to -display anything -normally that is not a problem -but since we might want to run the css -compiler -it would need access to graphics -it's also useful for other features so -if you want to generate images on the -server the ability to use java2d would -be useful -that's where xvfb comes in handy -x11 is the windowing system for linux -and other unix flavors -xvfb -uses x on frame buffer and effectively -allows us to draw even without a display -that's useful if we need to run gui code -that doesn't actually need the -screen -Unzip -we will next install unzip -we'll need it later to install and -Install MariaDB -the next step is the installation of -mariadb -which i mentioned earlier it's a fork of -mysql that is supported by centos -this installs the server itself -but we need a few additional steps -Add MariaDB -this adds mariadb -to the startup script so it loads on -system boot -this step verifies that the previous -step succeeded and mariadb is running -i would go into more details on this but -boot process and linux is a bit -different between distributions so i'd -rather not dig in too much -Security -mysql and mariadb ship with a great -script to harden security -i followed the advice and restricted as -much as possible although you might want -to allow your ip to have remote access -to the server -this might make it easier to administer -the server remotely -this is an important step -having a server discoverable on the -internet is pretty dangerous -one thing i did which is important -was setting the database password to the -same value as the one i have -in the development server -that means the code is exactly the same -albeit slightly less secure -to be fair that's not a problem -if the database can't be accessed -remotely -Port -the next step -is exposing the right port -unix-based systems block tcp ports below -1024 -you can't listen on that port without -root privileges -this is generally a security measure so -a random user who logs into the system -won't start a server on the machine -there are many ways around it but the -one i use most often is ip tables which -you need to run as root and effectively -redirect traffic in the kernel level -so traffic on port 80 gets redirected to -8080 in this command -in case you don't know port 80 is the -default port of the http protocol -Port 80 -port 443 isn't as known as port 80. -it's the default port for https protocol -so we are doing here the exact same -thing -for that and port 8443 -Builder -finally it's time to become the builder -notice we don't need a password to do -this as we are assuming the builder user -from the root -user -Home Directory -tild is a special character in -unix -it represents the home directory -of the user -so when we are root tild is slash root -and when we are builder it's slash home -slash builder -currently we are at slash root because -that's where we logged in -so we need to go to the home directory -of -builder -and that's what this command does -notice the sign next to the command is a -dollar sign -and not a pound sign or hash sign -that's because we are now using the user -account and not the root account diff --git a/docs/website/video-transcripts/54POZ4PFFBw.json b/docs/website/video-transcripts/54POZ4PFFBw.json deleted file mode 100644 index 172c8cb5d9..0000000000 --- a/docs/website/video-transcripts/54POZ4PFFBw.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 126, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 714, - "youtube_id": "54POZ4PFFBw" -} diff --git a/docs/website/video-transcripts/54POZ4PFFBw.txt b/docs/website/video-transcripts/54POZ4PFFBw.txt deleted file mode 100644 index 20b7ef9cd2..0000000000 --- a/docs/website/video-transcripts/54POZ4PFFBw.txt +++ /dev/null @@ -1,126 +0,0 @@ -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/video-transcripts/5EyrMpQqR-k.json b/docs/website/video-transcripts/5EyrMpQqR-k.json deleted file mode 100644 index f53bd2dbd3..0000000000 --- a/docs/website/video-transcripts/5EyrMpQqR-k.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 162, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 949, - "youtube_id": "5EyrMpQqR-k" -} diff --git a/docs/website/video-transcripts/5EyrMpQqR-k.txt b/docs/website/video-transcripts/5EyrMpQqR-k.txt deleted file mode 100644 index 198965a2f2..0000000000 --- a/docs/website/video-transcripts/5EyrMpQqR-k.txt +++ /dev/null @@ -1,162 +0,0 @@ -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/video-transcripts/5WpZmIkdUfs.json b/docs/website/video-transcripts/5WpZmIkdUfs.json deleted file mode 100644 index e90aa9d0c1..0000000000 --- a/docs/website/video-transcripts/5WpZmIkdUfs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 142, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 814, - "youtube_id": "5WpZmIkdUfs" -} diff --git a/docs/website/video-transcripts/5WpZmIkdUfs.txt b/docs/website/video-transcripts/5WpZmIkdUfs.txt deleted file mode 100644 index f214ae4ede..0000000000 --- a/docs/website/video-transcripts/5WpZmIkdUfs.txt +++ /dev/null @@ -1,142 +0,0 @@ -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/video-transcripts/5eMvwRcDcug.json b/docs/website/video-transcripts/5eMvwRcDcug.json deleted file mode 100644 index f01e302f1f..0000000000 --- a/docs/website/video-transcripts/5eMvwRcDcug.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 209, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/036-building-an-android-native-version-of-the-kitchen-sink.md", - "status": "transcript-fetched", - "word_count": 1214, - "youtube_id": "5eMvwRcDcug" -} diff --git a/docs/website/video-transcripts/5eMvwRcDcug.txt b/docs/website/video-transcripts/5eMvwRcDcug.txt deleted file mode 100644 index 2f8742f429..0000000000 --- a/docs/website/video-transcripts/5eMvwRcDcug.txt +++ /dev/null @@ -1,209 +0,0 @@ -now that we have the general drift let's -move on to android -in one aspect android is easier than ios -as it's based on java -however other aspects make it a bit -painful -and in most regards harder to work with -than ios -we will first use android studio 3 to -build the project since this is pretty -much what google demands -the first step in building on android is -mostly about running through the ide -steps -the first step is the new project option -when setting the project details the -most important bit is the package name -it should match our main package name -i left the default settings for the -target platform and changed this should -work fine -i chose the blank activity i'll create -one manually later -we are now presented with a progress -indicator and then after a while android -studio finally loads -notice that you might need to resolve -issues in the id if it connects to older -versions of the android sdk installed in -your system -the first thing we need to do -once the ide has finished loading is -configure java 8 support the codename -one build servers translate the built -byte codes so they will work with java 8 -support on android -we need to do that since we have no -access to the source code in the servers -and we want to remain compatible -however java 8 is now supported in -android studio and this will allow your -code to compile unchanged notice that -the core codename one code is still java -5 compatible -to do this we need to first pick the -project structure option from the menu -we then need to set the source and -target compatibility -to java 8. -the next step is to edit the -build.gradle file and add support for -our sources notice that there are two -build.gradle files in the project you -will only need to edit the one for the -android project itself which in my case -was marked as module -colon app -after you make changes to -system files android studio offers to -sync the project again -which you should accept -this is the full code but you should -probably take the lines i highlight in -the modifications -let's scroll down and look at the -changes -i added a source set section -that points at the sources of the -kitchen sink project -that way they appear in the hierarchy -but we don't need to copy them -i added all of the dependencies to the -build -notice that we don't actually need all -of the dependencies the build servers -delete code that you don't need based on -build hints so if facebook support isn't -needed necessary it will delete facebook -import class -and won't place the facebook dependency -the same is true for the other -dependencies mentioned -we also need to make a minor change to -the gradle properties file by adding the -this line -this is required for some of the star -code later on -the next step is copying the android -implementation sources into the project -since the android implementation sources -don't include the codename one sources -we need to start with that -these commands are exclu executed from -the kitchen sink directory so they are -relative to that path -notice we copy the codename one code -first and the android code second so -files get overwritten -also notice we delete all of the files -and the root of the project source not -recursively -the root of the codename one -and android projects include resource -files that files that should be placed -in a different location -android expects all resources that -aren't in the resident file -to be within the assets directory -we need to create that directory in the -hierarchy where the res directory -resides -this copies the files from the roots of -all these projects notice the order of -commands -sets the priority since projects on the -way can override the parent project -following these instructions you might -be thinking why copy -we included the source of the kitchen -sink why not do the same for the -implementation -the main problem is override overridden -files codename one redefines the -codename one thread class -in the android implementation and relies -on that redefinition taking the priority -we might do more of that in the future -this is also important because you might -want to delete some files such as -facebook support so copy makes more -sense in this special case -the next step -is the activity class which is -android's lifecycle object -this is pretty a pretty standard class -in android so i'll add the code and step -over it like before -we have a codename one activity base -class which is important for the main -activity -as we handle some nuanced events there -most android apps use oncreate to detect -app launch we use onresume which is more -consistent for our needs in terms of -suspend resume behavior -these lines effectively initialize -codename one and start the edt if it -isn't running -we also allocate the kitchen sink main -class if it isn't allocated yet -i did the allocation on the android -thread which isn't ideal but the -callback should be invoked on the -codename one thread which is why we have -this call serially here -now that -now on the edt -this is the first time we need to invoke -the init object method of the main class -otherwise we are resuming we try to be -smart about the current form since -android has a tendency to restart -activities for everything -start is invoked with every call to -resume to match the life cycle behavior -of codename one the same holds true to -stop and destroy both of which are -pretty trivial -the rest of the code is just boilerplate -and uninteresting we invoke stop and -onstop and invoke destroy and on destroy -now that all of that is out of the way -we just need to configure the xml files -first we have the manifest file which -represents all of the activities in the -application -the manifest isn't really important here -i'll just use the default manifest -generated by the ide and updated the -activity entry -notice that most of the entries within -the activity are essential for the app -we also need a layout -for the main application so under the -res slash layout directory we need to -add the file main.xml -that's a pretty simple layout just to -give room for codename one itself -we need three style files to support all -of this the first is styles.xml which -resides in the directory src slash main -slash res values -which just includes theme and not much -else -the second bears the same name but -resides in the directory src main res -values v11 again this is pretty trivial -and hard coded for all -the apps -the final one also has the same name but -resides in the directory src slash main -slash red slash values -21 it includes some additional settings -for newer versions of android -now that all of this is done you can -just press play in the ide and run -an android on an android device -there was a bit of boilerplate but i -hope it's clear diff --git a/docs/website/video-transcripts/65LciCzyRNQ.json b/docs/website/video-transcripts/65LciCzyRNQ.json deleted file mode 100644 index 803ea9f28f..0000000000 --- a/docs/website/video-transcripts/65LciCzyRNQ.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 160, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 910, - "youtube_id": "65LciCzyRNQ" -} diff --git a/docs/website/video-transcripts/65LciCzyRNQ.txt b/docs/website/video-transcripts/65LciCzyRNQ.txt deleted file mode 100644 index a561c7948a..0000000000 --- a/docs/website/video-transcripts/65LciCzyRNQ.txt +++ /dev/null @@ -1,160 +0,0 @@ -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/video-transcripts/65jD9oGw61w.json b/docs/website/video-transcripts/65jD9oGw61w.json deleted file mode 100644 index d1f9740a28..0000000000 --- a/docs/website/video-transcripts/65jD9oGw61w.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 404, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 2398, - "youtube_id": "65jD9oGw61w" -} diff --git a/docs/website/video-transcripts/65jD9oGw61w.txt b/docs/website/video-transcripts/65jD9oGw61w.txt deleted file mode 100644 index 90573b7b09..0000000000 --- a/docs/website/video-transcripts/65jD9oGw61w.txt +++ /dev/null @@ -1,404 +0,0 @@ -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/video-transcripts/6oTy-LcTm0s.json b/docs/website/video-transcripts/6oTy-LcTm0s.json deleted file mode 100644 index 54ae18c25f..0000000000 --- a/docs/website/video-transcripts/6oTy-LcTm0s.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 31, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-use-the-include-sources-feature-to-debug-the-native-code-on-iosandroid-etc.md", - "status": "transcript-fetched", - "word_count": 1010, - "youtube_id": "6oTy-LcTm0s" -} diff --git a/docs/website/video-transcripts/6oTy-LcTm0s.txt b/docs/website/video-transcripts/6oTy-LcTm0s.txt deleted file mode 100644 index 78c9b8c025..0000000000 --- a/docs/website/video-transcripts/6oTy-LcTm0s.txt +++ /dev/null @@ -1,55 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -For iOS we have the sources tar.bz2 file which includes an xcode project. - -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 - -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. - -When we download and unzip the respective source archives we can see the Android & iOS source structures. - -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. - -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. diff --git a/docs/website/video-transcripts/73d65cvyQv4.json b/docs/website/video-transcripts/73d65cvyQv4.json deleted file mode 100644 index 435fbe5c94..0000000000 --- a/docs/website/video-transcripts/73d65cvyQv4.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 22, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-netbeans.md", - "status": "transcript-fetched", - "word_count": 599, - "youtube_id": "73d65cvyQv4" -} diff --git a/docs/website/video-transcripts/73d65cvyQv4.txt b/docs/website/video-transcripts/73d65cvyQv4.txt deleted file mode 100644 index 4cb47efc04..0000000000 --- a/docs/website/video-transcripts/73d65cvyQv4.txt +++ /dev/null @@ -1,35 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -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/). diff --git a/docs/website/video-transcripts/77N2t2n8rbQ.json b/docs/website/video-transcripts/77N2t2n8rbQ.json deleted file mode 100644 index 86946c4052..0000000000 --- a/docs/website/video-transcripts/77N2t2n8rbQ.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 15, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-use-properties-to-speed-development.md", - "status": "transcript-fetched", - "word_count": 534, - "youtube_id": "77N2t2n8rbQ" -} diff --git a/docs/website/video-transcripts/77N2t2n8rbQ.txt b/docs/website/video-transcripts/77N2t2n8rbQ.txt deleted file mode 100644 index 30489a94c9..0000000000 --- a/docs/website/video-transcripts/77N2t2n8rbQ.txt +++ /dev/null @@ -1,27 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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 - -We can also construct an object by chaining setters together. This removes the need to create many obtuse constructors for an object - -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. - -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 diff --git a/docs/website/video-transcripts/7CoD9u6KM2Q.json b/docs/website/video-transcripts/7CoD9u6KM2Q.json deleted file mode 100644 index 5ed497235a..0000000000 --- a/docs/website/video-transcripts/7CoD9u6KM2Q.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 289, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1590, - "youtube_id": "7CoD9u6KM2Q" -} diff --git a/docs/website/video-transcripts/7CoD9u6KM2Q.txt b/docs/website/video-transcripts/7CoD9u6KM2Q.txt deleted file mode 100644 index 7354663b8d..0000000000 --- a/docs/website/video-transcripts/7CoD9u6KM2Q.txt +++ /dev/null @@ -1,289 +0,0 @@ -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/video-transcripts/7G4OkjgTWIQ.json b/docs/website/video-transcripts/7G4OkjgTWIQ.json deleted file mode 100644 index 1a62210566..0000000000 --- a/docs/website/video-transcripts/7G4OkjgTWIQ.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 159, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/023-arc-and-view-on-ios.md", - "status": "transcript-fetched", - "word_count": 955, - "youtube_id": "7G4OkjgTWIQ" -} diff --git a/docs/website/video-transcripts/7G4OkjgTWIQ.txt b/docs/website/video-transcripts/7G4OkjgTWIQ.txt deleted file mode 100644 index 43f085dfbb..0000000000 --- a/docs/website/video-transcripts/7G4OkjgTWIQ.txt +++ /dev/null @@ -1,159 +0,0 @@ -in this part we continue the iOS Port -work -starting with camera kit View -before I go into the four new update -methods I'd like to detour to the camera -kit view class first -this is pretty standard we just derived -from UI view which is the standard -component type for iOS UI -this is the layer we set keeping it as a -variable is convenient -this is a method we're overriding from -UI view it's invoked when the UI view is -arranged by iOS which also has something -similar to a layout manager -this is the set layer method we -discussed before -the implementation of the class is even -simpler we store the layer into the -member field in set layer -when laying out the view I update the -frame based on the bounds both represent -the physical location of The View on the -screen -codename one positions the UI view -automatically -but the ca layer within is positioned by -this class so when coding one places the -UI view based on the layout manager the -bonds of the UI view are copied into the -layer so it shows in the same place -now that this is out of the way let's go -into the update methods before I go into -the ones we already saw there is a -hidden one which is invoked when a user -invokes set facing to change the -direction -it's not in the code from before as the -functionality was embedded into that -code -when we change the camera direction we -need to pick a new device and -effectively start over again -which is what lazy init post -authorization does -here we discard the preview layer and -stop the capture session -Objective C uses reference counting we -need to release objects we allocated -this is handled automatically by Arc -normally -but Arc collides with the GC -because this is essentially recreating -the UI we go through the second part of -initialization over again -Objective C and Swift don't have a -garbage collector like Java does -instead they use reference counting -which means every object has a number -representing the areas of the code that -need it -when I don't want an object deleted I -invoke my object retain -and it's saved -when I don't care about it anymore I do -my object release a retain operation -increments a number and a release call -decrements it -when the number reaches zero the object -is released -there is more to it but that's the basic -gist of it -a few years back Apple introduced a -compiler enhancement that automatically -injects retain release calls into the -code so developers don't see this and it -feels more like working with a garbage -collector -this is called Arc which stands for -automatic reference Counting -unfortunately we can't get armed to play -nicely with our garbage collector -I won't go too too deep into the subject -of GC versus reference counting as it's -a problematic subject but here's the -gist of it reference counting can fail -with cyclic references object a needs -object B and vice versa -GCS are immune to such cases -reference counting provides more -deterministic behavior that means it -will always perform exactly -at exactly the same speed as we can rely -on its Behavior -garbage collectors are faster but -sometimes too -for uis this can sometimes be a problem -as a GC will behave one way and one -execution and differently in another -execution -we looked at using a hybrid reference -counter GC solution When developing rvm -and eventually scrapped that as there -were no benefits in that approach -both approaches are workable and you -need to be ready to debug their pitfalls -moving on let's look at the update video -quality method while it's a bit bigger -The Core Concepts are relatively simple -this is invoked before start -it's totally fine this method will be -invoked again when start is in a vote -when we manipulate some configurations -we need to acquire a device lock to -prevent concurrent modifications -the rest is a standard switch case to -map the standard constants to iOS -constants -this was pretty simple next on the line -is update flash which is also as simple -I can go over the method but there is -really nothing here that we didn't -discuss in the previous method we return -for null device we lock for -configuration and we convert the -constant type -surprisingly update Focus does have -something new to say despite being -pretty identical to the first two -I'll skip the identical part and discuss -the final section -there is no built-in focus on tap in iOS -so we need to use some code to do this -this invokes the temp to focus method on -self which is this object in Objective C -when the user Taps container -so let's look at the tab to focus method -notice that this logic isn't something I -came up with I took it from a stack -Overflow answer which is very convenient -for implementing these sort of things -a tap returns a point on the screen -which we can convert to a point relative -to the coordinate space of the preview -layer -if we can declare a focus point of -Interest we can just set the focus to -the right point -it's not necessarily trivial but moistly -mostly boilerplate -this brings us to the last and simplest -of these methods this is mostly a rehash -of the other method so I won't discuss -it notice this at Gods against zooming -too much or too little -and that's it with these changes camera -will basically work we just need to fill -in a few more details which are mostly -boilerplate diff --git a/docs/website/video-transcripts/7XHkBMK4NY.json b/docs/website/video-transcripts/7XHkBMK4NY.json deleted file mode 100644 index dda5697f38..0000000000 --- a/docs/website/video-transcripts/7XHkBMK4NY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 120, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 652, - "youtube_id": "7XHkBMK4NY" -} diff --git a/docs/website/video-transcripts/7XHkBMK4NY.txt b/docs/website/video-transcripts/7XHkBMK4NY.txt deleted file mode 100644 index eda5b0b75c..0000000000 --- a/docs/website/video-transcripts/7XHkBMK4NY.txt +++ /dev/null @@ -1,120 +0,0 @@ -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/video-transcripts/85MyytvMS9I.json b/docs/website/video-transcripts/85MyytvMS9I.json deleted file mode 100644 index 0353748afd..0000000000 --- a/docs/website/video-transcripts/85MyytvMS9I.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 226, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1234, - "youtube_id": "85MyytvMS9I" -} diff --git a/docs/website/video-transcripts/85MyytvMS9I.txt b/docs/website/video-transcripts/85MyytvMS9I.txt deleted file mode 100644 index 3ee3baa1cd..0000000000 --- a/docs/website/video-transcripts/85MyytvMS9I.txt +++ /dev/null @@ -1,226 +0,0 @@ -we'll start with common strategies and -performance pitfalls -this list is by no means exhaustive but -it represents a lot of experience we've -had over the years -Dont block the ET -don't block the edt -this is the number one performance tip -if you have one takeaway from this it -should be this statement -the event dispatch thread is the thread -that is invoked for every listener event -or paint operation it does everything in -codename one -the edt runs a cycle that's supposed to -finish -well below the 16 millisecond mark -that's important because the edt is only -one part and after it is done the native -thread needs to perform some work as -well -if you perform lengthy operations on the -edt such as passing you might see a -performance penalty to be fair we do -these things too a lot of our demos -perform some passing and image loading -on the edt -most apis must be invoked on the edt -which makes it hard to avoid blocking it -the core workaround for this penalty is -to delegate work into another thread -we can do this with the network manager -which implicitly uses the network thread -for communication -you can also open a thread to do -intensive work and return to the edt -with a call serially invocation -a simple example of this -would be something something like this -we launch a separate thread to run slow -code and then return the response to the -edt -with qual serially -one approach to offload processing is -invoke block which spawns a thread while -blocking the edt in a non-destructive -way -this makes development very convenient -as you can avoid nested callbacks -however invoke and block is more -expensive in terms of performance than a -regular thread and callback approach -so if you are concerned about -performance invoking block might not be -the best option -these concerns should also apply to the -add to cue and weight method from the -network manager which would also incur a -similar cost -one of the tricks i did -in the uber application for features -such as -shadows -was leverage the core serially on idle -method -this method works like call serially but -it invokes the code only when the edt is -idle -idle state happens when the edt is ready -to sleep -and doesn't have too much to do -initially i rendered the component -without a shadow -and added the shadow after the fact -using a call like this -Thread safety -threads aren't magic -this is somewhat contradictory to the -recent statements but threads aren't -enough -developers often perform a slow -operation on a thread only to discover -that rendering is seriously impacted -mobile cpus aren't as fast as people -think -and -they are bad at task switching -if you spawn a thread that takes too -long and uses a lot of cpu resources it -will starve the edt -and potentially cause rendering to be -sluggish as a result -in fact we force the edt to sleep with -every cycle -so it won't starve the native rendering -thread -the solution is to explicitly yield the -thread occasionally -if you don't do i o -notice that if you are reading or -writing to a stream -the underlying kernel stream reading -code should yield the cpu for you -assuming you have a long calculation -you can just -sprinkle util dot slip sleep one or even -longer through the thread code to make -sure the cpu delegates time to other -threads -while we are on the subject i'd like to -say a couple of things about thread -safety -creating threads is important but -performance synchronization is often -challenging and error prone -it might negatively impact the -performance benefit delivered -from threads to begin with -one of the advantages of the single -thread approach in the edt is that we -can avoid synchronization almost -entirely within the codename one code -this reduces overhead noticeably as -synchronization is a very expensive -operation -that can be optimized by a compiler -and even impacts jets -if you need to use a thread i would -recommend the easy thread class which -simplifies the process of communicating -with the edt -in a way similar to call serially -caching is at the core of almost every -optimization you will make -caching can be as simple as keeping a -value in a variable or as complex as -weak references with purge strategies -a core decision about caching -is where to cache and what to cache -let's say i have this code -should we cache the image or should we -cache the label -there is no simple answer -normally i wouldn't cache the label -preferring to cache the form -but the logic of image caching is tricky -if i keep an image and then create a -scaled version of that image i'll -effectively have two instances of that -image in ram -the only tip i can give about this is -awareness -look at the things you cache and try to -avoid caching in the wrong place -notice that a two gigabyte ram device -doesn't allocate that ram to the -application -the application gets far less and this -should be further restricted as device -os's kill applications that take up too -much ram -Reuse -that brings up a common question -should we reuse form instances -in the past we used to recommend that -developers discard form instances -once they were done with them -then if they needed to go back to the -form they could create a new instance -this was the strategy and the old call -name one gui builder and it has -advantages in terms of ram usage -however -it makes some things very hard to code -going back to the exact previous state -of the form -and it is arguably slower in terms of -performance -today for the most part we -advocate the use the use of form -instances -but not dialogues which are more -transient by nature -the logic behind reuse relates to the -amount of ram available on the devices -and taking taken up by a form -it should be okay for the most part the -problems start with forms that contain -many images -in those cases we need a strategy to -purge memory when leaving the form -there are some elements that might be -expensive to cache such as images -in these cases we want to cache the data -for the performance benefit but if we -don't have enough ram -we'd rather discard it -this is in fact the strategy taken by -encoded image which i'll discuss later -javasc has built-in classes that cover a -lot of the details -such as weak reference soft reference -etc -unfortunately soft references which are -the most useful option aren't available -the solution is available in the cn -class -javasc also offers -weak hash map which we refactored into -com codename one dot ui dot util -it uses the soft reference -references built -into codename one -this class is essentially a regular map -that can lose its value objects when -memory gets low -Cache -another common class for caching is the -cache map unlike weak hashmap it caches -data and saves extra data to storage -so it's meant to cache data that you -might fetch from the network or a -similar source it makes sense for larger -elements -for small cached data -i would just use the preferences diff --git a/docs/website/video-transcripts/8WER-8R0WqA.json b/docs/website/video-transcripts/8WER-8R0WqA.json deleted file mode 100644 index dcfee82a9a..0000000000 --- a/docs/website/video-transcripts/8WER-8R0WqA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 126, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 755, - "youtube_id": "8WER-8R0WqA" -} diff --git a/docs/website/video-transcripts/8WER-8R0WqA.txt b/docs/website/video-transcripts/8WER-8R0WqA.txt deleted file mode 100644 index 6e8e295d1b..0000000000 --- a/docs/website/video-transcripts/8WER-8R0WqA.txt +++ /dev/null @@ -1,126 +0,0 @@ -the sensible place to start is with the -login and sign up processes -and the way they tie to the server -the login change is trivial -we need to replace the action listener -of the login button with this new one -we use the user object as a data -transfer object -we set the password and phone slash -email to send them to the server -the infinite progress ui is a bit of a -shortcut but for a feature like login it -makes more sense as we can't avoid a -delay -a successful login shows the main ui -whereas an error just shows the error -message -both dispose the infinite progress -dialog -that's pretty simple and should work -right away if we had a user object in -place -to add a new user we need to overall the -signup form -as you may recall the signup form -has a series of methods to create the -wizard flow ui -this series of methods should generate a -user object that we can use for sign up -the first in the series -is the create terms method since that -method doesn't include any data there is -no need for a user object there -the second method is create name -and there we already have some work -since this is the first stage in the -wizard we create the user and binding -instances -binding means changes to the ui -instantly change this field and vice -versa -that's very convenient -the create birthday method now accepts -the user and binding objects so we can -fill them up with further details -that's simple enough -the binding implicitly updates the user -object with changes from the ui -next we can proceed to the changes in -the birthday method of pre obviously the -first change in the method is the -signature which now accepts the user and -binding -other than the method signature we added -this line to the bind -to bind the date picker to the birthday -and we added the argument -arguments to the gender method two -this leads us to the changes in the -create gender method -since most of the changes are repetitive -i'll skip the method signature and the -signature of the follow-up create number -method suffice to say we pass the user -and binding onward -this is the binding code for the gender -that automatically sets male female for -the gender attribute based on the radio -button selection it's a special case -because there are buttons -these are buttons so we need to provide -the values that match each button -the create number changes are a bit -different because of the symbiotic -relationship to the create email method -i'll list more of the code but i'll skip -the create email method as the changes -there are obvious -the change itself -is in -create the create mobile or email method -which is common to create number and -create -email this is the only change in this -method -it's right under the declaration of text -entry but in effect it can be anywhere -in the method -we bind the property to the text -component both of which are passed as -arguments to this method -the final piece of the wizard is the -create password method which follows up -with a code change that's pretty similar -to everything we saw so far -at least initially -the binding call is pretty standard as -we had up until now -we launch a progress dialog that runs -while we wait for sign up to complete -if signup was successful -we go to the confirmation ui and wait -for that -that effectively creates a user on the -server and triggered an email or sms to -us we can now check the confirmation -value to see if the user is indeed -valid -we do that by adding this code to the -create confirmation method -if verification succeeded -we show the main ui -otherwise we show an error message and -remain in the current form -we still have one more change to make to -the ui controller class -up until now we always started with the -login form but we should only start -there when we are logged out we can just -change the call to show main ui to this -if we aren't logged in correct correctly -we behave as before after the splash -screen however if we are logged in we go -to the main ui -with that a new user is in place and -should allow us to log in to the app diff --git a/docs/website/video-transcripts/8fxZVc1hw6Q.json b/docs/website/video-transcripts/8fxZVc1hw6Q.json deleted file mode 100644 index f832727837..0000000000 --- a/docs/website/video-transcripts/8fxZVc1hw6Q.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 343, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 5031, - "youtube_id": "8fxZVc1hw6Q" -} diff --git a/docs/website/video-transcripts/8fxZVc1hw6Q.txt b/docs/website/video-transcripts/8fxZVc1hw6Q.txt deleted file mode 100644 index 1e9ef63511..0000000000 --- a/docs/website/video-transcripts/8fxZVc1hw6Q.txt +++ /dev/null @@ -1,343 +0,0 @@ -hey everyone today we will look at the anatomy of a code name one application -now what does that actually mean well a codename one application has -lots of files and directories and some conventions and some weird things and -some of these applications look different if you're creating an application eclipse they look a bit -different from the same application and netbeans and the same goes to idea -although idea netbeans are far more similar in terms of the way the plugin structures the -application there but either way there are some files that -aren't aren't very clear for even people who have developed and -codenamed one for quite a while don't necessarily know and understand the underlying -reason for some files that exist in the project and here i'd like to give you -both a bird's-eye view in terms of how you can use it but also how it works -underneath because i think that helps understand uh by pulling away the sort of the veil -that the magician uses in this case codename one sort of magically builds an application the -moment i expose how the cog wheels work that might help you understand better -how to use it and how to manipulate it in the best way possible -so before we begin it's crucial that you have some basic -code name one experience which means basically just creating and running a hello world application -obviously installing codename one because the hello world video and the -website is so good i'm not going to try to replace it uh -so please go there install codename one on whichever id you prefer -and then we'll proceed from that point where i assume you ran your hello world -application so this is uh an aptly named high cn1 -world application and i did the most basic bare bones -native theme application but it would work pretty much any application when you create it it looks -like this i did build the application so it will be consistent between the ids because -eclipse builds applications automatically it had the build in this directory and -neither netbeans nor idea had uh these directories and i wanted -everything to look as similar as possible in the case of netbeans the default view -hides some of the actual files that exist within the ide -so i'm shifted to the files mode if you you're just learning that ide and chose -to start using it then you're by default in the projects mode -and some of the files that you see here are hidden so if you select files from -the view option you'll see something that's more similar to to this view and actually as you can -see all of the ids are very very similar they do different some things -you'll notice eclipse has a lot of additional files and those are mostly configuration files they're not that -important well not for us at least if -if you use eclipse and you want to manipulate them be wary of that because we depend on -things like the class path to remain consistent and we depend on all sorts of configurations like that so i'd suggest -not manipulating them when possible the same is true by the way for netbeans -an idea to one degree or another you'll notice that the nb project exists -both in uh netbeans and in idea and that is because -the idea plug-in that we wrote we tried to make it as close as possible to the netbeans projects so it can literally -open a netbeans project and that's very helpful for for us at least when we generate uh -demos and things like that it really helps -it does have an iml file which is again a unique -project file for idea as well as the idea directory both of which contain project internal files -that don't really matter but you'll notice that there is far more similar between -all three ides then there is a difference and that's because it's code name one throughout -and the plugins are generally as i said in the previous presentation very a very thin layer -so two of the directories that i'll start with which are probably the easiest to just kick off the bat or the -build in this directory their temporary directories that you can literally delete you can just go to -these directories and delete them and they'll be recreated and rebuilt without a problem -uh they the build directory is a temporary directory that contains the class files -that get compiled and all sorts of temporary files that we need for the build process -so it's interesting to look in that and see what we build and how we build it -but it's not very useful for after the build because we don't use it once the build is finished -that this directory contains the final jar file and that is used in some ids for the -simulator in the case of netbeans the simulator uses it in cases of eclipse and -idea it's not used as much in the simulator the simulator behaves a bit differently -there because the build process for debug and run doesn't go through the ant -xml file and in those ides but it does for netbeans -so but building for the cloud always goes through this process so that this -directory will include the jar file that we literally send to the cloud for the build process -and a lot of times this information is really useful so one of the things that we do when -people run into problems and they say oh there is this file that's uh or the size of my application is too -big or there's there's this file that's causing problems with the build process -well one of the first steps is to look in the disk directory after you've sent the build -and unzip the this jar file and looking inside it looking -at what was actually sent to the cloud and you can see the files that literally -reach our server uh and inspect them so if there's uh the jar size is huge when you -send it the resulting application will be huge i recently got a -support query from a user who said my app is 70 megabytes when i build it for -ios well his jar file was pretty big to begin with and -arm code is much larger than bart code and by a lot -and it all adds up and that's why i reached that -and one of the basic tips is to look inside the this direction you look at the jar -and see what is causing the big size issue -so the next file that's important is the codename one build client dot jar -and that's practically the first thing that we wrote in codename one and you'll notice the wrong spelling in the -in the file name might not be initially obvious but n is capitalized which is something that we -don't do because codename is one word and not two words but when we started it wasn't clear for to us either so -it was capitalized and we sort of didn't notice and that's it it's gone because it's people -use it in a case sensitive platforms and you can't change case uh -in a compatible way so we're stuck with that forever uh this file is essentially codename one uh -in a nutshell it's the file that sends the jars to the cloud it does a lot of -other things so for instance unit testing so it generates the sort of -uh text file that we need internally to run the right unit tests and that mode and -it uh generates the agree builder code the source code for the new gui builder -it's all sort of uh jack of all trades but generally it's the thing that sends the jar to the cloud build servers and -it does that through and tasks and i'll talk about them in a later slide -javascript jar is really the the thing that you work with most -without really realizing it so this is the simulator literally it's a port of codename one two swing to -java sc and it has some java effects in it that we added to support things like a web -browser and media and things like that that just aren't available in plain java -and that is a double edged sword in some regards because javafx is so flaky -but we don't really have much of a choice if we want to simulate uh complex things like web browsers and media -so this essentially uh includes the skin functionality but not the skins -themselves only the default iphone skin and it literally allows you to -to run the simulator and all the simulator menus and all the test recorder and everything all of that is -packaged into that one single file and you can test that by -literally running this line of code -right here notice that it's important to do that from the project working directory -because it will look for the codename one settings properties and and that and that -uh the main class name from there in order -to actually launch the simulator and it's essentially -everything that you need is in there and by the way it's a fat jar so it includes everything that's necessary you can take -that jar as is and one of the things that we sometimes uh -mention to people is that you can literally take that jar and package your own application -uh without using the desktop build and things like that because technically the -desktop build is relatively simple it's just that jar with some configuration and a special main file to configure it -correctly and you can get -codename one working on the desktop with very little effort if you look at the source code a bit and -go through it so and here's one of the more interesting files that we have here -it's the kernel one settings and the codename one settings properties file -is a standard properties file now you can edit it directly -sometimes it's a bit painful so normally you should open the codename1 -settings application and the right-click menu and use that almost all of the functionality in the -codename1 settings properties file is mapped into this application sometimes not in the most intuitive way -but usually in a more correct way however even if you use that application for -everything which i would recommend because it saves you from a lot of error-prone mistakes like -escaping incorrectly and things like of that type of that nature -it's still recommended that you look within the file occasionally to verify that the -settings that you've uh applied are indeed there and it sometimes gives you -a better bird's-eye view of everything because the code m1 settings application sometimes hides -information that might be relevant for ease of use -but you might want the ability to search within the entire configuration of your -application and that's where looking at the property file itself is useful -now i've snipped it here a bit because i don't want to go into all of -the attributes that we support because there is a lot of them but you get the general drift uh that we -overused the codename one prefix for one -but another valuable thing here is that we -we can configure literally everything from here and that includes the signing details -and you'll notice the app id entry at the bottom that represents how to sign -the specific application obviously package name main class name -or display name everything is configured within here one thing that's also very important is -the codenameone.org value everything that's prefixed with codenameone.org is a build hint -now this is sometimes confusing to new codename one users because the first thing you think is isn't -everything a build hint well not exactly most of these settings are -either hard coded or not even relevant to the server but rather than locally in -the client or in one way or another so a build hint -is some something that we mostly ignore in the client and then provide -the means to send it directly all the way to the build server not just -it sort of passes through the cloud server and goes directly to the build server that can query for that build -hint so obviously you can't make up any built-in willy-nilly -but the thing is that we as current one can expose one for you -relatively easily that means that when we build a feature into codename one so for -instance we want to expose a new capability and the android port that's very specific to -android and that the specific customer asked us for and we don't want to expose it to everyone -so we can say to that customer oh you add that build hint and type it like that -and within the android build code we can query for that build hint and do -that thing only for that particular customer without saying oh this is for customer x -we literally code everything to the build hints and that's really really useful -for us and because we it allows us to provide new features in a very agar way -and change code name one itself really quickly without impacting -everyone and also offering a lot of choice so a few we can just push out a feature -and someone can use it or not use it uh accordingly and we don't need to -get into the entire process in the middle we don't need to build ui into the codename one settings we don't need -to do a lot of things to expose functionality like that so it's a very valuable feature -now the next file that's really important is the build xml which is -essentially the thing that operates everything and i wanted to start with the -the file i mentioned earlier the codename one build client jar and you'll notice it featuring prominently here i've snipped -a lot of the code there are several task defs related to that and the whole build -for ios device target is much larger i just cut out lines -completely to focus on these specific lines so you'll get a sense of it but if -you want to get to really dig into it open the build xml of any project uh -code in one project that you have and look for these lines and you'll see the rest of them because there's quite a few -so at first you can see a task dev which essentially imports the jar and says -okay i want to use this specific class to implement that tag -and later on if you look at the actual target for build for ios device which happens when you right click the project -and say build for ios device naturally -in that case we use that specific tag -and we can actually uh -it actually calls the class from within the codename one build client jar -and this is literally the code that sends the jar to the cloud to our build server -and passes in all of the various uh values there's lots of other values i'll just snip them out -for simplicity but you will notice the package name main name and the target type obviously are all passed -in and this is very valuable because you'll also notice that the target definition -has lots of dependencies like copying ios override and all sorts of other things like that that we -do as part of the build process to make it simpler now you -can hack into that by adding things to be done before or after a -build is sent and that's a very powerful thing to do you can literally automate everything -within the build xml to a very high level and uh -it's very very useful and we do that a lot for things like uh continuous -integration where we can send the synchronous build and get the result back -and that allows us to automate some tasks very beautifully -uh not sure i have time to talk about that but it's it's very helpful -so one of the things about the build xml is that -we sometimes need to update it so a good example is when we came up -with the new uwp port of codename one for the universal windows platform and we didn't want to -replace the windows build because that was still supported so we needed to create a new build target -and that meant a new entry with the target like that and the build xml -so how do we provide that well we needed to -replace the existing xml file so if you look at the top of the xml file we'll see there's a version number there -and our codename one settings application and the system preferences for from the -id check that version number and check that it's up to date and if it's not up to date they will -literally show you a dialog that says you want to update the build xml and if you say yes it will override and -replace that file including any change that you might have made to that build xml file -and that that's what might not be desired and that's why we ask but obviously one of the side effects of -that is that sometimes people aren't aware of that so for instance when we came up with the uwp -port people tried to send the build and it failed and the reason it failed was because the -build xml was out of date so it's an important thing a distinction to -understand i'll talk about uh the other uh distinctions and update -later on another thing that's important to understand is that eclipse build xml -is very different from the netbeans and intellij build xml -and the reasons for that are due to the big different conceptual differences -between eclipse and any other java eye major java ide -and there isn't all that much we can do about it uh -there's just if you look at the build xml you'll still see the things that i mentioned are in eclipse as well so -these portions are identical between all ides but if you take the build xml from -netbeans try or from netbeans project and try to stick it into eclipse uh -it won't look pretty it's it's a very different system of uh build -there so but nb project uh is common between -netbeans and idea and it is used in uh in netbeans internally because the ide -obviously uses it for uh supplementary xml files and -preferences and things like that the thing is that it's also used in intellij because we try to keep the -build as close as possible so this is sometimes an intuitive when people tried to hack the internals of -the project they found out that they needed to edit values also within the nb project file -which wasn't intuitive for people working an idea as they assumed it was something special -remnant that wasn't important but it is -the next uh thing on the agenda is the lib directory -now the lib directory serves uh two slightly different purposes -uh but they're in and still in the same category of libraries so the first purpose is that it hosts -our built-in libraries that aren't javascript jar -in retrospect javascript should have been in the libs directory we just didn't do that for some reason -but uh well we're stuck with it forever now and uh -the lib directory includes the other jars the javascript is the one that we used -we use to run things the libraries here are used only to compile so in that sense essentially -the lib is the these three jars two jars in zip -sorry uh are really uh just during the compilation time -and the javascript jar is just used during runtime and in a sense they're sort of the -opposites of one another cldc11 is -well it was a standard used during the j2m era and when we started we limited the api -to to essentially that and as we moved forward we started -supporting java 5 and then java 8 and the problem is that we named that jar in -a particular way and when i reached the update process it might be clearer why -we can't change it and we're sort of stuck with that specific name -so one thing that lesson learned here is never give -files uh names that rely rely on a specific -version of something it would have been much nicer if would have called it jpi or something like -that it would have worked nicely but we're stuck with that name -this file only contains stubs it doesn't contain any code so sometimes people use -the ide option to go to code and they open a java native code a java -api code and they see stubs nothing else and -they say wait is codename one even implemented even string doesn't have any methods in it -well yeah it's implemented this is just for compilation it doesn't include any -of the code because we don't need it for compilation the implementation is completely different for every os so -obviously we we don't need to distribute that implementation -and and obviously there's complexities involved so we prefer just having stubs without any logic -the codename one jar is just the jar from the kernel one project it again allows us to compile it does include the -code mostly because we're moving it it's more is more of a pain than just keeping it in and the last one is the codename one src -which might not seem necessary but it is there to provide your code completion -with javadocs otherwise when you press the dot key and want to get completion -on a codename one application you will see just the the name of the api without the javadoc -description and by having the source and having it in the class path the ides know that when -you click component and click and try to create an instance of -component they will show you the constructors and also show you the documentation for each constructor -and that's really important for usability and that's why we have it -here the other uh use of the lib directory is -for cn1 libs which are codename one extensions that you can either install via -by downloading them and placing them within the lib directory or you can use the extension manager to -install them now one of the things that you do is you right click the -project and say refresh refresh libs and it refreshes um -and it essentially installs the cn1 libs that you have within the lib directory -the way that this works is through the build xml the build xml has a refresh -libs target and that uses the ant task to essentially -uh again codename one uh that kind of one jar and uh -turn in one build client jar so to avoid the confusion it uses that to unzip the -the cn1 lib and places it into the impul directory that you see in front of you -right now you'll notice the ample directory is effectively empty but when you when it's uh cn1 lib is -unzipped it's unzipped into these directories which include all of the classes within the cn1 lib and the -native code and the stubs which are source code stubs -which is a bit of a complexity here that also again allows code completions uh -with javadoc for four cn1 libs which is something that jars never supported natively which kind -of sucked so so with cn1 lips that actually works -normally you don't really need to know about this import portion but it's -useful because sometimes you install a cn1 lib and it doesn't work and you want to look wait where is the -code for that where is the at least the byte code for that where where is it so it's here it's under the impul directory -you should be able to literally see it after you do the refresh libs unless the c1 lib is corrupted all cn1 -libs are just zip files so you can unzip them and see that they're not corrupted and that they contain everything that -you need so that's again another useful thing -last but not least native directory now i'm going to skip that i reach this and say i'm going to skip -that because we'll talk about native interfaces later on and there's a lot to -talk about uh it isn't interesting unless we talk about native interfaces it's just -directory you can literally ignore without talking about native interfaces -so i'm going to take a rain check on that and that brings us to the final uh point -and that's the update process this is one of the most complex -issues uh that we sometimes face and even a long -time kerning one developers are still confused by -the the process that we have in place here mostly because -we have all of these different moving parts and each one of them updates separately -now i won't go into server updates and the other complexities which i sometimes do because there's -a lot more than these three but basically i'm only talking about the current -project so the current project can be affected by three update processes there are more -so the three are the build xml which we've already talked about so we're covered there the build xml updates to -the current one settings the plugin update and that's when you update the ide plugin and that has some -implications and the libs update so let's start with the plugin update -you can update the plugin through the ide every one of the ids that we support -has its own process for updating the plugin we don't issue plugin updates very often -because it requires more work and it's not as necessary -for most things we issue a plugin update when we need to update the gui builder or the designer or other pieces like -that we don't need to update these with all with regular consistency because -they're mostly stable uh one file is impacted by a plug-in update -and that's the codename one buildclient.jar now again that's also a very stable -piece of codename one it's core to codename one and its functionality doesn't change that much -recently we added to it support for the gui builder code generation -so things like that sometimes require more frequent updates and they require -that you go into codename one settings or or do something in the project but normally it just -works this update is seamless it doesn't prompt you for anything it doesn't ask it just updates the current one build -client because it's such a low level implementation detail that asking for or notifying you of that -update is sometimes would probably be more confusing than -just doing the update so that's one update -the build xml update we've discussed it's through the kernel one settings -we're good there and the final the third type of update is the libs update -now that updates the four uh libraries that we've discussed the java -c jar the chromium one jar cld c11 jar and the kernel one src jar -and usually when we do when we announce an update every friday -that's the piece we're updating because that's the most important thing that's the api that's what you're using -that's uh the simulator that's literally all the pieces that you feel as codename -one and these are updated on a very regular basis once a week -maybe less during the boot camp but generally once a week we produce an update for that -you can trigger an update manually to the latest version by going to codename one settings -basic select update client libs the button on the bottom there and it will fetch the latest version if there's a -newer one available but it also happens implicitly when you send the build to the cloud so when you -send the bill to the cloud the cloud might say wait wait you have an older version of the libraries -better update and that also happens implicitly and fetches the latest version -there is also a special case of sending a version build a version build allows -you to build to a specific version of codename one so for instance if i want -to maintain compatibility to version 3.6 of codename one i can select that -and just say build for 3.6 and it will build for that specific version and -that's it and uh -that when i select the version 3.6 it will fetch the -jars the the versions of these libraries that was the right one during 3.6 and -that will allow me to simulate exactly as it would be after the build -and the server as it was back then essentially -so that's that brings us to the end of the session -and i hope uh i didn't lose you along the way because this is a bit -uh technical nitty-gritty and it's not as sexy as some of the other subjects that -we might be talking about but i think it's important to understand and it will -help you when you want when you start looking at the more interesting things down the road -so i hope you understand better how things work and got a bit of a glimpse behind -the curtain at how these files look and how they are -relevant and if you have any questions i'm always here as usual -and thank you so much for listening all the way through diff --git a/docs/website/video-transcripts/8wzBpEp81Kc.json b/docs/website/video-transcripts/8wzBpEp81Kc.json deleted file mode 100644 index 8a74a14538..0000000000 --- a/docs/website/video-transcripts/8wzBpEp81Kc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 45, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-use-push-notification-send-server-push-messages.md", - "status": "transcript-fetched", - "word_count": 1770, - "youtube_id": "8wzBpEp81Kc" -} diff --git a/docs/website/video-transcripts/8wzBpEp81Kc.txt b/docs/website/video-transcripts/8wzBpEp81Kc.txt deleted file mode 100644 index 733ce742e8..0000000000 --- a/docs/website/video-transcripts/8wzBpEp81Kc.txt +++ /dev/null @@ -1,81 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -Push is initiated by your server to notify a device that something happened - -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 - -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 - -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 - -As I mentioned before each OS implements push differently - -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 diff --git a/docs/website/video-transcripts/91BrBoia4nM.json b/docs/website/video-transcripts/91BrBoia4nM.json deleted file mode 100644 index 73e83cb078..0000000000 --- a/docs/website/video-transcripts/91BrBoia4nM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 161, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 885, - "youtube_id": "91BrBoia4nM" -} diff --git a/docs/website/video-transcripts/91BrBoia4nM.txt b/docs/website/video-transcripts/91BrBoia4nM.txt deleted file mode 100644 index 5f8c443c78..0000000000 --- a/docs/website/video-transcripts/91BrBoia4nM.txt +++ /dev/null @@ -1,161 +0,0 @@ -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/video-transcripts/99DAeP9LG6c.json b/docs/website/video-transcripts/99DAeP9LG6c.json deleted file mode 100644 index 59337c535e..0000000000 --- a/docs/website/video-transcripts/99DAeP9LG6c.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 25, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-create-gorgeous-sidemenu.md", - "status": "transcript-fetched", - "word_count": 748, - "youtube_id": "99DAeP9LG6c" -} diff --git a/docs/website/video-transcripts/99DAeP9LG6c.txt b/docs/website/video-transcripts/99DAeP9LG6c.txt deleted file mode 100644 index e72049d526..0000000000 --- a/docs/website/video-transcripts/99DAeP9LG6c.txt +++ /dev/null @@ -1,44 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -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. - -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. - -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. diff --git a/docs/website/video-transcripts/9T8MBBuWBDs.json b/docs/website/video-transcripts/9T8MBBuWBDs.json deleted file mode 100644 index f19baa9372..0000000000 --- a/docs/website/video-transcripts/9T8MBBuWBDs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 110, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 617, - "youtube_id": "9T8MBBuWBDs" -} diff --git a/docs/website/video-transcripts/9T8MBBuWBDs.txt b/docs/website/video-transcripts/9T8MBBuWBDs.txt deleted file mode 100644 index ecd53a7382..0000000000 --- a/docs/website/video-transcripts/9T8MBBuWBDs.txt +++ /dev/null @@ -1,110 +0,0 @@ -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/video-transcripts/A72rY4rU7E0.json b/docs/website/video-transcripts/A72rY4rU7E0.json deleted file mode 100644 index 03073a13d5..0000000000 --- a/docs/website/video-transcripts/A72rY4rU7E0.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 215, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1165, - "youtube_id": "A72rY4rU7E0" -} diff --git a/docs/website/video-transcripts/A72rY4rU7E0.txt b/docs/website/video-transcripts/A72rY4rU7E0.txt deleted file mode 100644 index 52af8437c9..0000000000 --- a/docs/website/video-transcripts/A72rY4rU7E0.txt +++ /dev/null @@ -1,215 +0,0 @@ -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/video-transcripts/ACsZ8qiwR8Q.json b/docs/website/video-transcripts/ACsZ8qiwR8Q.json deleted file mode 100644 index f9c33e0c61..0000000000 --- a/docs/website/video-transcripts/ACsZ8qiwR8Q.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 42, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-create-a-9-piece-image-border.md", - "status": "transcript-fetched", - "word_count": 1876, - "youtube_id": "ACsZ8qiwR8Q" -} diff --git a/docs/website/video-transcripts/ACsZ8qiwR8Q.txt b/docs/website/video-transcripts/ACsZ8qiwR8Q.txt deleted file mode 100644 index 88fe4c862b..0000000000 --- a/docs/website/video-transcripts/ACsZ8qiwR8Q.txt +++ /dev/null @@ -1,76 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -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. - -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. - -A round border can be either a circle like a floating action button in Android or a pill style border like an iOS hint. - -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. diff --git a/docs/website/video-transcripts/AFvuY7Ev-XA.json b/docs/website/video-transcripts/AFvuY7Ev-XA.json deleted file mode 100644 index 8046eed7a9..0000000000 --- a/docs/website/video-transcripts/AFvuY7Ev-XA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 66, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 379, - "youtube_id": "AFvuY7Ev-XA" -} diff --git a/docs/website/video-transcripts/AFvuY7Ev-XA.txt b/docs/website/video-transcripts/AFvuY7Ev-XA.txt deleted file mode 100644 index 89217f67b6..0000000000 --- a/docs/website/video-transcripts/AFvuY7Ev-XA.txt +++ /dev/null @@ -1,66 +0,0 @@ -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/video-transcripts/AIsD5MXEK-c.json b/docs/website/video-transcripts/AIsD5MXEK-c.json deleted file mode 100644 index a84de0dbb8..0000000000 --- a/docs/website/video-transcripts/AIsD5MXEK-c.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 80, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 443, - "youtube_id": "AIsD5MXEK-c" -} diff --git a/docs/website/video-transcripts/AIsD5MXEK-c.txt b/docs/website/video-transcripts/AIsD5MXEK-c.txt deleted file mode 100644 index dd86e80f1c..0000000000 --- a/docs/website/video-transcripts/AIsD5MXEK-c.txt +++ /dev/null @@ -1,80 +0,0 @@ -mapping the news feed is pretty simple -now that we have a functional server api -first we need a new utility method for -the ui utils class -this is mostly boilerplate code notice -that a lot of the java developers -would do something like component -2 array -but that's problematic as it requires -reflection to detect the generic type of -the list -and allocate an array of that specific -type codename one doesn't support that -as it would include runtime overhead -Generic Code -once this is out of the way -the generic code in the newsfeed -container class -is simpler -we no longer need the last time variable -as the code works with standard paging -now -this is a special case for -the edge of the data where the number of -entries is smaller than a full page size -we request the page data using -a synchronous api call -this specific api call uses invoke and -block internally and thus safe on the -edt -we build -components for the responses and then -convert them to an array as required by -the api that's pretty simple and will -automatically fetch additional data as -we scroll down in the ui -Post Form -so the big missing piece is that actual -content that fits into that feed -to do that we need to make some changes -to the new post form -first i extracted two of the local -fields into class level variables that i -can use later -we can then change the event handling -code to invoke the post method instead -of the blank block we had -Post Object -after that all the logic is in the post -method itself -we create a post object on the fly right -now all posts are public as i don't want -to get into friends only ui etc -if the post succeeded we can go back to -the previous form -notice that you will need to refresh -after adding a post right now this is -something we'll resolve when integrating -push later -with that we can post and see the -entries appear in the news -feed because of the way login works our -User Object -user object -stored in server api dot me will include -the friend requests and people we should -know -this should work out of the box and -suggest friend requests whenever we log -in -one missing piece is refresh -since we cache the user objects we might -have additional friends waiting -we can implement that in the add port -refresh core within the friends -container constructor -that updates the user object and after -that the ui is rebuilt with the new -friends -English (auto-genera diff --git a/docs/website/video-transcripts/AMlnslUn1bA.json b/docs/website/video-transcripts/AMlnslUn1bA.json deleted file mode 100644 index dd232c3fde..0000000000 --- a/docs/website/video-transcripts/AMlnslUn1bA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 78, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 414, - "youtube_id": "AMlnslUn1bA" -} diff --git a/docs/website/video-transcripts/AMlnslUn1bA.txt b/docs/website/video-transcripts/AMlnslUn1bA.txt deleted file mode 100644 index 3a8d5dd270..0000000000 --- a/docs/website/video-transcripts/AMlnslUn1bA.txt +++ /dev/null @@ -1,78 +0,0 @@ -in the final segment we'll review how -this was integrated into the main code -i won't review every form but i'll look -at base navigation form which hasn't -changed much and i'll review one other -form that represents the changes made -in the base navigation form not a lot -has changed besides inheriting the ui -abstraction -in the previous iterations we had this -code in the init method and now it's -moved into the init toolbar method -other than that it's pretty much the -same -the subclasses of base navigation form -are more of the same so i wanted to -focus -on one of the forms that isn't a -subclass of that -and -chose address form -it now derives from ui abstraction -directly -and as you can see -not a whole lot has changed as a result -of that -in the validation code you will notice -that instead of just -binding the submit button i invoked -the add submit button from the base -class since i don't know where the ok -button will be -instead of binding listeners i use -the on back override to implement ok -back behavior -but it works exactly the same as before -the same is true for -on ok -which also works exactly the same as -before -both are invoked because we returned -true -from has ok -back method -we also override the color theme of the -toolbar but this will only apply to the -phone ui and not for the tablet ui -i i know this seems a bit much for a -four-part module but the work we did -here was relatively simple -adapting to tablets -isn't hard work -one of the reasons it took four modules -is the fact that codename one is very -flexible -and provides mostly low a low level api -the obvious question is why not move the -abstraction layer up a notch and make -tablet support an implicit part of -codename one -that's something we're considering but -so far it was very hard to pin down the -exact api we want -in this case we were able to simplify a -lot -as we knew the application domain -however -in codename one proper -we would need a more flexible api -and an abstraction like that can lead to -replication -once we commit the ap to an api -we are stuck with it for years -or even forever -so we are rather cautious -with this api -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/B4QCJRFpG-k.json b/docs/website/video-transcripts/B4QCJRFpG-k.json deleted file mode 100644 index 2a869d5386..0000000000 --- a/docs/website/video-transcripts/B4QCJRFpG-k.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 126, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 763, - "youtube_id": "B4QCJRFpG-k" -} diff --git a/docs/website/video-transcripts/B4QCJRFpG-k.txt b/docs/website/video-transcripts/B4QCJRFpG-k.txt deleted file mode 100644 index 7143700a8d..0000000000 --- a/docs/website/video-transcripts/B4QCJRFpG-k.txt +++ /dev/null @@ -1,126 +0,0 @@ -we are now ready to implement the news -feed -the newsfeed container class is a bit -big so i'll split down the functionality -into smaller methods that build the very -various pieces of the ui -will use infinite container which lets -us scroll an infinite list of entries -and implements paul to refresh -now that we have a sense of what we are -building -let's look at the high level class -skeleton then fill in the pieces -we use the last time value -when invoking fetch timeline posts so -the latest data arrives -this method is invoked when the user -scrolls and needs more data or when pull -to refresh is used -it's hard to tell how many components -will return so we'll use a list and -convert it to an array when we are done -if this is the first entry we reset the -value of lost time before making the -request -the top is always the same component -the post bar -that's where we can add a new post -we check with the server if there are -more posts -if we reached the end null is returned -and we return that to -otherwise we loop over the responses and -create a news item for each post -we add it with a space separator -notice the usage of create news item in -the code -we update the last time value to the -last element so the next request -continues from the last place -we convert the list to an array and -return that data -we referenced create post bar and create -news item -let's look at the former first -it's a relatively simple method -there are three buttons here none of -which look like a button and each one -should take us to a different form in -the finished app -we lay out the entire title area in a -border layout this allows the right post -field to grow and occupy the available -space -next we have -the create news title method which is -very similar in some regards -we need to format the time as something -that's more human readable such as three -minutes ago -again we place the elements in a border -layout but this time we wrap them in -flow layouts within -so they vertically align properly -this is subtle and hard to notice in the -image -notice that the name jots up just a -little bit in the unaligned version but -this can be more obvious when the font -is smaller or the icon is larger -also notice the placement of the menu -button -next up is create post stats -it's again a very simple method -i broke it out just to keep the code -size smaller so we can review the blocks -more easily -the status bar appears above -the like button -it's only there if there are likes or -comments otherwise it shouldn't show -i could have made this code better so it -will write one comment and two comments -etc but i wanted to keep the code simple -with that we are ready for the final -method in this class create news item -this method assembles the post entry -from the pieces we already built -the title entry -the rich text view or -span label etc -the three like comment share buttons are -packaged in a grid so they will have the -same size and align properly -everything else is packaged together in -a -y asus container with good padding -we have quite a few uids we didn't cover -in css yet so let's go into those -the button looks like a text field when -we press it -we go directly to the post editing form -the gallery button has small font and -padding so it can fit in the same row as -the other elements -the padded container uid -is often too heavily padded for some ui -elements this uses 1.5 millimeters -instead of three millimeters in the -standard padding -we'll use this in several places -as we go on -here it doesn't matter as much as it -mostly shows an icon -the title and subtitle use regular font -which makes them stand out next to the -light font used in the rest of the app -this is literally a round blue circle -with enough padding to show the thumb -within the circle -i just need a label with a font small -enough to fit -that's all the css entries we need so we -can lean back press play in the rd and -watch the first form element in the -application come to life diff --git a/docs/website/video-transcripts/BSj3KRM5Sj0.json b/docs/website/video-transcripts/BSj3KRM5Sj0.json deleted file mode 100644 index 12f821c548..0000000000 --- a/docs/website/video-transcripts/BSj3KRM5Sj0.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 75, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 425, - "youtube_id": "BSj3KRM5Sj0" -} diff --git a/docs/website/video-transcripts/BSj3KRM5Sj0.txt b/docs/website/video-transcripts/BSj3KRM5Sj0.txt deleted file mode 100644 index f8be5a241d..0000000000 --- a/docs/website/video-transcripts/BSj3KRM5Sj0.txt +++ /dev/null @@ -1,75 +0,0 @@ -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/video-transcripts/BSrupbUahRM.json b/docs/website/video-transcripts/BSrupbUahRM.json deleted file mode 100644 index ba326f1365..0000000000 --- a/docs/website/video-transcripts/BSrupbUahRM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 43, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 240, - "youtube_id": "BSrupbUahRM" -} diff --git a/docs/website/video-transcripts/BSrupbUahRM.txt b/docs/website/video-transcripts/BSrupbUahRM.txt deleted file mode 100644 index 67bca1b285..0000000000 --- a/docs/website/video-transcripts/BSrupbUahRM.txt +++ /dev/null @@ -1,43 +0,0 @@ -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/video-transcripts/BXwh2T7wvfc.json b/docs/website/video-transcripts/BXwh2T7wvfc.json deleted file mode 100644 index 4cdabcd371..0000000000 --- a/docs/website/video-transcripts/BXwh2T7wvfc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 92, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 480, - "youtube_id": "BXwh2T7wvfc" -} diff --git a/docs/website/video-transcripts/BXwh2T7wvfc.txt b/docs/website/video-transcripts/BXwh2T7wvfc.txt deleted file mode 100644 index db129559e2..0000000000 --- a/docs/website/video-transcripts/BXwh2T7wvfc.txt +++ /dev/null @@ -1,92 +0,0 @@ -in this module we'll take a psd file and -convert it into an application mockup -we will use the css plugin to implement -most of the ui -although we will mix it with some -plain theming -our goal isn't pixel perfect design -the goal is a close enough ui experience -i went a bit overboard with some of the -design decisions i took -normal code name one applications don't -need to be this complex -to achieve this level of design -Result -this is the screenshot -of the original psd design that we will -adapt -there are many things to notice here and -i'll go over some of them when we -implement them -but let's see the final result first -the screenshot on the right is taken -from my personal android device -and shows off -the mock-up running there -some differences are instantly apparent -some are intentional -and some are not -notice i chose to use material design -icons for the shopping cart menu and -search -instead of using the icons from the -design -i think -these just look better and they adapt -better to various resolutions -this is the second ui element from the -original design -on the right you can see the end result -from my device -there are many design decisions i had to -make -in order to get this right for instance -i had to make everything a shade darker -so the title will be visible -enough on a mobile screen -here is the mock-up running on my device -you will notice that it isn't functional -but can be manipulated -and the incoming transition for the -shopping cart ui -is implemented -as one would expect -Top area -let's zoom a bit to the top area -which is one of the more challenging -parts of the application -i use a layered toolbar in this form -so the background ui can be deeply -customized -the layered toolbar places the toolbar -area -above the form itself -so the form components stretch all the -way to the top -the image of the city in the background -is literally added to the parent form -as well as all the other elements you -see here -the form itself -is a border layout -and the whole top portion -is just placed in the north section -the toolbar itself floats above -includes the section -of the menu and search -i initially looked at this ui -and thought it made sense -as tabs -after reviewing the functionality it -became apparent that this is a filter -and shouldn't replace the entire ui -so -i went with a horizontal list -the separation line on which the -shopping cart and order are placed -is really just a layered layout -that hides the bottom part of an of the -image with an opaque label -that has the same color as the form i'll -discuss this further as we move -forward diff --git a/docs/website/video-transcripts/BY1lMQz873g.json b/docs/website/video-transcripts/BY1lMQz873g.json deleted file mode 100644 index d2a9de3dbd..0000000000 --- a/docs/website/video-transcripts/BY1lMQz873g.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 193, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1083, - "youtube_id": "BY1lMQz873g" -} diff --git a/docs/website/video-transcripts/BY1lMQz873g.txt b/docs/website/video-transcripts/BY1lMQz873g.txt deleted file mode 100644 index 2734309326..0000000000 --- a/docs/website/video-transcripts/BY1lMQz873g.txt +++ /dev/null @@ -1,193 +0,0 @@ -in this section we'll talk about -profiling profiling is the process of -using specialized tools to measure the -performance bottlenecks of your -application -despite the amazing ubiquity of these -tools many developers just don't use -them -Story Time -fifteen years ago i was contracted by a -bank to help them solve performance -problem -they were having where a huge query was -taking them seven minutes to perform -they needed sub sub-second performance -for that specific query and were lost -they had a hard time getting profiling -to work so they wasted quite a bit of my -time trying to guess the reason -i wasted a bit of time in a meeting -where everyone involved raised their own -theory of why this is happening -you can probably guess the end -i spent an afternoon setting up -profiling which was hard for that huge -distributed system -once that ran i had data that showed -everyone was wrong -it pointed at a specific set of calls -that should have taken almost no time -time at all -but each was invoked thousands of times -turns out that two guys sitting at -adjoining tables had each assumed that -the other guy was caching the results of -a query -so they performed it each time for every -entry of the table -that one change brought performance from -seven minutes to seven seconds -everyone has stories like these martin -fowler mentioned how a change -in how they were getting a default -constant date in a project reduced -processing time from more than a day to -a couple of hours -profiling is crucial -Performance Monitor -the performance monitor is a simple tool -built into the simulator -that provides some insight into -performance issues you might run into -the performance monitor tool focuses on -rendering speed and won't help you with -logical performance issues -perform the performance monitor monitor -tool is launched via the simulator -performance monitor menu option -in the simulator -this launches the following ui that can -help you improve application performance -the first tab of the performance monitor -includes a table of the drawn components -each entry includes the number of times -it was drawn and the slowest fastest and -average drawing time this is useful if a -form is slow you might be able to -pinpoint it to a specific component -using this tool -the log at the bottom of the screen -includes the bug related information -for instance it warns about the usage of -mutable images which might be slow in -some platforms this also displays -warnings when an unlocked image is drawn -etc -the rendering tree view -allows us to inspect the hierarchy -painting -you can press the refresh button which -will trigger the painting of the current -form -every graphics operation is logged and -so is the stack to it you can then -inspect the hierarchy and see what was -drawn by the various components -you can click the stack button to see -the specific stack traces that led to -that specific specific drawing operation -this is a remarkably powerful debugging -tool as you can literally see overdraw -within this tool -for instance if you see full wrecked or -similar apis invoked in the parent and -then again and again in the children -this could indicate a problem -i highly recommend traversing the paint -hierarchy in this tool and trying to -explain why every graphic operation -within this tree occurred i think it -would help you understand your -application -and codename one -Performance Monitoring -performance problems are usually -portable -the problem is that because one platform -can be faster than another -we'd often perceive of them only on the -slower platform -however with profilers we can measure -performance penalties and overhead on -the desktop -we can then assume and extrapolate to -perform optimizations that make sense on -the device -there are plenty of performance -monitoring tools for java sc -and they all should work just fine with -the codename one simulator -in this case i used the netbeans -built-in performance monitoring tool on -the kitchen sink demo -the tool has two applicable modes for -this case -cpu and memory -Performance Monitoring Results -after running kitchen sync under the -netbeans performance monitoring tool i -came up with these results -this shows the threads of the -application -and as we dig into them we can see which -method takes the line share of cpu time -the hotspots mode shows the method in -which the cpu spends the most time -both are a bit misleading as they give a -lot of time to methods that use invoke -and block -another reason to be wary of this api -which is why it's always useful to -isolate the area where the performance -problem starts and try to measure only -that section with this tool -at that point you will be able to -pinpoint the suspicious methods -it's important to read the values in the -table carefully they include both cpu -time and invocations -these can expose behaviors weren't -expecting -notice that you can click any method in -the hotspots view and see the stack -traces to that method -the memory mode of the profiler is a bit -more limiting -as you can see -we can see the battery -take up most of all active memory which -typic effectively means images are -responsible for the memory of the -application -that's not exactly useful as it doesn't -disclose which image is responsible -we can still get a lot of useful -information out of this by -taking a memory snapshot and then -another one after performing an -operation -we can then check the change in memory -and determine it acted as we expected -Back of napkin math -one of the important things we need to -do when looking at a profiler is basic -back of the napkin math -the numbers we see make sense -this is true both for both memory and -cpu profiler -say your code allocates 30 images that -are 130 by 130 pixels -your memory should grow by -30 times 130 130 times 4 -which means 2 million bytes -this will mostly show up in the byte -array growth if the memory grows by a -bigger margin then you have a problem -and should try to isolate that -the same is true for method invocations -fma method takes a lot of time then look -at the elements within it -add them up if a method is invoked too -many times try to understand why and who -is invoking that method diff --git a/docs/website/video-transcripts/BbVoa3vw7OM.json b/docs/website/video-transcripts/BbVoa3vw7OM.json deleted file mode 100644 index 873f116384..0000000000 --- a/docs/website/video-transcripts/BbVoa3vw7OM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 157, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 915, - "youtube_id": "BbVoa3vw7OM" -} diff --git a/docs/website/video-transcripts/BbVoa3vw7OM.txt b/docs/website/video-transcripts/BbVoa3vw7OM.txt deleted file mode 100644 index 957886c638..0000000000 --- a/docs/website/video-transcripts/BbVoa3vw7OM.txt +++ /dev/null @@ -1,157 +0,0 @@ -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/video-transcripts/BwWhFcHc6LM.json b/docs/website/video-transcripts/BwWhFcHc6LM.json deleted file mode 100644 index 18b0dcb192..0000000000 --- a/docs/website/video-transcripts/BwWhFcHc6LM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 157, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 902, - "youtube_id": "BwWhFcHc6LM" -} diff --git a/docs/website/video-transcripts/BwWhFcHc6LM.txt b/docs/website/video-transcripts/BwWhFcHc6LM.txt deleted file mode 100644 index 3b6788873a..0000000000 --- a/docs/website/video-transcripts/BwWhFcHc6LM.txt +++ /dev/null @@ -1,157 +0,0 @@ -we'll continue from the client source -part of push notification -going back to the app we can see -the main class implements push callback -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 and start methods it must be in -that class with no exception as it -represents the object life cycle which -is the underlying os concept push binds -to -we need one call to enable push and that -is the register push method -notice that this call was changed -recently and both arguments were removed -since both are no longer needed in -recent versions of the api -register push -should be invoked every time the app -runs -let's scroll down 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 -push invokes on build result method -this is important as the result call -is also invoked from the polling -fallback -when push isn't available so having -common code is valuable here -the first core -is of interest and might require some -explaining of the various types of push -messages -so let's skip for a second into a slide -to review these types -Push Types -i'll only talk about the three most -important push types but there are quite -a few other types and i recommend -checking the developer guide -when in doubt -first and most common push type is -push type zero or one -this is the push type you see visually -when an app isn't running -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 and 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 an 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 combina 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 mostly used for -signaling -type 3 is here to work around the issues -of type 2 and type 1. -with type 1 we might not have the data -we need to understand the content of -push -so for our specific case we might say -app build completed visually but we -would expect to -tap to trigger a download -and for that we would need a url -type 2 is problematic -as we do want to show something to the -user in this case -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 -let's go back to the code again -Push Code -as i said before this if statement is -interesting -the reason we have it is simple -we sent a type 3 push that includes both -a download completed message and a url -to launch -if the push message isn't -for the download url then it's just a -visual message and i don't care about it -i relied on the fact that this is a url -but it would have worked even for a json -string for example checking that the -string starts with a curly bracket -the rest of the code isn't really -related to push -it just chose a dialog to download the -finished app if necessary but it's not -about push so i'll skip it -it's relatively simple code anyway -Push Registration -these are the two other callbacks for -push registered for push 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 we can send push messages -to this device -however -in this case -we only use push when a build is sent -and by then registration either -completed or failed so we can check the -push there -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 dot get push key -the second method is invoked when there -is a registration error -notice that this -isn't always invoked and sometimes it -isn't called for a device when -where push is unavailable -another important thing to highlight is -that this is a very bad implementation -when i did this i didn't reproduce the -error -but this could trigger an error at a -relatively early point in launch and -produce a very problematic toast message -normally this should be handled later on -or logged diff --git a/docs/website/video-transcripts/C3PLjAWQ-XA.json b/docs/website/video-transcripts/C3PLjAWQ-XA.json deleted file mode 100644 index 1e0b0a160b..0000000000 --- a/docs/website/video-transcripts/C3PLjAWQ-XA.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 14, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-use-crash-protection-get-device-logs.md", - "status": "transcript-fetched", - "word_count": 497, - "youtube_id": "C3PLjAWQ-XA" -} diff --git a/docs/website/video-transcripts/C3PLjAWQ-XA.txt b/docs/website/video-transcripts/C3PLjAWQ-XA.txt deleted file mode 100644 index 2e13cccbff..0000000000 --- a/docs/website/video-transcripts/C3PLjAWQ-XA.txt +++ /dev/null @@ -1,20 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -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 - -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. - -Thanks for watching, I hope you found this helpful diff --git a/docs/website/video-transcripts/Cc8YG-_M02M.json b/docs/website/video-transcripts/Cc8YG-_M02M.json deleted file mode 100644 index ceee499fa5..0000000000 --- a/docs/website/video-transcripts/Cc8YG-_M02M.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 172, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 988, - "youtube_id": "Cc8YG-_M02M" -} diff --git a/docs/website/video-transcripts/Cc8YG-_M02M.txt b/docs/website/video-transcripts/Cc8YG-_M02M.txt deleted file mode 100644 index e7f31b3626..0000000000 --- a/docs/website/video-transcripts/Cc8YG-_M02M.txt +++ /dev/null @@ -1,172 +0,0 @@ -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/video-transcripts/CvbcWC4hLYk.json b/docs/website/video-transcripts/CvbcWC4hLYk.json deleted file mode 100644 index 7a3127017f..0000000000 --- a/docs/website/video-transcripts/CvbcWC4hLYk.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 64, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 322, - "youtube_id": "CvbcWC4hLYk" -} diff --git a/docs/website/video-transcripts/CvbcWC4hLYk.txt b/docs/website/video-transcripts/CvbcWC4hLYk.txt deleted file mode 100644 index 484c75477a..0000000000 --- a/docs/website/video-transcripts/CvbcWC4hLYk.txt +++ /dev/null @@ -1,64 +0,0 @@ -the next step is fixing the checkout -form -which has some issues -a huge problem in checkout is the -inability to change the order -i can't remove or change quantity -currently i can't even see the quantity -of items -i'm assuming this is a design mistake -although it's possible the designer -planned another form for editing the -order -Using Swipe -we can approach this problem in several -ways -the obvious first approach can be to use -swipe -it has a huge advantage of leaving the -design exactly as is -however -it's hard to discover that a swipe -action exists if you don't know the ui -and it might be less intuitive to those -that aren't tech savvy -it is easy to implement in codename one -with swipe container -Edit Mode Button -another approach is to provide an edit -mode button -probably on the top right -this would show modification controls -for every entry which is pretty common -approach on ios -however -we'd still need designs for everything -and we might as well show the -modification controls all the time -Quantity Button -we can include quantity -in the end of the price line -it might be hard to convey visually that -this quantity element is editable -we can use a text field design -but that might not be attractive -a delete button on the right can work -too -but might disrupt the ui -Delete Button -this is what i did eventually -i used the button design for the cart so -this should be consistent -i mixed the x with the number -so people understand this is a -multiplication -the reason for that is space if i had -the x outside of the button space ran -out -clicking the button opens the native -picker -allowing us to select the quantity or a -zero to delete -this way we don't need validation -or keyboard both of which are harder to -work with on the device diff --git a/docs/website/video-transcripts/DWifLQZyPDE.json b/docs/website/video-transcripts/DWifLQZyPDE.json deleted file mode 100644 index 32c5f77865..0000000000 --- a/docs/website/video-transcripts/DWifLQZyPDE.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 362, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1983, - "youtube_id": "DWifLQZyPDE" -} diff --git a/docs/website/video-transcripts/DWifLQZyPDE.txt b/docs/website/video-transcripts/DWifLQZyPDE.txt deleted file mode 100644 index 29bae4e6e9..0000000000 --- a/docs/website/video-transcripts/DWifLQZyPDE.txt +++ /dev/null @@ -1,362 +0,0 @@ -in this section we'll discuss the -various types of images in codename one -and their impact on performance -codename one has a few image types -loaded rgb built in rgb codename one -mutable encoded image multi-image font -image and more -there are a few others but i won't go -into every nuance -all image types are mostly seamless to -use and will just work with draw image -and various image related apis for the -most part with caveats on performance -performance and memory wise i'll review -each type and try to explain the -trade-offs involved with the image type -you use -the codename one designer tries to -conserve memory and be clever by using -only encoded image -while these are great for low memory you -need to understand -the complexities of image locking and be -aware that you might pay a penalty if -you don't -here are the pros and cons -and logic behind every image type -this covers the logic of how it's -created -this is the basic image you get when -loading an image from the jar or network -using image dot create image string -or image create image input stream image -dot create image -battery intent etc -the image is stored in ram based on -device logic and should be reasonably -efficient in terms of drawing speed -however it usually takes up a lot of ram -to calculate the amount of ram taken by -a loaded image we use the following -formula -image width times image height times 4 -equals the size of ram and bytes -so a 50 by 100 image -will take up 50 times 100 times 4 -and that's 20 000 bytes of ram -the logic behind this is simple -every pixel contains three color -channels -and an alpha component -hence three bytes for color and one for -alpha -this isn't the case for all images -but it's very common and we prefer -calculate calculating for the worst case -scenario -even with jpegs that don't include an -alpha channel some os's might require -that additional byte -there are two types of rgb constructed -images that are very different from one -another -but since -they are both technically rgb images we -are bundling them under the same -subsection -internal is a close cousin of the loaded -image -this image is created using the method -image dot create image into array int -and receives the argb data -to form the image -it's more efficient than the codename -one rgb image -but can't be modified after it's created -at least not on the pixel level -the goal of this image type is to -provide an easy way to render static rgb -data efficiently at platform native -speeds -it's technically a loaded image -internally -rgb image is effectively an argb array -that can be drawn by codename one -on most platforms this is quite -inefficient but for some pixel level -manipulations there is just no other way -an rgb image is constructed with an end -array that includes width times height -elements -you can then modify the colors and alpha -channel directly within the array and -draw the image to any source using -standard image drawing apis -this api is very inefficient in terms of -rendering speed and memory overhead -only use this technique if there is -absolutely no other way -encoded image is the workhorse of -codename one -images returned from resource files are -encoded images -and many apis expect it -encoded image can be instantiated via -the create method in the encoded image -class -pretty much any image can be converted -into an encoded image via the create -from image boolean method -encoded image is effectively a loaded -image that is hidden and extracted as -needed -to remove the memory overhead associated -with loaded image -when creating an encoded image only the -png or jpeg -is loaded into an array and ram -normally such images are very small -relatively so they can be kept in memory -without much overhead -when image information is needed pixels -the image is decoded into ram and kept -in a weak slash soft reference -this allows the image to be cached for -performance and allows the garbage -collector to reclaim it when memory -becomes scarce -when drawing an encoded image it checks -the weak reference cache and if the -image is cached then it sure is shown -otherwise the image is loaded and cached -since the fully decoded image can be big -width times height times four -the ability to store just the encoded -image can save a lot of ram -so taking our example from before 50 by -100 image will take up 20 000 bytes of -ram for a loaded image but an encoded -image can reduce that to 1 or 2 -kilobytes of ram -notice that an encoded image might be -more expensive than a loaded image -as it will take up both the encoded size -and the loaded size -so the cost might be slightly bigger in -some cases -its main value is its ability to shrink -when it isn't used -encoded image is not final and can be -derived to produce complex image -fetching strategies -for instance the url image class can -dynamically download its content from -the web -naturally loading the image is more -expensive so we want the images that are -on the current form to remain in cache -otherwise the gc will thrash a lot -that's where lock kicks in -when lock is active we keep a hard -reference to the actual native image so -it won't get gc'd -this significantly improves performance -internally -this is invoked automatically for -background images icons etc which -results in a huge performance boost -this makes sense since these images are -currently showing -and they will be in ram anyway -however -if you use a complex renderer or custom -drawing ui you should lock your images -where possible to verify that locking -might be a problem you can launch the -performance monitor tool -accessible from the simulator menu if -you get log messages that indicate that -an unlocked image was drawn you might -have a problem -i will discuss the performance monitor -tool -later -multi images don't physically exist as a -concept within codename one -api so there is no way to actually -create them -and they are in no way distinguishable -from encoded image -the only built-in support for -multi-images is in the resource file -loading logic where multi-image is -decoded -and only the version that matches the -current dpi is physically loaded -from that point on user code can treat -it like any other encoded image -performance of multi images should be -identical to encoded image with one -major caveat -they increase the size of the resource -file -since most apps keep the resource file -in ram the usage -later -for usage later this might impact rams -hence performance negatively -workaround a workaround might be to -split some of the data into separate -resource files -that won't be cached -however in recent years we try to reduce -the usage of multi images in favor of -font icons and similar strategies -mutable images are created via image dot -create image and end or image dot create -image and int -they accept the width -height of the image and potentially the -argb background color -once created a developer can invoke get -graphics on a mutable image -and draw to its surface -the image can then be drawn onto the -screen like any other image -in terms of performance mutable images -are -the most interesting and most -problematic -features -historically when we started luit and -codename one mutable images were fast -in fact we used them to implement common -optimization technique of the day called -double buffering -in double buffering a developer draws -onto a background image then draws that -onto the screen resulting in fast -flicker free rendering -this is strongly discouraged for modern -devices as specifically android versions -newer than 2.3 and ios versions newer -than full -since both of these platforms are -effectively dead you should no longer -use mutable images for performance -we even have an api in display to detect -that -display dot get instance are mutable -images fast -this usually returns false on ios and -android -on ios creating a mutable image is even -slower and might cause noticeable -rendering delay if we are creating a -large image -to understand why we need to understand -how modern rendering works -modern devices use hardware acceleration -aggressively which means every rendering -operation is sent to the gpu the -graphics processing unit -for fast processing -the more you can throw on the gpu the -faster your processing will be -images like this are rendered in -software and not in the graph by the -graphics hardware -so the underlying native os will spend -expensive cpu time drawing graphics and -memory -and then more time copying that memory -from the cpu ram to the gpu ram to -actually draw it -you would also take up memory -specifically with times heights times 4 -amount and sometimes even more than that -mutable images are problematic but -sometimes they are the only way to -accomplish something for instance if you -want to create -and save a new image the only way to do -it is through mutable image -however there is also a performance case -to be made for mutable images -if you have code that includes a lot of -rendering operations that repeat -themselves it might make sense to pay -the penalty of a mutable image once and -then draw that -it's something we do with some special -effects which would be expensive to -render repeatedly -we can just generate a rubber stamp -once the mutable image is rendered its -strong performance is as fast as a -loaded image -even though it has noticeable ram -overhead it might still be worthwhile -for a lot of use cases if you are -willing to make the trade-off -one important -bit of information is that mutable -images aren't thread safe -this is a bit problematic but there is -no way around it -mutable image access -must be done on the edt -some optimizers have tried to use a -separate thread to render mutable images -this might work for most platforms but -it doesn't work for ios which relies on -a global drawing state -so this is one performance optimization -tip you shouldn't -use -font image allows using an icon font as -if it was an image -you can specify the character color and -size then treat the font image as if -it's a regular image -the huge benefits are that -the font image can adapt to platform -conventions in terms of color -and easily scale to adapt to the dpi -it also comes bundled with material -design icons representing common -functionality -in terms of performance fonts are -smaller than pngs and jpegs -they are much smaller than multi-image -both in ram and distribution size -rendering performance is hard to measure -and depends on a lot on the os -performance difference shouldn't be -noticeable in the rendering layer -as a result if you can use font image -then you should use font image the image -type has almost no overhead in terms of -ram which makes it ideal -some optimizations in the label -rendering pipeline might be lost when -using font image however that is an edge -case behavior and you should only deal -with that if profiling highlights this -as a performance bottleneck -it's possible to convert a font image to -image which -internally uses mutable image -or encoded image -there is -no measurable performance advantage for -doing that -the main motivation for doing this is -apis that might rely on image data -behaving in a specific way -font image doesn't implement some common -apis such as get rgb -and some low-level apis might rely on -native image drawing which won't work -with font image diff --git a/docs/website/video-transcripts/Dh1bYqiKBQo.json b/docs/website/video-transcripts/Dh1bYqiKBQo.json deleted file mode 100644 index b8522bca1c..0000000000 --- a/docs/website/video-transcripts/Dh1bYqiKBQo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 116, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 607, - "youtube_id": "Dh1bYqiKBQo" -} diff --git a/docs/website/video-transcripts/Dh1bYqiKBQo.txt b/docs/website/video-transcripts/Dh1bYqiKBQo.txt deleted file mode 100644 index 5fd850a089..0000000000 --- a/docs/website/video-transcripts/Dh1bYqiKBQo.txt +++ /dev/null @@ -1,116 +0,0 @@ -in this module we'll talk about the -process of adapting the application to -look good -on tablets and desktops -by default apps work as is in those form -factors but this can produce a situation -when app feels like a scaled up phone -app -The Problem -case in point if we run the current app -on a tablet it looks horrible -it just doesn't fit -the images look bad and overscaled -the side menu and elements -positioning doesn't make use of the -available space -this is how the app looks after the -changes we will review today -the icons and the layout look great -the side menu is permanent and doesn't -change as we move through the elements -the app feels more like an app that was -designed for tablets -and can work reasonably well on a -desktop -Desktop -when we switch to other entries such as -the details entry -the form doesn't change only the content -in the center -you will notice that instead of using -commands in the title area -we use large and convenient buttons at -the bottom -which is possible now since we have more -screen real estate -Form -this is the key -with a regular app every entry is a form -and with a tablet app that doesn't work -as nicely -although it is possible to do so it's -often not the best approach -usually for tablet apps -we like having one form and replacing -the content within -to achieve this we derive a common base -class instead of form -to implement that i just looked at -everything that failed when i switched -the base class -and we implemented this in an -abstraction class -the implementation for form is pretty -straightforward -as a result -UI abstraction -going to the base navigation form -the changes here are instantly familiar -we now derive from ui abstraction -instead of four -let's open up ui abstraction and see -what we have there -ui abstraction opens with an inner class -representing the form delegate -this is effectively the class that -implements the standard form-based -navigation -naturally it does derive from form -as -all the calls apply to it -so instead of inheritance we now have a -form of composition -the constructor for ui abstraction -accepts the title and layout which are -both passed onwards to the form and the -default behavior -we don't need the fourth set of form -constructors in this case -this is a special callback -that subclasses can override -since many forms have an ok cancel pair -of buttons i generally generalize that -code so if a ui abstraction subclass -overrides this method to return true -we add an ok -or cancel -this allows us to have toolbar commands -in regular forms and ok cancel buttons -in tablet mode -we implement this in the form in a very -generic way -we just add a command and implicitly -show the previous form -we have an on back callback that allows -the subclass of the ui abstraction to -intercept this behavior and bind logic -to the cancel operation -ok is identical with two small -differences -the first is the fact that on ok is -invoked instead of on back -the second is that we keep a reference -to the ok button -we need that for the validation code so -validators can still -disable the ok button -if the input is valid -scrolling down a bit -we can see several methods of form that -we overrode to provide sensible -toolbar behavior in all cases -if you look at the existing forms before -this change you will see most of the -forms already had similar code within -them diff --git a/docs/website/video-transcripts/DyzEgAGyRcA.json b/docs/website/video-transcripts/DyzEgAGyRcA.json deleted file mode 100644 index 133c0a09e5..0000000000 --- a/docs/website/video-transcripts/DyzEgAGyRcA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 84, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 424, - "youtube_id": "DyzEgAGyRcA" -} diff --git a/docs/website/video-transcripts/DyzEgAGyRcA.txt b/docs/website/video-transcripts/DyzEgAGyRcA.txt deleted file mode 100644 index eb776b2e02..0000000000 --- a/docs/website/video-transcripts/DyzEgAGyRcA.txt +++ /dev/null @@ -1,84 +0,0 @@ -in this module we dig into the tablet ui -class -the tablet ui is created by the main -class of the application we initialize -it here -so when base navigation form uses -generic code -it would already be initialized we also -initialize the permanent side menu which -makes the side menu stay open all the -time -without the button -that's a ui paradigm that makes sense on -tablets and desktops -Tablet UI Class -the tablet ui class is a form that -encapsulates the full layout on a tablet -it's a singleton because we have just -one form in the application -and one ui -we use a border layout and place the -content of the ui -in the center -so we can replace it when navigating -between ui abstraction instances -Border Layout -the title and subtitle are editable here -too -this is effectively a copy of the code -from base navigation form -adapted for the tablet form factor -Navigation Form -this is also a copy from base navigation -form but thanks to the available space -we don't need to jump through hoops to -make this look good or fit -so the code here is much simpler -the logo image is now just an icon on -the side menu which makes it look far -better overall -Side Menu -you'll notice that all the side menu -entries are marked here -but i only highlight one as they are all -mostly the same since we don't derive -from the base navigation form -this code is a bit duplicate but it's -much simpler as we don't need the whole -hack with marking the selection -notice we aren't adding commands but -rather adding components using the -generic -create site navigation entry method -we'll discuss that method soon but -notice the bg argument we pass to each -one of them -that's a button group -and these methods return a radio button -this allows us to have the selection -effect -Background Image -we also add the edit background image -button -to the side -it's relatively trivial as there isn't -much we need to do here -Navigation -creates our navigation entry just -generalizes the code that creates a -radio but toggle button -it mostly saves some boilerplate and -doesn't feature much logic -Show Container -however show container is pretty -interesting -it's the method that's effectively -invoked when show or show back are -invoked -there are two modes here -for the very first screen we need to use -add -from that point on we'll always use -replace to show the next ui abstraction -instance diff --git a/docs/website/video-transcripts/EkiTDQn9Cpg.json b/docs/website/video-transcripts/EkiTDQn9Cpg.json deleted file mode 100644 index 4e12b90ac4..0000000000 --- a/docs/website/video-transcripts/EkiTDQn9Cpg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 388, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 2157, - "youtube_id": "EkiTDQn9Cpg" -} diff --git a/docs/website/video-transcripts/EkiTDQn9Cpg.txt b/docs/website/video-transcripts/EkiTDQn9Cpg.txt deleted file mode 100644 index ea20ea12ec..0000000000 --- a/docs/website/video-transcripts/EkiTDQn9Cpg.txt +++ /dev/null @@ -1,388 +0,0 @@ -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/video-transcripts/FKTM8jAepJs.json b/docs/website/video-transcripts/FKTM8jAepJs.json deleted file mode 100644 index e3dee882a8..0000000000 --- a/docs/website/video-transcripts/FKTM8jAepJs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 98, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 524, - "youtube_id": "FKTM8jAepJs" -} diff --git a/docs/website/video-transcripts/FKTM8jAepJs.txt b/docs/website/video-transcripts/FKTM8jAepJs.txt deleted file mode 100644 index ddf7800ecd..0000000000 --- a/docs/website/video-transcripts/FKTM8jAepJs.txt +++ /dev/null @@ -1,98 +0,0 @@ -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/video-transcripts/FSek5IOQ0IE.json b/docs/website/video-transcripts/FSek5IOQ0IE.json deleted file mode 100644 index 43e93d8d27..0000000000 --- a/docs/website/video-transcripts/FSek5IOQ0IE.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 245, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1239, - "youtube_id": "FSek5IOQ0IE" -} diff --git a/docs/website/video-transcripts/FSek5IOQ0IE.txt b/docs/website/video-transcripts/FSek5IOQ0IE.txt deleted file mode 100644 index 0cb16601e7..0000000000 --- a/docs/website/video-transcripts/FSek5IOQ0IE.txt +++ /dev/null @@ -1,245 +0,0 @@ -before i start this section -i'll start with a small disclaimer -this section was developed after the -application was almost finished -and so -the code you see attached to it -includes many additional features -that if you will look through -aren't covered in previous modules -however -it does fit in this location so i took -the liberty of sticking it in -i mentioned before push is fragile -and can be blocked by the user -in order for the app to work properly we -need to properly handle the situation -where push isn't available -and we can do that with a fallback -implementation -i already demonstrated the http polling -implementation -now i'd like to move to the better -solution which is websockets -but first it's bet if it's a better -solution -why didn't i start with that -websockets are newer -and some server developers -might -not be as comfortable with them -they are worth your time and effort -though -as they make event-based communication -trivial and efficient -http is a relatively expensive protocol -besides the cost of opening a socket we -also have the cost of the headers -this gets really bad when you go back -and forth from client to server -for many small requests -http works great when you return large -files which effectively mask the -overhead of the protocol -sockets are too low level -for the web environment -they don't work nicely with most -application servers -and it's really difficult to punch -through corporate firewalls -sockets are also very fragile -and it's really hard to deal effectively -with failure when working with them -websockets were devised as a middle of -the road solution -they start as an http or https -connection -but instead of closing the connection -the server keeps the socket open -and now the client and server can -communicate freely back and forth -this has several advantages -you can go through corporate firewalls -who are mostly oblivious to websockets -as they seem like a regular web traffic -which they are -a lot of application servers have -standardized support for websockets -since they are a w3c standard -you can easily work with your favorite -server -coding is as efficient -as regular websockets -as regular sockets once you paid -the connection overhead cost -and finally you can use web sockets from -codename one as well as javascript and -pretty much everywhere else on the -client -websockets aren't as flexible as regular -sockets since the api is a bit -simplified -you can send and receive messages either -as bytes or as strings -that's very convenient -one of the harder problems with -websockets -is -actually in the server -a lot of server apis are -very synchronous by nature -and it's sometimes hard to bind an event -from the server to a socket event -in fact that's the biggest difficulty i -had -when working with websockets -doing the right thing -in spring boot -spring boot has an optional standardized -protocol -on top of websockets -which i didn't use as i wanted something -very low level -and that seemed redundant -this -is the one problem with websockets -they are relatively new and a lot of the -best practices -haven't caught up yet -websockets support in codename one -is implemented as a cn1 lib -which you can install by launching the -codename one settings app and going to -the extensions -section when you press the download -button the library will be installed -just use refresh libs in the right click -menu -and you can proceed to use the websocket -library -the code in the code you might recall -the build app method -that builds the actual app -we now open a connection to the server -with the websocket class -the websocket class includes callbacks -representing the various states of the -web socket -the first callback is an -on open -you will notice i'm sending the secret -to the server -so we can bind to the right restaurant -initially i didn't pay attention to it -and just called the send method directly -after creating the socket object -this didn't work -but i didn't get any error either -since the socket worked works -asynchronously -it wasn't open and my call was failing -this method is crucial for the initial -handshake code -you'll notice we have two on message -methods -as the websocket api -one accepts string -and the other a byte array -this maps to the way websocket api works -where a special case is made for both of -these types -we only pass strings here -which maps -map directly to the messages we would -have gotten from the push notification -api -scrolling a bit downwards we can now see -the on error callback -it's important to override that to -handle errors -we can then see the sock.connect method -which initializes the connection -for this specific trivial usage of -websockets -this is -literally the entire client side but -even more elaborate usage -isn't much more complicated than that -websockets are just messages -we send and receive and the client side -is remarkably -simple -on the server side we need to first -create a configuration class to handle -web sockets -i defined the standard 8 kilobyte buffer -size -and add a handler for the specific -websocket url -scrolling further down -we can see the handler instance -that's created when a handler is needed -the handler class will handle a -websocket request -the hander class implements a text based -websocket which means it works with text -objects for the web socket -as you recall websockets work with -either binary data or text and in this -case we only work with strings -this is a bit of a hack -i probably could have found a better way -to implement it if i had more time -but as i mentioned before websockets are -new and some of the older apis just -don't fit very well -into the paradigm -i want -asynchronous built the asynchronous -build process to send a websocket -message -but there is no way to do that without -somehow passing -the websocket reference to the build -unfortunately the builders started via a -web service -a long term solution might be to use -only websockets for everything -but for now i kept -a static instance -this is bad practice as it won't allow -the app to scale -but i can live with this at this point -i remove the session entries as a -connection is closed -so we don't have a memory leak -the rest of the code in this class is -pretty simple but let's go over it block -by block -since websockets are very low level we -can potentially stream data as we work -this isn't as easy to handle but might -be more efficient in some cases -we don't need that -the push method allows us to send a push -message to the restaurant with the given -secret -this is just a look -look up within the session static object -and -sending the message -if we have a session this effectively -works exactly like push -we only send one message to the server -and that's the client secret -we rely on that here as we cache the -session -on the server -on the secret value -this effectively is the server -the build process calls push in all of -the places it sends a push notification -this is pretty trivial so i won't go -into that -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/FcHVK9ObONg.json b/docs/website/video-transcripts/FcHVK9ObONg.json deleted file mode 100644 index 3cef79656c..0000000000 --- a/docs/website/video-transcripts/FcHVK9ObONg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 105, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 543, - "youtube_id": "FcHVK9ObONg" -} diff --git a/docs/website/video-transcripts/FcHVK9ObONg.txt b/docs/website/video-transcripts/FcHVK9ObONg.txt deleted file mode 100644 index 9f8d29a031..0000000000 --- a/docs/website/video-transcripts/FcHVK9ObONg.txt +++ /dev/null @@ -1,105 +0,0 @@ -up until now we dealt mostly with client -code -but it's time to look at the full stack -and leverage the work we did in spring -boot -for this application as well -Server Code -we'll use the same server code as the -restaurant app we built earlier this -simplifies a lot of details although -in long term scaling it might be a -hindrance -i don't think this is the time to think -of that -premature optimization might not be the -root of all evil but it sure slows down -your project -one huge benefit is that we can just set -up the restaurant code directly -into the database and reuse some -business logic between the apps -again this is a depth first -implementation -with the goal of getting something -working and building up from there this -applies to the server just as much as it -applies to the client -Secret Key -in order to fetch data about a -restaurant we use a unique restaurant id -since the information is public -knowledge that's not a problem -purchase -uses its own key so it's secure in that -sense -however -when we want to edit a restaurant -we'd want a completely different and -separate key to work with -and that's where the secret key -comes into play -we can't use the restaurant id in order -to edit the restaurant details it's -something that any person -who reverse engineers the application or -its communications protocol can discover -we could use a password scheme but that -would be a hassle to manage -edit reset password etc -it's much easier to just generate a -completely secret -unique id -this is effectively a key that only the -restaurant owner has -but it's internal so he isn't aware of -its existence -the second key is used for write -requests to the server -and thus it's meaningless for the -regular restaurant -restaurant app which is unaware of it -Code -this is actually pretty simple in the -code -we add a secret field and a new find by -secret method -the main challenge is in maintaining the -discipline -of working only with the secret -when changing data -Challenges -normally when we add data like a dish -you would expect the submission to -include both the image of the dish and -all the properties -it's possible but i was having issues -with spring boot and multipart requests -conflicting with the json parsing -it's probably something i could have -solved but i wanted to move forward so i -separated this into two calls -for simplicity -i upload the images in post requests -which is the default for multi-part -request -and put the json in a put request -when the server finishes a build it -should send a push to the device with a -link to the final url -the url -just points to a file saved in the -server local file system -where we generated the app -the way the build -can -run that way the build can run -asynchronously and send the results -later -this also allows us to implement a build -queue -the queue is valuable as we don't want -to overburden the server -i'm skipping push at this moment it's a -hassle to implement and i'd rather focus -on the wider picture diff --git a/docs/website/video-transcripts/FczBAgUIb1c.json b/docs/website/video-transcripts/FczBAgUIb1c.json deleted file mode 100644 index 2417150433..0000000000 --- a/docs/website/video-transcripts/FczBAgUIb1c.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 64, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 339, - "youtube_id": "FczBAgUIb1c" -} diff --git a/docs/website/video-transcripts/FczBAgUIb1c.txt b/docs/website/video-transcripts/FczBAgUIb1c.txt deleted file mode 100644 index 4c135352d5..0000000000 --- a/docs/website/video-transcripts/FczBAgUIb1c.txt +++ /dev/null @@ -1,64 +0,0 @@ -the notifications container lists alerts -received by the app -it's relatively simple -to support this page we'll need to add a -few things into the data model -first we need to add a new notification -business object to the data package -the text of the notification should -arrive from the server -reaction is the material icon associated -with a notification -for instance a heart or a thumbs up -the background color can come from the -server as the original ui has different -color for every notification reaction -this is all pretty simple -to mark this in the ui -we'll need a new method in the server -api class -there can be a lot of notifications so -we are asking for the notifications -since a given time -we construct hard-coded notifications -for every entry with a specific -hard-coded reaction -just so we are on the same page this is -the ui of the notification tab -and these are the reactions i was -talking about -once we have -all these changes in place the -notification ui is pretty simple -we use an infinite container just like -we did in the news feed -and use the timestamp of the last entry -to fetch more -we update the timestamp for the next -request and create a notification -component -the code is simple since there are no -titles or special components -the reaction entry uses the blue circle -uiid to annotate the reaction -setting the color -won't work correctly and might impact -other borders -the solution is creating a new border -instance -the avatar placed in the layered layout -and the reaction is placed above the -z-axis -we then use the flow layout to align the -reaction to the bottom right -just like the other forms we enclose the -html label -and the blue label below into a y asus -container -there is just one missing piece in the -css -from the listing -the code here is pretty self-explanatory -it's a small blue label -with that we only have one last tab to -address diff --git a/docs/website/video-transcripts/GEG_S-dyxaM.json b/docs/website/video-transcripts/GEG_S-dyxaM.json deleted file mode 100644 index a427d848de..0000000000 --- a/docs/website/video-transcripts/GEG_S-dyxaM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 65, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 356, - "youtube_id": "GEG_S-dyxaM" -} diff --git a/docs/website/video-transcripts/GEG_S-dyxaM.txt b/docs/website/video-transcripts/GEG_S-dyxaM.txt deleted file mode 100644 index 02bfde0c63..0000000000 --- a/docs/website/video-transcripts/GEG_S-dyxaM.txt +++ /dev/null @@ -1,65 +0,0 @@ -in this section we'll discuss selection -in the category list and the search -functionality -when i initially built this code i had -some issues with the search feature in -the overlay toolbar which have since -been resolved -i ended up implementing this code -manually instead of using the built-in -search toolbar api -i think it's still a good exercise and -helps understand how these things work -so i didn't change the code back to -search toolbar despite the fact that it -should work now -Enabling Search -to enable search i started with this -method that might look intimidating but -it's really quite -simple -this is the command representing search -which we use instead of -the built-in command -search has two modes -in one of them the title is a regular -label title -and in the other the title is a text -field -where we are searching -when we press the search button we -effectively toggle between these two -we do several big things here first we -create the new text field representing -the search text and give it the hint -search -then we style everything to have the -title ui id -we then bind the listener to the text -field to call the onsearch method -when a user types into the text field -and then open the virtual keyboard -when we invoke the start editing method -OnSearch -the on search -method is actually pretty simple -if searches null we just show all the -entries and move on -if search contains a value we loop over -the dish entries and hide the ones that -don't have the search value within -notice that we do this by looking at the -dish object stored within the client -properties in advance -this makes the search logic simpler -when a user -flips through the category list -we filter the dishes using a method that -is almost identical to the search method -getting an app all the way to the finish -line is challenging but don't let -perfectionism stop you -you need to focus on what's important -about the app and get moving -thanks for watching i hope you found -this educational diff --git a/docs/website/video-transcripts/GEzM1MXkqnk.json b/docs/website/video-transcripts/GEzM1MXkqnk.json deleted file mode 100644 index 02cebdd667..0000000000 --- a/docs/website/video-transcripts/GEzM1MXkqnk.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 93, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 530, - "youtube_id": "GEzM1MXkqnk" -} diff --git a/docs/website/video-transcripts/GEzM1MXkqnk.txt b/docs/website/video-transcripts/GEzM1MXkqnk.txt deleted file mode 100644 index 7a4ce893cd..0000000000 --- a/docs/website/video-transcripts/GEzM1MXkqnk.txt +++ /dev/null @@ -1,93 +0,0 @@ -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/video-transcripts/GMnWMB_JfaQ.json b/docs/website/video-transcripts/GMnWMB_JfaQ.json deleted file mode 100644 index d906005507..0000000000 --- a/docs/website/video-transcripts/GMnWMB_JfaQ.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 130, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 649, - "youtube_id": "GMnWMB_JfaQ" -} diff --git a/docs/website/video-transcripts/GMnWMB_JfaQ.txt b/docs/website/video-transcripts/GMnWMB_JfaQ.txt deleted file mode 100644 index fd87e20ad2..0000000000 --- a/docs/website/video-transcripts/GMnWMB_JfaQ.txt +++ /dev/null @@ -1,130 +0,0 @@ -in the second part we'll cover some of -the other big ticket items in security -we'll start with storage encryption -which allows us to encrypt the storage -data -notice that storage isn't accessible by -default and is isolated -however if a hacker gets -physical access to the device or can get -through an os exploit -this might be an important second line -of defense -notice that this is about storage -encryption -people often confuse file system storage -and storage -they are very different things -file system is low level and pretty -similar to file systems on desktop os's -storage can be implemented on top of -file system -but it's more of an abstract concept -because of that we can apply encryption -seamlessly to the storage class -but can't do -that seamlessness -for the file system or sql -class -the bouncy castle cn1 lib -implements a wide range of encryption -algorithms -that you can use for most common -encryption tasks -we enhanced it with the ability to -seamlessly encrypt the storage -using the encrypted storage class -once this is applied the constant -content of storage will be completely -encrypted -we use aes -for the storage encryption -that's a multi-military grade and -algorithm that balances performance and -security -it should be enough for most purposes -the main weakness with storage -encryption -relates to key storage -you need to pass the key -to the encrypted storage class -this means the key will be accessible to -a hacker that might use it -if you need perfect security you have -the option of encrypting the data with a -randomly generated key -and save that key in the file system -obviously -this poses the problem of -where you will need to encrypt the -randomly -generated key -another option -is to store an encryption key in the -server that will be provided on login -this is the most secure option but it -will block the option of working offline -another important feature is screenshot -blocking -this can prevent potential attack or -even misuse when a screen containing -private information can be captured -you can't block screenshots on ios -that feature isn't available there -on android we can block screenshots -using the build hint to disable -screenshots -this also hides the screenshot from the -android task manager which might impact -the user experience -if you have a banking app you might be -concerned that user will grab a -screenshot that includes potentially -private information -and you might want to block that -some organizations have a security -policy that requires this flag -copy and paste is a common vector of -attack for spyware -if you have a malicious app in your -device one of the first things -it might do is peek into the clipboard -to see if you have -proprietary information there -that's why copying from -password fields is always disabled -but you might want to block some other -fields that contain proprietary -information -you can block copy and paste for the -entire app which might be a bit much for -most applications -alternatively you can block copy and -paste on a specific field -using a client property on that field -jailbroken devices remove important -layers of security -they allow apps to be installed outside -of the app store on ios -which means a lot of the security -measures for -an app are forfeited -they also allow root access for apps -which allows more exploits where -jailbroken devices -allow -full-on spyware to run in the background -the problem is that it's really hard to -detect a jailbroken device -as the very idea of jailbreaking is -hiding this fact -so the is jailbroken device method -makes the best effort but can't -guarantee -if the method returns true the device is -probably jailbroken but if it returns -false -it might still be a jailbroken device -that we failed to detect -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/H7WW4GwXK8o.json b/docs/website/video-transcripts/H7WW4GwXK8o.json deleted file mode 100644 index 7cf8977330..0000000000 --- a/docs/website/video-transcripts/H7WW4GwXK8o.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 45, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 232, - "youtube_id": "H7WW4GwXK8o" -} diff --git a/docs/website/video-transcripts/H7WW4GwXK8o.txt b/docs/website/video-transcripts/H7WW4GwXK8o.txt deleted file mode 100644 index 81646849a4..0000000000 --- a/docs/website/video-transcripts/H7WW4GwXK8o.txt +++ /dev/null @@ -1,45 +0,0 @@ -we're proceeding with image and video -integration -the changes to the client-side -post-class are trivial -we added the map entry for the -attachments -and added it to the list of properties -this will automatically pass it from -json and generate it to json when we -send a server request -we'll need one small change from ser -server api -this is the last infrastructure change -before we start wiring everything -together -when we submit an image post -we'll upload the media before the post -is written -this shouldn't be a problem when -for small images but anything larger or -on a slow connection could be a problem -so the right thing to do is show a -progress indicator for the upload -process -unfortunately the upload method in -server api doesn't allow us to do this -furthermore in one case we have a file -path with no byte array data -obviously we can load it -ourselves but the multi-part api already -has support for that -so we'll leverage it in the api -for now -we now return the multi-part request -so we can bind a progress indicator to -it -if there is no data byte array will fall -back to a file -now i didn't want to get into the i o -exception issue -and just converted it to runtime -exception -this is a simple change -and the final piece before the upload -code diff --git a/docs/website/video-transcripts/HJvMQKM5FPY.json b/docs/website/video-transcripts/HJvMQKM5FPY.json deleted file mode 100644 index 822e57747b..0000000000 --- a/docs/website/video-transcripts/HJvMQKM5FPY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 49, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 299, - "youtube_id": "HJvMQKM5FPY" -} diff --git a/docs/website/video-transcripts/HJvMQKM5FPY.txt b/docs/website/video-transcripts/HJvMQKM5FPY.txt deleted file mode 100644 index 45d23502de..0000000000 --- a/docs/website/video-transcripts/HJvMQKM5FPY.txt +++ /dev/null @@ -1,49 +0,0 @@ -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/video-transcripts/HRTLnLecoMA.json b/docs/website/video-transcripts/HRTLnLecoMA.json deleted file mode 100644 index cb87331ad2..0000000000 --- a/docs/website/video-transcripts/HRTLnLecoMA.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 110, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/030-introduction-and-installation.md", - "status": "transcript-fetched", - "word_count": 561, - "youtube_id": "HRTLnLecoMA" -} diff --git a/docs/website/video-transcripts/HRTLnLecoMA.txt b/docs/website/video-transcripts/HRTLnLecoMA.txt deleted file mode 100644 index b831e07aa7..0000000000 --- a/docs/website/video-transcripts/HRTLnLecoMA.txt +++ /dev/null @@ -1,110 +0,0 @@ -in this module we'll discuss maps in -codename one -focusing on google native maps -and we'll also delve a bit into placing -components on top -but first -let's explain the different kinds of -maps and why this is so confusing -currently the cn1 lib for native maps is -based on google maps -however the architecture is generic -enough -and this functionality can be ported to -apple maps bing -or other map providers -this would obviously require some work -on your part -but the logic within the maps -implementation doesn't include any -special code -and one could in theory adapt the cn1 -lib to pretty much any other map -provider -currently we support four different map -implementations -in the cn1 lib -most of them are based on google maps -but not all of them -the ios implementation -uses the native google maps api for ios -the android implementation uses the -standard native android maps from google -then we have two fallbacks -the javascript fallback uses the browser -component to show google maps within the -browser -and map component can work with either -google maps -or open street maps -we considered map component as legacy -code -and have deprecated it -the original code made a lot of sense in -2012 -when tiled maps were still used by -providers -but modern map implementations are -vector-based -and much faster -that's why we promote the javascript -option as a -fallback -on android or ios the native version for -the os is used -if that fails -the fallback is used -the fallback is also used in other -platforms such as the simulator web etc -map component is generally a subpar -fallback -that is too far apart from the native -map implementation -on the other hand the javascript maps -are closer to the final result -they are officially supported by google -and generally smoother to use -in order to use the maps -you need to obtain keys from google -this isn't too hard and has been greatly -simplified by google -notice that google imposes quotas on map -usage and you might be charged if your -app gets significant -usage volume -for security you should always store -keys in the server and fetch them upon -authentication -this is something i demonstrated in the -key service code in the builder app -however -to keep this code tutorial simple and -self-contained -i won't do this here and will include -the keys within the source -since this will use my keys -i won't leave them in the packaged -source you will need to fill out your -own keys based on the instructions here -these are the urls that allow you to -create the three map keys -you will need -one for javascript which will be used -for fallback -second for android and third for ios -notice that you can just google for -these urls -as they might change in the future -the process to generate the key requires -that you provide the app package name -notice that this must match your unique -app package -once this is done you can install the -native maps library from codename one -settings under the extensions section -you can do that by pressing the download -button and waiting for it to finish -once download finished you can right -click the project and select -refresh cn1 lib files which will finish -the installation of the cn1 lib diff --git a/docs/website/video-transcripts/IaalGP1UDeU.json b/docs/website/video-transcripts/IaalGP1UDeU.json deleted file mode 100644 index f03974eac7..0000000000 --- a/docs/website/video-transcripts/IaalGP1UDeU.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 44, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 238, - "youtube_id": "IaalGP1UDeU" -} diff --git a/docs/website/video-transcripts/IaalGP1UDeU.txt b/docs/website/video-transcripts/IaalGP1UDeU.txt deleted file mode 100644 index 8e1ce78594..0000000000 --- a/docs/website/video-transcripts/IaalGP1UDeU.txt +++ /dev/null @@ -1,44 +0,0 @@ -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/video-transcripts/IqsUSCgSVTo.json b/docs/website/video-transcripts/IqsUSCgSVTo.json deleted file mode 100644 index cc539d4ff6..0000000000 --- a/docs/website/video-transcripts/IqsUSCgSVTo.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 34, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-use-offline-build.md", - "status": "transcript-fetched", - "word_count": 1178, - "youtube_id": "IqsUSCgSVTo" -} diff --git a/docs/website/video-transcripts/IqsUSCgSVTo.txt b/docs/website/video-transcripts/IqsUSCgSVTo.txt deleted file mode 100644 index 3d0f77b882..0000000000 --- a/docs/website/video-transcripts/IqsUSCgSVTo.txt +++ /dev/null @@ -1,62 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -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 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. - -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. - -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 diff --git a/docs/website/video-transcripts/IxVUX0s2Nms.json b/docs/website/video-transcripts/IxVUX0s2Nms.json deleted file mode 100644 index d394d944ac..0000000000 --- a/docs/website/video-transcripts/IxVUX0s2Nms.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 136, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 700, - "youtube_id": "IxVUX0s2Nms" -} diff --git a/docs/website/video-transcripts/IxVUX0s2Nms.txt b/docs/website/video-transcripts/IxVUX0s2Nms.txt deleted file mode 100644 index 30fe074daa..0000000000 --- a/docs/website/video-transcripts/IxVUX0s2Nms.txt +++ /dev/null @@ -1,136 +0,0 @@ -in this video we cover some of the -additional forms in the app and how -we'll address them -starting with the details form -Details Form -the details form becomes far simpler -once most of the data moves to the title -area -the remaining details are mostly billing -which has its own form -and miscellaneous information such as -email url etc -Billing Form -the billing form is important not just -because we wanted the details to feel -less cluttered but also conceptually -billing usually needs to stand -on its own as people seek it out and -expect help specifically for that -feature -it's another relatively simple form that -could probably be created with instant -ui -the build form is pretty much a copy and -paste with slightly different buttons -i think consistency is a virtue in this -case -here's where things get interesting -the details form allows us to navigate -to a styling form -this styling form allows us to click on -an area within the ui of the restaurant -app so we can customize that area -Color/Font Settings -when a user touches the restaurant ui -below you should see a pop-up dialog -with a set of options for customization -based on the option picked -a different ui -will be shown -if foreground or background is selected -we'll open a color picker that allows us -to either type in an html color or -fiddle with intensities to set the right -foreground or background color -Set Font -the font ui should have a very limited -set of options -mostly around basic styling -unfortunately including -files as problematic as we can't open -such a file dynamically on all oss -Set Image (icon, background, Dish) Set Dish Image -when image background or icon is -customized we should get a ui that -allows us to crop the icon to the right -size -appropriately so this image sizes will -always appear correctly -on the device when the final app is -built -this is important -images are too large that are too large -might also burden the server -so we limit image size in kilobytes on -the server -when we adapt the image size locally -we can pretty much guarantee it's not a -huge image -that will cross a problematic threshold -Address/Location Settings Address/Location -the details form will contain a lot of -details and including -all the fields required for restaurant -address -would be overwhelming -so it might make sense to separate it to -a different form -that includes all of that -one important detail -we need -is the geographic location of the -restaurant so we can launch -accurate navigation -to the location -the buttons in this ui -allow us to request location from the -gps instead of typing in -latitude longitude coordinates -the about the tails form should allow -setting the html -for the about form in the original -design i wanted both the ability to set -a url -for an about page and the ability to -embed html but eventually i settled on -setting the url only -Skipped -these are features that should probably -be a part of the app in the future -to keep the app simple i decided to -focus on the core -and remove as much of the things that -aren't essential -removing features is probably one of the -most important things you can do -when designing an app -Implementation Plan -a plan can be something you write down -or have in your head -but without when you are constantly -putting out -fires -and you never get anywhere -an organized plan with process -with a process prevents you from -wavering back and forth and keeps you -dealing with the priorities -i decided on a depth first -implementation where we will get a -specific set of forms working first and -then slowly add features -breadth first is also a good direction -but i prefer depth first as it allows me -to validate the whole process from end -to end relatively early and i think -that's crucial -this is especially important in this -relatively complex app where we might -run into conceptual issues that will -require -re-evaluation of some early designs and -plans -thanks for watching -next we'll start implementing the core -ui elements diff --git a/docs/website/video-transcripts/JWLsSbbo4N4.json b/docs/website/video-transcripts/JWLsSbbo4N4.json deleted file mode 100644 index 954916797b..0000000000 --- a/docs/website/video-transcripts/JWLsSbbo4N4.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 83, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 458, - "youtube_id": "JWLsSbbo4N4" -} diff --git a/docs/website/video-transcripts/JWLsSbbo4N4.txt b/docs/website/video-transcripts/JWLsSbbo4N4.txt deleted file mode 100644 index b9c9e09a37..0000000000 --- a/docs/website/video-transcripts/JWLsSbbo4N4.txt +++ /dev/null @@ -1,83 +0,0 @@ -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/video-transcripts/JZvrsZzdays.json b/docs/website/video-transcripts/JZvrsZzdays.json deleted file mode 100644 index d713c85e6e..0000000000 --- a/docs/website/video-transcripts/JZvrsZzdays.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 93, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 578, - "youtube_id": "JZvrsZzdays" -} diff --git a/docs/website/video-transcripts/JZvrsZzdays.txt b/docs/website/video-transcripts/JZvrsZzdays.txt deleted file mode 100644 index 85297f945e..0000000000 --- a/docs/website/video-transcripts/JZvrsZzdays.txt +++ /dev/null @@ -1,93 +0,0 @@ -user service is a big class and it makes -sense to cover it in two parts -next on the agenda is get avatar it -returns the avatar picture of the user -as a byte array -notice we need an id and not a token to -get the avatar as anyone who is familiar -with the user id should be able to see -the avatar -if the avatar reference is null we -return null instantly -the set avatar method works in a similar -way -notice that unlike get avatar with set -avatar we need a token as this is a set -operation this prevents other users from -changing our avatar -when we map this to the web service we -need to first create a media entry and -only then set the avatar value -the friend request includes a few moving -parts -first we need to send a request -we need an auth token -as this is a secure operation -we obviously don't have the auth token -of the person we are friending -this is a special case where we modify -his entry without an auth token that's a -bit risky -the notification service should notify -the user that he got a new friend or -request -we'll dig into the notification service -soon -but first let's check out the other side -of the equation -the accept friend request method -this time me and him are flipped -this checks against a potential weakness -where a hacker could potentially call -accept friend request for a person that -didn't request it -when a request is accepted we need to -update the friend list on both sides -i didn't add a remove friend request -method which i probably should have -added i think it's trivial to add so i'm -leaving it as an exercise -the last method for user service is the -upload contacts method -it's invoked when the user uploads the -contacts from his phone -if we upload contacts in the past -it's a good idea to try and merge with -what we have -so we can check that by running a -count query -for every contact we find we check to -see if there is a user in the system and -if so we add that user to the people you -may know list -when adding a lot of elements using save -all is much faster than save -for simplicity i chose to merge based on -full name in retrospect it might have -been wiser to use a device unique id for -every contact but that might breed -duplicates too -i oversimplified a very complex process -of handling shadow users -if you take the time to build something -like this then make sure to analyze data -more thoroughly and create a more more -robust social graph -notice that we still need to declare the -update people you may know method we -mentioned before -we search for the shadow user in the -table of users using the find him method -which we'll cover soon -if a friend was found and isn't already -a friend or in the people you may know -list -then we can add that user to the people -you may know -the find him method is simple -boilerplate that searches based on all -the metadata in the shadow user we use -find by email phone etc to find the -shadow user in our regular user database -with that we finished the user service -class which is the largest service class diff --git a/docs/website/video-transcripts/Jk3tTyZroP0.json b/docs/website/video-transcripts/Jk3tTyZroP0.json deleted file mode 100644 index 5737bd7d43..0000000000 --- a/docs/website/video-transcripts/Jk3tTyZroP0.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 162, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 905, - "youtube_id": "Jk3tTyZroP0" -} diff --git a/docs/website/video-transcripts/Jk3tTyZroP0.txt b/docs/website/video-transcripts/Jk3tTyZroP0.txt deleted file mode 100644 index e33af4c618..0000000000 --- a/docs/website/video-transcripts/Jk3tTyZroP0.txt +++ /dev/null @@ -1,162 +0,0 @@ -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/video-transcripts/KckMUmmSzd4.json b/docs/website/video-transcripts/KckMUmmSzd4.json deleted file mode 100644 index e8c1b69335..0000000000 --- a/docs/website/video-transcripts/KckMUmmSzd4.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 182, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1035, - "youtube_id": "KckMUmmSzd4" -} diff --git a/docs/website/video-transcripts/KckMUmmSzd4.txt b/docs/website/video-transcripts/KckMUmmSzd4.txt deleted file mode 100644 index e91761a073..0000000000 --- a/docs/website/video-transcripts/KckMUmmSzd4.txt +++ /dev/null @@ -1,182 +0,0 @@ -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/video-transcripts/Ke8bjFdD1bc.json b/docs/website/video-transcripts/Ke8bjFdD1bc.json deleted file mode 100644 index 575bd33b0b..0000000000 --- a/docs/website/video-transcripts/Ke8bjFdD1bc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 95, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 584, - "youtube_id": "Ke8bjFdD1bc" -} diff --git a/docs/website/video-transcripts/Ke8bjFdD1bc.txt b/docs/website/video-transcripts/Ke8bjFdD1bc.txt deleted file mode 100644 index 38fb8f5a15..0000000000 --- a/docs/website/video-transcripts/Ke8bjFdD1bc.txt +++ /dev/null @@ -1,95 +0,0 @@ -next on the agenda is integrating the -model that we developed with the UI that -we built -first I didn't mention the format -currency call -initially when I started working on the -code I used the L10 manager to format -the code which made sense at a glance -however currency is something we get -from the restaurant itself -and it should handle the currency type -adding the dishes code -is code we already have however this -second block of code is pretty cool -first I'll bind a listener to the dish -quantity which allows me to track -changes to the map of dishes -to the quantities of each -that means that every time a dish is -added or removed in the map of dishes -regardless of the source of the change -we can get an event -and recalculate the amount in the order -updating the amount is literally as -trivial as looping over the dishes and -adding up the amounts when then setting -it to the label -notice that we call revalidate at the -bottom since the label might have a -longer string as a result and we might -need to enlarge the space allocated for -it -we have a very similar code in the -checkout form it updates the total value -based on the dish map -in this case we don't need listeners -since the checkout form only provides -one way to edit the data the dish -container hasn't changed much so I -snapped out most of the code -however the animation portion of the -removal now needs to update the price -after the animation completed -notice we call update price only after -the animation so the repaint of the -price won't interfere with the Running -Animation -the contact test form is practically the -same as before and the only major -difference is that the formerly -hard-coded data is now entirely from the -restaurant class -the main menu form has three interesting -pieces of code I'd like to focus on -first the code to create the categories -list is no longer hard-coded -the second part is the content container -which Loops over the dishes in the main -menu and places them into the UI unlike -the previous code this now uses the -restaurant model to show the data -the final part is the code which adds a -dish -or order is the button you see here it -adds the item to the order total -notice that we just put the dish into -the order and update the quantity -nothing else -the event handling code updates the -subtotal of the order seamlessly -I don't always use the top to bottom -approach of development -but I like it more -when I take that approach I can quickly -bring non-coders into the circle of -discussion as the UI aspect is instantly -communicatable -and everything else becomes far easier -there are always exceptions to the rule -for instance sometimes we build apps for -companies that have extensive back-ends -and no front ends in those cases it's -sometimes easier to just build the -mapping to the back end and then build a -user interface around that -in both cases I'm firmly in the camp of -running quickly through the design -you can't debug a design unless you are -building a mission critical app your -time might be better spent coding -than overthinking a design -thanks for watching I hope you found -this educational please join the -discussion in the comments section and -let me know what you think diff --git a/docs/website/video-transcripts/KhSWyE6rAN8.json b/docs/website/video-transcripts/KhSWyE6rAN8.json deleted file mode 100644 index 3fe47664af..0000000000 --- a/docs/website/video-transcripts/KhSWyE6rAN8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 95, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 527, - "youtube_id": "KhSWyE6rAN8" -} diff --git a/docs/website/video-transcripts/KhSWyE6rAN8.txt b/docs/website/video-transcripts/KhSWyE6rAN8.txt deleted file mode 100644 index a238dd0b7c..0000000000 --- a/docs/website/video-transcripts/KhSWyE6rAN8.txt +++ /dev/null @@ -1,95 +0,0 @@ -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/video-transcripts/L7ulPCUxIsw.json b/docs/website/video-transcripts/L7ulPCUxIsw.json deleted file mode 100644 index 3c4450df58..0000000000 --- a/docs/website/video-transcripts/L7ulPCUxIsw.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 121, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 611, - "youtube_id": "L7ulPCUxIsw" -} diff --git a/docs/website/video-transcripts/L7ulPCUxIsw.txt b/docs/website/video-transcripts/L7ulPCUxIsw.txt deleted file mode 100644 index 0bd41ef7c8..0000000000 --- a/docs/website/video-transcripts/L7ulPCUxIsw.txt +++ /dev/null @@ -1,121 +0,0 @@ -when we build an app with the server -side we still need to think about the -rest api even if it's strictly for our -app -there are still implications such as -security and future enhancement that we -need to consider even for in-house apis -Assumptions -i first start with -general assumptions about the api -it will have several methods to update -create delete elements in the cloud -we will then send a build request -to build the content -this might sound obvious but originally -i thought we'd have an api where -everything is managed locally -and a build accepts all the data from -the client in one request -the main reason i have that design in my -head -is because the design makes sense for -codename one itself -but it doesn't make sense for this -specific api -API Update -this is the first draft of my plan for -the api -update restaurant would allow typical -crude operations on the restaurant -object -the same would be true for dish -files would let us get a specific file -from the server it won't include listing -or anything like that -we'll use this to push out the result -do build will perform the actual -asynchronous build and return -immediately -this is -far from a complete list -but it's a starting point -REST Request -this is the basic implementation of the -update restaurant rest request -the post method accepts image uploads -notice it can accept an icon a logo or a -background image and can set them into -the restaurant object -also notice we use -secret as an argument to find the right -restaurant instance -we return the secret value even though -it's not necessary -the main reason for returning the value -has more to do with spring boot behavior -than anything needed by the client side -the second method in the restaurant -service is the put method that accepts -the json request containing the -restaurant data -this is all pretty straightforward -if we have an existing secret we just -update the restaurant otherwise we -create a new one -the build service accepts three -arguments -the secret -safeguards this process -the push key provides us -the device id to which we can send push -notification -and the target type indicates the type -of build requirement required -for example android source etc -in order for the asynchronous call to -work we need to enable -asynchronous calls and spring boot which -we can do by enhancing the main -application -the build app method -is marked as asynchronous and will run -in a thread queue -right now i implemented a step that just -takes the restaurant app -zip and creates a file with the same -name -in a fixed directory in the file system -File Upload -for upload to work -we need to define the right limits for -file uploads and multipart -we can do that in the application -properties by adding these two entries -to define -maximum file sizes -that's important for stability of the -servers as they shouldn't get overly -large files -the files web service just returns a -file from the given directory -notice we verify the file name doesn't -contain any illegal characters -then verify that the file is within the -destination directory and has a zip -extension -this seems harder -than just returning a file -and it is -the reason for that is that we are -effectively exposing part of our file -system here -and -this is a security risk -for example -someone could include a url like this -that points to the root directory -and fetches the password list -if we don't verify every character in a -url -this would be possible diff --git a/docs/website/video-transcripts/LFoSl_a2rs8.json b/docs/website/video-transcripts/LFoSl_a2rs8.json deleted file mode 100644 index 3d481caf8c..0000000000 --- a/docs/website/video-transcripts/LFoSl_a2rs8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 283, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1559, - "youtube_id": "LFoSl_a2rs8" -} diff --git a/docs/website/video-transcripts/LFoSl_a2rs8.txt b/docs/website/video-transcripts/LFoSl_a2rs8.txt deleted file mode 100644 index 839683b5b5..0000000000 --- a/docs/website/video-transcripts/LFoSl_a2rs8.txt +++ /dev/null @@ -1,283 +0,0 @@ -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/video-transcripts/LUHb5fuWzJE.json b/docs/website/video-transcripts/LUHb5fuWzJE.json deleted file mode 100644 index 3fa2b1f133..0000000000 --- a/docs/website/video-transcripts/LUHb5fuWzJE.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 171, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 965, - "youtube_id": "LUHb5fuWzJE" -} diff --git a/docs/website/video-transcripts/LUHb5fuWzJE.txt b/docs/website/video-transcripts/LUHb5fuWzJE.txt deleted file mode 100644 index ec0a2999dd..0000000000 --- a/docs/website/video-transcripts/LUHb5fuWzJE.txt +++ /dev/null @@ -1,171 +0,0 @@ -in this section we'll finish the -restaurant app -while skipping some big pieces such as -the implementation of the server -i'm attaching the server project to the -module so you would be able to reference -and probably run it -but the goal of this course is to teach -codename one and not to teach server -development -in the next course we'll cover server -development more in depth and touch on -some of the work we did for this server -the goal of this module -is to finish the app -it's not a goal to create a perfect app -but rather to reach a milestone that we -can use -one of the hardest things for developers -to do is to finish -we constantly find something new that -needs doing and it becomes harder to -reach a point of release -that's why it's crucial to define strict -limits and curb your ambition -so you can get something into the hands -of users -restricting the feature set to a strict -list -only works if you maintain discipline -and don't embellish the list -don't add to the entries and don't add -new entries -this is the current list i came up with -for this for this app -it includes many missing pieces and some -of them are pretty big -but generally it represents the things -we need to add or fix -i intend to implement all of these -within this module which is relatively -quick -normally a list like this should take -longer to implement -let's get started with the first item on -the agenda -the network layer -and i'll do this by creating an -abstraction of the server communication -despite skipping the server code most of -this code should be pretty clear -as we go over it -the dish service implements caching -for the dish list -we fetch from the server to make sure -the app loads instantly -if we fetch new data -we will show it later as it's more -important that the app loads quickly -here we check whether cache even exists -this is important for -first time activation -assuming cache exists -we can just load the data from the cache -into the global -restaurant object -the cache has the -json as it was returned from the server -so we can pause it using the standard -parsing code -this is convenient for debugging as we -can inspect the json file in the file -system -for the simulator and see what's saved -locally -the json file contains a map object -which contains two arrays -dishes and categories -the dishes array contains all the -information in a dish and every entry -within this map has the same key value -pair as the dish -object properties -one of the features of property objects -is the ability to populate a property -from a map -or from json directly -and this is exactly the case for which -this is useful -the categories are just string names of -all the available categories we could -probably just go over the dishes and -extract the categories from there -from there -once we add the categories we set the -boolean flag indicating the download was -finished -this is important as external code might -have a listener bound to the property -so it can refresh the ui once the data -finished loading -moving on we have two separate methods -to update the dishes they are both -identical and just use the add to queue -and wait or add to queue -the reason we need both is simple we use -add to queue when the app launches as we -want to launch -right away and don't want to wait for -the data however pull to refresh needs -to block until the data is here -and in that case we need to use add to -queue and wait -the aptly named -update dishes from server -is the common code for both update -methods -it creates a connection request to the -dish url that performs a get http method -on the -server the response is passed mostly -so we can check the timestamp and update -the local timestamp -the timestamp tick -trick allows us to -send a request with the last received -timestamp -and initially we just send zero -this means the server is responsible for -updating that value and if no change was -made since the last time the server -changed that value we don't need to -receive new data -you will notice that if the data is -changed we save it to cache then load it -from cache later -this is pretty important -we call the loading code from the post -response call which runs on the event -dispatch thread -the other callbacks and the connection -request occur on a network thread but -the post response call is on the event -dispatch thread and thus it can change -the ui -or ui related elements -as i mentioned before we send the -timestamp and also an authorization key -to the server the latter is hard-coded -for the app but the former gets updated -by the server every time the data -changes let's move on to the main menu -form where we now fetch dishes from -cache if they aren't loaded yet -you will also notice that the port -refresh call makes use of the update -sync method to fetch the updated dishes -synchronously -this block represents a very special -case of the first invocation when we -have no dishes available and are still -waiting -in this case we place an infinite -progress in place and bind the listener -to the finished download so we can -replace the progress -notice we aren't concerned about dishes -suddenly appearing in the background -thread because everything is modified -via the event dispatch grid so we can -rely on this being in sync diff --git a/docs/website/video-transcripts/LZ_ydua__gY.json b/docs/website/video-transcripts/LZ_ydua__gY.json deleted file mode 100644 index 288706f24a..0000000000 --- a/docs/website/video-transcripts/LZ_ydua__gY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 74, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 454, - "youtube_id": "LZ_ydua__gY" -} diff --git a/docs/website/video-transcripts/LZ_ydua__gY.txt b/docs/website/video-transcripts/LZ_ydua__gY.txt deleted file mode 100644 index 67dd555acd..0000000000 --- a/docs/website/video-transcripts/LZ_ydua__gY.txt +++ /dev/null @@ -1,74 +0,0 @@ -the hardest part in the mock-up is -behind us the next stage is the French -container which lists friends requests -and potential friends suggestions -the friends container is a simple UI and -simple class we have a few UI IDs within -this class that I'll cover soon -when I get to the CSS related changes -let's look at the code -we extend container instead of infinite -container as we don't need it in this -case -technically I could have gone with an -infinite approach but I wanted to keep -things simple -the container arranges elements -vertically using box layout since this -isn't a form it's not scrollable by -default so we need to enable -scrollability because the parent form -isn't scrollable -add pull to refresh removes the elements -and recreates the UI -init fills up the container it's invoked -when the class is created or refreshed -the Avatar and the side of the friend -appears as an 18 millimeter image -we use a blank image as the placeholder -for the URL image so the Avatar will -download dynamically to the local cache -if we have friend suggestions we Loop -over friend requests and add them with -the avatar for each one notice we used -the URL image create cached image API to -fetch the Avatar URL instead of get -Avatar Facebook used square images here -so it made sense to use something else -for this functionality -we do the same for the friend suggestion -list under a different title -next we have the friend request entry -method -we create the button labels based on the -type of request -we place the name above the buttons -using a box layout on the Y Asus then -place the two buttons within a grid -layout giving them the same size -the Avatar is placed in the west since -its size is constant we place the -content in the center which gives it -space to grow -the titles above the listing are created -in the init method with this code block -there this handles the red circle with -the number next to the title -in order to complete this we need a few -CSS changes -the subtitle is just a gray slightly -larger font -the red circle has padding that is large -enough to make it visible the size is -close enough so it will align reasonably -with the friend subtitle -friend name looks smaller than friend -subtitle but it's really because friends -subtitle is written in uppercase -the confirm button is a standard button -with rounded corners and a specific -background -the delete button removes the background -and adds a pixel wide border around it -with that the friend suggestion UI -should work and run as expected diff --git a/docs/website/video-transcripts/Lai--eYYJTw.json b/docs/website/video-transcripts/Lai--eYYJTw.json deleted file mode 100644 index 5bbf0ad539..0000000000 --- a/docs/website/video-transcripts/Lai--eYYJTw.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 148, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 831, - "youtube_id": "Lai--eYYJTw" -} diff --git a/docs/website/video-transcripts/Lai--eYYJTw.txt b/docs/website/video-transcripts/Lai--eYYJTw.txt deleted file mode 100644 index 231fb54f4b..0000000000 --- a/docs/website/video-transcripts/Lai--eYYJTw.txt +++ /dev/null @@ -1,148 +0,0 @@ -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/video-transcripts/M518p4_Horg.json b/docs/website/video-transcripts/M518p4_Horg.json deleted file mode 100644 index 5cb8a2cef8..0000000000 --- a/docs/website/video-transcripts/M518p4_Horg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 104, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 589, - "youtube_id": "M518p4_Horg" -} diff --git a/docs/website/video-transcripts/M518p4_Horg.txt b/docs/website/video-transcripts/M518p4_Horg.txt deleted file mode 100644 index 3ae467ccee..0000000000 --- a/docs/website/video-transcripts/M518p4_Horg.txt +++ /dev/null @@ -1,104 +0,0 @@ -the changes to newsfeed container -include the final pieces to post a media -object and the ability to view a media -post -while i was here i also fixed the code -for displaying a styled post which is -something i never implemented in the -code itself -we'll start at the bottom -with create post bar -which is where we reference the new apis -to post an image slash video file -this was changed from using the -constructor to the new factory method -the gallery picker launches in an all -mode which will show both images and -videos -we invoke the right type of post based -on the user selection -that's a simple change -and now we can actually do a media post -it would actually work -but won't show the image in the ui as -the code within this class still can't -render that -this is a simple fix -first we need a new method that will -create a component to match the given -media id -we'll use the image as a placeholder -image for url image we initialize it -lazily to a size that makes sense for -this device -since the image is scaled in the -download process -there is no need for the calc preferred -size trick here -we can just use a regular button or -label -a media stream can be played directly -from our server url which is pretty darn -cool -we need to override preferred size here -because the media -can be any size -this is a bit oversimplified -proper server media handling will -transcode the -video to multiple form factors and -deliver the right video type for every -device this saves bandwidth but also -makes sure the media is playable -avoiding multiple device related issues -now that this is in place we can tie it -in to the post rendering logic in create -news item -if we have an attachment we add a media -component to the bottom of the post -otherwise we use the same postcode as -before -with the media post -with that media post will work and show -up in your feed -there is however one additional -enhancement i added to the create news -item -method to support the styled posts which -up until now were ignored -i save the result of the if statement -here to make the following code shorter -and simpler -rich text view now accepts a ui id for -the content i'll go into that code soon -the body is given -the background style ui id -the same is true for span label version -of the code -this leads us directly to the changes in -rich text view -because of its nature rich text view is -a bit of a hack -without much support for styling -i wanted to give it some generic styling -support for the rich posts so this isn't -a huge change but it helps -first i had to make font size non-final -as this will change due to styling -the next step is simply a change to the -constructors and edit method init now -accepts an optional ui id which defaults -to null -we set this ui id -in the new constructor but leave it as -null -in the other cases -the ui id is isn't set -if the ui id isn't set everything acts -like it did before -otherwise we get the default font from -the style then resize the bold italic -font to match its size -that's it we can now style the default -font -of the rich text view diff --git a/docs/website/video-transcripts/MLbOdCt67Rw.json b/docs/website/video-transcripts/MLbOdCt67Rw.json deleted file mode 100644 index 0fd7a57839..0000000000 --- a/docs/website/video-transcripts/MLbOdCt67Rw.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 63, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/013-css-changes.md", - "status": "transcript-fetched", - "word_count": 350, - "youtube_id": "MLbOdCt67Rw" -} diff --git a/docs/website/video-transcripts/MLbOdCt67Rw.txt b/docs/website/video-transcripts/MLbOdCt67Rw.txt deleted file mode 100644 index ab7f77620d..0000000000 --- a/docs/website/video-transcripts/MLbOdCt67Rw.txt +++ /dev/null @@ -1,63 +0,0 @@ -now that we have a general picture -of our final destination -let's figure out how to get there -the transparent gradient at the bottom -of the dish view is almost entirely css -which is pretty cool -this css generates an image which is -then overlaid with transparency -i also chose an italic font for this -case which might be more appropriate for -the dish description -the toolbar -also uses the gradient tint -it derives from container to get the -zero padding and margin -the map address -is similar to the dish overview -description -with a bit of padding -the text of the map address -is a separate component unlike the dish -overview -which means these ui ids -are effectively different -i set the background of the side menu -to be white -besides fitting with the general design -and separating it from the form color -this has another important use -one of the important things to notice -about the side command -is that margin is zero except for the -bottom where we have a one pixel margin -if you will look at the entries you will -see a one pixel white line between the -command entries -that's the white color of the side -navigation panel coming through between -the entries -it's not a separation line it's -literally the background -site commands are styled differently -in the native themes -so -we need to override a lot of behaviors -such as text decoration which is set to -3d text in ios -3d text draws a subtle shadow under -every letter -normally there is a status bar component -on top of the side menu in ios -so the clock or battery indicators don't -draw on top of the side menu -since we have an image here -this isn't a problem -and we can set its size to zero -i thought about using the exact same ui -id -as i used for the shopping cart button -but that button has a bit of a margin to -push it away from the side of the form -i probably should have used inheritance -here but i didn't diff --git a/docs/website/video-transcripts/MbTEz-K8iwY.json b/docs/website/video-transcripts/MbTEz-K8iwY.json deleted file mode 100644 index 78adbf1b02..0000000000 --- a/docs/website/video-transcripts/MbTEz-K8iwY.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 193, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/024-capture-and-callbacks-in-ios.md", - "status": "transcript-fetched", - "word_count": 1073, - "youtube_id": "MbTEz-K8iwY" -} diff --git a/docs/website/video-transcripts/MbTEz-K8iwY.txt b/docs/website/video-transcripts/MbTEz-K8iwY.txt deleted file mode 100644 index c91d6c13eb..0000000000 --- a/docs/website/video-transcripts/MbTEz-K8iwY.txt +++ /dev/null @@ -1,193 +0,0 @@ -in this final part we'll finish the ios -portion implementation -there are some methods that have skipped -entirely so i won't discuss them here -but there are a lot of simple methods -like we had in the android port which -just delegate onwards -the getters are the simplest they are -they don't even need to enter the ios -thread -this returns the camera view which -should be initialized once start is -invoked -this method is a standard method in -every native interface everything in -between is trivial -the setters are a bit more verbose but -not by much they are all practically -identical so i'll only focus on the -first -i don't want to call update if the -update didn't actually change -we update on the main thread by invoking -the update methods from before -notice i use the async core as a rule of -thumb always use the async call unless -you must use the sync core -it's faster and has a lower chance of a -deadlock -in this case i don't need the action to -happen within this millisecond so async -will work just fine -i also have two special getters -that need to run on the event dispatch -thread -normally this wouldn't be a problem but -when we need to do -to return a value that's a bit -challenging -the block keyword allows us to mark the -field as a field -we can change from the lambda expression -here is a case where we must use -dispatch sync if we used async the -return statement -would have happened before we assign the -value -now all that is left is the actual -capture method -the capture methods are a bit more -complicated -surprisingly capturing a still image is -a bit harder than capturing a video -since -the capture image method is a bit long -i've split it into two lock before -this is a similar case -of ios 10 requiring a new api -all capture methods must run on the -native ui threads as they deal with the -native ui -failing to do so -leads to weird crashes -this is the new ios 10 api -if it isn't here -we'll execute the ios 9 compatible code -up to this point everything is pretty -trivial -delegate is a special concept in ios -similar to java's interfaces -this means the current class implements -the delegate -before i go to the ios 9 code within -this method let's look at the delegate -in order to implement the delegate we -need to make a small change to the -header file -the delegates are declared in a syntax -that's reminiscent of the java generics -syntax -i also added the delegate needed for -video recording while i'm here already -so -that's two delegates -now that these are in place -let's look at the delegate code -delegate method declarations are huge -this goes all the way to the curly brake -brackets -this makes java's verbosity seem quaint -this is all just one delegate callback -method declaration -this is an error or the delegate was -invoked because of the vid of a video -event -we don't want to step into image -processing code -ns data handles almost all i o and ios -it's similar to bytebuffer in javasc -and allows us to map files from slash -into storage -i use nsdata to byte array to convert -the nsdata object to a java byte array -notice that in the native code all java -objects are effectively java underscore -object -i can now invoke the callback method -with the given byte array notice the -callback method includes the full name -and argument types -the vm passes thread local context on -the stack for every method -this makes things like stack traces work -for the code to compile we need to add -these declarations below the hashtag -import statements -the vm generates c code so we can import -it with include -the ns -data2byter method -is a -method from the ios port i could have -just included a whole header but there -is no need in this case it makes the -process of converting an nsdata -much simpler -now that all this is out of the way -let's go back to the capture image class -and the ios 9 compatibility block -this is effectively code i got from -stack overflow -i needed this since most samples are for -ios 10 plus now and ignore the legacy -but i still have quite a few ios 9 -devices that can't upgrade to ios 10 so -i'm assuming there is still some market -for compatibility -of this code is boilerplate code for -detecting the camera -no wonder it was removed -the captcha image process is -asynchronous and invokes the lambda -expression below to process the -resulting image -the rest of the code should be very -familiar as it's almost identical to the -one we used and the ios 9 version -we just call back into java -with this image capture should now work -for both old and new devices -all the basics should work with the -exception of video -capturing video is surprisingly easier -than capturing still images -first the simplest method -is capture video -the no arguments version of this method -saves to temporary file -file name we invoke the version that -accepts the file -with a temp.mob file path -the version that accepts a path -isn't much harder -the av capture movie file output class -represents the recording process -we lazily initialize it -we bind the output value to the capture -session -now we can provide a url from the file -argument and start recording again -we use self as the delegate -which obviously leads us to the delegate -method -most of this should be very familiar now -that we went through the image capture -code -the callback accepts a path string which -we can translate from in a string using -the from in a string api call -the last remaining piece -is the stop video method -this is a trivial method if you've been -keeping up -we just stop the recording and clean up -and with that last bit of code we are -done -i hope you found this useful -there is a lot of code to go through but -most of it is -damn trivial once you look at it -you can create native interfaces that do -just about anything if you have the -patience to debug and google native apis diff --git a/docs/website/video-transcripts/Mcw8z_uP3BA.json b/docs/website/video-transcripts/Mcw8z_uP3BA.json deleted file mode 100644 index 41ee055c39..0000000000 --- a/docs/website/video-transcripts/Mcw8z_uP3BA.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 273, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/020-introduction-and-generic-code.md", - "status": "transcript-fetched", - "word_count": 1479, - "youtube_id": "Mcw8z_uP3BA" -} diff --git a/docs/website/video-transcripts/Mcw8z_uP3BA.txt b/docs/website/video-transcripts/Mcw8z_uP3BA.txt deleted file mode 100644 index 52d410ca5b..0000000000 --- a/docs/website/video-transcripts/Mcw8z_uP3BA.txt +++ /dev/null @@ -1,273 +0,0 @@ -this module covers low level apis and -some -ios slash android specific details -i don't aim to teach objective c or -some core concepts of android -development -you don't necessarily need to know these -things but they would help -it's important to notice that the code -in this module is fresh and complete -it's here as a porting reference -since this is pre-alpha level code it -should be treated as such -i've discussed low-level native -interfaces and third-party library -integrations before -but i never did it for an api as complex -as the camera api -arguably the maps support is harder so -developers could have used it as a -reference -my cheap motivation in doing this module -is building the cn1 lib itself -low-level camera access has been a wish -list of hours since we introduced said -ordering of components -as such -the tutorial is an afterthought -the first step of implementing a cn1 lib -is looking for available apis on both -sides -and sometimes surveying the work done by -other cross-platform tools for -inspiration -in this case other tools don't offer -something as comprehensive -so i don't spend too much time on that -android's native camera api is a mess -and at some point google just threw the -whole thing out and replaced it with -camera 2. -if you want to support older devices -you need to support both apis and -there's a lot of nuance with permissions -etc -i didn't want to use the native android -api as is -and started checking for options -two open source camera apis stood out -and camera kit android looked like the -best option -normally this is the place where i would -go over the android api and go over the -ios api and try to find common ground -instead i chose to skip that -and just write the android -implementation -then think about the ios implementation -after the fact -my reasoning was that camera kit already -spent a lot of time designing an -abstract api -it makes sense to follow their lead -i'm not a camera expert but they already -dealt with -user requirements and design issues -this would make the android -implementation trivial -and if something doesn't work on ios or -elsewhere we can create special cases -for that -i didn't build a cn1 lib -i started with an app that uses -native interfaces -apps can i can debug unlike a cn1 lib -once the app was done i just copied the -sources into a new cn1 lib project and -created that -so without further ado let's dive into -the portable portion of the code -as i mentioned before i decided to model -the api -based on the android camera kit api -which is mostly exposed in the camera -view class -you can see in the camera view project -camera view is a big class with a lot of -ui code so i wanted to distill the api -calls within -i searched within the class for public -methods and isolated them -i then created a new native interface -within com.codename1.camera -the one method that's a special case -that didn't exist in the original -is peer component -get view -this is a method that returns the -preview for viewing the camera -in the native code it's the actual -camera view class but in our case the -native interface hides that -this method returns the camera view -in the native code -camera view has a method -get camera properties which returned an -object called camera properties -that object contained two fields -horizontal and vertical -viewing angles which i just mapped -directly -in retrospect some of these apis like -toggle facing flash -is facing back front should have been -implemented in the java side -the api already used ins for constants -so this was pretty easy to translate -into a native interface -these two rely on event listener code -i'll elaborate more on that soon -these were originally preview and -capture size -they returned a size object which was -just width and height in a class -as you can see the translation was -mostly easy and other than a few methods -that returned objects everything was -really smooth -i did gloss over some apis i chose not -to implement within the native interface -you will notice all of these methods -have one thing in common callbacks -we can't pass a callback into the native -interface -so all of these apis can't be -implemented in the native interface -we can implement them in the high level -abstraction though and i'll go into and -i'll get into that soon -but before we go there i did mention the -constants from the native api -they are implemented in the camera kit -class in the native project i had to -implement our own constants which i did -using an interface -you'll notice that this is copied from -the android version and so values -returned from there should automatically -work for us on android -which is pretty cool -never expose -a native interface to a user it's a -recipe for disaster as things might not -work as expected -always -wrap the native interface -in a regular class as that allows for -fine grained control -it lets you change the native api and -move forward while keeping compatibility -and simplicity -so the next order of business was the -decision of -how to encapsulate this functionality -for that i created the camera kit class -which abstracts this native interface -we implement the constants interface -so we'll have easy access to them -the native interface instance is shared -between the methods of the class -this is a singleton -it's not how the native api works but -it's much simpler and we don't need -anything better -use this list -to send camera events to the user of the -api -i'll discuss that further soon -ios strips out unused code which can -cause callbacks to fail -we need to trick the ios vm into -thinking that some code will execute -ios needs a build hint we can just -inject it into the build hint list if -the user didn't declare it -we review the build hints if the ios dot -ns camera usage description isn't -declared -we add it -notice that this will work only -on the simulator -i called this create even though it's -more like a get instance method -while the camera kit won't work on the -simulator i can still bind functionality -to it -in this case we do -two things in the simulator -we registered the required build hint if -it isn't there already -we invoke all the callback methods -notice that this code will never execute -but our ios vm can't detect that because -this is for ios -is in private -without these calls -the ios vm would -strip out these methods and the c code -that invokes them -wouldn't compile -if the native interface is supported on -this device -then we can return the instance of this -class -otherwise we return null -there is a lot to digest here luckily -this was the hardest part the rest is -verbose -but it's far simpler than this -most of the class looks like this -we delegate the calls to the native -interface to keep that code hidden -nothing interesting -there are a few interesting methods -within the class so i'll address them -here and skip these boring calls -toggle facing -is in the native interface but instead -of implementing it in ios i chose to -implement it -in the camera kit and ignore the native -interface -the same is true for toggle flash i'm -sure i could do this for several other -methods and simplify the native -interface layer -the add remove listener code -just modifies the list of listeners -notice i trimmed the code a bit so it -would all fit in one slide -the fire methods are invoked internally -to send events to the listeners -since the native code invokes them -they might not be on the edt and so they -recurse via call serially -to move the context into the edt -the event dispatch logic is identical -for all the fire methods -it's just a loop over the listeners and -a method invocation -the rest of the listeners follow the -same semantic for updating a video -result or an image result -so -who invokes the fire methods -i deprecated this and the native -interface so developers won't use them -by mistake -this class is for internal usage only -all three events are handled in the same -way -through the fire method -notice the different types of arguments -to the constructors -we'll discuss the complexities of them -in the ios port -the final two missing pieces are the -listener interface -and event class -both should be pretty obvious -so i won't list them here -you can check out the project source -code if you are curious diff --git a/docs/website/video-transcripts/NVPIXnkoxv0.json b/docs/website/video-transcripts/NVPIXnkoxv0.json deleted file mode 100644 index 015b96e64b..0000000000 --- a/docs/website/video-transcripts/NVPIXnkoxv0.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 15, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-use-cloud-connect.md", - "status": "transcript-fetched", - "word_count": 421, - "youtube_id": "NVPIXnkoxv0" -} diff --git a/docs/website/video-transcripts/NVPIXnkoxv0.txt b/docs/website/video-transcripts/NVPIXnkoxv0.txt deleted file mode 100644 index 89c74b15c6..0000000000 --- a/docs/website/video-transcripts/NVPIXnkoxv0.txt +++ /dev/null @@ -1,29 +0,0 @@ -_Transcript source: embedded._ - -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. - -We’ll start by defining "cloud connect". Cloud connect synchronizes the GUI builder with devices seamlessly. - -When you make changes within the GUI builder those changes are instantly reflected on devices connected to your account via the cloud. - -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 diff --git a/docs/website/video-transcripts/Nv_0NVgCbSk.json b/docs/website/video-transcripts/Nv_0NVgCbSk.json deleted file mode 100644 index 73ee3d3ead..0000000000 --- a/docs/website/video-transcripts/Nv_0NVgCbSk.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 139, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 804, - "youtube_id": "Nv_0NVgCbSk" -} diff --git a/docs/website/video-transcripts/Nv_0NVgCbSk.txt b/docs/website/video-transcripts/Nv_0NVgCbSk.txt deleted file mode 100644 index 49e1db0f31..0000000000 --- a/docs/website/video-transcripts/Nv_0NVgCbSk.txt +++ /dev/null @@ -1,139 +0,0 @@ -next we have the name entry stage in the -sign up wizard -the name entry stage in the signup -wizard -is pretty trivial there's just one -interesting aspect here -the text -input notice that the text input looks -different in android when we enter text -and on ios it looks completely different -this is part of the magic behind the -text component class on android we -animate hint labels in the style of -material design -and in ios we show a grouped pair -let's go right into the create name -method -text component -is a text input component that combines -the label and text field into one -component and automatically adapts -it to the platform -text mode layout is a layout manager -designed for text component -it uses table layout on android and box -layout on ios -this will act like adding to a table -layout when running on android thus -placing the components side by side -on ios it would ignore the percentage -value and stack the components -vertically using a box layout on the y -axis -the text mode layout isn't really a -layout as much as it is a delegate -when running in the android mode which -we refer to as the on top mode -the layout is almost an exact synonym -of table layout and in fact delegates to -an underlying table layout -in fact there is a public final table -layout instance within the layout that -you can refer to directly -there is one small difference between -the text mode layout and the underlying -table layout and that's our choice to -default or to default to align entries -to top with this mode -it's important for error handling which -i didn't use here -when working in the non-android -environment we use a box layout on the -y-axis -as the basis -there is one thing we do have that's -different from a default box layout and -that's grouping -grouping allows the labels to align by -setting them to the same width -internally it just invokes component dot -set same width -since text components hide the labels -there is a special group method there -that can be used -however this is implicit with the text -mode layout which is pretty cool -this listens to the done button -in the virtual keyboard and -automatically treats it as a click on -the next button -for this form i need to add one change -to the css -this is the text that appears on top of -the android field when we have input -there -it's a really small label that uses -regular font for visibility instead of -light -with that we finished the name form and -it should just work -birthday is the form i'm currently least -happy with -we are in the process of rewriting the -picker component which would hopefully -allow a much better looking ui for this -form -for now i compromise on this -when the ui is clicked on the device you -will see a nice looking date picker ui -though -the flip side is that the code is super -simple -we set the picker type to date -20 years ago is a good place to start -for an age picker by default so that's -what i did -that's trivial and includes no changes -to the css -the gender form is pretty simple -we have a couple of ui ids for the -gender toggle and icons -before we can go into it i created a -small helper method to make the two -gender buttons -toggle buttons and codename one can be -radio buttons that means that we can -pick only one of them -when we click on female the mail entry -is deselected -we create the font icon based on a -separate ui id because we want it to be -much bigger than the text on the button -so we can't use set material icon -here we create the radio button and set -the text positioning -a radio button belongs to a group and -only one member of the group can be -selected -that's mostly simple boilerplate code -the form code is even simpler -we use button group to tie two or more -radio buttons together -only one of the radio buttons in a group -can be selected -grid layout gives both buttons the exact -same size -the last piece for this form is the css -for the ui ids we defined -the gender icon uses the same icon -settings as the logo with a different -color -we have a special case for the selected -pressed versions of the ui id to make -the icon white -the gender toggle ui id -uses a round wrecked border with a gray -line surrounding it once we did all that -we can run the app and we would be -halfway through the wizard diff --git a/docs/website/video-transcripts/OWHizrNyizQ.json b/docs/website/video-transcripts/OWHizrNyizQ.json deleted file mode 100644 index 35fe14b362..0000000000 --- a/docs/website/video-transcripts/OWHizrNyizQ.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 29, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 235, - "youtube_id": "OWHizrNyizQ" -} diff --git a/docs/website/video-transcripts/OWHizrNyizQ.txt b/docs/website/video-transcripts/OWHizrNyizQ.txt deleted file mode 100644 index 8532879517..0000000000 --- a/docs/website/video-transcripts/OWHizrNyizQ.txt +++ /dev/null @@ -1,29 +0,0 @@ -In this tutorial we will go over the process of signing an iOS application -We start by logging in to the iOS provisioning portal -In the certificates section you can download your development and distribution certificates. -Devices -In the devices section add device id's for the development devices you want to support. -Notice no more than 100 devices are supported! -Application ID -Create an application id, it should match the package identifier of your application -perfectly! -Development -Create a provisioning profile for development, make sure to select the right app and make -sure to add the devices you want to use during debug. -Download -Refresh the screen to see the profile you just created and press the download button -to download your development provisioning profile. -Create a distribution provisioning profile, it will be used when uploading to the app -store. -There is no need to specify devices here. -Download the distribution provisioning profile. -Import -We can now import the cer files into the key chain tool on a Mac by double clicking the -file, on Windows the process is slightly more elaborate -We can export the p12 files for the distribution and development profiles through the keychain -tool -Export -In the IDE we enter the project settings, configure our provisioning profile, the password -we typed when exporting and the p12 certificates. -It is now possible to send the build to the server. -Thanks for watching. diff --git a/docs/website/video-transcripts/OnZIaGXDZSo.json b/docs/website/video-transcripts/OnZIaGXDZSo.json deleted file mode 100644 index 8065eb666d..0000000000 --- a/docs/website/video-transcripts/OnZIaGXDZSo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 154, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 854, - "youtube_id": "OnZIaGXDZSo" -} diff --git a/docs/website/video-transcripts/OnZIaGXDZSo.txt b/docs/website/video-transcripts/OnZIaGXDZSo.txt deleted file mode 100644 index 4458ebf095..0000000000 --- a/docs/website/video-transcripts/OnZIaGXDZSo.txt +++ /dev/null @@ -1,154 +0,0 @@ -next we'll explore the comments ui -i've simplified the comments ui a bit -mostly to keep the code short and -understandable -i could support arbitrary depth threaded -comments and various other capabilities -but this creates some usability issues -eventually as space narrows and it -becomes harder to follow the discussion -thread -so i limited the replies to top level -comments only and try to simplify the -user experience a bit -i also used pearl shaped comments -instead of round rectangles they just -look nice and i left it at that -Text Field -let's check out the code -this is the text field where the user -can type -in his own comments to submit -comments are placed in a scrollable box -layout container -in the center of the form -when we press reply to a specific -comment its id is placed here -the border layout is needed so we can -place the text field input in the bottom -of the form -we don't add the text field directly -though we also add a send button with it -by wrapping them both into a border -layout together -i used static import of font image so -this line will work correctly -if the send button or the done button in -the virtual keyboard is pressed -we post the comment currently within the -text field -this adds the existing comment to the ui -Post Comment -there were a few methods mentioned in -this code that we need to go over -specifically post comment add comment -let's start with the former -we connect to the server so we need to -show a blocking ui dialog -we construct a comment object and set -the values to for it -notice we don't need the comment id as -that's generated in the server -the server api does the heavy lifting of -submitting the comment -when we are done we need to explicitly -clear the text field otherwise the -submitted comment would still be there -we animate the ui to valid state after -the addition of the comment -Add Comment -this naturally leads to the add comment -method -create comment creates the visual -representation of a comment we'll -discuss it next -the comment doesn't have a parent if the -comment doesn't have a parent -we'll just add directly to the list of -comments -we search through the hierarchy to find -the parent comment component -assuming we find it -we can nest the component -every parent -component comment component has a -container for replies children -that's nested into it -if the child container isn't there yet -we create it dynamically and add it -notice -that we add to a specific offset within -the container so the comments appear -below the parent -if the child container already exists -we can add to it directly notice that -since comments are already sorted by -time -adding to the end will always work -correctly and consistently -with that we effectively implemented one -level of comment nesting we can further -generalize the nesting logic to allow -additional or even infinite levels of -nesting but as i said earlier this might -have usability issues -Create Comment -finally we need a few additional methods -to implement the creation of the comment -etc -create comment generates the -component that represents this given -comment and potentially the reply button -the comment itself is just a text area -with a comment ui id -we make that text uneditable -fetch an avatar image for the chat -i could have used rounding and similar -effects but i chose to keep the code -simple -if we reply to a specific comment we -just change the id of the parent comment -the comment instance is stored in the -components client properties so we can -later connect them in the find -find parent comment method -Find Parent Comment -find parent comment loops through the -container and finds the component -matching this component id -if the comment we are looking at -has the same id -then we can return -this component -in this case the component matching this -id doesn't exist or isn't there yet -if this meth in this method we return an -image matching the user's avatar which -is exposed via slash user slash avatar -slash ur user id -on the server -Comment Form -with that comments form is nearly done -we need a css style for comment -it's -black text over light gray background in -perl rounded shape -the one thing to notice -is the padding -we need a lot of it to keep the text -away from the curve of the border -now commons form should just work once -we plug it in -to do that we need to go to create news -item in news feed container while we are -here -we might as well implement the like -functionality too -we set the like toggle buttons -selected state if the user already liked -this post we invoke like on a click -this is already implemented in the -server api so this is just one call -we add the action listener that invokes -the comments from a form and that's it -like and comments should work diff --git a/docs/website/video-transcripts/Otujb_KyofA.json b/docs/website/video-transcripts/Otujb_KyofA.json deleted file mode 100644 index f4f7c6ef0b..0000000000 --- a/docs/website/video-transcripts/Otujb_KyofA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 176, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 991, - "youtube_id": "Otujb_KyofA" -} diff --git a/docs/website/video-transcripts/Otujb_KyofA.txt b/docs/website/video-transcripts/Otujb_KyofA.txt deleted file mode 100644 index 5fcb33d1d8..0000000000 --- a/docs/website/video-transcripts/Otujb_KyofA.txt +++ /dev/null @@ -1,176 +0,0 @@ -notification is the last client data -class that maps to an entity -in that sense it will get more -interesting after this point -the class is pretty standard -again these map directly to the -client-side data -notification might relate to a post or a -comment so having its id here -might be useful to launch said post -when a notification is clicked -the constructor and dao match the -conversion of -the other classes -the rest is again getters and setters -the next step is the dao which again -maps very closely -there is literally nothing special here -we might as well have used the -notification entity to some degree -as you can see the rest of the code is -classic boilerplate -this is getting a bit more interesting -newsfeed represents the feed for the -user -i could have just created a really -complex join query on post and called it -today -but that's problematic -the news feed isn't just a list of posts -based on date -it's something that includes complex -a complex algorithm behind it -running that algorithm every time the -use of pages through data -might be very expensive -furthermore changes to the algorithm -shouldn't impact -past placements -we'd want consistency and we'd also like -to have a log -of what did the user see -in that sense constructing the news feed -as an entity in its own right makes a -lot of sense -we can place the articles into the news -feed using a sort order that makes sense -for a specific user without impacting -everyone we can tune and experiment with -article placement algorithms easily -because our processing can be done -offline on the table itself -we use an auto increment numeric id -since -this object isn't exposed to the client -side -at any stage the id doesn't matter as -much and this id type is faster -every entry within the news feed has a -user and post -notice that the user is probably a -different user -from the one who submitted the post -post day is the number of days since -epoch -this is useful for sorting i want -today's posts to always come before -yesterday's posts -even if they are higher quality -timestamp is also used for the sort -but to a lesser degree -rank can be manipulated by the ranking -algorithm to push good posts up -within the day -we have no doubt we don't need it this -is a server-side object -we do need the getters setters though -for the entity -the news feed also requires a crude -repository class -we need to use the auth and not the id -to prevent a case where another user -gets a peek at my feed -which might include friend only posts -with that we only have one more item -the shadow user is a potential user -of whom we have knowledge -but he might not be in the social -network -you ever wonder how facebook seems to -know so much about you before you even -signed up -they keep suggesting people that they -can't possibly know about -well -here's one of the tricks they use -when you agree to synchronize your -contacts to facebook and they nag a lot -about this -they upload the whole thing and store it -if you have friends that are already in -facebook you'll get suggestions -instantly but they keep everything -anyway -when one of your contacts signs up for -facebook they notice that you know and -instantly add a suggestion -this works in reverse too someone has -your email or phone number so you'd see -them as a suggestion -they go even further by associations for -instance -you might have a connection like that -through a third party so even if you -never synchronized contacts they'd still -know -i chose to implement a relatively simple -version of this and didn't include all -the metadata that facebook probably -stores -this is mostly a simple proof of concept -that should be enough for us to provide -friends suggestions -we again use auto increment for -an internal entity -this is the metadata we gather for a -user there is obviously a lot more we -can gather -this is the user who uploaded the data -so if the data in this record matches me -then i probably know the owner -we have a dow object but this dial is -right only -so the entity isn't exposed to the -outside world but the outside world is -exposed to the entity -that's why we don't have a get dial -method -we do have a special constructor type -that makes adding a new shadow user -easier -with the as with the other entities the -rest is just setters and getters -in a sense the shadow user dao is a bit -different from other dows -this is pretty standard now with two -bigger missions -id and owner both of which are missing -the idea is redundant as it's auto -generated the user is also redundant as -it's always the user that uploaded the -contacts there is no point of including -either one of those -values in the dao as they won't arrive -through through it from the client -the rest of the dao is just getters and -setters -in this class in this case the crude -repository is actually pretty -interesting we need it for the case -where we create a new user or update -data -we need to find all the related users -given a user's phone we look through the -database for users who might have that -phone in their contacts the same is true -for email -we check if a user has uploaded contacts -in the past -by counting his entries assuming he has -we will try to merge the data -we use the full name of the user when -merging contact data -with that we finish the entities and we -are finally ready to move up the food -chain to the service layer diff --git a/docs/website/video-transcripts/PERdJq3I-As.json b/docs/website/video-transcripts/PERdJq3I-As.json deleted file mode 100644 index be719d64e1..0000000000 --- a/docs/website/video-transcripts/PERdJq3I-As.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 77, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 429, - "youtube_id": "PERdJq3I-As" -} diff --git a/docs/website/video-transcripts/PERdJq3I-As.txt b/docs/website/video-transcripts/PERdJq3I-As.txt deleted file mode 100644 index 63fd8f60c3..0000000000 --- a/docs/website/video-transcripts/PERdJq3I-As.txt +++ /dev/null @@ -1,77 +0,0 @@ -it will take a bit of work to implement -the server and proper abstraction -however it's a good idea to put things -in the right place to begin with -using a server api class will help us -organize things correctly -piece by piece -we can then replace mock-ups with -implementations and see things working -almost instantaneously -currently the server api class -serves as a mock-up it's a -static class that hides the server -connection -i'm -blocking api calls within this class as -they are easier to integrate into the -infinite container which i'll introduce -soon the currently logged in user is a -global state in the application which is -a pretty standard practice -i've set up a few images from the -diverse ui project so we can have fake -users with avatars in the app -check out the diverse ui project at -diverseur.com -its goal is to feature more diversity in -app mockups -we need times in the timeline etc to -start from a reasonable enough date so i -can use -now as the baseline -we'll set up four user objects -that we can use soon -this method returns the current user -after lazy initializing it -this is the shorthand builder -constructor where i set a hard-coded -user -i add friends and people i may know -which are used later in the friends view -this method fetches the posts in the -current timeline -the method accepts time of the last post -so we can fetch the posts -since that time -we add a comment to the comment count -so the comment count wouldn't be zero -there is still no comment view though -the post itself doesn't include much -either just some html -for the post content -when we return null when there is no -further information that's it we are -almost ready to implement the news feed -before i start with the news feed itself -i'd like a couple of utility methods -for that i'll add a ui utils class as -such -i use these to present time logic more -efficiently -these create a gray spacer that's used -in several places in the facebook ui -this method formats time as statements -such as just now 15 minutes ago etc -then falls back to standard date and -time values -we use this when displaying time on -posts we'll use these in the newsfeed -container class -but before we get there -we have -the missing css statements for the ui -ids we used in the listing -these separators have a gray background -and right amount of padding to match the -ui diff --git a/docs/website/video-transcripts/PHb5YO77OQk.json b/docs/website/video-transcripts/PHb5YO77OQk.json deleted file mode 100644 index 9e48403180..0000000000 --- a/docs/website/video-transcripts/PHb5YO77OQk.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 120, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 666, - "youtube_id": "PHb5YO77OQk" -} diff --git a/docs/website/video-transcripts/PHb5YO77OQk.txt b/docs/website/video-transcripts/PHb5YO77OQk.txt deleted file mode 100644 index 5639d769ab..0000000000 --- a/docs/website/video-transcripts/PHb5YO77OQk.txt +++ /dev/null @@ -1,120 +0,0 @@ -there is a fine line between doing a -mock-up and implementing a data model -once we have a ui in place -designing the underlying data model -becomes a puzzle of filling in the -missing pieces -before we go into the news feed i'd like -to create the object that represents -users and posts having these in place -will make things easier when we want to -implement the server -we'll use property objects which make -server communication and caching trivial -first we'll start with the user object -we will need a new package to separate -the data model i went with com -codename1.fbclone.data -so what do we need for user a lot of -these things are obvious from a sign up -the signup ui -first name -family name -email -phone -gender and birthday -we also need a unique id since security -is crucial -i will go with a string based unique id -we also need a picture of the user to -show next to his posts so an avatar -entry will also help -this is all easy to express in a -business object class -these properties match the things we -said we needed to know about the user -dates are easy to store as long as value -has long values since epoch -getting the full name is a pretty common -thing so it makes sense to provide this -helper method -everything about this class is really -simple except for the avatar -it should point at an image url but how -would we use it -to simplify that i'll add a get avatar -method to user that will include the -functionality of fetching slash caching -slash resizing and shaping the avatar -image -the method accepts the image size in -millimeters and returns an image that -matches that size -we have an image in cache for this -for this user we'll return that image -we load the image from storage if -loading failed due to corrupt image -we'll delete the image file so the next -call to this method will recreate it -we create a mask image that's a -white on black circle that we'll use to -crop the image so it will appear -circular -we use the material design font image of -a person to create an avatar placeholder -image -masking doesn't work with font image -so we convert the font image to a -regular image and mask it -we have an avatar url -we fetch the data from the url into a -file and mask using the given image -automatically -this isn't trivial -but isn't hard either it will produce -the round avatar image we see in -facebook -but we aren't done yet -we need to -do the post object -for the news feed which lists posts on -the wall -again we can refer to the ui to decide -on the content of this class -user is the person who wrote the post -date -title -content -visibility is it public friend only etc -styling we can configure the background -image color of the post -comments -and likes -we'd also need an id for the post as -before likes can't be a numeric count -since each user can only like once -we need a list of users who liked the -post -comments should be represented as a -comment object as well -i'll get to that soon -this is the post class -again the class matches the ui almost to -the letter -comments and likes use list property -to handle multiple entries under a post -this raises the obvious question about a -comment since we aren't close to -implementing threaded comments yet i'll -have to guess about the content of this -entry -comments are bound to a parent post -they can be posted by any user notice i -use ids instead of objects -this will be simpler during passing of -the data -the idea of the parent comment or null -this allows us to implement nested -comment threads -we'll need one final piece and the data -model for now diff --git a/docs/website/video-transcripts/PRk7EhXQRqs.json b/docs/website/video-transcripts/PRk7EhXQRqs.json deleted file mode 100644 index d965be55df..0000000000 --- a/docs/website/video-transcripts/PRk7EhXQRqs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 213, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1134, - "youtube_id": "PRk7EhXQRqs" -} diff --git a/docs/website/video-transcripts/PRk7EhXQRqs.txt b/docs/website/video-transcripts/PRk7EhXQRqs.txt deleted file mode 100644 index 7dea04cff7..0000000000 --- a/docs/website/video-transcripts/PRk7EhXQRqs.txt +++ /dev/null @@ -1,213 +0,0 @@ -setting up a server in the cloud is -probably one of the most tedious tasks -we need to do -even if you are a corporate developer -who never has to do this i think there -is a lot of value in understanding what -goes into this -this module is aimed at a quick and -dirty deployment the goal of this -tutorial isn't to teach you scaling or -complex ideas -just just to follow through all the -before we begin we need to make some -changes to the pom file -this is one of the coolest features in -spring boot and that says a lot -you can generate an executable binary -script that's installed in front of the -jar -that means you can literally type the -name of the jar file on linux and it -will run -no java minus jar or anything like that -this might seem trivial but the truly -amazing thing is that you can use it as -a service on linux -which means you can bind it to common -system services in etc in a d -if you are not deeply familiar with -linux that might not seem like much -but it makes setting up the server much -easier -App Engine -i'm a big believer in sustainability -if running your startup's backend is -five dollars per month -that gives you a pretty decent chance of -running without breaking the bank -a lot of startups including ourselves -make the mistake of over scaling -we used app engine which was very -alluring at the time -the problem is that costs quickly -ballooned to thousands of dollars per -month -which was totally unjustified -using a vps is safe and scales with the -need of your business -but let's go on and talk about the -server itself -Server -i used mariadb instead of mysql in -production -on the desktop i preferred mysql since -it had a better installer for mac os 10. -mariadb is a fork of mysql that is -pretty much compatible -i normally prefer to have a similar -environment on production and -development -but in this case i use a mac instead of -linux anyway -so i just went with it -SSH -i use ssh to log into the server -if you use linux or mac this is just a -command line -for windows users there are a lot a lot -of free ssh clients in the wild and you -can use them -Linode -we'll start on the linode site -i'm skipping some of the points related -to signer billing etc -i just select the vps type which in this -case is the cheapest option -i create a new server instance -Operating System -once the server is active we need to -install an operating system -Deploy -i choose to deploy a new image which in -this case refers to the disk image -effectively this means operating system -install -i choose centos because i'm used to it -and it's well supported -i type in the root password -which is something you need to remember -so don't lose that -then deploy -once i do that the server will take some -time to set up -after it's done -press boot to start up -the new vps -User vs Root -once the server is booted you can ssh to -it with your root password notice that -you should normally use a user rather -than root as usage of the root user -should be restricted to a minimum -root is all powerful -you want to reduce usage of root for a -few reasons -the obvious one is that mistakes and -root can be costly -but another reason can be that if your -system was compromised with lower -privileges -a hacker has a good chance of elevating -these privileges -every time you use root -a few years back -apache was hacked -and -the hacker -relied on a vulnerability in a script on -the site to gain access -the thing is the script was running -under a regular user that isn't rude so -he couldn't really do much -he uploaded an exploit that would give -him access to root -only if a root user -executed a specific command -and bingo he got rude had someone not -used root -the hacker would have been stuck in -purgatory -for a long -time -Create a new user -so -the thing we need to do when we get to -the server -is create a new user called builder -we'll use that user to run everything -you will notice the sharp character -that is the command prompt of -a root -user on unix -Passwd command -when root invokes the passwd command it -doesn't ask for the existing password -as -root can override can override that -just type in the new password and verify -it -in linux we install packages and -dependencies are resolved automatically -the underlying system in centos is rpm -which stands for red hat package manager -centos is a fork of red hat -yum takes that to the next level by -connecting to servers and searching for -the rpm packages -and all the dependencies -and then -fetching everything you need to install -wget is a tool that can perform http -requests to download a file from command -line -since we don't have a -display opening a browser isn't really -practical and this is reasonably -convenient -jdk hack -this is a bit of a dirty trick -if you try to download the jdk you will -notice there is no way to get the -download link -oracle hides it until you check a box -which means you need to set -a cookie to get it -that's really annoying and the only way -i found to download the jdk directly to -the box -was this hack -the alternative which i've used in the -past is to download the jdk onto my mac -and then use the scp command to copy it -to the server -that's really slow as i need to download -and then upload the file -scp is secure copy and it allows copying -files over ssh -i'll discuss it a bit later -yum jdk -you remember i said yum fetches things -automatically -so you would expect to do something like -yum jdk and have the jdk installed -technically that would work and might -even work here -all linux distributions ship with -openjdk -which is the open source version of java -it's pretty similar but not identical to -the oracle jdk -that we had to download -one of the main reasons i did that this -was support for javafx -which doesn't exist in the jdk -we need it -for -the css compiler -and i wanted it to work -so i'm still using yum instead of rpm -directly to install a package -using yum is important as it can update -its installed database and resolve -dependencies diff --git a/docs/website/video-transcripts/PbRbzCGQCQE.json b/docs/website/video-transcripts/PbRbzCGQCQE.json deleted file mode 100644 index 874d25b7c9..0000000000 --- a/docs/website/video-transcripts/PbRbzCGQCQE.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 151, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/035-building-a-desktop-version-of-the-kitchen-sink.md", - "status": "transcript-fetched", - "word_count": 881, - "youtube_id": "PbRbzCGQCQE" -} diff --git a/docs/website/video-transcripts/PbRbzCGQCQE.txt b/docs/website/video-transcripts/PbRbzCGQCQE.txt deleted file mode 100644 index 7dbb66b01a..0000000000 --- a/docs/website/video-transcripts/PbRbzCGQCQE.txt +++ /dev/null @@ -1,151 +0,0 @@ -so we ran the kitchen sink as a mobile -app using the simulator -and that's great -before going to android and ios let's -try running the app as a desktop app -this will be a relatively simple -exercise that can help us understand the -more nuanced android and ios processors -as i mentioned before -we don't have a real main class we only -have the bootstrap codename one main -class -which we can't run as a standalone app -so we will need to create a new main -class -which i will place under -a directory called stubs -the term sub in codename one -is -one we use internally to refer to the -ios bootstrap glue code -in this case i place the stub under -stubs slash desktop slash src com -codename one demos kitchen -there is a bit of code here in the stub -so i'm going to go over the various -lines -it's basically a swing app that embeds -codename one so you can enhance the stub -with everything available under java se -i included import statements because -they refer to classes in java sc -and that might be confusing in the stub -it makes sense to import the -implementation code since this code has -no portability to other platforms so i'm -importing the javasc port directly -for simplicity i hard-coded the app -dimensions but it's pretty easy to -create more elaborate heuristics and -window positioning using swing -if your theme doesn't derive from native -this doesn't matter however if it does i -made it use the ios 7 theme which is the -default ios theme -this will make the app feel a bit more -like an ipad app -the javasc port is used for the -simulator too -so things like network monitor and other -simulator features might still be on if -this is a developer machine -so here we disable all of those flags -this is the location for the storage -file under the user home directory -in the simulator this is always.cn1 -but it makes more sense to use your app -name for real world apps -the simulator hides the full file system -and shows a fake one under the dot -cn1.hierarchy -this disables that behavior so getroots -will return the file systems in your -machine -the simulator does that by default to -make it feel more like a real device -when running on a desktop you would -prefer showing a tablet ui rather than a -phone ui -this means that his tablet and his -desktop would both return true -swing is also single threaded -this invokes the run method -on the swing thread so we can construct -the swing ui -we create a swing jframe and then -initialize codename one with the content -pane of that frame -these are display properties we rely on -in code -they aren't essential but it's good -practice to have them -the frame size and position is sized and -positioned -we bind a window listener for the app -close event -we make sure to call stop and destroy on -the main class before exiting -notice we invoke these methods on the -codename one thread -we go into the codename one thread to -invoke start and init once those are -done we call the method again on the -swing thread and show the frame itself -i took a bit of a shortcut by reusing -the same runnable instance for both -callbacks -now that this is out of the way -we are nearly done -we just need to compile that code and -link it with the javasc port code since -we already have an ant build xml file -this is probably the easiest way to do -that -i added a new target to the build.xml -file that looks like this -i want to make sure -that we are working with a clean slate -when running this build -there are two important things to notice -in this statement i don't use the boot -class path since the app is now a -standard javasc app -the classpath points at javasc.jar -because i'm compiling against an -implementation -not an api -notice that the stubs slash desktop -directory is added to the compilation -process so that would get bundled too -we copy the theme from the codename one -repo so the native theme will look like -ios -the final step is the executable jar -generation -for a jar to be executable we need the -main class in the manifest which i -define here -we need three things in the jar -first we need the concept contents of -javasc port which is already available -in the cn1 project notice i exclude the -skin file from there as we don't need -the simulator and will just increase the -jar size -the next thing we need are the compiled -sources of the kitchen sink project and -kitchen sink stub -and finally we need the resources -defined within the kitchen sink project -so i package them directly from the src -directory making sure to exclude the -java source files -after going through all of this i now -have a desktop app that i can run -using this -notice that this is a self-contained fat -jar -with no external dependencies which -means it should work well for tools such -as java packager etc diff --git a/docs/website/video-transcripts/Q0fkApAGMZA.json b/docs/website/video-transcripts/Q0fkApAGMZA.json deleted file mode 100644 index 3ec70196e1..0000000000 --- a/docs/website/video-transcripts/Q0fkApAGMZA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 125, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 697, - "youtube_id": "Q0fkApAGMZA" -} diff --git a/docs/website/video-transcripts/Q0fkApAGMZA.txt b/docs/website/video-transcripts/Q0fkApAGMZA.txt deleted file mode 100644 index 470159f3f8..0000000000 --- a/docs/website/video-transcripts/Q0fkApAGMZA.txt +++ /dev/null @@ -1,125 +0,0 @@ -we'll move on to some additional forms -starting with the billing form -the billing form like the details form -is a relatively simple cookie cutter -form -today i might have made it with the -instant ui -and would have been done with it -but when i wrote this code we didn't -have that class yet -despite the fact that the ui looks -simple the layout is non-trivial -first we have a border layout that -encloses the form -we use that to keep the green button at -the bottom of the form regardless of -size or scrolling -next we have the content container which -is a box y container that includes the -whole user interface except for the -button -we mark it as y scrollable -further down in the code so we can -scroll through elements within its -within -it's important that text fields always -reside in a y scrollable container -so the virtual keyboard will have space -to work with -and finally we have the table -the numeric values are too small to give -them each a line so i packed them in a -2x2 table -the entire table is added to the content -later on -notice that the table container isn't -scrollable as nesting scrollable -scrollables is a -mobile -bad idea or bad practice -we don't have scroll bars so when we -swipe the implementation needs to guess -which scrollable we actually mean -and that might not end well -as i mentioned before -this code predated the binding api -and didn't use it -so we have some helper code such as the -adds as double method which -automatically parses the text value and -if it's not a double returns the -existing value -this allows us to automatically update -the property -binding would have made this code -simpler and shorter -next we add the elements to the table -i'm not really sure why i chose to do a -2x2 table with box layout instead of a -2x4 layout -so we can place everything directly into -the table -possibly it was force of habit -both approaches work though -the separator line allows us to -differentiate between the brain three -arguments and the rest of the billing -details -i've mentioned this before but it bears -repeating a separator hair is just a -component with one pixel padding and a -grayish color -normally a label without text is hidden -by default -but if we invoke -set show even if blank -it will be rendered even if there is no -text or icon -there isn't all that much here in the -final part of the billing form -it's just the things i've mentioned -above like the scrollable y of the -content pane and its placement -in the center -now let's look at this at sending this -data to the server side -up until now when we sent data it didn't -include all the information we needed so -we created a new version of the app -settings call with the restaurant -details this allows us to still reuse -the existing object from the restaurant -model -this is a bit of a hack so -let's get go over what i did here -in the app settings class i get the -value of the object as a map object i -then add all the properties from the -restaurant object to create one large -json object -containing everything i need in the -server which doesn't have a -distinction between the two -then i'll remove the cart and menu -entries these entries are big and i -don't need them for the specific api as -they have their own model -i also remove the logo -and title background images as -they have separate web service -you might recall from before -that basic things like -title tagline etc weren't persisted -anyway well -now we have a bind to preferences method -that just implicitly saves and restores -from preferences -that way restaurant data is implicitly -saved locally in preferences -it's not sqlite but it works and saves -some of the effort today after the -improvements we have in the sql map -i might have used that -or i might have used the seamless -preferences persistence we have built in diff --git a/docs/website/video-transcripts/QY7josfVbdg.json b/docs/website/video-transcripts/QY7josfVbdg.json deleted file mode 100644 index 04d6a6a658..0000000000 --- a/docs/website/video-transcripts/QY7josfVbdg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 246, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1403, - "youtube_id": "QY7josfVbdg" -} diff --git a/docs/website/video-transcripts/QY7josfVbdg.txt b/docs/website/video-transcripts/QY7josfVbdg.txt deleted file mode 100644 index 406c9c0ee7..0000000000 --- a/docs/website/video-transcripts/QY7josfVbdg.txt +++ /dev/null @@ -1,246 +0,0 @@ -the entities section was long -boilerplate heavy and a bit boring -so while the server is never as -interesting as the client -the services layer is still far more -interesting -services encapsulate the generic -business logic of the application -this is the part of the code that -includes the intelligence of the -application -the entity layer is about storage only -the web service layer is about -connection to the client and this layer -is about things the app needs to do -we'll start with the obvious service -user service it encapsulates all the -things a user will need -let's start with the fields of this -class first -we include the user repository shadow -user repository and media repository -as we manipulate these data stores -directly -for notifications we work with the -service instead of -writing to storage notifications can be -sent via push later and using this -approach can make that seamless -the password encoder is used to hash the -password values in the database -we'll store property values here so we -can keep api keys outside of the source -code -the rest of the code has many methods -i'll break them down one by one -the login method returns an instance of -the user object after validating the -user it accepts an email or phone as -identification and the password for -authorization -we query either by email or by phone -either way it should result in a user -list -assuming a user was found in the query -we verify there is only one this is just -a to be sure scenario -we use the encoder tool to check the -password -since the passwords are hashed in the -database this is the only way assuming -the password and hash match we return -the login dao which should also include -the token -for the other cases we throw the login -exception this obviously raises the -question of the login exception it's -just the simple exception subclass we -just used this exception to mark the -type of error for the web service -up is probably the most challenging -method in this class if only because of -all the external dependencies it brings -to the table -let's go over the method and then go -into the dependencies -as we create a user we want to see if we -have shadow user listings so we can -suggest friends to him immediately -if the user is registering by email we -try to find that email -and fail with the right exception -assuming all is well we search the -shadow user list for the email -we do the exact same thing with a user -that logs in with a phone -notice that only one of the two should -have a value -since a user can be updated via sign up -or update the logic for updating from -the dao object is common in set props -which i'll cover soon -we hash the password into the database -this will generate a large random -looking secure sorted hash string notice -that password and token are updated -manually outside of set props -the auth token is generated here right -now i keep it simple but a secure app -will also invalidate the token -occasionally -verification code is saved to the -database here we'll discuss this method -soon -if we have shadow user entries we build -up a potential friend list -the new user object is stored into the -database -the email or sms for the activation code -is sent out right now right here -notice we return the login dial instance -and not the dao that was a lot but there -is a lot more -the methods set props create -verification code send activation email -and send activation sms were mentioned -in the code let's go over them -first in line is setprops it serves as a -generic setter that accepts a dow object -and copies its values into the user -entity -this might have made sense in the user -class -i placed it here because it's the only -place i need this method -notice that only basic features are -updated nothing secure or risky set -props might be invoked in a case of an -update and we don't want to accidentally -change a value that we shouldn't so the -code is trivial to -prevent accidental modification of -something important -next we have create verification code -which generates the random verification -code -we accept the number of digits to -generate -a random digit between 0 and 9 is -generated for every length and appended -to the strength -to the string -this is trivial so we can move to the -more interesting methods -this method uses the twillow web service -to send an sms message to a given number -we need the api keys to invoke the api -i'll go into those soon -technically we can do this just once but -for convenience i stuck it here -this is the api call to twillow's sdk it -sends the sms from the given phone -number to the device with the given text -the api key for twillow and mailgun are -stored in a file on disk so we don't -have to include them in the code this -method loads that file and caches it -we lazily initialize the variables we -saw earlier -we load a file called -fbclonekeys.properties into the api keys -this file should be in the user's home -directory -since this is pretty basic i'll convert -the exception to runtime exception as i -don't want to deal with checked -exceptions here -the next step is filling up this -properties file -we sign up to twillow.com and -mailgun.com to get send credentials for -the properties file i won't go too deep -into the whole process as it's a -standard signup process and the -respective documentation support will -probably serve you better -this is the properties file containing -the attributes we need from twillow and -milgan -all of these values should be available -from their website once you sign up -notice that the values in the slide are -random -and -won't work for you -the last remaining piece is the email -activation option -we could use a tool like -javax.mail which is available in spring -but that might be a problem getting an -email into people's inboxes is far -harder than just the basic mail api -luckily there are several companies that -specialize in email delivery -deliverability ranging from sendgrid mel -gun and even aws -i've explained working with sendgrid in -the past so i thought i'd give milgan a -try -pretty much all of these tools are the -same you need to sign up and configure -your domain that's the hardest part and -it's pretty hairy -i suggest following the instructions on -the site all sites includes instruction -for working with java and all of them -work out of the box with spring boot -this code was literally copied from the -mail gun introduction tutorial -the one change is the properties object -pointing at the api keys -we return a result from the mail gun -servers but for now we don't really use -it at at this time -now that this is done signup will -automatically send an email or an sms -based on the data submitted in the user -signup -when we make a change that might impact -the user object such as uploading -contacts etc -we would like to update the locally -cached user object on the device -that's exactly the purpose of refresh -user -you might think -why not log in again -that's simple we don't cache the -password as that would be insecure -but we can refresh the user object with -a token which is probably more secure -the method itself is trivial we just -find the user and return the login dial -which includes all the extra private -details -after signup is complete the user needs -to verify that he got the right -activation code this is done with the -verify method -verification succeeded -and the value is in the database matches -the code -verification can only be used once -otherwise a user can change the phone -email -and reuse the same code without -validating -if verification failed we need to notify -the client-side code -update is another trivial method we -reuse this code so it's common between -update and sign up -updating the password is optional -if it's null -we don't want to override the existing -password diff --git a/docs/website/video-transcripts/Qw7moLv-dE4.json b/docs/website/video-transcripts/Qw7moLv-dE4.json deleted file mode 100644 index 71650a5d06..0000000000 --- a/docs/website/video-transcripts/Qw7moLv-dE4.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 152, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 801, - "youtube_id": "Qw7moLv-dE4" -} diff --git a/docs/website/video-transcripts/Qw7moLv-dE4.txt b/docs/website/video-transcripts/Qw7moLv-dE4.txt deleted file mode 100644 index 08e2cdff67..0000000000 --- a/docs/website/video-transcripts/Qw7moLv-dE4.txt +++ /dev/null @@ -1,152 +0,0 @@ -in this module we will persist data -locally -so we don't have to go to the server to -fetch data every time -we'll use sqlite -not because it's the best storage medium -but because it's something i want to -teach -i'll use the automated properties -binding api -to map objects to sqlite -which makes working with sqlite far -easier if i didn't have that api i might -have just skipped over sqlite altogether -as working with sql -is unpleasant and error prone -in order to get the mapping right -i had to move some properties to getter -and setters -as at the time i originally did this -code -there was no option to exclude an entry -from storage -this is no longer the case and we can -now mark a property for exclusion -but changing that would mean changing -all the following modules and that's not -necessarily something i want to do right -now -i used a local abstraction -for all the persistence code to make it -more domain specific -which is a good practice in general -app storage abstracts the process of -saving the objects -so -right now it's implemented using sql map -but in theory we could replace that -let's review this class -the class is implemented as a singleton -and they initialized on first use -initialization creates the db instance -that is kept within the application -we create the sql mapping instance -to the database -notice -that the sqlite api doesn't have -separate methods for create or open -and does the former implicitly which is -pretty convenient -next we create the tables if they don't -exist -we create blank object instances -for the objects that we need -then define the primary key field -i also set the column type for price to -be double -this is no longer necessary as newer -versions of the sql map api detect the -double property and automatically set -the right type -create table -literally issues a create statement -notice that we don't support alter at -this time -although this might be introduced in the -future -insert and update are pretty much direct -wrappers to the built-in methods that -handle the exceptions locally instead of -propagating the i o exception -from the sqlite api -the reason for the delete listener -is that we want the dish list to -automatically remove the dish -when it's deleted by the edit form -this allows us to decouple that code and -get an event from the underlying data -model -we use the event dispatcher class to -subscribe and fire an event here -this is a really convenient class when -you are building an api -delete serves pretty much the same -purpose as update and insert with the -exception that it also fires the -dispatch event appropriately -app settings is a special object -as there is only one of it so when we -issue a select query -we should never expect more than one -entry -however it's still a select query -by default when we invoke the select -method -if a property value isn't null it's -added to the where clause -both name and tagline default to -non-null values -and so we need to set them to null -once that's done -we can just do the select query as usual -if there are no entries we just invoke -insert -for a new settings object -fetching the list of dishes is similar -but more standard as we return the full -list of dishes -the main point of interest is the use of -the name property to indicate the order -by option -for the select statement -some dishes might not have an image -within them -as we create them -and here we force a placeholder image to -prevent such a case -i would recommend that you always use -that strategy and have a good looking -placeholder image -now that we have the basic persistence -object in place -let's proceed to integration -but first let's discuss a conceptual -issue -why do we need persistence to begin with -technically -we shouldn't have a local database as -all the data is in the server and local -data is meaningless -in theory we could enable working -offline but that's not a current goal -and would create many complexities i -want to avoid -there is only one goal for this code and -its performance -users and developers often conflate -performance with something that has to -do with bits and bytes -but it's really about decisions we make -as developers -do we go to the server or do we use -local data do we cache -in this case it can create -a conflict between the local and remote -database -we need to guard against that obviously -but once -data duplication exists -we open up a risk -this risk is worth it for this level of -performance diff --git a/docs/website/video-transcripts/RBYGdCllnww.json b/docs/website/video-transcripts/RBYGdCllnww.json deleted file mode 100644 index 8f2e7078ea..0000000000 --- a/docs/website/video-transcripts/RBYGdCllnww.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 61, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 322, - "youtube_id": "RBYGdCllnww" -} diff --git a/docs/website/video-transcripts/RBYGdCllnww.txt b/docs/website/video-transcripts/RBYGdCllnww.txt deleted file mode 100644 index 69901b2782..0000000000 --- a/docs/website/video-transcripts/RBYGdCllnww.txt +++ /dev/null @@ -1,61 +0,0 @@ -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/video-transcripts/Rat-FDqDCTU.json b/docs/website/video-transcripts/Rat-FDqDCTU.json deleted file mode 100644 index fbd1425229..0000000000 --- a/docs/website/video-transcripts/Rat-FDqDCTU.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 103, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 624, - "youtube_id": "Rat-FDqDCTU" -} diff --git a/docs/website/video-transcripts/Rat-FDqDCTU.txt b/docs/website/video-transcripts/Rat-FDqDCTU.txt deleted file mode 100644 index 70ab8eefc0..0000000000 --- a/docs/website/video-transcripts/Rat-FDqDCTU.txt +++ /dev/null @@ -1,103 +0,0 @@ -Facebook has a splash screen which takes -a short time to load the login UI I'm -assuming Facebook tries to figure out as -much as possible about the device and -its owner before showing the login UI -since we don't have such capabilities -which I personally find disturbing we -might not need a splash screen at all -we can go directly to the login UI -however this isn't ideal and we should -show a splash screen the first time the -app is launched -iOS applications take a while to load -they feel much faster because they show -a splash screen when they first load -this means the image of the UI appears -almost instantly and is replaced by the -actual UI once the loading finished -to the use of this seems seamless in a -well-crafted app -codename 1 runs the application on the -Mac Cloud servers 16 times as of this -writing to produce 16 separate -screenshots matching the various device -configurations -some device configurations include both -landscape and portrait mode for instance -iPads high resolution phones -Etc -as a result the numbers add up to 16 -separate screenshots -notice that this is subject to change as -we are moving toward an automatically -generated native splash screen in iOS -but since that isn't there yet at this -time this is still relevant -for a typical application that doesn't -require login this isn't a problem it -can show the UI of the first form -for instance in the case of Facebook we -show the feed without any data -this would give a sense of loading data -which would pop in once loading finishes -unfortunately Facebook's login UI looks -very different from its feed UI -so the first activation would seem odd -if the feed UI was replaced with the -login UI -in that case we'd rather have a splash -screen a pair for the screenshot process -since the splash screen is pretty much -identical in the native iOS slash -Android implementation -there isn't much we need to do in terms -of design -first we need to add a new UI controller -class that will handle the UI navigation -and hide the underlying forms -here we can implement the splash screen -and later the navigation to additional -forms -this can be static as it happens once -and we can control it -using absolute Center the logo will be -in the middle of the screen for all -devices slash resolutions -we defined this UI ID earlier it uses -the Facebook icon font that means that -the f308 character will render as the -Facebook logo -this is important for the morph -transition we use that to animate to the -first form -when departing this form we will move -the component named logo in this form -to assume the position slash appearance -of the logo component in the destination -form -the duration of the transition is 1200 -milliseconds -this animation controls an effect where -the logo appears translucent and -gradually becomes opaque -every 20 milliseconds we update the -opacity of the logo until we finish -which should take 1 000 milliseconds -this will create an effect where the -logo fades in and then moves from the -center of the screen to the top or side -in landscape mode -when we are done we show the login form -which I currently implemented as a stub -or we now need -is one additional CSS entry to support -this -we set the background color to the blue -of the splash screen -we now need to bind this to the main UI -so we need one change to the main class -Facebook clone I replace the default -High World form with this call -pressing play should result in a splash -screen fading in diff --git a/docs/website/video-transcripts/Rok0Ubr4xes.json b/docs/website/video-transcripts/Rok0Ubr4xes.json deleted file mode 100644 index 8099d0a06d..0000000000 --- a/docs/website/video-transcripts/Rok0Ubr4xes.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 116, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 693, - "youtube_id": "Rok0Ubr4xes" -} diff --git a/docs/website/video-transcripts/Rok0Ubr4xes.txt b/docs/website/video-transcripts/Rok0Ubr4xes.txt deleted file mode 100644 index 43d7b92f0d..0000000000 --- a/docs/website/video-transcripts/Rok0Ubr4xes.txt +++ /dev/null @@ -1,116 +0,0 @@ -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/video-transcripts/RsVOanGYjqo.json b/docs/website/video-transcripts/RsVOanGYjqo.json deleted file mode 100644 index c076ac19dd..0000000000 --- a/docs/website/video-transcripts/RsVOanGYjqo.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 156, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/037-build-an-ios-native-version-of-the-kitchen-sink.md", - "status": "transcript-fetched", - "word_count": 845, - "youtube_id": "RsVOanGYjqo" -} diff --git a/docs/website/video-transcripts/RsVOanGYjqo.txt b/docs/website/video-transcripts/RsVOanGYjqo.txt deleted file mode 100644 index b6eaec2660..0000000000 --- a/docs/website/video-transcripts/RsVOanGYjqo.txt +++ /dev/null @@ -1,156 +0,0 @@ -the ios app is surprisingly easy when -compared to the android app -there are some nuances but overall the -process is pretty simple -first we need to compile the vm and port -so we need to go back to the cn1 -directory and execute these commands -we also need retro lambda which we can -compile from -github.com -orf jackal retro lambda or download the -binary from -oss sonotype dot org slash contacts -groups slash public slash net slash off -jackal slash retro lambda slash retro -lambda -i won't go into the compilation process -of retro lambda as i just downloaded the -binary -i created a new kitchen sync ios folder -and placed the binary there -you can run retro lambda with the -following command -once this command is complete a version -of kitchen sync classes targeting jdk 5 -resides under the kitchen sync classes -directory -i won't go into details about this -command as i think you would be better -served by the documentation in the retro -lambda project -the next step is to create and compile -the step class -which we will use to launch the -implementation -in the kitchen sync ios folder i created -a step directory i then added -the kitchensync stub dot java to that -directory -the one interesting thing in the stub is -the fact that the implementation invokes -the runnable callback when it's ready -everything else is -really trivial -this can be compiled using this command -notice that i used source -1.5 level when compiling this is crucial -as the vm doesn't support java 8 -directly -we use retro lambda to add support for -java 8 language features but don't do -that on the stub -once we do this we can create the output -folder -xcode proj and run the vm translation -code -there is a lot to digest in this command -so i'll break it down to one argument at -a time -this should be pretty easy we are -running the vm translator it's just an -executable jar -this represents the vm target os this is -currently the only supported option -in the future we might support -additional targets for the vm -next we have the path bash understands -this as multiple commands so we have to -put the entire path in quotes -these are the individual elements within -the first element is -the in the path is the stubs directory -where i compiled the kitchen sink stub -before -the second element is the classes of the -vm -this covers the java core classes -supported by codename one -these are the actual classes of the java -app after they went through the retro -lambda process -these are the classes of the ios port -implementation -these are the core implementation -classes of codename one itself -these are objective c sources that -implement the native code referenced -from the ios port -this is the target output directory we -created above -this will include the output project -this is the name of the stub class we -created -this is the package name of the step -class and the application itself -these are the title and version numbers -of the application -this is the target type ios indicates a -universal ipad iphone app but you can -specify iphone or ipad here -and finally these are the included -native libraries -frameworks codename one requires these -two by default -now that this is all done we have an -xcode project under xcode proj -dist -we can double click the project to -launch xcode this instantly opens a -ready to run file but first we need to -fix a few things -the first step is picking the main -project area -and select build settings then click -architectures entry and select standard -architectures -the next step is font registration -we need two fonts -one is handly which is used in the demo -the second is the material design font -which is used for common icons in -codename one -to do this we need to select the build -settings tab -and in it add the attributes for fonts -by clicking the small plus button next -to one of the attributes -in ios you need to declare ttf files -included in the project and can't use -them otherwise the build servers do that -automatically -you should be able to type fonts -provided by application and xcode -will offer that string to you as you -type -once you select that you should have one -entry called item0 and you should be -able to add another one -type in the font file names -for the project specifically -materialdesignfont.ttf -and handleyregular -dot ttf -notice that these are case sensitive -notice that this step is currently -necessary but it might be unnecessary in -the future -we need to update the two sib files in -the application -and select the deployment target as 8. -notice that xcode sometimes fails to -accept the selection -and i personally had to restart it twice -now that all of this is done you should -be able to press play in xcode and run -the codename one application diff --git a/docs/website/video-transcripts/Rt68rA2c6A8.json b/docs/website/video-transcripts/Rt68rA2c6A8.json deleted file mode 100644 index 41e6f455f8..0000000000 --- a/docs/website/video-transcripts/Rt68rA2c6A8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 40, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 227, - "youtube_id": "Rt68rA2c6A8" -} diff --git a/docs/website/video-transcripts/Rt68rA2c6A8.txt b/docs/website/video-transcripts/Rt68rA2c6A8.txt deleted file mode 100644 index ab5f93f308..0000000000 --- a/docs/website/video-transcripts/Rt68rA2c6A8.txt +++ /dev/null @@ -1,40 +0,0 @@ -there is still one small missing piece -for search -what happens when we click one of the -search results -when a person is clicked we want to show -his posts -when a post is clicked we want to show -the post itself -the user form ui is relatively spartan -when compared to facebook's ui -i didn't want to go too deep into the -full title area ui description and -everything else because those are -relatively trivial technically yet time -consuming -instead i chose to focus on the -functionality of showing the ui -this is the user shown in the form which -we found in the search results -all this is standard paging boilerplate -like we had in the other infinite -container instances -we request the posts of the specific -user with his user id -which we already implemented in the -server -this method should be made static -so we can use it here -this is trivial to do -so i won't go into that code -the infinite container is placed in the -center and this is pretty much the -entire functional ui -the same should be done for the post -search code but it's much simpler we -don't even need to save the post -instance since there isn't much here -this is pretty simple but it should work -fine and function correctly -with that searches fully working diff --git a/docs/website/video-transcripts/S1SHZLXQxSI.json b/docs/website/video-transcripts/S1SHZLXQxSI.json deleted file mode 100644 index c0215d4b3e..0000000000 --- a/docs/website/video-transcripts/S1SHZLXQxSI.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 148, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 863, - "youtube_id": "S1SHZLXQxSI" -} diff --git a/docs/website/video-transcripts/S1SHZLXQxSI.txt b/docs/website/video-transcripts/S1SHZLXQxSI.txt deleted file mode 100644 index 18f39e6acc..0000000000 --- a/docs/website/video-transcripts/S1SHZLXQxSI.txt +++ /dev/null @@ -1,148 +0,0 @@ -the native uber app is a bit weird when -it comes to account editing -when you want to edit any entry within -the account you click on it and it sends -you to a different form where you can -edit save -i'm assuming there is some logic in that -flow as it makes it harder to edit -details by mistake -but it's not exactly intuitive -i chose to just use text fields and map -them automatically to the user object -with the binding api -this makes everything really simple -one caveat is that i didn't want to send -user details for every minor change and -only wanted to send that detail -when going out of the form to save on -bandwidth noise -i needed some additional changes to make -the functionality work -but let's start with edit account form -itself -when we click on the avatar button in -this form we can open the camera to -capture a new picture -and set that as our avatar notice that i -set the image to be 512 pixels wide -the -1 argument -indicates i want to maintain aspect -ratio for the height -we need this code as the server limits -the size of files it can receive -you can customize that in spring boot -settings -but it's not a good idea to overdo the -size as it could be an attack vector -i'll discuss the two set avatar methods -soon -the edit tag -is the small circle on top of the image -indicating that it can be clicked -it i used a label and styled it in code -i could have done all of this in the -designer tour as well the padding makes -the circle bigger and the margin -position positions it in the right -position on top of the image -to actually position it on top -i use the layered layout where i place -the image below -and enclose the edit label in a flow -layout to push it down -i use the ui binding code to bind the -user object to the text fields -this will automatically update the -preferences whenever the user types in a -text field as the properties of the user -object will change -notice that the text fields get their -values automatically from the user -object too as the binding works both -ways -this is a bit of a hack -but it works great -i only want to save a user if the data -within the user changed so i generate a -tostring value of the user object which -i can later compare to the current value -if the values differ then the user -object was changed -i only want to save data when leaving -this form -and currently there is only one way out -back -i can detect the return to the previous -form and save the data to the server -there -it's important to unbind the object -since user is a global object and if we -keep the binding it can cause a memory -leak -the user object will reference the -binding which references the text fields -the text fields include the entire form -hierarchy indirectly -here we check that the user was modified -as i explained before -and sent the data to the server -asynchronously -that was a lot of code but it wasn't -that complicated relatively to the level -of functionality it delivered -let's start with the easiest missing -piece edit user -on the server we have a single method -for add slash edit user named ad add -edit user -it's mapped to slash user slash add -but it does edit to -we already have an add new user method -in the client but that method is -synchronous -and we don't want a blocking call that's -why we need this version that works -asynchronously -we have two separate methods for saving -the avatar the first one is in common -code it saves the avatar image to the -local storage -this is the exact masking code we have -in get avatar -it might have made sense to extract it -to a common method but the gist of it is -simple -we need to mask the image -we mask and set the image -to the variable instance that is used -later by get avatar -i return this value so it can be updated -into the ui instantly -notice that this is all entirely local -to my phone -so we need to update the user account in -the spring boot server -again this relies on a method that we -added a while back to the spring booth -server -multi-part request is a standard for -file upload and it's used when you -submit an html form tag over http with a -file within it -it takes care of base64 encoding -and related complexities -almost seamlessly -i need the token to write data -notice i encoded it in the url instead -of as an argument -adding arguments to multiple requests is -a bit challenging in spring boot -and i thought this was easier -since capture returned a file name -passing it -is literally the simplest approach at -this point -and that's it -avatar upload and download should -now work diff --git a/docs/website/video-transcripts/S5pAGkV4h4w.json b/docs/website/video-transcripts/S5pAGkV4h4w.json deleted file mode 100644 index b8741d9c21..0000000000 --- a/docs/website/video-transcripts/S5pAGkV4h4w.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 341, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1914, - "youtube_id": "S5pAGkV4h4w" -} diff --git a/docs/website/video-transcripts/S5pAGkV4h4w.txt b/docs/website/video-transcripts/S5pAGkV4h4w.txt deleted file mode 100644 index 9f3d43e375..0000000000 --- a/docs/website/video-transcripts/S5pAGkV4h4w.txt +++ /dev/null @@ -1,341 +0,0 @@ -i almost always start apps by building a -mock-up of the front end first -we are driven by visual feedback -the ability to see and interact with the -app clarifies the problem space and -motivates you to move forward back-end -and business logic become easier as you -can associate them with requirements -from the gui -one of the benefits of having markup -code is that it's easy to experiment -with the markup -for example -we can comment code in out -and see how it impacts everything move -your items around to judge impact etc -before we jump into the implementation -of the markup -we first need to understand what we are -building -and study the reference -in this case the reference is the -facebook app -so we'll start by deconstructing it -i'll begin with the login process -which is the first thing you see when -you install the facebook app -there are a few things i'd like to -discuss about the login process -deep versus flat also known as shallow -ui structures -forms with no functionality -plain text password used in the ui -delayed verification process -device orientation support in facebook -phone and tablet ui -and finally the adherence to platform -conventions in ios versus android -i'll go all -over all of these one by one -but first let's take a brief bird's eye -view of the sign in ui -this is the sign-in ui in the native -facebook app on android -don't panic i'll go over all the steps -i will also simplify it a lot because -this is a mess as you can tell -this is what i came up with -as you can see it's much simpler but -includes a lot of the features we saw -before -i'll go over all of this soon but first -let's get back to our agenda -there are two approaches we can take for -user input -with deep we ask simple questions in -each form and dig deep through the forms -until we have all the information -with flat we show one form with all the -information we need -the previous graphic showed a deep -approach whereas the edit details form -on the right takes the flat approach -facebook clearly picked deep for the -signup process and then did so for -various other concepts but within the -app it often picked up the flat approach -for instance for editing user -preferences -why was facebook inconsistent and why -pick one approach or the next -there are several reasons to pick a deep -it's intuitive -even those who have aversion to -technology can understand the flow of -the ui -it's portable -more white space on the screen allows -the ui to resize and adapt a to a wide -range of display sizes -it goes through a process as -humans we are used to this approach -so -if deep is so great why doesn't facebook -use it everywhere -because it has some limits -the biggest is expressiveness it's very -hard to communicate complex data and -ideas like we see above -where gender has complex options or a -person might not want to advertise their -birthday -annoyance is also an important limit -going through a seven step wizard to -change a single detail -is annoying to most users -picking deep or shallow approaches is a -matter for a ux expert -as developers are concerned as code -views -how do we express two distinct ui -paradigms and map to the same data model -since the facebook ui is so darn simple -i'd like to automate a lot of it and one -of the core goals is automating this -logic -let's go back to the flow graphic -we looked at before -the splash screen is shown when we start -this makes sense for platforms like ios -but it's there on android 2. -the logo blinks a bit -when we move to the login form it lets -you input the email or phone -notice a few interesting things about -this form -it doesn't use the material design style -of labels on top of the field -when elements gain focus -but it does try to comply with some -android paradigms for instance the -buttons use uppercase text -which is the convention on android -the create account ui -is -one form that literally has no purpose -when i looked over the ui i was -perplexed by this -i'm guessing there is some ux reason for -this form but i have no idea -i didn't include anything like that in -the clone and there is no equivalent of -this in the ios version version -of the native facebook app -before i discuss the name form notice a -few interesting things about this wizard -the word next is spelled in the style of -ios with case sensitive text instead of -uppercase -also notice that here the first and last -name fields use material design -convention for placement -and when you select them the hint -animates above the field unlike the -first form of the application -i think this inconsistency is due to -different teams building different parts -it contributes to an app that feels -unrefined in some regards -this is a long wizard with quite a few -stages -and notes -there are small inconsistencies with the -ui for birthday and the gender prompt -doesn't look great -during verification we can either enter -either a phone number -of an -or an email in the case of the latter we -can click the sign up with email address -link at the bottom of the form which -sends us to the email entry form -the email entry form is practically -identical to the phone number form -and you can toggle back -the final stage is where interesting -things happen typing a password on a -mobile phone is one of my biggest pet -peeves -there is literally no reason for doing -that -facebook solved that badly by making the -password visible by default -their old ui would show a regular -password field and offer to replace it -with a plain text field if you got the -password wrong -the current approach is problematic and -shows the lacks approach to security at -facebook -i will still clone that approach even -though i don't like it because it's -common practice -while we are on the subject of -verification facebook doesn't verify -during sign up -i could have given a completely fake -email -and the process would continue until the -end -facebook nags after the fact to make -sure the email phone -is correct but you can complete sign up -with a wrong email or phone -i'm assuming this relates to the -challenge of delivery to email or phones -on a global scale i won't do that though -although you can probably adapt the code -to copy that behavior -facebook supports device orientation -changes everywhere it often achieves -this ui by removing content -surprisingly this isn't consistent for -other apps from facebook for instance -instagram is locked to portrait -orientation -at first when i looked at how facebook -implemented landscape support my first -thought was lazy -to be fair a lot of apps lock -orientation change all together for -instance instagram uber etc -so landscape support in facebook is -refreshing -it still looks bad -it might be related to the universal -nature of facebook -the app should work everywhere and might -be under constraints we can't imagine -but i find that hard to believe -it just looks like something they didn't -care enough about to invest in -this makes some sense i doubt most users -rotate their device during the signup -process signing up on an ipad is -radically different to the signup -process in android -it's clear that there are distinct -groups developing the application the -entire ui is shown within a dialog that -updates with the progression of the -wizard -let's zoom in a bit on the stages -facebook clearly has two separate themes -for ios slash android the apps are -inspired by some common design language -but take very different approaches for -some basic ui features -the android version uses some aspects of -material design and the ios version -looks -somewhat like an ios app but neither one -stands out as an example of great design -in the given native platform -so what's the point -you will notice that the flow of the -stages in sign up is different between -ios and android terms are in the -beginning instead of the end and there -are no redundant forms -notice i magnified the ui so it will be -more visible as the ipad resolution is -so big it would make the entire text -unreadable -the stages are pretty much the same -conceptual stages as we saw in the -android app -but slightly different -again the password is plain text -which shows that this is a clear policy -decision -the gender question is probably the most -telling -in the ios version it's expressed as a -toggle button whereas an android that's -expressed as a radio button the former -is clearly superior but it's available -in ios only -the app itself doesn't use the ipad form -factor effectively -for the most part it feels like a -blown-up phone version of the app -which is stunning for an app like this -it does show a search ui on the side -when running in landscape mode but -that's pretty much it -how would this ui adapt better to tablet -form factor -the feed is about posts -right now they are just blown up to the -size to a size that doesn't help if i -use an ipad it's not for seeing larger -posts -we could fit two or three columns of -posts in the feed and significantly -increase information density -there are some other considerations for -facebook such as ad placement which -might be impacted -this ui choice is also reflected in the -web version of facebook so i'm guessing -it's a -conscious -choice -there is a lot to say about the main ui -of facebook -let's start by reviewing the android -version of the ui so we are all roughly -on the same page -i trimmed a lot of the hidden nuance and -details from this chart -after looking at this i developed -several insights into the facebook ui -but one of those was specifically -interesting -they removed the hamburger back in the -day facebook was one of the first apps -to feature the hamburger side menu -this is a ui element signified by three -horizontal lines -one on top of the other -hamburger it allows you to open or swipe -in a drawer with options -this menu is now a standard in android -however facebook chose to discard it in -favor of swipeable tabs at the top of -android and on the bottom in ios -on a personal level i'm conflicted about -this -it's cool that facebook goes against the -trend especially -since it's a trend they helped start -tabs have -the user experience advantage of -visibility -which is great -however they have three major -disadvantages -first they take up extra stream screen -space -second they show only an icon -new users might be confused about what -these icon icons mean -third it's step backwards tab based uis -were popular in 2009 -despite these reservations i'll copy -their style in the ui we built a lot of -demos with side menus so i'll use this -opportunity to build something different -i'll come back to this diagram later -when we go over the implementation diff --git a/docs/website/video-transcripts/SBqYZSdIdec.json b/docs/website/video-transcripts/SBqYZSdIdec.json deleted file mode 100644 index ed763a1355..0000000000 --- a/docs/website/video-transcripts/SBqYZSdIdec.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 118, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 679, - "youtube_id": "SBqYZSdIdec" -} diff --git a/docs/website/video-transcripts/SBqYZSdIdec.txt b/docs/website/video-transcripts/SBqYZSdIdec.txt deleted file mode 100644 index 1ee4e3c12d..0000000000 --- a/docs/website/video-transcripts/SBqYZSdIdec.txt +++ /dev/null @@ -1,118 +0,0 @@ -push is one of the more challenging -tasks on mobile the first time around -it's an uphill battle because it's just -so darn confusing and alien -however once you understand the -underlying logic and platform oddities -it becomes simple -we'll start with -gcm by going to developers.google.com -mobile slash ad -where we need to be logged in with our -google account -here start by clicking pick a platform -for which we have selected 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 google's cloud -and 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 -we can either share or not share service -data that's up to you -and we can proceed to the final stage of -creation of the app -which also entails some -waiting -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 codename1 settings -application and go to the build hints -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 -the registration is done -we'll still need some of these values -and the code but we finish the -configuration portion of 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 hire -within the preferences ui -otherwise you won't be prompted for push -details -once we log in to the apple developer -account -we can move to the next step -we pick or add devices i tend to 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 dialogue -but if you already have a certificate -and it's working for you -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 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 check box that we must -activate if this check box isn't here -then you aren't logged in with a pro -account -once we finish and press save the -certificate should be generated we -should also receive an email with the -instructions and url explaining how we -should integrate push the email should -contain urls in the cloud for two push -certificates which are also generated to -your local file system under ios certs -these urls are important when we need to -send the push message diff --git a/docs/website/video-transcripts/T8l7k2OeGpo.json b/docs/website/video-transcripts/T8l7k2OeGpo.json deleted file mode 100644 index 999fe7e73a..0000000000 --- a/docs/website/video-transcripts/T8l7k2OeGpo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 75, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 407, - "youtube_id": "T8l7k2OeGpo" -} diff --git a/docs/website/video-transcripts/T8l7k2OeGpo.txt b/docs/website/video-transcripts/T8l7k2OeGpo.txt deleted file mode 100644 index 4cfca4aa17..0000000000 --- a/docs/website/video-transcripts/T8l7k2OeGpo.txt +++ /dev/null @@ -1,75 +0,0 @@ -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/video-transcripts/TOWhbEhiRe4.json b/docs/website/video-transcripts/TOWhbEhiRe4.json deleted file mode 100644 index eb6957f484..0000000000 --- a/docs/website/video-transcripts/TOWhbEhiRe4.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 100, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 545, - "youtube_id": "TOWhbEhiRe4" -} diff --git a/docs/website/video-transcripts/TOWhbEhiRe4.txt b/docs/website/video-transcripts/TOWhbEhiRe4.txt deleted file mode 100644 index b202e8c954..0000000000 --- a/docs/website/video-transcripts/TOWhbEhiRe4.txt +++ /dev/null @@ -1,100 +0,0 @@ -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/video-transcripts/TRF76P1Dwwc.json b/docs/website/video-transcripts/TRF76P1Dwwc.json deleted file mode 100644 index ea1eb7b7fd..0000000000 --- a/docs/website/video-transcripts/TRF76P1Dwwc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 105, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 615, - "youtube_id": "TRF76P1Dwwc" -} diff --git a/docs/website/video-transcripts/TRF76P1Dwwc.txt b/docs/website/video-transcripts/TRF76P1Dwwc.txt deleted file mode 100644 index 5e8cb33af8..0000000000 --- a/docs/website/video-transcripts/TRF76P1Dwwc.txt +++ /dev/null @@ -1,105 +0,0 @@ -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/video-transcripts/U0Ms65vcVr4.json b/docs/website/video-transcripts/U0Ms65vcVr4.json deleted file mode 100644 index 46f30d087e..0000000000 --- a/docs/website/video-transcripts/U0Ms65vcVr4.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 145, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 800, - "youtube_id": "U0Ms65vcVr4" -} diff --git a/docs/website/video-transcripts/U0Ms65vcVr4.txt b/docs/website/video-transcripts/U0Ms65vcVr4.txt deleted file mode 100644 index 91f8d32603..0000000000 --- a/docs/website/video-transcripts/U0Ms65vcVr4.txt +++ /dev/null @@ -1,145 +0,0 @@ -we are now ready for the new post form -changes -up until now the changes were simple but -in this class we have a bit of work to -do -let's start with the top level changes -currently we support one attachment this -field represents the attachment id -this is the attachment mime type if -applicable -this is the button for sending a post -we need to disable this button until the -attachment is uploaded -the constructor is now private as the -class is instantiated via factory -methods -also notice we don't invoke -inner ui in the constructor anymore -the first big change is the init ui -method which up until now created the -same ui and was bound in the constructor -this will no longer work as the ui with -an attachment will be slightly different -this code is still there it just moved -to the factory method -here -the post styles code happened at the end -of the internet ui -originally but due to logistics we need -to tear -this allows the image and video versions -to avoid the styles bar at the bottom of -the ui -notice that those don't exist in the -native facebook app either -which brings us back to the init ui -method -this is the button for posting from the -toolbar -we can later disable this button -we can find the button -for toolbar commands -to get low level control -this code will behave exactly the same -as it did before if post styles had a -value and we'll just add the post to the -center if it doesn't -those are somewhat nuanced changes that -expose components -we can work with -let's see how the salau's image uploads -in the main method -this is a factory method -for an image post -star bar -in the bottom isn't included -we set the mime type here but notice we -don't know the media id yet -so the attachment value isn't set -the image will take up half the screen -height -this is better than using an api like -set preferred size because it will still -work if the device is rotated -here we upload the media -to the server and return a slider -component to track the upload progress -we place the image -on the bottom of the form -and the slider progress on top of it -further down -before we go to the logical next step in -the upload method i'd like to point out -a big missing piece -delete -if we close this post now the image -would still be in the servers and no one -would know -that's a flaw i left in place due to -time constraints -there are several potential fixes -probably the best one would be a -back-end server process that deletes -media -that has no references -we can also add a delete web service but -that wouldn't work perfectly for cases -of app crashes etc -i'll come back to the video post soon -for now i'll skip ahead to the upload -method -if we're uploading a media file -the post button should be disabled until -we are done -once upload finished -we enable the post button -notice we save the attachment value to -which we will need later -you can bind a connection request to a -slider using this utility class built -into codename one -every stream and codename one is -observable which means we can get update -events on any i o operation seamlessly -utilities like slider bridge take this -to the next logical step by binding the -progress event from network manager to a -slider with this -let's go directly to the changes -in post to support attachments -this code -had to move outside of the if statement -even though it hadn't changed -if we have an attachment -we add it to the post object -after this line the rest of the code is -identical -with that -image posting should work although we -still need to update the callers to use -the factory methods instead of the -constructor -we have one final change to new post -form -the create video post -most of this looks like the create image -post method with -minor changes such as mp4 instead of -jpeg etc -we can't -override the performed size of the video -component but we can replace it in the -container where we do override the size -we need -we that container anyway so we can place -a program progress indicator on top of -that to show the upload progress -this shows a frame of the video one -second into playback -notice i invoke play and only then try -seeking without doing that the behavior -is undefined for some features such as -well seek -with that the new post form is done -we just need to wire it in diff --git a/docs/website/video-transcripts/UU6HCbenVAA.json b/docs/website/video-transcripts/UU6HCbenVAA.json deleted file mode 100644 index 55f71ca9c6..0000000000 --- a/docs/website/video-transcripts/UU6HCbenVAA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 153, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 893, - "youtube_id": "UU6HCbenVAA" -} diff --git a/docs/website/video-transcripts/UU6HCbenVAA.txt b/docs/website/video-transcripts/UU6HCbenVAA.txt deleted file mode 100644 index 5741ce1a30..0000000000 --- a/docs/website/video-transcripts/UU6HCbenVAA.txt +++ /dev/null @@ -1,153 +0,0 @@ -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/video-transcripts/UhLFHJj8qnM.json b/docs/website/video-transcripts/UhLFHJj8qnM.json deleted file mode 100644 index 0d34e6a3e9..0000000000 --- a/docs/website/video-transcripts/UhLFHJj8qnM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 143, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 820, - "youtube_id": "UhLFHJj8qnM" -} diff --git a/docs/website/video-transcripts/UhLFHJj8qnM.txt b/docs/website/video-transcripts/UhLFHJj8qnM.txt deleted file mode 100644 index 712289e0ee..0000000000 --- a/docs/website/video-transcripts/UhLFHJj8qnM.txt +++ /dev/null @@ -1,143 +0,0 @@ -we need to make a decision on the ui -design -should we use the ios design the android -design or both -android and ios differ greatly in the -login process for facebook -i'm assuming the reason for this is -apple's review process -that places restrictions on what -facebook can do -i think starting with terms and -conditions makes more sense -you get that out of the way and then -proceed directly to use information -other than that for the most part i -chose to go with a design that looks -more like the android design -for the signup wizard -before i go into the code let's look at -the first form in the signup wizard so -we understand what we are up against -we need to adapt the title area to -landscape mode -we need to have different back command -behavior for ios we need a rich text -component to provide multi-line text -with links -the first two are common to all the -forms -to facilitate that i decided to create a -generic form that will handle all the -stages of the sign up -sign up form -i'll discuss that shortly -but first let's discuss the rich text -support -a very common request is support for -rich text and codename one -this is hard to do in a generic -performant cross-platform way -but can be done easily for simple tasks -like this -notice that for a lot of use cases i -would just use browser component -here that won't be ideal because i want -deep integration with the ui and deep -control -since we already have an xml parser -that's perfectly capable of passing html -i decided to use a very simple html -based syntax -i also added support for bold and italic -while demonstrating that simple things -like line breaks still work -let's go over the code step by step -this is a container as we pass we add -elements into it -this is the html text we set -it's useful for the get text method -font size is hard coded in millimeters -for simplicity -the event dispatcher lets us broadcast -link click -events as action event -the following variables are used by the -parser to store temporary pass estate -as it builds the ui -these are the fonts and constants we -will use when the parser asks us to -create a component -the constructors delegate to the init -method which in turn initializes the -default fonts -notice that the default layout is flow -layout -we rely on that to allow aligning the -text to the center or right -this method is invoked by the parser to -add a component -if the string has a space we split it to -multiple separate string strings so each -individual component can break a line in -the flow -layout we add either a label or a button -that looks like a label -the parser updates current link so if we -have a link -we are within an a tag and need to use a -button style to look like a link -current link value is the content of -ahref -we just fire the listener event -dispatcher with the ahref content as the -source -if this isn't a link it's a label -we remove the spaces -padding and margin -we then use the width of the space to -re-add a space in the form of padding -this allows line breaks on word -boundaries -the parser might need to remove a space -for some special cases for instance -if we close an a tag and place a dot -right next to it -the set text method -is the api that sets the html to this -component -we wrap the html so it's well formed and -pass the text using the parser inner -clause -these add listeners so when a user -clicks on a link these listeners will -receive the event -from the event dispatcher -the next stage is the parser in a class -it's a bit big -we derive xml parser and override -callback methods to tokenize the string -all the methods here are callbacks in a -sac star parser -we also support a dom style but i chose -a callback approach for performance -if the block element starts with a space -we add a space to the last block element -we remove the space from the previous -component if the block doesn't end with -a space -here we handle the individual html tags -we want to support by changing the -current font or color -when a tag ends we clear the current -font color and restore the default we -don't handle complexity like tag nesting -etc -when we're in an a tag we save the value -of the href attribute for use in the -event handler code -that's it -we now have a poor man's html renderer -that we can use for simple blocks of -highlighted text -we'll make use of it soon diff --git a/docs/website/video-transcripts/UwdI_tMqYgU.json b/docs/website/video-transcripts/UwdI_tMqYgU.json deleted file mode 100644 index 55b4dc782c..0000000000 --- a/docs/website/video-transcripts/UwdI_tMqYgU.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 88, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 479, - "youtube_id": "UwdI_tMqYgU" -} diff --git a/docs/website/video-transcripts/UwdI_tMqYgU.txt b/docs/website/video-transcripts/UwdI_tMqYgU.txt deleted file mode 100644 index fba421a231..0000000000 --- a/docs/website/video-transcripts/UwdI_tMqYgU.txt +++ /dev/null @@ -1,88 +0,0 @@ -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/video-transcripts/UxATbrfWveU.json b/docs/website/video-transcripts/UxATbrfWveU.json deleted file mode 100644 index 6396f3a50d..0000000000 --- a/docs/website/video-transcripts/UxATbrfWveU.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 117, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 739, - "youtube_id": "UxATbrfWveU" -} diff --git a/docs/website/video-transcripts/UxATbrfWveU.txt b/docs/website/video-transcripts/UxATbrfWveU.txt deleted file mode 100644 index 2f1dcf2a9e..0000000000 --- a/docs/website/video-transcripts/UxATbrfWveU.txt +++ /dev/null @@ -1,117 +0,0 @@ -we are finally down to the last service -and nearly finished the server -the post service handles everything -related to posting commenting and liking -it's not as complex as user service but -it still has a lot of logic and -dependencies -this is a common constant that's used in -the code we use a similar constant in -the client side -we reference multiple repositories here -notice the news feed and comments have -no public facing service class so their -data is encapsulated here -again we send the notifications via the -notification service and not by writing -to the repo -this method returns the posts for the -given user -if that user is us or a friend it shows -everything if the user is a stranger it -will only find the public posts -we need the ought user user so we can -check if the requested user is a friend -if the user is a friend or it's my own -posts then we look for all the posts -sorted by date -otherwise we only want the public posts -but with the same sort order of date -once we have the data we can just add -the whole thing into a list and return -that this is pretty simple despite the -verbosity it allows us to implement -client-side ui such as clicking on a -user to see his posts -newsfeed isn't just the list of posts -for user it includes friends mixed in -and can be organized in a unique way -that's why we separated it to a special -entity in the storage -most of the work in the news feed is -done when we insert it into it so there -is not not much left to do at this point -that's a good thing as this method -should be fast -the query does most of the work -we sort the results based on three -criteria day means all the posts from -today -will come before posts from yesterday no -matter the rank -rank lets us float the best posts posts -to the top and timestamp sorts within -that -we build dow responses like we did -before -the next method is the actual post -method that adds a new entry to the news -feeds posts -this is all pretty standard stuff we -create a new post entity and fill it up -based on the dao we then save this post -to the database -since the news feed is a separate entity -we now take the new post and add it -there -when we go over all our friends and add -the post to their feed -we return the id of the newly posted -article -this leads us to the implementation of -add posts to news feed which performs -the addition to the news feed entity -this is again pretty trivial we set the -values and save the news feed entry -this is the day since epoch it allows us -to differentiate easily between posts -made today and those made yesterday -we can probably do this using jpaquer -ajpa query but sometimes saving the data -we need is simpler -we can place a comment into a post using -the comment method -a user can only post a comment on a post -if it's public or by a friend -we need to protect that -we save the comment then add it into the -post and save that -we send out a notification of the new -comment to the user whose post this is -i oversimplified again a notification -would go out to people watching the post -and to other commenters as well but for -the sake of simplicity i left those out -currently i limited like to posts only -it should be something that we can apply -to comments and other features it should -also be pretty easy to create the more -nuanced like -emotions -that facebook introduced a couple of -years ago -i wanted to keep things simple so i vote -avoided those -i also didn't provide a way to unlike -which in this case is taking back a -previous like operation -not a negative like -this should be pretty easy to implement -once you have like working so i didn't -bother going there -we just add the user to the list of -likes if he isn't in the list already -we send a notification that the post was -liked to the author -with that we finally finished the -service layer entirely diff --git a/docs/website/video-transcripts/UxZ1HeheGwU.json b/docs/website/video-transcripts/UxZ1HeheGwU.json deleted file mode 100644 index 0b2c0d1b07..0000000000 --- a/docs/website/video-transcripts/UxZ1HeheGwU.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 335, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/001-working-with-css.md", - "status": "transcript-fetched", - "word_count": 1768, - "youtube_id": "UxZ1HeheGwU" -} diff --git a/docs/website/video-transcripts/UxZ1HeheGwU.txt b/docs/website/video-transcripts/UxZ1HeheGwU.txt deleted file mode 100644 index d89153edd6..0000000000 --- a/docs/website/video-transcripts/UxZ1HeheGwU.txt +++ /dev/null @@ -1,335 +0,0 @@ -the css plugin is one of the more -elaborate plugins for codename one -and while it was developed by steve -hanna -who is a codename one employee he -developed it sort of as a separate -stand-alone personal hobby project -and it stayed that way -until this moment specifically -mostly because it's a very very -complex idea -and we're still not exactly sure -how well it will fit into codename one -it's something that we're evaluating -whether to -bring it into codename one maybe to -build something a bit different but so -far it looks like it's gaining -some traction and might become an -official part of codename one moving -forward -but -at this moment no decision has been made -regardless it's still a very powerful -tool and because it's based on the -existing codename one capabilities -it should be supported forever -in terms of the functionality -the workings with working with css is -very familiar uh for better and for -worse i personally am not a big fan of -css because of its -uh -overly obtuse syntax but a lot of people -are used to it -and are familiar with it -and that can be good it can also be -sometimes bad because for codename one -we need a lot of custom attributes which -aren't necessarily things that you can -just use from by copying and pasting -from the internet and that's another -problem with css you'd expect to be able -to copy and paste css from the internet -which isn't exactly something you can -fully do -with uh this plugin only -to a partial extent -so -to install the plugin just go to -the url mentioned here -the npm installation process is the -easiest -if you have npm if not it might be more -practical to just install it manually -and -maybe even base your installation on -the project that i provided here with a -test that includes the source code of -everything just copying the -the -the jars and everything -the -the usage is also -explained in that website -at the github project -but i'll give you a couple of examples -so you will have a good context to begin -with css -so a very basic hello world css would be -just creating a button -and in this case you'll notice what i -was referring to when i said custom code -in one attribute notice the cn1 pill -border is a special case attribute and -there's a lot of documentation with a -lot of these and the website -it's essentially the round border from -codename one -only the cn1 round border is the round -version and the pill border is the -version that's -defined as rectangle in codename one -rounded with a -rectangle -and -here i define obviously the background -color foreground color those are -identical and notice the special case -for the font family which is native main -thin which is uh the native thin fonts -that are supported built-in barcode name -one -so -that's again another special case in css -here but ultimately this is actually -really really nice and it hides a lot of -complexity -because the button -will be applied to the pressed and -selected mode -and -will essentially just work as you pretty -much expect -the only other change i need to make to -the code is change the high world to use -a button instead of using a label by -default -and once i do that -i get this which is what i would expect -to happen and it worked the first time i -tried it which is really really really -impressive -uh in terms of this particular plug-in -it works really well and if you're -comfortable with css -it's a very interesting approach -so -what exactly happened here because you -know -i defined the theme do we still have a -theme how does it exactly work -if you understand the concepts of -codename one and the theme -res file -well -i'll try to explain it in a bit more -detail -so the css support uses a preprocessor -that parses the css and generates a -theme.res file actually -theme.css.js file by default technically -you can name it anything if you name -your css file x dot css it will call it -x.css.res -but one of the things that you need to -do is define -layering -and layering -essentially places -the -the base theme dot res and -places the css theme on top of that -and that means the -when our inner code loads it loads the -theme.s file initially and sets it as -the theme for the application -and -within that theme -we have a theme constant i'm not -sure if i don't think i discussed uh the -concept of theme content constants -before but there's essentially a tab -within the designer where you can -when you can see constants that you can -define to define all sorts of behaviors -in codename one and one of them is an -overlay where you can say okay in this -theme that this is just the base -after you finish loading this theme load -these other themes -and -essentially the theme.s file uh at least -when you use the npm install -uh adds another uh -overlay -theme -and points to the css uh file creator -and that way it's essentially -loaded automatically -for you and you get the css in place the -nice thing about that is that you can -still use the designer if you're more -comfortable doing some things in the -designer and that's just changing -stuff there in the theme the default -theme and then go to the css and do the -things that you're more comfortable -doing there so you have essentially two -theme two res files and uh -because of the way it's built the -the css one will take precedence because -it comes -after the -the base theme so -if there's a conflict there css always -takes it in this case -you can flip that as well but that would -require a bit of code -and -that's generally it -now -two things i i can go on about css and -the selectors there's this remarkably -huge list of attributes that steve -implemented there and obviously going -even over a fraction of them it includes -support for nine piece borders and other -capabilities of code name one but i -don't want to dig into all of that -because it's a bottomless pit of -of -subjects -the -two things i would like to touch is -images and basic selectors so -uh -i'm giving her two examples of how you -can customize various behaviors -and one of those is the button pressed -i can just do that and essentially this -is a bit of a darker color now the next -image won't show that because it shows -the regular button but -if you press it it would have the darker -color and that's all i need i don't need -to define all the other things i don't -need to define the border again i don't -need to define anything -it will just -work and that's -that's pretty nice in its own right -there's no need to do any extra work or -anything like that just works with the -darker color -and -by the way part of that is also one of -the nice things about that is that it -uses the round border in the past uh the -css plugin had support for round borders -but it used an image -and that was inferior in some regards -because it would deteriorate with the -various resolutions -in this case the round border sort of -solves that problem and allows us to -create very -uh compelling -user interface elements -and -all i just have another selector here -for form -and in the type place an image reference -now the nice thing about it is that i -can i created up directory images under -the css directory and placed duke.png -there which is a standard image of duke -but -one of the nice things about that is -that you can put any url you can put a -url off the internet literally an http -or well -and -it will work it will -get in there -and -the next thing that might not be -intuitive is the source dpi value -normally an image would just be included -but once we include that attribute it -automatically assumes that the image is -a multi-image and it will import it as a -multi-image and automatically scale it -to the various resolutions -as needed -and that's really really nice -because it allows you to add multi -images with essentially no real work -there's other ways that the css plugin -allows you to import multi images -including by define including all of the -various resolutions and all sorts of -other approaches -it's really nice -and -in this case i gave a 260 ppi uh -resolution or dpi resolution -which -essentially is close to very high in -terms of the resolutions -so the image would be scaled and that -that indicates the size of the source -image so if the resolution is very hard -we'll just use the source image as is if -it's trying to create a high -resolution image it will scale it down -by a specific factor to that determines -the difference between high and very -high -um -i can go into the semantics but -generally -if an image matches uh it was designed -for instance for an iphone -uh -what is it um iphone -5 -so it has a -dp uh dpi of -400 i think or 360 then you define it -you define that version that size of the -dpi here and the scaling -algorithm will automatically scale it to -the right resolutions -the don't -catch me on my word about the dpr of -iphones i don't remember my -uh by heart at -1am -anyway -the -background type uh essentially says we -want to align the image to the bottom -right and that's again a codename one -style -uh -specific style in css you can do -anything you can tile you can all of the -things that you can do in the designer -you can do to the css here and that's -really nice and this essentially places -duke at the bottom right corner of the -form -like this -and i can't press the high world here -because it's an image but it would -change the color based on that -so -i hope this -brief -presentation helped explain css -i think there's huge potential there -and -i hope you enjoy working with it -thank you diff --git a/docs/website/video-transcripts/VCNFKMeud-w.json b/docs/website/video-transcripts/VCNFKMeud-w.json deleted file mode 100644 index 064430b60e..0000000000 --- a/docs/website/video-transcripts/VCNFKMeud-w.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 117, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 621, - "youtube_id": "VCNFKMeud-w" -} diff --git a/docs/website/video-transcripts/VCNFKMeud-w.txt b/docs/website/video-transcripts/VCNFKMeud-w.txt deleted file mode 100644 index 7aa2cb56ae..0000000000 --- a/docs/website/video-transcripts/VCNFKMeud-w.txt +++ /dev/null @@ -1,117 +0,0 @@ -in this section we'll cover a lot of -minor features mostly related to user -interface -that are part of the missing pieces on -the road to complete the app -let's start with the categories that we -can see at the top of the restaurant -application -when we fetch a dish we can check if a -category is already added and if not -we can just add it into the list of -categories -this list is used later on when we build -the list model for the categories -in the dish edit form we can provide an -autocomplete text field -which allows us to type in new -categories or select an existing -category -in that same form when a user presses ok -we check if the category already exists -and if not -we add it to the category list -this makes the whole process automatic -and effectively removes the edit add -delete user interface -for the categories -that simplifies our work by removing -forms -we would have built -and also simplifies the user experience -laziness pays off -here we can see the server code for the -categories -and you will notice that it is handled -as part of the dish update -there is no separate web service for -categories further simplifying this -logic -we implicitly create a category if it's -missing -and save it to the mysql database -we have a separate call to call the -category which will go into -now -the -category call -just hops -just loops over the dishes to see if a -category is unused -if it is missing the method deletes that -category -the delete method also calls the -category -since the deleted dish -might be the last one using a specific -category so we need the call there too -back in the client we use the validator -api to place a constraint -on the category so we'll always have a -category for a dish -we also set a numeric constraint on the -price -notice that a validator is essential -even if you set the text field as a -numeric text field the definition only -indicates the type of virtual keyboard -used but doesn't enforce input since -mobile os's don't do that -next we'll handle prices on dishes -as you can see in this code -we can just add a price label with the -right price badge ui id -we use a property listener to -automatically update the price -as it changes in the underlying object -we align the price to the right using -the flow layout if we'd have used just -text alignment the purple background -would have stretched through the entire -width of the space -the price badge ui id has most of the -settings you would expect for -color alignment padding -and even a bit of -translucency the thing that pushes it -from the top is a four millimeter margin -on the top side which places it at just -the right place -next we'll address the details form -which is mostly pretty trivial -let's go over it step by step -i've enclosed common code for text field -and label creation -in the add text and label method -this ended up being reducing only a -small part of the clutter and wasn't as -worthwhile as i had initially -hoped -address colors and other elements are -represented by a button -which will navigate to a deeper editor -form -this is important as it allows us to -keep the user interface in this form -relatively simple -without too much clutter -i considered using something like an -accordion ui -but using navigation was -simpler -as i mentioned before -the add text field and label -is relatively simple -it sets -the hint and just adds the components -into place diff --git a/docs/website/video-transcripts/VDyltmk_Hu8.json b/docs/website/video-transcripts/VDyltmk_Hu8.json deleted file mode 100644 index d9966f24d2..0000000000 --- a/docs/website/video-transcripts/VDyltmk_Hu8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 58, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 328, - "youtube_id": "VDyltmk_Hu8" -} diff --git a/docs/website/video-transcripts/VDyltmk_Hu8.txt b/docs/website/video-transcripts/VDyltmk_Hu8.txt deleted file mode 100644 index b3ce9a4080..0000000000 --- a/docs/website/video-transcripts/VDyltmk_Hu8.txt +++ /dev/null @@ -1,58 +0,0 @@ -push is fragile and can be blocked by -the user -in order for the app to work properly we -need to properly handle the situation -where push isn't available -and we can do that with a fallback -implementation -i have two such implementations i will -start with the http based implementation -the async build code -needs a place where it can deliver the -message sent back to the client -the restaurant entity is pretty -convenient for that -we can store the result from the build -process -in the last result entry within the -restaurant entity -you might recall the build app service -class where we send the build async call -using a post method -we can just return the response by using -a get method to fetch the entity -database transactions take care of the -rest -and all we need to do now is pull from -the client for this update -in the client side the build app method -sends a build to the server -here we have a polling fallback -code to see how the connection is -working -if we have a push key -we don't need polling as push will take -care of this but if we don't have it -then polling needs to kick in -the polling code is pretty simple -let's review what we have here -first we have a request to to the build -app url again -this time however we pass false instead -of true to the second argument -that means we are making a get request -instead of a post request -we wait for the response and check if -it's valid -then schedule a timer that will keep -polling as long as the value hasn't -changed -once that's done we cancel the time -and return the value with on build -result this isn't ideal -and normally we try to avoid http -polling -check out a better approach demonstrated -in the websocket version -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/VnYaxvVn6OA.json b/docs/website/video-transcripts/VnYaxvVn6OA.json deleted file mode 100644 index a17b4c293c..0000000000 --- a/docs/website/video-transcripts/VnYaxvVn6OA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 49, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 257, - "youtube_id": "VnYaxvVn6OA" -} diff --git a/docs/website/video-transcripts/VnYaxvVn6OA.txt b/docs/website/video-transcripts/VnYaxvVn6OA.txt deleted file mode 100644 index d2931de1e4..0000000000 --- a/docs/website/video-transcripts/VnYaxvVn6OA.txt +++ /dev/null @@ -1,49 +0,0 @@ -a server is incomplete without the -client -let's go back to the client side and see -how to proceed from there -the builder class allows us to build a -new restaurant app -but the first thing we need is to obtain -a secret matching this device -in order to do that we have an api -called create restaurant if necessary -if we already have the secret it will -just return the secret and we can move -on -if we are already in the process of -requesting the secret it will wait for -that and if not it will actually connect -to the web service -to get a new secret -the connection to the web service code -is pretty standard -put operation which works asynchronously -without interrupting the user -notice the response is invoked in post -response to keep things on the edt -we use create restaurant if necessary -like this -which -effectively uses the closure code to get -the reference to the secret -it isn't as convenient as synchronous -code -but it still allows us to send a -connection request call -i've designed the client api and the ui -so -they don't get in the way -we send a request and trust it to work -if it fails we'll undo the change -after or retry -but the idea is to -not bother the user with details of -network reliability or -synchronicity -the api on the client and the server is -designed to use the network manager -queue -effectively -thanks for watching -i hope you found this informative diff --git a/docs/website/video-transcripts/WT4cFceBWRA.json b/docs/website/video-transcripts/WT4cFceBWRA.json deleted file mode 100644 index b6b7763436..0000000000 --- a/docs/website/video-transcripts/WT4cFceBWRA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 581, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 3705, - "youtube_id": "WT4cFceBWRA" -} diff --git a/docs/website/video-transcripts/WT4cFceBWRA.txt b/docs/website/video-transcripts/WT4cFceBWRA.txt deleted file mode 100644 index d4150c6852..0000000000 --- a/docs/website/video-transcripts/WT4cFceBWRA.txt +++ /dev/null @@ -1,581 +0,0 @@ -in this session I'll talk to you about -adapting a UI design -specifically from PSD a Photoshop file -and it's going to be a bit challenging -Adapting a UI Design -technically -but really cool because we're going to -do something as amazing as the image you -see on your right here -and this is the original that's the -Photoshop design that's not what we'll -end up with we'll end up with something -that looks very similar although not -pixel perfect because I'm not aiming at -that -I'm aiming at something that looks -uh more device like -and I'm not going to copy the fonts or -the icons I'm going to use material -icons because they're available -and I don't want to go into that if I -wanted to do it Pixel Perfect I could -but you know getting 95 percent is much -easier than getting that last 99 percent -and every bit you add beyond that -complicates things further so I want to -keep this relatively simple it's still -going to be a bit difficult -this design is something I got from that -envisionapp.com do URL you see at the -bottom here now check it out over there -you can also download uh the Photoshop -file the specific Photoshop file took it -from that includes several designs I -picked just one specific screen to -make things simple for the specific demo -Approach -so let's look a bit deeper -this specific image was first break it -down into the images that we need and -the first steps that we need to do is to -cut out -pieces out of the design that we'll need -to use -in this specific design there there -isn't that much to cut out because it's -a relatively simple design and which is -what I like and also because a lot of -these things are standard so we can use -the standard elements like I said before -we won't need the icons in this case if -we were building something that's Pixel -Perfect I'd also cut them out -but we won't be using them -we'll just use standard icons that we -have -and we'll need the background image -we'll cut it out and we'll put it in the -title area of course -we'll use the standard Fab a floating -action button from codename one -and it's uh -and we'll just configure the colors to -look like that that's the only change -that we need because it's already -implemented in codename one and will -just work -we will need another single image so -that makes two images in total that -we'll need from that photoshop file and -that is the image pointed at here the -green line at the left including the -selection itself -and we'll cut these as a nine piece -border I'll discuss non-piece borders a -bit later in case you're not familiar -with that concept but generally it just -allows us to -scale uh design or specific types of -designs like this one in a way that's -very efficient -and doesn't degrade the image quality in -any way -so they're pretty useful for things like -like this in terms of design elements -and those are the only two images that -we'll need -now as I said we'll ignore the icons for -the check boxes as well so I'll use the -standard material checkbox which looks -slightly different than this but very -close obviously the magnifier and the -back look a bit different as well -uh still it will end up looking really -nice in my not so humble opinion in this -particular case -so in the first part we'll open -Photoshop -now I don't know if you have a Photoshop -license or not you would probably want -to have Photoshop although I provide the -cut images if you don't have it I think -it's definitely worth to have it uh you -can do most of these things with -gameport are the three tools but as a -fan of from the Past who eventually -took the plunge into Photoshop -it's much easier when you're working -with a designer that produces a -Photoshop file similarly sketch the same -thing although I have less experience -with that so I don't want to get into -that at the moment -so this is the PSD it includes a lot of -images as you can see I'm zooming in and -I'll pick the right one back yeah -so this is it I'll just try and crop it -so we'll just have that because I don't -care about everything else -and I want to cut out the relevant image -files the the shop at the top and -without all the text and the Fab and -everything else just the image of the -sandwich -just the same sandwich that's that's -good enough as it is actually does look -good -uh and the trick to cutting it is I'm -converting it to a Smart object that's -an option Photoshop here you need to -click on on the right area there and it -converts then double click that and it -opens it and that's it that's the image -and that's really convenient now notice -I'm using export -to export as a JPEG and reducing the -quality a bit and that removes all sorts -of metadata that photoshop might include -inside there which is really annoying -and took me ages to find out so instead -of doing save as like most people do -like I used to do do export big tip huge -difference in terms of file size -and the reason I'm using JPEG and and -saving it is because we'll use the image -as is and we won't use all sorts of um -uh how should I put it uh -uh multi images or anything we'll use -the image just as is because it's it -fits and that since we'll scale it -anywhere on the device and it will work -in that way better it's at all keep the -size of the resulting app smaller now -I'm hiding the elements the text and the -check box and now I can just select the -area that matters -and I'll do a copy merged -now copy merged is uh copies just the -visible pixels unlike a regular copy -Ctrl C or command C won't work in -Photoshop you need copy merged and then -when I create new it automatically sees -the size of the clipboard and uses that -so I don't even need to set the size -that's pretty great ux there -so I just type in the name although I -didn't need to and I can just paste it -in and that's the image that I need and -I can just save it in this case it's -okay to save although I I could do an -export there as well it doesn't matter -though because I'm going to cut it into -an eyepiece border -and code name one will regenerate the -image anyway so so the way I save it -doesn't really matter in terms of -metadata it's discarded anyway -so that's Photoshop and these are just -the two images we need now other things -that we'll do here is picking colors and -things like that I won't show them to -you it's just using the the Color Picker -tool and things like that -I'll just type in the the values for the -specific colors when we'll get there -uh and let's skip right ahead to the -interesting part which is the code -FAB Location -so the one of the important things in -the code and I want to talk about that a -bit more in depth is the floating action -button -now the area at the top that you see -here that's a toolbar -and I could cheat by essentially not -having one of the tricks that I did in -the past is just not having a title at -all and then drawing the entire area on -the top using just components that I add -into that area and that would work and -that would work great and I could I have -shipping apps that work like that but I -want to use the toolbar at least to some -degree -uh but I'm not using it in the same way -as most apps use the toolbar and the -reason is the specific Floating Action -button -so it resides in the area between the -toolbar and the content -now uh The Floating Action button is -positioned in a special area in codename -one called -the layout pane -now the layout pane is a layer that -resides on top of the content pane which -is the content pane is where all of the -content of the form the whole body -resides -and the layered pane resides on top of -that visually so whatever you put in the -layout pane will appear on top of -whatever's in the form -for the title bar or the toolbar in in -that sense they're separate and the -reason we decided to put the layered -pane on top of the content Pane and not -on top of everything is partially -historic and in retrospect retrospect -possibly wrong -although it does have advantages it does -have the advantage of the common use -case like things like validation things -like that are mostly relevant for the -content pane -but uh in retrospect it would have been -useful to have the ability to place -things also on top of the toolbar -but we don't have that other than the -gloss pane -and specifically before I digress too -much the layered pain doesn't have that -so we literally can't -put a component like that between the -toolbar and the form -so there's a trick -and the trick is that the toolbar can be -drawn in essentially on top of the -content -and the use case for that is in some -cases content needs to slide below the -toolbar for various effects including -this specific effect that's exactly one -of the use cases that we built this -feature for -it's kind of a hidden feature you need -to construct the toolbar with a true -Constructor and you need to install it -manually instead of the global toolbar -settings so we'll use that in code and -we'll show you -how to -disable the global toolbar which you can -disable temporarily or permanently -whichever or just not set it to true -in the init method and then just install -a -the type of toolbar that you need -the thing is when you have an overlay -toolbar of this type -the content will hide underneath that -toolbar -so we'll need a special component to -push the content downwards so so the the -portion with the image so it won't hide -the Apple the the -what is the tab bananas and everything -that we need to buy so it won't hide -that uh we'll add another invisible -component that's exactly the size of -that image -and it's going to reside below that -image and that will push the rest of the -things downwards and that sort of -uh solves that problem and The Floating -Action button can just be positioned -with because the toolbar is now part of -the parent form and -it all works as expected -and that's why some of the things that -I'm doing in terms of the code will look -uh how shall we put it weird -there's another reason and I'm keeping -that to the end of the specific design -choices I've made I'm -made the code -not necessarily in the standard way and -I'm keeping that as a secret to to the -finishing slides -CheckBoxes vs. Toggle Buttons -so the next thing I want to talk about -is uh the check boxes now you can -customize checkboxes and codename one to -look exactly like that you can do that -I don't like you it's um -it's possible but the moment you start -customizing checkboxes you do that in a -global API because there's lots of -assumptions inside the checkbox code -that expected to behave in a particular -way and this is a really really really -simple thing -so the thing is in codename one we have -special modes for checkbox and radio -buttons that allow them to render -without the actual check sign now that -doesn't sound like what we're doing here -but that's exactly what I'm doing here -I'm turning on the togglebot button mode -which effectively hides the check mark -from the checkbox and then this icon -that you will see here is really just an -icon of a check -and these are really toggle buttons -they're not check boxes so -it's it's really easy to to do a -checkbox with the toggle button -which is technically a checkbox uh -perhaps I didn't explain that in the -best way so check boxes can essentially -be toggle buttons and toggle buttons are -just buttons that are either pressed or -released and they don't have a check -next to them -uh so in this case these are literally -buttons that are checked or released and -the difference that the V sign next to -them is really just an icon it's not -really uh the standard checkbox -component -and the reason I chose to work in this -particular way is it's just a bit -simpler to customize that's pretty much -the only reason to do that -uh otherwise you know the customizing -the checkbox it's -um some apis that aren't convenient -so let's start by creating a new project -and I'll create a simple Bare Bones -application almost every project that I -create is just the standard Bare Bones -application with uh nothing in it so I -can edit myself I use the native theme -and it's important to define a correct -package name at this stage if you don't -know that by now don't use codename one -for your names please I see lots of -people do that please please don't do -that that's not a good idea uh -because this package names must be -unique and must be owned by you the -domain must be owned by you that's -crucial because that's something that -you will use to ship your app later -so don't screw that -piece up because that's really bad -and it's creating a new project and uh -it's just the standard typical Bare -Bones codename one project nothing in -there -and here we go regular form -so now let's paste the shopping list in -here -and move on -to actually creating the right form with -uh the right name for the form and the -right title for the form -so it's a shopping list application -and I'll start with the building the -individual check boxes and separators -from that string array I pasted above -but this is currently everything is -hard-coded for simplicity's sake it's -not an actual application yet -so I need to import obviously these uh -component and checkbox and all of that -and netbeans is a bit acting up in terms -of uh everything I have running -concurrently on my machine -uh so forgive me about that uh the check -box uh I'm creating it with a string -Constructor and I need to do several -things in order for the checkbox to -appear as it is in the design -and one of those things is to set the -Gap the Gap determines the distance -between the icon and the text -so I need to set the Gap to three -millimeters so that it will look uh -similar to the design where everything -is spaced out -and uh notice I I will I also need to -place the text to the right of the icon -using the set text position -and that will -place it so so it looks that way -otherwise the text will be below the -icon and it's not the design UI that we -actually want -now notice that the design uses a lot of -spacing one of the things I see -designers do developers do incorrectly -is sort of ignore the spacing or -automatically set the margin and padding -to zero everywhere -and that's not good you need to use -white space and notice it really really -well in the designs white space is super -important when it comes to design -so pay attention to that and -try to use it effectively so designs -will look good -and of course last but not least we're -adding the icon itself that's a font -image so it's literally a font icon from -the built-in material design fonts -so I'm just using a material font with a -UI ID -and the UI ID will be used to get the -design the colors and the sign -sorry in this case not the size because -I'm passing the size as the last -argument but uh the colors foreground -background opacity of uh actually -transparency but -you know Nuance uh I'm giving it that UI -ID that's where I can customize that UI -ID later on in the designer tool which -will move to right after this -and set the correct colors I'm using -separate icons for the icon and the -Pressed icon so each one would look a -bit different -I'm obviously giving uh different UI IDs -although it's technically the same icon -if you look at it -and that's essentially the checkbox the -next stage is a separator the separator -is actually a really cool uh thing it's -just a label -that literally does nothing other than -have a specific uh UI ID -of separator -the thing is that I need to invoke a -special method here -and that method indicates that I want -the label to render -despite the fact that it's empty -because there's a special case for empty -labels that hides them -and this sort of disables that special -case because it's very common to use -labels and have them disappear if I -didn't want them so that's the default -so you need to call so show even if -blank to do that -and I will just do a for Loop over the -elements within uh the shopping list and -just add them one by one -and I'm just adding them all to the -shopping list add create check and then -add the separator to separator to the -next line -and um doing it as a single statement -mostly -to signify that it is a signals a single -statement -logically -if not physically -and next we'll move to the designer and -in the designer tool I'll -uh add elements first for the check -boxes uh the checked icon so it will -have the right color -and I have it in my clipboard here -that's specific the specifically the -selected color and I'll keep it -transparent -then I'll copy that -and I'll create an unchecked icon -and the color there is -pretty easy to remember -it's just a form of Gray -and the transparency again same thing we -don't want a background for that -and last but not least is separator -separator is pretty easy -because separate sorry a toggle button -first -so in the case of the toggle button -uh we have again the specific background -colors -not as easy to remember but not too hard -and one of the things that uh -we need to do with the toggle button -besides Define the transparency to a -pack in this particular case is to -disable the Border because toggle button -is a standard component and has a border -by default so I need to select an empty -border to override the built-in default -border for toggle button next I need to -define a lot of padding so it will feel -um -spacious again back to the the original -design and the importance of using -padding to make user interface field -wide and I'm setting the margin to zero -because they need to literally touch one -another I'm also explicitly setting the -alignment to left because the default -alignment for buttons is often Center -and I'm setting the font to be a light -font uh sorry a thin font at a specific -millimeter size and that will -um experimented with it with it and -looked reasonably good now I'm using the -native fonts which don't look identical -everywhere -and I'm pasting it to the different -stages -now and here the last stage and this is -really important -I pasted the toggle button to uh -the selected now lifted as is it's not -necessarily what I'd want to do but I -did it right now I might change it in -the future -but the Press state which is the one -we're looking at is when when a toggle -button is physically pressed down -and in this case I want the nine piece -border that I mentioned earlier -so before we go into doing that I want -to have a word about what nine piece -9-Piece Border -borders are in case you -aren't familiar with them enough -haven't played with them didn't read -that developer guide section about them -which I worked really hard on writing so -take that passive aggressive and use -that to study that that section against -uh and if it's not great or excellent -please edit it on the wiki so a nine -piece border essentially takes it's -great for symmetric shapes although it -works with some asymmetric shapes you -will notice the shapes on the left are -colored in green those are great shapes -or good shapes in the case of the -rightmost one -four nine piece borders they will work -with nine piece of borders the shapes on -the right will not -and the reason will become apparent when -I explain uh more accurately why it's -also harder to work with some types of -gradients with nine piece borders -the advantages that nine piece borders -have is that they're efficient and -they're low memory and they're they -don't degrade when you uh -scale them to Infinity they look -essentially the same -and the reason is the way that they work -so a nine piece border Cuts an image -into these nine pieces that you see -before View the four corners -four sides and a center -and what diff --git a/docs/website/video-transcripts/WYlM2utu4Ps.json b/docs/website/video-transcripts/WYlM2utu4Ps.json deleted file mode 100644 index 905a716f36..0000000000 --- a/docs/website/video-transcripts/WYlM2utu4Ps.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 32, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 171, - "youtube_id": "WYlM2utu4Ps" -} diff --git a/docs/website/video-transcripts/WYlM2utu4Ps.txt b/docs/website/video-transcripts/WYlM2utu4Ps.txt deleted file mode 100644 index 3042494d69..0000000000 --- a/docs/website/video-transcripts/WYlM2utu4Ps.txt +++ /dev/null @@ -1,32 +0,0 @@ -right now we have only one subclass of -the base form -the main menu form which represents the -main ui -the list of elements on top -is a standard code codename one list -normally selection won't appear in the -list once our finger is lifted -so we had to explicitly invoke some -methods to force that behavior -we also set the list to horizontal mode -and fixed center mode -the dish container uses a text area for -the content -since text areas can grow -we need to disable that behavior -as we prefer cropping of the text in -this case -we're adding the two buttons using this -code which includes the pricing in the -label -notice these are enclosed in a grid -layout to the south portion of the -container -we discussed masking before -and here is where we apply it to the -image object -so it has the rounded corner appearance -the mask with round rect method uses -this image as a mask -and applies it -to the given image diff --git a/docs/website/video-transcripts/W_1S2Rzgff8.json b/docs/website/video-transcripts/W_1S2Rzgff8.json deleted file mode 100644 index eaa690f6ee..0000000000 --- a/docs/website/video-transcripts/W_1S2Rzgff8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 99, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 583, - "youtube_id": "W_1S2Rzgff8" -} diff --git a/docs/website/video-transcripts/W_1S2Rzgff8.txt b/docs/website/video-transcripts/W_1S2Rzgff8.txt deleted file mode 100644 index b234625557..0000000000 --- a/docs/website/video-transcripts/W_1S2Rzgff8.txt +++ /dev/null @@ -1,99 +0,0 @@ -all media objects for instance images -videos etc -are stored in the media entity -this is pretty convenient as it lets us -store media files in the database under -a separate database table -which would be better for performance -there are pros and cons for storing -media in a database -a pro would be that it works with -clustering as data in the day is in the -database and not on a specific server -file -however it might make the database too -large and harder to back up -performance is more complicated it -should be reasonable as long as table is -separate -historically performance of blobs in the -database was pretty bad and there might -be issues in some deployments or some -database types so this might be -challenging -both approaches would be simple unless -scaling is involved in which case we'd -need to store files somewhere -there is a common alternative of using a -service dedicated to hosting files -either homegrown or in the cloud for -instance s3 -since the media object is a is separate -it makes sense to have it anyway and if -we choose to use such a service we can -include the file key for the cloud -object instead of the actual media file -the media object itself is pretty simple -just like the user object we use a ui -uuid to represent a media object -this is the file name used when -uploading which might be relevant if we -let the user browse the files he -uploaded in the future -the timestamp when the media was -uploaded using long makes this simpler -to work with although -date might be more convenient in a -database -role can be used to indicate the purpose -of the image for instance if it can be -an avatar to indicate the image serves -as an avatar -visibility is the privacy scope of the -image for instance friends would -indicate only friends can see the image -whereas public would indicate it's -visible to everyone -this is the user that uploaded this -media object -the data is marked as a lob which lets -us store relatively large objects notice -that there is still a limit on file -upload size and forced by the web server -support in spring boot -the media object has a dow as well it -lets us pass the media data back and -forth through the service layer -as you can see the rest of the code is -only getters and setters -nothing much -the object is relatively simple and only -contains -one small dependency media dao -we could potentially enhance it with -additional data such as description -tagging and even auto generated content -based on image recognition -but for now that's enough -before we go into the media dao we also -need to mention the crude repository -interface for this class -it's simple as we don't need any queries -at this point -now that we got this out of the way we -can look at the dao it's trivial too -the fields map directly to the fields of -the media class -in this case -the obvious exception is the user dial -object which uses the dial type instead -of the user type -as we did with the user object we have a -default constructor and a convenience -constructor both of these were generated -by the ids completion suggestions -the rest of the code is just -auto-generated getters and setters -nothing else -and with that we conclude the media -object entity diff --git a/docs/website/video-transcripts/Wiy2goFwfQA.json b/docs/website/video-transcripts/Wiy2goFwfQA.json deleted file mode 100644 index 6952633750..0000000000 --- a/docs/website/video-transcripts/Wiy2goFwfQA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 30, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 150, - "youtube_id": "Wiy2goFwfQA" -} diff --git a/docs/website/video-transcripts/Wiy2goFwfQA.txt b/docs/website/video-transcripts/Wiy2goFwfQA.txt deleted file mode 100644 index 126ed8c49d..0000000000 --- a/docs/website/video-transcripts/Wiy2goFwfQA.txt +++ /dev/null @@ -1,30 +0,0 @@ -i've discussed the star settings as part -of the star form -here is the class itself -the style setting -class -is a standard property business object -class which means it can be persisted -implicitly into the database and -serialized to the server using -json web services -to support this in the app storage i've -added an insert or update method which -implicitly checks if a style exists in -the database -and if not it adds it -the fetch style method works in a very -similar way to the api i showed for -fetching the app storage -but a bit simpler -in that sense fetch styles -is similar as it fetches the list of all -the styles -the last bit of changes just consists of -creating the tables -and setting the primary key -so this class can be -properly saved -into sql -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/XSztvFzQAr0.json b/docs/website/video-transcripts/XSztvFzQAr0.json deleted file mode 100644 index 0dac158859..0000000000 --- a/docs/website/video-transcripts/XSztvFzQAr0.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 392, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 5507, - "youtube_id": "XSztvFzQAr0" -} diff --git a/docs/website/video-transcripts/XSztvFzQAr0.txt b/docs/website/video-transcripts/XSztvFzQAr0.txt deleted file mode 100644 index 3737c0c777..0000000000 --- a/docs/website/video-transcripts/XSztvFzQAr0.txt +++ /dev/null @@ -1,392 +0,0 @@ -hi everyone today i'd like to talk about the basic one of the first questions we -get asked on the chat in the website and that's what is code name one -now my two second answers obviously -it's java for mobile application developers that's sort of the quick answer -but it kind of hides a lot of the complexity and the history so this is -really the long version of that our answer which includes -everything that describes what codename one is now even if you've worked with -codename one built an app and shipped it and did everything -i'm sure there's some thing within this presentation that you didn't know -or didn't remember and even i had to look up a couple of things like how many -releases we've had since we launched which is something oh that literally surprised me -so first let's start with a bit of history so -the origin of codename one is when by the way it's not chen -if you only saw his name online uh ken fishbein uh -started the luit project at uh sun microsystems back in 2006 2007 -and he started it with the goal of ending device fragmentation -back then it was uh the mobile device fragmentation it was before android existed was before -ios obviously uh existed so device fragmentation was mostly j2me -and rim devices back then and he built something it was used -internally the project for which it was built actually eventually closed down but the library sort of -became very popular and uh we saw the potential and -eventually we started working with customers and eventually we open sourced it in 2008 as part of sun microsystems it was -a big announcement java 1 main stage and everything and -a really a really big deal and it gained quite a bit of community traction -the problem is it was standing on top of j20 which was declining fast -and some support for rim the community -came came through and actually did a port to android which we were prohibited -from doing because of all of the complexity related to that and we had some proof of concept uh port -to ios that worked but again not something we could release as part of sun microsystems -and not as part of oracle later on so eventually uh -in 2012 we left and formed cone name one both myself and hen -and there we took that open source project as -the bit of the beginning and built the whole thing around it the -whole thing that is code name one today is much much bigger and you'll see in the -following slide of what it actually is so and since then we've -gone from literally zero to millions of installs hundreds of millions of installs -10 releases and added lots of additional platform support uh besides the ios and android -with which we launched we also added things we never imagined that will add like javascript support -and desktop support and windows support we built three ports for windows -nightmare but it was a huge journey to get to this -point from that beginning but -let's take a step back now and look at what that is so -from the list these are essentially the five things that are code name one -and it's really a combination of all of those together so -i'll talk about each one of these sentences in the next slide in the next few slides because for each -one of them i want to talk uh more in depth and -the thing is that one of the reasons we came up with the -the name code name one was really by accident we literally had a code name written on uh slides and sort of yeah it -sounds nice and the url is available let's take that but the thing is that it really fits -we take these five completely separate things that are supposedly unrelated to one another -and we ship them as a single product you just install the id plug-in and that's it codename one is installed -and that's why in retrospect that's why we call it codename one it sort of works -and uh and we decided that we like that so anyway the first thing is a virtual -machine now on most platforms we don't actually ship a virtual machine -so for instance uh on android we just use dalvik or art -that exists it works there rather well and we just work with that for the desktop port we can just embed uh -the jre from oracle into the port and essentially just ship that -but some of the platforms that we target don't have java built in and the best example for that is ios -which doesn't have java support initially when we launched we used an existing product called xmlvm which is -an open source project that translates uh by code to to c code essentially -and uh it worked reasonably well but it wasn't really well maintained maintained it was huge it had a lot of complexity -that we didn't really need and eventually when we saw the project -winding down and had lots of issues that we've constantly struggled with -we decided that the right thing to do was build something of our own -and in retrospect that was very good decision to make because we wanted to keep the simplicity -of xml vm and wanted to simplify it further by having the bytecode translated to c -and not directly to arm code and this is really important because when we translate you see -we can we can compile using xcode using the official tool that apple ships -and that's crucial because that means we'll be future compatible to any version -of ios that apple ships because they won't stop supporting c because all the games -are built with it everything is built with it so codename one will always work uh -with apple's uh products with minimal changes at best for -instance they came up with uh arm64 a while back -and a lot of competitors scrambled to try and support that -and they have bitcode now which again people are scrambling to support and don't support -we support both without much of a problem just flipping a switch and the reason for that is a very -conservative approach of trans uh transpiling to c code instead of directly to -arm assembly which would be problematic this has another advantage that we can -actually take the compiled code essentially the c project -and with the include sources feature we can literally run the compiled application on apple's -tools and debug and find issues and it's easy for us when we find an issue in -codename one itself but a lot of developers also use it to debug and profile their projects which -i'll talk about in the boot camp as well -windows works a bit differently we had a version that transpiled to c-sharp but that was problematic because -of small really painful nuances between c-sharp and java that make it -almost impossible to do something like that in an efficient way -so instead we chose to adapt a port of ikvm which is a virtual -machine for that takes java bytecode and runs it on.net -and we took that and adapted it to work with the universal windows platform and -it's uh we got some community code and we forked it and we did a lot of work there and -it's on our repository just like uh our vm jet which does uh sorry vm -that does uh the bytecode to c translation all of that is open source obviously -and it's a pretty huge project that we did just recently -last year and it's uh working reasonably well -and the last vm that we have essentially isn't is one that we didn't write at all -it's tvm and it works beautifully and it transpires -java bytecode to javascript and it works in a very similar way to gwt -but doesn't require the source code it works with a bytecode and it's more efficient more modern it works with -threads which is really impressive and it's a pretty great solution so -that's one of the few solutions that we're able to take as is and just use it -so it's pretty a pretty nice solution so that's the virtual machines that we -use the second piece of the puzzle is the api for devices and that's what -essentially luit evolved into so a huge part of that is the user -interface aspect which includes a lot of the legacy of luit so sometimes if you -see something in codename one that doesn't make sense so looks like it's old it's because it is old -we built on that infrastructure that took us years and years and years to build and -we kept on building on top of it because essentially codename one is to some degree an operating system of -its own and that takes years to build and we've had these years to build that with a big -open source community buckets and microsystems also did a lot of that work -and we've got this huge api for mostly user interface because that's the -biggest piece of pretty much any mobile application but we also abstract everything else from -file system to storage to databases to media to pretty much anything you'd -expect there's some legacy there but that's actually part of the good -because it provides stability and continuity the porting layer is -the co-name one implementation and that's something that allows us to separate the api -that's portable to all the platforms from our specific implementation now -that's a mostly hidden piece of codename one we hide it behind package protected fields and things like that you're not -supposed to touch it because it's uh an implementation detail and might -change without warning but essentially that's the piece that allows us to port codename one anywhere -so for instance the javascript port the vm is open source but the the port -itself is something that we actually had to sit down and write this huge port where we draw on the canvas and do all -of that stuff behind the scenes to get everything to work in the browser the same thing is -true for ios where we spent a lot of time building writing a lot of opengl code -and all sorts of other 2d graphic code and you name it all of that is written in -native cn objective c to to map everything to -the requirements of the api and that's how we maintain portability -for all the various platforms now the lightweight ui approach i'll -talk about it more in depth after i finish this segment because it deserves -its own discussion the whole ui is uses that approach and -that's probably uh the biggest differentiator between codename one and pretty much -everything else that is out there although there's quite a few it's not a new invention that we -made it's it's a very old idea that originates from small talk -and and it's pretty much the only way to build a completely portable platform -now the api itself is really simple it's designed for portability and to be -really easy to use so some things are sometimes oversimplified to keep them -sensible but it's designed designed to still be flexible enough so you could do anything -you can control any pixel you can do anything and the last thing and this is really -important the api on the device is statically linked now -that dynamic linking is a very powerful tool if you're building an operating system if you're building an app -static linking has huge benefits because it provides you the stability and consistency -that you'd often want for your apps so for instance if i build an app and ship -it i'd like it to be as stable as possible and i wouldn't want something to change -suddenly with the running app but if you depend on an external library and the operating system replaces that -library you might your app might break because it depends depended on something that -might be minor in theory but could be devastating for your app so -codename one codeman's library is statically linked in and -one of the advantages it gives us as the developers of codename one is that we can move fast and sometimes break things -in terms of compatibility because we know that -shipping apps won't break if your app has already shipped changes that we make right now -won't break that app because that app is oblivious to the changes that we've made to codename one because it's statically -linked if you will try to rebuild the app you will find that something broke -but then you will find it at that point and fix it or notify us and we'll fix it -and that allows us to move faster than say google so for instance google if -they change something even minor with an android it might break shipping apps -and that's a huge problem for them so they need to move very carefully and -they often break things and it's a desire disaster in our case if we break something it -isn't as big a deal for our users because you can see it and you can fix it and -there's also versioned builds that allow you to build against a stable release -within codename one and that way even if we do break something you can still target a specific version and won't -fail on that so the ide plugins are often -given more credit than they're worth the ide plugins are relatively simple -containers that contain the whole shebang so you install them and they include the -pieces of the api and project templates and they include the tooling which i'll talk about in the next slide -but they themselves aren't that clever they don't include that much logic -they generally serve as a sort of glorified installer for codename -one's uh tools and for our [Music] -templates they don't really include that much logic within them -but they're useful as as an installer essentially -now the tools are the are the brains behind everything so we've got build -tools which are essentially uh based on ant and i can go into a -long discussion on why we chose and mostly it was legacy -but we've evaluated other build approaches like maven and gradle and -we constantly looked at options like that and they don't provide an advantage for our -very very narrow use case and only disadvantages so -i'll probably write about it at some point but we we don't just don't have an advantage of -leaving ant and and it makes a lot of sense because it's very familiar and is supported by all -ids except for android studio -but it works on intellij it works on the clips it works on netbeans so that's great -and we also have the device simulator and that's crucial for the device simulator to work we need lightweight -architecture which i'll talk about later otherwise this would be impossible we've got -two gui builders essentially the new gui builder and the older one -which is inside the resource editor slash designer the resource center slash designer -serves as a sort of universal tool a sort of swiss army knife that provides theming -localization image management and obviously the old gui build and all of these tools combined -are essentially uh the ways you interface with codename one besides the api -and they're really important for that and the last piece of the puzzle and possibly one of the more controversial -and important pieces is the cloud build now with recent versions we also support -offline build but if you'd actually use that you'd understand why we have the cloud build -offline build is painful you need to have a mac if you want to build for ios -you need to configure it in a very specific way and this is very error prone and things -can fail and lots of annoying ways and that's just for mac os if you want -windows built and you need a windows machine and different type of configuration and that's a pain in its own right -and android also the installation of the right versions of the sdks and -everything it's it's hell on wheels to to get a build setup working -so we essentially have machines in the cloud that are set up properly to build -uh our current versions of the applications and by the way these are things that we -need to constantly update because apple releases a new version google releases something new and and so forth so we -constantly need to update these machines with uh additional uh version tools versions of -the sdks and things like that so this is a really -something that's much simpler thanks to the central uh management of this -the build tools essentially communicate with our cloud and essentially send tasks that are later on picked by the -build server compiled with native tooling and that way you don't need a mac thanks to the -cloud build and the build is actually faster i personally use the cloud build even though i can use the offline builder -because it's faster and it's easier so when i use offline build it's -it's much slower so because the machines we have in the cloud are more powerful it's actually -faster to send my jar into the cloud and get the binary back then to build everything -locally that's uh that surprised me when i first found found that out and i have a relatively -powerful laptop so it was weird um -and it's it's a bit controversial because people sometimes have issue with sending things -to the cloud and uh i don't think that's as much a problem -in 2017 because we use dropbox for everything and we store important files -everywhere and it's not as if our computers are -halos of impenetrable security so the idea our cloud is secure and we do -spend a lot of time there but uh source code theft existed in -the 90s and our cloud never gets your source it only gets your bart code -so in that sense it's a very secure solution and i think most people who -have an issue with that haven't really gone through the -various ways in which offline build can fail -and all of these ways that it can fail -are applicable in the same way anyway -the thing i wanted to focus about most here is the lightweight architecture because i think that's -probably uh even more controversial than uh the -cloud build approach if if that's possible and that -it's one of those things that uh is sometimes hard to convey -to developers both in terms of how it's the advantages and the disadvantages -so first let's uh talk about what frameworks you might be familiar -with that are lightweight versus the ones that are heavyweight that you might be familiar with -now uh notice broadway lightweight architecture obviously a ui aspect not -so it's irrelevant to the other aspects of codename one so codename one is lightweight -and swing and java fx and qt are all lightweight architectures i didn't mention flash and lots of other -uh frameworks are lightweight frameworks and heavyweight frameworks -there's several examples swt awt xamarin uh accelerator all heavyweight -uh frameworks to one degree or another some frameworks mix heavyweight and lightweight -to some degree mostly if they mix it heavily to one side then they're considered heavy weight if -we we allow some heavy weight widgets but i digress so -the the word lightweight is sometimes when i used it to refer to codename one sometimes people hey you're -making fun of other frameworks as if they're heavyweight well it's not a term that we made up it's a term that comes -from a swing and originally uh -it was just used to differentiate between swing and awt now in a lightweight framework uh the -framework draws its own components in the heavyweight framework it uses native widgets for everything -and that is sounds more efficient but it isn't because you need to constantly -communicate with the native system and that has uh carries a weight -and in that sense it's heavyweight uh so that term was uh given by the swing team -to indicate that uh that it uses lightweight architecture as opposed to the peer architecture of -heavyweight frameworks now -the best way to describe them the you can't really say that one approach -is better than another well i can okay i'll i'll go with it i'll go -on a limb and say lightweight is better but obviously that's an opinion there's facts that -work for both sides both sides have reasonable claims i think that lightweight has better claims -objectively but obviously i'm biased so it's hard to to tell one of the things -that i've noticed over the years -well before codename one when people would argue between swt and swing -this is a religious debate you either buy into lightweight or you buy into heavyweight -if you bought into the religion there's little that i can say that will sway you one way or another -if you are one of the swinging voice voice votes in the middle or if you're on the lightweight side -then that can work otherwise if you're so gang-ho on heavyweight -well not much i can say so in lightweight frameworks we draw our -own widgets that means literally every pixel in the screen is under our control -and that's a huge advantage so for instance i can just overwrite paint on a painter and just draw in a specific -location and be able to do a very complex and yet still portable -code and we handle the events and -user input for pretty much every widget so when we draw a button we literally -draw the lines of the button or all the images that represent the button then the text and when a user touches the -screen we detect oh we touched in the area of the button and do all of the things like the button -press animation and everything we literally do all of that we lay out the widgets we arrange them we do everything -that's why it took us so long to build codename to build luit essentially and then codename one -because we needed to do all of that and that's essentially replicate what the operating system does -and we also provide all the tooling that's related to that from the gui builders -and uh apis and everything heavyweight goes uh -the other way around in some regards it's simpler to implement for the guys building the framework -uh they just wrap the native platform you know i'm saying just it's not -necessarily as simple as that but often it is because some of the heavyweight -frameworks are just scripts that take the native documentation and map it directly -the api is generally thinner and simpler because -they can't go much beyond the native widgets if they try to be portable they need to -restrict themselves to the lowest common denominator we in lightweight frameworks i often refer -to it as the highest common denominator because we can implement things that aren't available -in a platform in a way that's portable whereas with heavyweights -this isn't usually done and when they do that it's often harder because they're kind of -working against the platform and it's uh all of the things like the layouts and -everything might flunk if you use them in a way that they're not supposed to be used -so and one of the more important things about heavyweight is that you need to use the platform native tools so for -instance if you're using a heavyweight framework like xamarin you'd need a mac to debug -an ios app otherwise it just won't work because you'd um -you really need apple's simulator to run and -run your application uh you you can't run it with uh with -your own simulator because they didn't implement the functionality they only implemented wrappers around the -functionality built built by apple so with a lightweight framework we can sort -of hide apple doesn't really exist for us we can debug on windows and -application and it will work and with xamarin and this is really where -we're getting to the advantages with xamarin or with a tool like that you'd end up having a -layer on top of the native tool and in that case you -you take all of the complexities all of the bugs and all of the issues of the native platform -and add to them the framework issues so i've heard some people complain about -some of the native frameworks of the heavyweight ones that they're not as -stable as they should be the problem is when people make that complaint -you never really know if the problem is within the framework or within the user's installation on his machine -where he might have installed it on an unsupported version of xcode or something like that and -something in the connection there broke with us because the implementation is -based on uh on a lightweight framework we literally implemented everything so when something -breaks it's totally our fault but that's part of the beauty of it we -can actually fix everything and we're not as dependent on -apple or google for uh anything other than the ability to draw -which is mostly consistent and doesn't break as often and -that provides us a huge level of portability and there's a good reason uh -that lightweight frameworks existed through history and that's for portability they were invented during -the age of small talk and later on advocated by people who needed extreme levels of portability -and that's a huge advantage and disputed of lightweight frameworks -on the other side os conventions are sometimes harder to get -from lightweight frameworks so native frameworks automatically get the -os conventions without a problem we resor we use theming to give -os style behavior but obviously it's sometimes a race to -keep up with the way an operating system behaves so nuances might exist -that's always a trade-off between portability ease of use that we provide -and customizability and the other hand it's os conventions -now the ability to be consistent is another advantage of -lightweight frameworks where we can customize the application to be pixel perfect to the way your -designer envisioned it and that's something that's much harder to do -with uh heavyweight framework because sometimes uh one operating system will behave in -one way and another even from the same vendor so an android version -x from samsung might behave differently from android version y from htc -and it's really hard to get these things to match up perfectly with a heavyweight -framework but we can get pixel perfect behavior and one of the nice things here is -performance so a lot of times performance is -very portable so when you look at a specific -use case where performance isn't as great the solution to performances -in at least 90 percent of the cases is to cache cache data keep it in memory -or keep it in the gpu and the graphic processing unit and to do that -we we need to load the right data in advance put it in the right place and make sure it act -it works fast enough and when we do an optimization like that -it's usually very portable and because everything is most of the -code is written in java in our case and its performance will be very consistent -across platforms so if we optimize something in caching or in some other strategy for -android this optimization will work similarly to ios etc -so this is a huge advantage of lightweight -solutions with heavyweight solutions performance is sometimes -good because heavyweight widgets themselves are optimized by the operating system but their behavior isn't consistent so -something that might uh make a heavyweight weight widget like -a list behave quickly on ios might make it behave more slowly on android and so -you end up with a lot of nuance when it comes to performance that's really hard to get right across -operating systems and some of the strategies might not translate as well from ios to android -and so forth so this is a -very debatable and complex issue the issue of performance -the heavyweight frameworks are easier to build for the framework designer -so it's if someone wants to build a framework it's much easier to build a heavyweight one than lightweight one -that's what i meant by the easier to build it was probably -what i was thinking and the last bit about heavyweight and -by the way i really struggled trying to find entries that would make heavyweights -seem uh good to find the advantages uh column -because honestly that's a bit of confirmation bias -but there isn't that much other than matches os conventions and potentially -performant so access to native os features essentially when you want to do uh -to include a feature like google maps for instance so in codename one you can add google maps but that's because we -uh added a peer component and that's a bit challenging you need to know how to do -that and you need to uh to add a peer and it's not as bad today -it actually works relatively well today to do that but um -it's not as easy as some of the native frameworks allow you to -of the heavyweight frameworks uh that allow you to do that and uh -in that sense it's a bit harder sometimes so it's possible in both but -in codename one because of the lightweight architecture we draw things and then if you add something like a map -which is drawn by the system we need to know -when to draw on top of it and when to draw below it and that is sometimes a bit complex and we -also lay it out so so there's various nuances that you need to be familiar with when it comes to -dealing with the peer components and with a heavyweight framework it -works a bit better so -in codename one as i mentioned earlier native peers uh are supported which is -really important some of the lightweight frameworks don't allow so for instance qut doesn't allow its swing has issues with -it uh actually not that far from our issues that we had with it uh -but it's it's a pretty big deal because usually lightweight frameworks don't support native peers and we -think that's really important that we do -we also support z ordering and native components which is also very important -being able to put something on top of that now the simulator -which is a point that i didn't mention earlier is enabled thanks to the lightweight -architecture because we draw everything we can simulate everything on our own and we don't need a mac for that in -order to debug the moment that exists we don't need a mac at all -so we can use uh the cloud build to actually build the native application go -directly to device because we can debug locally and then go directly to the device we -don't need um that problem and that thing and uh with native frameworks that's not an -option so cloud build would be redundant because you'd need a mac anyway -so with heavyweight widgets uh it's it's redundant in that sense -um now codename one as i said is very performant because we use opengl to do -all the drawing and everything so it has native level of performance just like games are fast code name one is fast -and uh it was optimized during the days of uh feature phones so if it ran on a two -megabyte nokia it should run fine on a on a gigabyte worth of ram on an iphone -so it's pretty it's a very efficient framework in that sense -and it's remarkably customizable we control literally every pixel and so do you so you can literally change -everything in the screen and that's unparalleled by heavyweight widget -solutions so i hope this presentation -gave you the tools to understand what a part of codename one -where it came from uh how big it is in terms of the functionality and understand the pieces -that comprise of it and also how they all compete with one another -for better and for worse thank you diff --git a/docs/website/video-transcripts/Xrolmjg8Dr0.json b/docs/website/video-transcripts/Xrolmjg8Dr0.json deleted file mode 100644 index 436f7650c5..0000000000 --- a/docs/website/video-transcripts/Xrolmjg8Dr0.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 26, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 712, - "youtube_id": "Xrolmjg8Dr0" -} diff --git a/docs/website/video-transcripts/Xrolmjg8Dr0.txt b/docs/website/video-transcripts/Xrolmjg8Dr0.txt deleted file mode 100644 index ab0fc9038a..0000000000 --- a/docs/website/video-transcripts/Xrolmjg8Dr0.txt +++ /dev/null @@ -1,43 +0,0 @@ -We are nearing the end and it's time the last two big features: media & push. - -Here I'll focus on the former. Media includes all the aspects of the media from acquisition whether from the camera or gallery, to the upload & then viewing said media. -For the sake of simplicity I'll support only one media file and not the full breadth of ways Facebook allows you to submit/upload and manage media files. -We'll start with the changes in the server then move through the steps in that sequence. - -The changes in the server for media support are minimal. Most of the work is already done we just need to wire and tune a few things. One thing I did need to change was the packet size in the database. - -I had to add the argument --max_allowed_packet=256M to mySQL's launch arguments so it would work with large video files. - -The most obvious change is to Post & its related DAO/Service classes. We'll start with Post itself. - -I've added a relation to media files that lets us reference a specific media object. Notice you can add multiple media files. I won't use that in the code because that makes layout of these files a bit harder and I don't want to get into that. I also added a getter & setter but I won't show them as it's pretty obvious. - -The next change in this class is to the DAO method of Post. Notice attachments are represented as a Map of Strings and not as MediaDAO objects, we don't want to expose the full DAO to the client - -A map is enough as all we need in the client is the media ID & mime type - -This map is now accepted in the PostDAO constructor - -This leads me directly to the PostDAO class which has the predictable change within it. This map includes the set of attachments we created in Post - -it's reflected in the constructor & in the getters/setters that we won’t detail. This will actually work seamlessly for return values - -but for upload we will need an additional set of changes in PostService. -First we need media access which is the one repository we didn't include in the PostService. We need this so we can translate client request to server side media objects. - -The work in the service is done in the post() method, it's a relatively simple change of translating media id's to media objects. - -Since we are creating a new Post it's safe to create a set from scratch - -We just use findById to set a media object into place. That's a trivial change but with it everything will work seamlessly on the server side as the DAO will handle the new JSON requests and server API doesn't need to change! - -There is one tiny change we need to make to media service. It's very nuanced, see if you can spot it… I’ll give you a hint - -See it? If you don’t I can give you a bigger hint - -Notice it yet? We switched @RequestHeader to @RequestParam so the auth can be passed as an argument. -This is a security risk, if you share that URL from your app it will essentially hand out the key to your app! -So why do something like this if there is a risk in it? -In the client side when I want to show an image I can just submit a URL to the URLImage API. However, if I have a required header the only way to pass it is via global headers which would be a mistake here as it might leak that key to a 3rd party website by mistake. I could have left the API as it was and just written more elaborate code on the client... -I chose to take this shortcut both because it's simple and also to explain the nuances of security risks. This isn't a vulnerability, but it can become one if we aren't careful. When you are the only person working on the project this isn't a big deal but with more engineers coming on board details like this might be ignored/missed by latecomers. -With that the changes to support media on the server are done and we can move to the client side. diff --git a/docs/website/video-transcripts/YE7OQFQE0yA.json b/docs/website/video-transcripts/YE7OQFQE0yA.json deleted file mode 100644 index 976bc90c61..0000000000 --- a/docs/website/video-transcripts/YE7OQFQE0yA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 86, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 458, - "youtube_id": "YE7OQFQE0yA" -} diff --git a/docs/website/video-transcripts/YE7OQFQE0yA.txt b/docs/website/video-transcripts/YE7OQFQE0yA.txt deleted file mode 100644 index 8d6ed607d6..0000000000 --- a/docs/website/video-transcripts/YE7OQFQE0yA.txt +++ /dev/null @@ -1,86 +0,0 @@ -animations often evolve in close -proximity to the final application -i don't want to spend too much time -building up things that are -overly specific to uber's design -as you would have to throw them away -anyway -one uber specific animation that i liked -a lot -is a blue highlight circle around the -floating action button -this is android specific for uber on ios -they have a circle but it's inside the -floating action button -i'm guessing this has more to do with -the implementation -than a deliberate design choice -this is pretty easy to implement the -round border allows us to stroke the -border with any color -and to any angle -that allowed this animation with almost -no code -the class has a private constructor -associated with the floating action -button -we create instances of this class by -using the bind method -we need the existing ui id of the -floating action button -which might be different from the -default -we manipulate the style of the floating -action button directly -so we will use the ui id to restore the -default settings -when we are done -the animation stroke and speed are hard -coded to 0.5 millimeter stroke -and 1.5 second per progress rotation -this can be manipulated to create more -deterministic -progress indicator -i use a timer every 30 milliseconds to -update the ui -notice that if a timer runs too fast it -will be throttled and invoked much later -this won't make much of a difference -since we use motion to determine the -actual speed of progress -every time the motion finishes a cycle -we start again from scratch to give the -feel of -infinite progress -i update all three styles individually -instead of using -get all styles -the main reason is preserving the -uniqueness of each style -which get all styles would override -i just get the existing border -while relying on the fact that it's a -round border -and customize the angle properly -bind starts the timer and sets the -animation on -we store the instance of this class in a -client property so we can stop the -animation -later -when stopping the animation i also set -the ui id again to reset all the changes -that were made to the style object -unlike the other animations we've had -i took a radically different approach -of using the ui timer and the style -i used ui timer instead of timer as it -makes sure to run the callbacks directly -on the event dispatch thread -so there is no need for call serially -i've added this to the code in enter -password form so the progress indication -appears there -i essentially replaced all of the code -that referred to infinite progress -with fab progress diff --git a/docs/website/video-transcripts/YwD35TG6WAg.json b/docs/website/video-transcripts/YwD35TG6WAg.json deleted file mode 100644 index 9930510c72..0000000000 --- a/docs/website/video-transcripts/YwD35TG6WAg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 92, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 453, - "youtube_id": "YwD35TG6WAg" -} diff --git a/docs/website/video-transcripts/YwD35TG6WAg.txt b/docs/website/video-transcripts/YwD35TG6WAg.txt deleted file mode 100644 index 1708b7ff65..0000000000 --- a/docs/website/video-transcripts/YwD35TG6WAg.txt +++ /dev/null @@ -1,92 +0,0 @@ -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/video-transcripts/YwkwqXkHGwg.json b/docs/website/video-transcripts/YwkwqXkHGwg.json deleted file mode 100644 index 98cfc360bf..0000000000 --- a/docs/website/video-transcripts/YwkwqXkHGwg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 103, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 584, - "youtube_id": "YwkwqXkHGwg" -} diff --git a/docs/website/video-transcripts/YwkwqXkHGwg.txt b/docs/website/video-transcripts/YwkwqXkHGwg.txt deleted file mode 100644 index 424f338e9e..0000000000 --- a/docs/website/video-transcripts/YwkwqXkHGwg.txt +++ /dev/null @@ -1,103 +0,0 @@ -there is only so far we can go with the -simulator -sometimes we run into issues we can't -trace in the simulator and can figure -out by trial and error -in those cases if the device -causing an issue is an android device we -can generate an android native project -using include source and run the -built-in android profiler tool -there isn't that much to discuss about -the android profiler it improved a great -deal in recent years and now provide -something close to the level of the -profiler we have on the desktop it's -still not remotely remotely as thorough -so we can use it only as a basic -guideline -when we run the profiler we see some -information at the bottom of the android -ide -when we expand it and dig in we can -study the cpu overhead -and memory overhead of a specific -operation so we can narrow down where -the cpu is spending its time -one of the cool features in android -devices is the developer options menu -this menu is hidden by default in -android and you need to activate it in -the settings by clicking the about phone -option then scrolling all the way down -and tapping the build number entry seven -times -once you do that you -will have a new developers option -menu option -in the above menu -there are many interesting features you -can enable and play with to help test -your android app but my personal -favorite is the debug gpu overduel -when you click this option you are -presented with three options we can -enable the show overdraw areas option -which will give us the results on the -right -as a side note -deuteronomy -the -utronomy which i hope i pronounced -correctly refers to the green color -blindness -and is unrelated to performance it's -related to accessibility of your color -choices -the colors look weird at first -but here's what they say -true color -no overdraw you are perfect and even -google doesn't hit that much -blue -overdrawn one time that's probably the -normal for most of your ui if your -background isn't blue or true color you -might have a problem -green -overdrawn two times -green is reasonable for nested elements -pink is overdrawn three times -this is okay for text and special cases -red is over drawn four or more times -this is something you should inspect and -check -if it's acting as it should -it's normal to see some pink in an app -but if you have red it might be a -problem -notice that the kitchen sink has red in -the clock which indeed animates the tick -effect so -overdraw in that section makes a lot of -sense -the ios version of the profiler has some -advantages over the android version -it's in fact based on the dtrace tool -that originated at sun microsystems -when you activate the profiling in io in -ios the app is recompiled and you are -presented with a set of options -there are many interesting options but -most of them are relevant for low level -development -most of us can use something relatively -simple such as time profiler -this is the output of the time profiler -after you play around with the app a bit -you can dig in and get a lot of -additional information from the ui -once you expand the hierarchy you can -look into the cpu time of every method -within the thread and isolate potential -bottlenecks on the device diff --git a/docs/website/video-transcripts/ZEe_hb1Lz6Y.json b/docs/website/video-transcripts/ZEe_hb1Lz6Y.json deleted file mode 100644 index b4308eb4da..0000000000 --- a/docs/website/video-transcripts/ZEe_hb1Lz6Y.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 224, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1364, - "youtube_id": "ZEe_hb1Lz6Y" -} diff --git a/docs/website/video-transcripts/ZEe_hb1Lz6Y.txt b/docs/website/video-transcripts/ZEe_hb1Lz6Y.txt deleted file mode 100644 index ca08a01c06..0000000000 --- a/docs/website/video-transcripts/ZEe_hb1Lz6Y.txt +++ /dev/null @@ -1,224 +0,0 @@ -in this section we continue with other -performance tips and pitfalls starting -with lists -when we initially created the list API -we were heavily inspired by swing's -architecture the ability to create an -infinitely sized list without -performance penalty was attractive and -seemed like a good direction for our -original two megabyte Ram targeted -devices back in 2006 seven -we knew the renderer model approach was -hard for developers to perceive but we -also assumed a lot of Swing developers -would find it instantly familiar -we made attempts to improve list in the -years since for instance multi-less -generic less cell renderer container -list Etc -these hoped but the core problem of list -remain -I won't go into all of the problems with -list and only focus on the fact that -performance benefits of list are dubious -list can perform well but writing -performance list code is a challenge -anything under 5000 entries would -perform better with alternative -Solutions if you need more than 5000 -rows reconsider -scrolling Beyond 1000 rows on a mobile -device is challenging -it made sense for a swing application -but it doesn't make sense for a mobile -phone or even a tablet -most developers will be better served by -an infinite container -if you do choose to go with list the -performance of the render and model -methods is crucial as they are invoked -multiple times per page over and over -again -in fact this is such a problem that for -anything under 200 elements a container -would perform much better than this -overdraw is the process of drawing the -same pixel more than once so if I paint -the background in white then draw text -on top of the white background I've -effectively overdrawn every pixel where -the text is drawn -this is perfectly normal for an -application for example I can fill the -background in white then draw text on -top of that -however if we over draw pixel four times -there might be a problem that might mean -the logic of the application is drawing -a background for the root component then -drawing again on top of that and again -that might still be okay but we need to -be aware of this and minimize it as -overdraw can be expensive -reducing overdraw can boost rendering -performance and animation smoothness -to reduce overdraw we need to look at -the hierarchy in the component inspector -and minimize opaque slash translucent -elements -we want no more than two backgrounds per -hierarchy and the ideal circumstances -I will discuss the process of debugging -overdraw later -networking is usually conducted on its -own thread and for the most part -shouldn't impact performance too much as -the network thread yields CPU seamlessly -when you use the network manager however -there are some nuances that should still -be followed by default code name one -allocates one thread for networking so -developers can rely on simple -consistency when making a request -otherwise you might write code like the -code above and request 2 would finish -before request 1. that's perfectly fine -for some applications but you need to be -aware of that -assuming you understand this we -recommend two Network threads for most -applications -you can add more but that might impact -overall performance without providing a -huge benefit to network speed -you can set the thread count in the init -object method in the main class using -update Network thread count method -the network threads are often the best -place to do long running Network related -tasks such as parsing -for instance the code on top might seem -simple but it carries a price it runs on -the EDT -the code above will work correctly since -the UI change will run on the ADT but -the parse data invocation might be slow -and might slow the entire UI a better -approach will be the usage of the code -below which runs the parsing on the -network thread -this has the added benefit where the -response stream isn't read to a byte -array first before parsing notice that -the rest API and codename 1 implicitly -does the parsing of Json on the network -thread to protect the EDT -it's easy to lose track of size slash -performance when you are working with -the within the Comforts of a visual tool -like the codename one designer -when optimizing resource files you need -to keep in mind one thing it's all about -image sizes -images will take up 95 to 99 percent of -the resource file size everything else -pales in comparison -large resource files can both harm your -build quota and hurt your performance by -taking up too much RAM in runtime they -increase the distribution size which -means users might uninstall your app or -even forget they installed it by the -time it was downloaded -like every optimization the first rule -is to reduce the size of the biggest -images which will provide your biggest -Improvement for this purpose we -introduced the ability to see image -sizes and kilobytes to launch that -feature use the menu item image sizes in -the designer make sure to backup your -resource file on a regular basis -especially before doing this stuff -unless you've found a specific image -that takes up most of the space it's -probable that space is taken up by many -multi images -multi images are essentially a single -image in multiple resolutions for -different device densities -these images can grow to a very large -size as we store a lot of resolutions -for every such image -if there are densities you don't need -such as low very low both of which don't -exist in small devices or 4K you can -just delete all of these image from -existing multi images through the menu -item image Advanced remove DPI -image delete unused images presents you -with a dialog containing the images that -are unused in the theme -it allows you to select the images you -wish to delete -all are selected by default -notice that this method doesn't scan the -code for image usage so an image which -you refer to from code will be marked as -unused and you would need to unmark it -manually -if you have a very large image that is -opaque you might want to consider -converting it to JPEG and replacing the -built-in pngs notice that jpegs work on -all supported devices and are typically -smaller -you can use the excellent Opti PNG tool -from HTTP -optipng.sourceforge.net to optimize -image files right from the codename one -designer -to use this feature you will need to -install Opti PNG then select images -launch optipng from the menu once you do -that the tool will automatically -optimize all your pngs -when faced with size issues make sure to -check the size of your res file if your -jar file is large open it with a tool -such as 7-Zip and sort Elements by size -start reviewing which element justifies -the size overhead -some apis might impact performance -negatively in some cases let's start -with round border and round rect border -since their low-level rendering code can -be expensive we use a mutable image to -Cache the data into the parent component -object this trades off performance for -Ram and might cause an upfront -performance penalty normally it -shouldn't matter if you have a couple of -round buttons however this could be very -costly if you have a list containing -many elements with such borders -gradients used to be very slow in -codename one as they relied on software -rendering for portability -newer versions are much faster for the -most part but I would strongly advise -benchmarking to make sure -using the gradient as a background -creates an image object background which -can result in a huge memory overhead -you would often be served with a better -served with solid colors or image -backgrounds -gaussian blur is an inherently slow -algorithm -there is no getting around it if you -need to use this API you need to use a -trick like call Sierra Leone idle or a -thread notice that apis such as drop -shadow use gaussian blur under the hood diff --git a/docs/website/video-transcripts/ZzSCPcnFkxs.json b/docs/website/video-transcripts/ZzSCPcnFkxs.json deleted file mode 100644 index 50009a3c64..0000000000 --- a/docs/website/video-transcripts/ZzSCPcnFkxs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 105, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 559, - "youtube_id": "ZzSCPcnFkxs" -} diff --git a/docs/website/video-transcripts/ZzSCPcnFkxs.txt b/docs/website/video-transcripts/ZzSCPcnFkxs.txt deleted file mode 100644 index dab303bf09..0000000000 --- a/docs/website/video-transcripts/ZzSCPcnFkxs.txt +++ /dev/null @@ -1,105 +0,0 @@ -continuing with the style form for the -final two pieces -the color -and font picker -moving on to the color picker the ui for -this component is relatively simple -we accept a color value and provide a -callback whenever the color value -changed -these are the pieces that make up the -component -three sliders for picking a color -channel -the text field and a label whose -background color is used to preview the -selected color -let's take a quick peek -at color customizer before we proceed -i can spend a while discussing the color -customizer but there is really very -little to discuss -we create a round thumb image and place -it on an editable slider component -honestly this code is a bit of a mess -and should have been in the theme -but because of the special nature of -this form -i couldn't place this stuff -in the theme -so i had to do it here -that's a major reason for this method -so the code would be a bit more bearable -every time a channel color changes -we update the color with a new value and -shift the channel color -into place for integration with the -other current channels in the current -color -let's take a second and check out update -color -update color just sets the color into -place and calls on success -for us -it also updates the text field with the -right hex color -an important piece is that update color -uses -acquire lock and release lock -which we need to guard against -recursive access -when we invoke set text to update the -hex value -when the user drags -the slider -it triggers a text change event -but since lock isn't acquired by the -text event it won't do the change -the same is true -the other way around -if it wasn't we'd run into an infinite -loop where changing the text updates the -slider -and vice versa to infinity -speaking about the hex text field -we acquire the lock here too -and update the various channels as -needed -the method is relatively -simple -let's move on to the font editor which -is in some regards even more elaborate -than the color editor -this starts with code that tries to -calculate the font size -once a font is loaded its size is -determined in pixels not in millimeters -but we want -to size it in millimeters as that's more -portable -so we need some way to infer that -here we loop over the values and try to -infer the millimeter value for a given -pixel value -from the list -appropriately we will pick the right -string from the list -in the picker -moving on we have the buttons within the -button group -that allow us to pick the right style -for the resulting font -we have the text preview obviously -but that's relatively simple -when we pick a size we need to update -the size all around appropriately -and delivered to the callback method -and finally we have one huge action -listener that handles all of the buttons -to give give each one its right style so -a button for bold will apply the right -font and so forth -with italic etc -there are probably more elegant ways to -do this but honestly fonts are always a -huge pain regardless of what you choose diff --git a/docs/website/video-transcripts/_EXEN52wQvs.json b/docs/website/video-transcripts/_EXEN52wQvs.json deleted file mode 100644 index 67c26d9b29..0000000000 --- a/docs/website/video-transcripts/_EXEN52wQvs.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 20, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-use-storage-file-system-sql.md", - "status": "transcript-fetched", - "word_count": 833, - "youtube_id": "_EXEN52wQvs" -} diff --git a/docs/website/video-transcripts/_EXEN52wQvs.txt b/docs/website/video-transcripts/_EXEN52wQvs.txt deleted file mode 100644 index 534f03c373..0000000000 --- a/docs/website/video-transcripts/_EXEN52wQvs.txt +++ /dev/null @@ -1,31 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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 - -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. - -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 query API is pretty simple we can iterate over rows in a query and pull out the column values. - -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 diff --git a/docs/website/video-transcripts/_JCsyyIH02E.json b/docs/website/video-transcripts/_JCsyyIH02E.json deleted file mode 100644 index b317ebf630..0000000000 --- a/docs/website/video-transcripts/_JCsyyIH02E.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 126, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 650, - "youtube_id": "_JCsyyIH02E" -} diff --git a/docs/website/video-transcripts/_JCsyyIH02E.txt b/docs/website/video-transcripts/_JCsyyIH02E.txt deleted file mode 100644 index 7e90770a91..0000000000 --- a/docs/website/video-transcripts/_JCsyyIH02E.txt +++ /dev/null @@ -1,126 +0,0 @@ -a big missing piece is the settings code -one of the important things we need to -expose -is the ability to customize information -about the user -ideally this should be as flexible as -possible so when we add additional -attributes to track -they can be incorporated right into the -ui -i simplified a lot of the facebook -ui -had and tried to focus on two big ticket -items -the image pickers and generic user -settings -i've encapsulated the image picking in -the image picker class -this is convenient as we might want to -make it more elaborate in the future -at the moment it simply picks from the -device gallery -we don't need a class in terms of -implementation it's there to encapsulate -the metadata of the image in a single -operation -the file name is useful for the upload -process as we maintain that metadata -data in the server for -reference open gallery -launches the os native code in this case -using image mode -we keep the file name and the image -object -this class encapsulates the upload -feature too -so the file name can remain encapsulated -that's pretty simple -we could -possibly do more by offering an option -to launch the camera etc -the settings ui is encapsulated in the -settings form class -cover and avatar represents the two -images in the settings ui -the background cover image and the -avatar image -the two -change fields represent the camera -change buttons that allow us to replace -the avatar images -next let's explore the constructor code -it's mostly standard and creates the -main settings ui for replacing the -images -here we bind the back button behavior -the current avatar is set into the -avatar label -if a cover image is set we fetch the -image asynchronously -both image buttons delegate to picker -methods so we can replace that logic in -the future -the cover label places the change button -on top -using a layered layout and wraps the -button in a flow layout so it will align -to the bottom right -we do the exact same thing with the -avatar image and its picker button -we then wrap both of those layered -layouts and another layered layout and -the avatar -in a flow layout to align to the bottom -center region -the layer layout usage raises the -obvious question -why not use one layer layout instead of -three -that would be hard to accomplish with -the picker button -we would have a hard time aligning the -picker button to the avatar -of the avatar to the bottom right corner -since the parent layout would be too big -next up -are the buttons we create at the bottom -of the ui -as mentioned at the end of the -constructor -the button bar -are just four buttons in a horizontal -grid -we only map the settings to -functionality -of full editing -in show user edit form -image picker does most of the heavy -lifting here we launch it and get an -image object back -and if -we get an image object back we proceed -we upload the image and get a callback -with the media id value -the avatar is set both locally and -remotely so we don't need a server -refresh call -we do refresh the icon though and make -sure to shape it as it was before -cover picking is very similar but has -simpler code as we don't need to shape -the resulting image and have a simpler -api -next we override this method -to have a custom toolbar -a toolbar is created using the layer -layered mode -the true argument -which makes the content run under it -we use the container ui id for the -toolbar to make it transparent -finally the last piece of code is the -show user edit form since there is still -a lot to cover here i'll continue in the -next -lesson diff --git a/docs/website/video-transcripts/_WKRuO0Izxg.json b/docs/website/video-transcripts/_WKRuO0Izxg.json deleted file mode 100644 index 32093300fa..0000000000 --- a/docs/website/video-transcripts/_WKRuO0Izxg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 76, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 409, - "youtube_id": "_WKRuO0Izxg" -} diff --git a/docs/website/video-transcripts/_WKRuO0Izxg.txt b/docs/website/video-transcripts/_WKRuO0Izxg.txt deleted file mode 100644 index 4c5e86ed4e..0000000000 --- a/docs/website/video-transcripts/_WKRuO0Izxg.txt +++ /dev/null @@ -1,76 +0,0 @@ -we proceed with search into the web -service and client layers -once we have the service layer the web -service layer is trivial -with one small caveat that i'll -highlight soon -first let's review the clause -the base url maps to slash search -which makes sense -people search -maps to the slash people url it takes -the query slash paging arguments and -auth header -for simplicity i didn't incorporate -oauth -but it should be incorporated -searching for posts is practically -identical to searching for people -this method implements -one-time initialization for search data -the search database -it's the one caveat i mentioned earlier -this is a bit of an ugly hack i open a -browser url with search slash rebuild -search db -question mark key equals -this huge string u s h i u y etc -this allows me to initialize the search -database when i set up a server on a new -machine -once the app goes into production i can -remove this method to prevent malicious -usage -i could have used other tricks such as -checking a special is initialized flag -whenever search is invoked -finally we have one last part the -client-side code -first we need to expose the server -search api via the server api class -since the code to search people and -posts is almost identical -i delegated it to a generic method -the method returns a varying type but -also accepts a type for the business -object so we can instantiate the right -class -we can't rely on the value of -the t generic -and runtime due to generic erasure -and java generic argument is syntax -sugar that is stripped out by the -compiler -when codename one compiles to the device -we remove everything related to that -since it's only relevant with reflection -anyway -and would balloon the code size -significantly -in both cases search is a get operation -that accepts page and query data -assuming there are results we loop over -them and add the business object to the -response list -keep the code generic we use the new -instance method -to create the business object -the populate from json code -is still the same -this can be thrown by a new instance -but we should be okay here since this -method is only used internally with -known class types -with that search is now mapped to the -client side the last piece of the puzzle -is tying it into the ui diff --git a/docs/website/video-transcripts/a6q1M_wqEYY.json b/docs/website/video-transcripts/a6q1M_wqEYY.json deleted file mode 100644 index da25ff4a3f..0000000000 --- a/docs/website/video-transcripts/a6q1M_wqEYY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 171, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 959, - "youtube_id": "a6q1M_wqEYY" -} diff --git a/docs/website/video-transcripts/a6q1M_wqEYY.txt b/docs/website/video-transcripts/a6q1M_wqEYY.txt deleted file mode 100644 index f3c7c5a1d1..0000000000 --- a/docs/website/video-transcripts/a6q1M_wqEYY.txt +++ /dev/null @@ -1,171 +0,0 @@ -after going over the css it's time to go -into the code changes we made for this -design -first we need some common business logic -to represent a dish -i use the properties api to represent -the dish object -a dish contains a unique id -price name description and image in two -sizes thumbnail and four size -the about form is pretty trivial -we store the full html hierarchy under -the html directory which acts as a -special case and allows us to view html -with all the bills and whistles on the -device -we use enclosed layout to position the -cart icon on the top right -this encloses the button in a flow -layout to position it -in the right place -the top area is big enough to contain -the image -we see here -and its size is determined by the size -of the top bar -which is -the top of the main form -however -because we use and close right -the cart button is positioned correctly -the pickup component is just a -hard-coded set of values from 1 to 100 -with a special case for 0. -we use a picker class to represent that -but the layout of the main widget -is interesting here -we have a box layout x -for the elements which arrange them -in a row -it's important not to use flow layout -for the entire row as it might break a -line which isn't something we want at -this point -however if we use flow layout for a -single element -breaking a line isn't an option -so this won't be a problem -we wrap the quantity button -in a flow layout so it will be in the -middle -we do the same for the two labels since -both comprise of one item -the flakiness that is sometimes -associated with full layout layout -isn't applicable -and -we can just use it as -a trick to center align elements -we don't need to center align the image -because it is physically larger than the -other elements -the labels are arranged as a box layout -y -notice they are in the middle because of -the flow layout surrounding the box y -the quantity button allows us to edit -the number of elements -and also remove an element from the list -if you look at the removal effect -looping to the right here -you will notice that -this animation has several stages -first we set the dish to an x position -outside of the form -and call animate unlayout -to show the float floating -out -animate unlayout moves a layout from a -valid laid out state to an invalid state -and is designed exactly for this use -case next we physically remove the -element which is now outside of the form -and we animate the ui layout -this creates the scroll layout effect -the contact us form is relatively simple -it has a lot of data in it -but all of that is pretty standard -first we have the map container class -which is a part of the codename one -google maps cn1 lib -that we installed -on into the application -this can be installed via the extensions -menu -and allows us to place a native map on -the form the coordinates of the map are -all hard-coded at this point -the address is just a text area that -isn't editable -styled to appear in this specific way -the phone and navigate buttons are just -standard codename one buttons -with the shopping cart style -they map to the appropriate native -functionality in the display class -we overlay the text and buttons -container on top of the map -using the layered layout since the map -is a native component there are -implications to placing an overlay on -top -but -there -are a bit too much for this discussion -if you are interested -in learning more about that check out -the developer guide section on peer -component -the dish view -is very similar to the map view in -regard to the bottom description -and the overlay -notice -that i went with image viewer instead of -using an image -this allows pinch to zoom that will -even work with the overlay on top -and will allow us to look more closely -at the dishes -the price label is essentially the same -your order component that appears -in the top of the standard forms -however if we tried to draw it -cut off -the round border wouldn't let us -the layout manager prevents us from -placing a component off screen as well -there are other hacks we could do but i -chose to just paint the component onto -an image that is too small to hold the -full size of the component then place -that image on the screen using a glass -pane -a glass pane is drawn as an overlay on -top of the entire screen -and as you can -draw anything there -there are surprising parallels between -best practices in almost every -discipline i ever learned design is no -different -you can take a lot of the coordinates of -programming and apply them to design -once you understand the basic philosophy -if you look at the previous ui elements -it's pretty obvious which ones were -designed by a designer and which ones -weren't -but if we showed them to someone who -isn't sensitive to design nuance -it might take some guesses -so we can get by -but not much more than that -when in doubt i take the lazy approach -i just use images or other elements to -cover up the lack of design skills -thanks for watching i hope you found -this educational please join the -discussion in the comments section -and let me know what you think diff --git a/docs/website/video-transcripts/a7MF3oSCASE.json b/docs/website/video-transcripts/a7MF3oSCASE.json deleted file mode 100644 index 05e69b2275..0000000000 --- a/docs/website/video-transcripts/a7MF3oSCASE.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 70, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 391, - "youtube_id": "a7MF3oSCASE" -} diff --git a/docs/website/video-transcripts/a7MF3oSCASE.txt b/docs/website/video-transcripts/a7MF3oSCASE.txt deleted file mode 100644 index 127f2ba17f..0000000000 --- a/docs/website/video-transcripts/a7MF3oSCASE.txt +++ /dev/null @@ -1,70 +0,0 @@ -there are two about forms we need to -discuss -the first one is a part of the app and -describes the app builder -the second one is a form that designs -the about form that will appear in the -end result restaurant app -in both cases about is an important page -to have as we can stick a lot of -important information there without -impacting the flow or usability of the -final app -About Us Form -the about form of the application is -probably the simplest form we have -it just displays the placeholder html -i'm not a big fan of html -based uis but one thing html does -relatively well is about forms and it's -really easy to just move this out -notice i'm using set url hierarchy which -allows us to package the entire html -source tree with css javascript etc -under the html package this will display -implicitly in a portable way for the -devices -Placeholder -this is the placeholder html it doesn't -really matter as you can see it's just a -placeholder i'm showing it only for -completeness sake -Validation -the about form also has validation for -the url value -which won't allow us to accept the form -until we have a valid url -that disables the checkbox for ok until -we enter a valid url -i'll just enter the url for -codenameone's homepage so we can see -that this doesn't just take the url it -also shows the preview -originally i thought about embedding -html within the final app -but that is painful to implement in a -generic way -i'm assuming that every restaurant would -have -would already have a website in place -notice the flickering is an issue in the -simulator and not on the actual device -UI Binding -the implementation of this form is also -pretty trivial -we use ui binding to update the about -page url but it's such a simple form -that there isn't much need for anything -as we scroll down we see that the page -has okay and back commands -that -either release or commit the single -binding object -we can also see the validation binding -that disables the ok command -but the one interesting thing is -that we load the page url -only when the user interface is finally -valid -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/aNFKBDeky2s.json b/docs/website/video-transcripts/aNFKBDeky2s.json deleted file mode 100644 index eed2cc2d7e..0000000000 --- a/docs/website/video-transcripts/aNFKBDeky2s.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 127, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 649, - "youtube_id": "aNFKBDeky2s" -} diff --git a/docs/website/video-transcripts/aNFKBDeky2s.txt b/docs/website/video-transcripts/aNFKBDeky2s.txt deleted file mode 100644 index f7562184c9..0000000000 --- a/docs/website/video-transcripts/aNFKBDeky2s.txt +++ /dev/null @@ -1,127 +0,0 @@ -now that we reviewed the basic design -elements let's apply this to the code -we will start with the base form which -is a base class from which the top level -forms will derive in the future -Base Form -the init method of the base form -initializes the common pieces including -the search entry which is currently just -a search command -it doesn't do anything right now and is -there mostly for the icon -the psd didn't include a design for the -side menu -it included only the icon indicating the -side menu is there -i added a side menu entry to make the -icon appear -but it doesn't have any functionality or -design -Filler Label -i've mentioned the usage of a layered -toolbar before -a layered toolbar floats on top which -would mean the list of filters etc would -appear underneath it in normal -circumstances -to avoid this i added a filler label -that is as tall as the toolbar area -and placed it -in the top portion of the form -it pushes all the other elements -downwards so they don't overlap with the -title -the background image is a part of the -style -and will stay in place behind everything -Order Entry -we discussed the your order entry in the -css -but i would like to call out the fact -that i used the localization manager to -format the currency price here -that's why the price is listed as ils -instead of usd -in this particular case -this is probably wrong -as prices should be delivered -from the server with the currency type -we discussed the shopping cart style in -the css 2. -notice we apply the shopping cart style -before calling the material icon -which effectively used the styles from -the css for the icon -Label -this label -is placed into a grid layout -with a transparent container -so the top portion shows the background -image and this label abstracts it -creating the effect of a line crossing -that way it looks like the shopping cart -is peaking from the bottom of the ui -this almost works -Grid Layout -remember when i mentioned going -overboard i meant this part -the label and the grid layout trick -almost worked -and this is the workaround to get it to -work -a grid layout takes two components and -give them gives them the exact same size -to fill up available space -this works great except for cases where -the size isn't divisible by two -in which case one pixel at the bottom -remains empty -the workaround was simple -make sure the size of the order bar is -always even -and that way the two components get -exactly the same size -otherwise in some weird cases -we'd have a line of the image -peaking in the bottom part -Category List -the category list -is just a list -it's implemented -in the actual form -and not within the base form which is -common to all forms -so this method might return null as not -all forms will need that list -i mentioned the separator line -in the css section -but here i'd like to draw your attention -to set show -even if blank on the label by default an -empty label is completely removed in -codename one -this helps achieve several design -aesthetics -in this case we don't want that behavior -and we can disable that by using this -method -Top Bar -you might recall the top bar from the -css -it includes the title image -fill style -again notice that the portion that -supposedly doesn't have the title image -showing -still -has it -it's just obscured -the whole title area is just placed in -the north section of a border layout -the content differs from form to form -so -this is the content of the main form and -isn't implemented here -we are just invoking the method of the -subclass diff --git a/docs/website/video-transcripts/aVkeOIx-Is8.json b/docs/website/video-transcripts/aVkeOIx-Is8.json deleted file mode 100644 index f78df68c27..0000000000 --- a/docs/website/video-transcripts/aVkeOIx-Is8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 178, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 912, - "youtube_id": "aVkeOIx-Is8" -} diff --git a/docs/website/video-transcripts/aVkeOIx-Is8.txt b/docs/website/video-transcripts/aVkeOIx-Is8.txt deleted file mode 100644 index ad49ac4402..0000000000 --- a/docs/website/video-transcripts/aVkeOIx-Is8.txt +++ /dev/null @@ -1,178 +0,0 @@ -in this module we'll discuss animations -first a word of caution -don't overdo it -animations aren't about making things -flashy or good looking -when you read apples or google's -documentation on animations they are -always explained -the first animation type exists to draw -attention to something -a good example is when we enter a new -form and want the user to notice a -specific important feature -we can animate it -if we have more than one animation going -on the user will be distracted and will -miss out on our highlight -an example for this is an animation on -an important button so the user will -notice it -the second common use case is -confirmation -one example of this is a form transition -in ios forms move from left -from right to left -when we dig deeper into the app -when we go back they move in the -opposite direction -thus confirming that we are going back -we also have a cover transition that is -used to signify global functionality -that resides outside of the navigation -stack -but this sort of animation is important -in many such cases -elements animate outside -when we delete them so we will get a -sense of confirmation that deletion did -occur -we have a lot of animation types that -allow you to deliver these use cases -as well as many frivolous effects if you -so -desire we'll start with transitions -these are pretty much the first -animation types people notice when -running an app -here i'll demonstrate transitions on a -form -but we can apply a transition to a -dialog or an arbitrary component by -using the -container.replace method -using transitions is trivial we just -apply them to a form a dialogue or use -them and replace -the rest just works -we have several built-in transitions -common transitions include things such -as slide cover fade etc -flip transition is a card like 3d effect -that relies on device native perspective -transform -it will look pretty different on devices -and the simulator -morph -converts one component to another and is -aimed at the material design paradigm -where a ui element in one form -transforms to another when moving to a -new form -bubble transition is also inspired by -material design -and represents a dialogue growing into -place using a bubbling effect -for most the most part we use common -transition for almost all transitions -i mentioned slide fade cover etc -slide is the default transition codename -one except for ios where the transition -is slide fade -by default -this is a special case transition that -slides the body -while fading the and sliding the title -elements at a different pace -the theme has common constants to define -the default transition to the various -types of elements -so you can define the default transition -for form dialog etc -directly from the theme without changing -the code -when we apply a transition to a form -we have both an in -and out transition -this is often confusing -for dialogues the transition in is what -we show when the dialogue is shown and -the transition out is played when the -dialog is disposed -so we usually need both -for forms we usually only use transition -out by convention -there are some -edge cases where both would make sense -but they are pretty rare -and unless you build your own custom -transition you will probably never need -to transition in -on form -these are the uncover and cover -transitions running on the cart action -when i pick this transition i mostly try -to be consistent with other apps but in -retrospect using cover for this feature -makes perfect sense -as it's outside of the regular -navigation stack -i use the morph transition to move from -a thumbnail to the full screen image -the effect has its kinks -but it works -exiting from the morph isn't as -attractive -so i chose to use the uncover -in the reverse direction -going into the code we can see the show -checkout method -which shows how the transition is -applied -to the form -notice we apply both cover and uncover -respectively so the effect of going in -and going out of the form -remains -another important thing to notice -is that we save the old transition out -of the parent form -the reason for this is that we'd like to -restore the old transition when we go -out so cover won't be used for all -future navigation -this is restored here when we press the -x button -that's handled in the constructor of the -checkout -form -the dish effect is a bit more nuanced -morph transition works by converting one -component to another component -in this case we convert the thumb -component matching the dish id -to the background image -notice that these map to the name -properties of the respective components -the reason for this -is that a transition can be defined and -cloned -before a form exists -or while a form changes -by binding to a component name we -effectively -disconnect the transition from a -specific component instance -notice that every thumb also has the id -appended to it -this allows us to distinguish between -different entries within the form -we use the show listener to restore the -original transition out -since a new form instance of dish view -form -is created every time this shouldn't be -a problem -if this wasn't the case we would have -needed a remove show listener -too diff --git a/docs/website/video-transcripts/aW3OlGTmasw.json b/docs/website/video-transcripts/aW3OlGTmasw.json deleted file mode 100644 index 25d7d7de3b..0000000000 --- a/docs/website/video-transcripts/aW3OlGTmasw.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 116, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 617, - "youtube_id": "aW3OlGTmasw" -} diff --git a/docs/website/video-transcripts/aW3OlGTmasw.txt b/docs/website/video-transcripts/aW3OlGTmasw.txt deleted file mode 100644 index 044c75e1a0..0000000000 --- a/docs/website/video-transcripts/aW3OlGTmasw.txt +++ /dev/null @@ -1,116 +0,0 @@ -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/video-transcripts/aZ2Y74xPj_0.json b/docs/website/video-transcripts/aZ2Y74xPj_0.json deleted file mode 100644 index c7127edac6..0000000000 --- a/docs/website/video-transcripts/aZ2Y74xPj_0.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 87, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 397, - "youtube_id": "aZ2Y74xPj_0" -} diff --git a/docs/website/video-transcripts/aZ2Y74xPj_0.txt b/docs/website/video-transcripts/aZ2Y74xPj_0.txt deleted file mode 100644 index c6e8286661..0000000000 --- a/docs/website/video-transcripts/aZ2Y74xPj_0.txt +++ /dev/null @@ -1,87 +0,0 @@ -in the previous module -we built a mock-up for restaurant -application from pre-existing psd files -but it had -a lot of missing features and some -design -issues are several missing pieces in the -app design most importantly missing form -designs -we need a dish view that we will see -when clicking the more info button for a -dish we need an about form -this isn't mentioned in the design but -it's crucial in every app -we can use it for disclaimers licenses -marketing and much -more contact us is important for any -restaurant -so this is also an essential ui element -the side menu should allow us to reach -those additional forms -and as we stand right now we don't have -a design for that element -before we continue it's important to -understand the motivation -this isn't a design class it's still a -programming class -in fact the designs i came up with are -subpar -understanding design is hugely -beneficial for us as developers -the specific example -is -excellent -where -we only got parts of the designs but -still need to deliver something -but a better example is that we develop -a sense of -what designers need and see -this helps us develop -taste and perspective -it allows us to distinguish between good -and bad design it also teaches us to -demand the right thing from our -designers -and communicate with them more -effectively -a blank -page is both liberating and frightening -for designer -and a developer alike -we don't have that here we have an -existing imperfect design that we can -build on -top one of the mistakes developers make -is -overly focusing on the external -appearance of a design instead of -understanding its intentions and -motivations -i think -laziness is -one of the great qualities of a good -developer it encourages us to build -great reusable software -the same principle applies to design -reuse -sentiment bears repeating -good design -is consistent and follows -common conventions -another tip that bears repeating -is avoiding the core factor -back in the lure days -we added a cube transition effect -between forms -this instantly became the only way -developers transitioned between forms -and that looked awful -apple's design rarely does things for -the sake of pretty -every animation conveys a meaning -when things swoosh in -an apple ui -it's always to convey direction deletion -or some other deeper purpose diff --git a/docs/website/video-transcripts/anfVMvBXXX0.json b/docs/website/video-transcripts/anfVMvBXXX0.json deleted file mode 100644 index b2a0732ade..0000000000 --- a/docs/website/video-transcripts/anfVMvBXXX0.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 150, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 763, - "youtube_id": "anfVMvBXXX0" -} diff --git a/docs/website/video-transcripts/anfVMvBXXX0.txt b/docs/website/video-transcripts/anfVMvBXXX0.txt deleted file mode 100644 index 47ae1b4b23..0000000000 --- a/docs/website/video-transcripts/anfVMvBXXX0.txt +++ /dev/null @@ -1,150 +0,0 @@ -layout animations are some of the most -important yet easy to use -animations in codename one -they make the application flow feel -reactive and responsive -at the core of layout animations -is the concept of explicit reflow -in codename one layouts determine the -position of components -that means that if you add a new -component -it will be positioned by the layout -however there is a big caveat -if you add the component after the form -was shown -it won't position itself -unless you do something like rotate the -device which implicitly lays out the -component -you need to ask for layout explicitly -you can invoke revalidate to force -layout -this is valuable for cases where you can -add multiple components -in those cases the layout logic can -happen only once -and can be much -simpler -layout animation works by animating the -validate effect -it takes the components from their -previously invalid or incorrect -positions -and moves them into their real final -position -normally when you add a component -you won't set a size or position to it -as those will be determined by the -layout manager -but with layout animations this can be -useful -as you can position a component -explicitly -and then it will animate from that -position -this allows you to create a flow -in the ui so components settle into -place -this serves to draw attention to changes -in the ui in most cases -animate unlayout is -the exact opposite or -evil twin -of the animate layout method -animate layout takes layout in an -invalid state and returns it to normal -layout with an animation -animate unlayout takes layout in an -invalid state -returns it to normal instantly -then animates it into the invalid state -so if we want to move a component out of -the form -with an animation -we can set its x position -to the screen width -if we call animate layout we will get an -effect of the component sliding into the -screen -from right to left -if we invoke animate and layout we get -the effect of the component sliding out -of the screen -when we run animate layout -we do something that's effectively the -equivalent of revalidate so the ui is -valid once we finished -with animate unlayout the situation is -the exact opposite -the ui becomes invalid -when we are done -which means a typical animate and layout -will be followed by an animate layout or -revalidate -the purpose of animate and layout is -strictly as a visual effect unlike its -animate layout counterpart -there are a few varieties and a bit of -noise -but layout animations generally fall -into into these two categories i -mentioned -and weight versions of the methods use -invoke and block to help you serialize -calls to layout and unlayout -this is useful when you want to create -elaborate effect -with one piece relying on the completion -of the next piece -fade versions of the layout methods can -fade components in or out -for unlayout components are faded out -and for layout they are faded in -this allows components to materialize or -de-materialize gracefully -the hierarchy related animate methods -such as animate hierarchy -are very problematic -they recurse into a hierarchy and try to -lay out all of the elements -within -but this is tricky as you can run into a -lot of edge cases such as -a component that moves from outside the -clipping bounds -to of its parents container -in this video i activated slow motion -mode which is very useful for debugging -animations but for some reason the -unlayout effect wasn't slowed down -so you can only see the deletion briefly -and then the animate layout that follows -is slow -let's go over the code that creates -this animation -the entire animation is done in five -lines of code -let's go over them one by one -first we set the deleted dish container -to the display width which pushes it out -of the visible range -with set x core -we now perform animate and layout and -wait -we maintain full opacity which -corresponds to the second argument -this animates the removal of the dish -from the screen by sliding it to the -right -we next remove the component that we -animated out -this is important as we are currently in -an invalid state -but the component is still in the -hierarchy -now we can animate the other element -into their new place since the dish was -removed there is more available space -and we can use it by shifting everything -into the new position diff --git a/docs/website/video-transcripts/bH7cS5ENNlw.json b/docs/website/video-transcripts/bH7cS5ENNlw.json deleted file mode 100644 index 1bc1d67518..0000000000 --- a/docs/website/video-transcripts/bH7cS5ENNlw.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 75, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 406, - "youtube_id": "bH7cS5ENNlw" -} diff --git a/docs/website/video-transcripts/bH7cS5ENNlw.txt b/docs/website/video-transcripts/bH7cS5ENNlw.txt deleted file mode 100644 index 2146a839a2..0000000000 --- a/docs/website/video-transcripts/bH7cS5ENNlw.txt +++ /dev/null @@ -1,75 +0,0 @@ -in this part we'll review the designs -for the additional forms and the process -are used to produce them -design is based on common principles -so the place to start is by looking at -what other people did -when faced with a similar ui design -requirement -it goes without saying that we are -looking for inspiration only -a basic principle exists in all of these -designs -the dish is the most important element -so -how -do we combine that design with the -design language of -the guy who designed this -they don't fit very well together -so the best thing to do -is to probably minimize the impact of -the design by letting the dish -take up the available space -of the new form -the main challenge is in displaying the -text -and the price -and keeping consistency with this design -language -so how do you combine these two -i came up with this -which isn't my best work -but we'll do for now -the pricing is listed on the right in a -label inspired by the price in the main -screen -and the close -add to cart is inspired by the checkout -form -html has its issues but it's a great -tool for building an about form -it doesn't look great on the simulator -because we need to rely on the browser -component in javasc -but in the device -it looks great -i use the template for the restaurant -as the html in this form -so we can get a decent markup showing -how this should appear in production -i looked at a few contact us forms and -noticed that the map was often very -dominant -this is really important for a -restaurant which has a physical location -i made the map take over the whole form -and that way the ui looks great thanks -to the implementation of the map -i used the existing cart design to -implement -the call and navigate buttons and some -of the ui -fade look from the dish form -i'm a bit frustrated that i couldn't get -the map to stretch all the way up as -it's restricted to the content area -i didn't spend too much time on the side -menu -i just took the colors of the theme -and the top image to create this design -since the side menu -are pretty standard ui elements -i -leaned on my experience with other apps -when designing this interface diff --git a/docs/website/video-transcripts/bU6pZPs3uto.json b/docs/website/video-transcripts/bU6pZPs3uto.json deleted file mode 100644 index 44370c4079..0000000000 --- a/docs/website/video-transcripts/bU6pZPs3uto.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 110, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 683, - "youtube_id": "bU6pZPs3uto" -} diff --git a/docs/website/video-transcripts/bU6pZPs3uto.txt b/docs/website/video-transcripts/bU6pZPs3uto.txt deleted file mode 100644 index 9e2bb7d3c4..0000000000 --- a/docs/website/video-transcripts/bU6pZPs3uto.txt +++ /dev/null @@ -1,110 +0,0 @@ -in this part we dig deeper into the UI -abstraction clause -moving on we reach the fields of the -class and the start of the UI let's step -over those -first we have the form delegate instance -this is only relevant if we are in a -phone mode and not in the tablet mode -where we have a different class in place -the container is the current UI since -form is a subclass of container this -will work for both the form and the -tablet UI mode -elements such as the OK cancel buttons -are added to a container and then the -main container is wrapped up to include -these additional buttons -this allows us to keep the code mostly -seamless -but we sometimes need access to the -actual decorated container for example -when we need to replace it -don't worry if you don't understand this -it will be clearer as I go through the -actual code -this is essentially the equivalent of -get current form -only in this case we get the current UI -abstraction which will work correctly -let's move on to the creation code and -specifically the code that occurs when -working with a tablet notice that is -tablet will return true for desktops as -well so there isn't as much of a need -for is desktop unless you need it to be -truly distinctive from tablet -by default in codename one the content -pane of a form is scrollable on the Y -Asus so I tried to replace this Behavior -here so the code will feel seamless -the UI abstraction client property -allows us to get access to the UI -abstraction class from the component -this is useful for methods that need -access to navigation logic but run from -the UI code -for instance a button in the UI that -triggers form navigation -the decorated container defaults to this -container -unless we have a back slash OK button -pair in which case we create a new -wrapper of the container and use the -decorated container for that -we just created okay and cancel buttons -which we add into the south of the -container using a grid layout notice the -usage of grid layout ensures both -buttons will have exactly the same size -which contributes to nice Aesthetics -if you recall the previous code for ok -at cancel we had in the form delegate -this will seem almost identical -but then you might notice the current UI -is really the UI abstraction class -and we use showback from there and not -from the form -this is the else statement that runs in -case of regular forms or in phone mode -it just defers everything to the form -delegate -moving on we can see the common methods -for show are implemented normally as one -would expect by calling show or showback -respectively -but there is something interesting here -tablet UI which we use to implement the -show logic -tablet UI is the equivalent for form -Delegate for tablets but unlike that -class it's a single form that never -changes -I'll go to that class soon -other delegates work seamlessly -with the container notice that add on a -form will work well and so we'll add on -a container -so in this case we can just seamlessly -delegate -bind Fab Works exactly as we would -expect for a form but with the tablet we -need to re-wrap the decorated container -and use that as the new decorated -container to understand why you need to -understand that Fab wraps the content of -a container and adds a layered layout -where it resides -the parent is a form already then we -have a layered Pane and it just uses -that -but if it's not then we need to use the -container that the Fab returns which is -where the delegated container comes in -really handy -finally all of these methods are just -callbacks that we invoke in the code -the submit button validation is handled -here so it will work with the internal -OK button without exposing a button -component that might be fragile diff --git a/docs/website/video-transcripts/cRqvkIpJlkg.json b/docs/website/video-transcripts/cRqvkIpJlkg.json deleted file mode 100644 index 0aaf54adbd..0000000000 --- a/docs/website/video-transcripts/cRqvkIpJlkg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 206, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1158, - "youtube_id": "cRqvkIpJlkg" -} diff --git a/docs/website/video-transcripts/cRqvkIpJlkg.txt b/docs/website/video-transcripts/cRqvkIpJlkg.txt deleted file mode 100644 index fc4c81f1e6..0000000000 --- a/docs/website/video-transcripts/cRqvkIpJlkg.txt +++ /dev/null @@ -1,206 +0,0 @@ -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/video-transcripts/cZiHmpw2FVI.json b/docs/website/video-transcripts/cZiHmpw2FVI.json deleted file mode 100644 index a7a3ce4be1..0000000000 --- a/docs/website/video-transcripts/cZiHmpw2FVI.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 62, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 385, - "youtube_id": "cZiHmpw2FVI" -} diff --git a/docs/website/video-transcripts/cZiHmpw2FVI.txt b/docs/website/video-transcripts/cZiHmpw2FVI.txt deleted file mode 100644 index 3dc86e54e7..0000000000 --- a/docs/website/video-transcripts/cZiHmpw2FVI.txt +++ /dev/null @@ -1,62 +0,0 @@ -camera support can be a book in its own -right -so i'm keeping this to the bare minimum -we'll use the camera kit cn1 lib to -implement camera support in the -application -since that isn't supported everywhere -for instance the simulator -will implement a fallback to use the -capture api in those cases -before we proceed we need to install the -camera kit cn1 lib from the codename one -extension manager -following that we need to refresh libs -so we can use that api -the main form defines the camera button -on the top left portion of the form -we need to bind our ui to that call we -need to add a new camera method -this leads us to the new camera method -if camera kit is null which will happen -in the simulator -we need to fall back to use the capture -api -captcha is trivial to use it just -returns a file path for the image or -null if the user canceled the operation -here we use one of the new create -methods we added to image picker then -launch the new post form as usual -the new camera form class provides a -custom camera ui -this leads us to the camera form class -which implements a camera viewport we -use a layered layout so we can place -widgets on top of the camera view -scrolling should be disabled as we can't -scroll a camera layered layout doesn't -implicitly disable scrolling like border -layout -events from the camera notify us of -things such as image capture which is -the only thing we handle here at this -time -this adds the camera viewport to the ui -so we can see what we are capturing -this button allows us to capture a photo -of what we are seeing -this is -really basic i didn't handle video flash -zoom or any other important feature we -can handle all of those things but that -would mean building a camera app which -is a task in its own right -so i've left it at that for now -however once you do this you can see how -this leads directly into -the post ui and everything comes -together for media posting -media uploading has a lot of nuances and -complexities are then touched due to -time and breadth diff --git a/docs/website/video-transcripts/csTtSj6TqRE.json b/docs/website/video-transcripts/csTtSj6TqRE.json deleted file mode 100644 index 0d3f3b150b..0000000000 --- a/docs/website/video-transcripts/csTtSj6TqRE.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 290, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1710, - "youtube_id": "csTtSj6TqRE" -} diff --git a/docs/website/video-transcripts/csTtSj6TqRE.txt b/docs/website/video-transcripts/csTtSj6TqRE.txt deleted file mode 100644 index 1858fa069f..0000000000 --- a/docs/website/video-transcripts/csTtSj6TqRE.txt +++ /dev/null @@ -1,290 +0,0 @@ -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/video-transcripts/cxllJwt10VU.json b/docs/website/video-transcripts/cxllJwt10VU.json deleted file mode 100644 index 385e73d453..0000000000 --- a/docs/website/video-transcripts/cxllJwt10VU.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 141, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-01-java-for-mobile-devices/008-theme-basics.md", - "status": "transcript-fetched", - "word_count": 992, - "youtube_id": "cxllJwt10VU" -} diff --git a/docs/website/video-transcripts/cxllJwt10VU.txt b/docs/website/video-transcripts/cxllJwt10VU.txt deleted file mode 100644 index a1e0d77db5..0000000000 --- a/docs/website/video-transcripts/cxllJwt10VU.txt +++ /dev/null @@ -1,141 +0,0 @@ -In this short video we’ll discuss theming -in Codename One -We’ll start with a bare bones native style -application to keep things simple. -We’ll start in the designer tool -In the designer tool we can use the native -theme option to see how our theme will look -in iOS and Android. -We can thus test the native feel of our theme -We can add a new entry to the theme by pressing -the add button. -We can pick a theme entry from the combo box -or type a custom entry. -The derive flag indicates if we want to inherit -the appearance from the native theme. -We can define many background image and gradient -types but now we’ll focus on simpler things -I will create a green button with red foreground -because my goal is to teach not create something -“pretty”. -I can set the color by typing the color hex -values into the respective entries or by using -the color picker widget. -I will also need to set the background to -opaque which is 255, 0 is transparent -I can pick the alignment of the text, center -is pretty common for buttons so I’ll use -that but I can align to the left & right too -Padding and margin can be defined in pixels, -percentage units of the screen size or millimeters. -We always recommend using millimeters as it -is consistently portable. -This allows me to customize the size and spacing -of the button, don’t be stingy with padding -as elements need to be big enough for touch -interaction -There is a lot of confusion over padding and -margin so let’s clarify this. -Margin represents the space outside of the -component so you use that to push components -away from one another or bring them closer -together. -Padding makes the component larger or smaller, -it’s the additional space within the component -beyond what is normally needed to draw the -component. -I’ll discuss border & derive soon. -For now we’ll move directly to font -Font’s can be pretty complex. -I recommend only using one of these native -fonts which will look decent on Android & iOS. -You should usually size the native font in -millimeters to avoid platform inconsistency -This looks great on iOS, lets see how it looks -on Android… -Uh oh, that was totally surprising and unexpected. -This happens because on Android the button -has a border. -Lets edit the button entry -Border always takes precedence over other -background types so we should derive it and -use an empty border if we don’t want a border -That seems to have fixed it but if we click -the button we see there is still a border -for the selected style -We solve this by adding a new selected style -for the button that derives from the unselected -button and also overrides the border style -with an empty option. -The selected style is effectively the focused -state of the button -I use command-c or control-c on windows to -copy and paste the selected style into the -pressed style. -When I click the button the problem is gone -but I don’t see any effect either -I can edit the selected style of the button -and pick a dark green color I then do the -same for the pressed style of the button. -Once all that is done pressing the button -shows the correct highlighting and all is -good in the world once more -The code to show this is trivial, just a button -added to a form -And this is how it looks on the simulator -Lets say we want to add a red button to the -app, we’ll just add a new theme entry and -call it RedButton. -We will then derive from Button so we will -get a similar look -We will set the background to red and foreground -to white while leaving everything else in -the default state -We’ll copy and paste the red button style -into the selected state, we’ll leave everything -as is and only change the background color -to be a shade darker than the current red -color by tuning it in the picker -Now we’ll just paste this into pressed and -go back to the IDE -We add the red button entry by creating a -new button and adding it into the form. -We use set UIID to set the theme entry ID -to red button. -We can also pass this to the button constructor -as the second argument but I wanted the code -to be clearer -As you can see the red button looks and acts -like the green button -The cool thing is that you can apply a UIID -to damn near anything so I can apply the red -button style to a text field thru set UIID -and get almost the same effect -As you can see this is pretty close to the -button appearance applied to a component that -has completely different behavior -The theme can be manipulated directly from -code in roughly the same way. -We’ll set the button color to blue by setting -the background color of the red buttons unselected -style. -Notice the pressed/selected behavior for the -new blue button is missing -We can fix that by changing the selected & pressed -styles as well directly from code -I set them to the same color so we don’t -see much but you hopefully get my point… -There is a better way though, by using get -all styles we can set the property on all -the styles in one swoop. -One last subject is theme constants, with -constants we can manipulate many aspects of -the theme. -You can see them in the theme UI and look -at the help text below each constant to understand -what they are about. -I suggest reading in the developer guide about -the various options in the theme constants -and theming in general. -Thanks for watching, I hope you enjoyed this -tutorial and found it helpful diff --git a/docs/website/video-transcripts/d1T25sbFUKE.json b/docs/website/video-transcripts/d1T25sbFUKE.json deleted file mode 100644 index 1a0e52f83f..0000000000 --- a/docs/website/video-transcripts/d1T25sbFUKE.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 102, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/018-dependencies-gradle-and-cocoapods.md", - "status": "transcript-fetched", - "word_count": 531, - "youtube_id": "d1T25sbFUKE" -} diff --git a/docs/website/video-transcripts/d1T25sbFUKE.txt b/docs/website/video-transcripts/d1T25sbFUKE.txt deleted file mode 100644 index 3197525cca..0000000000 --- a/docs/website/video-transcripts/d1T25sbFUKE.txt +++ /dev/null @@ -1,102 +0,0 @@ -in this section we'll focus on the -native dependencies before reviewing the -implementation -most native code requires that we link -against specific libraries and this is -often the most challenging part of -implementing the actual native interface -if we review the braintree setup guide -for android we'll see that the -instructions include a gradle dependency -change -this is a common tool for integrating -third-party libraries to android native -apps -and it usually follows syntax like the -one we see above -normally this is pretty easy to -integrate -the build hint android gradle depth -should solve the problem in no time -but if we try to build the app we'll get -this build error which is not just -unreadable it's also snipped because it -overflows the screen -so let's zoom in a bit but this doesn't -tell us more one of the mistakes people -make when looking at the gradle errors -is -the focus on the gradle exceptions which -are confusing and verbus -the error is usually printed above -that -and here it is in bold -the minus decay version is the actual -error we are experiencing the problem is -that the suggestion entry is wrong -we can just fix our men sdk version and -codename one -instead -we have a build hint for setting the -android men sdk version to 16 -which will align it with the min sdk -version required by braintree -the problem is that even after that -change -i still got a compilation error which -was just about the most unclear error -message possible -i googled this -which is pretty much the best way to -solve issues like this -and i found a stack overflow entry that -explained -that this is caused by the wrong sdk -version setting in -android -at this time codename one defaults to -sdk 23. -notice we update that value occasionally -so this might be incorrect when you see -this -however -in this particular case the fix was -simple -set the sdk version to 25. -once this was set build went through as -expected and we were able to get a -working project for android -on the ios side things were also a bit -tricky -ios has a tool called cocoapods -which allows us to define dependencies -since we already use maps in the contact -us form we already have a dependency on -that -we can just add a brain tree drop-in -dependency as explained in the -integration guide in the braintree site -and this should work -however -it seems that drop-in requires ios 9 or -newer -as highlighted in their site -so we can define the build hint -pods platform -to force the minimal -platform -but this creates a conflict with the -google maps cn1 lib -which defined the pods platform to 7.0 -this is something that should be -resolved by now -but if you happen to run into something -like this and need to fix it -at this exact moment -then what i did is simple -i edited the required properties in the -cn1 lib file -and just removed the offending line -notice that the cn1 lib is just a zip -file so i was able to just edit the -properties file diff --git a/docs/website/video-transcripts/exL7bS0StP4.json b/docs/website/video-transcripts/exL7bS0StP4.json deleted file mode 100644 index 8e3272e551..0000000000 --- a/docs/website/video-transcripts/exL7bS0StP4.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 174, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-03-build-real-world-full-stack-mobile-apps-java/001-server.md", - "status": "transcript-fetched", - "word_count": 963, - "youtube_id": "exL7bS0StP4" -} diff --git a/docs/website/video-transcripts/exL7bS0StP4.txt b/docs/website/video-transcripts/exL7bS0StP4.txt deleted file mode 100644 index b1678ff408..0000000000 --- a/docs/website/video-transcripts/exL7bS0StP4.txt +++ /dev/null @@ -1,174 +0,0 @@ -i skipped the server creation tutorial -before -as i felt it left the domain of codename -one -too much -and ventured into server programming -in -this level we don't skip such things -so i'm going to review the server code -related to the restaurant app and why it -was built like that -i find server code to be boring that's -not a bad thing boring can be great when -you need something you can rely on which -is exactly what a good server should be -i'll try to focus on the interesting -pieces only and i will mention some of -the entities or the relationships as -they are just more of the same -there are some great java ee and spring -boot courses out there and i don't think -it's our place to displace them -because we did the client first -and the server portion is mostly filling -in the blanks there are still some -hidden complex nuggets -but it's mostly simple -security is one of the more challenging -things about server programming i'll try -to explain as much as i can -but once you go into security there is -always more to know -it's a specialty in its own right -our starting point will be the same -spring boot project i did in the base -course -i'll use mysql again and create a blank -database we'll use app backend server as -the project name -the same database and project will be -used for the app maker -as well as the app itself -there are some nuances in spring such as -service components which are similar to -session beans and java -i'm not using any of those things which -would make sense in an architectural -standpoint but aren't really useful for -our narrow use case -let's start right away -assuming the project was created adding -braintree support to the server side -starts with -editing -the -pom file -with the write dependency -this will allow us to compile the code -that supports braintree -the braintree service is pretty simple -it maps to the braintree url -the first thing we do is provide access -to the braintree -gateway -notice that -the normal braintree api hard codes all -of these values but we load them -dynamically and then place them in a -cache for reuse -the reason for this is that the -braintree credentials are specific to -restaurant and we will have multiple -restaurants running on a single server -the service methods themselves are -pretty trivial -the token is generated using the -standard braintree api there isn't much -here it just returns the string which we -use in the client-side code -the interesting method is the purchase -method where we create a purchase -request and finally -fill in the amount we want to pay -this isn't obvious from the code here -but we have a small security issue in -this code -right here -calculate amount runs on the order which -we received from the client -this means that the prices of the -elements in the order were also received -from the client -our client is theoretically trustworthy -but if someone reverse engineers the -communication protocol for the client -they could just change the prices of the -dishes to give themselves a discount -however if we just -take the dish id and calculate the price -on the server -this will make no difference -this doesn't even have to be malicious -if a price was updated and a local copy -of the app still has the old price -because the user hasn't launched the app -in a year -it's important to make these -calculations in the server -always -in the final version of the server this -is fixed but i kept it for now in the -interest of simplicity and also to -demonstrate how nuanced security issues -can be -this is the basic restaurant entity it -allows us to save the details we need of -the restaurant -we'll add a lot more -into this class but for now -all we need are these things -notice that instead of using a numeric -auto-generated id -i chose to use a string -which i've set to a universal unique -identifier in the constructor -numeric ids are sometimes problematic as -one can potentially guess the id and -scan your database -for example if someone notices that we -have an api called -list dishes that accepts the restaurant -id -this might be a problem he could scrape -the api for all the dishes of all the -restaurants by scanning integer values -scanning your universal unique -identifier values will take centuries or -even eons -when looking at the code you will see -some duplication -in this example we can see the order -entity next to the order data access -object or dao -you will notice that these values are -very similar to one another a dao -transfers data from the client or within -the server -and an entity stores the data -there are often mirror images of one -another but not always -in this case we can see that the dao -contains things that aren't part -of the order -also the architecture is different since -the entity needs to map to storage -structure and the dao is designed for -our convenience -this is a common practice that saves the -need to pass entities around and break -the separation of the tiers within the -application -it allows us to have some flexibility -with our data -the versioning service allows us to -detect if a new version of the app is -available and effectively allows us to -disable outdated versions -this is important as we might be -stuck with an out of date protocol that -we wish to retire -we might need to change the server -address -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/exQ8xXAxtoU.json b/docs/website/video-transcripts/exQ8xXAxtoU.json deleted file mode 100644 index f479ba3dd0..0000000000 --- a/docs/website/video-transcripts/exQ8xXAxtoU.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 103, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 511, - "youtube_id": "exQ8xXAxtoU" -} diff --git a/docs/website/video-transcripts/exQ8xXAxtoU.txt b/docs/website/video-transcripts/exQ8xXAxtoU.txt deleted file mode 100644 index 145a5dd0a6..0000000000 --- a/docs/website/video-transcripts/exQ8xXAxtoU.txt +++ /dev/null @@ -1,103 +0,0 @@ -in this part we will discuss the process -of cutting a psd file -into the images that we need using -photoshop -we'll start with the title image i click -on it to find it within the list of -layers -after i identify the main layer -i see there is another blend -layer next to it -i select them and merge them -in the right-click menu -i convert this new layer -to a smart object -and then i open that -in a new tab -now -we can use file export -to save this layer as a jpeg -i'm using jpeg as the quality of the -background -isn't crucial and doesn't require -translucency -so it can benefit -from the smaller file size -next up -are the items in the list of dishes -we'd like the white rounded square -background -and to do this we'll start hiding -everything that we don't need -now that everything is hidden -we can trim the empty pixels -and leave only the part that matters -we can now remove all the elements from -within the square -so we'll have a completely blank square -this time we'll export png as we need -the transparency -we will then undo everything -so we can keep cutting -the other ui elements -the dish images -have rounded corners around them -and we'd like to apply that uniformly -the trick for that is masking -where we will extract the shape of the -round corner as a black and white image -i isolate the responsible layer -then paint the layer in white -and everything else in black -using the bucket fill tool -once that is done -i try to crop that image -to the smallest size reasonable -i will use it later to shape the dish -image -in the final app -now we can save this image -even though it doesn't have any -transparency in it -we will still use png as we don't want -lossy encoding for this image -now we need the buttons from the items -first we select the button -to find the right layer group -which we then convert to a smart object -we can now edit the smart object -directly -and hide the text on the button -again we export the file to a png -as we need the image to have -transparency -and then we go back -to get the second button in the exact -same way -we go through the exact same process of -selecting the layer and converting it to -a smart object -then removing the text -we export as png -the last piece -is the border of the checkout -we can just hide everything else that -exists in the file so -only the border background and separator -line -remain -once we have that -we can trim the pixels so we'll have an -image -of the right -size -we can first cut out the top portion -above the line -i'll skip the save part as it's pretty -standard -and last we can cut out the bottom -portion -leaving the top line -as part of the bottom -border -you diff --git a/docs/website/video-transcripts/fJD45Mz8SZM.json b/docs/website/video-transcripts/fJD45Mz8SZM.json deleted file mode 100644 index 8825cf4447..0000000000 --- a/docs/website/video-transcripts/fJD45Mz8SZM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 98, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 566, - "youtube_id": "fJD45Mz8SZM" -} diff --git a/docs/website/video-transcripts/fJD45Mz8SZM.txt b/docs/website/video-transcripts/fJD45Mz8SZM.txt deleted file mode 100644 index fe79844735..0000000000 --- a/docs/website/video-transcripts/fJD45Mz8SZM.txt +++ /dev/null @@ -1,98 +0,0 @@ -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/video-transcripts/fmNpMFLwABA.json b/docs/website/video-transcripts/fmNpMFLwABA.json deleted file mode 100644 index 8e460e6b32..0000000000 --- a/docs/website/video-transcripts/fmNpMFLwABA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 54, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 596, - "youtube_id": "fmNpMFLwABA" -} diff --git a/docs/website/video-transcripts/fmNpMFLwABA.txt b/docs/website/video-transcripts/fmNpMFLwABA.txt deleted file mode 100644 index 3f87b7eba0..0000000000 --- a/docs/website/video-transcripts/fmNpMFLwABA.txt +++ /dev/null @@ -1,54 +0,0 @@ -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. -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. -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. -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 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. diff --git a/docs/website/video-transcripts/gJoJQST5jyM.json b/docs/website/video-transcripts/gJoJQST5jyM.json deleted file mode 100644 index 8f25fce87e..0000000000 --- a/docs/website/video-transcripts/gJoJQST5jyM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 426, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 2607, - "youtube_id": "gJoJQST5jyM" -} diff --git a/docs/website/video-transcripts/gJoJQST5jyM.txt b/docs/website/video-transcripts/gJoJQST5jyM.txt deleted file mode 100644 index 774d36128e..0000000000 --- a/docs/website/video-transcripts/gJoJQST5jyM.txt +++ /dev/null @@ -1,426 +0,0 @@ -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/video-transcripts/gM-InTtdhVE.json b/docs/website/video-transcripts/gM-InTtdhVE.json deleted file mode 100644 index 9eed596f52..0000000000 --- a/docs/website/video-transcripts/gM-InTtdhVE.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 177, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 995, - "youtube_id": "gM-InTtdhVE" -} diff --git a/docs/website/video-transcripts/gM-InTtdhVE.txt b/docs/website/video-transcripts/gM-InTtdhVE.txt deleted file mode 100644 index a837ca4baf..0000000000 --- a/docs/website/video-transcripts/gM-InTtdhVE.txt +++ /dev/null @@ -1,177 +0,0 @@ -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/video-transcripts/gmqFd2bU_fM.json b/docs/website/video-transcripts/gmqFd2bU_fM.json deleted file mode 100644 index 05e29b1a45..0000000000 --- a/docs/website/video-transcripts/gmqFd2bU_fM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 109, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 623, - "youtube_id": "gmqFd2bU_fM" -} diff --git a/docs/website/video-transcripts/gmqFd2bU_fM.txt b/docs/website/video-transcripts/gmqFd2bU_fM.txt deleted file mode 100644 index 38ae5fae0a..0000000000 --- a/docs/website/video-transcripts/gmqFd2bU_fM.txt +++ /dev/null @@ -1,109 +0,0 @@ -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/video-transcripts/h-I62aFYBNA.json b/docs/website/video-transcripts/h-I62aFYBNA.json deleted file mode 100644 index 4b44441b1e..0000000000 --- a/docs/website/video-transcripts/h-I62aFYBNA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 78, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 465, - "youtube_id": "h-I62aFYBNA" -} diff --git a/docs/website/video-transcripts/h-I62aFYBNA.txt b/docs/website/video-transcripts/h-I62aFYBNA.txt deleted file mode 100644 index 44bdccaf09..0000000000 --- a/docs/website/video-transcripts/h-I62aFYBNA.txt +++ /dev/null @@ -1,78 +0,0 @@ -with this last part we'll finally finish -the server code -post web service is even simpler than -user web service as it delegates to the -post service class which is smaller -the per service prefixes all calls with -post as opposed to the user web service -which used slash user -this class carries a one-to-one -relationship to the post-service class -we make no use of other services -the methods here can throw a permission -exception which as usual what will -translate to an error dao -the methods of the class translate the -business logic to uh web service as -before they are trivial for the most -part -list and feed just return pageable data -for the given user notice that every -method here accepts the auth header -when making a new post or comment the -dao object is passed along with the -header this allows for clean client-side -code that can be unaware of the -authorization for the most part -that's it the class itself is a trivial -wrapper around the service -class this brings us to the last class -we will cover in the server before going -back to the client the media web service -the media web service is as simple as -the post web service but has a few -gotchas due to the complexity of mime -types and file upload -the media is mapped to the slash media -base url -again the class can throw a permission -exception which will translate to an -error dial -now that we got the main boilerplate out -of the way let's look at the methods -this lets us fetch a public image or -media file it assumes the media is -public and so we need no authorization -the idea of the media is passed in the -url path -itself -we get the mime type from the media -entry in the database since media can be -literally anything -an all request will also return friends -only media entry -and is thus marked as all -other than the alt entry it's identical -to the public request -upload is a multi-part upload request -that accepts a file with additional -metadata as a multi-part request -multipart is a part of the http -specification that includes specific -structure -for submitting a file in a web-based -form -codename one has built-in support for -uploading files using multipart request -so this is pretty convenient solution -for file upload with that we are done -with the server -this has been a lot of work for the -server but most of it was simple -boilerplate -the server isn't hard technically in -fact it's simple and that's a bit boring -but we had to get through that in order -to get the more interesting pieces the -following lessons would be far more -interesting as a result diff --git a/docs/website/video-transcripts/hCjmHoktlrU.json b/docs/website/video-transcripts/hCjmHoktlrU.json deleted file mode 100644 index a69150318d..0000000000 --- a/docs/website/video-transcripts/hCjmHoktlrU.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 22, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-use-desktop-javascript-ports.md", - "status": "transcript-fetched", - "word_count": 960, - "youtube_id": "hCjmHoktlrU" -} diff --git a/docs/website/video-transcripts/hCjmHoktlrU.txt b/docs/website/video-transcripts/hCjmHoktlrU.txt deleted file mode 100644 index 06003d9554..0000000000 --- a/docs/website/video-transcripts/hCjmHoktlrU.txt +++ /dev/null @@ -1,43 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -On Windows an EXE or an MSI file can be generated as a result, this is determined by build hints. - -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. - -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. - -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. - -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 diff --git a/docs/website/video-transcripts/hwsWdhJHl8Q.json b/docs/website/video-transcripts/hwsWdhJHl8Q.json deleted file mode 100644 index 3063b2a4b3..0000000000 --- a/docs/website/video-transcripts/hwsWdhJHl8Q.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 377, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 5258, - "youtube_id": "hwsWdhJHl8Q" -} diff --git a/docs/website/video-transcripts/hwsWdhJHl8Q.txt b/docs/website/video-transcripts/hwsWdhJHl8Q.txt deleted file mode 100644 index 9d089ff1a9..0000000000 --- a/docs/website/video-transcripts/hwsWdhJHl8Q.txt +++ /dev/null @@ -1,377 +0,0 @@ -hello everyone in this session i want to talk about core concepts of mobile development -some of them are related to codename one and i'll go into that but most of them -are very generic and the reason for that is that sometimes -i see developers coming into codename one and into the mobile world and -suddenly hit with a realization that oh you can't do that in -mobile or you can't do that in a cross-platform way or this is only applicable to android and lots of other -things of that nature because they're not familiar enough with the restrictions and limitations and the -mentality that exists within this particular niche and here i'd like to talk about -some of these things i don't know if everything will sync in but to sort of give a sense of why -mobile is so different from desktop programming or server programming uh if those were the experiences you had -before and also if you just started out in mobile it gives you a better sense of -where the limitations are so the first thing i -want to talk about is density because density and device resolution -Density vs. Resolution -are well most of us know about them to some degree or another but even people who -know what density is i sometimes run into -the unintuitive nature of of this particular problem -so first density is the dots per inch or the points per inch -or pixels per inch it's essentially how close the pixels are to one another so if you take an -inch for inch instance uh from an iphone 7 and you look at it you you can count uh -literally 401 pixels if if you'll have a big magnifying glass or something -and you'll literally be able to count lots of pixels there because it has a relatively high uh -pixel ratio although nowhere near some of the android devices which go -above 600 and even more and -all the devices and even relatively new devices sometimes are as low as 100 -ppi and that initially doesn't seem like a big deal most of us are -obviously usually focused on resolution but here i give a relatively simple -example of two relatively new devices iphone 7 plus and the ipad 4. both of them are -relatively new the ipad 4 is almost twice the size of the iphone 7 -but they have almost the same number of pixels within them -and there is a difference there's a few hundred pixels difference obviously but -it's not as much as you'd expect there to be and the reason is the iphone is far more -dense than the ipad and the thing is that when you take a -device that's a tablet and you take a a device that's a phone usually people -expect the tablet to have far more pixels and that's not always true -now i didn't find a good graphic to represent density but i did find a great -graphic from open signal that you can see here that represents the number of resolutions that we have for common -android devices this is only android devices iphone devices are much simpler but -still the variety is astounding and -you really need to be resolution independent when you're working in mobile -and unlike web designs where you can say okay i'll design to a specific resolution and -people will just have lots of white space and a phone people won't accept that they want the ui to fit -so a lot of the cheats that we had on the web we can't do them here we need to -actually use the available space so -lots of the differences as i said uh can be very extreme -the if you take -a relatively low resolution image and try to show it on a very high -density device it will look very pixelated and that's something you just can't fix so you need high resolution -images the problem is that usually low dpi devices have less memory because -usually when we want to build a device we don't want to stick in too much memory because that brings up the price -that brings up the battery usage that causes a lot of cascading sort of effect -so uh these devices usually have less memory so they will really be hit if we have -too many high resolution images and also scaling down a high resolution image -doesn't look as good it doesn't look as bad as scaling up but you still get artifacts that are not -as bad but but still artifacts and that's not an option either -so there's two solutions essentially for -the density problem the first solution is the one that we use most often -because it's essentially the easiest all operating systems have a solution of this type all mobile operating systems -that have a solution of this type and that's to include images for -the various dpis and the way that it works is you give an -image for a high resolution dpi for high density dpi and -lower and lower and lower and each one is adapted in this and smaller and that way in runtime you request an -image and you get the right resolution image in codename one we have that we call it -multi images and we do it sort of seamlessly you just give us a high resolution image and we scale it -now on the device scaling we'll have artifacts but when we do it on the desktop we can run a very -uh refined algorithm that scales things with a very high quality -and you can't really do that in runtime on a device it will be too expensive -but on the desktop we can and uh and that when you get a multi-image you -can literally get very accurate -size accurately sized image and that's probably the simplest approach and the -one we usually take because we get resources that are images and it's really easy to work with that -there is another approach and that's vector images vector graphics -and those contain two big uh genres within -Vector Graphics -the first which we support relatively well is icon fonts and that allows you to essentially -if you think about it a lot of the icons are just a drawing -that's very similar to text but just with a specific shape -so we can there's this trend that's been going on for several -years of creating a font file that contains within it uh various -shapes logos of social network companies the share icon and all sorts of pretty -much any icon you can imagine and there's lots of great uh -repositories that contain many icon types like that and we have built in -the material design icon set from google which is free for any sort of use and that's why we -bundled it in so we can use it within uh our code to represent things uh -and have them appear beautifully with icons and everything by default -and that way it's actually pretty small one of the nice things about icon fonts is -that they are the file the entire material design icon font is 100k -and it contains quite a lot of icons and that's pretty cool we can offer -a relatively large amount of icons for literally no cost at all and just use the drawstring uh logic to draw it -and uh and it looks beautiful and it's anti-aliased and it's just perfect you can color it -based on the system colors which is one of the best uh aspects of icon fonts because -if we style a button and we say we want the foreground to be in this color when it's unselected and -that color when it's pressed and the icon font will automatically change the color uh -based on that state and that's really really really convenient so i love this approach and -we use it a lot the second approach is vector files now we don't support it at this time because -it's not as popular or wasn't as popular over the years the starting started -getting some traction in recent years as google added some support for -for svg files into their ide now no mobile device supports svg natively -they support it within the web browser but not for actual drawings so if you -you can open a png file you can open a jpeg file when drawing in the native code but an svg file won't work and the -reason for that is that it's kind of hard to render an svg file properly -because it contains a lot of information it's kind of bound into the -dom structure of a web browser and when you you start looking into all of the -complexities of the standard you your head explodes and you say okay let's just leave it for the web browser -so one of the things that google did which was something very smart and we wanted to do it too -is an ability to import an svg file and convert it into java source essentially -that you can compile as part of your application and then it draws everything and that's something -that we actually we have an issue on that and maybe by the time you view this in the future iteration it might already -be implemented but it's something that we don't have at -this particular time there is a disadvantage sometimes and the performance of -vector files because when we have an image file -it's just pixels and the device draws it really really quickly it just copies -pixels from one place to another and everything is already rendered and a vector file or -a future vector file of this type would be harder to draw at the same -performance level the benefit is that it's obviously smaller and it's very sharp for every -resolution so it's something that we we are still interested because the -price might be good for some use cases -uh the next thing i want to talk about is certificates in provisioning one of the harder concepts in coding one and in -mobile development in general and uh uh -it will be discussed more in the development process of -the boot camp but i want to go into details about -about this in big breaststrokes -because there's a lot to discuss here even without going into every single detail -or into the process itself of making them so to understand all mobile os's require -some form of signing by default there are sort of exceptions -but not really in in production -basically to to actually sell something to a store or pass it onwards or debug -it or anything you need to sign code to run on the device and uh -apple and google and microsoft have very different approaches to what -signing is why and why is it there so -google and microsoft look at signing as a way to protect your -app after it's been installed on a device so for instance -say you wrote an app and you upload it to google play -and you have a google play account you paid for it it's yours and tomorrow someone hacks your google play account -and he's in some foreign country you don't know who he is but he now controls your -google play account so great you can report him to google but what if during that time he uploaded -fake versions of your apps and essentially distributed them to all your users to the automatic update -and now all your users have their data wiped well -google isn't so much concerned about us as developers as much as it is concerned about all of our users -and they don't want that so what they they did here is that they did -a double solution here so if someone hacks your account that's he broke stage -one but to upload an app you'll need your certificate and your certificate essentially -is meaningless it doesn't say for real who you are it just says that -yeah i'm the same guy that uploaded the application initially so i can create my -own certificate and i'm just saying hey i can call myself bob it doesn't matter -but the thing is that once i upload an app to the store with a bob certificate -from this point on only the bob certificate can provide an update for that app -otherwise that app will not update so if you lose your google uh -certificate it's gone the app can't be updated anymore -and this is sometimes it can makes things complicated because -you know sometimes if you build a debug version and install it on your phone then a release version won't work -because they have different certificates so that's with the android version -so it's kind of a pain but it's a very -convenient approach because you don't need to go to google and ask them for a certificate -apple took a very different approach so with apple -they're giving you a certificate to indicate that you paid them to uh build -uh an app and the logic here isn't just a greed -it's also a verification process that way the the moment you pay them -money they can verify who you are to some degree and -in that sense they want to weed out the potential that you're a malicious player -at least not of a low level if you're a government operative they don't really have the ability to block that -but at least it will stop a script kitty who wants to distribute his malware through -the app store so it's powerful in that sense at least in -stopping the basic abuse but the thing is that if -with ios if you lose the certificate it's not a problem you know if -you can just wipe it the you can just essentially revoke it and -create a new certificate and you can submit every time now how does that work because an apple if you try it during -debug and you revoke a certificate and you create a new one and then you try to install on top of that it won't work -either well the thing is that when you submit a certificate to -itunes you have two certificates one of them you use during debug you use -the debug certificate to install applications on your phone during development but when you submit an app to apple you -use a distribution certificate now the moment you use a distribution certificate you can't install it on your -phone you can only submit that app to apple and -that app is reviewed by apple engineers and once they reviewed it they sign it -with their certificate and that's the one the device checks so when you install your own app from the -store it will be signed by apple as well as you and the thing is that when you want to -say replace it with your own copy during debug you won't be able to because the -certificates will collide so they have a similar approach to security as google has where -they check their certificates during install but the thing is that they take the responsibility -of what's the allowed certificate and i'm sorry i might have made it more -complicated than it is actually no it's already too complicated -to begin with it's not my fault i'm blaming apple completely -but generally you have two certificates you can use the debug version to debug -you can use only the release version only to submit to the app store uh -or for test flight which is similar to app store submission but i won't go there in this slide -in this session at the moment because that's too much uh -now the next uh aspect of this -Provisioning (iOS) -is the provisioning so we have two certificates we have a debug certificate -and a distribution certificate so but the thing is that those two aren't -enough we need two more files that represent provisioning -and they're essentially kind of the document that comes with the -app that says how that app may be used so -when during development apple has a special restriction where they say you can't install on more than -100 phones during development and the reason for that restriction is don't circumvent the app store -if you can install an unlimited amount of devices then you could theoretically just put -the app on your website and that's it and people will just go out and install it -so by limiting the install number to a hundred devices and not just that these -hundred devices you can't just add and remove devices willy-nilly every device you removed is sort of -ignored it still counts to your 100 and only once a year when you renew your certificate uh do they allow you to wipe -the slate and start in a clean situation -so the provisioning profile during debug includes that list of devices where -you're allowed to install and without your device being listed there you can't install onto your own -device so that's really important and the other -things that it includes all sorts of you know are you using a specific api like icloud like push like things like that -all of that is itemized within that file so you still need it even for distribu for distribution -and it includes all sorts of information that apple needs when they install when they test -when they deploy that application so and -the provisioning is also directly tied to the certificate so if you revoke the certificate -you need to re download a new version of the provisioning with a new certificate -uh the other way doesn't apply so if you make change to the provisioning you can just download the new version and the -same certificate will still work and that's useful -one thing that i didn't mention in the slides not sure if i want to go into it too -much but you can use the same certificate for lots of projects with -ios and also with google if you want to that that although that's a completely different debate -whether you should uh but in the case of ios -you can use one you should usually use one certificate for debug and one certificate for distribution for all -your projects and just generate provisioning for each -one because the provisioning would usually be different and that's more convenient obviously the -moment you you revoke or need to update the certificate because once a year you need to update the certificate from -apple uh then you need to go over all your projects and update that of -all the active projects with the new certificate and your provisioning and it's it's tedious -and the last portion i want to talk about is the push certificate and -Push Certificates (iOS) -well technically i don't really want to talk about it because i didn't mention push here which is a big complex subject -i want to talk about it separately but generally push allows you to send -data to the device the thing the reason i'm talking about that is that if we're talking about -certificates there's also these and ios also has certificates for push -now this is a huge i mean if we thought push was confusing on ios it just got worse -and i've seen so many people fail on on this particular -nuance uh so we have the debug push we have the distribution push but we also have two -uh uh push certificates uh now say debug push -debug certificate and distribution certificate we also have two push certificates one -of them for the sandbox and one of them for production and the reason we have those is -that when your server needs to send a push to a device and the way this works -is the server says i want to send a message to device x that's out there so -it just connects to usually with us to our server which is the codename one server but our server -goes and connects to uh the itunes server and tells it notify device x that such -and such happened so that server needs to authenticate me and for that it needs -a certificate that indicates who we are and that is also a certificate that we need -we need two of them because there's two push environments one of them is during the use during the bug which is the -sandbox and another one is used during a -run time with the production and -these certificates are completely different from build and during build we don't even know they exist -so don't confuse those two groups of certificates they're -completely different although the push certificates are mentioned in the provisioning so -yeah it's it's as complicated as that but once we actually go through that it's not as bad -once you do that a few times although it's error prone -so i just noticed that i mentioned sandbox there and i'm mentioning it here and here i'm mentioning it in a -completely different context and here i'm mentioning it as the application sandbox -Application Sandbox -so application sandbox is the way your app is sort of restricted -so when we install an application on a regular pc or at least a 1990s pc -that application could do whatever the hell it wanted because it was running on our system and it could -change files anyway and everywhere so in pcs and macs we kind of restricted -things a bit oh you can't go to this directory if you're a regular user application if you're not an admin if -you're not root you can't access this you can't access that well apps take it to a whole different level -apps are really really really restricted they're installed through randomly generated -directory names in some cases and -they can't access folders that aren't theirs essentially -so on ios there's literally no real shared space or ability to -normally communicate between apps or things like that it's it's very restricted so things like uh -that we'd expect to to exist when we're a desktop developer often just don't mean -and we need permissions to do all sorts of things -like access photos and cameras and things like that which i'll discuss soon -so one common thing that people constantly ask about is you know file -pickers or just go to their image folder and these sort of things just don't physically exist on devices -one of the things that does exist is for instance we have the open image gallery api -and you can essentially ask the system to pick a file an image file for you -and then it opens the native image picker uh gallery you pick an image and that image is sent back to your app -and initially it sounds really an awkward way to do it -but then you start thinking about it what if uh an app just goes into your image gallery and starts uploading all -of your images to to someone's private cloud and uh you start realizing that uh oh that's a -huge privacy violation so that's just impossible our apps are -blocked from even accessing the the image area -unless they have a very explicit permission to do so -and inter-app communication is very very limited one app can't access a folder of -a different app because then essentially if you have a banking app installed -it would be very heavily attacked by pretty much everyone trying to crack it -to get into that sweet sweet cache so obviously -there's a very uh strict sandbox between applications and -things like communicating between one app and another app is usually done through urls for intents -to other means it's always awkward we support some of that so for instance -you can just use a url to open a different application that works in ios and in android and often -is reasonably portable in in some cases -and you can do a lot of things uh with those sort of apis -Permission Systems -and as i mentioned uh the permission system allows you to request uh abilities and essentially get -them so for instance uh uh when we open a camera or -contacts the operating system asks the user and that's the operating system that needs to ask it not -not something that we're asking whether the user wants to allow the app to do it -and that is a very transparent way to do it historically android didn't have that it -worked in a uh by declarative permissions so essentially uh we'd write into the -manifest uh the set of permissions an app expects and uh during install you were prompted -with the list of permissions the app needed which was usually very unintuitive because you're looking at -this huge list of all sorts of things that an app uses and you have no idea -how that actually relates so now apps and android install instantly and ask you for permission and -runtime which is far more convenient you still need to list the permissions -in the manifest to support all the devices and one of the nice things in codename -one is that we automatically scan your code and automatically insert permissions into place -where it's needed so this is almost seamless to you and know that -it's a huge pain in standard android programming uh if -something you didn't ask for the right permission or something like that it's painful -so this works like that today anyway uh -background uh i think this is the last uh section -uh background is another thing where people expect things to work like they do on -Background Processes -on desktop and in this particular case also expect them to work the way it works in android -so this is one of those things that that changed a lot and that's -both os is sort of gravitated to more common middle ground and this is -something by the way that i've noticed over the years that ios and android started from very -different locations and both of them moved a lot toward one -another and converged on almost anything and you know ios fans say oh android -stole our ideas and vice versa both stole a lot of ideas from one -another extensively and uh -it's a good thing they're both they've both come a long way and improved as a result -so one of the examples here is that ios didn't have multitasking at all when it launched -steve jobs literally said it will drain the battery we don't want to do that and technically he was right -android had background processors and multitasking and everything and initially it didn't seem that bad -because the initial android phone had decent battery life but then once you started installing apps and -installing apps you quickly found out that oh well it doesn't -and the reason it doesn't is that often you don't really notice that an application is -still running in the background and if you kill it you actually create a worse situation in some cases because the -service isn't necessarily dead and the relaunch later might occur -and it's kind of problematic -so ios started by gravitating towards android by adding background processes -and unlike uh android that sort of allowed anything to run as a service in the background so -you could just write any bit of java code and it will run in the background and all is -good in ios they required you to define a set of uh one of -one use case out of a set of allowed use case use cases so for instance if you want to -track location gear location download the file play music or do all sorts of -background tasks like that you literally need to declare that about your application -and a guy at apple during review time would actually look at that and say okay -the particular app in question warrants that so for instance if you -have a game that's completely unrelated to background -location and you say oh wait i want to track background location and -just don't tell don't tell anyone why -that might raise a flag during review because they want an actual explanation -of why you are tracking background location and -that's useful otherwise all apps would just -use that and waste battery obviously but also potentially violate our privacy -so there's good in that to some degree android 6 -made a lot of improvements to battery life and one of those improvements is that it curtailed some of the usage of -these background services so if you're using android 6 you will -notice that some things sometimes when you get back to your phone it kind of takes a while to -get going again and the reason for that is exactly this it sort of stops background process in -the track and it has a system that's closer to the way that ios works with -uh specific use cases and things like that so it can -not waste as much battery when it's off -so we support a lot of these use cases and we work in a way that's -more similar to the way ios works which makes more sense for them from the portability standpoint -and uh well but but still background processes are -harder to get uh to work properly across platforms -because obviously the vm isn't fully running and all sorts of other things like that -it's complex so if you're thinking about app functionality that relates to things -running in the background uh do your research uh because it's a very -uh tricky uh part of mobile development -so there's a lot i didn't cover i sort of uh went over the basics here -What did we learn? -i don't know if you knew the things i covered or didn't -but i hope i did give you a bit of sense of -some of the less intuitive things that we have here -it's i think app development when you come to it from the server side from desktop -initially it seems very familiar especially with codename one which is a very familiar program framework for -desktop developers and sometimes that's misleading and i hope -you are less misled by that and you get a better sense that -uh how different this is from desktop development -and uh if you didn't get by my awkward explanation of ios certificates and -signing just how painful ios signing is then trust me you'll get it when you -actually try to go through it and seriously people -guard your certificate keys for android because if they're gone they're gone -and i've heard the stories there is no work around google itself can't fix it -commit these certificates to your versioning system -and make sure you have them backed up before you submit your first application to google store otherwise -i don't want to talk about otherwise that he shouldn't be gone ever -so keep it safe that's the final word on that -and thank you i hope you've enjoyed that uh this presentation diff --git a/docs/website/video-transcripts/hys6Rpkru50.json b/docs/website/video-transcripts/hys6Rpkru50.json deleted file mode 100644 index 8e976a7954..0000000000 --- a/docs/website/video-transcripts/hys6Rpkru50.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 95, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 517, - "youtube_id": "hys6Rpkru50" -} diff --git a/docs/website/video-transcripts/hys6Rpkru50.txt b/docs/website/video-transcripts/hys6Rpkru50.txt deleted file mode 100644 index f5ad681a1a..0000000000 --- a/docs/website/video-transcripts/hys6Rpkru50.txt +++ /dev/null @@ -1,95 +0,0 @@ -i'm continuing where i left off with the -show user edit form method -a quick reminder this is the final -output of the show user edit form -i want this form to be low maintenance -as user properties would be added -frequently -and this might become a bottleneck -the solution for that is instant ui -which automatically creates user -interfaces from property objects -there are obviously constraints we need -to adhere to -so the ui would be usable -we'll discuss those -these properties don't fit into the ui -notice birthday uses a long value -instead of date -and so it's hard to map to the ui -when i implemented this i thought about -converting the birthday from long to a -date object but eventually decided -against that -dates are very flaky as java implicitly -includes time zone -and we naturally think of them as -calendar dates -birthday is a perfect example of this -serialization via json with date objects -is painful so i wanted to avoid that -the solution is a bit convoluted -but it works -i added a metadata entry and hid it from -the serialization code -i'll discuss that later when we go over -the changes to the user object -the gender string is a multiple choice -option -so we have the labels and values for the -choices so the right ui is shown -this creates a container that lets us -edit the content of the me -object -i add some padding and make the -container scrollable -since it contains text components and -they must have a scrollable parent -instant ui implicitly binds the ui to -the user object -so we unbind when leaving the form -when we save the changes asynchronously -this creates a dynamic editable form -one of the cheap chief benefits here is -that the generated ui can be improved -seamlessly with no change to your code -this won't create the image from before -though -since the property names don't have -spaces -and -right -casing this won't look like that we also -need to add a birthday property to match -that ui -this is a meta property that looks like -a date field but delegates everything to -the actual birthday property -we also add the birthday date property -to the index -the -display labels for every property are -set here -if we didn't do this we'd see the -property names instead -the birthday date would be ignored from -json map code so it won't mess -with the other logic -there is a bit of css we need to -implement for the entire settings form -this is the white border surrounding the -avatar -we need some padding to make the border -visible -the margin on top pushes it down so it -will peek below the cover image -the camera circle is black over gray -with a small enough size so it won't -intrude -we want the title area to have margin at -the bottom so the avatar image will peak -out -this is a big enough label for the name -of the user below the -the avatar image -with that settings are done diff --git a/docs/website/video-transcripts/iCY64ThCleI.json b/docs/website/video-transcripts/iCY64ThCleI.json deleted file mode 100644 index bd9c6f3b8c..0000000000 --- a/docs/website/video-transcripts/iCY64ThCleI.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 403, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 2311, - "youtube_id": "iCY64ThCleI" -} diff --git a/docs/website/video-transcripts/iCY64ThCleI.txt b/docs/website/video-transcripts/iCY64ThCleI.txt deleted file mode 100644 index 31b92fd9fa..0000000000 --- a/docs/website/video-transcripts/iCY64ThCleI.txt +++ /dev/null @@ -1,403 +0,0 @@ -brace yourselves a bit -server programming isn't as exciting as -client-side code -it includes lots of boilerplate code and -some complex concepts -please bear with me -facebook back and server logic is -remarkably complex -what i built took roughly a day of work -maybe a bit longer with debugging -i cut a lot of corners to get this -working especially in the complex data -driven aspects some features such as -search will be discussed later -this seems like a lot to swallow but -this architecture is remarkably simple -once you start looking at it -we have four big pieces -web services -these are thin layers that provide a -standard rest for api -they encapsulated -in the rest controller classes that -expose the json web api -services provide a generic -implementation of the underlying logic -so it isn't mixed with web service -specific adaptations -service beans abstract the business -logic -and jpa from the web service -this means -in the future we can port everything to -use something like web sockets -and we use 100 of the code -within the service class -dao -data transfer objects -are used to move data from the storage -layer -all the way to the client -spring boot can translate a dow object -to json -in a response and create a dao from json -jpa entities -we use jpa to access the database and -query slash right into it -this includes both the entities and the -crude repositories -this architecture lets us mix and match -pieces as we evolve -i chose to use web services for this app -but tomorrow we could migrate the code -to websockets and reuse most of the code -as all the pieces from the services -layer onwards wouldn't need to change -the same is true for the database -replacing mysql -should be trivial as jpa can work with -any sql database -however you could go even further and -replace the entire way data is stored -for instance with a nosql database or -with direct sql access -since the data is hidden from the user -this can be accomplished while leaving -major portions of the code intact -to understand how this works let's -review a small sample of the new user -creation logic -request arrives to the server as a web -service -it then passes between the layers -initially the request comes in as a user -dao object containing the attributes of -the user -the response is a new user dao object -that includes additional attributes -specifically the user id -and security token -Spring Boot -i'm assuming you have mysql installed -and ready to use -so the next step is the new spring boot -project -we will need the following features from -spring boot jpa jersey security web -services and mysql -for completeness this is the palm xml -file dependency section -notice i'm using spring boot 2. -we need jpa -jersey allows us to automatically -marshal objects to slash from json -security allows us to hash our passwords -we obviously need web services support -we also need the mysql jdbc plugin -we might as well add two additional -dependencies this is a simple rest api -which we'll use to communicate with -mailgun to send emails -twilio allows us to send sms messages -for device activation -now that all of that is in place let's -proceed to the code -my preferred place to start when working -on server code is the database -i might start with the web service if i -have an api in mind but i usually pick -the database as it's easier to quantify -but before we go there we need to add -some boilerplate -assuming you've created your app with -the initializer you would have this -class -if you don't have it then you need this -class as it's the entry point for your -application -next to it we can place the security -configuration class -which handles basic security boilerplate -as such -here we disable some security -protections in spring boot -these make sense for browser clients but -not so much for native mobile clients -where we might need to jump through -hoops to get things working -this is the password encoder that we -will use later to hash the user -passwords -as i mentioned before -the best place to start a server -implementation -is the data structure i will skip the -database schema and instead discuss the -object entities that we need and how -they can serve us -the nice thing about jpa is that it can -automatically create the schema for us -which is really useful for fast -prototyping -the most obvious object to start with is -user -the user class is an entity that handles -the data of a person using the app -facebook obviously holds far more data -over each user but this should be pretty -simple to extend -before i go into the code there is one -concept i'd like to discuss first -unique ids also known as primary keys -a very common concept in databases is -the usage of an auto increment unique id -this means a database would -automatically generate a numeric primary -key for us matching every user -this is a really cool but also -problematic notion -if our ids are numeric and sequential -that means we can't really use them -outside of the server -if we expose them to the outside world -someone can just scan all our users -numerically -a far better approach is a string based -long random id -which we can generate with the -uuid api in java -the chief value of using this as a -primary key is performance -since queries based on primary key are -practically free -it makes a lot of sense to expose the -primary key to the client side -another advantage is database -flexibility -it would be possible to migrate to a -different database type in the future if -we use this approach an auto-generating -strategy can cause a problem as we try -to move live data from one database to -another -User Object -now that we got that out of the way -let's go to the code you will notice -that the user object in the server is -very similar to the user object in the -client -there are some differences but -essentially they are they correlate to -one another -we'll start with the field declarations -this is a string id as i mentioned -before notice it lacks the auto-generate -annotation you might see in jpa entities -most of the other fields are simple -persistent fields with the exception of -email and phone where we demand -uniqueness from the sql database -to verify an email or phone we send out -a code and store it here -if we had a memory db such as redis or -memcached we'd use that -but it's not a big deal to use the -database for this at first -a user might change his email or phone -after the fact so we need to maintain a -reference to the value -we verified -since dates and java are technically -timestamps we need to explicitly state -the sql data we need here -we store media files such as pictures in -a separate media entity -we'll discuss that entity soon -we have three relations to other users -for friends -friend requests and people you may know -the field stores a hashed version of the -password which is encrypted -it's never exposed to the user passwords -in the database are hashed and salted -this is handled automatically by spring -as we'll see soon enough -hashing is a form of encryption that -only goes one way for instance if my -password is x y zed -i can hash it and generate a value that -looks completely random -i can't decrypt it ever again -however if i know the password is xyz -i can verify it against the hash -salting means random data is inserted -into the hash to make it even harder to -break the hash -the token is -a special field that allows us to edit a -user we expose it only to the logged in -user and he can use that token to edit -the -data we have a unique id for every user -but we don't use it for write operations -our id is public knowledge so if a user -needs to refer to my user object he'd -use my unique id -this is efficient and accurate since ids -never change they are primary keys -when a user logs in we provide the token -so only the user can update his own data -this means the password isn't stored on -the device and a token can be updated -slash revoked -it's also long enough and random enough -which isn't always the case for -passwords -naturally tokens can't be primary keys -since tokens might need resetting in -case of a vulnerability and primary keys -are forever -if i was super concerned about security -to -a paranoid level i'd encrypt the tokens -in the database in the same way we -encrypt passwords that would mean we -would need to give different token a -different token to every device since -hashing is a one-way street -naturally that's a pain to handle so i -avoided it here -we initialize the primary key in the -constructor -this will be overridden when loading -from database but makes sure we have a -unique id when saving a new user -these methods check the list of friends -to see if a person with the given id or -token is in our friend list we'll use it -later -codename one on the client side doesn't -support java 8 streams at this time -they help writing some complex ideas a -bit more concisely but we find them hard -to compile to efficient code for ios -on the server this isn't a problem -here we have another case of a stream -with a for each method -this is pretty easy to explain with the -block above -the stream code is roughly identical to -the standard java for loop -this method handles conversion of lists -of users to list of user dao -this is a common practice as we get a -lot of those -we'll use the full name a lot in the -code so it makes sense to have this as a -helper method -the birthday can be null so we need to -check before converting to a long value -here we create a dao object matching -this user i'll discuss the dao in more -detail soon -but as you recall from before -we use it to transfer data to the client -notice that no private information is -passed when the dial is created not even -the friend list no password token etc -we pass the -auth token here but not the password -this method is invoked when a user logs -in and returns -to the user his own data -looking over the rest of the code you -will notice -that the rest is just a lot of -boilerplate setters and getters -there is nothing interesting here -but we need this for jpa to function -properly -these getters and setters are made -through id refactorings so i didn't -write them -DAO -before we move to the methods i'd like -to discuss the concept of a dao -a dao stands for data access object this -is a conceptual idea there is no dow api -or requirement -you can skip it entirely -however it's a -very common best practice -when working with back-end systems for -instance in our application we have -three layers -web services the user-facing code -services the back-end logic -entities jpa the database -the roles are clearly separate -that's important as it means we can -replace or change one layer -significantly without impacting the -others -for instance we can move to websockets -replacing the web services layer -or we can move to nosqldb and throw away -the entity layer -so how do we transfer data between the -layers while keeping them logically -separate -enter the dow objects they aren't -entities entities are too close to the -data and are hard to modify those are in -place simply to pass along the data -the cool part about daos is that -springboot can automatically convert -them to json when sending a response -from the web service and automatically -create a new instance from json when -receiving a call -we could just pass the entity itself but -that would break the separation of -layers and might inadvertently expose -private data to the client side -so for the user object we have a similar -user dial equivalent -the fields are almost identical to the -fields of the user object -you will notice that even the relations -are dao objects -notice that the class includes private -data such as password and oauth -as you might recall from the user object -we never pass password into the dial and -it's hashed anyway so why do we need the -password in the dial -when the user is created or updated the -password value can be set the dow is -sent from the client side too -and that value may come from there -the token is returned in the dao once -after login or create -the dao must include a no arg construct -constructor so it can be instantiated by -spring boot -we also have a convenience constructor -for our use -the rest of the code is all -automatically generated getters and -setters -the one last missing piece for the user -object is the user repository interface -which allows us to query the user -objects -the implementation of these queries is -generated automatically by spring boot -based on the names of the jpa fields -ignore case is a special case keyword -for spring which works exactly as one -would think -with that we have the first entity and -the data area -and the basic spring boot server diff --git a/docs/website/video-transcripts/jR7_OTg-aG0.json b/docs/website/video-transcripts/jR7_OTg-aG0.json deleted file mode 100644 index f65b631d11..0000000000 --- a/docs/website/video-transcripts/jR7_OTg-aG0.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 27, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 157, - "youtube_id": "jR7_OTg-aG0" -} diff --git a/docs/website/video-transcripts/jR7_OTg-aG0.txt b/docs/website/video-transcripts/jR7_OTg-aG0.txt deleted file mode 100644 index 4e4728187d..0000000000 --- a/docs/website/video-transcripts/jR7_OTg-aG0.txt +++ /dev/null @@ -1,27 +0,0 @@ -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/video-transcripts/jVuNrLw4e-A.json b/docs/website/video-transcripts/jVuNrLw4e-A.json deleted file mode 100644 index 65df906081..0000000000 --- a/docs/website/video-transcripts/jVuNrLw4e-A.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 199, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/017-the-native-interface-callback.md", - "status": "transcript-fetched", - "word_count": 1011, - "youtube_id": "jVuNrLw4e-A" -} diff --git a/docs/website/video-transcripts/jVuNrLw4e-A.txt b/docs/website/video-transcripts/jVuNrLw4e-A.txt deleted file mode 100644 index c94f5f4af9..0000000000 --- a/docs/website/video-transcripts/jVuNrLw4e-A.txt +++ /dev/null @@ -1,199 +0,0 @@ -as i built this app -i took the code i used within it -and created a cn1 lib -that implements the native interface -described here -so a better way to implement braintree -support would be to use the official -braintree cn1 lib -however this is still useful -as an educational tool explaining both -braintree and how to map a native -interface -braintree is a pretty simple api on the -client side -as most of the -charging process is handled on the -server side of the api -the reason for this is that the client -side is far more hackable -even in a native app -in order to work with the api we start -with a token -which we can get from the server code -and then perform the charge process -which is handled most mostly seamlessly -by the library api -if you go to the braintree online docs -and look at the integration instructions -for native android code you will find -this code -this is native android code for making a -request -the token is something we need to get -from the server although conveniently -the braintree site provides a helpful -debugging token we can work with -this can fit right into our native -interface -but first i want to review the native -api to decide what sort of native calls -we would need -notice that even though a view object is -passed -it isn't really used and that a token -value is needed even though it isn't -passed -moving to the ios side the objective c -code isn't as readable for java -developers -so let's review this in pieces -this is a method declaration that -accepts -one string argument -technically in objective c -these are messages not methods but i -won't go into subtle language -differences -here we allocate a request -notice that this is the equivalent of a -new call in java -the init entry is a method invocation of -init -methods are invoked using a square -bracket syntax -init is a special case method in -objective c -and is usually used as a constructor -this is really an interesting block -so i'll split the block itself into -several -pieces that's simple it's just code that -allocates the drop-in controller not -much to it -this is like java's new however you will -notice that the brackets aren't closed -because the init method -or the constructor -should be invoked -the init method here is special -it accepts the token as an argument -as well as the request -notice two interesting things -the client token is just passed -but request is written twice -in objective c arguments are named that -means that you need to type the name of -every argument -to a method -and you can have two methods with the -same name but different argument names -to make matters more confusing the first -argument is a special case that doesn't -need a name -so -we have one argument -followed by the other arguments each -containing the name of the argument -and then the value -so the request argument name and value -make sense -but the last argument might seem odd -handler is an argument that accepts a -lambda expression with a callback -that callback -accepts several argument types -and will be invoked asynchronously -when a purchase -produces a result -the lambda expression itself -checks if the arguments represent an -error cancellation -or success -this code should be pretty familiar to -java programmers -the one weird thing is that objective c -strings start with the at character -which i won't go into here -the final piece of code is the present -view controller -which -should be -which shows the ui -notice it happens before the lambda -expression is invoked -as that is a callback -also notice we use the objective-c -keyword -self -which is the objective-c equivalent -of java's this keyword -in this particular case self is assumed -to be a view controller -so now that we've reviewed everything in -the method -it's pretty clear that the big thing -that's needed from the java side of the -api -is the token and not much else -this is something we can represent with -this simple method -once we implement that in the java side -we can use generate native stubs to -generate stub code for the various -native platforms and fill up that stub -code -one important piece that's missing from -the native interface is the callback -code -we need to monitor callback events so we -can know if a purchase succeeded -or failed -a more common approach would be to pass -the callback object instance -to the call -but here i use static callback code -the reason for that is simple -native interfaces have a lot of -restrictions on them and one of those is -that we can't pass arbitrary object -types -the reason for those restrictions is -simplicity and portability how would you -represent an object instance and the -objective c mapping -to implement callbacks -we use static methods -and some platform-specific code -calling the native interface directly is -often a bad practice -native interfaces are often fragile and -should be wrapped in an abstraction that -hides potential platform issues for -instance -if the ios and android native interfaces -have different features we might be able -to hide or abstract it in the java side -which is normally much easier to do -than implementing native code -in this code -we even have an attempt at fallback code -based on the javascript port of -braintree -it doesn't work all that well but that's -mostly because i didn't pay that much -attention to it -this class is mostly trivial sense the -native interface is trivial too -notice the code at the bottom that -invokes the static callbacks -this is a special case for ios -where the vm strips away unused code -by writing this com -this the compiler can't tell that the -code will never be invoked and will keep -it in place -notice that even making a seemingly -small change like making flag into a -private variable -might break this diff --git a/docs/website/video-transcripts/jhPep9vHv_U.json b/docs/website/video-transcripts/jhPep9vHv_U.json deleted file mode 100644 index 2bff7f7bad..0000000000 --- a/docs/website/video-transcripts/jhPep9vHv_U.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 109, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 558, - "youtube_id": "jhPep9vHv_U" -} diff --git a/docs/website/video-transcripts/jhPep9vHv_U.txt b/docs/website/video-transcripts/jhPep9vHv_U.txt deleted file mode 100644 index 7506f20c9d..0000000000 --- a/docs/website/video-transcripts/jhPep9vHv_U.txt +++ /dev/null @@ -1,109 +0,0 @@ -finally we'll discuss the side menu and -preview functionality -two relatively big pieces of code -getting side menu navigation ui to look -good -has -many nuances and pitfalls -the specific ui includes some small -details including a different color to -the selected entry representing the -current form -a couple of points i'd like to highlight -are first -the large amount of padding -three millimeters on every side -that is very important for the feel of -an application don't be stingy with -padding the one pixel -bottom margin creates the effect of a -thin white line -separating the side command entries -and looks like a border between the -entries -going over the code we can see the image -at the top of the side menu is the same -one -in the main form -it wouldn't matter if it was a different -image -might actually look better -i use the logo icon mostly to give the -ui the right feel -otherwise the sign menu feels bare -the same is true for the tagline i use a -special ui id with some padding to space -it a bit from the bottom -without this tag line the side menu -would look empty and weird -here is an interesting trick normally -side menu commands have the side command -style -however i wanted the current form -command to be darker -so i -check what's the current form -and use the uiid attribute of the -command to determine the ui id -to -selected side command which happens to -look exactly like the side command's -pressed -style -but there is a problem -since the commands use -a material design command -icon it's determined before the ui id -has time to take effect and the icon -would have the lighter background color -the hack -is to use -the pressed icon for the icon as it will -look exactly like the selected side -command in this case -the last feature in this module -is preview -we can preview the app by pressing the -play button -and the trick is that we literally run -the restaurant app in place -we only modified it slightly -by adding an exit button to the side -menu -i copied the source of the restaurant -app -instead of making it more generic -it's a lazy solution -but i am lazy and proud of it -if the need arises i'll refactor -everything and build a -shared code base -but only when the effort justifies -itself and not a moment -sooner -to get this to work i had to rename the -theme.res file and the builder app so we -won't conflict with the theme of the -restaurant app -i did copy the sources -and did a few minor hacks in the code -but most of that could be generalized in -the future -i stepped out purchase.java as the -native interface portion would have been -hard to replicate -and it's not something i'd want in the -preview -showing the preview literally means -creating the main menu form from the -restaurant app and setting the theme -from there -then showing the first form -exiting means the reverse of that -with the now renamed builder menu -there are more efficient ways of doing -this but this is the most simple and -consistently reliable way -i found to accomplish this functionality -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/kfKsVAx659s.json b/docs/website/video-transcripts/kfKsVAx659s.json deleted file mode 100644 index 4ba18b4737..0000000000 --- a/docs/website/video-transcripts/kfKsVAx659s.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 48, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 273, - "youtube_id": "kfKsVAx659s" -} diff --git a/docs/website/video-transcripts/kfKsVAx659s.txt b/docs/website/video-transcripts/kfKsVAx659s.txt deleted file mode 100644 index 5def3f20c1..0000000000 --- a/docs/website/video-transcripts/kfKsVAx659s.txt +++ /dev/null @@ -1,48 +0,0 @@ -the more tab is effectively all the -missing functionality facebook removed -when it eliminated the side menu ui -we won't implement most of this stuff -with the exception of user settings -which i'll do later -to keep the code short i added a static -import of the font image class so i can -reference material icon names directly -the code itself is trivial -we use container again and not infinite -container which is necessary in this -case we also have some padding all -around and activate scrolling which is -important the me button will lead us -later to the settings ui -right now the css api -doesn't support line borders -yet -so code handles that -every button icon has a different color -slash icon with roughly the same ui -so a generic method to create the button -can solve -that generic method just generates a -multi button -with the proper border color to create -the circle effect on the icon -there are quite a few ui ids here but -most of them aren't custom ui ids they -are built-in ui ids from multi-button -multi-line one represents the first line -of a multi-button -the avatar of the user we want -uh -for for the avatar of the user we want -more margin -so the text will be better positioned -the second line of the multi button is -gray and noticeably smaller -the circle icon itself has noticeable -padding and a large font size for the -icon -with this the mock-up of the main ui -should work and run -there is one last piece we need to go -through before going into the server -code diff --git a/docs/website/video-transcripts/kjUFJro0yUQ.json b/docs/website/video-transcripts/kjUFJro0yUQ.json deleted file mode 100644 index 40ce68f6a2..0000000000 --- a/docs/website/video-transcripts/kjUFJro0yUQ.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 283, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1452, - "youtube_id": "kjUFJro0yUQ" -} diff --git a/docs/website/video-transcripts/kjUFJro0yUQ.txt b/docs/website/video-transcripts/kjUFJro0yUQ.txt deleted file mode 100644 index 06ffbc6909..0000000000 --- a/docs/website/video-transcripts/kjUFJro0yUQ.txt +++ /dev/null @@ -1,283 +0,0 @@ -security is a huge topic -that we can probably talk about for ages -for now i'll try to limit myself to the -basics -in this first part we'll discuss the -basics of security and certificate -pinning -Security Basics -before we begin i'd like to make a short -disclaimer -security slows stuff down and sometimes -makes your app more cumbersome the -question of why we didn't solve security -is a question of usability -a lot of core concepts and programming -would become harder and less efficient -this would also impact usability to the -user who often is the biggest -vulnerability there is -throwing permission prompts at users is -problematic -it triggers permission fatigue and users -tend to ignore these prompts at some -point -for programmers things as basic as -mutable state -can often cause vulnerability and code -removing security weaknesses -is pretty difficult -if you are still with me -let's start with the basics and common -terms -a term you should all be familiar with -is vulnerability -we hear that term a lot when discussing -security but it's a bit confusing -sometimes -a vulnerability is a security bug -but that doesn't mean anything all -software has vulnerabilities because -it's so hard to avoid for instance if -you have a method that returns a java -array -your code is probably vulnerable -a caller can change the element in the -returned array and impact internal state -working around this vulnerability by -creating a new copy of the array will be -expensive -most of us are willing to risk security -for extra ram and cpu power an exploit -converts a vulnerability or a few -different vulnerabilities into a hack -so if you offer a banking sdk and return -internal state in an array i can then -change that internal state and hack the -sdk -that's an exploit -there are different grades of exploit -and -sometimes you will need to chain them -the worst kind of exploit is a remote -root exploit -a remote root exploit is a vulnerability -you can use for full control of a system -without physical access to the machine -that's the worst kind of exploit and -it's pretty rare -usually security works in layers with -penetration of one layer -isn't enough you would still face -another layer below it -so let's take the example of returning -internal state data -and continue with that -let's say one of the objects in the -array has an os native method -let's say that method -has a vulnerability that allows us to -step outside of the virtual machine -the os -should have restrictions too -so even if we can step outside of the vm -we still be in a sandbox -let's say the os has a user escalation -vulnerability where the attacker can -elevate his privileges to root -so something small like returning an -array can become the missing piece in a -big attack -this isn't common as such -vulnerabilities are usually fixed by -vendors -our chief concern as developers is the -safety of our app data -and access -more than device root exploits -codename one tries to be as secure as it -can be by default -some of our design choices make codename -one more secure by default -even when compared to native coding and -especially when compared to other -approaches -the first is obfuscation by default -codename one obfuscates its code -this isn't a complete -roadblock to a hacker a diligent hacker -can get around obfuscation -still it makes reverse engineering -an application -harder -obfuscation is especially hard to deal -with in codename one -even compared to native code -both android and ios -store ui data in readable formats -reverse engineering tools can decode -these uis by default -a hacker can point at a button and find -the code that binds to that button -this undermines obfuscation and native -applications -in codename one even if you use the gui -builder the ui compiles to bytecode -and thus to native -so the ui gets obfuscated with the rest -of the business logic code -this means you will have more code -but no readable layout files -reverse engineering tools will have -nothing to go on with codename one -even when tools adapt to codename one -this is unlikely to change due to code -obfuscation -in that sense codename one is even more -secure than a typical native app -think about the way a typical hacker -will crack your application -let's say you have a login button -and the hacker wants to grab the -username and password -the hacker can upload a hacked version -of your app to the store and grab the -credentials from a fake app -users of the fake app will be oblivious -to the steel -as the app will look and behave the same -way -the first step to hacking an app this -way -is to find the button and fields code -in a native app this would be doable -as both would be in the xml -so -the hacker's job is trivial -for a codename one app this would be a -tedious process of digging through -obfuscated code -finally release builds -add a few layers of default security for -instance blocking remote debugging etc -this is especially true for android -Certificate Pinning -as i mentioned before we try to be -secure by default however some potential -vulnerabilities -would impact usability too much the -first protection system i'd like to -discuss is certificate pinning -certificate pinning thwarts a man and -middle attack on ssl -to understand this -we need to understand -ssl and man in the middle attacks -when we connect -to a server -there are many servers along the way -for instance if your user is in an -internet cafe -and a hacker hacked the wi-fi server -when he connects to your server the hack -can spy -and even change all of the communication -we use ssl or https to encrypt all the -data -and this works by sending an encryption -key from the server to the client -what if the hacker sends his own -encryption key -that's why we have a certificate -authority -you can create a certificate for -yourself -you need an authority to verify your -identity -for instance i can buy a certificate -from very sign who will verify my -identity -that way the client knows the -certificate can be trusted -the problem is that certificate -authorities got hacked in the past -so it's possible a hacker would get a -valid trusted certificate -in that case he can execute a -man-in-the-middle attack -this scenario is pretty challenging -a hacker would need to get in the middle -he would also also need a valid fake -certificate -this is a ca a case that would be -possible in the age of government -sponsored hacking -if you are building an app for the -government -or financial institutions -you should be aware of this -in those cases you should use -certificate pinning as an extra layer of -security on top -certificate pinning verifies a -certificate signature of the server -we can thus verify that the certificate -we are communicating with is a specific -one -the downside for this is updates if we -update the certificate on the server we -need to update the installed -applications they might stop working or -start warning users -in valid server update situations -Connection Request -the connection request class contains a -check ssl certificates method -that accepts an array of the -certificates supported by a secure -connection -you can verify that a signature of a -server -matches a specific signature -that you expect -and that way prevent a potential exploit -you can loop over the array of -certificates and verify that a proper -certificate is in place -the certificate -unique key you get in the simulator -should match the one you have in -production -the drawback of the security layer -is in flexibility -normally we can just replace the server -and set up a new certificate -as long as the certificate is valid this -is pretty seamless -but with pinning we need to jump through -a few hoops -that might mean that if you update the -server apps -that are installed on a user device will -stop working -another important bit -is the algorithm used -most servers support multiple signing -algorithms for maximum compatibility -however -algorithms such as -sha-1 are already long in the tooth -sha-1 -can be defeated with modern hardware -and it might be better to pin a stronger -signature -such as sha 256 etc -every certificate has an algorithm -associated with it -and you should pick the best option -notice that you shouldn't depend on the -number of certificates -or their offsets -as this will block the flexibility of -the server team -to make changes diff --git a/docs/website/video-transcripts/l1TPKuHYr9c.json b/docs/website/video-transcripts/l1TPKuHYr9c.json deleted file mode 100644 index 3ceae8c3bb..0000000000 --- a/docs/website/video-transcripts/l1TPKuHYr9c.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 76, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/034-running-the-kitchen-sink-in-the-simulator.md", - "status": "transcript-fetched", - "word_count": 444, - "youtube_id": "l1TPKuHYr9c" -} diff --git a/docs/website/video-transcripts/l1TPKuHYr9c.txt b/docs/website/video-transcripts/l1TPKuHYr9c.txt deleted file mode 100644 index 02d01a7014..0000000000 --- a/docs/website/video-transcripts/l1TPKuHYr9c.txt +++ /dev/null @@ -1,76 +0,0 @@ -now that we have the layer of the land -let's start building things specifically -the kitchen sink demo -first we'll build cldc11 as it's pretty -trivial -after that we should have -this slash cldc11 cldc11.jar -next we'll build the library itself -we now have codename one compiled notice -a pattern here -it's relatively simple at this stage -next we want the simulator so we will re -repeat the same process for the javasc -port notice i invoked and jar twice this -isn't a mistake the first time copies a -skin file into place -but does that a bit too late a second -time generates a proper jar -we should now have codename1.jar -from the codename1project this directory -and the javasc jar from the javasc -project this should allow us to build a -hello codename one project -the catch and sync is a simple yet -comprehensive demo that reviews a wide -range of the codename one functionality -it's a good candidate to compile so we -can start by pulling it from git -alternatively you can download the -zipped url from github -a codename one project is a standard and -project which we can build and run -similarly to other and projects -notice that we are missing the codename -one build client.jar -unless we use the codename one ide -plugin -this is the jar that effectively -implements the build server -functionality in codename one -if you use the codename one plugin you -can just copy it from another codename -one project -however if you don't intend to use the -build servers at all you don't really -need it -since ant is sensitive to missing -dependencies you will need to remove the -codename one ant tasks from the file -notice that this is only necessary if -you don't have codename one build -client.jarrow -the file is really simple and doesn't -include many targets most of the targets -included in the file -are included in the file nb project -slash build example.xml -which netbeans defines -this is just a bit of guarding against -compilation issues i included the full -file in the resources for this module -finally in order to compile the project -we need to copy the following files into -the kitchen sync directory and run and -after this is done you can now run the -project in the simulator using either -and or the manual java command -you will notice we are running the -simulator class not the main class -which in the case of the kitchensync -project is called com.codnam1 -the simulator loads the class -dynamically and handles the device skin -that class and package are defined in -the codename one settings -dot properties file diff --git a/docs/website/video-transcripts/l97sDefhHMM.json b/docs/website/video-transcripts/l97sDefhHMM.json deleted file mode 100644 index 54f8001781..0000000000 --- a/docs/website/video-transcripts/l97sDefhHMM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 143, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 800, - "youtube_id": "l97sDefhHMM" -} diff --git a/docs/website/video-transcripts/l97sDefhHMM.txt b/docs/website/video-transcripts/l97sDefhHMM.txt deleted file mode 100644 index 32a2270db8..0000000000 --- a/docs/website/video-transcripts/l97sDefhHMM.txt +++ /dev/null @@ -1,143 +0,0 @@ -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/video-transcripts/m43A68sNcvg.json b/docs/website/video-transcripts/m43A68sNcvg.json deleted file mode 100644 index 971e261714..0000000000 --- a/docs/website/video-transcripts/m43A68sNcvg.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 82, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/031-hello-world-and-devices.md", - "status": "transcript-fetched", - "word_count": 435, - "youtube_id": "m43A68sNcvg" -} diff --git a/docs/website/video-transcripts/m43A68sNcvg.txt b/docs/website/video-transcripts/m43A68sNcvg.txt deleted file mode 100644 index 6e2ed0bb65..0000000000 --- a/docs/website/video-transcripts/m43A68sNcvg.txt +++ /dev/null @@ -1,82 +0,0 @@ -now that we have maps installed and have -the keys let's proceed to the hello -world stage -the obvious hello world is -this thing -we just create map container and show it -this looks awful -that is the map component with -openstreetmap -it takes a while to load and isn't -exactly attractive -so let's add the javascript key to the -constructor so the fullback will be the -javascript maps instead of open street -maps -this looks more familiar and modern -now we could use google map provider -with map component -which might -improve the look of the component a bit -but i don't see a reason to dwell on -that when we can just use the javascript -fallback -in the past before z-order appears that -made some sense but it no longer does -now let's configure the build hints for -the individual os settings -first the android native map -api key needs to be specified using this -exact syntax -you can copy and paste it from the maps -github page where all of these keys are -listed -don't forget to replace your android api -key -with the key generated in the urls i -mentioned in the previous module -next we need to do that same thing for -ios and specify the ios key -in this build hint -and last but not least we need the -javascript key2 -normally this isn't essential since we -pass the javascript key in the -constructor -we need this for the javascript port -where the key -needs to be specified in another -location -building this to native android produces -this result on the device -however -the ios build fails -looking at the build error it's clear -there was a null pointer exception -when running the application -but this is an error from the build -server -as you might recall ios native -applications need a splash screen -the codename one build server solves -this by running the application several -times in the servers -where it generates splash screens for us -however the build servers don't support -the web browser component -the reason for this is simple -the web browser component will look -different on the device and so a -screenshot won't work well -the solution is to handle a case where a -web browser isn't supported -this fix is relatively trivial -if web browser isn't supported -it has to be the ios screenshot mode -and in this case we can just mention -that we are loading -a nice graphic might be relevant here -if we want to refine this further -and now -that this was built it looks like this -in ios diff --git a/docs/website/video-transcripts/mFFjxs9EDW8.json b/docs/website/video-transcripts/mFFjxs9EDW8.json deleted file mode 100644 index 779eb3a752..0000000000 --- a/docs/website/video-transcripts/mFFjxs9EDW8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 93, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 498, - "youtube_id": "mFFjxs9EDW8" -} diff --git a/docs/website/video-transcripts/mFFjxs9EDW8.txt b/docs/website/video-transcripts/mFFjxs9EDW8.txt deleted file mode 100644 index 6847b4fc20..0000000000 --- a/docs/website/video-transcripts/mFFjxs9EDW8.txt +++ /dev/null @@ -1,93 +0,0 @@ -up until now we focused on the visual -aspect of the application -without giving much thought to the -architecture -sometimes we start by designing the -model or underlying logic and sometimes -we take a more top-down approach -in mobile i like starting with the ui -design -as this is such an important part of the -product experience -we'll start by breaking the ui and -business logic apart -the first step is understanding the -pieces that are involved in the backend -model -we then need to integrate that model -data into the ui code and decide on the -right point of separation between the -model and the ui -i don't spend much time in front of a -whiteboard drawing architectural -diagrams -architecture is usually an iterative -process -where i can adapt the design to match -the evolving need -good architecture starts with necessity -i needed the dish class to represent the -data model -before -and now it's a great point to begin with -our ui already communicated some of the -necessity from the model -dish is obvious -but the main ui maps perfectly to a -restaurant menu -the checkout ui maps to a specific order -which we should also represent within -the model -we need a global state that contains the -menu -current order etc -this object can be represented by a -restaurant object -to which we can push a few other -variables -into that object -the menu object is simple it includes a -list of dishes and categories there -isn't much here -order is also pretty self-explanatory -notice that the menu object didn't have -an id -while the order has an id -we have one menu for the app so an id -isn't really necessary but we can have -multiple orders and might want to keep -a record of each order -also notice i used a string -for an id rather than a numeric id which -allows more flexibility in id generation -dishes are stored in a map to quantity -which might not be the best data model -but it works -this necessitates -an equals and hashcode method -implementation on dish -the restaurant object is a singleton -for simplicity's sake at least at this -point most of the details i need in the -app from the name of the restaurant to -its location -are all within this object -this is important as it provides us with -the necessary flexibility and allows us -to adapt the app to fit any restaurant -we want without code changes -we initialize the restaurant -properties in the constructor -i've snipped this a bit to keep it -narrow -but generally we set the values from a -properties file so we can customize this -easily -i hard coded the menu and dishes -at this point so we don't need to deal -with the complexities related to this -set of features -as i mentioned before the restaurant is -a singleton i load the values for it -from a properties file -within the app at this point diff --git a/docs/website/video-transcripts/mqNtfN2C3fM.json b/docs/website/video-transcripts/mqNtfN2C3fM.json deleted file mode 100644 index 62890a9926..0000000000 --- a/docs/website/video-transcripts/mqNtfN2C3fM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 117, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 618, - "youtube_id": "mqNtfN2C3fM" -} diff --git a/docs/website/video-transcripts/mqNtfN2C3fM.txt b/docs/website/video-transcripts/mqNtfN2C3fM.txt deleted file mode 100644 index 0004088c66..0000000000 --- a/docs/website/video-transcripts/mqNtfN2C3fM.txt +++ /dev/null @@ -1,117 +0,0 @@ -let's go to the client side for a moment -and do some operations on your local -machine -specifically file copy -scp is secure copy -it allows us to copy a file from our -desktop to the server or vice versa -on windows there are some visual scp -tools -but on mac i like using the command line -which i find more convenient -the syntax might seem odd -if you are familiar with the unix cp -command this is roughly the same syntax -only through the network this code is -executed on my local mac -so the first argument copies my instance -of -restbuilder.properties -which includes server configuration -settings that's the file we will be -copying -the second argument -is the destination -since it's remote it starts with the -username on the remote machine -followed by the at sign -and the ip address of the remote machine -then a colon is used to give the -destination path on the remote machine -you will be prompted for the password -of builder at which point copy will -proceed -this is very useful -command -now that we got that out of the way i -can just copy the actual jar to the -server -same exact thing -and now back on the server -we can open a text editor and set the -value of -the server url to the current ip address -nano -is a reasonably easy to use text editor -at least when compared to vi -you can use control x to exit -you will be prompted to save -next -let's log in to mysql -we'll be prompted for the password we -need to run the create database command -notice that the tables will be created -implicitly by jpa -the next step is to download apache and -which we need in order to build the -resulting application -we can unzip it -in the user's home directory thanks to -the unzip command -we installed previously -since the version number for ant is a -bit problematic i'm renaming the -directory and -so the build process can find it -the mv command represents move -and works as rename -i also rename -the backend server it shouldn't include -the version -as we'll update it in the future and we -don't want it to carry an outdated -version number -technically it might have been more -correct to use a sim link -but i want to keep things simple -you might recall we did a su builder -so i'm just exiting from that su command -and returning to the root account if i -use exit from there i will log off from -the server -this command might be confusing to -windows users -since there is nothing like this on one -on windows -the unix file system supports a feature -called symbolic links -essentially it creates what looks and -acts like a file or a directory -but it's really a file system pointer at -a file that resides elsewhere -in this case i created a sim link to etc -in a d -named app backend server -notice that it doesn't have the jar -extension -this points to the jar file we have in -the builder home directory -if you know linux or unix this might -look -really weird -if you know linux or unix you might be -thinking -what the hell just happened -well -the jar is really a linux shell script -too -so by placing a sim link in the nfd -directory it starts working as a service -just like mysql -i can't stress enough how amazing this -is -everything works exactly like you would -expect from any other service on linux -for instance you can see the output logs -for the service -under the var log directory diff --git a/docs/website/video-transcripts/nF4eqzVcsic.json b/docs/website/video-transcripts/nF4eqzVcsic.json deleted file mode 100644 index 548e93123e..0000000000 --- a/docs/website/video-transcripts/nF4eqzVcsic.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 92, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 635, - "youtube_id": "nF4eqzVcsic" -} diff --git a/docs/website/video-transcripts/nF4eqzVcsic.txt b/docs/website/video-transcripts/nF4eqzVcsic.txt deleted file mode 100644 index 255802b0fe..0000000000 --- a/docs/website/video-transcripts/nF4eqzVcsic.txt +++ /dev/null @@ -1,92 +0,0 @@ -hello guys today I want to talk to you -about uh image capture and grabbing a -photo with your camera so as you can see -I have a UI here with a capture button -and just a photo label label right below -it so to get started all I need to do is -actually uh click the capture and add an -action event to that capture entry so -we'll go to net beans as you can and as -you can see we have this uh right here -Capture Methods -and what I can do is essentially uh just -use the capture class which allows me to -capture all sorts of things from audio -to video and obviously photos of -different -kinds so to capture a photo I can just -use one of these three methods the -simplest one just captures a photo and -places it in a in a file and gives me -the name of the file or null if the the -file doesn't exist the second one is -interesting it uh returns immediately as -you can see it returns void and it will -call the action listener later with the -string of the file this is uh useful but -more complicated to invoke and use in -general it allows you to do things -asynchronously which is sometimes more -convenient to some people uh I think -personally this and this method are much -simpler -uh and the last one uh is an interesting -one it captures a photo just like the -first method but it also scales it after -the fact so you effectively get uh a -photo that is of the given width and -given height uh within uh your -Resort so that that is really useful uh -because cameras might capture really -really large images that might take a -lot of memory so we recommend you don't -uh use or load such images and usually -we recommend using just this method and -scaling to something that makes sense so -in this case I'm going to uh use the -display width as the Target and notice -Create Image -I'm giving minus one as the height that -means that we'll maintain aspect ratio -based on the width of the image and here -I can get the image back and if the -image isn't -null which it can be if the user cancels -or something like that then I'll just uh -find the photo and set the IM the icon -to my image and to create the image all -I'll do is image create image and give -it -this and I'll need to wrap this up with -a try -catch -block like -this -no I said -like this in this particular case this -is obviously an oversimplification but -it should work now uh one more thing I -need to do is get the component form and -revalidate because if I don't revalidate -then this line will effectively do -nothing it will set the icon but I won't -see anything because it will be too -small so to see this in action I can -just -play and we'll see the UI that I've -created earlier with the capture button -I can press the capture button you will -notice that it just gives me uh the file -dialogue and I can pick the image that I -want this is how it behaves in the -simulator on the device obviously it -will literally capture an image and I -can pick the image I want in this -particular case I have screenshots from -applications right here and as you can -see it captured the image on the device -it will literally capture uh from the -camera so thanks for watching I hope -you've enjoyed this tutorial and learned -how to uh capture images in code name -one thank you diff --git a/docs/website/video-transcripts/nImSppBdgkY.json b/docs/website/video-transcripts/nImSppBdgkY.json deleted file mode 100644 index 3a0cb34c22..0000000000 --- a/docs/website/video-transcripts/nImSppBdgkY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 186, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1121, - "youtube_id": "nImSppBdgkY" -} diff --git a/docs/website/video-transcripts/nImSppBdgkY.txt b/docs/website/video-transcripts/nImSppBdgkY.txt deleted file mode 100644 index 43d6e3d526..0000000000 --- a/docs/website/video-transcripts/nImSppBdgkY.txt +++ /dev/null @@ -1,186 +0,0 @@ -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/video-transcripts/oR3KHYf5OrY.json b/docs/website/video-transcripts/oR3KHYf5OrY.json deleted file mode 100644 index 63ea63e3f7..0000000000 --- a/docs/website/video-transcripts/oR3KHYf5OrY.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 23, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-create-a-basic-hello-world-application-send-it-to-my-device-using-intellij-idea.md", - "status": "transcript-fetched", - "word_count": 607, - "youtube_id": "oR3KHYf5OrY" -} diff --git a/docs/website/video-transcripts/oR3KHYf5OrY.txt b/docs/website/video-transcripts/oR3KHYf5OrY.txt deleted file mode 100644 index 666454bf86..0000000000 --- a/docs/website/video-transcripts/oR3KHYf5OrY.txt +++ /dev/null @@ -1,37 +0,0 @@ -_Transcript source: embedded._ - -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. - -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. - -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. - -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/). diff --git a/docs/website/video-transcripts/owhInk5YAtg.json b/docs/website/video-transcripts/owhInk5YAtg.json deleted file mode 100644 index 43e58ea8fc..0000000000 --- a/docs/website/video-transcripts/owhInk5YAtg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 184, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 990, - "youtube_id": "owhInk5YAtg" -} diff --git a/docs/website/video-transcripts/owhInk5YAtg.txt b/docs/website/video-transcripts/owhInk5YAtg.txt deleted file mode 100644 index 55a1d25999..0000000000 --- a/docs/website/video-transcripts/owhInk5YAtg.txt +++ /dev/null @@ -1,184 +0,0 @@ -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/video-transcripts/p05yMCleSLo.json b/docs/website/video-transcripts/p05yMCleSLo.json deleted file mode 100644 index 3ece953998..0000000000 --- a/docs/website/video-transcripts/p05yMCleSLo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 139, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 749, - "youtube_id": "p05yMCleSLo" -} diff --git a/docs/website/video-transcripts/p05yMCleSLo.txt b/docs/website/video-transcripts/p05yMCleSLo.txt deleted file mode 100644 index f8e7f91f1b..0000000000 --- a/docs/website/video-transcripts/p05yMCleSLo.txt +++ /dev/null @@ -1,139 +0,0 @@ -in this section we'll discuss the star -form -which allows us to determine the style -of elements within the application -the style form allows us to view the -main ui of the application -and touch various elements -so we can customize their foreground -color background color and font -there are a lot of limitations i imposed -on this ui to keep things relatively -simple -fonts can't be customized on something -that doesn't actually have a font -such as an icon -we only allow a very small selection of -font options -the reason for that is -difficulty of -getting this right across platform -where there are so many options -available -the color picker is also relatively -a relatively simple widget -that just allows us to drag the rgb -channels or set the hex value of the -color -i wanted to keep this ui as trivial as -possible -you can reset the changes in two ways -use the back button on the top left -without saving or use the restore style -option -within the popup dialog -the style form allows us to define -that ui -notice that it derives from form -but is otherwise a pretty standard class -we derive from form instead of the base -form class as we don't want the side -navigation menu and other thrills that -come -with a custom base class -there is another reason -we want to apply styling aggressively -and if we derive from the main form -we might run into some issues -the style setting class contains -information about changes to a specific -style -we use this to store the changes defined -by the user -and send this data to the user -i'll -cover that class soon enough -the toolbar styling is something we need -to do in code -otherwise when we start shifting styles -around things can get messy -this is the back command and right below -we can see the ok command -with the check next to it -both do exactly the same thing -they reload the builder theme -so we can restore it just like we did in -the preview code in the preview code -then show the previous form -one thing i do want to highlight here is -the insert or update call -which loops over the styles and saves -them -when we make a change -as you can see -the theme for the restaurant tab -is set when we first launched this form -so it controls -most of the ui here -moving ahead we can see the code that -deals with applying the styles saved in -storage -we load the styles from storage and then -loop over them -for each type font foreground or -background we create a theme entry -a theme in codename one is a hash table -that has keys and values -with the format you see in the code -for instance -button dot fg color represents the -foreground color for the theme -and so forth -we can then use add theme props to layer -this theme on top of the existing theme -and thus load the style settings from -the database -moving on we place the main menu form -from the restaurant app in the center of -the user interface -so we can click on the actual restaurant -app -notice we make the toolbar focusable -we need that so the tapping on the -toolbar will allow us to customize it -otherwise some special case logic of the -form class -might kick in -we override the pointer events in the -form such as pointer dragged pressed etc -if the event is -in the content pane -we discard it -if not -we send it on -this allows events in the toolbar but -blocks events everywhere -this works because events in codename -one propagate through the parent form -and so if we override the pointer events -and don't call super it's as if the -event never happened -that can be useful if we want to provide -complete custom functionality -the pointer released -override is below the get start setting -and we'll discuss that more in depth -soon enough -but first i want to talk a bit about -getstar settings method -this method caches styles from sql and -loads them lazily you might think why -would we need that -the reason is cancel -we need the ability to cancel the -changes so we can't persist every star -change -we make -we need to save them in the list and -only update the database when the ok -button is pressed diff --git a/docs/website/video-transcripts/p6UFNw0nGik.json b/docs/website/video-transcripts/p6UFNw0nGik.json deleted file mode 100644 index 2ac7585f74..0000000000 --- a/docs/website/video-transcripts/p6UFNw0nGik.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 166, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 887, - "youtube_id": "p6UFNw0nGik" -} diff --git a/docs/website/video-transcripts/p6UFNw0nGik.txt b/docs/website/video-transcripts/p6UFNw0nGik.txt deleted file mode 100644 index 9db10ee6c1..0000000000 --- a/docs/website/video-transcripts/p6UFNw0nGik.txt +++ /dev/null @@ -1,166 +0,0 @@ -in this session i'm going to talk about -one of the more -complicated subjects and that's -threading and the edt -threading in general is one of the -harder things in -programming not because of -the immediate things but rather the -less obvious implications of -some behaviors when it comes to threads -and there's a lot of nuance when it -comes to threading i'm not going to go -into that nuance and mostly leave that -to the more -advanced thread theory books and things -related to that -i'm going to talk mostly about threading -in a higher level -which is -one that we mostly apply in codename one -but notice that a lot of things when -people -rely on behaviors of the java virtual -machine memory model like double locking -semantics and things like that if you're -familiar with that -don't try to be adventurous when it -comes to codename one because -it's about -portability and -one vm will behave in one way and -another will behave in another way when -it comes to the memory model -we can't keep up with the same level of -accuracy that uh standard jvms keep up -because we translate to native code -and this is sometimes very challenging -to do -so -even experts sometimes have an issue -with uh doing the nuances -uh -try to keep threading as simple as -possible and use it just uh as much as -needed to know as needed there's no and -no more than that -so -about reading uh -the most important thing in coding one -is the event dispatch thread -and that's essentially codename one is -single threaded in the sense that you -have that thread and that thread alone -cannot access the user interface or -should access the user interface -actually -as the correct term -you can open other threads and the -network sits on a different thread and -there are other complexities related to -threads but generally -as a whole codename one is -considered a single threaded environment -that means it's not -thread safe -there aren't -many apis in the modern area era that do -thread safe -apis to developers and that's because -it's just -almost impossible to do that correctly -awt tried to do that uh ads and -microsystems and to this day there are -open bugs that are just unfixable in awt -in terms of threading because it's so -damn hard to get thread safe to work -correctly -so -the approach there is to go the exact -opposite direction and try not to be -thread safe at all -and then also optimize the code as a -result because locking is one of the -most expensive things you can do in -terms of -low-level apis and when you need to -render things to draw things on the -screen you don't want to waste time with -one of the most expensive apis you can -use -so you don't want to do it often or much -you need to do it to some degree because -there is absolutely no choice -uh -actually we need to do it -so you don't have to the adt uses -locking inside it but you and your code -don't need to as a result of of the -existence of the edt -so -uh -the os's themselves have their own event -dispatch to it this is pretty much -for all -and it doesn't actu necessarily be uh -act as one native underlying os thread -but usually whenever an application is -instantiated it gets one thread that's -allowed to access the the native system -user interface and that's it -and that's per application usually -and -we -with our edt essentially hide the native -event dispatch grid -and that sounds redundant you know why -have two edts why have a native one and -why not just expose that directly to the -developer well -that's very unportable -and the native edts behave very very -very differently between os's -and they don't have all sorts of -abilities -not all of them some of them do but not -all of them have something like invoke -and block -and -when we want one of the things in -codename one is that -the secret to its portability is first -lightweight components but the second -secret which is -arguably bigger -is the fact that we have the edt and the -edt makes sure that events arrive at a -consistent way -and at a consistent -behavior -and with the native thread you wouldn't -be able to get the same level of -consistency -throughout -so -that's really important for for the -overall portability -and -i normally try to avoid uh -charts in my -videos because i'm not a very visual -person in terms of structural thinking -but in the case of ddt it's sometimes -hard to explain what is actually going -on so i did a really really trivial -chart that i later enhanced to explain -another another idea -and the basic edt is just a while loop -and this is something that people need -to understand it's it's an infinite -while loop that just runs as long as the -code in one application runs -and it paints and then it sleeps and it -paints and it sleeps and the sleep by -the way is a very important part of it -because -when you do threads you need to diff --git a/docs/website/video-transcripts/q4YjUClRHBk.json b/docs/website/video-transcripts/q4YjUClRHBk.json deleted file mode 100644 index 58af7b872b..0000000000 --- a/docs/website/video-transcripts/q4YjUClRHBk.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 88, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 477, - "youtube_id": "q4YjUClRHBk" -} diff --git a/docs/website/video-transcripts/q4YjUClRHBk.txt b/docs/website/video-transcripts/q4YjUClRHBk.txt deleted file mode 100644 index 83d98714bf..0000000000 --- a/docs/website/video-transcripts/q4YjUClRHBk.txt +++ /dev/null @@ -1,88 +0,0 @@ -since the facebook app has many ui -elements the mock-up stage for this app -stretches on longer than i would have -hoped -the nice thing about this is that the -logic of the app is relatively simple -so once the mock-up would be done -it should be pretty easy to implement -the basic business logic -notice that the smart portion in -facebook -is the back-end analysis which are -mostly skip -it's a subject for a big data course not -a mobile development course -i discussed the main ui before so i -won't go into a recap of the of its -purpose -we'll mark up the basics so we can get -started with implementation and server -code for now i'll skip camera and video -as we'll address them later -the big piece is the main ui which -includes the four tabs for news feed -friends notifications and more features -let's jump right into the main ui -the main form includes four containers -that are arranged as tabs you will -notice that the tabs default to top -placement on android and bottom -placement on ios -this matches how the native facebook app -behaves -to support this ui we need container -classes representing each tab -we need to add four classes named -newsfeed container friends container -notifications container and more -container -we'll get to those four containers one -by one -but first i'll explain the main form -itself -it's important to place the tabs in the -center -they never scroll off the screen and -take up the available -space -we add each tab and give it a five -millimeter icon -from the material icon set -the camera and chat icons aren't -implemented at this time -search looks like a text field but it's -a button that leads to the search form -i'll implement that form later -this is -relatively simple -we also need some css to support that -this the image includes a few ui ids -we'll cover a bit later when discussing -the news feed -let's look at the css code behind that -i chose to implement the search ui in -the ios style with a round pill border -shape -we create an opaque white tab and give -it a small margin and pixels on top and -bottom -the tab container below -the tab is gray -it lets us form a gray line above and -below the tab -the selected tab has a border on some -native platforms so it's important to -disable that -here we just want a different color for -the selected and pressed styles -once this is done -we have the basic tab ui -we can wire it into the main ui by -implementing show main ui in ui -controller -once this is done pressing login will -show the main ui form -now we can proceed to implement the tabs -but first we need -something else diff --git a/docs/website/video-transcripts/qEgDZHZJEYo.json b/docs/website/video-transcripts/qEgDZHZJEYo.json deleted file mode 100644 index 52b221d488..0000000000 --- a/docs/website/video-transcripts/qEgDZHZJEYo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 69, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 386, - "youtube_id": "qEgDZHZJEYo" -} diff --git a/docs/website/video-transcripts/qEgDZHZJEYo.txt b/docs/website/video-transcripts/qEgDZHZJEYo.txt deleted file mode 100644 index c7bc738e08..0000000000 --- a/docs/website/video-transcripts/qEgDZHZJEYo.txt +++ /dev/null @@ -1,69 +0,0 @@ -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/video-transcripts/qGJaVaQ_MNY.json b/docs/website/video-transcripts/qGJaVaQ_MNY.json deleted file mode 100644 index 718d75c8ad..0000000000 --- a/docs/website/video-transcripts/qGJaVaQ_MNY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 115, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 617, - "youtube_id": "qGJaVaQ_MNY" -} diff --git a/docs/website/video-transcripts/qGJaVaQ_MNY.txt b/docs/website/video-transcripts/qGJaVaQ_MNY.txt deleted file mode 100644 index 76a2a6ea2b..0000000000 --- a/docs/website/video-transcripts/qGJaVaQ_MNY.txt +++ /dev/null @@ -1,115 +0,0 @@ -performance is one of those vague -subjects that is often taught by example -in this section i want to discuss some -of the performance optimization -strategies we took when working on the -second version of the kitchen sync demo -during our debugging of the contacts -demo that is a part of the new kitchen -sync demo we noticed its performance was -subpar -we assumed this was due to the -implementation of get all contacts and -there is nothing to do -while debugging another issue we noticed -an anomaly during the loading of the -contacts -this led to the discovery that we are -loading the same resource file over and -over again for every single contact in -the list -conclusion -don't assume -Dont Assume -in the contacts portion of the demo -we have a share button for each contact -the code for constructing a share button -looks like this -this seems reasonable -until you realize that the constructors -for sms share email share and facebook -share -load the icons for each of those -these icons are in a shared resource -file that we load and don't properly -cache -the initial workaround was to cache this -resource but a better solution -was to convert this code -this way the resource uses -lazy loading as needed -this small change boosted the loading -performance and is probably the general -performance due to the less memory -fragmentation -the lesson that we should learn every -day is never assume about performance -Image Loading -another performance pitfall in this same -demo came during scrolling scrolling was -janky uneven and smooth right after -loading finished -would and would recover after a couple -of minutes -this relates to the images of the -contacts -to hasten the loading of contacts we -load them all without images -we then launch a thread that iterates -the contacts and loads an individual -image for a contact -then sets that image to the contact and -replaces the placeholder image -this performed well in the simulator -but didn't do too well even on -a powerful mobile phones -we assumed this wouldn't be a problem -because we used util dot sleep to yield -cpu time but that wasn't enough -often we -when we see performance penalty the -responses move it to a separate thread -the problem is that -this separate thread -needs to compete for the same system -resources and merge its changes back -onto the edt -when we perform something intensive we -need to make sure that the cpu isn't -needed right -now we added a variable called last -Scrolling -scroll which updates when the user tries -to scroll the screen -with the current time -then we did this within the background -loading thread this effectively sleeps -when the user interacts with the ui -and only loads the images if the user -hasn't touched the ui in a while -notice that we also check if the scroll -changes this allows us to notice cases -like the animation -of scroll winding down -all we need to do now is update the last -scroll variable whenever user -interaction is in place -the code above works for user touches -the code below works for general -scrolling -due to technical constraints we can't -use a lambda in this specific case -once this was done scrolling became -smooth and filled -out the entries when we were idle -we wrote this code before call serially -on idle existed i wonder if it would -have worked as well for this particular -use case -it's really easy to optimize in a -cross-platform way -i optimized by moving stuff to a -separate thread and yielding for the -main thread -this would impact all platforms and -provide a smoother experience all around diff --git a/docs/website/video-transcripts/qKc1hZyw360.json b/docs/website/video-transcripts/qKc1hZyw360.json deleted file mode 100644 index 9cb7115ba7..0000000000 --- a/docs/website/video-transcripts/qKc1hZyw360.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 159, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 941, - "youtube_id": "qKc1hZyw360" -} diff --git a/docs/website/video-transcripts/qKc1hZyw360.txt b/docs/website/video-transcripts/qKc1hZyw360.txt deleted file mode 100644 index 57790226b6..0000000000 --- a/docs/website/video-transcripts/qKc1hZyw360.txt +++ /dev/null @@ -1,159 +0,0 @@ -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/video-transcripts/qUJ4FwZMEQY.json b/docs/website/video-transcripts/qUJ4FwZMEQY.json deleted file mode 100644 index 0fcd2b80cf..0000000000 --- a/docs/website/video-transcripts/qUJ4FwZMEQY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 150, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 758, - "youtube_id": "qUJ4FwZMEQY" -} diff --git a/docs/website/video-transcripts/qUJ4FwZMEQY.txt b/docs/website/video-transcripts/qUJ4FwZMEQY.txt deleted file mode 100644 index 4e46ca902c..0000000000 --- a/docs/website/video-transcripts/qUJ4FwZMEQY.txt +++ /dev/null @@ -1,150 +0,0 @@ -in this part we will go over the -remaining big subjects and animations -we start with style animation which we -discussed before -we used that to animate the title bar -but we can do pretty much anything a -style can do -although -some things work better than others -for instance animating a color works -really well -but animating alignment will not -as it only has -three hard states -most of the other animation types have -simple methods to implement them -such as animate layout -but the style animation is only -accessible via the animation manager -class -the reason for this is that it's a newer -animation type the older animations -predated the animation manager and were -adapted to work with it -the style animation was created with the -animation manager and so it relies on it -this code animates a label uiid to a -text field uiid -you will notice we created a generic -component animation class -and use animation manager to set it -these component animations are pretty -powerful -you can sequence the and run them in -parallel -you can do quite a lot of powerful -things with them -the animation manager is a late addition -to codename one it came after we already -had layout animations and a lot of -infrastructure in place so we had to -upload everything and stick it below -this created interesting situations and -made the api awkward at some points -the main reason we had to do that is -orchestration -the animation manager takes care of all -the animations within the form -from one point in the code -before we had this it was really -difficult to run an animation without -collision -so if an animation was running and a -user triggered a button that removed an -unrelated component -the animation would crash -because the number of components was -suddenly different -so the animation manager came along and -it handles animations but also handles -serialization of operations -that means -that if you try to remove or add a -component while an animation is in -progress -this operation will be postponed until -the animation completes -you can also tie together component -animation objects and effectively create -elaborate animations -i would discourage that for most cases -as it can lead to overly complex -animations -that serve -form instead of function -and finally i'll cover -the exact opposite -of these high-level animations -the low-level animation infrastructure -animations ui timers and many other -features in codename one -rely on the animate method of component -which is core to codename one but so few -people understand it -when the edt runs a cycle -we call it a tick -and the edt aims -for up to 60 ticks per second -although the speed varies -with each tick -the -form is tossed with handling animations -and it invokes the animate method -if the animate method returns true the -animation will be painted -notice that animation in this case -refers to the animation interface but -all components implement that so you can -use the component instead -now -this would be very costly if we had to -go over every component 60 times per -second -so only components that use register -animated will receive the animate call -the thing is that while there are -registered animations the edt cannot -truly sleep -but that's probably okay -the real problem is when animate methods -return true too often or perform long -complex operations -this can become a serious battery drain -that will slowly seep power from the -device -so it's very important to deregister -unnecessary animations -we try to do it with our components -but my point here is that this is pretty -low level stuff -and you should be very careful when -dealing with it as the performance -implications can be significant -all our components use this feature -text fields use it to blink their -cursors url images use animations to -update the image -when we receive an up-to-date image from -the server -it's pretty powerful -the clock demo in the kitchen sync uses -it too let's have a look -notice we register animated and -deregister it based on the need -in the initialize and the initialize -callbacks -this triggers a call to animate method -notice we only refresh the clock once a -second this is important otherwise -excessive repaints could drain the -battery -and finally we draw the animation in the -background -callback -notice that this is pretty low level -these types of animations are designed -for graphics -not for components -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/qaYExTzcCVY.json b/docs/website/video-transcripts/qaYExTzcCVY.json deleted file mode 100644 index 2ac522eff9..0000000000 --- a/docs/website/video-transcripts/qaYExTzcCVY.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 128, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 739, - "youtube_id": "qaYExTzcCVY" -} diff --git a/docs/website/video-transcripts/qaYExTzcCVY.txt b/docs/website/video-transcripts/qaYExTzcCVY.txt deleted file mode 100644 index d805183a68..0000000000 --- a/docs/website/video-transcripts/qaYExTzcCVY.txt +++ /dev/null @@ -1,128 +0,0 @@ -in this part we'll cover the server side -of the push -you might recall the async class from a -previous review of a server code -here it is more fleshed out and we'll -also send out a push when the build is -true -Variables -we'll start by reviewing these variables -we have two IRS certificate URLs and -passwords for these two URLs we also -have a flag indicating whether this is a -production or debug push when we send a -push to iOS we need a special -certificate to authenticate us to Apple -it's not the signing certificate it's a -special Cloud certificate -Apple has two separate server -deployments -one you use while developing and one -during production they don't mix -each has its own certificate -an annoying bit is that if you use a -phone during development it will -sometimes take up for a day for it to -start working for the production -deployment -notice that the production deployment -will only work once you install through -the App Store and doesn't work with -debug builds -Push Token -the push token is taken from the -codename One account settings in the -build server and is used to authenticate -with the codename one server -the GCM server key is the other value -from the initial Android registration -process we need it for the Android push -Rest Builder -we initialized these fields from a file -in the user home directory called rest -Builder the reason for this is both easy -maintenance but mostly convenience so I -won't have to include my keys and the -source code and constantly remember to -remove them I can just set the values in -a properties file and forget about this -Build App -I'll take a short detour through the -build app method you might recall where -a lot of customization is now happening -on the general generated application you -will notice we generate the right -package name and Main class we also set -the right restaurant ID -Etc so the generated app will work as -expected -CSS -a bit lower we can see the CSS generated -by the style objects we configured in -the client this will allow customizing -the look of the final restaurant app -Android Target -further down the source Target hasn't -changed much but you will notice the -Android Target is now implemented -it sets the automated flag to True which -allows sending a blocking build which -returns a result synchronously -this is an Enterprise feature and it's -useful for continuous integration or -cases such as this where you want the -build servers to do a build for you and -return a result -I execute the r and command using the -process Builder and wait for it to -complete -scrolling a bit down we can see the loop -over the result zip from the synchronous -ant build -where we look for the result APK and -extract it to send the result to the -client -but that's not what I'm looking at -Push Notification -if we have an error we invoke the method -send push notification with the push key -if we have it -Send Push Notification -going further down we can see -push being sent and here are the -arguments are far clearer for the -success -the app build completed successfully -string is pretty clear but we also have -a semicolon followed by a URL and number -three -if you recall that means a Type 3 push -with a string message and a result URL -so it's effectively two separate -messages the first visible the second -hidden -send push notification is actually -really simple -since we do it from the server we need -to post to the codename one per servers -at -https push dot codename1.com slash push -slash push -with a post method -we need to provide all the arguments in -the post body from token to the device -key and certificates -the push server returns a response which -we should should log for our records in -case of failure but we should normally -keep track of in case of key a key that -expires -in this particular case this isn't -likely since the key is sent with the -request so it should always be 100 valid -there are some additional details but -the basic gist of sending a push message -response for this case should be -reasonably clear with this code -thanks for watching -I hope you found this informative diff --git a/docs/website/video-transcripts/r_ti2QAhm9s.json b/docs/website/video-transcripts/r_ti2QAhm9s.json deleted file mode 100644 index e84fdb0774..0000000000 --- a/docs/website/video-transcripts/r_ti2QAhm9s.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 134, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 762, - "youtube_id": "r_ti2QAhm9s" -} diff --git a/docs/website/video-transcripts/r_ti2QAhm9s.txt b/docs/website/video-transcripts/r_ti2QAhm9s.txt deleted file mode 100644 index d8c36eadf6..0000000000 --- a/docs/website/video-transcripts/r_ti2QAhm9s.txt +++ /dev/null @@ -1,134 +0,0 @@ -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/video-transcripts/rjIQqfrFvbI.json b/docs/website/video-transcripts/rjIQqfrFvbI.json deleted file mode 100644 index 3d4c5719ca..0000000000 --- a/docs/website/video-transcripts/rjIQqfrFvbI.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 106, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 625, - "youtube_id": "rjIQqfrFvbI" -} diff --git a/docs/website/video-transcripts/rjIQqfrFvbI.txt b/docs/website/video-transcripts/rjIQqfrFvbI.txt deleted file mode 100644 index b07201d452..0000000000 --- a/docs/website/video-transcripts/rjIQqfrFvbI.txt +++ /dev/null @@ -1,106 +0,0 @@ -the last piece of the server code is the -web service layer which again is again -relatively boilerplate driven -the web services mostly invoke the -service layer without much imagination -that's a good thing in engineering terms -boring is good -but it doesn't make a thrilling story so -please bear with me as this will pick up -pace a bit when we connect to the client -side -we have three web service classes user -web service media web service and post -web service -just like the user service this class is -pretty big -and also includes some additional -capabilities from the notification -service despite that most of the classes -boilerplate there is still a lot of it -so i'll try to keep it manageable by -splitting it down a bit -the web service is located at the slash -user base which means the ur any url -would be below that -we map two services in this single -service -since notifications has just one method -it made sense bringing it here -the exception handler grabs one of the -three exception types that can be thrown -in one of the methods and in this class -and convert them to an error response -object -notice that the error response is the -error dao which generates json with the -error message this is the error da class -the error dial object is just a simple -dao that includes the error message and -an optional error code -the user login url -returns the response as json in the body -and accepts a json request in the body -of the post -the exception will be converted to an -error dial if it's thrown -the use the slash user slash refresh -method -is a get operation -notice that it doesn't accept an -argument it uses an http header named -auth for the -authorization token -notice i used the auth header almost -everywhere -as it's a standard part of the request -you will notice the code is very similar -simple it's declarative code that -defines how the web service will look -before delegating to the underlying -service class -next we have the operations that map an -avatar this means the urls user slash -avatar slash user id would return the -avatar jpeg directly and would work even -if the browser with the browser so we -can share this url -response entity lets us control the http -response including the mime type and -media content -in this case we send a 404 response when -the user doesn't have an avatar -i use the get method to make a change in -the set avatar request -this simplifies the code as it already -requires a separate operation to upload -the media object -the friend request methods are trivial -and nearly identical they both accept -the same arguments and pass them on to -the underlying web service call service -call -i use a get operation because i don't -want to add a dial and get into the -complexities of post for this case -normally a get operation is a bad idea -for an operation that triggers a change -i wouldn't do this on a on the web since -it can lead to accidental resubmission -of data -however since my client here is an app -the use of get operation simplifies some -of the syntax -finally the last piece of the user web -service code is the notification and -contacts support -this method allows us to page through -the list of notifications fetching a new -page dynamically as needed -we can submit a list of contacts for the -shadow user through this api notice that -a list from json will implicitly -translate to the given contact list -with that we can finish the relatively -simple -user web service diff --git a/docs/website/video-transcripts/sBAiPOnjX6o.json b/docs/website/video-transcripts/sBAiPOnjX6o.json deleted file mode 100644 index 1c368907aa..0000000000 --- a/docs/website/video-transcripts/sBAiPOnjX6o.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 190, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1031, - "youtube_id": "sBAiPOnjX6o" -} diff --git a/docs/website/video-transcripts/sBAiPOnjX6o.txt b/docs/website/video-transcripts/sBAiPOnjX6o.txt deleted file mode 100644 index 09db5d860d..0000000000 --- a/docs/website/video-transcripts/sBAiPOnjX6o.txt +++ /dev/null @@ -1,190 +0,0 @@ -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/video-transcripts/sH5GY816sRc.json b/docs/website/video-transcripts/sH5GY816sRc.json deleted file mode 100644 index 70141aba0f..0000000000 --- a/docs/website/video-transcripts/sH5GY816sRc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 205, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1060, - "youtube_id": "sH5GY816sRc" -} diff --git a/docs/website/video-transcripts/sH5GY816sRc.txt b/docs/website/video-transcripts/sH5GY816sRc.txt deleted file mode 100644 index 9187e820c3..0000000000 --- a/docs/website/video-transcripts/sH5GY816sRc.txt +++ /dev/null @@ -1,205 +0,0 @@ -now comes the world of pain -ssl support -this is actually not too horrible -usually -but i chose to go with the -free let's encrypt option -that might have been a mistake in -retrospect -in the ide on our development machine -let's open -application.properties and set -several important -variables notice that i will need to -comment out these values when running -locally since i can't get ssl when -running locally -the server must use ssl for everything -as ios won't allow regular http -connections -to do this i'll define the server port -to -8443 -and require ssl -the next few lines point at the keystore -file -password and alias -we'll configure all of these values soon -i'll need to redeploy the updated server -with ssl setting -by copying it to the server -and obviously i'll need to do this line -again -to -to move the uploaded file to replace the -existing backend -server -first we need to set up dns for the -server -assuming you already registered a domain -setting the a record to point at the ip -address of your server gives it a name -you can't apply https to an ip address -and it's not a good idea in the long run -as it might pose a problem with scaling -or migrating -let's -for a second stop and explain how https -works -the server sends a certificate which -verifies the identity of the server -for example -this is build.magimob.com -and to prove that certificate is signed -by a signing authority -this signing authority is a company we -trust to verify that identity -in the past signing authorities -literally looked you up through duns -numbers -which is a horrible thing i don't want -to go into right now -that way they could verify your identity -let's encrypt was formed to allow -everyone to get a valid ssl certificate -for free -it does that by automating the process -completely -and the trick is composed of two pieces -the first is an application you run on -your server to verify that this is the -right server -the second trick -is -the short lifetime of a certificate -only 90 days -even if an exploit exists -it will expire in 90 days naturally -normally certificates last a year or two -and it's really painful to replace them -the 90 day time is horribly short -and the reason it's so short is so we -would be forced to automate the process -completely -that means our server should request a -new certificate on its own without any -action from us -that's actually pretty cool -it would mean the certificate will -always be renewed -and -current without any work from us -once we do the actual work -one caveat for java developers is -this -in order to recognize a certificate -authority -the root certificate should be embedded -into browsers and the jdk itself -most devices browsers etc already -recognize let's encrypt -but -the jdk only added it in update 8 u101 -101 -which is pretty late in the game -that might mean you will need to update -your jdk to access the secure site -so let's get this off the bat -i really regretted picking let's encrypt -as i walked through the process it sucks -on spring boot -the automation doesn't work out for the -box and i needed to do so many so much -wizardry -it's amazing -i don't understand why the guys at -spring or tomcat didn't implement -built-in support for this out of the box -i hope they will add it in an update -but i haven't seen anything so far i -seriously hope the rest of this module -becomes out of date -we start by installing some packages -required by let's encrypt -this is pretty much -the instructions you can find online to -do that this isn't a smooth experience -and part because of the newness of let's -encrypt -the first thing we install isn't so much -a thing as it is a third-party -repository to look in for additional -packages -next we install some utilities that -allow us to use the config manager in -the next step -i -honestly have no idea why the hell this -is needed -but it's part of the setup instructions -so i went along with it -the search bot is a command line tool -that allows us to renew certificates -automatically -certificate robot -got it -we need to stop the server if it's -running -the reason for this is clear when we -understand how let's encrypt works -it can integrate with some servers -but not with spring boot -so the alternative is to let it spin its -own server on port 443 which we would -normally need so we need to shut down -the server while we renew the -certificate -now that the server is off -we can launch the cert bot command which -fetches a certificate from let's encrypt -service automatically -the command includes several arguments -so let's review the important ones -dash dash standalone means we'll be -using the standalone server and can't -use integrations -to one of the existing supported servers -dash d -passes the domain name that we want the -certificate for -which needs to be the same one -associated with the dns from before -the next two arguments represent the -actual ports used for the servers notice -that port 8 -and 443 will be used internally but -since we redirect those to different -local ports with the iptable settings -we need to indicate the real local ports -to use -running this one command should generate -the certificate -but it doesn't end there -this is another big obtuse command let's -encrypt generates a certificate and -saves it using a pem format -which we can't use from java -we can use this openssl command to -convert the pem file saved in the slash -edc slash let's encrypt directory -to the keystore format we need -we ran that command as root so i'm using -the change own or shown command to -change the ownership of the file -from root to builder this is generally -good practice for files within the user -directory -it would have probably been been a -better idea to just run the open ssl -command -as builder -now that this is done i can relaunch the -backend server and https should work as -expected -but there is still one -thing missing diff --git a/docs/website/video-transcripts/sK-u1TBWFX8.json b/docs/website/video-transcripts/sK-u1TBWFX8.json deleted file mode 100644 index 60e99911bd..0000000000 --- a/docs/website/video-transcripts/sK-u1TBWFX8.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 27, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-fetch-an-image-from-the-resource-file-add-a-multiimage.md", - "status": "transcript-fetched", - "word_count": 1319, - "youtube_id": "sK-u1TBWFX8" -} diff --git a/docs/website/video-transcripts/sK-u1TBWFX8.txt b/docs/website/video-transcripts/sK-u1TBWFX8.txt deleted file mode 100644 index 8feeae4ef6..0000000000 --- a/docs/website/video-transcripts/sK-u1TBWFX8.txt +++ /dev/null @@ -1,46 +0,0 @@ -_Transcript source: embedded._ - -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?" - -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! - -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 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. - -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. - -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. diff --git a/docs/website/video-transcripts/sUhpCwd0YJg.json b/docs/website/video-transcripts/sUhpCwd0YJg.json deleted file mode 100644 index 73c8331cc8..0000000000 --- a/docs/website/video-transcripts/sUhpCwd0YJg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 11, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 439, - "youtube_id": "sUhpCwd0YJg" -} diff --git a/docs/website/video-transcripts/sUhpCwd0YJg.txt b/docs/website/video-transcripts/sUhpCwd0YJg.txt deleted file mode 100644 index eee27a8272..0000000000 --- a/docs/website/video-transcripts/sUhpCwd0YJg.txt +++ /dev/null @@ -1,11 +0,0 @@ -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. -WebService Wizard -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. -WebService servlet -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. diff --git a/docs/website/video-transcripts/sed5OPQSfe0.json b/docs/website/video-transcripts/sed5OPQSfe0.json deleted file mode 100644 index cf876e6006..0000000000 --- a/docs/website/video-transcripts/sed5OPQSfe0.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 165, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/032-markers-lightweight-overlays-and-map-layout.md", - "status": "transcript-fetched", - "word_count": 815, - "youtube_id": "sed5OPQSfe0" -} diff --git a/docs/website/video-transcripts/sed5OPQSfe0.txt b/docs/website/video-transcripts/sed5OPQSfe0.txt deleted file mode 100644 index 8ed73d31b8..0000000000 --- a/docs/website/video-transcripts/sed5OPQSfe0.txt +++ /dev/null @@ -1,165 +0,0 @@ -we can spend a lot of time talking about -maps -since there are just so many features in -the map api -but the most basic one is markers -we can add a marker to a map using this -api -notice that we can pass an encoded image -that will be used instead of the default -image -in this case i chose to pass null -the resulting map looks like this -pretty familiar -but a bit limited -we don't have a lot of control over the -marker -a marker is great -but sometimes we want more -why not place a component on the map in -the location that we want -this used to be a problem historically -all native components -commonly referred to as pair or -heavyweight components -we're always on top -we solve this problem by going through a -lot of hoops and it's now possible to -place -a component on top of the map -however it's still not trivial -how do we know where to place the -component -we could use gloss paint to draw on top -of the map -but that's a bit limiting and requires a -lot of low level -code -we need a simple layout manager that -allows us to place components based on -their longitude and latitude values -it sounds hard to create a layout -manager but it really isn't -in this layout manager i make some -assumptions such as -an assumption that the layout manager is -placed exactly on top of a map container -within a layered layout -this means that when we position a -component it will be exactly above the -map -[Music] -the first step in implementing the -layout manager is extending the layout -class -notice i also implement the map listener -interface -so the layout can update positions when -the map changes -in the constructor i break encapsulation -a bit -by binding the map listener -this isn't ideal code but it can work -for our current purposes -in the future -i hope to create a more generic version -of this class in the map cn1 lib -even when we do that this code would -still be useful to explain how things -like this work -the map layout -is a constraint based layout -similar to border layout -that means -you need to add a component to the map -layout with a constraint -in this case a coordinate object -notice -i do a cast -so if someone adds a component with a -different object type as a constraint -you will get a class cast -exception -this is the entire layout manager class -notice it's only two pages -in large font -the area surrounded in red is the entire -layout -code -it's trivial -first we iterate over the components -within the parent container -we get the current coordinate -that we added -with the constraint -we can then -use the map api to convert the -coordinate to a point in the screen -normally we shouldn't call set size set -x or set y -but a layout manager is a special case -it's the one place where we are supposed -to set -the component position and size -we do several things here -we give all components their natural -preferred size -some layout managers do that others -don't -in our case using the preferred size -makes a lot of sense -the next thing we do is set the x and y -based on the screen point we got from -the map -notice we position the coordinate -in the center below the component which -turns out nicely for the component type -i tried -but it might make sense to make this -more configurable -and that's it -this is effectively the entire layout -manager logic -most layout managers should define a -preferred size -but since we will always reside on top -of a map -we can rely on its preferred size to -define -the amount of space -required -this is the event callback for map -modification -every time the map is modified we -revalidate the parent and trigger -repositioning of the components -this breaks the separation between -layout manager and the parent component -so this isn't necessarily a best -practice in terms of design -but for the purposes of a simple -tutorial -it works -adapting our previous code i can just -create a button to represent moscone -instead of a marker -notice i wrap the map container -with a marker container -markers container that uses the map -layout -i can now do whatever i want with the -button -when i add it i need to pass the -coordinate object of moscone -for this thing to work -and -this is the result -it doesn't look much different on the -surface -but for us this is a huge difference -instead of markers i can just place -anything i want -anywhere on top of the map -that's huge -thanks for watching -i hope you found this informative diff --git a/docs/website/video-transcripts/set12jVQl_A.json b/docs/website/video-transcripts/set12jVQl_A.json deleted file mode 100644 index 2024896d95..0000000000 --- a/docs/website/video-transcripts/set12jVQl_A.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 335, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 2203, - "youtube_id": "set12jVQl_A" -} diff --git a/docs/website/video-transcripts/set12jVQl_A.txt b/docs/website/video-transcripts/set12jVQl_A.txt deleted file mode 100644 index aaa050bcf6..0000000000 --- a/docs/website/video-transcripts/set12jVQl_A.txt +++ /dev/null @@ -1,335 +0,0 @@ -Hello guys, welcome to another -installment of how do I and today I want -to talk to you about something that is -immensely important to all of us -performance. Now this is a big subject -so I'll try to cover it as much as I can -but still be relatively brief. So go -into the if you don't experience good -performance with code name one then -please talk to us in the forums check -the developer guide and uh go over other -things. I'll provide as many tips as I -can but obviously the platform is huge -and contains a lot of hidden information -in it and things sometimes don't act as -you would expect. So let's go over the -Lists -basics here of what -uh the pitfalls people usually run into -in -performance. So let's start with lists. -If you used a list and you made made use -of your own custom list model or list -renderer, then there are several -pitfalls within these two tools. So to -explain that, let's look at the API -itself. The API for the list model is -here under list. And normally if you -don't actually create your own model, -then you shouldn't have a problem. The -only problem you would have is if you -have a list that's already showing on -the screen. -that if you have a list that's already -showing on the screen, you might have a -problem if you repeatedly call uh the -default list models add item entry or -remove item -entry. And the reason behind -this is that both of these methods are -rel uh they every time you add an item -or remove an item, the list model needs -to fire an event and notify the list. So -if you do that repeatedly, add lots of -items, remove lots of items all the -time, then the data change event is -fired to the parent list and you're -effectively causing lots of events. All -of them trigger repaints and are very -expensive and they're very hard to pull. -So to change a lot of data list, you're -better off replacing the entire model -than firing this event cons constantly. -So that's really important if you're -using the default list. If you implement -your own list, however, you should make -the methods like get and get item at be -very very very fast. If you can't make -them, if you connect to the internet or -do any other heavy lifting sort of -things, then what you should do is -return immediately with fake data or any -data something and then update them -later on. It's crucial because these -methods are called all the time and very -frequently and we rely on them being -cached locally. This is hugely important -and you should observe that when making -a network uh bound list or something -like that and using the list model -properly. The same holds true for the -renderer. A common mistake people make -is when they call the renderer -component, they return a new instance of -a component. Never ever do that. That -sort of uh blocks your performance -instantly, thrushes the garbage -collector and creates uh a lot of -garbage because this method is called a -lot. It's not called per the number of -the items because we have animations -within the list and things like that. It -can be called repeatedly o repeatedly -over and over and over and over again. -So take that into consideration. -whenever you're building any form of uh -interface on top of the list -component. So that those two things are -important and I urge you to pay -attention to them. -Uh the next issue is if you're doing any -sort of drawing of strings or things -like that, the string width method -uh which is supported by fonts and -allows you to essentially measure the -width of a string is really really -really slow on some platforms, not on -all of them. And that's a feature of the -platform because uh actually measuring -the width of a string is much more -complicated than it might seem. you need -to go through the glyphing process and -everything. So, uh that's one of the -reasons why our label implementation -isn't multi-line by default -automatically -uh and why text area is much slower than -label. So if you want to build something -that's efficient, I suggest you use -labels and not try to have line breaks -automatically because you would get far -worse performance -uh because of the repeated calls to -string width. Now we have internal -optimizations for that that sort of make -lots of assumptions and try to uh reduce -the cost of string width. So it's not a -log in of uh of the cost because you -essentially need to measure every -characters with it's I I -digress. But still automatic line -breaking is far more expensive than you -deciding in advance. Okay, I want to -break a line here. So don't thrush -string with w with you'd be surprised -how how much of the performance costs we -pay in codeame one is related to that -particular method that's uh that's -difficult to optimize -uh bitmap fonts are deprecated in -codeame one we recommend you use true -type fonts for everything they perform -as fast as any font but unfortunately -they're unsupported in the low-end -devices ES uh in J2ME and stuff like -that. So we are aware of some people -still choosing to use bitmap fonts -despite their many disadvantages. One of -which is their performance. -Surprisingly, they actually work faster -than uh system fonts in some -cases, but uh they work slower on other -platforms and it's it's a matter of uh -uh it's really difficult to assess their -performance on real world devices. So, -we recommend people don't use them uh at -all. They're more expensive in terms of -memory. They're they're not -internationalizable. -they have many problems. So, we just -don't recommend people use them at all -and we have deprecated -them. Uh, small images doesn't seem like -such a performance issue and it isn't. -If you're drawing one small image, then -no big deal. It doesn't cost you almost -anything to draw an image small or -large. The problem is when you draw -small images and you draw lots of small -images. So for instance using a one -pixel image and tiling it in the -background that's really really really -slow and the reason for that is how the -GPU works. Essentially every time you -draw to the screen you need to go back -and forth between the CPU and uh the -graphic processing unit and going back -and forth there is really expensive for -doing lots of small operations. you -you're better off uh doing larger -operations. So in that same coin, it is -much faster to draw an image than draw -lots of lines or draw lots of graphic -primitives. So you're better off just -drawing a bit map image that's as large -as reasonably possible. Keep in mind -that larger images might take more -memory and might not look good on -smaller device. And my next point about -uh scaling is also very important. So -anyway, small images are generally -problematic if you draw if you choose to -make lots of small images uh small image -drawings. That's that's a big cost. -That's why when we cut borders, we -recommend you cut relatively larger im -portions of the border for the nine -piece borders. See my tutorial on that. -It's just much much faster. And we find -that this is a great piece of uh cost in -ter terms of performance for some of our -users. Now image -scale is an issue uh where images uh if -you invoke the method image scale scaled -sorry I missed that here. It should be -scaled really not image scale. Uh that -method creates a new image that is -essentially scaled to the given -resolution. The thing is that this -method while it works it -produces not the highest quality image -because it doesn't do any interpolation -or anything it cost you in terms of -performance still because we do all of -this in Java code and but it's although -it's relatively fast the problem is uh -twofold the first there's a problem in -memory here uh once you invoke image -scaled you effectively uh get an image -that is much larger in terms of RAM. Uh -the default images you get in code name -one and you I suggest you read the image -section in the developer guide. It's -it's really complex but interesting I -think -fascinating. Uh the image section in uh -and that discusses it. Images don't -actually take their full size. they take -the size of uh the compressed image, the -PNG or the JPEG that you chose, which -isn't much. However, when you invoke -scale, the image will automatically take -up uh the width, the scale width you've -selected times the height* 4 and -possibly times two again for iPhone and -other -platforms. So, that is a pretty huge -size. So for instance, if you want to -set a background image for an iPad 3, -which -is48x536, then you'd have to do -* -uh 1536 * 4 * 2 because we keep the -image twice both in RAM and and on GPU -on the OpenGL uh section. So don't use -that method whenever possible. The -substitute is to use uh the draw -version. The if we will go into the -graphics section here -uh then you have a method which isn't -ideal. Normally you'd uh the best -approach when working with icons and -things like that is to use a method like -uh -uh when working with graphics like that -is to use a method uh uh like a -multi-image or something like that when -working with icons. But if you need -something like scaling, we suggest use a -method like this which is automatically -used when uh when we set a scaled -background or things like that. This -doesn't have the cost of uh uh the RAM -overhead and it's better in general -because it's usually handled by the GPU -on devices like J2ME. Unfortunately, it -isn't supported. So this works only with -uh the smarter devices out -there. Uh the two last things uh so the -Issues -problem is in our developer guide that -this is not always uh immediately -obvious that the roundrect issue is u -problematic. Uh the roundrect issue is -related to um the fact that when we draw -round rect that is really simp easy and -simple just drawing a roundre wctck. The -problem is when we combine it with a -background image and -transparency and that's where things -start to -fail because there is uh we don't have -roundrect clipping or the ability to -construct things in such a way. So the -performance of this is remarkably -remarkably slow and the memory overhead -is use uh nine piece images. Don't use -this. This is really really really -really really really -really -slow. Uh we have it there mostly for -historical reasons. Uh we'll probably -deprecate it because it is so slow. Uh I -would strongly recommend you don't use -it and use instead uh ninepiece images. -So don't use the roundrect border uh -unless you you're doing something really -trivial and even then you're better off -using a npiece image which is properly -multi anti-aliased and etc. So don't use -that uh at least not in the current -version of code name -one. Uh last but not not least is uh -translucency. Translucency is really -fast on modern devices. You shouldn't -worry about that. it should work okay. -Obviously, if you do lots of layers of -translucency, you will pay for that. So, -take that into consideration. However, -if you're targeting older devices, -specifically Symbian, Symbian sucks in -terms of translucency. This is a -well-known issue with uh the Symbian -devices. So, just don't use translucent -images or any form of -translucency if you want good -performance on a Symbian device. they -are just really slow with it and there -isn't much we can do about that. That's -uh a known issue with that device family -and the problem is that some of our -themes use translucency quite heavily. -So they won't perform as well on that on -those particular -devices. Uh so you might want to -optimize uh those themes. Now to track -performance issues is relatively simple. -just create a blank theme or a theme -without that much information in it and -try to reproduce the issue there. This -often helps you in finding whether the -performance is related to a theme -element or to something within your -application and work through a process -of elimination uh trying to get uh an -insight into which portion is causing uh -the performance effect that you are -seeing. Obviously, this isn't always a -trivial case because without the theme, -it is often harder to to truly perceive -performance. But uh but sometimes that -helps. So, thank you again for watching -this tutorial and I hope you've enjoyed -it and uh uh learned something from it. -Thanks. diff --git a/docs/website/video-transcripts/swgT_aDsv3U.json b/docs/website/video-transcripts/swgT_aDsv3U.json deleted file mode 100644 index dd4f9997f6..0000000000 --- a/docs/website/video-transcripts/swgT_aDsv3U.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 164, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/019-the-native-code.md", - "status": "transcript-fetched", - "word_count": 947, - "youtube_id": "swgT_aDsv3U" -} diff --git a/docs/website/video-transcripts/swgT_aDsv3U.txt b/docs/website/video-transcripts/swgT_aDsv3U.txt deleted file mode 100644 index a6ea3ba140..0000000000 --- a/docs/website/video-transcripts/swgT_aDsv3U.txt +++ /dev/null @@ -1,164 +0,0 @@ -the implementation code for android and -ios is pretty trivial and is almost -identical to the code we already saw -from braintree -the best way to implement a native -interface is to send a build with the -include source option -we can then open the source code in -xcode or android studio and implement -the native interface there -then we can copy the native interface -code back into codename one -project and continue working like before -i don't always do that with android -since i find android studio to be slow -and painful to work with but if the -native interface is complicated it's -sometimes better than the alternative -another important point to notice is the -different threads -when we call the native code we usually -do so from our edt which isn't the -native os thread -if the edt is blocked or waiting on the -native thread things can get ugly -so it's important to understand which -thread you are using and for what -purpose -this is the android implementation of -the native interface -let's -go over the various pieces -pretty much every sample code from any -third-party library will assume you -derived the activity class which is -pretty common -so most will just use this to indicate -your activity class in codename one you -will need to get the current activity -luckily this is pretty easy as we have a -class called android native util -which provides a few helper method for -android native code including get -activity -as i mentioned before this method is -invoked on the codename one thread and -so it might -cause an issue since android has its own -native thread where things should -execute -run on ui thread is android's equivalent -of the edt and this means the rest of -the code will run in the android native -thread -unless stated otherwise code is expected -to run on the native os thread so this -is a good move for most cases -this might beg the question -why not do it for everything -why aren't native interface calls -implicitly invoked on the native os -thread -the answer is that it's not always true -for for instance in i o methods -we wouldn't need the dispatch to it -it's also unclear if call is synchronous -or asynchronous and dispatching includes -an overhead -moving on we can see that -the start activity for result call -that we had in the original code -however in this case we invoke it on -android native util -since we don't derive from activity -we also pass activity as an argument -start activity for result is a special -method that triggers callbacks -calls within the activity -to allow this common use case the -android native util version includes an -intent result listener class that has -passed to that method -and receives callback invocations -the callback from the braintree code can -have several results -such as ok error or cancel -just like in the ios version -here we just invoke the static callback -method on result and pass the data -the ios version of the code looks a bit -like a mess -the main reason is the long method names -that are part of our vm -they might look intimidating at first -but as we go over the code you will see -that this isn't as hard as it looks -the dispatch async call is identical to -call serially or -run on ui thread it executes the lambda -block on the native ios event dispatch -thread -the next two lines are practically -identical to the braintree code -we'll discuss the lambda callback soon -but first let's look at the present view -controller you will recall this was -invoked on self and the original code -which was the view controller in -codename one we have a global view -controller which you can use with that -syntax instead of using -self for most such code -once we get into the callback code the -logic is a bit different -it looks a bit long but you will notice -that a method invocation is just a -really long c function call -it starts with a package name -followed by the class name followed by -the argument types and possibly by the -return type -all of which are required to make a -method unique and see -the arguments we pass -start with a constant that allows us to -pass additional arguments -there are several argument types like -this i suggest checking out the -developer guide to fully understand this -argument but it generally represents the -thread context -the next argument is the string -for which we use the special from ns -string method to generate a javalang -string -since the method is static -we don't need to pass an object context -or deal with the v table -this makes the call far simpler -a lot of developers shy away from native -interfaces which is a shame -they are slightly challenging but they -are -surmountable challenges -some things are cryptic but as you saw -in the example we practically copied and -pasted the native code from braintree -with very few changes and this was a -relatively complex api to support -a huge pain is in the error logs on both -platforms -android is actually regressed in this -sense and became far worse -i would suggest asking our team on the -forum or stack overflow when you are -stuck with such errors google is -hugely helpful in finding solution for -these sort of errors -working with the native ide to implement -the native interface stubs is a great -time saver especially if you are a -relative novice -thanks for watching i hope you found -this educational diff --git a/docs/website/video-transcripts/sxCADu-SjpQ.json b/docs/website/video-transcripts/sxCADu-SjpQ.json deleted file mode 100644 index e83548057c..0000000000 --- a/docs/website/video-transcripts/sxCADu-SjpQ.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 111, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 652, - "youtube_id": "sxCADu-SjpQ" -} diff --git a/docs/website/video-transcripts/sxCADu-SjpQ.txt b/docs/website/video-transcripts/sxCADu-SjpQ.txt deleted file mode 100644 index a9f91cd5cc..0000000000 --- a/docs/website/video-transcripts/sxCADu-SjpQ.txt +++ /dev/null @@ -1,111 +0,0 @@ -we are finally ready to tie this into -the ui -of the client -the search ui -is in a dedicated form -it works as you type and lets you toggle -between people slash post search mode by -pressing the buttons on the top right -corner -let's dive into the code starting with -the fields and general types -this is set to true when we are in -people search mode -we use this flag to detect if the search -text actually changed -the timer waits to make sure the user -isn't still typing so we won't send too -many search queries at once -the time of the last search helps us -decide how long we should wait for the -upcoming search query so we don't send -too many queries -this is the search text field where the -user can type -search results add up -into the infinite container for paging -slash refresh capabilities -if search text is less than two -characters we shouldn't start searching -this is the standard page calculation -logic that we have in other infinite -container classes -we build results for people or posts in -the same way -there are two create entry methods that -cover the logic for both cases -the resulting components are returned on -null in the case of an empty set -this is pretty direct and should be -relatively obvious -the two big pieces that are obviously -missing from this code are the create -entry methods so let's get to them -the version that accepts the user object -just create a creates a multi button for -that -the same is true for the post entry -i'll get to the event handling code -later -the user form and post form mentioned -here don't exist yet so i'll discuss -them after finishing the search class -we use border layout so the infinite -container can fit the cen in the center -there is no other reason -the title ui id makes the search field -fit into the title area -in ios the title is centered by default -and that doesn't look good while editing -so we left a line explicitly for this -case -this is the search operation with every -change to the text field we can we call -the update search method which performs -the actual search -set title component places the component -in the title area instead of the label -that's there by default -this closes the search form by going to -the previous form -there are two buttons in the title area -that let us toggle people and post -search modes we invoke refresh when -there is a change which triggers a call -to fetch components in the infinite -container for the first page -when we show the form -the -search field will instantly launch into -editing mode -this is important with a virtual -keyboard so it won't just have focus it -will actually be in edit mode -this leads us to the update search -method which performs the actual search -logic -if the text doesn't change -we don't do anything -if we have a search pending we kill that -search request since this -all runs on the adt this is a search -that didn't start yet -if we already started the search we make -sure it wasn't too soon otherwise we'll -postpone the search for later -when the timer elapses we update the -search time and refresh -notice that since this is a ui timer -elapsing happens on the edt -if there is nothing pending we just do -the search right now and refresh the -infinite container -with this the search form should work -and show search results instantly -the one last piece of the puzzle -is plugging it into the ui which is -actually trivial -we add this line to the main form -constructor with that you can now click -the search button and it will actually -work with the data from the database diff --git a/docs/website/video-transcripts/tBBtpdz-JJg.json b/docs/website/video-transcripts/tBBtpdz-JJg.json deleted file mode 100644 index 6d4b741a46..0000000000 --- a/docs/website/video-transcripts/tBBtpdz-JJg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 113, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 687, - "youtube_id": "tBBtpdz-JJg" -} diff --git a/docs/website/video-transcripts/tBBtpdz-JJg.txt b/docs/website/video-transcripts/tBBtpdz-JJg.txt deleted file mode 100644 index 2447e188a9..0000000000 --- a/docs/website/video-transcripts/tBBtpdz-JJg.txt +++ /dev/null @@ -1,113 +0,0 @@ -up until now the app focused on a very -narrow use case -and the side menu was just for show -i won't fill up the entire app as the -for uber app is very wide and nuanced -but i would like to add a few essential -ui elements to show just how easy it is -and how much work we already did in the -server to prepare ourselves for this -the uber signup process prompts the user -for details which is a nuance we skipped -early on -i won't rectify it but i will map the -menu item for settings to show the -settings form -and i'll map a click on the user's -avatar to the edit account form -this means we need to add two new forms -settings form and edit account form -i didn't take the full ui of the -settings form and i didn't implement -everything -however the basic design is there and it -should provide a good framework for -additional forms -in this application -the settings form is encapsulated in -this code -this is the standard header for forms -with the black title -it initializes the ui and adds the -scroll down collapse effect -we defined this method before and it -hasn't changed -i changed get avatar to return an image -as a callback -this can happen if the image wasn't -downloaded yet from the server -the rest of the code is mostly ui mockup -meant to replicate some of the ui -elements in the uber app -most of these things aren't challenging -notice that i have new create entry -methods -in common code -i refactored them from the completion -container as is -i added a relatively trivial create -separator method which i reused later in -edit account form as well -for completeness this is the create -separator method from common code -there isn't much to discuss about that -method so let's move on to the next step -the next step is a review of the changes -to the get avatar method most of the -code is the same as it was before -all of this didn't change at all -we still need the mask object regardless -of the outcome as we will need to mask -an image avatar from the server to in -order to give it the route effect -the reason we need to do it -all of that in the client is -that only the client knows the size of -the image it needs -this will also give give the server -flexibility in the future to increase -image resolution slash size -the fetch avatar method might fetch the -avatar image from local storage or -download it from the internet -when it's done it will invoke the -callback method with the image object -the image was fetched from local storage -the avatar might already be there so we -don't need to do anything else -otherwise we can continue with the same -code we had before in the get avatar -method -now let's look at the fetch avatar -method in user service -which we used in that code -i made two variants of the method -one accepts the id of the user whose -avatar we are fetching -and the other one uses our id -right now i only use the one that -doesn't take the id however it should be -trivial to show the image of the driver -scheduled to pick us up next to his name -this is a web service i already mapped -in the server code a while back -the image is returned for the user with -the given id -in the path -a new user won't have an avatar and we -will get a -full response -which is 100 valid for this case -without this line -we would get a default error prompt -which we don't want -the built-in method in connection -requests -checks if an image already exists in -storage under the given name -and if so invokes the callback with that -name -otherwise it adds the request to the -queue downloads the image storage -file name loads it and invokes the -callback with that image object diff --git a/docs/website/video-transcripts/tWMVUDScyfQ.json b/docs/website/video-transcripts/tWMVUDScyfQ.json deleted file mode 100644 index b291f82cd9..0000000000 --- a/docs/website/video-transcripts/tWMVUDScyfQ.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 109, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 594, - "youtube_id": "tWMVUDScyfQ" -} diff --git a/docs/website/video-transcripts/tWMVUDScyfQ.txt b/docs/website/video-transcripts/tWMVUDScyfQ.txt deleted file mode 100644 index a686fc0ef7..0000000000 --- a/docs/website/video-transcripts/tWMVUDScyfQ.txt +++ /dev/null @@ -1,109 +0,0 @@ -the next big step is making this into a -real app by implementing a lot of -the details such as comments search and -settings -each one of these seems like a huge task -but surprisingly they aren't as hard as -one would have expected -for search we need to leap back into the -server code to implement search -on the surface search seems like a -complex problem but thankfully spring -boot and hibernate magically solve that -for us -hibernate is the default jpa -implementation in spring boot -one of its features is integration with -lucine elasticsearch -which you can read about at the -hibernate.org -search url -springboot makes that even simpler -we can support search by making a small -change -to the palm.xml file -then we need to configure search in the -application.properties file as such -these just mean the search index is -saved into the file system which is the -simplest approach for implementing this -there is a lot of power behind this tool -and you can learn more about it in their -website -users can't search everything that would -be bad as we don't want the ability to -search through passwords etc -we can add search support by marking an -entity with the at indexed annotation -then marking each field that should be -indexed with the at field annotation -notice that we should use the -org.hibernate.search.annotations package -for these annotations as there is -another indexed class in spring boot -we only index the name of the user as we -don't necessarily want to expose private -data through search -we'll do a similar change to post which -contains fields worth indexing -we only index the text content as other -content might produce weird results in -search -i go further with searching comments etc -but for simplicity i stopped here -with that the entities should be -completely searchable -we now need to expose the search -functionality in the service layer -this warrants a brand new search service -clause -the entity manager provides direct -access to the underlying jpa -implementation -normally spring boot hides that but for -search we need direct access -this is a small helper method that -shortens the boilerplate syntax of f to -ft -instead of that code -we first need to build a search -search database this is a one-time -operation once the database is built -hibernate will keep the database up to -date -we have two types of searches so i -combine their common logic into this -method -the query builder searches a specific -entity -for instance post user etc once we have -it we can construct a complex search -query -the search for keywords is marked as -fuzzy which means it will find answers -that don't match complex completely for -the given text -we create the query and issue it -we give it a range of results so we can -page through the query query results -like we do with other pageable content -searching people or posts becomes a -matter of passing the entity type field -names and paging numbers -we then convert the entity result to a -dow object -as return types -post searches are exactly the same same -only with post objects instead of user -objects -that was a bit of code but the premise -is relatively simple and heavy lifting -is done by hibernate -the interesting part is the query -builder portion -you can customize the query builder with -any fields you wish to search and -complex constraints -related to the search but for the simple -implementation we have right now this -will do just fine diff --git a/docs/website/video-transcripts/tiY5ofnJop8.json b/docs/website/video-transcripts/tiY5ofnJop8.json deleted file mode 100644 index eca1ea4a71..0000000000 --- a/docs/website/video-transcripts/tiY5ofnJop8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 120, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 629, - "youtube_id": "tiY5ofnJop8" -} diff --git a/docs/website/video-transcripts/tiY5ofnJop8.txt b/docs/website/video-transcripts/tiY5ofnJop8.txt deleted file mode 100644 index 0c63be12ac..0000000000 --- a/docs/website/video-transcripts/tiY5ofnJop8.txt +++ /dev/null @@ -1,120 +0,0 @@ -the restaurant app maker -is an application that we will build -together based on the restaurant app i -introduced earlier -it generates a custom application -for a specific restaurant -unlike the restaurant app which is -pretty standard -and for which we could derive -inspiration from existing apps -this is a relatively fresh idea there -aren't many app makers out there -and the few that are in the market are -web-based -so we need to design something without -any psd -and still have something that looks -reasonably good -when when we are faced with a task like -this the solution is the construction -i break the problem into smaller -manageable problems and attack each one -individually -the best place to start is the user -interface even though we don't have a -design -it helps to visualize a problem before -we get to the implementation details -and ui is a great place to start -this is the first page of details that -the restaurant app will need within it -so we'll need to build an app that -accepts all of this information and some -of the information from the next page -notice that this is only a partial list -and some of the things here like color -scheme or fonts -can be pretty challenging -this is a mock-up design i created after -looking at several app maker sites -i understood most of them use a -tabbed-based ui so -that's where i started -i took the information that we need from -the list of items and placed it into the -app -the thing is -that this was a bad direction -once i drew that it became relatively -clear that this isn't what i want -i left it because i think it's useful -for you to see the path i took to reach -the end result -the other issue is the boring details -stuff -one of the most important things for any -app -is a first impression -it's crucial that the first screen of -the app -looks good by default and keeps users -engaged otherwise they might skip the -app entirely -opening with the details -makes sense for a developer but doesn't -make sense from a user interface -design standpoint -this is my second attempt -the elements on the left are the side -menu elements -the core design is inspired by material -design with the floating action button -and toolbar elements -dishes use the material design grid -design -and focus on the food image -this is important as pictures sell -a lot -of the more complex details have default -values -so -a -user can launch the app and instantly -see his custom app in action -with almost no work -to see the app you can just press the -play button which will preview the app -locally -a lot of the elements in the title are -editable -you can edit the title background using -the edit background -and this will impact the final app -both the title and tagline are text -fields -and can be edited in place -i considered making the dish editable in -place -but eventually decided to have a -separate form -to edit it -as we can go into a lot of details -for the new dish -edit dish form i drew inspiration from -material design again -i used a gradient so the title will be -readable on top of of the dish image and -use the material design floating action -button -that is placed on the border of the -title -the reason for that placement was so we -don't click delete dish -or the description -by accident -the camera button is meant as a generic -action to pick an image not necessarily -through a camera -i chose to enable delete through here -as it removes clutter from the main form diff --git a/docs/website/video-transcripts/tjgyigzxo5U.json b/docs/website/video-transcripts/tjgyigzxo5U.json deleted file mode 100644 index 7ad43ab21b..0000000000 --- a/docs/website/video-transcripts/tjgyigzxo5U.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 108, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 613, - "youtube_id": "tjgyigzxo5U" -} diff --git a/docs/website/video-transcripts/tjgyigzxo5U.txt b/docs/website/video-transcripts/tjgyigzxo5U.txt deleted file mode 100644 index 35c00d2ea4..0000000000 --- a/docs/website/video-transcripts/tjgyigzxo5U.txt +++ /dev/null @@ -1,108 +0,0 @@ -the next obvious entity would be post -a user posts to facebook so it makes -sense to have an entity for that -the post object on the server is again -very similar to the post object in the -mobile client -as they represent the same logical data -we still need the layer of abstraction -as the server might require additional -information that might not be applicable -to the client -a post is made by a user this allows us -to query and maintain relational -integrity on the posts -the date and title and content of the -post are pretty self-explanatory they -contain everything that's important -about the post -visibility represents -whether the post is public or for -friends only similarly to the media -object -styling maps directly to the client-side -object of the same name representing the -style of the post such as background -type etc -we need to add a comments entity as well -just like we have in the client side -notice that they are ordered based on -date within the relation -likes can't be counter a counter -we need to track the individual users -who liked -we need dao objects both for the post -and the comments of the post -as it was with the other entities -the rest of the code is just getters and -setters -i'll get to the dao object soon -but i'd like to first start with a -comment object -the user who made the comment isn't -necessarily the guy who made the post -the parent comment can be null -but can point at a parent -comment for nesting discussion -a comment has a date and text notice it -doesn't have a parent post as we don't -need to -we get the comments through the post so -we don't need the back reference -the constructor and dial are pretty -standard for this type of object -the rest of the code is getters and -setters -there are four additional blocks of -boilerplate we need to add to get this -to work -first we need the post dow -the fields are pretty similar to the -entity again with the dow object -objects replaced -the constructors are similar to the -other dows -the rest are getters and setters as -usual -comment dao is more of the same -there is one tiny difference here -instead of sending the comment dial -object we pass the primary key of the -parent if applicable -again the constructors are as usual -the getters and setters take up the rest -of the space -finally we need the crude repository -objects -for both entities i'll start with post -repository -this finder method finds a post by -specific user with a specific visibility -for instance public or friend -this finder method finds all the posts -by a user before invoking it we need to -make sure the user has permission to do -so -notice that the find query returns a -page object instead of a list and -accepts a pageable value -this is built in support in spring for -the process of paging -paging allows us to run a query and go -through the results one page at a time -we can request a specific page from the -result and specify the amount of items -we expect to receive -i'll go over all of these when dealing -with the service classes -notice i did this for elements where we -should expect a large volume of -responses -that should be sent to the end user -the last piece of information is the -comment repository which has nothing -interesting -with that -post and comment entities are done we -are left with one last entity that has a -client counterpart diff --git a/docs/website/video-transcripts/ts-Q1zBGXco.json b/docs/website/video-transcripts/ts-Q1zBGXco.json deleted file mode 100644 index cb236dc6b2..0000000000 --- a/docs/website/video-transcripts/ts-Q1zBGXco.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 86, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 424, - "youtube_id": "ts-Q1zBGXco" -} diff --git a/docs/website/video-transcripts/ts-Q1zBGXco.txt b/docs/website/video-transcripts/ts-Q1zBGXco.txt deleted file mode 100644 index f3fdc98d8f..0000000000 --- a/docs/website/video-transcripts/ts-Q1zBGXco.txt +++ /dev/null @@ -1,86 +0,0 @@ -in this final part we'll discuss the -checkout form -yes i said form -i know it looks like a dialogue but i -chose to implement it as a form and if -you look at it closely you will see why -the functionality of the checkout ui -is outside of the receipt section -which we would normally think of as the -dialog -both the x button and -the order button ours are outside of -that ui -hence this should appear as a form -to get this appearance -we grab a screenshot of the existing -form -by drawing it onto an image -we then blur and tint the screenshot -and set it as the background of our form -we also -use cover transition which provides that -cool ios style transition -for the checkout ui -the close button is just a button placed -in the north of the form -i considered using a toolbar and -eventually chose to just place -components in the north area -both -would have worked -out to -identical results -so there was no real reason to use -components -here i construct the title and close -button -it's mostly one long line of code -if we ignore the code required to -implement the x -the checkout button is placed in the -south -again -not a lot of functionality for that -the dish container code is a bit -simplistic -i extracted it to a separate method -that just constructs a border layout -entry with the image title and the price -you might recall the dialogue top uid -from css -and cutting tutorial -this is effectively the background of -the receipt -the same would be true for the dialog -bottom -which is also from css -i can go on a bit -but most of the functionality of the app -should be straightforward by now -one of the main things i hope you -learned from this module is the -attention to detail and compromises -required in building a mobile app -when building an app i always start with -the mockup -it lets me gain perspective of the -functionality and communicate with -non-technical people -about the effort -the original psd -looked trivial -but some tricks -like the even sized container for the -grid layout or even -the layout i chose -weren't as simple -other effects that might have seemed -hard like the receipt ui -were -much easier -just a nine piece border -thanks for finishing this module i hope -everything was clear to you and please -feel free to ask questions and make -comments so we can improve diff --git a/docs/website/video-transcripts/tugJ_7xMdzs.json b/docs/website/video-transcripts/tugJ_7xMdzs.json deleted file mode 100644 index 9f7889f19a..0000000000 --- a/docs/website/video-transcripts/tugJ_7xMdzs.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 331, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 5048, - "youtube_id": "tugJ_7xMdzs" -} diff --git a/docs/website/video-transcripts/tugJ_7xMdzs.txt b/docs/website/video-transcripts/tugJ_7xMdzs.txt deleted file mode 100644 index 70f71166b5..0000000000 --- a/docs/website/video-transcripts/tugJ_7xMdzs.txt +++ /dev/null @@ -1,331 +0,0 @@ -in this tutorial i'm going to talk about spring boot and introduce you and i don't expect -that you know java e or anything like that and or servlets or anything -really because the and that's part of the reason why i -chose spring boot i'm assuming most of you know servlets but i don't know that for sure -and it's uh teaching that if you don't know that is much much harder spring -boot is easy it's modern it's popular pretty much everyone is picking it up -and for good reason it's really really really easy to build it it's as easy as building a command line application and -practice frankly it is a command line application in the most basic sense -it's trivial to install and it integrates with all the web servers services the databases -everything that you basically need to build an app and it's java it's completely java in -even a more fundamental way than enterprise java which is a bit uh -overly cluttered and spring strips away a lot of these complexities -but still includes all the power and it's integrated with all the major -ides so uh i'm using netbeans because of force of habit mostly -uh and it's a good idea also it's a very good ide -and there's also intellij obviously an eclipse eclipse is actually supported officially by spring so -it's got its own dedicated ide for that and they all work really well it works with great old works with maven it's -a really really nice approach to building apps but i didn't answer exactly what so this is the official -explanation from the spring website you can pause and look at what it says in terms of the -text but the gist of it is really simple -it's it tries to mimic a lot of the ideas from other uh languages and frameworks by making -everything by convention so spring just creates the application because we all created the same -applications and we generally when we build an app with java e 90 of us just -go on stack overflow and copy and paste this huge block of code that's boilerplate that's pretty much what -everyone does because everyone does the same exact application over and over and over again -and they just copy all that boilerplate and start building an app spring essentially says -forget that you don't the spring boot it just says forget that we'll do the boilerplate for you because -you're doing the same we'll let you customize it if you don't need this or if you need that to work a -bit differently but generally you're doing the same you're just replacing the database you're blessing placing this address you're doing that -and it just gets you up and running with nothing practically -and it's instantly instantly includes all the services that you need -so that's really convenient so generally the output of spring boot is just one -jar one fat fat fat jar that includes tomcat or jetty or -whatever you chose inside of it literally inside of the jar so -when you run that jar using java minus jar and the name of your spring boot project literally tomcat -launches connects to databases does everything that you needed to do with that one jar and that's really -really really convenient when you want to deploy an update or anything like that you just -it's just a jar you don't need to do all that much to get an update up and running -and that's really really convenient and -it integrates with everything including uh any database i specifically chose to use mysql -now the reason i chose to use mysql is very uh it had a better installer for mac os -which i use so i i very much was in conflict between mysql or maria i didn't -think postgres because i have some experience with it but not enough and uh myself is more popular and -usually it's a question i get more frequently working with mysql rather than postgres -so i chose to go with mysql there's also maria db which looks really great i -haven't worked with it much but it just didn't have an installer for mac os that's the main reason why i -didn't pick that and picked mysql instead which is much easier to install here -and but they both should be mostly interchangeable so if you prefer mariadb -go for it and one of the nice things about -spring is that it works with jpa or hibernate and that's an object-relational mapping tool -that allows you to map an object to uh fields within the table and it -also creates the tables and the sql implicitly so this is really easy to do in spring -boot and it's even less boilerplate than you'd normally have with jpa -it's still jpa but the nice thing it's also a standard so if you're familiar with java e this -should be instantly familiar and it's even easier than it is in java -e to work with it so -it's mysql is as a database it's familiar it's simple and it performs -really really well and it's very powerful it scales to huge numbers -but the obvious question i i got when i said okay we'll go with mysql to some of my friends is uh -why not no sql because that's the big thing and people are building with -and with lots of other solutions based on nosql it's very popular in the startup world today and -for good reason it's really easy to do some of the more social type of applications with a document based -storage rather than a tabular based storage although -sometimes that's overblown by people who don't really who aren't used to building uh sql-based -applications so when we started codename one we started -it on app engine from google and we're sort of um didn't really have a choice the only -option to store back then this changed since but back then the only option to store was to use google's big table -and we thought okay that one shouldn't be a big deal to use an sql database to this day it's a remarkable pain that -we're stuck with that um i don't want to say a bad word in this -video so imagine me cursing to high heaven and that's pretty much how i feel about -google app engine and about their data store api which is one of the worst things i've ever worked -with by a pretty good margin and uh -the reason for that isn't so much um the implementation it's designed to -scale at google levels of scale and if you need just you know just tens -of millions or hundreds of millions and not trillions and billions of uh operations -and literally that's the scale of difference then you'd be served better by sql -because sql can do millions really really easily and even billions to some degree -and to show that to prove that google itself uses sql or at least used sql for -adsense for the things that make money because it's more reliable and easier to work with when -you need to actually have accountability nosql is really really good -at storing very abstract data that has no form and by definition -abstract data this has no form can't be queried easily and it's all -uh essentially you throw away the dba but you have to do the work of the dba now and you're throwing away all of the -tools for reporting and all of that stuff but you have to do that work now -so it doesn't really solve those problems it just lays them on you instead of laying it on -the dba the the person that manages the database and that's really really really painful -if you need to just you know a simple question how many users do you have that do x -and with nosql it's sometimes hard because you need to index things and do all sorts of -operations in order to actually do a query so there's solutions like bigquery and stuff like that but then you have to -share the data and don't get me started and the complexities related to that -and sql solved all of those things really really efficient efficiently and at scale and added reliability at a -level that's amazing so sql works -now the thing is that moving to sql from nosql is almost impossible because your data is all over the place now and just -shoving it into the really rigid structure of sql would be -almost impossible it's it's hard to do that but going the other way around if -you find that sql is restrictive moving to nosql is actually a very easy -move so i'd rather start with sql because it's easy because i can experiment i can -query it works it's reliable and not worry about the scale issue i mean -huge data operations run on mysql and work fine -and it's it can scale well uh if we ever need to go further that's -possible so i'd rather start from that always it's easier surprisingly -so let's start with a small mini mission it's not an actual mission and missions themselves won't be in this format but i -don't want this to be a video about installing stuff so i'm not going to -start showing you how to install mysql also because i use a mac and it's possible that probable that you are not -and just firing up windows and recording videos and screencasts for something that frankly will change -and all the time and probably has lots of videos on it on -the internet i just looked at the tutorials on the mysql website and -installed through there it worked really really easy for the most part the problem -i ran into two problems when installing mysql on my machine and currently and that's uh -the installer doesn't activate activate the database -so you actually need to go to the system settings and nicely it actually adds an entry to the mac os system settings for -mysql and you literally click there and activate the database and you can set it to -launch on startup and that's really really nice and once that exists you can just launch -mysql which is in a weird location for mac and not in the path but fine -and it will prompt you for a password that you got during installation -and once you do that you're effectively logged in to the database -and there you can actually create a database and we'll create a database called test and -in that database you we will essentially write stuff later on -now one of the things i ran into as well is that newer versions of mysql -uh as a security measure uh require that you change the password uh upon -installation even though the password is completely random you need to to change it before actually creating any database -so this is the line i had to use obviously you need to replace your new password with -the password you have and maybe the user root which uh in my case that was the user -during installation it might be different it's supposed to be the user of the user that installed -in this case root on a mac i'm guessing normally you'd want the user to be mysql -but that's linux where we will be deploying eventually -right now this is mostly around uh the local machine so -let's move on to spring boot which is probably more interesting than mysql which as i said covered quite a lot -and so is spring boot by the way you can google pretty much anything about it and there's tons of resources on the -internet this is just to show you the very basic -thing and this will serve also as a basis for following tutorials that i'm going to do -which is why i'm doing it and not just sending you off to read something on the internet -so i just used the id plug-in as is for netbeans i didn't test this on the other -ides i'm assuming it's as simple as that and -in the netbeans id when i do a new project i have once the plugin is installed -i have an option to select a spring boot initialized initializer project now -notice this is an initializer project and not not the basic one without anything -so it will actually offer you to select entries to add -into the project and by the way the netbeans project that i provided is just a maven project so you can open it in -any id that supports maven which is all ids and just work with it as is it it's not -physically netbeans project per se it's a generic project so it will work in eclipse it will work in intellij -so if you just want to take what i did and open it in your favorite id just do that -so the the new project uh staging the wizard -uh provides uh prompts you for lots of details about the -output project the one really important thing here is the package name because the package -name includes the default application and -all of the classes related need to be below that package to to be detected and injected by spring -and uh so you need to pay attention to that -the next stage in the the wizard is uh where we select the packages that we'd -like to support from spring boot so i picked uh -several important packages the first one is web obviously so we'll be able to do servlets and stuff like that -although we don't need it as much what we need is uh jersey which allows us to do -web services and we'll use that websockets obviously -jpa for storage and mysql for writing to the database which includes -jdbc driver and everything that you need to connect to the mysql database there's -lots of others including redness for replication and uh and -all sorts of things that would be really really useful i'm -ignoring most of them for simplicity i don't want to over burden here obviously -when it comes to scale we can add things later and i'm a big believer in doing the -minimum possible and no more than that because everything that you add effectively um -makes things more complicated later on so only add when you need it that's a -general philosophy when you're building stuff like this so the final stage is just you know the app -name as it will appear in the file system and uh and an id -uh after the creation the app doesn't have any um -jars in it so it would seem like it doesn't compile but maven has an option -the id at least in netbeans you can just right click the id and say resolve conf -missing libraries and it will offer you to update everything from maven and once you update the library everything will -run and just work as you'd expect which is actually nice it was one of the -very rare few times that maven worked for me properly so that's good -the next stage is we need to configure that's the only configuration i actually had to do to the basis app basic app in -order for it to work normally when you work with java e it's configuration hill -it's all tons of xmls or annotations which include all sorts of configuration -this took some work to discover but not much really -the main problem i had was with the database table dropping issue which was -uh it would clear the database uh tables every time that i built the app -and that was a bit painful other than that everything worked very seamlessly you can just use -these lines in the application properties which allow you to configure various behaviors of -of the spring application of the spring boot application and that just configures it to work with -the mysql database if you want to work with a different database you can very easily do so -by just using these configurations so it's it's pretty cool -the code that's generated when you create a new app is this that's the entire application and you'll notice -first of all that it's a main method which is kind of weird for a person -coming from java ee but this is literally that's what spring boot is it's a main application that -just runs spring which runs everything within it that means it's just literally a main file -and that's pretty nice so let's build something that's actually useful based on -this uh minimal minimal amount of effort and to -me just getting something running is hugely important so we'll first start with the jpa -because storage is something we'll obviously need we always need storage for something in the server -so uh we'll use jpa which is specifically the hibernate -implementation and it allows us to as i said before map objects directly to the database i'm not -going to mention sql at all here because i don't like sql i don't like -working with it all that much i know i said i like i like the databases i like the tabular structure writing sql is -a unpleasant at best so jpa really simplifies that for for me -and for my personal tastes i know i'm not necessarily not everyone agrees with that uh -approach uh but i prefer that i like the abstraction the jpa provides and i prefer working with that over -direct sql when reasonably possible and -normally you can build things that are very performant in that way it's debatable obviously or everything -is uh i'm not i'm not going to dig too much into a jpa i'm just going to sort of -show you uh what it is how it works and uh in the very basic sense so if you're not -familiar with jpa it's a standard java e api notice that all the imports here which i made sure to include -they're all javax persistence so they're all a standard api they're not something unique to -string and all of these fields essentially get persisted that means the database table -when you look at it will include an id column which will be able to generate an auto incremented which is really nice -because it's it's really hard to do that um in a portable way -uh the you'll have a column called name marked deleted etc and the owner -now this is a table that will contain a list of items and we'll use -this to provide a sort of item api later on -i trimmed some of the code uh so it will fit in a slide you know obviously there's lots of boilerplate -here still but it's mostly getters and setters and then the constructor values -that's mostly java boilerplate it's not something that spring can do much about -also because that's a java standard so it's not in their control now item repository is a very -interesting thing and that is a spin class now it extends crude repository now -uh and i included here a special method i could have left it blank you know just the interface itself with nothing but i -included a special method called find by owner which will return uh the items -based on their owner a list of items now crude if you're unfamiliar with that -term means essentially the operations you normally do on a database which is -create read update and delete and most applications that's what we need to do you know pretty much all -applications work as just taking entries in a database creating a -new entry reading it you know listing and entries and things like that updating for -changes and deleting when necessary and this uh interface is spring's -abstraction for the various crude operations that exist -and it's automatically created by spring that means even the find by owner method -that you see here that's implemented by spring i don't need to write that code at all it will -just implement that and that's really really nice that it just works -and if you'll look at the next slide you'll see that -we have literally uh that value and it's automatically injected and created when -we need it now this is the item service class uh the class declaration is sort of -on the top here as you can see and notice that above the class declaration there is a request mapping annotation -and it maps to slash item that means that in the server there will be a url called slash item -and it will map to the methods within this class -now the first method and by the way the names of the methods such as stuff i picked up i could have named the methods -uh zubavel or whatever and they would still work because they -were they map based on the uh the method type the request method so -for instance the first method is a get method notice it's mapped by request mapping to get the second one is mapped -by request mapping to put and that means the first method will be called when the http method with get is -invoked on uh slash item and the second one will again for slash -item but with the method put uh i hope you're familiar with http -methods where you can pass get put post delete uh etc so -these map directly to that now notice they don't have the typical of -servlet request response if you're used to java ee and the reason is that they're mapped directly to -json so uh which is the default here so when we request uh -a json url uh we when we send a get request and return an -array of items then literally that is translated to a json -representation and that json representation is returned -in the body of the response as you can see by the response body annotation -and when we do a get or or go in this particular case -uh i literally uh find the id of the entry if if a specific entry was asked for and -if not i use find by owner which was mentioned earlier -similarly if you look at the put when someone submits an item and as you can see -literally the item is submitted and created the submit would literally be a -json body and i'll show you later how this is used and remember -json is passed and automatically translated to a java object which is remarkable -and that's jersey by the way doing all of that it's not something that spring boot does spring boot simplifies some of -this this is actually the work of the jersey standard so this is this is part of java e -partially now you'll notice below that i just call repo dot save and that's again the crude -interface that we mentioned earlier it allows us to just call save and pass in the item and that item just gets stored -into the database which is spectacular if -it's just really easy i can do this with persist and lots of other things in the jpa but this is just easier -slightly less boilerplate code and that's it essentially i have a -simple get and put operations with almost no effort -again notice that the item repository here is injected -by spring i literally didn't implement that that interface and notice i don't just sign into it anywhere i just use it -as if it's already there magically so normally if this wasn't with spring -boot it would be uh null and this is really nice -uh i'm return one thing i want to highlight is that i'm returning the item and -you'll notice the item was an entity now that's really bad design -i'm doing it on purpose because i'm trying to teach something how it's -how you can get something out quickly and sometimes you know you do bad design choices -to get something out and i don't think that's necessarily bad because -if you'll build the best designed project in the world that never sees the light of day then that's the worst -design you can possibly build so it's important to be realistic to not -try to reach to the sky of some theoretical level of abstraction -and doing extra work because of theoretical value i'm generally against that -but returning the entity instance could be dangerous because -i'm right now all the data that's there isn't private so i can just return it and it's okay -but in theory if i wanted to build a really secure application i wouldn't necessarily want the user to -know the id of the line within the database that includes the entity the item -because if i have a weakness someplace else in the code he might be able to use his knowledge of the -database primary key to circumvent something and the fact that primary keys also sequential -provide some form of weakness so it's often you more useful to use -a sort of unguessable hash value i'm getting a bit carried away with a -complexity analogy here but generally returning uh an internal representation -is bad and you normally want to have something in between obviously -the design is very much um we can talk about that quite a lot the design is very much -a set of compromises because when you return if i would have created an item -dao something like that uh it would have been generally a copy and paste in the current situation and -that's not great design either so whatever i would have done the system is too simple -to do good design and too simple uh in that sense i prefer to to keep it -relatively simple as is i'm just highlighting this -so you don't think that's a good approach a better approach would be to work with data access objects that -are specifically designed to return the json correctly and that way you separate -the actual database data that's stored and the communication layer which is something that's logic that logically -should be separate both for security and for maintainability in the future into the future -so the next thing uh we want to do is actually show you how -this works because you know we did all of that now how does it does it map so -i'm not sure if you're familiar with curl i'm guessing not so much it's a really nice command -line utility that allows you to essentially send http requests now i'm not using codename one to do these -connections because i'm keeping it to the next presentation -where i'll show you how it's done in codename one and that's a lengthy presentation in its -own right so i'd rather use this approach and then if that's still unclear you can wait for -that presentation but generally the crow command just requests and the -url with given values and returns something in this particular case i wanted to to -do the put request so curl minus h adds a header in this case the header is -content type application json the next header is minus x which where we determine the type of http method in -this case put which means it goes to the method uh for adding an entry -the next thing represents the body of the entry in this case it's um -the literally the item fields you'll notice they map to the item fields name -marked deleted owner uh etc and the last entry is the url remember -the url is item in this case localhost item and the output for that because you -remember that method returned the instance of the item is exactly that it's the same value that i -sent because that's what was received in the server with one extra entry and that's the id -which was auto generated now you'll notice i didn't pass the id when you pass it which implicitly set it to null -now the server when it gets another id understands it needs to create it and then uh -works with that similarly we can fetch an items items -with just accrual request this will set use cruel in the default settings which is that just uh send a get request -in this particular case i passed the owner value as shy and because i made that previous request three times you'll -notice i have three entities uh from the database that all have -these uh values so that that's essentially -shows you how this works and that you can add pretty much anything into that uh -database so i hope this was educational and helpful -for you there's lots of resources out there on spring boot i did this video so you -don't go all over the place because these resources are often over ambitious and try to teach you lots -of things and these are the things that i need you to know and i'll try to teach you the things -that i need you to know for the successful completion of the boot camp -but obviously there's a huge sea of valuable spring and spring boot -resources that you can delve into if you want to go deeper i'm not going to talk about all of the -complexities and the security elements and how to set up mysql and because this -never ends and frankly people did better than me on the internet -i do hope you understood how the rest4 service works and how easy it is to build it i didn't yet go into websockets -which i will go probably later uh thank you for watching this and i hope -again that you enjoyed it and wasn't too heavy diff --git a/docs/website/video-transcripts/uYCCbE70kE0.json b/docs/website/video-transcripts/uYCCbE70kE0.json deleted file mode 100644 index 8c261ebf61..0000000000 --- a/docs/website/video-transcripts/uYCCbE70kE0.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 128, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 646, - "youtube_id": "uYCCbE70kE0" -} diff --git a/docs/website/video-transcripts/uYCCbE70kE0.txt b/docs/website/video-transcripts/uYCCbE70kE0.txt deleted file mode 100644 index 8b261b9c8b..0000000000 --- a/docs/website/video-transcripts/uYCCbE70kE0.txt +++ /dev/null @@ -1,128 +0,0 @@ -the star form is a pretty large class -and as such i've split the segment into -multiple parts -let's move on to the pointer released -method -this method is a special case as it -seems to behave -like the other methods -but then it binds functionality to the -else -clause -first though -why pointer released and not pointer -pressed -by convention all events -in all operating systems that might -trigger a ui change are based on pointer -or key release -this is a convention that's used -everywhere -and the reason is simple -say i have a pointer event and it -triggers showing a dialogue if i use -trust then the released event -might be sent to the dialogue -this can breed ui inconsistency -if someone relies on this behavior and -might trigger a race condition between -pressed and released events -so we always use release events for -everything that isn't scrolling or input -where reaction speed matters -having covered that let's look at what -this method actually does -first we get the component at the right -position -of the pointer event -and gets get its ui id -there is nothing we can do with a -container -so it's a special case that we don't -customize -scrolling down a bit -if there is a border we can't customize -the background since border will -override that -so we don't need to take that into -account -so -if -this is a text area without text -then we can't customize the font either -and we might as well return -the same is true for a label -if it's a label without text -then font doesn't make sense either -if it's not a text area or label -customizing the font is also meaningless -so we can return in that case too -scrolling further down we can see the -customization dialog -which is just a simple dialogue with -four buttons to customize the various -elements -change foreground color seems a bit -intimidating -but it isn't as intimidating as it might -seem -first we dispose of the existing dialog -then we create a new one to which we add -a color picker -we'll discuss the color picker in depth -later on -however -notice that this method accepts a -callback closure -that calls set fg color and repaint to -update the user interface -live with every change -made -we then use show popup dialog to show -the pop-up dialog on the component that -we clicked -the pop-up dialog is a special type of -dialogue with an arrow which we used -here -that's it -the background color -has nearly the same code with one minor -difference -the background -should be opaque -for the background to show properly -and right now we don't provide a way to -determine opacity -in the color dialog -font customization also uses a custom -call -to a font editor which we'll discuss -later but the basic logic is pretty -similar to the rest -the default value button is just one -line of code which is a common trick -calling set ui id with the same ui id -just resets the value to the ui id -default so all the changes we made -would be -implicitly discarded -when we construct the ui there are -several options to consider -if this is a label or text field then we -need to offer a way to customize the -font and foreground color -otherwise we only need the background -color -notice other options were already dealt -with at the top of the method -where we returned for cases where there -was nothing to do -if this is a label or text field then -the only question is whether we can also -customize the background in which case -we will add that button -too -finally we show the popup -i disable the transition as the default -fade transition for popups -doesn't look great in this case diff --git a/docs/website/video-transcripts/uvQKs_PdB64.json b/docs/website/video-transcripts/uvQKs_PdB64.json deleted file mode 100644 index 420e425e1a..0000000000 --- a/docs/website/video-transcripts/uvQKs_PdB64.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 155, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 846, - "youtube_id": "uvQKs_PdB64" -} diff --git a/docs/website/video-transcripts/uvQKs_PdB64.txt b/docs/website/video-transcripts/uvQKs_PdB64.txt deleted file mode 100644 index 652f456944..0000000000 --- a/docs/website/video-transcripts/uvQKs_PdB64.txt +++ /dev/null @@ -1,155 +0,0 @@ -in-app purchase is a relatively simple -feature -with a bit of a hassle from the stores -who handle the product management -the api itself is trivial -before we begin let's review the basics -of what in-app purchase -is -in-app purchase allows us to use the -same billing support -apple and google use for buying apps -in order to sell items within the app -you can use this to sell virtual goods -which means things that are part of the -application such as digital content -upgrades -in-app features -game elements etc -using in-app purchase is much easier -than credit card billing or any other -form of payment as the whole process is -managed by google or apple -however it's also a requirement -if you have elements sold within your -app -you might get banned if you don't use -in-app purchase -as an example we recently updated the -kitchen sync demo in the apple app store -and got a rejection -the tester reviewed the link in the app -to codenameone.com -and complained that we sell items within -the site the subscription -and don't use in-app purchase -we explained that the app is completely -free and the subscription has nothing to -do with the app -and the app past verification -this should obviously teach you that -communication in these cases is -bidirectional -and also how serious apple is about the -in-app purchase requirement -so -why don't we sell codename one -subscriptions via in-app purchase -this is something we are actually -considering -but the main the motivation is the 30 -commission to apple -they now have a special case for -subscriptions that might make them more -financially viable -so we might add that in the future as an -option -but overall in-app purchase is expensive -if you are having thoughts about -charging extra for in-app purchase -for purchases made through in-app -purchase -that would be a problem as it's -prohibited by apple's terms of service -let's start by looking at the code for -in-app purchase -just like -push requires the push callback in the -main class -in-app purchase requires the callback in -the main class too -i'll skip ahead to the callback methods -for the purchase -item purchased is the actual callback -method -and it includes the sku representing the -item that was purchased -an sku is a constant value that you -define when you set up the items in -google play or itunes connect -the sku lets you determine the item that -the user purchased -when you have more than one item -we are calling into the pending build -method in this case to proceed with the -purchase flow we started there -i'll go into that soon -we have an error callback -as well -which we can use for error logging -the refund callback might be interesting -if -this is a feature you can disable or -refund -but in this case if a user asks for a -refund there isn't much we can do as the -app was already built -the rest of the callbacks relate to -subscription and manual purchase -callback -which is no longer used -subscription is a subject in its own -right that i won't get into here -but it's a very good direction as it -allows a constant and steady stream of -revenue -let's move into the app form code for -purchase -the purchase ui allows shows a dialog -with commands for purchasing an ios -android or source code build -for now we don't support the ios build -but might provide this in a future -update -based on the command selected by the -user we can select the right target and -perform the right purchase type -once pending build is invoked -it just invokes build app as usual -since this is invoked by the purchase -callback this is effectively seamless -we could have used the sku value from -the server and called the build app -callback with the sku which would have -worked exactly the same as this -as you can see the code is pretty -trivial the main hassle is handling -this in google's and apple's interfaces -in google play after you've uploaded -your initial apk you can select in-app -product we click add new product then -select managed product which allows us -to add a new product sku -i type in build for android as the sku i -can use an arbitrary string value to -represent the sku -i type in a name and a description -for the item we're selling -this will be displayed to the user -as part of the native -process by google play -the next -stage is scrolling down to the price and -clicking the add price button -so we can enter the price for the item -using the local currency -i set the price for 40 40ns which is -mostly a trial and error thing -of what google will generate in terms of -the pricing table -once this is set i can scroll to the top -and save the data -and this in a nutshell is an app -purchase -thanks for watching i hope you found -this informative diff --git a/docs/website/video-transcripts/v-kJ_nIYq3I.json b/docs/website/video-transcripts/v-kJ_nIYq3I.json deleted file mode 100644 index 160ed0ce10..0000000000 --- a/docs/website/video-transcripts/v-kJ_nIYq3I.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 105, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 606, - "youtube_id": "v-kJ_nIYq3I" -} diff --git a/docs/website/video-transcripts/v-kJ_nIYq3I.txt b/docs/website/video-transcripts/v-kJ_nIYq3I.txt deleted file mode 100644 index fb6772c9aa..0000000000 --- a/docs/website/video-transcripts/v-kJ_nIYq3I.txt +++ /dev/null @@ -1,105 +0,0 @@ -we are nearing the end of the sign up -wizard ui -the next stage would be -your mobile number -but that's a bit of a special case -because it leads to the email address -and both of these are effectively the -same form -they both look pretty similar to one -another -and similar between ios and android with -the obvious platform differences -this is create number notice that it -delegates all of the work to the create -mobile or email -so let's start there first and come back -you can reach this form either from -gender or from email number based on -going back and forth so the title of the -back command can differ -the text entry is just one text field -so the ui is pretty trivial -notice i use constraint which will -determine the type of virtual keyboard -i'll use -we pass the action listener -on the link -it will navigate to the other form -eg if we are in a -phone entry it will go to email entry -we add a button to the south container -this is the sign up with email address -link -or the similar sign up with mobile -number -we pass through if this is a phone -number and false for email -we also pass the text of the phone email -we'll display that in the confirmation -form that was mostly standard stuff it -will be further clarified with the -actual calls to this method -these are the labels we show in the form -the text input constraint in this case -it's a phone number in other case in the -other case it will be an email address -constraint -navigation to the other form here we -navigate to the email entry form and -there we'll navigate to the number -as you can see the email version is -practically identical -that's pretty much it for these two -forms we will need one css entry though -it's the same as blue link only bold -once we do this email number toggle -should -work -there is nothing interesting how i -consider making a generic method that -will generalize this -and the phone number slash email form -but decided against that i think that -would have over generalized -uh it would have created an overly -confusing code -the code is pretty much textbook -boilerplate nothing much to talk about -the only thing worth noticing in this -form -is the phone and value variables that -are passed right through the method to -the confirmation form -we can now move to the last stage of -signup as there is no css or anything -the final stage of the sign up process -is the account confirmation form -this is a trivial form -as well but it does include minor things -to notice -the subtitle can span lines here -depending on the size of the phone -screen -we have a special center line message -that includes the content we passed from -the phone slash email stage of the -wizard -when done we show the main ui -i'll add a stub for this method soon -we just have one more tiny change to the -css -we need to add the new -center label style -finally we need to add some wiring to -show the signup process in ui controller -we'll add a new signup call -and we will also need a stub for show -main ui -so the sign up wizard will compile -and finally we bind the signup wizard to -the login form constructor using this -code -with that the sign up process mockup is -done and we can move to mocking up the -main ui diff --git a/docs/website/video-transcripts/vAiTW4Y8QuA.json b/docs/website/video-transcripts/vAiTW4Y8QuA.json deleted file mode 100644 index 38411faf5d..0000000000 --- a/docs/website/video-transcripts/vAiTW4Y8QuA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 100, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 554, - "youtube_id": "vAiTW4Y8QuA" -} diff --git a/docs/website/video-transcripts/vAiTW4Y8QuA.txt b/docs/website/video-transcripts/vAiTW4Y8QuA.txt deleted file mode 100644 index cf4afc2a60..0000000000 --- a/docs/website/video-transcripts/vAiTW4Y8QuA.txt +++ /dev/null @@ -1,100 +0,0 @@ -now that we covered the basic -persistence abstraction -let's proceed to integrate it into the -app -we fetch dishes as we need them -implicitly from storage -this is pretty simple code that just -adds all the dish elements to the dishes -list proper property -as i mentioned before the dish list form -needs to detect the deletion of a dish -so it can remove it from the ui -we do this by binding the delete -listener and removing the element -one of the really cool things about -properties is the ability to bind -property change listeners two properties -so we can decouple code that uses the -same data model to display a view in two -different places -i wrote this code before the ui binding -api code existed in codename one -but it's pretty trivial either way -i just update the text -when the property changes -it's better to beg for forgiveness than -to ask for permission -is a proverb sometimes attributed to -programming pioneer grace hopper -regardless of the source of that great -quote it's a wonderful example of -good mobile app design -in a mobile app you shouldn't bother -users with are you sure prompts -you should just do what the user does -and provide a way to undo which is -exactly what we do when we delete a dish -in this code -i'll skip a bit ahead because the code -above is asynchronous -when delete is pressed we show the -previous dish list form -since the edit form is no longer -applicable -then we delete the dish in the server -and in the storage you will notice that -there is no prompt -now we need to show a ui -that allows the user to undo the delete -operation -but here is the problem -we just navigated to the dish list -from the form -we were in -since the edit form is no longer -applicable -so -we use a listener on the parent form so -we will know when it is shown again -the problem is that if we do that -without the next line we'll get prompts -every time we enter that form -after the deletion -so the obvious question is -why don't we just call remove show -listener this -well -funny story -this is a lambda expression -and here this is always the parent class -so remove show listener this -won't have the desired effect -so the solution is a bit of a hack -but it was either this or use -inner classes so i chose this hack -this is relatively simple -we are now in the right form after the -deletion and i can now show the undo -toast message that will appear in the -bottom of the form -if a user taps this popup it will undo -the deletion instantly -the undo code doesn't really undo as -there is no meaningful way to undo -encode -we just re-add the dish -by comparison saving the dish is much -easier -and practically trivial -all of the logic to save a dish to the -server and update it to storage is just -a couple of lines of code -in the case of the -tagline and title -we currently only save them locally but -we will add the server update code as -well -thanks for watching i hope you found -this -informative diff --git a/docs/website/video-transcripts/vLBQhAJ6aTk.json b/docs/website/video-transcripts/vLBQhAJ6aTk.json deleted file mode 100644 index 22560c315f..0000000000 --- a/docs/website/video-transcripts/vLBQhAJ6aTk.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 153, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 836, - "youtube_id": "vLBQhAJ6aTk" -} diff --git a/docs/website/video-transcripts/vLBQhAJ6aTk.txt b/docs/website/video-transcripts/vLBQhAJ6aTk.txt deleted file mode 100644 index 873e21f15c..0000000000 --- a/docs/website/video-transcripts/vLBQhAJ6aTk.txt +++ /dev/null @@ -1,153 +0,0 @@ -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/video-transcripts/vOcKbbW9HBM.json b/docs/website/video-transcripts/vOcKbbW9HBM.json deleted file mode 100644 index a53c1f7123..0000000000 --- a/docs/website/video-transcripts/vOcKbbW9HBM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 56, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 324, - "youtube_id": "vOcKbbW9HBM" -} diff --git a/docs/website/video-transcripts/vOcKbbW9HBM.txt b/docs/website/video-transcripts/vOcKbbW9HBM.txt deleted file mode 100644 index 2689cd42ea..0000000000 --- a/docs/website/video-transcripts/vOcKbbW9HBM.txt +++ /dev/null @@ -1,56 +0,0 @@ -in this section we'll discuss the -address input logic and validation -one of the things that i missed when i -wrote the section on the ui elements -that are missing is the address -collection ui the reason i missed it -was that i assumed incorrectly that the -billing api will do that for me -it doesn't do that and -so i needed to add some form of design -for address collection as you can see in -the video -it's relatively bare bones for now -but it will do -to collect the address i used this -business object i also added some simple -mapping into preferences -as you can see below -so the last entered address will be -cached for future orders -the rest of the logic is pretty easy -i just bind each text field to the -property -notice that this original code was -developed before we had property binding -and instant ui support -if i had to do it again i'd probably use -instant ui -for this feature as it's an ideal use -case -a lot of the logic here -can probably be simplified in the -current api but for now it works -this block -is really a form of validation -we have a shipping range property that -can -be set for restaurant -and here we use the device gps to make -sure the device is within -reasonable range of the restaurant -here we slide the components of the -shopping cart out to make room for the -address fields -which we add and animate into place here -this is mostly mostly simple code -for form input as the binding was done -before -the validation code is pretty simple -i mentioned the shipping range but we -also have a minimum order value -which is now enforced -i also added support for a delivery fee -that can be levied on top of the total -cost this is applied in the summary code -of the form diff --git a/docs/website/video-transcripts/vdFr815Dhpg.json b/docs/website/video-transcripts/vdFr815Dhpg.json deleted file mode 100644 index 32398818f4..0000000000 --- a/docs/website/video-transcripts/vdFr815Dhpg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 262, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1543, - "youtube_id": "vdFr815Dhpg" -} diff --git a/docs/website/video-transcripts/vdFr815Dhpg.txt b/docs/website/video-transcripts/vdFr815Dhpg.txt deleted file mode 100644 index 517b9d93c7..0000000000 --- a/docs/website/video-transcripts/vdFr815Dhpg.txt +++ /dev/null @@ -1,262 +0,0 @@ -the next step is the login form which -requires a bit more -the design of the login ui varies -dramatically and in both cases it wasn't -great -so i ended up taking a great deal of -creative liberty in my interpretation on -how this should look -there are a lot of things i chose to -change about the ui most of them are due -to taste and personal preference -i try to make the code as close as -possible -so i would reuse more of it -but i still wanted to show how i can -create -very different uis for very different -platforms -for instance -this is the ios version running on a -tablet you will notice i copied a lot of -the ui but you will also notice i took -some inspirations from android -i made sure to get everything -working in landscape notice i took a -very different approach to the ui and -landscape that i think looks much better -than the native version which frankly -looks awful -notice that in the ipad version in the -clone i don't move the title image -it doesn't move to the side like it does -in other platforms when shifting to -landscape -i also ignored the top area with more -functionality and languages -it doesn't exist on ios and didn't -strike me as too important -since -the login form is a complex form i chose -to create a new class -for that i also chose to create a new -package com.codename1.fbclone.forms -this class is a bit big -but before i get into it i'd like to -explain how i implemented this ui in the -ios version -what we see here are two text fields -grouped together so the first and the -last have rounded look while entries in -the middle which we don't have here -would not -this is a common look in ios although it -has gone somewhat out of fashion -in codename one we can apply that look -using the component group class -this class is a box layout container -that automatically assigns special ui -ids to components so the first component -would be group element first the second -would be group element and the last -would be group element lost -notice that there is also a special ui -id for buttons added to the group -we don't use it in this case so that's -just an fyi -the ios theme includes styling for these -elements they include rounding on the -top of the first element and rounding on -the bottom for the last element -on android this design paradigm is -uncommon -as a result there is no styling for -these elements on android -a group layout acts as a regular box -layout container on android and doesn't -change the natural -styling of the elements -this works seamlessly because the native -ios theme -has a theme constant component group -ball -if it is set to true that means the ui -id should be changed -component group ball is only defined on -ios and defaults to false -elsewhere -in the code i use that theme constant to -decide whether to show the android ui or -the ios ui -let's get into the code first the class -variables and method declarations -the logo is pretty much the same value -we saw for the splash screen it doesn't -have to be the same though -email and password are just standard -input fields -the following buttons exist in the ios -and android uis although the signup -button has different text in the android -version -the login form uses border layout so we -can place the content where we want -without the ui id the toolbar would -appear on the top of the screen -container is transparent with zero -padding margin which hides the toolbar -we set the name of the logo so the logo -from the splash screen will animate into -place due to the morph transition -we're placing the logo on the top of the -screen in the center notice i used the -login title to set the background around -the logo -we are on a phone -if we are on a phone i want the logo -container to shift to the right side of -the screen -when rotating to landscape this is -handled automatically by border layout -if we have component group support i'll -show the ios style ui -i prefer this to testing whether this is -ios since other platforms might choose -to apply that look -we place the ui -in the absolute center so it's in the -middle of the content we wrap the button -and component group in a box layout on -the y axis -so they stick together in the center -the login and password go into the -component group and the login button is -placed below -the padded container ui id allows us to -create some white space around the -components -so they don't stick to the edges when -running in a phone -i add the content pane to the center of -the ui so it will take up the available -space -we place the two up buttons on the -bottom of the ui -the android version is far simpler we -place the components in a box layout y -and tweak a few ui ids slash texts -and finally this hides the status bar -line on the top of the form -and lets the image occupy the space -the status bar appears on top of the -toolbar and grabs space to push down the -content to leave space for the iphone x -notch and ios clock etc it's a feature -of ios only -one item i skimmed over when reviewing -the ios look code is why we needed the -content container -we could have just used the form which -is already in border layout and just -changed it to use center behavior center -absolute -that would have worked for portrait -but not for landscape -since both -since south takes up the space below -east and west -it would have produced this image -which isn't what we want -the next big thing is setting the css -values -before i go into the new ui ids -we'll start with one change to the -existing css selector -this allows the buttons in android to -appear in all caps -there these are the ui ids that should -be considered as buttons -since the button class is very common we -don't apply the caps behavior to all -buttons by default if the ui id isn't -button we need to explicitly declare -that we are interested in the text -becoming uppercase -notice that this will only apply to -platforms where buttons are caps to -begin with -so if we use blue text on ios it won't -be capitalized -before we go into the full set of css -changes here is a map of the ui ids i'll -mention you can reference this map if -something isn't clear -when i review the code -we need to add the following to the css -file to support all the new ui ids there -is quite a lot of code here -this automatically adds the file to the -resources -the login banner is this jpeg which you -should place in css slash images in your -project -by default the plugin adds multi images -where -we can specify the dpi of the source -image -by setting the dpi to zero i state that -i want a regular image -multi images are great but for -background that would be scale to fill -anyway -the pixel perfect scaling of multi -images will probably be wasted -multi images take extra space in the -resource file and thus in ram -jpeg is -especially efficient in terms of size -and for relatively large background -images it can be the ideal option -the title image fills available space -which should look decent regardless of -the resolution of this type of image -i use a pretty big padding value to give -the title area space in both -orientations so the image background -will show -we shouldn't change the container ui id -so i added this generic ui id for -spacing -out the ui a bit -i use this as the base ui id for buttons -as they have a lot of properties in -common such as center alignment etc -i usually prefer to derive ui ids from -my own style and avoid deriving from -built-in ui ids -as those might change in unpredictable -ways -we derive the base button ui id -we customize the background color and -use a round rect for the border -this pattern repeats in almost all of -the following ui ids -the default text hint -looked bad -so we need to customize it to have -better font padding -etc -this is the text that appears before we -type in the text field -addition additional padding is needed in -the text field and group elements to -make them fit better into the ui -with these changes we are nearly done -there is just one last change to make -this all work -we just create and show the login form -for now no extra logic -with that we can run the code and see -the ui running -next i'll continue with the sign up -process diff --git a/docs/website/video-transcripts/vjTexRDCihA.json b/docs/website/video-transcripts/vjTexRDCihA.json deleted file mode 100644 index 4645d10978..0000000000 --- a/docs/website/video-transcripts/vjTexRDCihA.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 127, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 701, - "youtube_id": "vjTexRDCihA" -} diff --git a/docs/website/video-transcripts/vjTexRDCihA.txt b/docs/website/video-transcripts/vjTexRDCihA.txt deleted file mode 100644 index a4812f91ac..0000000000 --- a/docs/website/video-transcripts/vjTexRDCihA.txt +++ /dev/null @@ -1,127 +0,0 @@ -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/video-transcripts/vxsUf3gw1zg.json b/docs/website/video-transcripts/vxsUf3gw1zg.json deleted file mode 100644 index ff0895decf..0000000000 --- a/docs/website/video-transcripts/vxsUf3gw1zg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 151, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 832, - "youtube_id": "vxsUf3gw1zg" -} diff --git a/docs/website/video-transcripts/vxsUf3gw1zg.txt b/docs/website/video-transcripts/vxsUf3gw1zg.txt deleted file mode 100644 index 8607fff4fa..0000000000 --- a/docs/website/video-transcripts/vxsUf3gw1zg.txt +++ /dev/null @@ -1,151 +0,0 @@ -let's dive right into the code starting -with base navigation form -the main class for the top level forms -is the base navigation form -here we have the material commands in -the side menu that allow us to navigate -to the other top level forms -the title of the forms -is really a text field -as it's editable in a place -the trick in making a text field feel -like title is to style it as such -which is why we created navigation title -uiid -and that style has zero margin -one millimeter of padding -with five millimeter white font that's -aligned to the left -that looks and feels like a title -one important thing to notice is the -left align -which doesn't match the ios convention -center aligned text fields are flaky -across platforms so we don't support -them properly -that's why we use left alignment here -the tagline is similarly a text field -with the right styling -technically the styling derives -navigation title -so -we can be in sync -with changes to the title style -it just uses a smaller three millimeter -font -Edit Background UI -in case this wasn't clear -these ui ids map to the title and -subtitle of the form right -here moving on -the edit background ui on the bottom -right portion of the form maps to this -button -which allows us to edit the background -image in the title area -you will notice we use the constructor -that accepts the button label and ui id -the edit background ui id is styled to -match the navigation title but it also -adds padding to the bottom -so the text doesn't appear within the -white area -i'll talk about that more in the next -section -besides that the other interesting thing -about this ui id is the right alignment -which we use to make it sure it appears -to the right of the icon -the background image is just a scaled -image label that stretches all across -we added a four millimeter margin to the -bottom of this background image -and that allows the icon to peak exactly -four millimeters outside of the bottom -portion -this looks decent enough and works well -for all resolutions -since the size is in millimeters and the -icon is also scaled in millimeters -the logo image -uses a special rounded scaled logo -getter -which adapts the logo's look to the -design of the ui -let's go see how that method works -Rounded Logo -the rounded scaled logo is -just an image with no setter -so -where is this initialized -when we set the property for the logo we -automatically generate this image too -by drawing the image in the right place -let's go over these steps -we have two important sizes here -millimeter -or mm is the size of the arc -in the corner -and size is the size of the entire icon -i got these numbers through trial and -error until i got something that looked -satisfactory and the various device -densities -a general path is a shape object we can -fill stroke or clip based on a shape -notice that shape operations can -sometimes be expensive in terms of -graphics processing power so while they -are very powerful -you need to be very aware of what you -are trying to do -a general path allows us to define an -arbitrary shape we can then use in the -drawing primitives -this raises the question of why not use -the built-in round rect methods within -graphics those don't allow the same -level of customizability and power as -the shape api -they aren't as consistent across -platforms either -building the shape is -essential -uh essentially about moving a virtual -pen around and drawing lines arcs or -curves along the path -this is mostly straightforward -here we create a mask from the shape -a mask for the image -we create a solid black image -and we fill the shape area with a white -color -then we create a mask -a mask can be applied to an image to -remove the black pieces -and leave the white pieces -this begs the question why not use shape -clipping that i mentioned earlier -masking supports anti-aliasing which -renders the elements in the corners with -a slight alpha channel to make the -corner seem smooth -a clip is a binary operation where a -pixel can either be in the clip or it's -outside of the clip -so there is no room for interpretation -using shape clipping won't look as good -furthermore shape clipping isn't as -portable as masking which is -100 portable to all supported -codename one platforms -last line of this code -we do two separate things -the fill method scales the image but it -does that while cropping redundant areas -so -if we have an image that isn't a square -the area outside will be clipped -and the center will remain -we then apply the mask to the clipped -image to create that round effect diff --git a/docs/website/video-transcripts/w7xvlw3rI6Y.json b/docs/website/video-transcripts/w7xvlw3rI6Y.json deleted file mode 100644 index f7dcebc711..0000000000 --- a/docs/website/video-transcripts/w7xvlw3rI6Y.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "line_count": 9, - "quality": "needs-review", - "source": "embedded", - "source_path": "content/howdoi/how-do-i-get-repeatable-builds-build-against-a-consistent-version-of-codename-one-use-the-versioning-feature.md", - "status": "transcript-fetched", - "word_count": 264, - "youtube_id": "w7xvlw3rI6Y" -} diff --git a/docs/website/video-transcripts/w7xvlw3rI6Y.txt b/docs/website/video-transcripts/w7xvlw3rI6Y.txt deleted file mode 100644 index f8a78693eb..0000000000 --- a/docs/website/video-transcripts/w7xvlw3rI6Y.txt +++ /dev/null @@ -1,16 +0,0 @@ -_Transcript source: embedded._ - -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? - -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. - -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. - -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. - -The pro versions include 5 month support which typically maps to the last one or two releases. - -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. diff --git a/docs/website/video-transcripts/wp6gqYLD-XM.json b/docs/website/video-transcripts/wp6gqYLD-XM.json deleted file mode 100644 index 1a5e598db5..0000000000 --- a/docs/website/video-transcripts/wp6gqYLD-XM.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 69, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 403, - "youtube_id": "wp6gqYLD-XM" -} diff --git a/docs/website/video-transcripts/wp6gqYLD-XM.txt b/docs/website/video-transcripts/wp6gqYLD-XM.txt deleted file mode 100644 index ff4856a564..0000000000 --- a/docs/website/video-transcripts/wp6gqYLD-XM.txt +++ /dev/null @@ -1,69 +0,0 @@ -facebook allows you to attach an image -while you are editing a post -this includes some nuanced behaviors i -would like to skip -instead i'll use the gallery icon next -to the post to create a dedicated image -video post -i've changed a lot in image picker -so much so that it literally doubled in -size -the first change includes support for -picking video or photo -the image picker method is refactored to -a more generic pick method -that works with both images and videos -this is the same code as before -notice that if the mode -is image only the new video pick -functionality would be irrelevant -assuming the callback isn't null which -can happen on cancellation -this -points to a file system storage path -to the image or video file -we check the extension to see if this is -an image otherwise we assume video -since sl is -lowercase we don't need to ignore case -the encoded image is created and a -callback is invoked for the case of an -image -in the case of a video -we don't need to save the file as it's -very transient when compared to image -files -when uploading we need to check if this -is a video right now i oversimplified -all uploads are marked as jpeg or mp4 -that isn't correct for most cases -the mime type should defer in some cases -but for the sake of simplicity i chose -to keep it this way -since we create the image in this class -it makes sense to provide a get image -method for users of the new create -methods which i'll address now -there are a few additional changes we -need in the class to support custom -capturing -when we capture an image using a low -level api we don't need the pick method -and we'll just submit the data directly -in these cases we we need a different -api -but want to retain the picker as we use -it later in the code -the factory method creates a picker with -a pre-existing file -we read the file into an encoded image -to create a picker instance -in this factory method we already have -the battery data of the image -we make -up a fake file name -for the update process -obviously an event would be redundant -for these two use cases -this will be clearer when we reach the -camera portion later diff --git a/docs/website/video-transcripts/wqcM8pSOGTY.json b/docs/website/video-transcripts/wqcM8pSOGTY.json deleted file mode 100644 index bd54044ee2..0000000000 --- a/docs/website/video-transcripts/wqcM8pSOGTY.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fetch_method": "youtube_transcript_api", - "line_count": 270, - "quality": "needs-review", - "source": "fetched-automated", - "source_path": "content/courses/course-02-deep-dive-mobile-development-with-codename-one/033-introduction-and-setup.md", - "status": "transcript-fetched", - "word_count": 1468, - "youtube_id": "wqcM8pSOGTY" -} diff --git a/docs/website/video-transcripts/wqcM8pSOGTY.txt b/docs/website/video-transcripts/wqcM8pSOGTY.txt deleted file mode 100644 index cb30dc62e7..0000000000 --- a/docs/website/video-transcripts/wqcM8pSOGTY.txt +++ /dev/null @@ -1,270 +0,0 @@ -working with the codename one sources -is not for the faint of heart -you can learn a lot from going through -the process -however if your only goal is to avoid -the build servers you might find it -harder to work with -in fact i personally use the build -servers when building apps and testing -them -i almost never use offline build or the -sources directly -instead i hack and test things via the -include source option -however -learning this is still valuable and i'm -aware of a few people who don't share my -opinion on this matter -still -why would i write a guide for something -like this -there are three individuals i think of -who might benefit from this guide -if you are the type of person who needs -to do everything yourself -then this is pretty much it -if you want to understand the -underpinnings of codename one at a -deeper level than -more the more abstract descriptions -then this is a good first step -if you want to feel secure that you can -hack codename one manually if our -service changes or becomes unavailable -in the future then the mere existence of -this guide should help calm some of -those concerns -to understand how to work with codename -one code -we first need to understand what -codename one does -for this case i'll remove the build -servers from the equation and focus on -the portability aspect codename one -includes three distinct pieces of four -portability -vm -for some platforms such as android the -vm is natively included -however we work on top of rvm in ios -kvm in windows -and tvm for javascript -api represents the actual calls to the -com.codename1 -classes -ports are the implementations of the api -for the various platforms -this is all pretty trivial the api is -always the same we change the vm and -port to support all the platforms so if -you understand this -then the simulator is simple too -in the simulator the vm is just the -standard java vm -and the port is just the port of -codename one to java se -there is a bit more to that though -the port javasc includes a lot of -specialized glue code to show the -simulator and other features such as the -network monitor etc -but let's start with this and move on -all of the instructions that follow will -work regardless of whether you have the -plugin installed -i will focus on using the command line -and assume you can translate that to -your ide of choice -technically you don't really need the -plugin if you are working from the -source code -however it's convenient to work with -it means that selecting new project and -picking codename one just works -it means that double clicking a resource -file launches the designer tool -but technically you don't really need it -what you do need is jdk8 -notice that newer versions might not -work -we generally use the oracle jdk because -it comes bundled with javafx -and that makes everything simpler as the -simulator needs javafx for some features -specifically media and browser -if you choose to use openjdk it might -work if you install -it with opengfx -but it's not something we tried -apache aunt can be downloaded from the -apache website -make sure ant is in your system path and -that the command runs -note that you will need to define the -java home environment variable properly -git is optional you can download the zip -from github but if you'd like to -contribute pull requests back -into the project this can be useful -for android development we'll use -android studio 3 which you can download -from google -for ios development we will use xcode 9 -point x or 0.2 -from apple -we need to clone these repositories or -just download the source zips -of these repositories and expand them -these repository repositories should -reside in the same hierarchy and i will -rely on that later on when giving out -instructions you will notice we have -three repositories to start with -codename one is the main repo the skins -make sense as we use them to show the -phone skins but why have binaries -the immediate question would be why do -we need binaries for an open source -project -the answer is that they are harder to -build on their own in some cases and in -others we need stubs or nate of native -platforms so we can compile against them -without downloading the full sdk -let's go over the files and directories -and the binaries project and explain -them one by one -the svg directory -is from the open source batik project -it's unmodified we need it for the -designer tool for svg support -the ikvm project is hard to compile -without windows so we have a -pre-compiled version here -these are legacy stubs of the blackberry -os -i won't cover blackberry here so these -aren't really needed -we need stubs to compile javafx code we -don't embed this -this is the jh labs project for javasc -image filters we use this to implement -gaussian blur in the simulator -these are used by the sqlite -implementation -in the simulator -these are part of the open source assemb -project which we use in powerpower vm -everything here is stubs which we use to -compile the android native -implementation without downloading the -full android sdk -we don't run any of this just use it for -compilation -these are legacy stubs for j2me which is -no longer supported -these are libraries used by the codename -one designer tool -there are a lot of swing libraries used -here -all of them are open source -this is an open source library we -developed to improve javadoc generation -check out the repos for the project -this is a pre-compiled binary from the -cldc directory in the project -it's here for convenience -this is technically a set of stubs of -the java api supported by codename one -this is a customized version of the -cldc.jar file that exposes -classes.getresources -string which we normally don't want to -expose -the entire directory contains binaries -for the uwp port -i won't go into that because i won't -cover the offline building of the uwp -version here -before we go into building the project -let's inspect the separate pieces -most of the pieces we fetched in the -codename one repository are netbeans -projects -notice that netbeans projects are just -specific folder structures with an ant -build xml file -that means you don't need netbeans in -order to build or run them -the codename one folder includes the -codename one apis -this is just a standard netbeans and -project that contains 100 portable code -notice that at this time this project -still uses java 5 syntax due to some -technical constraints -codename one designer contains the -designer tool which is a swing -application built with a swing app -framework -it's a standard netbeans project too -with major pieces written using the -netbeans matisse gui builder -the vm directory hosts the power vm -source code -it includes two subfolders each of which -is a standalone netbeans and project -the batco translator is a java netbeans -project that reads java bytecode and -generates c code it includes a couple of -built-in c files but most of the c code -is generated -the portion of the vm that isn't a part -of the codename one api -this is a netbeans project that includes -implementation of all the java packages -needed by parker vm -factory is a simple project that -contains one class -com.coname1.impo -dot implementation factory -that lets us decouple the implementation -of codename one from the ports -themes are the native os theme files -which we load when a theme derives from -native -these are embedded into the ports and -into the skins -tests contain unit tests for the -platform -ports are the various open source os -native ports of codename one -and some platform-specific tools -this is where most of the -platform-specific code of codename one -resides -the android directory obviously includes -the android port -the same is true for the ios port -javasc -includes both the desktop port and the -simulator -uwp includes port to universal windows -platform -notice that this also includes our fork -of ikvm which is the vm implementation -for that platform -cldc11 -is not a port but rather a set of -supported stub apis this isn't an actual -vm but rather stubs we use when -compiling codename one apps to make sure -they don't use unsupported apis -retro is an adaptation to the -retroweaver that added support for java -5 code to run on old blackberry j2me -devices -this code is no longer used -the same is true for the other ports -within this directory diff --git a/docs/website/video-transcripts/x-mUTz23Cd4.json b/docs/website/video-transcripts/x-mUTz23Cd4.json deleted file mode 100644 index 9e7756b506..0000000000 --- a/docs/website/video-transcripts/x-mUTz23Cd4.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 180, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 965, - "youtube_id": "x-mUTz23Cd4" -} diff --git a/docs/website/video-transcripts/x-mUTz23Cd4.txt b/docs/website/video-transcripts/x-mUTz23Cd4.txt deleted file mode 100644 index 397a464bb5..0000000000 --- a/docs/website/video-transcripts/x-mUTz23Cd4.txt +++ /dev/null @@ -1,180 +0,0 @@ -performance is a pretty complex subject -arguably one of the more complex -subjects i've covered here -so this module requires some patience -How This Module Is Structured -this guide is divided into two distinct -parts -tips on app performance -and how to find the cause of performance -issues by using profiling tools both in -the simulator -and on devices -within that i will also discuss two -separate aspects of performance -speed and ram -those are often conflicting forces -where an optimization of one is the -terminal to the other -i will finish the module with an example -of a performance issue i debugged in the -kitchen sink a while back -but before we go into these -we need to discuss a common theme of -misclassified performance problems -we would on occasion get bug reports or -complaints about performance from -developers -who nested two scrollable containers or -placed a focusable component within -another one -these are ui bugs where the api is told -to perform two conflicting tasks and -makes the wrong choice -the result can feel like a ui that isn't -responsive -but this clearly isn't a performance -problem -it's crucial to check against these -things and verify before going into -performance tuning -What is Performance? -we all know performance when we see it -but it boils down to a few separate -types of performance issues that have -radically different solutions -before we get started i'd like to review -these five big concepts related to -performance -Rendering Performance -this is often defined as frame rate -although i think that term makes more -sense in the gaming industry -this is a very visible -and much discussed issue even though -it's probably responsible for a much -smaller percentage of issues in apps -the performance issue is often expressed -in the feel of the app which seems off -the app will run reasonably well and -perform things fast enough -but animations and scrolling might seem -jumpy usually rendering performance is -impacted uniformly which means you would -see the performance degraded in a -consistent way and it won't improve or -degrade further as you go on rendering -performance issues are usually really -small issues -in order to reach 60 frames per second -we need to render a frame within 16 -milliseconds -that means that a delay of 5 to 10 -milliseconds in code can trigger a -rendering performance problem -Logic Performance -this is a far more common issue -that sometimes triggers rendering issues -it generally means the business logic of -the application -is slowing performance -this issue occurs -if you are doing too many things and -don't leave enough cpu for rendering -this is -generally expressed in a delay or a -bounce within the app -unlike rendering performance -this won't be uniform -a specific thing is taking a long time -and this can usually be isolated -reasonably well -logic performance issues can be solved -by proper use of caching threading -usually delays for these sort of issues -can be in the seconds region and impact -would be very noticeable -Memory Thrashing & Crashes -an app that allocates and discards -memory can trigger a performance penalty -with gc thrashing -it can also cause cache clearance which -will impact cpu performance further -cache clearance means that elements that -we store in cache to increase -performance -are discarded due to low memory -in the zeal to over optimize developers -often overuse memory and as a result end -up with crashes due to out of memory or -with cache that is constantly cleared -a huge piece of the performance puzzle -is perception -do this experiment -create an app with a button that shows a -dialog after a one second delay -when the user taps the button he will -get no indication of the delay -then do the same thing with a two second -delay but replace the button with a -loading -three dots after the tap -the user will perceive the second -version to be more responsive -this is easy to improve in the ui -instead of using tools like infinite -progress show a ui and fill it -dynamically -ideally have cached data from the last -run -ready and then update that data into -place -if a user sees a spinning animation with -nothing happening they will consider the -app slow -if the app loads the data and refreshes -as new data arrives they will consider -the app -fast -i find that when we dig into a lot of -user complaints about performance this -is probably the most common complaint -Pareto Distribution: the 80-20 Rule -i love this quote of donald knuth who -wrote the art of computer programming -books -what was true when he started developing -is still 100 true in this day and age -the real problem is that programmers -have spent -far too much time worrying about -efficiency in the wrong places -and at the wrong times -premature optimization is the root of -all evil -or at least most of it in programming -in other words donald is saying -only optimize when you actually need to -and based on measurements -the first rule of performance is to do -what's right for your app -if you spend time optimizing at an early -stage you will have an unmaintainable -app worse it would be harder to optimize -the app properly -after the fact -in almost all cases of optimization that -i worked through in my career -spanning mobile desktop and servers -optimizations generally boil down to -caching -if you cash in the wrong place or cash -the wrong thing you can cause logic -problems and pay -a performance penalty -eighty percent -of your performance gains will come from -changes to twenty percent of the code -i'd argue the number in the real world -is closer to ninety eight percent to two -percent diff --git a/docs/website/video-transcripts/xCEOuV43Uqw.json b/docs/website/video-transcripts/xCEOuV43Uqw.json deleted file mode 100644 index 0de792ce92..0000000000 --- a/docs/website/video-transcripts/xCEOuV43Uqw.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 224, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1348, - "youtube_id": "xCEOuV43Uqw" -} diff --git a/docs/website/video-transcripts/xCEOuV43Uqw.txt b/docs/website/video-transcripts/xCEOuV43Uqw.txt deleted file mode 100644 index 7e70591cde..0000000000 --- a/docs/website/video-transcripts/xCEOuV43Uqw.txt +++ /dev/null @@ -1,224 +0,0 @@ -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/video-transcripts/xLa5yeeVsE8.json b/docs/website/video-transcripts/xLa5yeeVsE8.json deleted file mode 100644 index 063e01f476..0000000000 --- a/docs/website/video-transcripts/xLa5yeeVsE8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 130, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 816, - "youtube_id": "xLa5yeeVsE8" -} diff --git a/docs/website/video-transcripts/xLa5yeeVsE8.txt b/docs/website/video-transcripts/xLa5yeeVsE8.txt deleted file mode 100644 index 9208fa6bc6..0000000000 --- a/docs/website/video-transcripts/xLa5yeeVsE8.txt +++ /dev/null @@ -1,130 +0,0 @@ -the sign of form abstracts the whole -process and generalizes most of the -common features in the sign up stages -all of these forms are are handled by -one class sign up form let's start by -reviewing the generic aspect of that -class and then dig into every stage -the sign up form is a form that -encapsulates common functionality in all -of these forms we use the content -container to place the main UI and the -South container for the links on the -bottom -The Constructor is protected as it's -used only within this class -only the content pane should be -scrollable everything else should be -fixed in place -this supports the back behavior when -arrow is shown on Android and back label -is shown in iOS -we give the title a different UI ID when -it's in landscape mode so it can shrink -when we rotate the device -the problems link is added to the bottom -of the UI inside a container so we can -add additional elements there -this is the next button that appears in -each stage the static methods below -create each stage of the wizard -now we have all the infrastructure in -place to build the individual stages of -sign up -the first stage is the terms and -conditions form which we create in the -create terms method -in this form we have some highlighted -text for this we will need the rich text -view control that provides Rich Text -viewing capabilities -we could use a browser component but -that might lead us down a problematic -path of matching fonts behaviors with -the HTML renderer -for this case I'd rather have something -simple -the code for this form is simple -the sign in label appears in the iOS -back button -we Center align the HTML since it -renders into flow layout this is pretty -easy -we need a different label for the next -button -we show the create name form on the next -operation -I left this as a mock-up -clicking the links isn't used but you -can Implement these links in any way you -want -the content is in the center of the form -and uses box layout on the Y Asus we add -elements directly to it simple -next we need to style a lot of elements -for this form -a lot of these will apply to the -following sign up form methods so this -CSS listing will be a bit longer -first let's look at the UI IDs involved -next let's look at the CSS needed for -this -first we need to change two things in -the constants entry -we need to add the next button UI ID to -the list of caps buttons -landscape title UI ID ball indicates -that we should use landscape suffixes -suffixed UI IDs for the title elements -notice that Facebook didn't uppercase -the next button on Android I chose to do -that as it fits better to the platform I -didn't do that for the problems link or -other links as that's not the convention -on Android -let's go over the new additions to the -CSS now the toolbar has a bit of padding -and right the right background color -the title is four millimeters with a -large font this doesn't look like the -more understated Facebook font but I -think it looks better -the title command is smaller than the -title so it won't take up too much space -back command is only used in iOS so we -keep it even smaller so it will fit with -the title which can be a problem with -long text -next we'll discuss the landscape UI IDs -toolbar landscape reduces the padding -further in the toolbar -it also reduces the size of the title -font -if we don't reduce the padding in the -command and title area the title area as -a hole won't shrink -when I initially implemented this I -didn't reduce the sizes of the title -command and back command Styles the font -shrank but the toolbar maintained its -height -I use the component inspector tool in -the simulator to look at the differences -between the preferred height and actual -height of all the elements in order to -figure out which was at fault -we use a bold Center aligned font for -the subheading in the forms -the sign-up process forms have an -off-white color which I took from the -Android version of The Wizard -initially I used a blue button for this -but eventually I decided to create a UI -ID for it as I wanted more spacing from -the top I might change next button in -the future to work better in landscape -by reducing margin in landscape mode -the link at the bottom of the form is -relatively small and blue with this you -can run and see the first signup form -the rest becomes trivial by comparison diff --git a/docs/website/video-transcripts/xzwq4P9ogoU.json b/docs/website/video-transcripts/xzwq4P9ogoU.json deleted file mode 100644 index 76b8157e94..0000000000 --- a/docs/website/video-transcripts/xzwq4P9ogoU.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 65, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 364, - "youtube_id": "xzwq4P9ogoU" -} diff --git a/docs/website/video-transcripts/xzwq4P9ogoU.txt b/docs/website/video-transcripts/xzwq4P9ogoU.txt deleted file mode 100644 index 913a42530c..0000000000 --- a/docs/website/video-transcripts/xzwq4P9ogoU.txt +++ /dev/null @@ -1,65 +0,0 @@ -the restaurant app maker is an -application that we will build together -based on the restaurant app i introduced -earlier -it will generate a custom application -for a specific restaurant -as i mentioned before we are taking a -depth first strategy where we implement -a narrow user interface and logic -all the way to the server code -so we can test the full process before -we fill in the pieces across the stack -so -we are starting with a relatively simple -baseline where we only include a part of -the ui -due to that i removed almost every -complex detail form and chose to focus -only on the very basic features -i -chose to copy the data model from the -restaurant app -i copied which is the lazy approach -i think it often makes sense to copy at -first and if you find yourself copying -repeatedly -then spend the time to refactor -i see developers trying to over -generalize things that don't need to be -generic -and this often ends up taking -more time than most of us expect -for code specific for the app i added a -new app settings class -this is a class mostly for data related -to the code generation -i did the data model now because it was -already implemented for the most part -just like i did the ui first for the -restaurant app as it was already shown -in the psd -in this case the data model is a good -foundation to build on top -let's try to draw how this will look -we start with the standard codename one -form class as the base class -which i will demonstrate here -base navigation form derives from form -to provide common capabilities such as -the -the common side menu -and other common top level form -abstractions -it will have several subclasses matching -the top level forms but right now we'll -focus on the two common uis -one of them is -the dish list -and the other is the build form -the dish edit form derives from form -directly -as it doesn't include the side menu or -related logic so the base navigation -form doesn't fit -in this case diff --git a/docs/website/video-transcripts/yamsuV5Airc.json b/docs/website/video-transcripts/yamsuV5Airc.json deleted file mode 100644 index de1d6eb7f2..0000000000 --- a/docs/website/video-transcripts/yamsuV5Airc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 227, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 1328, - "youtube_id": "yamsuV5Airc" -} diff --git a/docs/website/video-transcripts/yamsuV5Airc.txt b/docs/website/video-transcripts/yamsuV5Airc.txt deleted file mode 100644 index a3b6aa2a95..0000000000 --- a/docs/website/video-transcripts/yamsuV5Airc.txt +++ /dev/null @@ -1,227 +0,0 @@ -now that the server is working we need -to connect the app mockup to it and get -all the pieces working in cohort -since we did most of the work this is -actually simpler than it sounds -i completely rewrote the server api -which was a mock-up beforehand i -considered building an abstraction -similar to the one on the server side -with separate classes for user media and -post but eventually decided this doesn't -warrant that -it might -as the api grows but right now the api -is still simple enough the code is -generally just glue code that doesn't -contain any real logic -the code doesn't change up to here -everything else is different -we have two new variables the url of the -server and the token we use for -authentication -the get method is a shorthand for rest -get methods that already inserts the -right base url token and json headers -for spring boot -post works in the same way and saves us -from a lot of boilerplate code -we can check if a user is logged in by -checking the token value this is useful -as -a key password scenario -the rest of the code is mostly mirror of -the server code -first we have login and signup which are -practically identical so their common -code is encapsulated by one method -both methods are post operations that -submit data to the respective url -a login failure might still invoke the -success callback method -this automatically populates the user -object with the json data from the -server -the token is also saved separately for -convenience -we save the user object in storage as a -json file this means we can just read -that file the next time we launch -saving as json is very convenient as we -can literally read the content of the -file during development and also produce -test cases using this approach -notice we save the json data to storage -not far system storage -storage is generally private to the app -and while it's not secure enough for -your bank account details it should be -reasonably safe for most apps -as i mentioned before signup and login -use the same method with different data -and url -refresh is invoked when we need to fetch -updated user data after making changes -the operation is synchronous so we can -just read the data and store it into -json immediately -the verification step just sends the -code using a get request and expects an -ok response notice this isn't a json -string just the word -ok -update works in a similar way but posts -the user data -notice how much simpler this is than the -signup method -partially due to the synchronous api -notice we save the changes into the -cached local file -when we use this method we would need to -already have a media object id -so set avatar will point at a valid -image -most of these are pretty uniform and -don't bring anything new or interesting -now to do the next two methods for -friend request -a friend request is sent as a get method -as we only need the user id and not much -more -accepting a friend request is -practically the same operation -if there were more than two methods it -would have made sense to -extract the common logic to one method -but for this amount of code it's -probably not worth it -the next stage is a bit more interesting -we need an api to upload the user -contacts -all the heavy lifting is done in the -contacts to json method -this call just posts the data returned -from that method to the url -so let's look at the context to json -method -we need to translate the codename one -contact object -two json strings matching the shadow -user dao object in the server side -the first step is looping over all the -elements to generate the json i chose to -generate it manually as a string as a -quick and dirty solution -we create a map and place the properties -into it we translate the nuanced -contacts object into a simplified object -the logic used here is a bit too -simplistic but it will do -the a more realistic approach would -upload all of the data -the advantage of using this api rather -than just writing the json -is that special characters are escaped -correctly so this is far simpler -the two json api only supports -outputting maps not arrays slash lists -as we need -so this portion of the hud is a -hard-coded string -that's just a bit verbose and slightly -hacky but it works -the next method continues the direction -of upload with media -upload api -for media upload we use the uniform the -four multipart w3c standard which is -built into codename one in the multipart -request class -upload media submits a multipart request -which is a special type of post -operation that encodes binary data as -base64 for upload -this is a callback from connection -request that allows us to read the -response directly to a string notice -this method is invoked on the network -thread -this callback is invoked on the event -dispatch thread -when the connection request finishes -it's important to call the success -method on the edt to avoid threading -issues -add to queue is asynchronous so the -media will upload in the background and -invoke the success callback when ready -uploading an image is a bit challenging -but most of the complexity is hidden by -codename one -the me method returns the logged in user -instance if we are logged in we lazily -initialize the user instance -we load the json file saved during login -signup from the storage -the next step is the list of -notifications which we fetch -synchronously page bar page -you may recall that in the mock-up code -i used timestamps to define the paging -process in the infinite container that -approach works great for some paging -strategies but for the strategy we -picked on the server it's not ideal -in fact the approach on the server is -even simpler than what we did -we just need a page and the size of the -page -we use synchronous api calls -since the infinite container api expects -us to return values -the json passing always returns a map -but the server return returns a list in -this case -for those cases a map with a single -entry called root is implicitly created -we create and populate a notification -object for every entry in the list -if there are no entries or if there was -an error we return null to end the -listing -this method is replicated by other -services that return pageable data -specifically following the following -methods deal with posts -we -have two lists of post objects this -method concentrates the common code from -both of them it's roughly identical to -the notifications method -this is pretty much the only difference -we instantiate post instead of -notification -here we list the post for a specific -user we construct and launch the query -so map the map response returns to the -post method -the newsfeed call is identical with -different urls and it doesn't -need the user id -with that we are nearing the end of the -server api class the last few methods -are again pretty simple methods covering -the actions one can take to submit data -we submit a post object in the body of -the web service and return true if the -post succeeds -a comment object is submitted in much -the same way as a post but it sets the -id of the comment entry added into the -comment object -even though a like receives a post -we only send the post id and use the -post object to keep this type safe -with that we mirrored the current server -changes to the client and can move to -the integration aspect diff --git a/docs/website/video-transcripts/ymocxBIQn0o.json b/docs/website/video-transcripts/ymocxBIQn0o.json deleted file mode 100644 index 63cb46b66e..0000000000 --- a/docs/website/video-transcripts/ymocxBIQn0o.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 61, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 352, - "youtube_id": "ymocxBIQn0o" -} diff --git a/docs/website/video-transcripts/ymocxBIQn0o.txt b/docs/website/video-transcripts/ymocxBIQn0o.txt deleted file mode 100644 index 5239ee2be1..0000000000 --- a/docs/website/video-transcripts/ymocxBIQn0o.txt +++ /dev/null @@ -1,61 +0,0 @@ -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/video-transcripts/yv3WXt8o88k.json b/docs/website/video-transcripts/yv3WXt8o88k.json deleted file mode 100644 index 347f74f565..0000000000 --- a/docs/website/video-transcripts/yv3WXt8o88k.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 133, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 697, - "youtube_id": "yv3WXt8o88k" -} diff --git a/docs/website/video-transcripts/yv3WXt8o88k.txt b/docs/website/video-transcripts/yv3WXt8o88k.txt deleted file mode 100644 index ab5a08ac41..0000000000 --- a/docs/website/video-transcripts/yv3WXt8o88k.txt +++ /dev/null @@ -1,133 +0,0 @@ -we'll continue with the code for the -dish list -the dishless form is the form that -contains the set of dishes we can select -from -the elements within this form are stored -in a grid layout -you will notice that the number of rows -is hard coded -that is because rows are added -implicitly -as we add more elements -but columns -won't change by default -this means that if we only have one or -two elements -the rest of the places will remain blank -and -the one element we have won't take up -all the space -the floating action button at the bottom -allows us to add a new dish to the set -of existing dishes -notice that we instantly add a dish -and show -the edit ui forcing the user to delete -the dish -if you want to cancel this edition -that's a common practice on mobile -we're dealing with the ok cancel process -is sometimes harder than just doing -something like this -the -add dish method adds a ui element to the -list of dishes -that element includes a black gradient -overlay which makes the white text on -top -readable even with a -white image below -the entire component is a lead component -which means all events within the -container are delegated to the scale -image button -that means that the listener on the -scale image button in the last line will -receive all of the events in the -container hierarchy -we show the dish edit form when editing -a dish -since the dish is instantly created and -edited there is no form for a new dish -and this form doesn't have -two modes like you normally would -or -for edit or create -in this app there is only one mode -edit -title area of -dish edit form is -built of components -because it is large and relatively -custom -i prefer to handle all of the commands -and functionality and code -so the title itself -is a -text field again just like in the main -form -as the digital title can be edited in -place -the back and ok buttons are just -standard buttons with the right ui id -and the icon -this is where it gets interesting -the title component is now really tall -because of the background image -so if i had used a standard add material -command it would have been placed -somewhere along the middle of the layout -instead of -at the top -so to get around this i created a border -layout which effectively occupies -this area -but again -if i'd add the components directly -they will be placed in the middle -vertically instead of at the top as i -would like -so i wrap the two commands with a flow -layout to push them to the top -i wrap the text field with a box layout -though and the reason is a bit more -complicated -flow layout doesn't deal well with -resizable components like text fields -since it tries to break a line when we -run out of space -if the title is too long it will try to -break a line instead of letting the text -field overflow which is what i want -box layout doesn't have that problem -code right here positions the floating -action button -on the border between the title area and -the body -notice that this component is positioned -on top of both and doesn't require a -special empty region -like the icon in the previous form -we bind the floating action button -directly to the layered layout on top -on the top right -which should position it really high in -the form -but we then use this code to set the -margin of the floating action button -so it will match the toolbar height -minus half the height of the floating -back button -this positions it perfectly -last but not least we can see the -component representing the delete button -at the bottom -followed by -stub action handling code for the -various buttons -delete is placed in the south so it will -always appear at the very bottom of the -form regardless of -scrolling thanks for watching -i hope you found this informative diff --git a/docs/website/video-transcripts/zJ7L5fi60H8.json b/docs/website/video-transcripts/zJ7L5fi60H8.json deleted file mode 100644 index 6a56a8f9fc..0000000000 --- a/docs/website/video-transcripts/zJ7L5fi60H8.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "line_count": 75, - "quality": "needs-review", - "source": "fetched-manual", - "status": "transcript-fetched", - "word_count": 450, - "youtube_id": "zJ7L5fi60H8" -} diff --git a/docs/website/video-transcripts/zJ7L5fi60H8.txt b/docs/website/video-transcripts/zJ7L5fi60H8.txt deleted file mode 100644 index a11b27e7d1..0000000000 --- a/docs/website/video-transcripts/zJ7L5fi60H8.txt +++ /dev/null @@ -1,75 +0,0 @@ -the notification service is arguably one -of the simplest services -we have right now -its main reason for existence is push -notification that will integrate into it -later on -as i said the class is trivial and -contains just two methods -the -only repository is the notification -repository as we don't need access to -anything else for the service -send notification is important as this -would be a great location to send push -notification or trigger a -websocket-based push in the future -right now we just save the data to the -database which we would do anyway as we -want to keep track of notifications -this is an api for the client side so it -can list the notifications pending to -the user it accepts a page number so we -can go through the pages of results -when we go through the notification -pages we also pass the sort value to -indicate we want notifications arranged -by date -the return value is the dows -for the current page carrying the -notification data -the send notification api is an api -designed for the server side -notifications only originate within the -server to prevent abuse by malicious -clients -we continue the trend of simple services -with the media service which is almost -as simple as the notification service -the media service works with the media -repository only but it needs to -authenticate some actions against the -user repository -when adding a new media we accept almost -all the values from the client with the -exception of time which we store in the -server to prevent abuse -we need an auth token to make sure the -media is saved under the correct user -we return the media id when we are done -so it can be referenced by the user -public media can be requested via the -idea of the media and doesn't require -any authentication -we need to check that the user is indeed -requesting a public media object and -isn't spoofing a request -this obviously brings up the visibility -constance enum which is pretty trivial -right now we just have two visibility -types for public and friend as you can -see there is public method tests if the -field is public or now -the permission exception is thrown -otherwise -that exception is pretty simple and -again just a marker exception for the -web service layer -finally for media that could potentially -be private we need this method it -accepts the user token and media id -if the media is public then there is no -problem we can just return it -if the media is for friends only we need -to verify that the user requesting the -media is indeed a friend