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:
Andrew n marshall
2018-08-07 11:37:11 -07:00
parent 0fb1281cb3
commit 5262544538
9 changed files with 472 additions and 9 deletions

View File

@@ -1,5 +1,6 @@
/build
/captures
/app/src/main/assets/blockly/msg
.settings
.project

45
demos/android/README.md Normal file
View 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.

View File

@@ -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

View File

@@ -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'

View 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. */

View 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>

View File

@@ -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
}

View File

@@ -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);

View File

@@ -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>