mirror of
https://github.com/google/blockly.git
synced 2026-01-10 02:17:09 +01:00
Basic Fragment with webview, and Blockly inside.
The necessary Blockly files are copied from the parent directories via a gradle task.
This commit is contained in:
1
demos/android/.gitignore
vendored
1
demos/android/.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
/build
|
||||
/captures
|
||||
/app/src/main/assets/blockly/msg
|
||||
.settings
|
||||
.project
|
||||
|
||||
|
||||
45
demos/android/README.md
Normal file
45
demos/android/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Blockly in an Android WebView
|
||||
|
||||
This code demonstrates how to get Blockly running in an Android app by
|
||||
embedding it in a WebView.
|
||||
|
||||
### BlocklyWebViewFragment
|
||||
|
||||
Most of the work is done within the fragment class `BlocklyWebViewFragment`.
|
||||
This fragment instantiates the WebView, loads the HTML
|
||||
(`assets/blockly/webview.html`), and provides a few helper methods.
|
||||
|
||||
### Copying web assets with gradle
|
||||
|
||||
This android project copies the necessary files from the main Blockly
|
||||
repository (i.e., parent directory). In `app/build.gradle`, note the
|
||||
`copyBlocklyWebFiles` task and `preBuild.dependsOn copyBlocklyWebFiles` line.
|
||||
|
||||
In your own project, these files can be placed directly in the `assets/blockly`
|
||||
directory without the copy step. However, if you do use the copy step, make
|
||||
sure you adapt the copy paths appropriately. Additionally, you may want to
|
||||
update your `.gitignore` or similar file.
|
||||
|
||||
### Loading Block Definitions and Generator functions
|
||||
|
||||
The `webview.html` loads the block definitions and generator functions directly
|
||||
into the page, without support or coordination with the Android classes. This
|
||||
assumes the app will always utilize the same blocks. This does not mean all
|
||||
blocks are visible to the user all the time; that is controlled by the toolbox
|
||||
and workspace files. This should accommodate almost all applications.
|
||||
|
||||
This does mean loading your own block definitions and generators will involve
|
||||
editing the HTML, adding you own `<script>` tag, and possibly removing
|
||||
the `blocks_compressed.js` if you do not use any standard blocks.
|
||||
|
||||
### Connecting a Developer Console
|
||||
|
||||
While the console output of the WebView will be visible in the Android log
|
||||
(i.e., `logcat`), some times a more intrusive approach is required to isolate
|
||||
a problem. For instructions on connecting the WebView to Chrome's Developer
|
||||
Tools, see this article:
|
||||
|
||||
https://developers.google.com/web/tools/chrome-devtools/remote-debugging/
|
||||
|
||||
The WebView must be visible in the connect device or emulator before the
|
||||
WebView will included in the list of available pages to connect to.
|
||||
5
demos/android/app/.gitignore
vendored
5
demos/android/app/.gitignore
vendored
@@ -1 +1,6 @@
|
||||
/build
|
||||
|
||||
# Files copied during build:
|
||||
src/main/assets/blockly/blockly_compressed.js
|
||||
src/main/assets/blockly/blocks_compressed.js
|
||||
src/main/assets/blockly/media
|
||||
|
||||
@@ -20,6 +20,18 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
task copyBlocklyWebFiles(type: Copy) {
|
||||
from('../../..') {
|
||||
include 'blockly_compressed.js', 'blocks_compressed.js', 'msg/js/**', 'media/**'
|
||||
exclude 'media/test_*'
|
||||
}
|
||||
into project(':app').file('./src/main/assets/blockly')
|
||||
}
|
||||
|
||||
project.afterEvaluate {
|
||||
preBuild.dependsOn copyBlocklyWebFiles
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation 'com.android.support:appcompat-v7:27.1.1'
|
||||
|
||||
333
demos/android/app/src/main/assets/blockly/toolbox_standard.js
Normal file
333
demos/android/app/src/main/assets/blockly/toolbox_standard.js
Normal file
@@ -0,0 +1,333 @@
|
||||
|
||||
var BLOCKLY_TOOLBOX_XML = BLOCKLY_TOOLBOX_XML || Object.create(null);
|
||||
|
||||
/* BEGINNING BLOCKLY_TOOLBOX_XML ASSIGNMENT. DO NOT EDIT. USE BLOCKLY DEVTOOLS. */
|
||||
BLOCKLY_TOOLBOX_XML['standard'] =
|
||||
// From XML string/file, replace ^\s?(\s*)?(<.*>)$ with \+$1'$2'
|
||||
// Tweak first and last line.
|
||||
'<xml>'
|
||||
+ '<category name="Logic" colour="%{BKY_LOGIC_HUE}">'
|
||||
+ '<block type="controls_if"></block>'
|
||||
+ '<block type="logic_compare"></block>'
|
||||
+ '<block type="logic_operation"></block>'
|
||||
+ '<block type="logic_negate"></block>'
|
||||
+ '<block type="logic_boolean"></block>'
|
||||
+ '<block type="logic_null" disabled="true"></block>'
|
||||
+ '<block type="logic_ternary"></block>'
|
||||
+ '</category>'
|
||||
+ '<category name="Loops" colour="%{BKY_LOOPS_HUE}">'
|
||||
+ '<block type="controls_repeat_ext">'
|
||||
+ '<value name="TIMES">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">10</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="controls_repeat" disabled="true"></block>'
|
||||
+ '<block type="controls_whileUntil"></block>'
|
||||
+ '<block type="controls_for">'
|
||||
+ '<value name="FROM">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">1</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="TO">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">10</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="BY">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">1</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="controls_forEach"></block>'
|
||||
+ '<block type="controls_flow_statements"></block>'
|
||||
+ '</category>'
|
||||
+ '<category name="Math" colour="%{BKY_MATH_HUE}">'
|
||||
+ '<block type="math_number" gap="32">'
|
||||
+ '<field name="NUM">123</field>'
|
||||
+ '</block>'
|
||||
+ '<block type="math_arithmetic">'
|
||||
+ '<value name="A">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">1</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="B">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">1</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="math_single">'
|
||||
+ '<value name="NUM">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">9</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="math_trig">'
|
||||
+ '<value name="NUM">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">45</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="math_constant"></block>'
|
||||
+ '<block type="math_number_property">'
|
||||
+ '<value name="NUMBER_TO_CHECK">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">0</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="math_round">'
|
||||
+ '<value name="NUM">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">3.1</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="math_on_list"></block>'
|
||||
+ '<block type="math_modulo">'
|
||||
+ '<value name="DIVIDEND">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">64</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="DIVISOR">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">10</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="math_constrain">'
|
||||
+ '<value name="VALUE">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">50</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="LOW">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">1</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="HIGH">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">100</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="math_random_int">'
|
||||
+ '<value name="FROM">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">1</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="TO">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">100</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="math_random_float"></block>'
|
||||
+ '</category>'
|
||||
+ '<category name="Text" colour="%{BKY_TEXTS_HUE}">'
|
||||
+ '<block type="text"></block>'
|
||||
+ '<block type="text_join"></block>'
|
||||
+ '<block type="text_append">'
|
||||
+ '<value name="TEXT">'
|
||||
+ '<shadow type="text"></shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_length">'
|
||||
+ '<value name="VALUE">'
|
||||
+ '<shadow type="text">'
|
||||
+ '<field name="TEXT">abc</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_isEmpty">'
|
||||
+ '<value name="VALUE">'
|
||||
+ '<shadow type="text">'
|
||||
+ '<field name="TEXT"></field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_indexOf">'
|
||||
+ '<value name="VALUE">'
|
||||
+ '<block type="variables_get">'
|
||||
+ '<field name="VAR">text</field>'
|
||||
+ '</block>'
|
||||
+ '</value>'
|
||||
+ '<value name="FIND">'
|
||||
+ '<shadow type="text">'
|
||||
+ '<field name="TEXT">abc</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_charAt">'
|
||||
+ '<value name="VALUE">'
|
||||
+ '<block type="variables_get">'
|
||||
+ '<field name="VAR">text</field>'
|
||||
+ '</block>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_getSubstring">'
|
||||
+ '<value name="STRING">'
|
||||
+ '<block type="variables_get">'
|
||||
+ '<field name="VAR">text</field>'
|
||||
+ '</block>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_changeCase">'
|
||||
+ '<value name="TEXT">'
|
||||
+ '<shadow type="text">'
|
||||
+ '<field name="TEXT">abc</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_trim">'
|
||||
+ '<value name="TEXT">'
|
||||
+ '<shadow type="text">'
|
||||
+ '<field name="TEXT">abc</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_count">'
|
||||
+ '<value name="SUB">'
|
||||
+ '<shadow type="text"></shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="TEXT">'
|
||||
+ '<shadow type="text"></shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_replace">'
|
||||
+ '<value name="FROM">'
|
||||
+ '<shadow type="text"></shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="TO">'
|
||||
+ '<shadow type="text"></shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="TEXT">'
|
||||
+ '<shadow type="text"></shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_reverse">'
|
||||
+ '<value name="TEXT">'
|
||||
+ '<shadow type="text"></shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<label text="Input/Output:" web-class="ioLabel"></label>'
|
||||
+ '<block type="text_print">'
|
||||
+ '<value name="TEXT">'
|
||||
+ '<shadow type="text">'
|
||||
+ '<field name="TEXT">abc</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="text_prompt_ext">'
|
||||
+ '<value name="TEXT">'
|
||||
+ '<shadow type="text">'
|
||||
+ '<field name="TEXT">abc</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '</category>'
|
||||
+ '<category name="Lists" colour="%{BKY_LISTS_HUE}">'
|
||||
+ '<block type="lists_create_with">'
|
||||
+ '<mutation items="0"></mutation>'
|
||||
+ '</block>'
|
||||
+ '<block type="lists_create_with"></block>'
|
||||
+ '<block type="lists_repeat">'
|
||||
+ '<value name="NUM">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">5</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="lists_length"></block>'
|
||||
+ '<block type="lists_isEmpty"></block>'
|
||||
+ '<block type="lists_indexOf">'
|
||||
+ '<value name="VALUE">'
|
||||
+ '<block type="variables_get">'
|
||||
+ '<field name="VAR">list</field>'
|
||||
+ '</block>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="lists_getIndex">'
|
||||
+ '<value name="VALUE">'
|
||||
+ '<block type="variables_get">'
|
||||
+ '<field name="VAR">list</field>'
|
||||
+ '</block>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="lists_setIndex">'
|
||||
+ '<value name="LIST">'
|
||||
+ '<block type="variables_get">'
|
||||
+ '<field name="VAR">list</field>'
|
||||
+ '</block>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="lists_getSublist">'
|
||||
+ '<value name="LIST">'
|
||||
+ '<block type="variables_get">'
|
||||
+ '<field name="VAR">list</field>'
|
||||
+ '</block>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="lists_split">'
|
||||
+ '<value name="DELIM">'
|
||||
+ '<shadow type="text">'
|
||||
+ '<field name="TEXT">,</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="lists_sort"></block>'
|
||||
+ '<block type="lists_reverse"></block>'
|
||||
+ '</category>'
|
||||
+ '<category name="Colour" colour="%{BKY_COLOUR_HUE}">'
|
||||
+ '<block type="colour_picker"></block>'
|
||||
+ '<block type="colour_random"></block>'
|
||||
+ '<block type="colour_rgb">'
|
||||
+ '<value name="RED">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">100</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="GREEN">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">50</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="BLUE">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">0</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '<block type="colour_blend">'
|
||||
+ '<value name="COLOUR1">'
|
||||
+ '<shadow type="colour_picker">'
|
||||
+ '<field name="COLOUR">#ff0000</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="COLOUR2">'
|
||||
+ '<shadow type="colour_picker">'
|
||||
+ '<field name="COLOUR">#3333ff</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '<value name="RATIO">'
|
||||
+ '<shadow type="math_number">'
|
||||
+ '<field name="NUM">0.5</field>'
|
||||
+ '</shadow>'
|
||||
+ '</value>'
|
||||
+ '</block>'
|
||||
+ '</category>'
|
||||
+ '<sep></sep>'
|
||||
+ '<category name="Variables" colour="%{BKY_VARIABLES_HUE}" custom="VARIABLE"></category>'
|
||||
+ '<category name="Functions" colour="%{BKY_PROCEDURES_HUE}" custom="PROCEDURE"></category>'
|
||||
+ '</xml>';
|
||||
/* END BLOCKLY_TOOLBOX_XML ASSIGNMENT. DO NOT EDIT. */
|
||||
29
demos/android/app/src/main/assets/blockly/webview.html
Normal file
29
demos/android/app/src/main/assets/blockly/webview.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- HTML file to host Blockly in a mobile WebView. -->
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
html, body, #blocklyDiv {
|
||||
border: 0;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<script src="blockly_compressed.js"></script>
|
||||
<script src="blocks_compressed.js"></script>
|
||||
<!-- TODO: Select msg file based on locale. -->
|
||||
<script src="msg/js/en.js"></script>
|
||||
<script src="toolbox_standard.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="blocklyDiv"></div>
|
||||
<script type="text/javascript">
|
||||
var workspacePlayground = Blockly.inject('blocklyDiv', {
|
||||
media: 'media/',
|
||||
toolbox: BLOCKLY_TOOLBOX_XML['standard']
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.google.blockly.android.webview;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* This fragments contains and manages the web view that hosts Blockly.
|
||||
*/
|
||||
public class BlocklyWebViewFragment extends Fragment {
|
||||
protected @Nullable WebView mWebView = null;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
mWebView = new WebView(inflater.getContext());
|
||||
WebSettings webSettings = mWebView.getSettings();
|
||||
webSettings.setJavaScriptEnabled(true);
|
||||
mWebView.loadUrl("file:///android_asset/blockly/webview.html");
|
||||
return mWebView;
|
||||
}
|
||||
|
||||
// TODO: Method to invoke code generation
|
||||
// TODO: Method to load workspace from string (or InputStream?)
|
||||
// TODO: Method to serialize workspace to string (or OutputStream?)
|
||||
// TODO: Clear / reset workspace
|
||||
// TODO: Load toolbox
|
||||
// TODO: Listener for event JSON
|
||||
// TODO: Method to evaluate JavaScript string in the WebView
|
||||
}
|
||||
@@ -5,8 +5,11 @@ import android.os.Bundle;
|
||||
|
||||
import com.example.blocklywebview.R;
|
||||
|
||||
/**
|
||||
* The primary activity of the demo application. The activity embeds the
|
||||
* {@link com.google.blockly.android.webview.BlocklyWebViewFragment}.
|
||||
*/
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
@@ -7,13 +7,11 @@
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.google.blockly.android.webview.demo.MainActivity">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"/>
|
||||
<fragment
|
||||
android:id="@+id/blockly_webview"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:name="com.google.blockly.android.webview.BlocklyWebViewFragment"
|
||||
/>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
Reference in New Issue
Block a user