11package com .samsung .microbit .ui .activity ;
22
33import android .Manifest ;
4+ import android .annotation .SuppressLint ;
45import android .app .Activity ;
56import android .app .AlertDialog ;
67import android .bluetooth .BluetoothAdapter ;
2122import android .provider .DocumentsContract ;
2223import android .util .Log ;
2324import android .view .Menu ;
25+ import android .view .MenuItem ;
2426import android .view .View ;
2527import android .view .Window ;
28+ import android .webkit .ValueCallback ;
2629import android .webkit .WebChromeClient ;
2730import android .widget .LinearLayout ;
2831import android .widget .ListView ;
3134
3235import androidx .annotation .NonNull ;
3336import androidx .annotation .RequiresApi ;
37+ import androidx .appcompat .widget .PopupMenu ;
3438import androidx .core .app .ActivityCompat ;
3539import androidx .core .content .ContextCompat ;
3640import androidx .core .content .PermissionChecker ;
6266import com .samsung .microbit .utils .Utils ;
6367import com .samsung .microbit .utils .irmHexUtils ;
6468
69+ import java .io .BufferedReader ;
6570import java .io .ByteArrayOutputStream ;
6671import java .io .File ;
6772import java .io .FileInputStream ;
7075import java .io .FilenameFilter ;
7176import java .io .IOException ;
7277import java .io .InputStream ;
78+ import java .io .InputStreamReader ;
7379import java .io .OutputStream ;
7480import java .net .URLDecoder ;
7581import java .nio .ByteBuffer ;
99105import static com .samsung .microbit .ui .activity .PopUpActivity .INTENT_GIFF_ANIMATION_CODE ;
100106import static com .samsung .microbit .utils .FileUtils .getFileSize ;
101107
108+ import org .microbit .android .partialflashing .HexUtils ;
109+
102110// import com.samsung.microbit.core.GoogleAnalyticsManager;
103111
104112/**
@@ -768,7 +776,7 @@ private void setConnectedDeviceText() {
768776 || mActivityState == FlashActivityState .FLASH_STATE_WAIT_DEVICE_REBOOT
769777 || mActivityState == FlashActivityState .FLASH_STATE_INIT_DEVICE
770778 || mActivityState == FlashActivityState .FLASH_STATE_PROGRESS
771- ) {
779+ ) {
772780 // connectedIndicatorIcon.setImageResource(R.drawable.device_status_connected);
773781 connectedIndicatorText .setText (getString (R .string .connected_to ));
774782
@@ -903,8 +911,19 @@ private void setupListAdapter() {
903911
904912 @ Override
905913 protected void onActivityResult (int requestCode , int resultCode , Intent data ) {
914+ switch ( requestCode ) {
915+ case REQUEST_CODE_IMPORT :
916+ onActivityResultScriptsImport ( requestCode , resultCode , data );
917+ super .onActivityResult (requestCode , resultCode , data );
918+ return ;
919+ case REQUEST_CODE_EXPORT :
920+ onActivityResultScriptsExport ( requestCode , resultCode , data );
921+ super .onActivityResult (requestCode , resultCode , data );
922+ return ;
923+ }
924+
906925 boolean flash = mActivityState == FlashActivityState .STATE_ENABLE_BT_INTERNAL_FLASH_REQUEST ||
907- mActivityState == FlashActivityState .STATE_ENABLE_BT_EXTERNAL_FLASH_REQUEST ;
926+ mActivityState == FlashActivityState .STATE_ENABLE_BT_EXTERNAL_FLASH_REQUEST ;
908927 boolean connect = mActivityState == FlashActivityState .STATE_ENABLE_BT_FOR_CONNECT ;
909928
910929 if (requestCode == RequestCodes .REQUEST_ENABLE_BT ) {
@@ -982,6 +1001,7 @@ private void proceedAfterBlePermissionGrantedAndBleEnabled() {
9821001 /**
9831002 * Starts activity to enable bluetooth.
9841003 */
1004+ @ SuppressLint ("MissingPermission" )
9851005 private void enableBluetooth () {
9861006 Intent enableBtIntent = new Intent (BluetoothAdapter .ACTION_REQUEST_ENABLE );
9871007 startActivityForResult (enableBtIntent , RequestCodes .REQUEST_ENABLE_BT );
@@ -991,7 +1011,7 @@ private boolean havePermission(String permission) {
9911011 return ContextCompat .checkSelfPermission ( this , permission ) == PermissionChecker .PERMISSION_GRANTED ;
9921012 }
9931013
994- private boolean havePermissionsFlashing () {
1014+ private boolean havePermissionsFlashing () {
9951015 boolean yes = true ;
9961016 if ( Build .VERSION .SDK_INT >= Build .VERSION_CODES .S ) {
9971017 if ( !havePermission ( Manifest .permission .BLUETOOTH_CONNECT ))
@@ -1103,15 +1123,8 @@ public void sendProject(final Project project) {
11031123 @ Override
11041124 public void onClick (final View v ) {
11051125 switch (v .getId ()) {
1106- case R .id .createProject : {
1107- Intent launchMakeCodeIntent = new Intent (this , MakeCodeWebView .class );
1108- startActivity (launchMakeCodeIntent );
1109- /*
1110- Intent intent = new Intent(Intent.ACTION_VIEW);
1111- intent.setData(Uri.parse(getString(R.string.my_scripts_url)));
1112- startActivity(intent);
1113- */
1114- }
1126+ case R .id .createProject :
1127+ scriptsPopup ();
11151128 break ;
11161129
11171130 case R .id .backBtn :
@@ -1781,7 +1794,6 @@ private String[] universalHexToDFU(String inputPath, int hardwareType) {
17811794// return new String[]{"-1", "-1"};
17821795// }
17831796
1784-
17851797 private void pfRegister () {
17861798 if (pfRegistered ) {
17871799 return ;
@@ -1922,7 +1934,7 @@ public void onClick(View v) {
19221934 },//override click listener for ok button
19231935 null );//pass null to use default listener
19241936 } else if (intent .getAction ().equals (PartialFlashingService .BROADCAST_PF_ATTEMPT_DFU )) {
1925- Log .v (TAG , "Use Nordic DFU" );
1937+ Log .v (TAG , "Use Nordic DFU" );
19261938 startDFUFlash ();
19271939 } else if (intent .getAction ().equals (PartialFlashingService .BROADCAST_PF_FAILED )) {
19281940
@@ -2294,4 +2306,246 @@ public boolean onCreateOptionsMenu(Menu menu) {
22942306 return true ;
22952307 }
22962308
2309+
2310+
2311+ private void scriptsPopup () {
2312+ PopupMenu popupMenu = new PopupMenu ( this , findViewById (R .id .createProject ));
2313+ int itemID = Menu .FIRST ;
2314+ popupMenu .getMenu ().add ( 0 , itemID , 0 , "Create Code" );
2315+ itemID ++;
2316+ popupMenu .getMenu ().add ( 0 , itemID , 1 , "Import" );
2317+ itemID ++;
2318+ popupMenu .getMenu ().add ( 0 , itemID , 2 , "Export" );
2319+ itemID ++;
2320+
2321+ popupMenu .setOnMenuItemClickListener ( new PopupMenu .OnMenuItemClickListener () {
2322+ @ Override
2323+ public boolean onMenuItemClick (MenuItem item ) {
2324+ switch ( item .getItemId () - Menu .FIRST ) {
2325+ case 0 : scriptsCreateCode (); break ;
2326+ case 1 : scriptsImport (); break ;
2327+ case 2 : scriptsExport (); break ;
2328+ }
2329+ return false ;
2330+ }
2331+ });
2332+ popupMenu .show ();
2333+ }
2334+
2335+ private void scriptsCreateCode () {
2336+ Intent launchMakeCodeIntent = new Intent (this , MakeCodeWebView .class );
2337+ startActivity (launchMakeCodeIntent );
2338+ }
2339+
2340+ private static final int REQUEST_CODE_EXPORT = 1 ;
2341+ private static final int REQUEST_CODE_IMPORT = 2 ;
2342+
2343+
2344+ private void scriptsImport () {
2345+ String messageTitle = "Import" ;
2346+ Intent intent = new Intent (Intent .ACTION_OPEN_DOCUMENT );
2347+ intent .addCategory (Intent .CATEGORY_OPENABLE );
2348+ intent .setFlags (Intent .FLAG_GRANT_READ_URI_PERMISSION );
2349+ intent .setType ("application/octet-stream" );
2350+ startActivityForResult ( Intent .createChooser (intent , messageTitle ), REQUEST_CODE_IMPORT );
2351+ }
2352+
2353+ protected void onActivityResultScriptsImport (int requestCode , int resultCode , Intent data ) {
2354+ if ( resultCode != RESULT_OK ) {
2355+ return ;
2356+ }
2357+ Toast .makeText (this , "Importing project" , Toast .LENGTH_LONG ).show ();
2358+ new Thread ( new Runnable () {
2359+ @ Override
2360+ public void run () {
2361+ int error = scriptsImportOpen ( data .getData ());
2362+ runOnUiThread (new Runnable () {
2363+ @ Override
2364+ public void run () {
2365+ switch (error ) {
2366+ case 0 :
2367+ if ( minimumPermissionsGranted ) {
2368+ updateProjectsListSortOrder (true );
2369+ }
2370+ break ;
2371+ case 1 :
2372+ Toast .makeText ( ProjectActivity .this ,
2373+ "Project import failed" , Toast .LENGTH_LONG ).show ();
2374+ break ;
2375+ case 2 :
2376+ Toast .makeText ( ProjectActivity .this ,
2377+ "A project with the same name already exists" ,
2378+ Toast .LENGTH_LONG ).show ();
2379+ break ;
2380+ }
2381+ }
2382+ });
2383+ }
2384+ }).start ();
2385+ }
2386+
2387+ private int scriptsImportOpen ( Uri uri ) {
2388+ String fileName = "microbit-import.hex" ;
2389+
2390+ String scheme = uri .getScheme ();
2391+ String mime = getContentResolver ().getType (uri );
2392+ if ( scheme .equals ("file" )) {
2393+ String encodedPath = uri .getEncodedPath ();
2394+ String path = URLDecoder .decode (encodedPath );
2395+ fileName = fileNameForFlashing ( path );
2396+ } else if ( scheme .equals ("content" )) {
2397+ Cursor cursor = null ;
2398+ cursor = getContentResolver ().query (uri , null , null , null , null );
2399+ if (cursor != null && cursor .moveToFirst ()) {
2400+ int index = cursor .getColumnIndex (DocumentsContract .Document .COLUMN_DISPLAY_NAME );
2401+ if (index >= 0 ) {
2402+ fileName = cursor .getString (index );
2403+ }
2404+ }
2405+ }
2406+
2407+ String projectPath = ProjectsHelper .projectPath (this , fileName );
2408+ if ( FileUtils .fileExists ( projectPath )) {
2409+ return 2 ;
2410+ }
2411+
2412+ boolean ok = true ;
2413+ FileInputStream fis = null ;
2414+ BufferedReader reader = null ;
2415+ try {
2416+ IOUtils .copy (getContentResolver ().openInputStream (uri ), new FileOutputStream (projectPath ));
2417+
2418+ // Check file is hex
2419+ int lineCount = 0 ;
2420+ fis = new FileInputStream ( projectPath );
2421+ reader = new BufferedReader ( new InputStreamReader ( fis ));
2422+ while ( true ) {
2423+ String line = reader .readLine ();
2424+ if ( line == null ) {
2425+ break ;
2426+ }
2427+ lineCount ++;
2428+ if ( !line .isEmpty () && !line .startsWith (":" )) {
2429+ ok = false ;
2430+ break ;
2431+ }
2432+ if ( lineCount == 0 ) {
2433+ ok = false ;
2434+ }
2435+ }
2436+ } catch (Exception e ) {
2437+ logi ( e .toString ());
2438+ ok = false ;
2439+ }
2440+
2441+ if ( reader != null ) {
2442+ try {
2443+ reader .close ();
2444+ } catch (IOException e ) {
2445+ }
2446+ }
2447+
2448+ if ( fis != null ) {
2449+ try {
2450+ fis .close ();
2451+ } catch (IOException e ) {
2452+ }
2453+ }
2454+
2455+ if ( !ok ) {
2456+ FileUtils .deleteFile ( projectPath );
2457+ }
2458+ return ok ? 0 : 1 ;
2459+ }
2460+
2461+ private void scriptsExport () {
2462+ String messageTitle = "Export" ;
2463+ String name = "microbit-projects" ;
2464+ String mimetype = "application/zip" ;
2465+ Intent intent = new Intent (Intent .ACTION_CREATE_DOCUMENT );
2466+ intent .addCategory (Intent .CATEGORY_OPENABLE );
2467+ intent .setFlags (Intent .FLAG_GRANT_WRITE_URI_PERMISSION );
2468+ intent .setType ( mimetype );
2469+ intent .putExtra (Intent .EXTRA_TITLE , name );
2470+ startActivityForResult ( Intent .createChooser (intent , messageTitle ), REQUEST_CODE_EXPORT );
2471+ }
2472+
2473+ protected void onActivityResultScriptsExport (int requestCode , int resultCode , Intent data ) {
2474+ if ( resultCode != RESULT_OK ) {
2475+ return ;
2476+ }
2477+ Toast .makeText (this , "Saving Projects ZIP file" , Toast .LENGTH_LONG ).show ();
2478+ new Thread ( new Runnable () {
2479+ @ Override
2480+ public void run () {
2481+ int error = scriptsExportSave ( data .getData ());
2482+ runOnUiThread (new Runnable () {
2483+ @ Override
2484+ public void run () {
2485+ switch ( error ) {
2486+ case 0 :
2487+ Toast .makeText ( ProjectActivity .this ,
2488+ "Saved Projects ZIP file" , Toast .LENGTH_LONG ).show ();
2489+ break ;
2490+ case 1 :
2491+ Toast .makeText ( ProjectActivity .this ,
2492+ "Projects export failed" , Toast .LENGTH_LONG ).show ();
2493+ break ;
2494+ case 2 :
2495+ Toast .makeText ( ProjectActivity .this ,
2496+ "A file with the same name already exists" ,
2497+ Toast .LENGTH_LONG ).show ();
2498+ break ;
2499+ }
2500+ }
2501+ });
2502+ }
2503+ }).start ();
2504+ }
2505+
2506+ private int scriptsExportSave ( Uri uri ) {
2507+ boolean ok = true ;
2508+
2509+ byte [] buffer = new byte [1024 ];
2510+ File [] projects = ProjectsHelper .projectFilesListHEX ( this );
2511+
2512+ OutputStream os = null ;
2513+ ZipOutputStream zipOutputStream = null ;
2514+ FileInputStream fileInputStream = null ;
2515+ try {
2516+ os = getContentResolver ().openOutputStream ( uri );
2517+ zipOutputStream = new ZipOutputStream (os );
2518+ for ( int i = 0 ; i < projects .length ; i ++) {
2519+ fileInputStream = new FileInputStream ( projects [i ]);
2520+ zipOutputStream .putNextEntry (new ZipEntry ( projects [i ].getName ()));
2521+
2522+ int length ;
2523+ while ((length = fileInputStream .read (buffer )) > 0 ) {
2524+ zipOutputStream .write (buffer , 0 , length );
2525+ }
2526+
2527+ zipOutputStream .closeEntry ();
2528+ fileInputStream .close ();
2529+ fileInputStream = null ;
2530+ }
2531+ zipOutputStream .close ();
2532+ os .close ();
2533+ } catch (Exception e ) {
2534+ ok = false ;
2535+ try {
2536+ if ( fileInputStream != null ) {
2537+ fileInputStream .close ();
2538+ }
2539+ if ( zipOutputStream != null ) {
2540+ zipOutputStream .close ();
2541+ }
2542+ if ( os != null ) {
2543+ os .close ();
2544+ }
2545+ } catch (Exception e2 ) {
2546+ e .printStackTrace ();
2547+ }
2548+ }
2549+ return ok ? 0 : 1 ;
2550+ }
22972551}
0 commit comments