mirror of
https://github.com/scratchfoundation/scratchjr.git
synced 2024-11-25 00:28:20 -05:00
Merge pull request #284 from LLK/issue/274-refactor-os-interface
Split iOS and Android interfaces
This commit is contained in:
commit
d6e1a77327
37 changed files with 1129 additions and 557 deletions
116
android/ScratchJr/.idea/codeStyles/Project.xml
Normal file
116
android/ScratchJr/.idea/codeStyles/Project.xml
Normal file
|
@ -0,0 +1,116 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<codeStyleSettings language="XML">
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
<arrangement>
|
||||
<rules>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:android</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>xmlns:.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:id</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*:name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>name</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>style</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||
</rule>
|
||||
</section>
|
||||
<section>
|
||||
<rule>
|
||||
<match>
|
||||
<AND>
|
||||
<NAME>.*</NAME>
|
||||
<XML_ATTRIBUTE />
|
||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||
</AND>
|
||||
</match>
|
||||
<order>BY_NAME</order>
|
||||
</rule>
|
||||
</section>
|
||||
</rules>
|
||||
</arrangement>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
|
@ -5,7 +5,7 @@
|
|||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="10">
|
||||
<list size="12">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
|
@ -16,12 +16,14 @@
|
|||
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
|
||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
|
||||
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
|
||||
<item index="10" class="java.lang.String" itemvalue="android.annotation.Nullable" />
|
||||
<item index="11" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="9">
|
||||
<list size="11">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
|
@ -31,6 +33,8 @@
|
|||
<item index="6" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
|
||||
<item index="7" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
|
||||
<item index="8" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
|
||||
<item index="9" class="java.lang.String" itemvalue="android.annotation.NonNull" />
|
||||
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
|
|
|
@ -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() {
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 79 KiB |
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 25 KiB |
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Localization from '../../utils/Localization';
|
||||
import IO from '../../iPad/IO';
|
||||
import IO from '../../tablet/IO';
|
||||
|
||||
let loadCount = 0;
|
||||
|
||||
|
|
|
@ -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*</g, '><');
|
||||
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));
|
||||
|
|
|
@ -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*</g, '><');
|
||||
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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 () {
|
||||
|
|
|
@ -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'));
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
375
src/iPad/iOS.js
375
src/iPad/iOS.js
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
277
src/tablet/Android.js
Normal file
277
src/tablet/Android.js
Normal file
|
@ -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;
|
|
@ -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 {
|
269
src/tablet/OS.js
Normal file
269
src/tablet/OS.js
Normal file
|
@ -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;
|
284
src/tablet/iOS.js
Normal file
284
src/tablet/iOS.js
Normal file
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
Loading…
Reference in a new issue