|
| 1 | +import { Meta, Description } from '@storybook/addon-docs' |
| 2 | + |
| 3 | +<Meta |
| 4 | + title="Mobile app/Android/Scratch" |
| 5 | + parameters={{ |
| 6 | + viewMode: 'docs', |
| 7 | + previewTabs: { |
| 8 | + canvas: { hidden: true } |
| 9 | + }, |
| 10 | + }} |
| 11 | +/> |
| 12 | + |
| 13 | +## Scratch Interface Documentation Android |
| 14 | + |
| 15 | +Scratch is a widely recognized visual programming language developed to simplify the process of learning to code. It's particularly useful for educational purposes due to its intuitive drag-and-drop interface. In relation to the Thymio2+ robot, Scratch provides a friendly and engaging way for users, especially younger learners, to program and control their robots. By using Scratch with Thymio2+, students can visually assemble code blocks to control the robot's actions and responses to environmental inputs, making complex concepts in robotics and programming accessible and fun. This integration empowers educators and students to explore the fundamentals of robotics programming in an interactive and supportive environment. |
| 16 | + |
| 17 | +## Integration of Scratch in the Thymio Suite Mobile App |
| 18 | + |
| 19 | +To integrate the Scratch web interface into the Android mobile application, an underlying Android tool is used that allows for serving web files internally without the need for an external web server. Specifically, the files should be placed in the directory /android/app/src/main/assets. They can then be loaded into the WebView using the path file:///android_asset. This method simplifies the deployment process by utilizing the Android platform's native capabilities to handle local web content directly within the app. |
| 20 | + |
| 21 | +### Generating the URL |
| 22 | + |
| 23 | +To construct this URL, necessary data must first be acquired from the 'scanner' page, which can be accessed using the following URL pattern: |
| 24 | + |
| 25 | +```javascript |
| 26 | +http://127.0.0.1:3000/scanner/index.html?data=${ |
| 27 | + JSON.stringify({ |
| 28 | + ...LTServices |
| 29 | + })}&gl=${JSON.stringify( |
| 30 | + { |
| 31 | + interface: 'vpl3', |
| 32 | + }, |
| 33 | + )}&lang=${language}` |
| 34 | +``` |
| 35 | + |
| 36 | +Here, `LTServices` is an object containing device information structured as follows: |
| 37 | + |
| 38 | +```typescript |
| 39 | +interface DeviceInfo { |
| 40 | + port: number; |
| 41 | + txt: { |
| 42 | + uuid: string; |
| 43 | + 'ws-port': string; |
| 44 | + }; |
| 45 | + addresses: string[]; |
| 46 | + name: string; |
| 47 | + fullName: string; |
| 48 | + host: string; |
| 49 | +} |
| 50 | +
|
| 51 | +interface DevicesMap { |
| 52 | + [hostname: string]: DeviceInfo; |
| 53 | +} |
| 54 | +``` |
| 55 | + |
| 56 | +### Purpose of the Scanner Page |
| 57 | + |
| 58 | +The scanner page uses the data from `LTServices` to scan each TDM and retrieve a list of connected robots. Users can select the appropriate robot on this page, which then enables the Scratch page to be loaded with all necessary data to initiate the interface correctly. |
| 59 | + |
| 60 | + |
| 61 | +#### Example URL Structure |
| 62 | + |
| 63 | +An example URL to access the Scratch interface might look like this: |
| 64 | + |
| 65 | +- **device Host**: The uuid of the device, typically a Thymio2+ that hosts the TDM (Thymio Device Manager) with which Scratch communicates. |
| 66 | +- **pass**: The password for accessing the Thymio Device Manager. |
| 67 | +- **ws**: The WebSocket ip and port used by the TDM for real-time communication. |
| 68 | + |
| 69 | +``` |
| 70 | +file:///android_asset/scratch/index.html?device=%7Bd768ed1a-5831-425a-89a0-50d10f8c1a4c%7D&ws=ws://127.0.0.1:8597&pass=CX87IR |
| 71 | +``` |
| 72 | +
|
| 73 | +## File Manager |
| 74 | +
|
| 75 | +The VPL3 interface uses a WebView to provide a block-based programming environment. File operations within this environment—specifically saving current projects and uploading existing ones—are facilitated through communication between the WebView and the native code via `postMessage`. This ensures a seamless integration of web-based components with mobile functionalities. |
| 76 | +
|
| 77 | +### Workflow Description |
| 78 | +
|
| 79 | +#### Saving Files |
| 80 | +
|
| 81 | +**1. Trigger Save Action:** |
| 82 | + - The save process is initiated by the user from the VPL3 web interface, typically through a save button. |
| 83 | +
|
| 84 | +**2. Capture Data in WebView:** |
| 85 | + - When the save action is triggered, the web environment captures the current state of the program, which is serialized into JSON format. |
| 86 | +
|
| 87 | +**3. PostMessage to Native App:** |
| 88 | + - The serialized data is sent to the native side using `postMessage`. This message includes an action type, such as `{ action: "saveProgram", content: jsonData }`. |
| 89 | +
|
| 90 | +**4. Native Handling:** |
| 91 | + - The native code listens for messages from the WebView. Upon receiving the data, it invokes file writing operations using React Native's file system libraries. |
| 92 | +
|
| 93 | +**5. Confirm and Alert User:** |
| 94 | + - After the file is successfully saved, the native app can send a confirmation back to the WebView or display a native alert confirming the save. |
| 95 | +
|
| 96 | +
|
| 97 | +```javascript |
| 98 | +// React Native side |
| 99 | +const handleFileUpload = async () => { |
| 100 | + try { |
| 101 | + const result = await DocumentPicker.pick({ type: [DocumentPicker.types.json] }); |
| 102 | + const fileContent = await ReactNativeBlobUtil.fs.readFile(result.uri, 'utf8'); |
| 103 | + webViewRef.current.postMessage(JSON.stringify({ action: "loadProgram", content: fileContent })); |
| 104 | + } catch (err) { |
| 105 | + console.error('File selection error:', err); |
| 106 | + } |
| 107 | +}; |
| 108 | +``` |
| 109 | + |
| 110 | +#### Uploading Files |
| 111 | + |
| 112 | +**1. Open Document Picker:** |
| 113 | + - The user initiates the upload process, prompting the native side to open a document picker for file selection. |
| 114 | + |
| 115 | +**2. Read and Send Data:** |
| 116 | + - Once a file is selected, its content is read into a string using file reading utilities. |
| 117 | + - This content is then sent back to the WebView using `postMessage`, similar to `{ action: "loadProgram", content: fileString }`. |
| 118 | + |
| 119 | +**3. Load Data in WebView:** |
| 120 | + - Inside the WebView, an event listener processes the received message and loads the program content into the VPL3 environment. |
| 121 | + |
| 122 | +```javascript |
| 123 | +// React Native side |
| 124 | +const handleSaveToFile = (jsonData) => { |
| 125 | + const path = `${ReactNativeBlobUtil.fs.dirs.DocumentDir}/savedProgram.json`; |
| 126 | + ReactNativeBlobUtil.fs.writeFile(path, jsonData, 'utf8') |
| 127 | + .then(() => console.log('Save successful')) |
| 128 | + .catch(err => console.error('Save failed', err)); |
| 129 | +}; |
| 130 | + |
| 131 | +// WebView side |
| 132 | +window.ReactNativeWebView.postMessage(JSON.stringify({ action: "saveProgram", content: programData })); |
| 133 | +``` |
| 134 | + |
| 135 | +```javascript |
| 136 | +// WebView side |
| 137 | +window.addEventListener('message', function(event) { |
| 138 | + const data = JSON.parse(event.data); |
| 139 | + if (data.action === 'loadProgram') { |
| 140 | + loadProgramFromJSON(data.content); |
| 141 | + } |
| 142 | +}); |
| 143 | +``` |
0 commit comments