Compare commits

..

No commits in common. 'master' and 'v1.1.2' have entirely different histories.

@ -8,18 +8,16 @@ android:
- android-27 - android-27
before_install: before_install:
- yes | sdkmanager "platforms;android-27" # accept android licenses - yes | sdkmanager "platforms;android-27"
install: install:
- nvm install 12 - nvm install 8
- npm install -g ionic cordova - npm install -g ionic cordova
- npm install - npm install
script: script:
- npm run android:build # implies web build in www/ dir - npm run android:build # implies web build in www/ dir
before_deploy: "cp platforms/android/app/build/outputs/apk/release/app-release-unsigned.apk sensebox_blockly_${TRAVIS_TAG}.apk"
deploy: deploy:
# deploy web build to gh-pages branch # deploy web build to gh-pages branch
- provider: pages - provider: pages
@ -30,10 +28,9 @@ deploy:
tags: true tags: true
local_dir: www local_dir: www
- provider: releases # deploy android project for fdroid builds
api_key: $GITHUB_TOKEN - provider: script
file: script: bash tools/deploy-fdroid.sh
- "sensebox_blockly_${TRAVIS_TAG}.apk" skip-cleanup: true
skip_cleanup: true
on: on:
tags: true tags: true

@ -1,35 +1,23 @@
# Blockly for senseBox app [![Build Status](https://travis-ci.org/sensebox/blockly-app.svg?branch=master)][travis] [![Fdroid Badge](https://img.shields.io/f-droid/v/de.sensebox.blockly.svg)][fdroid] [![github releases](https://img.shields.io/github/release/sensebox/blockly-app.svg?logo=github)][releases] # Blockly for senseBox app
[![Build Status](https://travis-ci.org/sensebox/blockly-app.svg?branch=master)](https://travis-ci.org/sensebox/blockly-app)
Blockly for senseBox is a visual programming editor for the senseBox:edu on mobile. Blockly for senseBox is a visual programming editor for the senseBox:edu on mobile.
It is based on Google's [Blockly](https://developers.google.com/blockly/) and Carlos Pereira Atencio's [Ardublockly](https://github.com/carlosperate/ardublockly). It is based on Google's [Blockly](https://developers.google.com/blockly/) and Carlos Pereira Atencio's [Ardublockly](https://github.com/carlosperate/ardublockly), which has been forked.
### Features ## Features
- generate Arduino code with visual drag-and-drop blocks, with blocks for the senseBox platform. - generate Arduino code with visual drag-and-drop blocks, with blocks for the senseBox platform.
- online compiler for senseBox MCU - online compiler for senseBox MCU
- over the air programming via WiFi - over the air programming via WiFi for senseBox MCU
- Android 7+ & Web-browser support. (iOS support is upcoming) - Android 7+ & Web-browser support. iOS support is upcoming!
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75" />][fdroid]
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png" height="75" />][playstore]
[travis]: https://travis-ci.org/sensebox/blockly-app
[releases]: https://github.com/sensebox/blockly-app/releases
[fdroid]: https://f-droid.org/packages/de.sensebox.blockly/
[playstore]: https://play.google.com/store/apps/details?id=de.sensebox.blockly
## Development ## Development
This is an Ionic 3 / Angular 5 application using Cordova Plugins for mobile-native functionality.
This is an Ionic 3 / Angular 5 application using Cordova Plugins for mobile-native functionality.
### dev env setup ### dev env setup
For a basic web version, only Node.js 8+ is required. For a basic web version, only Node.js 8+ is required.
For Android & iOS builds the respective platform tooling is required. For Android & iOS builds the respective platform tooling is required.
This repo contains mandatory submodules; so you need to clone this repo via
```sh
git clone --recursive https://github.com/sensebox/blockly-app.git
```
To install npm dependencies run `npm install` once. To install npm dependencies run `npm install` once.
Then run `npm start` to start a hot-reloading development view in the browser on <http://localhost:8100>. Then run `npm start` to start a hot-reloading development view in the browser on <http://localhost:8100>.
@ -47,18 +35,22 @@ export ANDROID_HOME=$ANDROID_SDK
export PATH=$PATH:$ANDROID_SDK/emulator:$ANDROID_SDK/tools:$ANDROID_SDK/tools/bin:$ANDROID_SDK/platform-tools:$ANDROID_SDK/build-tools/28.0.3 export PATH=$PATH:$ANDROID_SDK/emulator:$ANDROID_SDK/tools:$ANDROID_SDK/tools/bin:$ANDROID_SDK/platform-tools:$ANDROID_SDK/build-tools/28.0.3
``` ```
To create a signed release build, add a file `platform/android/release-signing.properties` following this template.
You need a keystore with a valid signing key!
```properties
storeFile=../../reedu-android.keystore
storeType=jks
keyAlias=reedu-android
keyPassword=xxxxxxxxxxxxxxxxxxxxxxxxx
storePassword=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
To build & deploy on an emulator or device use the `android:*` build commands defined in `package.json` (some only work on linux), for example: To build & deploy on an emulator or device use the `android:*` build commands defined in `package.json` (some only work on linux), for example:
```bash ```bash
npm run android:dev # build debug build & deploy to connected device & restart app npm run android:dev # build debug build & deploy to connected device & restart app
``` ```
###### release signing
You need a keystore with a valid signing key!
```
zipalign -p 4 blockly-unsigned.apk blockly-aligned.apk
apksigner sign -ks reedu-android.keystore --in blockly-aligned.apk --out blockly-signed.apk
```
### updating blockly ### updating blockly
Blockly is included as a submodule, linking to <https://github.com/sensebox/ardublockly-1>. Blockly is included as a submodule, linking to <https://github.com/sensebox/ardublockly-1>.
To update it, just pull in the commit you want, and commit the change in this repository: To update it, just pull in the commit you want, and commit the change in this repository:

@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<widget id="de.sensebox.blockly" version="1.1.4" android-versionCode="10104" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> <widget id="de.sensebox.blockly" version="1.1.2" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>Blockly for senseBox</name> <name>Blockly for senseBox</name>
<description>graphical programming &amp; OTA upload for senseBox MCU</description> <description>graphical programming &amp; OTA upload for senseBox MCU</description>
<author email="kontakt@reedu.de" href="https://reedu.de/">re:edu</author> <author email="kontakt@reedu.de" href="https://reedu.de/">re:edu</author>
@ -15,12 +15,10 @@
<preference name="android-minSdkVersion" value="24" /> <preference name="android-minSdkVersion" value="24" />
<preference name="BackupWebStorage" value="none" /> <preference name="BackupWebStorage" value="none" />
<preference name="SplashMaintainAspectRatio" value="true" /> <preference name="SplashMaintainAspectRatio" value="true" />
<preference name="FadeSplashScreenDuration" value="500" /> <preference name="FadeSplashScreenDuration" value="300" />
<preference name="AutoHideSplashScreen" value="false" />
<preference name="ShowSplashScreenSpinner" value="false" />
<preference name="SplashShowOnlyFirstTime" value="false" /> <preference name="SplashShowOnlyFirstTime" value="false" />
<preference name="SplashScreen" value="screen" /> <preference name="SplashScreen" value="screen" />
<preference name="SplashScreenDelay" value="10000" /> <preference name="SplashScreenDelay" value="4000" />
<platform name="android"> <platform name="android">
<allow-intent href="market:*" /> <allow-intent href="market:*" />
<icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" /> <icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />

1471
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
{ {
"name": "blockly-sensebox", "name": "blockly-sensebox",
"version": "1.1.4", "version": "1.1.2",
"author": "Reedu GmbH", "author": "Reedu GmbH",
"homepage": "https://reedu.de/", "homepage": "https://reedu.de/",
"private": true, "private": true,
@ -8,7 +8,7 @@
"start": "ionic-app-scripts serve", "start": "ionic-app-scripts serve",
"clean": "ionic-app-scripts clean", "clean": "ionic-app-scripts clean",
"build": "ionic-app-scripts build --prod", "build": "ionic-app-scripts build --prod",
"android:build": "npm run clean && ionic cordova build --release --prod android", "android:build": "ionic cordova build --release --prod android",
"android:build:debug": "ionic cordova build android", "android:build:debug": "ionic cordova build android",
"android:deploy": "adb install -r platforms/android/app/build/outputs/apk/debug/app-debug.apk", "android:deploy": "adb install -r platforms/android/app/build/outputs/apk/debug/app-debug.apk",
"android:start": "adb shell am force-stop de.sensebox.blockly; adb shell am start -n de.sensebox.blockly/de.sensebox.blockly.MainActivity; sleep 5; adb logcat | grep -F \"`adb shell ps | grep de.sensebox.blockly | awk -F' ' '{print $2}'`\"", "android:start": "adb shell am force-stop de.sensebox.blockly; adb shell am start -n de.sensebox.blockly/de.sensebox.blockly.MainActivity; sleep 5; adb logcat | grep -F \"`adb shell ps | grep de.sensebox.blockly | awk -F' ' '{print $2}'`\"",
@ -51,7 +51,7 @@
"zone.js": "0.8.26" "zone.js": "0.8.26"
}, },
"devDependencies": { "devDependencies": {
"@ionic/app-scripts": "^3.2.4", "@ionic/app-scripts": "3.2.3",
"typescript": "~2.6.2" "typescript": "~2.6.2"
}, },
"description": "OpenSenseApps by IfGI 2018 App-Dev course.", "description": "OpenSenseApps by IfGI 2018 App-Dev course.",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 14 KiB

@ -0,0 +1 @@
41e1c536c5dfb47db1311f7335b357b3

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 92 KiB

@ -0,0 +1 @@
287a089b0cd0ff44dfda2fd7bd7ad812

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

@ -6,7 +6,6 @@ import { TranslateService } from '@ngx-translate/core';
import { COLORS, DEFAULT_LANG } from '../constants'; import { COLORS, DEFAULT_LANG } from '../constants';
import { BlocklyPage } from '../pages/blockly/blockly'; import { BlocklyPage } from '../pages/blockly/blockly';
import { StorageProvider, SETTINGS } from '../providers/storage/storage';
@Component({ @Component({
templateUrl: 'app.html' templateUrl: 'app.html'
@ -44,16 +43,12 @@ export class openSenseApp {
constructor( constructor(
public translate: TranslateService, public translate: TranslateService,
private storage: StorageProvider,
platform: Platform, platform: Platform,
statusBar: StatusBar, statusBar: StatusBar,
splashScreen: SplashScreen, splashScreen: SplashScreen,
) { ) {
this.translate.addLangs(['en', 'de']);
this.translate.setDefaultLang(DEFAULT_LANG) this.translate.setDefaultLang(DEFAULT_LANG)
const lang = this.getPreferredLanguage() this.translate.use(this.getPreferredLanguage())
this.translate.use(lang)
this.storage.get(SETTINGS).language = lang
platform.ready() platform.ready()
.then(() => { .then(() => {
@ -78,7 +73,7 @@ export class openSenseApp {
private getPreferredLanguage () { private getPreferredLanguage () {
const langsAvailable = this.translate.getLangs() const langsAvailable = this.translate.getLangs()
const lang = this.storage.get(SETTINGS).language || this.translate.getBrowserLang() const lang = this.translate.getBrowserLang()
return langsAvailable.indexOf(lang) === -1 ? DEFAULT_LANG : lang return langsAvailable.indexOf(lang) === -1 ? DEFAULT_LANG : lang
} }
} }

@ -1 +1 @@
Subproject commit cc464a6466c2e6b2648e93e386d574d526cd3f40 Subproject commit 11ba718798e3d097d87ec1cc2ea0d7a62a026ca6

@ -237,6 +237,64 @@
</div> </div>
</div> </div>
<!-- Select Additional Blocks menu -->
<div id="blocks_menu" class="modal modal-fixed-footer hidden">
<div class="modal-content">
<div>
<h4><span class="translatable_addBlocksTitle">Additional Blocks</span></h4>
</div>
<div id="blocks_menu_body"></div>
</div>
<div class="modal-footer">
<a href="#" class="waves-effect btn-flat modal-close"><span class="translatable_return">Return</span></a>
</div>
</div>
<!-- General Alert: Content is loaded using JavaScript to display alerts -->
<div id="gen_alert" class="modal modal_small modal-fixed-footer hidden">
<div class="modal-content">
<h5 id="gen_alert_title">Empty Alert</h4>
<p><span id="gen_alert_body">Empty alert text</span></p>
</div>
<div class="modal-footer">
<a id="gen_alert_ok_link" href="#" class="waves-effect btn-flat modal-close"><span class="translatable_okay">Okay</span></a>
<a id="gen_alert_cancel_link" href="#" class="waves-effect btn-flat modal-close"><span class="translatable_cancel">Cancel</span></a>
</div>
</div>
<!-- Prompt: Content is loaded using JavaScript to display input prompt -->
<div id="gen_prompt" class="modal modal_small modal-fixed-footer hidden">
<div class="modal-content">
<p><span id="gen_prompt_message">Prompt message</span></p>
<p><input id="gen_prompt_input" value="test" /></p>
</div>
<div class="modal-footer">
<a id="gen_prompt_ok_link" href="#" class="waves-effect btn-flat modal-close"><span class="translatable_okay">Okay</span></a>
<a id="gen_prompt_cancel_link" href="#" class="waves-effect btn-flat modal-close"><span class="translatable_cancel">Cancel</span></a>
</div>
</div>
<!-- Local Modal to be shown if Ardublockly Server is not running. -->
<div id="not_running_dialog" class="modal">
<div class="modal-content">
<div>
<h4 id="gen_alert_title"><span class="translatable_noServerTitle">Ardublockly app not running</span></h4>
</div>
<div class="translatable_noServerBody">
<p>For all the Ardublockly features to be enabled, the Ardublockly desktop application must be running locally on your computer.</p>
<p>If you are using an online version you will not be able to configure the settings nor load the blocks code into an Arduino.</p>
<p>Installation instruction can be found in the <a href="https://github.com/sensebox/ardublockly">Ardublockly repository</a>.</p>
<p>If you have Ardublockly already installed, make sure the application is running correctly.</p>
</div>
</div>
<div class="modal-footer">
<a id="gen_alert_ok_link" href="#" class="waves-effect btn-flat modal-close"><span class="translatable_okay">Okay</span></a>
</div>
</div>
<!-- Desktop version of Ardublockly JS, needs to be loaded first. -->
<script src="blockly/ardublockly/ardublockly_desktop.js"></script>
<!-- jQuery and Materialize JS --> <!-- jQuery and Materialize JS -->
<script src="blockly/ardublockly/js_libs/jquery-2.1.3.min.js"></script> <script src="blockly/ardublockly/js_libs/jquery-2.1.3.min.js"></script>
<script src="blockly/ardublockly/materialize/materialize.js"></script> <script src="blockly/ardublockly/materialize/materialize.js"></script>

@ -1,5 +1,4 @@
{ {
"CANCEL": "Abbrechen",
"MENU": { "MENU": {
"TITLE": "Menü", "TITLE": "Menü",
"ABOUT": "Über", "ABOUT": "Über",
@ -23,20 +22,13 @@
"BLOCKLY": { "BLOCKLY": {
"TITLE": "Blockly für senseBox", "TITLE": "Blockly für senseBox",
"BTN_CODE": "Quellcode Ansicht", "BTN_CODE": "Quellcode Ansicht",
"BTN_OTA": "OTA Upload" "BTN_OTA": "OTA Programmierung"
}, },
"SETTINGS": { "SETTINGS": {
"TITLE": "Einstellungen", "TITLE": "Einstellungen",
"LANGUAGE": {
"TITLE": "Sprache"
},
"LOG_OPTIN": { "LOG_OPTIN": {
"TITLE": "Sende Fehlerberichte", "TITLE": "Sende uns automatisch Fehler-Berichte zu",
"TEXT": "Das hilft uns beim Beheben von Fehlern.<br/>Keine persönlichen Daten außer deinem Sketch werden übertragen." "TEXT": "Das hilft uns beim Beheben von Fehlern.<br/>Keine persönlichen Daten außer deinem Sketch werden übertragen."
},
"RESET": {
"TITLE": "App zurücksetzen",
"TEXT": "Dies setzt die Einstellungen zurück und löscht deine Sketche!"
} }
}, },
"OTAWIZ": { "OTAWIZ": {

@ -1,5 +1,4 @@
{ {
"CANCEL": "Cancel",
"MENU": { "MENU": {
"TITLE": "Menu", "TITLE": "Menu",
"ABOUT": "About", "ABOUT": "About",
@ -27,16 +26,9 @@
}, },
"SETTINGS": { "SETTINGS": {
"TITLE": "Settings", "TITLE": "Settings",
"LANGUAGE": {
"TITLE": "Language"
},
"LOG_OPTIN": { "LOG_OPTIN": {
"TITLE": "Automatically send error reports", "TITLE": "Automatically send error reports to us",
"TEXT": "These reports help us in troubleshooting issues.<br/>No personal information is collected, except for your Sketch." "TEXT": "These reports help us in troubleshooting issues.<br/>No personal information is collected, except for your Sketch."
},
"RESET": {
"TITLE": "Reset App",
"TEXT": "This will reset your settings and delete your Sketches!"
} }
}, },
"OTAWIZ": { "OTAWIZ": {

@ -8,16 +8,10 @@ import { LoggingProvider } from '../../providers/logging/logging';
*/ */
export class BlocklyMessageProtocol { export class BlocklyMessageProtocol {
// resolve ready promise once the blocklyFrame is ready ready: Promise<void>
ready = new Promise(resolve => {
window.addEventListener('message', (ev: IframePostMessageEvent) => {
// @HACK @FIXME: timeout is required, as blockly resolves some async functions
// after firing the `ready` event..
if (ev.data.type === 'ready') setTimeout(resolve, 300)
})
})
static reqResPatterns: BlocklyReqPatterns = { // defines the expected response type for each request type
private reqResPatterns: BlocklyReqPatterns = {
'getSketch': 'sketch', 'getSketch': 'sketch',
'getXml': 'xml', 'getXml': 'xml',
'setXml': null, 'setXml': null,
@ -32,10 +26,20 @@ export class BlocklyMessageProtocol {
else else
this.log.debug(`received ${ev.data.type} message from blockly`, { message: ev.data }) this.log.debug(`received ${ev.data.type} message from blockly`, { message: ev.data })
}) })
// resolve ready promise once the blocklyFrame is ready
this.ready = new Promise(resolve => {
window.addEventListener('message', (ev: IframePostMessageEvent) => {
// @HACK @FIXME: timeout is required, as blockly resolves some async functions
// after firing the `ready` event..
if (ev.data.type === 'ready') setTimeout(resolve, 300)
})
})
} }
toggleView() { this.sendRequest({ type: 'toggleView' }) } toggleView() { this.sendRequest({ type: 'toggleView' }) }
setXml(data: string) { this.sendRequest({ type: 'setXml', data }) } setXml(data: string) { this.sendRequest({ type: 'setXml', data }) }
getXml() { return this.sendRequest({ type: 'getXml' }) } getXml() { return this.sendRequest({ type: 'getXml' }) }
getSketch() { return this.sendRequest({ type: 'getSketch' }) } getSketch() { return this.sendRequest({ type: 'getSketch' }) }
@ -44,13 +48,13 @@ export class BlocklyMessageProtocol {
if ( if (
!this.blocklyFrame || !this.blocklyFrame ||
!this.blocklyFrame.nativeElement || !this.blocklyFrame.nativeElement &&
!this.blocklyFrame.nativeElement.contentWindow !this.blocklyFrame.nativeElement.contentWindow
) { ) {
throw new Error('cannot access blockly frame') throw Error('cannot access blockly frame')
} }
const expectResponse = BlocklyMessageProtocol.reqResPatterns[req.type] const expectResponse = this.reqResPatterns[req.type]
this.log.debug(`sending ${req.type} message to blockly, expecting response: ${expectResponse}`, { message: req }) this.log.debug(`sending ${req.type} message to blockly, expecting response: ${expectResponse}`, { message: req })
if (!expectResponse) if (!expectResponse)
@ -61,14 +65,14 @@ export class BlocklyMessageProtocol {
const resHandler = ({ data: res }: IframePostMessageEvent) => { const resHandler = ({ data: res }: IframePostMessageEvent) => {
if (expectResponse !== res.type) return if (expectResponse !== res.type) return
window.removeEventListener('message', resHandler) window.removeEventListener('message', resHandler)
if (res.type === 'error') reject(res.data) if (expectResponse === 'error') reject(res.data)
else resolve(res.data) else resolve(res.data)
} }
// TODO: promise reject after timeout? // TODO: promise reject after timeout?
window.addEventListener('message', resHandler) window.addEventListener('message', resHandler)
}) })
// send message *after* registering the response handler! // send message after registering the subscribe handler!
this.blocklyFrame.nativeElement.contentWindow.postMessage(req, '*') this.blocklyFrame.nativeElement.contentWindow.postMessage(req, '*')
return resPromise return resPromise
} }

@ -9,20 +9,6 @@
<ion-content padding> <ion-content padding>
<ion-list> <ion-list>
<ion-item>
<ion-icon name="text" item-start></ion-icon>
<ion-label>
<h2 style="color: black;" translate>SETTINGS.LANGUAGE.TITLE</h2>
</ion-label>
<ion-select
interface="popover"
(ngModelChange)="onSettingsChange('language', $event)"
[ngModel]="settings.language"
>
<ion-option *ngFor="let lang of translate.langs" value="{{lang}}">{{ lang }}</ion-option>
</ion-select>
</ion-item>
<ion-item> <ion-item>
<ion-icon name="megaphone" item-start></ion-icon> <ion-icon name="megaphone" item-start></ion-icon>
<ion-label> <ion-label>
@ -31,13 +17,5 @@
</ion-label> </ion-label>
<ion-toggle (ngModelChange)="onSettingsChange('logOptin', $event)" [ngModel]="settings.logOptin"></ion-toggle> <ion-toggle (ngModelChange)="onSettingsChange('logOptin', $event)" [ngModel]="settings.logOptin"></ion-toggle>
</ion-item> </ion-item>
<button ion-item (click)="resetStorage()">
<ion-icon name="refresh" item-start></ion-icon>
<ion-label>
<h2 translate>SETTINGS.RESET.TITLE</h2>
</ion-label>
</button>
</ion-list> </ion-list>
</ion-content> </ion-content>

@ -1,3 +1,3 @@
.error { page-settings {
color: red !important;
} }

@ -1,9 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { IonicPage, ActionSheetController } from 'ionic-angular'; import { IonicPage } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { StorageProvider, SETTINGS } from '../../providers/storage/storage'; import { StorageProvider, SETTINGS } from '../../providers/storage/storage';
import { DEFAULT_LANG } from '../../constants';
@IonicPage() @IonicPage()
@ -12,46 +10,14 @@ import { DEFAULT_LANG } from '../../constants';
templateUrl: 'settings.html', templateUrl: 'settings.html',
}) })
export class SettingsPage { export class SettingsPage {
settings = {} as any settings = {}
constructor( constructor(private storage: StorageProvider) {
public actionCtrl: ActionSheetController,
public translate: TranslateService,
private storage: StorageProvider,
) {
this.settings = this.storage.get(SETTINGS) this.settings = this.storage.get(SETTINGS)
this.settings.language = this.translate.currentLang
} }
onSettingsChange (name, value) { onSettingsChange (name, value) {
this.settings[name] = value this.settings[name] = value
this.storage.set(SETTINGS, this.settings) this.storage.set(SETTINGS, this.settings)
if (name === 'language')
this.translate.use(value)
}
resetStorage() {
const handler = () => {
this.storage.reset()
// update UI state & reset language
this.settings = this.storage.get(SETTINGS)
const lang = this.getPreferredLanguage()
this.storage.get(SETTINGS).language = lang
this.translate.use(lang)
}
this.actionCtrl.create({
title: this.translate.instant('SETTINGS.RESET.TEXT'),
buttons: [
{ text: this.translate.instant('CANCEL') },
{ text: this.translate.instant('SETTINGS.RESET.TITLE'), cssClass: 'error', handler },
]
}).present()
}
private getPreferredLanguage () {
const langsAvailable = this.translate.getLangs()
const lang = this.storage.get(SETTINGS).language || this.translate.getBrowserLang()
return langsAvailable.indexOf(lang) === -1 ? DEFAULT_LANG : lang
} }
} }

@ -29,10 +29,12 @@ export class OtaWifiProvider {
return WifiStrategy.Manual return WifiStrategy.Manual
} }
// FIXME: iOS 11+ supposedly allows WiFi API queries (see WifiWizard2 docs), if (
// but testing in emulator gives "not supported". might be an emulator issue? this.platform.is('android') ||
if (this.platform.is('android')) this.platform.is('ios') && this.platform.version().major >= 11
) {
return WifiStrategy.Automatic return WifiStrategy.Automatic
}
return WifiStrategy.Manual return WifiStrategy.Manual
} }
@ -81,7 +83,7 @@ export class OtaWifiProvider {
} }
export enum WifiStrategy { export enum WifiStrategy {
Automatic = 'Automatic', // android Automatic = 'Automatic', // android, iOS 11+
Manual = 'Manual', // iOS, browser Manual = 'Manual', // older iOS
Unavailable = 'Unavailable', // currently unused Unavailable = 'Unavailable', // browser
} }

@ -8,11 +8,6 @@ export class StorageProvider {
private cache: Map<string, any> = new Map() private cache: Map<string, any> = new Map()
constructor () { constructor () {
this.init()
}
init () {
// set up default values
this.registerKey(SETTINGS, { logOptin: false }) this.registerKey(SETTINGS, { logOptin: false })
this.registerKey(LASTSKETCH, '') this.registerKey(LASTSKETCH, '')
} }
@ -32,10 +27,4 @@ export class StorageProvider {
localStorage.setItem(key, JSON.stringify(value)) localStorage.setItem(key, JSON.stringify(value))
this.cache[key] = value this.cache[key] = value
} }
reset () {
localStorage.clear()
this.cache = new Map()
this.init()
}
} }

@ -0,0 +1,16 @@
#!/bin/bash
# expects travis env vars: $TRAVIS_TAG, $GITHUB_TOKEN
rev=$(git rev-parse --short HEAD)
target=https://$GITHUB_TOKEN@github.com/sensebox/blockly-app.git
branch=fdroid
git clone --depth=1 -b $branch $target fdroid
rm -rf fdroid/*
mv -f platforms/android/* fdroid/
cd fdroid
git config user.name "Travis-CI"
git config user.email "travis@travis-ci.org"
git add -f --ignore-errors .
git commit -m "android project for $TRAVIS_TAG at $rev" --amend # dont add new commits to avoid increasing repo size
git push -f $target $branch > /dev/null 2>&1 # hide output to not leak GITHUB_TOKEN in logs

@ -21,10 +21,7 @@ const handleLog = (req, res) => {
req.on('end', () => { req.on('end', () => {
try { try {
const msg = JSON.parse(body) const msg = JSON.parse(body)
msg.logclient = { msg.useragent = req.headers['user-agent']
ip: req.connection.remoteAddress,
ua: req.headers['user-agent']
}
fileStream.write(JSON.stringify(msg)) fileStream.write(JSON.stringify(msg))
fileStream.write('\n') fileStream.write('\n')
@ -52,10 +49,7 @@ const requestHandler = (req, res) => {
} }
} }
const fileStream = fs.createWriteStream(logfile, { const fileStream = fs.createWriteStream(logfile, 'utf-8')
encoding: 'utf-8',
flags: 'a', // append
})
const server = http.createServer(requestHandler) const server = http.createServer(requestHandler)
server.listen(port, err => { server.listen(port, err => {

Loading…
Cancel
Save