diff --git a/android/ScratchJr/.idea/codeStyles/Project.xml b/android/ScratchJr/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..681f41a
--- /dev/null
+++ b/android/ScratchJr/.idea/codeStyles/Project.xml
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ xmlns:android
+
+ ^$
+
+
+
+
+
+
+
+
+ xmlns:.*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*:id
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ .*:name
+
+ http://schemas.android.com/apk/res/android
+
+
+
+
+
+
+
+
+ name
+
+ ^$
+
+
+
+
+
+
+
+
+ style
+
+ ^$
+
+
+
+
+
+
+
+
+ .*
+
+ ^$
+
+
+ BY_NAME
+
+
+
+
+
+
+ .*
+
+ http://schemas.android.com/apk/res/android
+
+
+ ANDROID_ATTRIBUTE_ORDER
+
+
+
+
+
+
+ .*
+
+ .*
+
+
+ BY_NAME
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/ScratchJr/.idea/misc.xml b/android/ScratchJr/.idea/misc.xml
index e8ee3d9..d5d4a5c 100644
--- a/android/ScratchJr/.idea/misc.xml
+++ b/android/ScratchJr/.idea/misc.xml
@@ -5,7 +5,7 @@
diff --git a/android/ScratchJr/app/src/main/java/org/scratchjr/android/ScratchJrActivity.java b/android/ScratchJr/app/src/main/java/org/scratchjr/android/ScratchJrActivity.java
index c47a469..a58b89f 100644
--- a/android/ScratchJr/app/src/main/java/org/scratchjr/android/ScratchJrActivity.java
+++ b/android/ScratchJr/app/src/main/java/org/scratchjr/android/ScratchJrActivity.java
@@ -337,7 +337,7 @@ public class ScratchJrActivity
}
// We send the project Base64-encoded to JavaScript where it's processed and unpacked
String base64Project = Base64.encodeToString(projectData.toByteArray(), Base64.DEFAULT);
- runJavaScript("iOS.loadProjectFromSjr('" + base64Project + "');");
+ runJavaScript("OS.loadProjectFromSjr('" + base64Project + "');");
}
public RelativeLayout getContainer() {
diff --git a/editions/free/ios-resources/Default-Landscape@2x~ipad.png b/editions/free/ios-resources/Default-Landscape@2x~ipad.png
index ed49202..34d2de1 100644
Binary files a/editions/free/ios-resources/Default-Landscape@2x~ipad.png and b/editions/free/ios-resources/Default-Landscape@2x~ipad.png differ
diff --git a/editions/free/ios-resources/Default-Landscape~ipad.png b/editions/free/ios-resources/Default-Landscape~ipad.png
index f6ee582..e946fe5 100644
Binary files a/editions/free/ios-resources/Default-Landscape~ipad.png and b/editions/free/ios-resources/Default-Landscape~ipad.png differ
diff --git a/ios/ScratchJr.xcodeproj/project.pbxproj b/ios/ScratchJr.xcodeproj/project.pbxproj
index 5c156f1..3c9959a 100644
--- a/ios/ScratchJr.xcodeproj/project.pbxproj
+++ b/ios/ScratchJr.xcodeproj/project.pbxproj
@@ -419,7 +419,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "EDITION=free;\n\n../bin/bundle-compile.sh;\n\nrsync -pvtrlL --cvs-exclude \\\n ../editions/$EDITION/ios-resources/* \\\n \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/\";\n\nrsync -pvtrlL --cvs-exclude \\\n ../editions/$EDITION/src/* \\\n \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/HTML5\";\n \nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/HTML5/pnglibrary\";\n\n../bin/convert-svg-to-png.py -i \"../editions/$EDITION/src/svglibrary/\" -o \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/HTML5/pnglibrary\";";
+ shellScript = "EDITION=free;\n\n../bin/bundle-compile.sh;\n\nrsync -pvtrlL --cvs-exclude \\\n ../editions/$EDITION/ios-resources/* \\\n \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/\";\n\nrsync -pvtrlL --cvs-exclude \\\n ../editions/$EDITION/src/* \\\n \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/HTML5\";\n \nmkdir -p \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/HTML5/pnglibrary\";\n\n../bin/convert-svg-to-png.py -i \"../editions/$EDITION/src/svglibrary/\" -o \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/HTML5/pnglibrary\";\n";
};
/* End PBXShellScriptBuildPhase section */
diff --git a/ios/ScratchJr/src/IO.m b/ios/ScratchJr/src/IO.m
index f2eea80..3324e53 100644
--- a/ios/ScratchJr/src/IO.m
+++ b/ios/ScratchJr/src/IO.m
@@ -282,7 +282,7 @@ NSMutableDictionary *soundtimers;
+ (void)soundEnded:(NSTimer*)timer {
NSString *soundName = [[timer userInfo] objectForKey:@"soundName"];
if (sounds[soundName] == nil) return;
- NSString *callback = [NSString stringWithFormat:@"iOS.soundDone('%@');", soundName];
+ NSString *callback = [NSString stringWithFormat:@"OS.soundDone('%@');", soundName];
UIWebView *webview = [ViewController webview];
dispatch_async(dispatch_get_main_queue(), ^{
[webview stringByEvaluatingJavaScriptFromString:callback];
diff --git a/ios/ScratchJr/src/ViewController.m b/ios/ScratchJr/src/ViewController.m
index 0827e9f..5571b85 100644
--- a/ios/ScratchJr/src/ViewController.m
+++ b/ios/ScratchJr/src/ViewController.m
@@ -164,7 +164,7 @@ JSContext *js;
NSLog(@"could not load the website caused by error DESC: %@", error);
NSDictionary *userInfo = [error userInfo];
NSString *desc = [NSString stringWithFormat:@"%@", ([userInfo objectForKey: @"NSLocalizedDescription"] == NULL)? [error localizedDescription]: [userInfo objectForKey: @"NSLocalizedDescription"]];
- NSString *callback = [NSString stringWithFormat: @"iOS.pageError('%@');",desc];
+ NSString *callback = [NSString stringWithFormat: @"OS.pageError('%@');",desc];
UIWebView *webview = [ViewController webview];
dispatch_async(dispatch_get_main_queue(), ^{
[webview stringByEvaluatingJavaScriptFromString: callback];
@@ -172,7 +172,7 @@ JSContext *js;
}
- (void) receiveProject:(NSString *)project{
- NSString *callback = [NSString stringWithFormat:@"iOS.loadProjectFromSjr('%@');", project];
+ NSString *callback = [NSString stringWithFormat:@"OS.loadProjectFromSjr('%@');", project];
UIWebView *webview = [ViewController webview];
dispatch_async(dispatch_get_main_queue(), ^{
NSString *res = [webview stringByEvaluatingJavaScriptFromString:callback];
@@ -310,7 +310,7 @@ JSContext *js;
return [ScratchJr captureimage:onCameraCaptureComplete];
}
-//iOS.sendSjrToShareDialog = function(fileName, emailSubject, emailBody, shareType, b64data) {
+//OS.sendSjrToShareDialog = function(fileName, emailSubject, emailBody, shareType, b64data) {
-(NSString*) sendSjrUsingShareDialog:(NSString*) fileName :(NSString*) emailSubject :(NSString*) emailBody :(int) shareType :(NSString*) b64data {
return [IO sendSjrUsingShareDialog:fileName :emailSubject :emailBody :shareType : b64data];
diff --git a/src/editor/ScratchJr.js b/src/editor/ScratchJr.js
index 52c2cf8..d6852cb 100644
--- a/src/editor/ScratchJr.js
+++ b/src/editor/ScratchJr.js
@@ -6,8 +6,8 @@ import Undo from './ui/Undo';
import Alert from './ui/Alert';
import Palette from './ui/Palette';
import Record from './ui/Record';
-import IO from '../iPad/IO';
-import iOS from '../iPad/iOS';
+import IO from '../tablet/IO';
+import OS from '../tablet/OS';
import UI from './ui/UI';
import Menu from './blocks/Menu';
import Library from './ui/Library';
@@ -183,7 +183,7 @@ export default class ScratchJr {
document.body.scrollTop = 0;
time = (new Date()) - 0;
var urlvars = getUrlVars();
- iOS.hascamera();
+ OS.hascamera();
ScratchJr.log('starting the app');
BlockSpecs.initBlocks();
Project.loadIcon = document.createElement('img');
@@ -345,7 +345,7 @@ export default class ScratchJr {
static saveProject (e, onDone) {
if (ScratchJr.isEditable() && editmode == 'storyStarter' && storyStarted && !Project.error) {
- iOS.analyticsEvent('samples', 'story_starter_edited', Project.metadata.name);
+ OS.analyticsEvent('samples', 'story_starter_edited', Project.metadata.name);
// Localize sample project names
var sampleName = Localization.localize('SAMPLE_' + Project.metadata.name);
// Get the new project name
@@ -381,14 +381,14 @@ export default class ScratchJr {
ScratchJr.stopStripsFromTop(e);
ScratchJr.unfocus(e);
ScratchJr.saveProject(e, ScratchJr.flippage);
- iOS.analyticsEvent('editor', 'project_editor_close');
+ OS.analyticsEvent('editor', 'project_editor_close');
}
static flippage () {
Alert.close();
- iOS.cleanassets('wav', doNext);
+ OS.cleanassets('wav', doNext);
function doNext () {
- iOS.cleanassets('svg', ScratchJr.switchPage);
+ OS.cleanassets('svg', ScratchJr.switchPage);
}
}
@@ -525,7 +525,7 @@ export default class ScratchJr {
ScratchJr.displayStatus('none');
inFullscreen = true;
UI.enterFullScreen();
- iOS.analyticsEvent('editor', 'full_screen_entered');
+ OS.analyticsEvent('editor', 'full_screen_entered');
document.body.style.background = 'black';
}
@@ -537,7 +537,7 @@ export default class ScratchJr {
inFullscreen = false;
UI.quitFullScreen();
onBackButtonCallback.pop();
- iOS.analyticsEvent('editor', 'full_screen_exited');
+ OS.analyticsEvent('editor', 'full_screen_exited');
document.body.style.background = 'white';
}
@@ -905,26 +905,6 @@ export default class ScratchJr {
/////////////////
//Application on the background
-
- // XXX: does this ever happen?
- // I'm pretty sure this is dead code -TM
- static saveProjectState () {
- ScratchAudio.sndFX('tap.wav');
- if (frame.style.display == 'none') {
- Paint.saveEditState(ScratchJr.stopServer);
- } else {
- ScratchJr.unfocus();
- ScratchJr.stopStrips();
- if (ScratchJr.isEditable() && currentProject && !Project.error && changed) {
- Project.save(currentProject, ScratchJr.stopServer);
- }
- }
- }
-
- static stopServer () {
- iOS.stopserver(iOS.trace);
- }
-
/**
* The functions that are invokved when the Android back button is clicked.
* Methods are called from the rear and popped off after each invocation.
diff --git a/src/editor/blocks/BlockSpecs.js b/src/editor/blocks/BlockSpecs.js
index 40f6f5a..14e4db6 100755
--- a/src/editor/blocks/BlockSpecs.js
+++ b/src/editor/blocks/BlockSpecs.js
@@ -1,5 +1,5 @@
import Localization from '../../utils/Localization';
-import IO from '../../iPad/IO';
+import IO from '../../tablet/IO';
let loadCount = 0;
diff --git a/src/editor/engine/Page.js b/src/editor/engine/Page.js
index e92e064..f1d9045 100644
--- a/src/editor/engine/Page.js
+++ b/src/editor/engine/Page.js
@@ -5,9 +5,9 @@ import UI from '../ui/UI';
import Sprite from './Sprite';
import Palette from '../ui/Palette';
import BlockSpecs from '../blocks/BlockSpecs';
-import iOS from '../../iPad/iOS';
-import IO from '../../iPad/IO';
-import MediaLib from '../../iPad/MediaLib';
+import OS from '../../tablet/OS';
+import IO from '../../tablet/IO';
+import MediaLib from '../../tablet/MediaLib';
import Undo from '../ui/Undo';
import Matrix from '../../geom/Matrix';
import Vector from '../../geom/Vector';
@@ -125,7 +125,9 @@ export default class Page {
return;
}
var me = this;
- var url = (MediaLib.keys[name]) ? MediaLib.path + name : (name.indexOf('/') < 0) ? iOS.path + name : name;
+ var url = (MediaLib.keys[name]) ?
+ MediaLib.path + name :
+ (name.indexOf('/') < 0) ? OS.path + name : name;
var md5 = (MediaLib.keys[name]) ? MediaLib.path + name : name;
if (md5.substr(md5.length - 3) == 'png') {
@@ -137,7 +139,7 @@ export default class Page {
if (md5.indexOf('/') > -1) {
IO.requestFromServer(md5, doNext);
} else {
- iOS.getmedia(md5, nextStep);
+ OS.getmedia(md5, nextStep);
}
function nextStep (base64) {
doNext(atob(base64));
@@ -145,7 +147,7 @@ export default class Page {
function doNext (str) {
str = str.replace(/>\s*<');
me.setSVG(str);
- if ((str.indexOf('xlink:href') < 0) && iOS.path) {
+ if ((str.indexOf('xlink:href') < 0) && OS.path) {
me.setBackgroundImage(url, fcn); // does not have embedded images
} else {
var base64 = IO.getImageDataURL(me.md5, btoa(str));
diff --git a/src/editor/engine/Sprite.js b/src/editor/engine/Sprite.js
index c0949d3..75b2312 100755
--- a/src/editor/engine/Sprite.js
+++ b/src/editor/engine/Sprite.js
@@ -12,9 +12,9 @@ import Project from '../ui/Project';
import Thumbs from '../ui/Thumbs';
import UI from '../ui/UI';
import BlockSpecs from '../blocks/BlockSpecs';
-import iOS from '../../iPad/iOS';
-import IO from '../../iPad/IO';
-import MediaLib from '../../iPad/MediaLib';
+import IO from '../../tablet/IO';
+import OS from '../../tablet/OS';
+import MediaLib from '../../tablet/MediaLib';
import Undo from '../ui/Undo';
import ScriptsPane from '../ui/ScriptsPane';
import SVG2Canvas from '../../utils/SVG2Canvas';
@@ -77,12 +77,14 @@ export default class Sprite {
getAsset (whenDone) {
var md5 = this.md5;
var spr = this;
- var url = (MediaLib.keys[md5]) ? MediaLib.path + md5 : (md5.indexOf('/') < 0) ? iOS.path + md5 : md5;
+ var url = (MediaLib.keys[md5]) ?
+ MediaLib.path + md5 :
+ (md5.indexOf('/') < 0) ? OS.path + md5 : md5;
md5 = (MediaLib.keys[md5]) ? MediaLib.path + md5 : md5;
if (md5.indexOf('/') > -1) {
IO.requestFromServer(md5, doNext);
} else {
- iOS.getmedia(md5, nextStep);
+ OS.getmedia(md5, nextStep);
}
function nextStep (base64) {
doNext(atob(base64));
@@ -90,7 +92,7 @@ export default class Sprite {
function doNext (str) {
str = str.replace(/>\s*<');
spr.setSVG(str);
- if ((str.indexOf('xlink:href') < 0) && iOS.path) {
+ if ((str.indexOf('xlink:href') < 0) && OS.path) {
whenDone(url); // does not have embedded images
} else {
var base64 = IO.getImageDataURL(spr.md5, btoa(str));
@@ -716,7 +718,7 @@ export default class Sprite {
var sprites = JSON.parse(page.sprites);
sprites.push(this.id);
page.sprites = JSON.stringify(sprites);
- iOS.analyticsEvent('editor', 'text_sprite_create');
+ OS.analyticsEvent('editor', 'text_sprite_create');
if ((this.str == '') && !whenDone) {
this.setTextBox();
this.activateInput();
@@ -806,7 +808,7 @@ export default class Sprite {
document.body.scrollLeft = 0;
var form = document.forms.activetextbox;
var changed = (this.oldvalue != form.typing.value);
- iOS.analyticsEvent('editor', 'text_sprite_close');
+ OS.analyticsEvent('editor', 'text_sprite_close');
if (this.noChars(form.typing.value)) {
this.deleteText(this.oldvalue != '');
} else {
@@ -891,7 +893,7 @@ export default class Sprite {
var ti = document.forms.activetextbox.typing;
gn('textbox').style.visibility = 'visible';
var me = this;
- iOS.analyticsEvent('editor', 'text_sprite_open');
+ OS.analyticsEvent('editor', 'text_sprite_open');
ti.onblur = function () {
me.unfocusText();
};
diff --git a/src/editor/ui/Library.js b/src/editor/ui/Library.js
index 96ce963..76cde19 100644
--- a/src/editor/ui/Library.js
+++ b/src/editor/ui/Library.js
@@ -1,8 +1,8 @@
import ScratchJr from '../ScratchJr';
-import iOS from '../../iPad/iOS';
-import IO from '../../iPad/IO';
-import MediaLib from '../../iPad/MediaLib';
+import OS from '../../tablet/OS';
+import IO from '../../tablet/IO';
+import MediaLib from '../../tablet/MediaLib';
import Paint from '../../painteditor/Paint';
import Events from '../../utils/Events';
import Localization from '../../utils/Localization';
@@ -406,12 +406,12 @@ export default class Library {
// (this is possible if we receive a duplicate project, for example)
Library.assetThumbnailUnique(data.altmd5, type, function (isUnique) {
if (isUnique) {
- iOS.remove(data.altmd5, iOS.trace);
+ OS.remove(data.altmd5, OS.trace);
}
});
}
- IO.deleteobject(key, data.id, iOS.trace);
+ IO.deleteobject(key, data.id, OS.trace);
}
static parseAssetData (data) {
@@ -525,7 +525,7 @@ export default class Library {
if (!(selectedOne in MediaLib.keys)) {
analyticsName = 'user_asset';
}
- iOS.analyticsEvent('editor', 'new_character', analyticsName);
+ OS.analyticsEvent('editor', 'new_character', analyticsName);
}
Library.close(e);
}
@@ -542,7 +542,7 @@ export default class Library {
if (!(selectedOne in MediaLib.keys)) {
analyticsName = 'user_background';
}
- iOS.analyticsEvent('editor', 'choose_background', analyticsName);
+ OS.analyticsEvent('editor', 'choose_background', analyticsName);
}
Library.close(e);
}
diff --git a/src/editor/ui/Palette.js b/src/editor/ui/Palette.js
index a22dc3b..6f0ec7a 100644
--- a/src/editor/ui/Palette.js
+++ b/src/editor/ui/Palette.js
@@ -7,8 +7,8 @@ import Block from '../blocks/Block';
import BlockSpecs from '../blocks/BlockSpecs';
import ScriptsPane from './ScriptsPane';
import Undo from './Undo';
-import iOS from '../../iPad/iOS';
-import MediaLib from '../../iPad/MediaLib';
+import OS from '../../tablet/OS';
+import MediaLib from '../../tablet/MediaLib';
import Events from '../../utils/Events';
import Rectangle from '../../geom/Rectangle';
import DrawPath from '../../utils/DrawPath';
@@ -582,7 +582,7 @@ export default class Palette {
e.preventDefault();
switch (Palette.getLandingPlace(element, e)) {
case 'scripts':
- iOS.analyticsEvent('editor', 'new_block_' + element.owner.blocktype);
+ OS.analyticsEvent('editor', 'new_block_' + element.owner.blocktype);
var sc = ScratchJr.getActiveScript();
var dx = localx(sc, element.left);
var dy = localy(sc, element.top);
diff --git a/src/editor/ui/Project.js b/src/editor/ui/Project.js
index 8229dc5..3ad7a9a 100644
--- a/src/editor/ui/Project.js
+++ b/src/editor/ui/Project.js
@@ -5,8 +5,8 @@ import Palette from './Palette';
import UI from './UI';
import Page from '../engine/Page';
import Sprite from '../engine/Sprite';
-import iOS from '../../iPad/iOS';
-import IO from '../../iPad/IO';
+import OS from '../../tablet/OS';
+import IO from '../../tablet/IO';
import Paint from '../../painteditor/Paint';
import SVG2Canvas from '../../utils/SVG2Canvas';
import {frame, gn, newHTML, scaleMultiplier, getIdFor,
@@ -417,7 +417,7 @@ export default class Project {
json.cond = 'deleted = ? AND id != ? AND gallery IS NULL';
json.items = ['name', 'thumbnail', 'id'];
json.values = ['NO', projectID];
- IO.query(iOS.database, json, function (result) {
+ IO.query(OS.database, json, function (result) {
var pdata = JSON.parse(result);
var isUnique = true;
for (var p = 0; p < pdata.length; p++) {
@@ -444,7 +444,7 @@ export default class Project {
if (thumb.md5.indexOf('samples/') < 0) { // In case we've exited story-starter mode
Project.thumbnailUnique(thumb.md5, id, function (isUnique) {
if (isUnique) {
- iOS.remove(thumb.md5, iOS.trace); // remove thumb;
+ OS.remove(thumb.md5, OS.trace); // remove thumb;
}
});
}
@@ -454,14 +454,14 @@ export default class Project {
Project.getThumbnailPNG(ScratchJr.stage.pages[0], 192, 144, getMD5);
function getMD5 (dataurl) {
var pngBase64 = dataurl.split(',')[1];
- iOS.getmd5(pngBase64, function (str) {
+ OS.getmd5(pngBase64, function (str) {
savePNG(str, pngBase64);
});
}
function savePNG (md5, pngBase64) {
var filename = ScratchJr.currentProject + '_' + md5;
- iOS.setmedianame(pngBase64, filename, 'png', doNext);
+ OS.setmedianame(pngBase64, filename, 'png', doNext);
}
function doNext (md5) {
diff --git a/src/editor/ui/Record.js b/src/editor/ui/Record.js
index 47bb8e6..a9dc43f 100644
--- a/src/editor/ui/Record.js
+++ b/src/editor/ui/Record.js
@@ -1,7 +1,7 @@
import ScratchJr from '../ScratchJr';
import Palette from './Palette';
import Undo from './Undo';
-import iOS from '../../iPad/iOS';
+import OS from '../../tablet/OS';
import ScratchAudio from '../../utils/ScratchAudio';
import {frame, gn, newHTML, isTablet, isAndroid, setProps} from '../../utils/lib';
@@ -60,7 +60,7 @@ export default class Record {
// Dialog box hide/show
static appear () {
- iOS.analyticsEvent('editor', 'record_dialog_open');
+ OS.analyticsEvent('editor', 'record_dialog_open');
gn('backdrop').setAttribute('class', 'modal-backdrop fade in');
setProps(gn('backdrop').style, {
display: 'block'
@@ -72,7 +72,7 @@ export default class Record {
}
static disappear () {
- iOS.analyticsEvent('editor', 'record_dialog_close');
+ OS.analyticsEvent('editor', 'record_dialog_close');
setTimeout(function () {
gn('backdrop').setAttribute('class', 'modal-backdrop fade');
setProps(gn('backdrop').style, {
@@ -148,13 +148,13 @@ export default class Record {
if (isRecording) {
Record.stopRecording(); // Stop if we're already recording
} else {
- iOS.sndrecord(Record.startRecording); // Start a recording
+ OS.sndrecord(Record.startRecording); // Start a recording
}
}
}
static startRecording (filename) {
- iOS.analyticsEvent('editor', 'start_recording');
+ OS.analyticsEvent('editor', 'start_recording');
if (parseInt(filename) < 0) {
// Error in getting record filename - go back to editor
recordedSound = undefined;
@@ -169,7 +169,7 @@ export default class Record {
Record.soundname = filename;
Record.toggleButtonUI('record', true);
var poll = function () {
- iOS.volume(Record.updateVolume, Record.recordError);
+ OS.volume(Record.updateVolume, Record.recordError);
};
interval = setInterval(poll, 33);
timeLimit = setTimeout(function () {
@@ -202,7 +202,7 @@ export default class Record {
// Start playing the sound and switch UI appropriately
static startPlaying () {
- iOS.startplay(Record.timeOutPlay);
+ OS.startplay(Record.timeOutPlay);
Record.toggleButtonUI('play', true);
isPlaying = true;
}
@@ -244,7 +244,7 @@ export default class Record {
// Stop playing the sound and switch UI appropriately
static stopPlayingSound (fcn) {
- iOS.stopplay(fcn);
+ OS.stopplay(fcn);
Record.toggleButtonUI('play', false);
isPlaying = false;
window.clearTimeout(playTimeLimit);
@@ -253,7 +253,7 @@ export default class Record {
// Stop the volume monitor and recording
static stopRecording (fcn) {
- iOS.analyticsEvent('editor', 'stop_recording');
+ OS.analyticsEvent('editor', 'stop_recording');
if (timeLimit != null) {
clearTimeout(timeLimit);
timeLimit = null;
@@ -272,7 +272,7 @@ export default class Record {
static volumeCheckStopped (fcn) {
isRecording = false;
Record.recordUIoff();
- iOS.recordstop(fcn);
+ OS.recordstop(fcn);
}
// Press OK (check)
@@ -293,12 +293,12 @@ export default class Record {
}
static closeContinueSave () {
- iOS.recorddisappear('YES', Record.registerProjectSound);
+ OS.recorddisappear('YES', Record.registerProjectSound);
}
static closeContinueRemove () {
// don't get the sound - proceed right to tearDown
- iOS.recorddisappear('NO', Record.tearDownRecorder);
+ OS.recorddisappear('NO', Record.tearDownRecorder);
}
static registerProjectSound () {
diff --git a/src/editor/ui/Thumbs.js b/src/editor/ui/Thumbs.js
index 8f07bc9..1e6d1d2 100644
--- a/src/editor/ui/Thumbs.js
+++ b/src/editor/ui/Thumbs.js
@@ -8,7 +8,7 @@ import Page from '../engine/Page';
import ScriptsPane from './ScriptsPane';
import Undo from './Undo';
import UI from './UI';
-import iOS from '../../iPad/iOS';
+import OS from '../../tablet/OS';
import Events from '../../utils/Events';
import ScratchAudio from '../../utils/ScratchAudio';
import {frame, gn, localx, newHTML, scaleMultiplier, getIdFor,
@@ -83,7 +83,7 @@ export default class Thumbs {
var tb = Thumbs.getType(Thumbs.t, 'pagethumb');
if (ScratchJr.shaking && (e.target.className == 'deletethumb')) {
ScratchJr.clearSelection();
- iOS.analyticsEvent('editor', 'delete_scene');
+ OS.analyticsEvent('editor', 'delete_scene');
ScratchJr.stage.deletePage(tb.owner);
return;
}
@@ -378,7 +378,7 @@ export default class Thumbs {
sc.owner.deactivate();
}
ScratchJr.unfocus(e);
- iOS.analyticsEvent('editor', 'add_scene');
+ OS.analyticsEvent('editor', 'add_scene');
new Page(getIdFor('page'));
}
diff --git a/src/editor/ui/UI.js b/src/editor/ui/UI.js
index 8bd8f1e..f9a7761 100644
--- a/src/editor/ui/UI.js
+++ b/src/editor/ui/UI.js
@@ -13,15 +13,16 @@ import Stage from '../engine/Stage';
import ScriptsPane from './ScriptsPane';
import Undo from './Undo';
import Library from './Library';
-import iOS from '../../iPad/iOS';
-import IO from '../../iPad/IO';
-import MediaLib from '../../iPad/MediaLib';
+import OS from '../../tablet/OS';
+import IO from '../../tablet/IO';
+import MediaLib from '../../tablet/MediaLib';
import Paint from '../../painteditor/Paint';
import Events from '../../utils/Events';
import Localization from '../../utils/Localization';
import ScratchAudio from '../../utils/ScratchAudio';
-import {frame, gn, CSSTransition, localx, newHTML, scaleMultiplier, fullscreenScaleMultiplier, getIdFor, isTablet, newDiv,
- newTextInput, isAndroid, getDocumentWidth, getDocumentHeight, setProps, globalx} from '../../utils/lib';
+import {frame, gn, CSSTransition, localx, newHTML, scaleMultiplier, fullscreenScaleMultiplier,
+ getIdFor, isTablet, newDiv, newTextInput, isAndroid, getDocumentWidth, getDocumentHeight,
+ setProps, globalx} from '../../utils/lib';
let projectNameTextInput = null;
let info = null;
@@ -156,7 +157,7 @@ export default class UI {
};
}
- iOS.deviceName(function (name) {
+ OS.deviceName(function (name) {
gn('deviceName').textContent = name;
});
@@ -257,7 +258,7 @@ export default class UI {
setTimeout(saveAndShare, 500); // 500ms delay to wait for loading GIF to show and keyboard to hide
- iOS.analyticsEvent('editor', 'share_button', (shareType == EMAILSHARE) ? 'email' : 'airdrop');
+ OS.analyticsEvent('editor', 'share_button', (shareType == EMAILSHARE) ? 'email' : 'airdrop');
function saveAndShare () {
// Save the project's new name
@@ -276,7 +277,7 @@ export default class UI {
var emailSubject = Localization.localize('SHARING_EMAIL_SUBJECT', {
PROJECT_NAME: IO.shareName
});
- iOS.sendSjrToShareDialog(IO.zipFileName, emailSubject, Localization.localize('SHARING_EMAIL_TEXT'),
+ OS.sendSjrToShareDialog(IO.zipFileName, emailSubject, Localization.localize('SHARING_EMAIL_TEXT'),
shareType, contents);
shareLoadingGif.style.visibility = 'hidden';
@@ -332,7 +333,7 @@ export default class UI {
static handleTextFieldSave (dontHide) {
// Handle story-starter mode project
if (ScratchJr.isEditable() && ScratchJr.editmode == 'storyStarter' && !Project.error) {
- iOS.analyticsEvent('samples', 'story_starter_edited', Project.metadata.name);
+ OS.analyticsEvent('samples', 'story_starter_edited', Project.metadata.name);
// Get the new project name
var sampleName = Localization.localize('SAMPLE_' + Project.metadata.name);
IO.uniqueProjectName({
@@ -366,7 +367,7 @@ export default class UI {
}
Project.metadata.name = pname;
ScratchJr.changed = true;
- iOS.setfield(iOS.database, Project.metadata.id, 'name', pname);
+ OS.setfield(OS.database, Project.metadata.id, 'name', pname);
if (!dontHide) {
ScratchAudio.sndFX('exittap.wav');
gn('infobox').className = 'infobox fade';
@@ -757,7 +758,7 @@ export default class UI {
static switchGrid () {
ScratchAudio.sndFX('tap.wav');
UI.setShowGrid(Grid.hidden);
- iOS.analyticsEvent('editor', Grid.hidden ? 'hide_grid' : 'show_grid');
+ OS.analyticsEvent('editor', Grid.hidden ? 'hide_grid' : 'show_grid');
}
static setShowGrid (b) {
diff --git a/src/entry/app.js b/src/entry/app.js
index bcd31bf..136b7d6 100644
--- a/src/entry/app.js
+++ b/src/entry/app.js
@@ -1,9 +1,9 @@
import {preprocessAndLoadCss} from '../utils/lib';
import Localization from '../utils/Localization';
import AppUsage from '../utils/AppUsage';
-import iOS from '../iPad/iOS';
-import IO from '../iPad/IO';
-import MediaLib from '../iPad/MediaLib';
+import OS from '../tablet/OS';
+import IO from '../tablet/IO';
+import MediaLib from '../tablet/MediaLib';
import {indexMain} from './index';
import {homeMain} from './home';
@@ -11,7 +11,6 @@ import {editorMain} from './editor';
import {gettingStartedMain} from './gettingstarted';
import {inappInterfaceGuide, inappAbout, inappBlocksGuide, inappPaintEditorGuide} from './inapp';
-
function loadSettings (settingsRoot, whenDone) {
IO.requestFromServer(settingsRoot + 'settings.json', (result) => {
window.Settings = JSON.parse(result);
@@ -42,7 +41,7 @@ window.onload = () => {
preprocessAndLoadCss('css', 'css/thumbs.css');
/* For parental gate. These CSS properties should be refactored */
preprocessAndLoadCss('css', 'css/editor.css');
- entryFunction = () => iOS.waitForInterface(indexMain);
+ entryFunction = () => OS.waitForInterface(indexMain);
break;
case 'home':
// Lobby pages
@@ -50,7 +49,7 @@ window.onload = () => {
preprocessAndLoadCss('css', 'css/base.css');
preprocessAndLoadCss('css', 'css/lobby.css');
preprocessAndLoadCss('css', 'css/thumbs.css');
- entryFunction = () => iOS.waitForInterface(homeMain);
+ entryFunction = () => OS.waitForInterface(homeMain);
break;
case 'editor':
// Editor pages
@@ -62,14 +61,14 @@ window.onload = () => {
preprocessAndLoadCss('css', 'css/editormodal.css');
preprocessAndLoadCss('css', 'css/librarymodal.css');
preprocessAndLoadCss('css', 'css/paintlook.css');
- entryFunction = () => iOS.waitForInterface(editorMain);
+ entryFunction = () => OS.waitForInterface(editorMain);
break;
case 'gettingStarted':
// Getting started video page
preprocessAndLoadCss('css', 'css/font.css');
preprocessAndLoadCss('css', 'css/base.css');
preprocessAndLoadCss('css', 'css/gs.css');
- entryFunction = () => iOS.waitForInterface(gettingStartedMain);
+ entryFunction = () => OS.waitForInterface(gettingStartedMain);
break;
case 'inappAbout':
// About ScratchJr in-app help frame
diff --git a/src/entry/editor.js b/src/entry/editor.js
index 74d6925..76434b1 100644
--- a/src/entry/editor.js
+++ b/src/entry/editor.js
@@ -1,14 +1,14 @@
import ScratchJr from '../editor/ScratchJr';
-import iOS from '../iPad/iOS';
+import OS from '../tablet/OS';
import Camera from '../painteditor/Camera';
import Record from '../editor/ui/Record';
export function editorMain () {
- iOS.getsettings(doNext);
- iOS.analyticsEvent('editor', 'project_editor_open');
+ OS.getsettings(doNext);
+ OS.analyticsEvent('editor', 'project_editor_open');
function doNext (str) {
var list = str.split(',');
- iOS.path = list[1] == '0' ? list[0] + '/' : undefined;
+ OS.path = list[1] == '0' ? list[0] + '/' : undefined;
if (list.length > 2) {
Record.available = list[2] == 'YES' ? true : false;
}
diff --git a/src/entry/home.js b/src/entry/home.js
index aac133c..3f49588 100644
--- a/src/entry/home.js
+++ b/src/entry/home.js
@@ -1,15 +1,15 @@
import {gn} from '../utils/lib';
import Localization from '../utils/Localization';
-import iOS from '../iPad/iOS';
+import OS from '../tablet/OS';
import Lobby from '../lobby/Lobby';
export function homeMain () {
gn('logotab').ontouchend = homeGoBack;
homeStrings();
- iOS.getsettings(doNext);
+ OS.getsettings(doNext);
function doNext (str) {
var list = str.split(',');
- iOS.path = list[1] == '0' ? list[0] + '/' : undefined;
+ OS.path = list[1] == '0' ? list[0] + '/' : undefined;
Lobby.appinit(window.Settings.scratchJrVersion);
}
}
diff --git a/src/entry/index.js b/src/entry/index.js
index 27415d5..baa849c 100644
--- a/src/entry/index.js
+++ b/src/entry/index.js
@@ -1,6 +1,6 @@
import ScratchAudio from '../utils/ScratchAudio';
import {gn, getUrlVars, isAndroid, isiOS} from '../utils/lib';
-import iOS from '../iPad/iOS';
+import OS from '../tablet/OS';
import UI from '../editor/ui/UI';
import Localization from '../utils/Localization';
import AppUsage from '../utils/AppUsage';
@@ -49,9 +49,9 @@ function indexFirstTime () {
gn('blueguy').className = 'blue show';
gn('redguy').className = 'red show';
}
- iOS.askpermission(); // ask for sound recording
+ OS.askpermission(); // ask for sound recording
setTimeout(function () {
- iOS.hidesplash(doit);
+ OS.hidesplash(doit);
}, 500);
function doit () {
window.ontouchend = function () {
@@ -94,7 +94,7 @@ function indexLoadStart (afterUsage) {
gn('usageOther').className = 'usageOther hide';
gn('usageNoanswer').className = 'usageNoanswer hide';
}
- iOS.setAnalyticsPlacePref(AppUsage.currentUsage);
+ OS.setAnalyticsPlacePref(AppUsage.currentUsage);
}
gn('gettings').className = 'gettings show';
gn('startcode').className = 'startcode show';
@@ -133,7 +133,7 @@ function indexLoadUsage () {
}
function indexGohome () {
- iOS.setfile('homescroll.sjr', 0, function () {
+ OS.setfile('homescroll.sjr', 0, function () {
doNext();
});
function doNext () {
@@ -171,7 +171,7 @@ function indexSetUsage (e) {
break;
}
// Send one-time analytics event about usage
- iOS.analyticsEvent('lobby', 'scratchjr_usage', usageText);
+ OS.analyticsEvent('lobby', 'scratchjr_usage', usageText);
AppUsage.setUsage(usageText);
ScratchAudio.sndFX('tap.wav');
indexLoadStart(true);
diff --git a/src/iPad/iOS.js b/src/iPad/iOS.js
deleted file mode 100644
index 8062bfe..0000000
--- a/src/iPad/iOS.js
+++ /dev/null
@@ -1,375 +0,0 @@
-import {isiOS, gn} from '../utils/lib';
-import IO from './IO';
-import Lobby from '../lobby/Lobby';
-import Alert from '../editor/ui/Alert';
-import ScratchAudio from '../utils/ScratchAudio';
-
-//////////////////////////////////////////////////
-// Tablet interface functions
-//////////////////////////////////////////////////
-
-// This file and object are named "iOS" for legacy reasons.
-// But, it is also used for the AndroidInterface. All function calls here
-// are mapped to Android/iOS native calls.
-
-let path;
-let camera;
-let database = 'projects';
-let mediacounter = 0;
-let tabletInterface = null;
-
-export default class iOS {
- // Getters/setters for properties used in other classes
- static get path () {
- return path;
- }
-
- static set path (newPath) {
- path = newPath;
- }
-
- static get camera () {
- return camera;
- }
-
- static get database () {
- return database;
- }
-
- // Wait for the tablet interface to be injected into the webview
- static waitForInterface (fcn) {
- // Already loaded the interface
- if (tabletInterface != null) {
- fcn();
- return;
- }
-
- // Android device
- if (typeof AndroidInterface !== 'undefined') {
- tabletInterface = AndroidInterface;
- if (fcn) {
- fcn();
- }
- return;
- }
-
- // iOS device - might not be loaded yet
- if (typeof (window.tablet) != 'object') {
- // Come back in 100ms
- setTimeout(function () {
- iOS.waitForInterface(fcn);
- }, 100);
- } else {
- // All set to run commands
- tabletInterface = window.tablet;
- if (fcn) {
- fcn();
- }
- }
- }
-
- // Database functions
- static stmt (json, fcn) {
- var result = tabletInterface.database_stmt(JSON.stringify(json));
- if (typeof (fcn) !== 'undefined') {
- fcn(result);
- }
- }
-
- static query (json, fcn) {
- var result = tabletInterface.database_query(JSON.stringify(json));
- if (typeof (fcn) !== 'undefined') {
- fcn(result);
- }
- }
-
- static setfield (db, id, fieldname, val, fcn) {
- var json = {};
- var keylist = [fieldname + ' = ?', 'mtime = ?'];
- json.values = [val, (new Date()).getTime().toString()];
- json.stmt = 'update ' + db + ' set ' + keylist.toString() + ' where id = ' + id;
- iOS.stmt(json, fcn);
- }
-
- // IO functions
-
- static cleanassets (ft, fcn) {
- tabletInterface.io_cleanassets(ft); fcn();
- }
-
- static getmedia (file, fcn) {
- mediacounter++;
- var nextStep = function (file, key, whenDone) {
- var result = tabletInterface.io_getmedialen(file, key);
- iOS.processdata(key, 0, result, '', whenDone);
- };
- nextStep(file, mediacounter, fcn);
- }
-
- static getmediadata (key, offset, len, fcn) {
- var result = tabletInterface.io_getmediadata(key, offset, len);
- if (fcn) {
- fcn(result);
- }
- }
-
- static processdata (key, off, len, oldstr, fcn) {
- if (len == 0) {
- iOS.getmediadone(key);
- fcn(oldstr);
- return;
- }
- var newlen = (len < 100000) ? len : 100000;
- iOS.getmediadata(key, off, newlen, function (str) {
- iOS.processdata(key, off + newlen, len - newlen, oldstr + str, fcn);
- });
- }
-
- static getsettings (fcn) {
- var result = tabletInterface.io_getsettings();
- if (fcn) {
- fcn(result);
- }
- }
-
- static getmediadone (file, fcn) {
- var result = tabletInterface.io_getmediadone(file);
- if (fcn) {
- fcn(result);
- }
- }
-
- static setmedia (str, ext, fcn) {
- var result = tabletInterface.io_setmedia(str, ext);
- if (fcn) {
- fcn(result);
- }
- }
-
- static setmedianame (str, name, ext, fcn) {
- var result = tabletInterface.io_setmedianame(str, name, ext);
- if (fcn) {
- fcn(result);
- }
- }
-
- static getmd5 (str, fcn) {
- var result = tabletInterface.io_getmd5(str);
- if (fcn) {
- fcn(result);
- }
- }
-
- static remove (str, fcn) {
- var result = tabletInterface.io_remove(str);
- if (fcn) {
- fcn(result);
- }
- }
-
- static getfile (str, fcn) {
- var result = tabletInterface.io_getfile(str);
- if (fcn) {
- fcn(result);
- }
- }
-
- static setfile (name, str, fcn) {
- var result = tabletInterface.io_setfile(name, btoa(str));
- if (fcn) {
- fcn(result);
- }
- }
-
- // Sound functions
-
- static registerSound (dir, name, fcn) {
- var result = tabletInterface.io_registersound(dir, name);
- if (fcn) {
- fcn(result);
- }
- }
-
- static playSound (name, fcn) {
- var result = tabletInterface.io_playsound(name);
- if (fcn) {
- fcn(result);
- }
- }
-
- static stopSound (name, fcn) {
- var result = tabletInterface.io_stopsound(name);
- if (fcn) {
- fcn(result);
- }
- }
-
- // Web Wiew delegate call backs
-
- static soundDone (name) {
- ScratchAudio.soundDone(name);
- }
-
- static sndrecord (fcn) {
- var result = tabletInterface.recordsound_recordstart();
- if (fcn) {
- fcn(result);
- }
- }
-
- static recordstop (fcn) {
- var result = tabletInterface.recordsound_recordstop();
- if (fcn) {
- fcn(result);
- }
- }
-
- static volume (fcn) {
- var result = tabletInterface.recordsound_volume();
- if (fcn) {
- fcn(result);
- }
- }
-
- static startplay (fcn) {
- var result = tabletInterface.recordsound_startplay();
- if (fcn) {
- fcn(result);
- }
- }
-
- static stopplay (fcn) {
- var result = tabletInterface.recordsound_stopplay();
- if (fcn) {
- fcn(result);
- }
- }
-
- static recorddisappear (b, fcn) {
- var result = tabletInterface.recordsound_recordclose(b);
- if (fcn) {
- fcn(result);
- }
- }
-
- // Record state
- static askpermission () {
- if (isiOS) {
- tabletInterface.askForPermission();
- }
- }
-
- // camera functions
-
- static hascamera () {
- camera = tabletInterface.scratchjr_cameracheck();
- }
-
- static startfeed (data, fcn) {
- var str = JSON.stringify(data);
- var result = tabletInterface.scratchjr_startfeed(str);
- if (fcn) {
- fcn(result);
- }
- }
-
- static stopfeed (fcn) {
- var result = tabletInterface.scratchjr_stopfeed();
- if (fcn) {
- fcn(result);
- }
- }
-
- static choosecamera (mode, fcn) {
- var result = tabletInterface.scratchjr_choosecamera(mode);
- if (fcn) {
- fcn(result);
- }
- }
-
- static captureimage (fcn) {
- tabletInterface.scratchjr_captureimage(fcn);
- }
-
- static hidesplash (fcn) {
- if (isiOS) {
- tabletInterface.hideSplash();
- }
- if (fcn) {
- fcn();
- }
- }
-
- static trace (str) {
- console.log(str); // eslint-disable-line no-console
- }
-
- static parse (str) {
- console.log(JSON.parse(str)); // eslint-disable-line no-console
- }
-
- static tracemedia (str) {
- console.log(atob(str)); // eslint-disable-line no-console
- }
-
- ignore () {
- }
-
- ///////////////
- // Sharing
- ///////////////
-
-
- // Called on the JS side to trigger native UI for project sharing.
- // fileName: name for the file to share
- // emailSubject: subject text to use for an email
- // emailBody: body HTML to use for an email
- // shareType: 0 for Email; 1 for Airdrop
- // b64data: base-64 encoded .SJR file to share
-
- static sendSjrToShareDialog (fileName, emailSubject, emailBody, shareType, b64data) {
- tabletInterface.sendSjrUsingShareDialog(fileName, emailSubject, emailBody, shareType, b64data);
- }
-
- // Called on the Objective-C side. The argument is a base64-encoded .SJR file,
- // to be unzipped, processed, and stored.
- static loadProjectFromSjr (b64data) {
- try {
- IO.loadProjectFromSjr(b64data);
- } catch (err) {
- var errorMessage = 'Couldn\'t load share -- project data corrupted. ' + err.message;
- Alert.open(gn('frame'), gn('frame'), errorMessage, '#ff0000');
- console.log(err); // eslint-disable-line no-console
- return 0;
- }
- return 1;
- }
-
- // Name of the device/iPad to display on the sharing dialog page
- // fcn is called with the device name as an arg
- static deviceName (fcn) {
- fcn(tabletInterface.deviceName());
- }
-
- static analyticsEvent (category, action, label) {
- tabletInterface.analyticsEvent(category, action, label);
- }
-
- static setAnalyticsPlacePref (preferredPlace) {
- tabletInterface.setAnalyticsPlacePref(preferredPlace);
- }
-
- // Web Wiew delegate call backs
-
- static pageError (desc) {
- console.log('XCODE ERROR:', desc); // eslint-disable-line no-console
- if (window.location.href.indexOf('home.html') > -1) {
- if (Lobby.errorTimer) {
- Lobby.errorLoading(desc);
- }
- }
- }
-}
-
-// Expose iOS methods for ScratchJr tablet sharing callbacks
-window.iOS = iOS;
diff --git a/src/lobby/Home.js b/src/lobby/Home.js
index 161bb2b..6a78204 100755
--- a/src/lobby/Home.js
+++ b/src/lobby/Home.js
@@ -3,8 +3,8 @@
//////////////////////////////////////////////////
import Lobby from './Lobby';
-import iOS from '../iPad/iOS';
-import IO from '../iPad/IO';
+import OS from '../tablet/OS';
+import IO from '../tablet/IO';
import Project from '../editor/ui/Project';
import Localization from '../utils/Localization';
import ScratchAudio from '../utils/ScratchAudio';
@@ -135,7 +135,7 @@ export default class Home {
if (md5 && (md5 == 'newproject')) {
Home.createNewProject();
} else if (md5) {
- iOS.setfile('homescroll.sjr', gn('wrapc').scrollTop, function () {
+ OS.setfile('homescroll.sjr', gn('wrapc').scrollTop, function () {
doNext(md5);
});
}
@@ -144,10 +144,10 @@ export default class Home {
ScratchAudio.sndFX('cut.wav');
Project.thumbnailUnique(Home.actionTarget.thumb, Home.actionTarget.id, function (isUnique) {
if (isUnique) {
- iOS.remove(Home.actionTarget.thumb, iOS.trace);
+ OS.remove(Home.actionTarget.thumb, OS.trace);
}
});
- iOS.setfield(iOS.database, Home.actionTarget.id, 'deleted', 'YES', Home.removeProjThumb);
+ OS.setfield(OS.database, Home.actionTarget.id, 'deleted', 'YES', Home.removeProjThumb);
break;
default:
if (Home.actionTarget && (Home.actionTarget.childElementCount > 2)) {
@@ -156,13 +156,13 @@ export default class Home {
break;
}
function doNext () {
- iOS.analyticsEvent('lobby', 'existing_project_edited');
+ OS.analyticsEvent('lobby', 'existing_project_edited');
window.location.href = 'editor.html?pmd5=' + md5 + '&mode=edit';
}
}
static createNewProject () {
- iOS.analyticsEvent('lobby', 'project_created');
+ OS.analyticsEvent('lobby', 'project_created');
var obj = {};
// XXX: for localization, the new project name should likely be refactored
obj.name = Home.getNextName(Localization.localize('NEW_PROJECT_PREFIX'));
@@ -172,7 +172,7 @@ export default class Home {
}
static gotoEditor (md5) {
- iOS.setfile('homescroll.sjr', gn('wrapc').scrollTop, function () {
+ OS.setfile('homescroll.sjr', gn('wrapc').scrollTop, function () {
doNext(md5);
});
function doNext (md5) {
@@ -230,7 +230,7 @@ export default class Home {
//////////////////////////
static displayYourProjects () {
- iOS.getfile('homescroll.sjr', gotScrollsState);
+ OS.getfile('homescroll.sjr', gotScrollsState);
function gotScrollsState (str) {
var num = Number(atob(str));
scrollvalue = (num.toString() == 'NaN') ? 0 : num;
@@ -239,7 +239,7 @@ export default class Home {
json.items = ['name', 'thumbnail', 'id', 'isgift'];
json.values = ['NO', version];
json.order = 'ctime desc';
- IO.query(iOS.database, json, Home.displayProjects);
+ IO.query(OS.database, json, Home.displayProjects);
}
}
diff --git a/src/lobby/Lobby.js b/src/lobby/Lobby.js
index 680a7c3..eb416e2 100644
--- a/src/lobby/Lobby.js
+++ b/src/lobby/Lobby.js
@@ -4,7 +4,7 @@
import {libInit, getUrlVars, gn, isAndroid, newHTML} from '../utils/lib';
import ScratchAudio from '../utils/ScratchAudio';
-import iOS from '../iPad/iOS';
+import OS from '../tablet/OS';
import Localization from '../utils/Localization';
import Cookie from '../utils/Cookie';
@@ -98,7 +98,7 @@ export default class Lobby {
var doNext = function (page) {
Lobby.changePage(page);
};
- iOS.setfile('homescroll.sjr', gn('wrapc').scrollTop, function () {
+ OS.setfile('homescroll.sjr', gn('wrapc').scrollTop, function () {
doNext(page);
});
} else {
@@ -203,7 +203,7 @@ export default class Lobby {
ScratchAudio.sndFX('tap.wav');
let newLocale = window.Settings.supportedLocales[e.target.textContent];
Cookie.set('localization', newLocale);
- iOS.analyticsEvent('lobby', 'language_changed', newLocale);
+ OS.analyticsEvent('lobby', 'language_changed', newLocale);
window.location = '?place=gear';
};
}
diff --git a/src/lobby/Samples.js b/src/lobby/Samples.js
index 240032a..82ae417 100644
--- a/src/lobby/Samples.js
+++ b/src/lobby/Samples.js
@@ -3,9 +3,9 @@
//////////////////////////////////////////////////
import Lobby from './Lobby';
-import IO from '../iPad/IO';
-import iOS from '../iPad/iOS';
-import MediaLib from '../iPad/MediaLib';
+import OS from '../tablet/OS';
+import IO from '../tablet/IO';
+import MediaLib from '../tablet/MediaLib';
import ScratchAudio from '../utils/ScratchAudio';
import Localization from '../utils/Localization';
import {gn, newHTML} from '../utils/lib';
@@ -75,7 +75,7 @@ export default class Samples {
e.preventDefault();
e.stopPropagation();
ScratchAudio.sndFX('tap.wav');
- iOS.analyticsEvent('samples', 'sample_opened', mt.textContent);
+ OS.analyticsEvent('samples', 'sample_opened', mt.textContent);
var md5 = mt.md5;
window.location.href = 'editor.html?pmd5=' + md5 + '&mode='
+ ((window.Settings.useStoryStarters) ? 'storyStarter' : 'look');
diff --git a/src/painteditor/Camera.js b/src/painteditor/Camera.js
index 2ca1720..0d4dfbd 100644
--- a/src/painteditor/Camera.js
+++ b/src/painteditor/Camera.js
@@ -1,5 +1,5 @@
import ScratchJr from '../editor/ScratchJr';
-import iOS from '../iPad/iOS';
+import OS from '../tablet/OS';
import ScratchAudio from '../utils/ScratchAudio';
import Paint from './Paint';
import PaintUndo from './PaintUndo';
@@ -53,7 +53,7 @@ export default class Camera {
data.mw = Paint.workspaceWidth;
data.mh = Paint.workspaceHeight;
data.image = mask.toDataURL('image/png');
- iOS.startfeed(data, iOS.trace);
+ OS.startfeed(data, OS.trace);
Paint.cameraToolsOn();
}
@@ -80,7 +80,7 @@ export default class Camera {
case 'cameraflip':
ScratchAudio.sndFX('tap.wav');
view = (view == 'front') ? 'back' : 'front';
- iOS.choosecamera(view, Camera.flip);
+ OS.choosecamera(view, Camera.flip);
break;
case 'camerasnap':
Camera.snapShot();
@@ -101,7 +101,7 @@ export default class Camera {
target = undefined;
view = 'front';
Camera.active = false;
- iOS.stopfeed();
+ OS.stopfeed();
Paint.cameraToolsOff();
if (isAndroid) {
ScratchJr.onBackButtonCallback.pop();
@@ -109,7 +109,7 @@ export default class Camera {
}
static snapShot () {
- iOS.captureimage('Camera.processimage'); // javascript call back;
+ OS.captureimage('Camera.processimage'); // javascript call back;
}
static getLayerMask (elem) {
@@ -197,5 +197,5 @@ export default class Camera {
}
}
-// Exposing the camera for the tablet callback in iOS.snapShot
+// Exposing the camera for the tablet callback in OS.snapShot
window.Camera = Camera;
diff --git a/src/painteditor/Paint.js b/src/painteditor/Paint.js
index 1329bc7..407bf13 100644
--- a/src/painteditor/Paint.js
+++ b/src/painteditor/Paint.js
@@ -3,9 +3,9 @@ import BlockSpecs from '../editor/blocks/BlockSpecs';
import SVGTools from './SVGTools';
import SVG2Canvas from '../utils/SVG2Canvas';
import Ghost from './Ghost';
-import iOS from '../iPad/iOS';
-import IO from '../iPad/IO';
-import MediaLib from '../iPad/MediaLib';
+import OS from '../tablet/OS';
+import IO from '../tablet/IO';
+import MediaLib from '../tablet/MediaLib';
import Localization from '../utils/Localization';
import Alert from '../editor/ui/Alert';
import PaintAction from './PaintAction';
@@ -169,7 +169,7 @@ export default class Paint {
// log two events:
// * paint editor is opened
// * type of edit (edit_background, edit_character, new_character)
- iOS.analyticsEvent('paint_editor', 'paint_editor_open');
+ OS.analyticsEvent('paint_editor', 'paint_editor_open');
if (bkg) {
action = 'edit_background';
label = (md5 in MediaLib.keys) ? md5 : 'user_background';
@@ -177,7 +177,7 @@ export default class Paint {
action = sname ? 'edit_character' : 'new_character';
label = (md5 in MediaLib.keys) ? md5 : 'user_character';
}
- iOS.analyticsEvent('paint_editor', action, label);
+ OS.analyticsEvent('paint_editor', action, label);
PaintUndo.buffer = [];
PaintUndo.index = 0;
maxZoom = 5;
@@ -356,7 +356,7 @@ export default class Paint {
}
static close () {
- iOS.analyticsEvent('paint_editor', 'paint_editor_close');
+ OS.analyticsEvent('paint_editor', 'paint_editor_close');
saving = true;
paintFrame.className = 'paintframe disappear';
frame.style.display = 'block';
@@ -774,7 +774,7 @@ export default class Paint {
Paint.addSidePalette(rightpal, 'selectortools', ['select', 'rotate']);
Paint.addSidePalette(rightpal, 'edittools', ['stamper', 'scissors']);
Paint.addSidePalette(rightpal, 'filltools',
- (iOS.camera == '1' && Camera.available) ? ['camera', 'paintbucket'] : ['paintbucket']);
+ (OS.camera == '1' && Camera.available) ? ['camera', 'paintbucket'] : ['paintbucket']);
}
static addSidePalette (p, id, list) {
@@ -1098,7 +1098,7 @@ export default class Paint {
Paint.loadChar(md5);
} else if (!MediaLib.keys[md5]) {
// Load user asset
- iOS.getmedia(md5, nextStep);
+ OS.getmedia(md5, nextStep);
} else {
// Load library asset
Paint.getBkg(MediaLib.path + md5);
@@ -1185,7 +1185,7 @@ export default class Paint {
Paint.loadChar(md5);
} else if (!MediaLib.keys[md5]) {
// Load user asset
- iOS.getmedia(md5, nextStep);
+ OS.getmedia(md5, nextStep);
} else {
// Load library asset
Paint.loadChar(MediaLib.path + md5);
@@ -1277,14 +1277,14 @@ export default class Paint {
static addToBkgLib (fcn) {
var dataurl = IO.getThumbnail(svgdata, 480, 360, 120, 90);
var pngBase64 = dataurl.split(',')[1];
- iOS.setmedia(pngBase64, 'png', setBkgRecord);
+ OS.setmedia(pngBase64, 'png', setBkgRecord);
function setBkgRecord (pngmd5) {
var json = {};
var keylist = ['md5', 'altmd5', 'version', 'width', 'height', 'ext'];
var values = '?,?,?,?,?,?';
json.values = [saveMD5, pngmd5, ScratchJr.version, '480', '360', 'svg'];
json.stmt = 'insert into userbkgs (' + keylist.toString() + ') values (' + values + ')';
- iOS.stmt(json, fcn);
+ OS.stmt(json, fcn);
}
}
@@ -1364,14 +1364,14 @@ export default class Paint {
var h = box.height.toString();
var dataurl = IO.getThumbnail(svgdata, w, h, 120, 90);
var pngBase64 = dataurl.split(',')[1];
- iOS.setmedia(pngBase64, 'png', setCostumeRecord);
+ OS.setmedia(pngBase64, 'png', setCostumeRecord);
function setCostumeRecord (pngmd5) {
var json = {};
var keylist = ['scale', 'md5', 'altmd5', 'version', 'width', 'height', 'ext', 'name'];
var values = '?,?,?,?,?,?,?,?';
json.values = [scale, saveMD5, pngmd5, ScratchJr.version, w, h, 'svg', cname];
json.stmt = 'insert into usershapes (' + keylist.toString() + ') values (' + values + ')';
- iOS.stmt(json, fcn);
+ OS.stmt(json, fcn);
}
}
diff --git a/src/tablet/Android.js b/src/tablet/Android.js
new file mode 100644
index 0000000..90d0fee
--- /dev/null
+++ b/src/tablet/Android.js
@@ -0,0 +1,277 @@
+//////////////////////////////////////////////////
+// Android interface functions
+// AndroidInterface will be the class defined for all the native function calls
+//////////////////////////////////////////////////
+
+let mediacounter = 0;
+
+export default class Android {
+ // Database functions
+ static stmt (json, fcn) {
+ var result = AndroidInterface.database_stmt(JSON.stringify(json));
+ if (typeof (fcn) !== 'undefined') {
+ fcn(result);
+ }
+ }
+
+ static query (json, fcn) {
+ var result = AndroidInterface.database_query(JSON.stringify(json));
+ if (typeof (fcn) !== 'undefined') {
+ fcn(result);
+ }
+ }
+
+ // IO functions
+
+ static cleanassets (ft, fcn) {
+ AndroidInterface.io_cleanassets(ft); fcn();
+ }
+
+ static getmedia (file, fcn) {
+ mediacounter++;
+ var nextStep = function (file, key, whenDone) {
+ var result = AndroidInterface.io_getmedialen(file, key);
+ Android.processdata(key, 0, result, '', whenDone);
+ };
+ nextStep(file, mediacounter, fcn);
+ }
+
+ static getmediadata (key, offset, len, fcn) {
+ var result = AndroidInterface.io_getmediadata(key, offset, len);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static processdata (key, off, len, oldstr, fcn) {
+ if (len == 0) {
+ Android.getmediadone(key);
+ fcn(oldstr);
+ return;
+ }
+ var newlen = (len < 100000) ? len : 100000;
+ Android.getmediadata(key, off, newlen, function (str) {
+ Android.processdata(key, off + newlen, len - newlen, oldstr + str, fcn);
+ });
+ }
+
+ static getsettings (fcn) {
+ var result = AndroidInterface.io_getsettings();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static getmediadone (file, fcn) {
+ var result = AndroidInterface.io_getmediadone(file);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static setmedia (str, ext, fcn) {
+ var result = AndroidInterface.io_setmedia(str, ext);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static setmedianame (str, name, ext, fcn) {
+ var result = AndroidInterface.io_setmedianame(str, name, ext);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static getmd5 (str, fcn) {
+ var result = AndroidInterface.io_getmd5(str);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static remove (str, fcn) {
+ var result = AndroidInterface.io_remove(str);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static getfile (str, fcn) {
+ var result = AndroidInterface.io_getfile(str);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static setfile (name, str, fcn) {
+ var result = AndroidInterface.io_setfile(name, btoa(str));
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ // Sound functions
+
+ static registerSound (dir, name, fcn) {
+ var result = AndroidInterface.io_registersound(dir, name);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static playSound (name, fcn) {
+ var result = AndroidInterface.io_playsound(name);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static stopSound (name, fcn) {
+ var result = AndroidInterface.io_stopsound(name);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ // Web Wiew delegate call backs
+
+ static sndrecord (fcn) {
+ var result = AndroidInterface.recordsound_recordstart();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static recordstop (fcn) {
+ var result = AndroidInterface.recordsound_recordstop();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static volume (fcn) {
+ var result = AndroidInterface.recordsound_volume();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static startplay (fcn) {
+ var result = AndroidInterface.recordsound_startplay();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static stopplay (fcn) {
+ var result = AndroidInterface.recordsound_stopplay();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static recorddisappear (b, fcn) {
+ var result = AndroidInterface.recordsound_recordclose(b);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ // camera functions
+
+ static hascamera () {
+ return AndroidInterface.scratchjr_cameracheck();
+ }
+
+ static startfeed (data, fcn) {
+ var str = JSON.stringify(data);
+ var result = AndroidInterface.scratchjr_startfeed(str);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static stopfeed (fcn) {
+ var result = AndroidInterface.scratchjr_stopfeed();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static choosecamera (mode, fcn) {
+ var result = AndroidInterface.scratchjr_choosecamera(mode);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static captureimage (fcn) {
+ AndroidInterface.scratchjr_captureimage(fcn);
+ }
+
+ static hidesplash (fcn) {
+ // just call funct, splash is hidden in native code
+ if (fcn) {
+ fcn();
+ }
+ }
+
+ ///////////////
+ // Sharing
+ ///////////////
+
+
+ // Called on the JS side to trigger native UI for project sharing.
+ // fileName: name for the file to share
+ // emailSubject: subject text to use for an email
+ // emailBody: body HTML to use for an email
+ // shareType: 0 for Email; 1 for Airdrop
+ // b64data: base-64 encoded .SJR file to share
+
+ static sendSjrToShareDialog (fileName, emailSubject, emailBody, shareType, b64data) {
+ AndroidInterface.sendSjrUsingShareDialog(fileName, emailSubject, emailBody, shareType, b64data);
+ }
+
+ // // Called on the Objective-C side. The argument is a base64-encoded .SJR file,
+ // // to be unzipped, processed, and stored.
+ // static loadProjectFromSjr (b64data) {
+ // try {
+ // IO.loadProjectFromSjr(b64data);
+ // } catch (err) {
+ // var errorMessage = 'Couldn\'t load share -- project data corrupted. ' + err.message;
+ // Alert.open(gn('frame'), gn('frame'), errorMessage, '#ff0000');
+ // console.log(err); // eslint-disable-line no-console
+ // return 0;
+ // }
+ // return 1;
+ // }
+
+ // Name of the device/iPad to display on the sharing dialog page
+ // fcn is called with the device name as an arg
+ static deviceName (fcn) {
+ fcn(AndroidInterface.deviceName());
+ }
+
+ static analyticsEvent (category, action, label) {
+ AndroidInterface.analyticsEvent(category, action, label);
+ }
+
+ static setAnalyticsPlacePref (preferredPlace) {
+ AndroidInterface.setAnalyticsPlacePref(preferredPlace);
+ }
+
+ // // Web Wiew delegate call backs
+ //
+ // static pageError (desc) {
+ // console.log('XCODE ERROR:', desc); // eslint-disable-line no-console
+ // if (window.location.href.indexOf('home.html') > -1) {
+ // if (Lobby.errorTimer) {
+ // Lobby.errorLoading(desc);
+ // }
+ // }
+ // }
+}
+
+// // Expose Android methods for ScratchJr tablet sharing callbacks
+// window.Android = Android;
diff --git a/src/iPad/IO.js b/src/tablet/IO.js
similarity index 95%
rename from src/iPad/IO.js
rename to src/tablet/IO.js
index db9727e..e378faf 100644
--- a/src/iPad/IO.js
+++ b/src/tablet/IO.js
@@ -1,4 +1,4 @@
-import iOS from './iOS';
+import OS from './OS';
import MediaLib from './MediaLib';
import JSZip from 'jszip';
import {setCanvasSize, drawThumbnail, gn} from '../utils/lib';
@@ -90,10 +90,10 @@ export default class IO {
IO.requestFromServer(md5, gotit); // get url contents
return;
}
- if ((IO.getExtension(md5) == 'png') && iOS.path) {
- fcn(iOS.path + md5); // only if it is not in debug mode
+ if ((IO.getExtension(md5) == 'png') && OS.path) {
+ fcn(OS.path + md5); // only if it is not in debug mode
} else {
- iOS.getmedia(md5, nextStep);
+ OS.getmedia(md5, nextStep);
} // get url contents
function gotit (str) {
@@ -109,8 +109,8 @@ export default class IO {
function nextStep (dataurl) { // iOS 7 requires to read the internal base64 images before returning contents
var str = atob(dataurl);
- if ((str.indexOf('xlink:href') < 0) && iOS.path) {
- fcn(iOS.path + md5); // does not have embedded images
+ if ((str.indexOf('xlink:href') < 0) && OS.path) {
+ fcn(OS.path + md5); // does not have embedded images
} else {
var base64 = IO.getImageDataURL(md5, dataurl);
IO.getImagesInSVG(str, function () {
@@ -210,11 +210,11 @@ export default class IO {
var json = {};
json.stmt = 'select * from ' + db + ' where id = ?';
json.values = [md5];
- iOS.query(json, fcn);
+ OS.query(json, fcn);
}
static setMedia (data, type, fcn) {
- iOS.setmedia(btoa(data), type, fcn);
+ OS.setmedia(btoa(data), type, fcn);
}
static query (type, obj, fcn) {
@@ -222,14 +222,14 @@ export default class IO {
json.stmt = 'select ' + obj.items + ' from ' + type +
' where ' + obj.cond + (obj.order ? ' order by ' + obj.order : '');
json.values = obj.values;
- iOS.query(json, fcn);
+ OS.query(json, fcn);
}
static deleteobject (type, id, fcn) {
var json = {};
json.stmt = 'delete from ' + type + ' where id = ?';
json.values = [id];
- iOS.stmt(json, fcn);
+ OS.stmt(json, fcn);
}
////////////////////////
@@ -258,7 +258,7 @@ export default class IO {
addValue('thumbnail', JSON.stringify(obj.thumbnail));
}
json.stmt = 'insert into ' + database + ' (' + keylist.toString() + ') values (' + values + ')';
- iOS.stmt(json, fcn);
+ OS.stmt(json, fcn);
function addValue (key, str) {
keylist.push(key);
values += ',?';
@@ -272,7 +272,7 @@ export default class IO {
json.values = [obj.version, obj.deleted, obj.name, JSON.stringify(obj.json),
JSON.stringify(obj.thumbnail), (new Date()).getTime().toString()];
json.stmt = 'update ' + database + ' set ' + keylist.toString() + ' where id = ' + obj.id;
- iOS.stmt(json, fcn);
+ OS.stmt(json, fcn);
}
// Since saveProject is changing the modified time of the project,
@@ -282,7 +282,7 @@ export default class IO {
var keylist = ['isgift = ?'];
json.values = [obj.isgift];
json.stmt = 'update ' + database + ' set ' + keylist.toString() + ' where id = ' + obj.id;
- iOS.stmt(json, fcn);
+ OS.stmt(json, fcn);
}
static getExtension (str) {
@@ -407,7 +407,7 @@ export default class IO {
});
} else {
// User file
- iOS.getmedia(md5, addB64ToZip);
+ OS.getmedia(md5, addB64ToZip);
}
};
@@ -496,7 +496,7 @@ export default class IO {
json.cond = 'deleted = ? AND gallery IS NULL';
json.items = ['name'];
json.values = ['NO'];
- IO.query(iOS.database, json, function (existingProjects) {
+ IO.query(OS.database, json, function (existingProjects) {
var newNumber = null;
existingProjects = JSON.parse(existingProjects);
@@ -613,14 +613,14 @@ export default class IO {
if (subFolder == 'thumbnails' || subFolder == 'sounds') {
// Save these immediately to the filesystem - no additional processing necessary
- iOS.setmedianame(b2data, name, ext, function () {
+ OS.setmedianame(b2data, name, ext, function () {
saveActual++;
});
} else if (subFolder == 'characters') {
// This code is messy - needs a refactor sometime for all the database calls/duplication for bkgs...
// Save the character, generate its thumbnail, and add entry to the database
- iOS.setmedianame(b2data, name, ext, function () { // Saves the SVG
+ OS.setmedianame(b2data, name, ext, function () { // Saves the SVG
// Parse SVG to determine width/height
var svgParser = new DOMParser().parseFromString(data, 'text/xml');
var width = svgParser.getElementsByTagName('svg')[0].width.baseVal.value;
@@ -636,7 +636,7 @@ export default class IO {
var charName = characterNames[fullName];
- iOS.setmedia(thumbnailPngBase64, 'png', function (thumbnailMD5) {
+ OS.setmedia(thumbnailPngBase64, 'png', function (thumbnailMD5) {
// Sprite thumbnail is saved - save character to the DB
// First ensure that this character doesn't already exist in the exact form
@@ -660,7 +660,7 @@ export default class IO {
json.stmt = 'insert into usershapes ('
+ keylist.toString() + ') values (' + values + ')';
- iOS.stmt(json, function () {
+ OS.stmt(json, function () {
saveActual++;
});
} else {
@@ -672,13 +672,13 @@ export default class IO {
});
} else if (subFolder == 'backgrounds') {
// Same idea as characters, but the dimensions are fixed
- iOS.setmedianame(b2data, name, ext, function () {
+ OS.setmedianame(b2data, name, ext, function () {
IO.getImagesInSVG(data, gotSVGImages);
function gotSVGImages () {
var thumbnailDataURL = IO.getThumbnail(data, 480, 360, 120, 90);
var thumbnailPngBase64 = thumbnailDataURL.split(',')[1];
- iOS.setmedia(thumbnailPngBase64, 'png', function (thumbnailMD5) {
+ OS.setmedia(thumbnailPngBase64, 'png', function (thumbnailMD5) {
// First ensure that this bg doesn't already exist in the exact form
var json = {};
@@ -696,7 +696,7 @@ export default class IO {
json.values = [fullName, thumbnailMD5, 'iOSv01', '480', '360', 'svg'];
json.stmt = 'insert into userbkgs (' + keylist.toString() +
') values (' + values + ')';
- iOS.stmt(json, function () {
+ OS.stmt(json, function () {
saveActual++;
});
} else {
diff --git a/src/iPad/MediaLib.js b/src/tablet/MediaLib.js
similarity index 100%
rename from src/iPad/MediaLib.js
rename to src/tablet/MediaLib.js
diff --git a/src/tablet/OS.js b/src/tablet/OS.js
new file mode 100644
index 0000000..794b7ef
--- /dev/null
+++ b/src/tablet/OS.js
@@ -0,0 +1,269 @@
+import {isiOS, isAndroid, gn} from '../utils/lib';
+import IO from './IO';
+import iOS from './iOS';
+import Android from './Android';
+import Lobby from '../lobby/Lobby';
+import Alert from '../editor/ui/Alert';
+import ScratchAudio from '../utils/ScratchAudio';
+
+//////////////////////////////////////////////////
+// Tablet interface functions
+//////////////////////////////////////////////////
+
+let path;
+let camera;
+let database = 'projects';
+let tabletInterface = null;
+
+export default class OS {
+ // Getters/setters for properties used in other classes
+ static get path () {
+ return path;
+ }
+
+ static set path (newPath) {
+ path = newPath;
+ }
+
+ static get camera () {
+ return camera;
+ }
+
+ static get database () {
+ return database;
+ }
+
+ // Wait for the tablet interface to be injected into the webview
+ static waitForInterface (fcn) {
+ // Already loaded the interface
+ if (tabletInterface != null) {
+ fcn();
+ return;
+ }
+ if ((isAndroid && typeof AndroidInterface === 'undefined') || (isiOS && typeof (window.tablet) !== 'object')) {
+ // interface not loaded - come back in 100ms
+ setTimeout(function () {
+ OS.waitForInterface(fcn);
+ }, 100);
+ }
+
+ tabletInterface = isiOS ? iOS : Android;
+ if (fcn) {
+ fcn();
+ }
+ return;
+ }
+
+ // Database functions
+ static stmt (json, fcn) {
+ tabletInterface.stmt(json, fcn);
+ }
+
+ static query (json, fcn) {
+ tabletInterface.query(json, fcn);
+ }
+
+ // DB helper - shared by both
+ static setfield (db, id, fieldname, val, fcn) {
+ var json = {};
+ var keylist = [fieldname + ' = ?', 'mtime = ?'];
+ json.values = [val, (new Date()).getTime().toString()];
+ json.stmt = 'update ' + db + ' set ' + keylist.toString() + ' where id = ' + id;
+ OS.stmt(json, fcn);
+ }
+
+ // IO functions
+
+ static cleanassets (ft, fcn) {
+ tabletInterface.cleanassets(ft, fcn);
+ }
+
+ static getsettings (fcn) {
+ tabletInterface.getsettings(fcn);
+ }
+
+ // note the interfaces (iOS and Android) are responsible for deciding how
+ // to manage getting media (e.g. whether it needs to be done in chunks etc)
+ static getmedia (file, fcn) {
+ tabletInterface.getmedia(file, fcn);
+ }
+
+ static setmedia (str, ext, fcn) {
+ tabletInterface.setmedia(str, ext, fcn);
+ }
+
+ static setmedianame (str, name, ext, fcn) {
+ tabletInterface.setmedianame(str, name, ext, fcn);
+ }
+
+ static getmd5 (str, fcn) {
+ tabletInterface.getmd5(str, fcn);
+ }
+
+ static remove (str, fcn) {
+ tabletInterface.remove(str, fcn);
+ }
+
+ static getfile (str, fcn) {
+ tabletInterface.getfile(str, fcn);
+ }
+
+ static setfile (name, str, fcn) {
+ tabletInterface.setfile(name, str, fcn);
+ }
+
+ // Sound functions
+
+ static registerSound (dir, name, fcn) {
+ tabletInterface.registerSound(dir, name, fcn);
+ }
+
+ static playSound (name, fcn) {
+ tabletInterface.playSound(name, fcn);
+ }
+
+ static stopSound (name, fcn) {
+ tabletInterface.stopSound(name, fcn);
+ }
+
+ // Web Wiew delegate call backs
+
+ static soundDone (name) {
+ ScratchAudio.soundDone(name);
+ }
+
+ static sndrecord (fcn) {
+ tabletInterface.sndrecord(fcn);
+ }
+
+ static recordstop (fcn) {
+ tabletInterface.recordstop(fcn);
+ }
+
+ static volume (fcn) {
+ tabletInterface.volume(fcn);
+ }
+
+ static startplay (fcn) {
+ tabletInterface.startplay(fcn);
+ }
+
+ static stopplay (fcn) {
+ tabletInterface.stopplay(fcn);
+ }
+
+ static recorddisappear (b, fcn) {
+ tabletInterface.recorddisappear(b, fcn);
+ }
+
+ // Record state
+ static askpermission () {
+ if (isiOS) {
+ iOS.askpermission();
+ }
+ }
+
+ // camera functions
+
+ static hascamera () {
+ camera = tabletInterface.hascamera();
+ }
+
+ static startfeed (data, fcn) {
+ tabletInterface.startfeed(data, fcn);
+ }
+
+ static stopfeed (fcn) {
+ tabletInterface.stopfeed(fcn);
+ }
+
+ static choosecamera (mode, fcn) {
+ tabletInterface.choosecamera(mode, fcn);
+ }
+
+ static captureimage (fcn) {
+ tabletInterface.captureimage(fcn);
+ }
+
+ static hidesplash (fcn) {
+ if (isiOS) {
+ iOS.hidesplash();
+ }
+ if (fcn) {
+ fcn();
+ }
+ }
+
+ static trace (str) {
+ console.log(str); // eslint-disable-line no-console
+ }
+
+ static parse (str) {
+ console.log(JSON.parse(str)); // eslint-disable-line no-console
+ }
+
+ static tracemedia (str) {
+ console.log(atob(str)); // eslint-disable-line no-console
+ }
+
+ ignore () {
+ }
+
+ ///////////////
+ // Sharing
+ ///////////////
+
+
+ // Called on the JS side to trigger native UI for project sharing.
+ // fileName: name for the file to share
+ // emailSubject: subject text to use for an email
+ // emailBody: body HTML to use for an email
+ // shareType: 0 for Email; 1 for Airdrop
+ // b64data: base-64 encoded .SJR file to share
+
+ static sendSjrToShareDialog (fileName, emailSubject, emailBody, shareType, b64data) {
+ tabletInterface.sendSjrToShareDialog(fileName, emailSubject, emailBody, shareType, b64data);
+ }
+
+ // Called on the Objective-C side. The argument is a base64-encoded .SJR file,
+ // to be unzipped, processed, and stored.
+ static loadProjectFromSjr (b64data) {
+ try {
+ IO.loadProjectFromSjr(b64data);
+ } catch (err) {
+ var errorMessage = 'Couldn\'t load share -- project data corrupted. ' + err.message;
+ Alert.open(gn('frame'), gn('frame'), errorMessage, '#ff0000');
+ console.log(err); // eslint-disable-line no-console
+ return 0;
+ }
+ return 1;
+ }
+
+ // Name of the device/iPad to display on the sharing dialog page
+ // fcn is called with the device name as an arg
+ static deviceName (fcn) {
+ tabletInterface.deviceName(fcn);
+ }
+
+ static analyticsEvent (category, action, label) {
+ tabletInterface.analyticsEvent(category, action, label);
+ }
+
+ static setAnalyticsPlacePref (preferredPlace) {
+ tabletInterface.setAnalyticsPlacePref(preferredPlace);
+ }
+
+ // Web Wiew delegate call backs
+
+ static pageError (desc) {
+ console.log('XCODE ERROR:', desc); // eslint-disable-line no-console
+ if (window.location.href.indexOf('home.html') > -1) {
+ if (Lobby.errorTimer) {
+ Lobby.errorLoading(desc);
+ }
+ }
+ }
+}
+
+// Expose OS methods for ScratchJr tablet sharing callbacks
+window.OS = OS;
diff --git a/src/tablet/iOS.js b/src/tablet/iOS.js
new file mode 100644
index 0000000..747751e
--- /dev/null
+++ b/src/tablet/iOS.js
@@ -0,0 +1,284 @@
+//////////////////////////////////////////////////
+// iOS interface functions
+// window.tablet is the class where native functions are injected for calling in
+// javascript. It will be initialized prior to calling any functions in this class
+//////////////////////////////////////////////////
+
+let mediacounter = 0;
+
+export default class iOS {
+
+ // Database functions
+ static stmt (json, fcn) {
+ var result = window.tablet.database_stmt(JSON.stringify(json));
+ if (typeof (fcn) !== 'undefined') {
+ fcn(result);
+ }
+ }
+
+ static query (json, fcn) {
+ var result = window.tablet.database_query(JSON.stringify(json));
+ if (typeof (fcn) !== 'undefined') {
+ fcn(result);
+ }
+ }
+
+ // IO functions
+
+ static cleanassets (ft, fcn) {
+ window.tablet.io_cleanassets(ft); fcn();
+ }
+
+ static getsettings (fcn) {
+ var result = window.tablet.io_getsettings();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static getmedia (file, fcn) {
+ mediacounter++;
+ var nextStep = function (file, key, whenDone) {
+ var result = window.tablet.io_getmedialen(file, key);
+ iOS.processdata(key, 0, result, '', whenDone);
+ };
+ nextStep(file, mediacounter, fcn);
+ }
+
+ static getmediadata (key, offset, len, fcn) {
+ var result = window.tablet.io_getmediadata(key, offset, len);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static processdata (key, off, len, oldstr, fcn) {
+ if (len == 0) {
+ iOS.getmediadone(key);
+ fcn(oldstr);
+ return;
+ }
+ var newlen = (len < 100000) ? len : 100000;
+ iOS.getmediadata(key, off, newlen, function (str) {
+ iOS.processdata(key, off + newlen, len - newlen, oldstr + str, fcn);
+ });
+ }
+
+ static getmediadone (file, fcn) {
+ var result = window.tablet.io_getmediadone(file);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static setmedia (str, ext, fcn) {
+ var result = window.tablet.io_setmedia(str, ext);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static setmedianame (str, name, ext, fcn) {
+ var result = window.tablet.io_setmedianame(str, name, ext);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static getmd5 (str, fcn) {
+ var result = window.tablet.io_getmd5(str);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static remove (str, fcn) {
+ var result = window.tablet.io_remove(str);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static getfile (str, fcn) {
+ var result = window.tablet.io_getfile(str);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static setfile (name, str, fcn) {
+ var result = window.tablet.io_setfile(name, btoa(str));
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ // Sound functions
+
+ static registerSound (dir, name, fcn) {
+ var result = window.tablet.io_registersound(dir, name);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static playSound (name, fcn) {
+ var result = window.tablet.io_playsound(name);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static stopSound (name, fcn) {
+ var result = window.tablet.io_stopsound(name);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ // Web Wiew delegate call backs
+
+ static sndrecord (fcn) {
+ var result = window.tablet.recordsound_recordstart();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static recordstop (fcn) {
+ var result = window.tablet.recordsound_recordstop();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static volume (fcn) {
+ var result = window.tablet.recordsound_volume();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static startplay (fcn) {
+ var result = window.tablet.recordsound_startplay();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static stopplay (fcn) {
+ var result = window.tablet.recordsound_stopplay();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static recorddisappear (b, fcn) {
+ var result = window.tablet.recordsound_recordclose(b);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ // Record state
+ static askpermission () {
+ window.tablet.askForPermission();
+ }
+
+ // camera functions
+
+ static hascamera () {
+ return window.tablet.scratchjr_cameracheck();
+ }
+
+ static startfeed (data, fcn) {
+ var str = JSON.stringify(data);
+ var result = window.tablet.scratchjr_startfeed(str);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static stopfeed (fcn) {
+ var result = window.tablet.scratchjr_stopfeed();
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static choosecamera (mode, fcn) {
+ var result = window.tablet.scratchjr_choosecamera(mode);
+ if (fcn) {
+ fcn(result);
+ }
+ }
+
+ static captureimage (fcn) {
+ window.tablet.scratchjr_captureimage(fcn);
+ }
+
+ static hidesplash (fcn) {
+ window.tablet.hideSplash();
+ if (fcn) {
+ fcn();
+ }
+ }
+
+ ///////////////
+ // Sharing
+ ///////////////
+
+
+ // Called on the JS side to trigger native UI for project sharing.
+ // fileName: name for the file to share
+ // emailSubject: subject text to use for an email
+ // emailBody: body HTML to use for an email
+ // shareType: 0 for Email; 1 for Airdrop
+ // b64data: base-64 encoded .SJR file to share
+
+ static sendSjrToShareDialog (fileName, emailSubject, emailBody, shareType, b64data) {
+ window.tablet.sendSjrUsingShareDialog(fileName, emailSubject, emailBody, shareType, b64data);
+ }
+
+ // // Called on the Objective-C side. The argument is a base64-encoded .SJR file,
+ // // to be unzipped, processed, and stored.
+ // static loadProjectFromSjr (b64data) {
+ // try {
+ // IO.loadProjectFromSjr(b64data);
+ // } catch (err) {
+ // var errorMessage = 'Couldn\'t load share -- project data corrupted. ' + err.message;
+ // Alert.open(gn('frame'), gn('frame'), errorMessage, '#ff0000');
+ // console.log(err); // eslint-disable-line no-console
+ // return 0;
+ // }
+ // return 1;
+ // }
+
+ // Name of the device/iPad to display on the sharing dialog page
+ // fcn is called with the device name as an arg
+ static deviceName (fcn) {
+ fcn(window.tablet.deviceName());
+ }
+
+ static analyticsEvent (category, action, label) {
+ window.tablet.analyticsEvent(category, action, label);
+ }
+
+ static setAnalyticsPlacePref (preferredPlace) {
+ window.tablet.setAnalyticsPlacePref(preferredPlace);
+ }
+
+ // // Web Wiew delegate call backs
+ //
+ // static pageError (desc) {
+ // console.log('XCODE ERROR:', desc); // eslint-disable-line no-console
+ // if (window.location.href.indexOf('home.html') > -1) {
+ // if (Lobby.errorTimer) {
+ // Lobby.errorLoading(desc);
+ // }
+ // }
+ // }
+}
+
+// Expose iOS methods for ScratchJr tablet sharing callbacks
+// window.iOS = iOS;
diff --git a/src/utils/Localization.js b/src/utils/Localization.js
index c009a22..a11b00c 100644
--- a/src/utils/Localization.js
+++ b/src/utils/Localization.js
@@ -1,6 +1,6 @@
import Cookie from './Cookie';
import Intl from 'intl';
-import IO from '../iPad/IO';
+import IO from '../tablet/IO';
if (!window.Intl) {
window.Intl = Intl;
diff --git a/src/utils/ScratchAudio.js b/src/utils/ScratchAudio.js
index 7add306..1330654 100755
--- a/src/utils/ScratchAudio.js
+++ b/src/utils/ScratchAudio.js
@@ -1,6 +1,6 @@
import {isAndroid} from './lib';
import Sound from './Sound';
-import iOS from '../iPad/iOS';
+import OS from '../tablet/OS';
////////////////////////////////////////////////////
/// Sound Playing
@@ -64,7 +64,7 @@ export default class ScratchAudio {
fcn(name);
}
};
- iOS.registerSound(url, snd, whenDone);
+ OS.registerSound(url, snd, whenDone);
} else {
// In Android, this is handled outside of JavaScript, so just place a stub here.
dict[snd] = new Sound(url + snd);
diff --git a/src/utils/Sound.js b/src/utils/Sound.js
index 1d7aba9..66ede8f 100644
--- a/src/utils/Sound.js
+++ b/src/utils/Sound.js
@@ -1,5 +1,5 @@
import {isAndroid} from './lib';
-import iOS from '../iPad/iOS';
+import OS from '../tablet/OS';
export default class Sound {
constructor (name, time) {
@@ -23,7 +23,7 @@ export default class Sound {
if (this.playing) {
this.stop();
}
- iOS.playSound(this.name);
+ OS.playSound(this.name);
this.playing = true;
}
}
@@ -51,7 +51,7 @@ export default class Sound {
}
this.soundPlayId = null;
} else {
- iOS.stopSound(this.name);
+ OS.stopSound(this.name);
this.playing = false;
}
}
diff --git a/src/utils/lib.js b/src/utils/lib.js
index 99d5673..a2664a2 100755
--- a/src/utils/lib.js
+++ b/src/utils/lib.js
@@ -8,8 +8,21 @@ export const WINDOW_INNER_WIDTH = window.innerWidth;
export const scaleMultiplier = WINDOW_INNER_HEIGHT / 768.0;
export const fullscreenScaleMultiplier = 136;
-export const isiOS = (typeof AndroidInterface == 'undefined');
-export const isAndroid = (typeof AndroidInterface != 'undefined');
+export function detectOS () {
+ var userAgent = window.navigator.userAgent.toLowerCase();
+ const ios = /iphone|ipod|ipad/.test( userAgent );
+ // safari = /safari/.test( userAgent ), // currently do not need to detect browser vs webview
+ // android = /android/.text.(userAgent);
+
+ if( ios ) {
+ return 'iOS';
+ } else {
+ // for now assume Android, this could be further refined to detect Chromium etc.
+ return 'android';
+ }
+}
+export const isiOS = (detectOS() == 'iOS');
+export const isAndroid = (detectOS() == 'android');
export function libInit () {
frame = document.getElementById('frame');