mirror of
https://github.com/scratchfoundation/scratchjr.git
synced 2024-11-28 18:15:37 -05:00
Android: import project in native
This commit is contained in:
parent
e88b25dea0
commit
d77fc04f2d
4 changed files with 229 additions and 24 deletions
|
@ -1,6 +1,8 @@
|
|||
package org.scratchjr.android;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
@ -14,6 +16,7 @@ import android.database.sqlite.SQLiteDatabase;
|
|||
import android.database.sqlite.SQLiteDatabase.CursorFactory;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
|
@ -200,6 +203,27 @@ public class DatabaseManager {
|
|||
return Long.toString(id);
|
||||
}
|
||||
|
||||
public String insert(String table, JSONObject data) throws DatabaseException {
|
||||
List<String> values = new ArrayList<>();
|
||||
List<String> keys = new ArrayList<>();
|
||||
List<String> placeholders = new ArrayList<>();
|
||||
JSONArray names = data.names();
|
||||
for (int i = 0; i < names.length(); i++) {
|
||||
String key = names.optString(i);
|
||||
keys.add(key);
|
||||
placeholders.add("?");
|
||||
values.add(data.optString(key));
|
||||
}
|
||||
String statement = String.format(
|
||||
"INSERT INTO %s (%s) VALUES (%s)",
|
||||
table,
|
||||
TextUtils.join(",", keys),
|
||||
TextUtils.join(",", placeholders)
|
||||
);
|
||||
|
||||
return stmt(statement, values.toArray(new String[0]));
|
||||
}
|
||||
|
||||
private JSONObject getRowDataAsJsonObject(Cursor cursor)
|
||||
throws SQLiteException, JSONException
|
||||
{
|
||||
|
|
|
@ -8,12 +8,16 @@ import java.io.InputStream;
|
|||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -272,4 +276,98 @@ public class IOManager {
|
|||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
|
||||
public void receiveProject(ScratchJrActivity activity, Uri uri) throws JSONException, IOException, DatabaseException {
|
||||
File tempDir = new File(activity.getCacheDir() + File.separator + UUID.randomUUID().toString());
|
||||
tempDir.mkdir();
|
||||
List<String> entries = ScratchJrUtil.unzip(uri.getPath(), tempDir.getPath());
|
||||
if (entries.isEmpty()) {
|
||||
Log.e(LOG_TAG, "no entries found");
|
||||
// no files
|
||||
return;
|
||||
}
|
||||
// read project json
|
||||
JSONObject json = ScratchJrUtil.readJson(tempDir + "/project/data.json");
|
||||
JSONObject projectData = json.optJSONObject("json");
|
||||
JSONObject projectJson = new JSONObject();
|
||||
projectJson.put("isgift", "1");
|
||||
projectJson.put("deleted", "NO");
|
||||
projectJson.put("json", projectData.toString());
|
||||
JSONObject thumbnail = json.optJSONObject("thumbnail");
|
||||
projectJson.put("thumbnail", thumbnail.toString());
|
||||
projectJson.put("version", "iOSv01");
|
||||
projectJson.put("name", projectData.optString("name"));
|
||||
_databaseManager.insert("projects", projectJson);
|
||||
|
||||
HashMap<String, JSONObject> spriteMap = new HashMap<>();
|
||||
JSONArray pages = projectData.optJSONArray("pages");
|
||||
for (int i = 0; i < pages.length(); i++) {
|
||||
String pageName = pages.optString(i);
|
||||
if (pageName == null) {
|
||||
continue;
|
||||
}
|
||||
JSONObject page = projectData.optJSONObject(pageName);
|
||||
JSONArray spriteNames = page.optJSONArray("sprites");
|
||||
for (int j = 0; j < spriteNames.length(); j++) {
|
||||
String spriteName = spriteNames.optString(j);
|
||||
JSONObject sprite = page.optJSONObject(spriteName);
|
||||
spriteMap.put(sprite.optString("md5"), sprite);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
String entry = entries.get(i);
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
if (!(entry.endsWith(".png") || entry.endsWith(".wav") || entry.endsWith(".svg"))) {
|
||||
continue;
|
||||
}
|
||||
// copy file to target file
|
||||
File sourceFile = new File(tempDir + File.separator + entry);
|
||||
|
||||
String fileName = sourceFile.getName();
|
||||
File targetFile = new File(activity.getFilesDir().getPath() + File.separator + fileName);
|
||||
if (!targetFile.exists()) {
|
||||
ScratchJrUtil.copyFile(sourceFile, targetFile);
|
||||
}
|
||||
String folderName = sourceFile.getParentFile().getName();
|
||||
if ("thumbnails".equals(folderName) || "sounds".equals(folderName)) {
|
||||
continue;
|
||||
}
|
||||
String table = "characters".equals(folderName) ? "usershapes" : "userbkgs";
|
||||
String statement = String.format("SELECT id FROM %s WHERE md5 = ?", table);
|
||||
JSONArray rows = _databaseManager.query(statement, new String[]{fileName});
|
||||
if (rows.length() > 0) {
|
||||
Log.e(LOG_TAG, "asset for " + fileName + "exists");
|
||||
continue;
|
||||
}
|
||||
String pngName = fileName.replace(".svg", ".png");
|
||||
JSONObject asset = new JSONObject();
|
||||
asset.put("version", "iOSv01");
|
||||
asset.put("md5", fileName);
|
||||
asset.put("altmd5", pngName);
|
||||
asset.put("width", "480");
|
||||
asset.put("height", "360");
|
||||
asset.put("ext", fileName.split("\\.")[1]);
|
||||
if ("characters".equals(folderName)) {
|
||||
JSONObject sprite = spriteMap.get(fileName);
|
||||
if (sprite == null) {
|
||||
continue;
|
||||
}
|
||||
asset.put("width", sprite.optString("w"));
|
||||
asset.put("height", sprite.optString("h"));
|
||||
asset.put("scale", sprite.optString("scale"));
|
||||
asset.put("name", sprite.optString("name"));
|
||||
}
|
||||
File png = new File(activity.getFilesDir().getPath() + File.separator + pngName);
|
||||
if (!png.exists()) {
|
||||
String js = String.format("ScratchJr.makeThumb('%s', %s, %s)", fileName, asset.optString("width"), asset.optString("height"));
|
||||
Log.d(LOG_TAG, js);
|
||||
activity.runJavaScript(js);
|
||||
}
|
||||
_databaseManager.insert(table, asset);
|
||||
}
|
||||
// refresh lobby
|
||||
activity.runJavaScript("Lobby.refresh();");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ import android.os.Handler;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
|
@ -36,10 +35,7 @@ import android.widget.RelativeLayout;
|
|||
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Vector;
|
||||
|
||||
|
@ -110,6 +106,11 @@ public class ScratchJrActivity
|
|||
/* Firebase analytics tracking */
|
||||
private FirebaseAnalytics _FirebaseAnalytics;
|
||||
|
||||
/**
|
||||
* Project uri that need to be imported.
|
||||
*/
|
||||
private ArrayList<Uri> projectUris = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
@ -310,7 +311,15 @@ public class ScratchJrActivity
|
|||
}
|
||||
}
|
||||
|
||||
private void receiveProject(Uri projectUri) {
|
||||
private void receiveProject(final Uri projectUri) {
|
||||
if (!isSplashDone()) {
|
||||
projectUris.add(projectUri);
|
||||
return;
|
||||
}
|
||||
importProject(projectUri);
|
||||
}
|
||||
|
||||
private void importProject(final Uri projectUri) {
|
||||
String PROJECT_EXTENSION = getApplicationContext().getString(R.string.share_extension_filter);
|
||||
String scheme = projectUri.getScheme();
|
||||
Log.i(LOG_TAG, "receiveProject(scheme): " + scheme);
|
||||
|
@ -324,25 +333,16 @@ public class ScratchJrActivity
|
|||
if (scheme.equals(ContentResolver.SCHEME_FILE) && !projectUri.getPath().matches(PROJECT_EXTENSION)) {
|
||||
return;
|
||||
}
|
||||
// Read the project one byte at a time into a buffer
|
||||
ByteArrayOutputStream projectData = new ByteArrayOutputStream();
|
||||
try {
|
||||
InputStream is = getContentResolver().openInputStream(projectUri);
|
||||
|
||||
byte[] readByte = new byte[1];
|
||||
while ((is.read(readByte)) == 1) {
|
||||
projectData.write(readByte[0]);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
_ioManager.receiveProject(ScratchJrActivity.this, projectUri);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.i(LOG_TAG, "File not found in project load");
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
Log.i(LOG_TAG, "IOException in project load");
|
||||
return;
|
||||
}
|
||||
// We send the project Base64-encoded to JavaScript where it's processed and unpacked
|
||||
String base64Project = Base64.encodeToString(projectData.toByteArray(), Base64.DEFAULT);
|
||||
runJavaScript("OS.loadProjectFromSjr('" + base64Project + "');");
|
||||
});
|
||||
}
|
||||
|
||||
public RelativeLayout getContainer() {
|
||||
|
@ -483,6 +483,10 @@ public class ScratchJrActivity
|
|||
|
||||
public void setSplashDone(boolean done) {
|
||||
_splashDone = done;
|
||||
while (projectUris.size() > 0) {
|
||||
Uri uri = projectUris.remove(0);
|
||||
importProject(uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,16 +2,22 @@ package org.scratchjr.android;
|
|||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
|
@ -186,4 +192,77 @@ public class ScratchJrUtil {
|
|||
}
|
||||
return segments[segments.length - 1];
|
||||
}
|
||||
|
||||
public static List<String> unzip(String zipPath, String toPath) {
|
||||
List<String> entries = new ArrayList<>();
|
||||
File zipFile = new File(zipPath);
|
||||
ZipInputStream zin;
|
||||
try {
|
||||
zin = new ZipInputStream(new FileInputStream(zipFile));
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return entries;
|
||||
}
|
||||
try {
|
||||
ZipEntry ze;
|
||||
while ((ze = zin.getNextEntry()) != null) {
|
||||
String path = toPath + File.separator + ze.getName();
|
||||
File unzipFile = new File(path);
|
||||
if (ze.isDirectory()) {
|
||||
if(!unzipFile.isDirectory()) {
|
||||
unzipFile.mkdirs();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
File folder = unzipFile.getParentFile();
|
||||
if (!folder.isDirectory()) {
|
||||
folder.mkdirs();
|
||||
}
|
||||
FileOutputStream fout = new FileOutputStream(path, false);
|
||||
BufferedOutputStream bout = new BufferedOutputStream(fout);
|
||||
try {
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = zin.read(buffer)) != -1) {
|
||||
bout.write(buffer, 0, read);
|
||||
}
|
||||
bout.flush();
|
||||
zin.closeEntry();
|
||||
entries.add(ze.getName());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
fout.close();
|
||||
bout.close();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
zin.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static JSONObject readJson(String path) throws IOException, JSONException {
|
||||
byte[] data;
|
||||
InputStream in = new FileInputStream(new File(path));
|
||||
|
||||
try {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
int len;
|
||||
byte[] buffer = new byte[1024];
|
||||
while ((len = in.read(buffer)) != -1) {
|
||||
bos.write(buffer, 0, len);
|
||||
}
|
||||
bos.close();
|
||||
data = bos.toByteArray();
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
return new JSONObject(new String(data));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue