66import android .net .Uri ;
77import android .os .Bundle ;
88import android .os .Environment ;
9+ import android .os .Handler ;
10+ import android .os .Looper ;
911import android .util .Base64 ;
1012import android .util .Log ;
1113import android .view .View ;
1618import android .webkit .WebSettings ;
1719import android .webkit .WebView ;
1820import android .webkit .WebViewClient ;
21+ import android .widget .Toast ;
1922
2023import com .samsung .microbit .BuildConfig ;
2124import com .samsung .microbit .R ;
25+ import com .samsung .microbit .utils .FileUtils ;
2226import com .samsung .microbit .utils .ProjectsHelper ;
2327
2428import java .io .File ;
@@ -40,10 +44,11 @@ public class MakeCodeWebView extends Activity implements View.OnClickListener {
4044 public static String makecodeUrl = "https://makecode.microbit.org/?androidapp=" + BuildConfig .VERSION_CODE ;
4145 public static Activity activityHandle = null ;
4246
43- Uri hexToFlash ;
47+ boolean projectDownload = false ;
4448
4549 private static final int REQUEST_CODE_SAVEDATA = 1 ;
4650 private static final int REQUEST_CODE_CHOOSE_FILE = 2 ;
51+ private static final int REQUEST_CODE_FLASH = 3 ;
4752 private byte [] dataToSave = null ;
4853 private ValueCallback <Uri []> onShowFileChooser_filePathCallback ;
4954
@@ -81,15 +86,9 @@ protected void onCreate(Bundle savedInstanceState) {
8186 webSettings .setBuiltInZoomControls (true );
8287 webSettings .setDisplayZoomControls (false );
8388 webSettings .setDomStorageEnabled (true );
84- webView .setWebContentsDebuggingEnabled (true );
89+ WebView .setWebContentsDebuggingEnabled (false );
8590
8691 webView .addJavascriptInterface (new JavaScriptInterface (this ), "AndroidFunction" );
87- webView .evaluateJavascript ("javascript:(function f() { document.getElementsByClassName(\" brand\" )[0].addEventListener(\" click\" , function(e) { AndroidFunction.returnToHome(); e.preventDefault(); return false; }) })()" , new ValueCallback <String >() {
88- @ Override
89- public void onReceiveValue (String s ) {
90- Log .d (TAG , s );
91- }
92- });
9392
9493 webView .setWebViewClient (new WebViewClient () {
9594 @ Override
@@ -104,6 +103,13 @@ public void onLoadResource(WebView view, String url) {
104103 super .onLoadResource (view , url );
105104 Log .v (TAG , "onLoadResource(" + url + ");" );
106105 }
106+
107+ @ Override
108+ public void onPageFinished (WebView view , String url ) {
109+ super .onPageFinished (view , url );
110+ Log .v (TAG , "onPageFinished(" + url + ");" );
111+ onPageFinishedJS ( view , url );
112+ }
107113 }); //setWebViewClient
108114
109115 webView .setWebChromeClient (new WebChromeClient () {
@@ -194,40 +200,49 @@ public void onReceiveValue(String s) {
194200 else if ( !hexName .isEmpty ()) {
195201 hexToWrite = getProjectFile (hexName );
196202
197- /*
198- // Append n to file until it doesn't exist
199- int i = 0;
200-
201- while (hexToWrite.exists()) {
202- hexName = hexName.replaceAll("-?\\d*\\.","-" + i + ".");
203- hexToWrite = getProjectFile( hexName);
204- i++;
203+ // // Replace existing file rather than creating *-n.hex
204+ // // Append n to file until it doesn't exist
205+ // int i = 0;
206+ //
207+ // while (hexToWrite.exists()) {
208+ // hexName = hexName.replaceAll("-?\\d*\\.","-" + i + ".");
209+ // hexToWrite = getProjectFile( hexName);
210+ // i++;
211+ // }
212+
213+ if ( !FileUtils .writeBytesToFile ( hexToWrite , decode )) {
214+ ProjectsHelper .importToProjectsToast (
215+ ProjectsHelper .enumImportResult .WriteFailed , MakeCodeWebView .this );
216+ return ;
205217 }
206- */
207- // Replace existing file rather than creating *-n.hex
208- if (hexToWrite .exists ()) {
209- hexToWrite .delete ();
210- }
211-
212- // Create file
213- hexToWrite .createNewFile ();
214- outputStream = new FileOutputStream (hexToWrite );
215- outputStream .write (decode );
216- outputStream .flush ();
217218
218- // Get file path
219- hexToFlash = Uri . fromFile ( hexToWrite ) ;
219+ boolean download = projectDownload ;
220+ projectDownload = false ;
220221
221- openProjectActivity ();
222+ if ( download ) {
223+ openProjectActivity ( hexToWrite );
224+ } else {
225+ Toast .makeText ( MakeCodeWebView .this ,
226+ "Saved to FLASH page" , Toast .LENGTH_LONG ).show ();
227+ }
222228 }
223- } catch (IOException e ) {
229+ } catch ( Exception e ) {
224230 e .printStackTrace ();
225231 }
226232 }
227233 }); // setDownloadListener
228234
229235 //Check parameters Before load
230236 Intent intent = getIntent ();
237+
238+ boolean importExtra = intent .getBooleanExtra ("import" , false );
239+ if ( importExtra ) {
240+ importInitialise ();
241+ } else {
242+ importHex = null ;
243+ importName = null ;
244+ }
245+
231246 webView .loadUrl (makecodeUrl );
232247 } // onCreate
233248
@@ -244,27 +259,22 @@ private void saveData( String name, String mimetype, byte[] data) {
244259 protected void onActivityResult (int requestCode , int resultCode , Intent data ) {
245260 super .onActivityResult (requestCode , resultCode , data );
246261
247- if ( requestCode == REQUEST_CODE_SAVEDATA ) {
262+ if ( requestCode == REQUEST_CODE_FLASH ) {
263+ if ( resultCode != RESULT_OK ) {
264+ return ;
265+ }
266+ } else if ( requestCode == REQUEST_CODE_SAVEDATA ) {
248267 if ( resultCode != RESULT_OK ) {
249268 dataToSave = null ;
250269 return ;
251270 }
252- OutputStream os = null ;
253- try {
254- os = getContentResolver ().openOutputStream ( data .getData ());
255- if ( dataToSave != null && dataToSave .length > 0 ) {
256- os .write (dataToSave , 0 , dataToSave .length );
271+ if ( dataToSave != null && dataToSave .length > 0 ) {
272+ Uri uri = data .getData ();
273+ if ( !FileUtils .writeBytesToUri ( uri , dataToSave , this )) {
274+ Toast .makeText (this , "Could not save file" , Toast .LENGTH_LONG ).show ();
257275 }
258- } catch (IOException e ) {
259- e .printStackTrace ();
260- } finally {
261- try {
262- dataToSave = null ;
263- if ( os != null ) {
264- os .close ();
265- }
266- } catch (IOException e ) { }
267276 }
277+ dataToSave = null ;
268278 } else if (requestCode == REQUEST_CODE_CHOOSE_FILE ) {
269279 if ( resultCode != RESULT_OK ) {
270280 onShowFileChooser_filePathCallback .onReceiveValue ( null );
@@ -295,27 +305,169 @@ public void onClick(final View v) {
295305 }
296306 }
297307
298- void openProjectActivity () {
308+ public final static String ACTION_FLASH = "com.samsung.microbit.ACTION_FLASH" ;
309+
310+ void openProjectActivity ( File hexToWrite ) {
299311 Intent i = new Intent (this , ProjectActivity .class );
300- i .setData (hexToFlash );
301- startActivity (i );
312+ i .setAction ( ACTION_FLASH );
313+ i .putExtra ("path" , hexToWrite .getAbsolutePath ());
314+ startActivityForResult ( i , REQUEST_CODE_FLASH );
315+ }
316+
317+ public static String importHex = null ;
318+ public static String importName = null ;
319+ private boolean importPosting = false ;
320+ Handler importHandler = null ;
321+
322+ private void importInitialise () {
323+ if ( importHex != null ) {
324+ importHex = importHex .replaceAll ("\r \n " , "\\ \\ n" );
325+ importHex = importHex .replaceAll ("\r " , "\\ \\ n" );
326+ importHex = importHex .replaceAll ("\n " , "\\ \\ n" );
327+
328+ //TODO - does MakeCode signal when ready?
329+ Looper looper = Looper .getMainLooper ();
330+ importHandler = new Handler (looper );
331+ importHandler .postDelayed (importCallback , 2000 );
332+ }
333+ }
334+ private final Runnable importCallback = new Runnable () {
335+ @ Override
336+ public void run () {
337+ if ( importHex != null ) {
338+ importPostMessage ();
339+ importHandler .postDelayed ( importCallback , 1000 );
340+ } else {
341+ importHandler .removeCallbacks ( importCallback );
342+ importHandler = null ;
343+ }
344+ }
345+ };
346+
347+ public void importPostMessage () {
348+ Log .d (TAG , "importPostMessage" );
349+
350+ if ( importHex == null ) {
351+ return ;
352+ }
353+ if ( importPosting ) {
354+ return ;
355+ }
356+ if ( webView == null ) {
357+ return ;
358+ }
359+ if ( importName == null || importName .isEmpty ()) {
360+ importName = "import.hex" ;
361+ }
362+
363+ importPosting = true ;
364+
365+ StringBuilder sb = new StringBuilder ();
366+ String nl = "\n " ;
367+ sb .append ( "javascript:(" );
368+ sb .append (nl ).append ("function f() {" );
369+ sb .append (nl ).append ( "var ret = 'OK'" );
370+ sb .append (nl ).append ( "try {" );
371+ sb .append (nl ).append ( "var loading = document.getElementById('loading')" );
372+ sb .append (nl ).append ( "if ( loading && loading.parentElement) {" );
373+ sb .append (nl ).append ( "ret = 'loading'" );
374+ sb .append (nl ).append ( "} else {" );
375+ sb .append (nl ).append ( "var name = '" ).append (importName ).append ("'" );
376+ sb .append (nl ).append ( "var hex = '" ).append (importHex ).append ("'" );
377+ sb .append (nl ).append ( "var msg = {" );
378+ sb .append (nl ).append ( "type: 'importfile'," );
379+ sb .append (nl ).append ( "filename: name," );
380+ sb .append (nl ).append ( "parts: [ hex ]" );
381+ sb .append (nl ).append ( "}" );
382+ sb .append (nl ).append ( "window.postMessage( msg, '*')" );
383+ sb .append (nl ).append ( "}" );
384+ sb .append (nl ).append ( "} catch( err) {" );
385+ sb .append (nl ).append ( "ret = err.message" );
386+ sb .append (nl ).append ( "}" );
387+ sb .append (nl ).append ( "return ret" );
388+ sb .append (nl ).append ("}" );
389+ sb .append (nl ).append (")()" );
390+
391+ webView .evaluateJavascript ( sb .toString (), new ValueCallback <String >() {
392+ @ Override
393+ public void onReceiveValue (String s ) {
394+ Log .i (TAG , "importPostMessage: " + s );
395+ String loading = "\" loading\" " ;
396+ String ok = "\" OK\" " ;
397+ if ( s .equals ( ok )) {
398+ importHex = null ;
399+ } else if ( !s .equals ( loading )) {
400+ }
401+ }
402+ });
403+ importPosting = false ;
302404 }
303- }
405+
406+ public void onPageFinishedJS ( WebView view , String url ) {
407+ Log .v (TAG , "addListeners(" + url + ");" );
408+
409+ StringBuilder sb = new StringBuilder ();
410+ String nl = "\n " ;
411+ sb .append ( "javascript:(" );
412+ sb .append (nl ).append ("function f() {" );
413+ sb .append (nl ).append ( "var ret = 'OK'" );
414+ sb .append (nl ).append ( "try {" );
415+ sb .append (nl ).append ( "var brands = document.getElementsByClassName(\" brand\" )" );
416+ sb .append (nl ).append ( "for (let i = 0; brands != null && i < brands.length; i++) {" );
417+ sb .append (nl ).append ( "brands[i].addEventListener(\" click\" ," );
418+ sb .append (nl ).append ( "function(e) {" );
419+ sb .append (nl ).append ( "AndroidFunction.clickBrand();" );
420+ sb .append (nl ).append ( "e.preventDefault();" );
421+ sb .append (nl ).append ( "return false;" );
422+ sb .append (nl ).append ( "})" );
423+ sb .append (nl ).append ( "}" );
424+ sb .append (nl ).append ( "var downs = document.getElementsByClassName(\" download-button\" )" );
425+ sb .append (nl ).append ( "for (let i = 0; downs != null && i < downs.length; i++) {" );
426+ sb .append (nl ).append ( "downs[i].addEventListener(\" click\" ," );
427+ sb .append (nl ).append ( "function(e) {" );
428+ sb .append (nl ).append ( "AndroidFunction.clickDownload();" );
429+ sb .append (nl ).append ( "e.preventDefault();" );
430+ sb .append (nl ).append ( "return false;" );
431+ sb .append (nl ).append ( "})" );
432+ sb .append (nl ).append ( "}" );
433+ sb .append (nl ).append ( "} catch( err) {" );
434+ sb .append (nl ).append ( "ret = err.message" );
435+ sb .append (nl ).append ( "}" );
436+ sb .append (nl ).append ( "return ret" );
437+ sb .append (nl ).append ("}" );
438+ sb .append (nl ).append (")()" );
439+
440+ webView .evaluateJavascript ( sb .toString (), new ValueCallback <String >() {
441+ @ Override
442+ public void onReceiveValue (String s ) {
443+ Log .d (TAG , s );
444+ }
445+ });
446+ }}
304447
305448/* Javascript Interface */
306449class JavaScriptInterface {
307- Context mContext ;
450+ MakeCodeWebView mContext ;
308451
309- JavaScriptInterface (Context c ) {
452+ JavaScriptInterface ( MakeCodeWebView c ) {
310453 mContext = c ;
311454 }
312455
313456 @ JavascriptInterface
314- public void returnToHome () {
457+ public void clickBrand () {
315458 try {
316459 MakeCodeWebView .activityHandle .finish ();
317460 } catch (Exception e ) {
318461 Log .v (TAG , e .toString ());
319462 }
320463 }
464+
465+ @ JavascriptInterface
466+ public void clickDownload () {
467+ try {
468+ mContext .projectDownload = true ;
469+ } catch (Exception e ) {
470+ Log .v (TAG , e .toString ());
471+ }
472+ }
321473}
0 commit comments