package org.geogebra.web.tablet; import java.util.ArrayList; import java.util.TreeMap; import org.geogebra.common.main.MaterialsManager; import org.geogebra.common.move.ggtapi.models.JSONParserGGT; import org.geogebra.common.move.ggtapi.models.Material; import org.geogebra.common.move.ggtapi.models.Material.MaterialType; import org.geogebra.common.move.ggtapi.models.MaterialFilter; import org.geogebra.common.move.ggtapi.models.SyncEvent; import org.geogebra.common.move.ggtapi.models.json.JSONException; import org.geogebra.common.move.ggtapi.models.json.JSONObject; import org.geogebra.common.util.debug.Log; import org.geogebra.web.html5.main.AppW; import org.geogebra.web.touch.FileManagerT; import org.geogebra.web.web.util.SaveCallback; import com.google.gwt.core.client.Callback; public class TabletFileManager extends FileManagerT { private abstract class MyCallback implements Callback<Object, Object> { private int id; public void setId(int id){ this.id = id; } public int getId(){ return id; } } private static int NO_CALLBACK = 0; private TreeMap<Integer, MyCallback> callbacks; private int callbacksCount = NO_CALLBACK; public TabletFileManager(AppW tabletApp) { super(tabletApp); } @Override protected void init(){ callbacks = new TreeMap<Integer, MyCallback>(); exportJavascriptMethods(); } private int addNewCallback(MyCallback callback){ callbacksCount++; callbacks.put(callbacksCount, callback); callback.setId(callbacksCount); return callbacksCount; } private void runCallback(int id, boolean success, Object result){ if (id != NO_CALLBACK){ MyCallback cb = callbacks.remove(id); if (success){ cb.onSuccess(result); }else{ cb.onFailure(result); } } } @Override protected void getFiles(final MaterialFilter filter) { final int callbackParent = addNewCallback(new MyCallback() { public void onSuccess(Object result){ int length = (Integer) result; debug("catchListLocalFiles: "+length); for (int i = 0; i < length; i++){ int callback = addNewCallback(new MyCallback() { public void onSuccess(Object result){ try { String[] resultStrings = (String[]) result; String name = resultStrings[0]; String data = resultStrings[1]; Material mat = JSONParserGGT.prototype.toMaterial(new JSONObject(data)); if (mat == null) { mat = new Material( 0, MaterialType.ggb); mat.setTitle(getTitleFromKey(name)); } mat.setLocalID(MaterialsManager.getIDFromKey(name)); if (filter.check(mat)) { addMaterial(mat); debug("add material: "+name+", id: "+mat.getLocalID()); } } catch (JSONException e) { e.printStackTrace(); } } public void onFailure(Object result){ // not needed debug("on failure"); } }); getMetaDataNative(i, callback, getId()); } } public void onFailure(Object result){ // not needed debug("parent -- on failure"); } }); listLocalFilesNative(callbackParent); } private native void listLocalFilesNative(int callback) /*-{ if ($wnd.android) { $wnd.android.listLocalFiles(callback); } }-*/; /** * this method is called through js (see exportJavascriptMethods()) */ public void catchListLocalFiles(int length, int callback) { runCallback(callback, true, length); } private native void getMetaDataNative(int i, int callback, int callbackParent) /*-{ if ($wnd.android) { $wnd.android.getMetaData(i, callback, callbackParent); } }-*/; /** * this method is called through js (see exportJavascriptMethods()) */ public void catchMetaData(String name, String data, int callback) { runCallback(callback, true, new String[] {name, data}); } /** * this method is called through js (see exportJavascriptMethods()) */ public void catchMetaDataError(int callback) { runCallback(callback, false, null); } @Override public void openMaterial(final Material material) { String fileName = getFileKey(material); debug("openMaterial: "+fileName+", id: "+material.getLocalID()); int callback = addNewCallback(new MyCallback() { public void onSuccess(Object result){ material.setBase64((String) result); doOpenMaterial(material); } public void onFailure(Object result){ // not needed } }); getBase64(fileName, callback); } private native void getBase64(String fileName, int callback) /*-{ if ($wnd.android) { $wnd.android.getBase64(fileName, callback); } }-*/; /** * this method is called through js (see exportJavascriptMethods()) */ public void catchBase64(String data, int callback) { runCallback(callback, true, data); } @Override public void saveFile(final String base64, final long modified, final SaveCallback cb) { Material material = createMaterial("", modified); material.setBase64(""); final Material saveFileMaterial = material; int callback; if (cb != null){ callback = addNewCallback(new MyCallback() { public void onSuccess(Object result){ saveFileMaterial.setLocalID((Integer) result); cb.onSaved(saveFileMaterial, true); } public void onFailure(Object result){ cb.onError(); } }); }else{ callback = NO_CALLBACK; } saveFileNative(getApp().getLocalID(), getTitleWithoutReservedCharacters(getApp() .getKernel().getConstruction().getTitle()),base64, saveFileMaterial.toJson().toString(), callback); } /** * this method is called through js (see exportJavascriptMethods()) */ public void catchSaveFileResult(int result, int cb) { runCallback(cb, result > 0, result); } private native void saveFileNative(int id, String title, String base64, String metaDatas, int callback) /*-{ if ($wnd.android) { $wnd.android.saveFile(id, title, base64, metaDatas, callback); } }-*/; @Override public void uploadUsersMaterials(final ArrayList<SyncEvent> events) { debug("uploadUsersMaterials"); final int callbackParent = addNewCallback(new MyCallback() { public void onSuccess(Object result){ int length = (Integer) result; debug("catchListLocalFiles: "+length); setNotSyncedFileCount(length, events); for (int i = 0; i < length; i++){ int callback = addNewCallback(new MyCallback() { public void onSuccess(Object result){ try { String[] resultStrings = (String[]) result; String name = resultStrings[0]; String data = resultStrings[1]; debug("sync "+name); Material mat = JSONParserGGT.prototype.toMaterial(new JSONObject(data)); mat.setLocalID(MaterialsManager.getIDFromKey(name)); sync(mat, events); } catch (JSONException e) { ignoreNotSyncedFile(events); e.printStackTrace(); } } public void onFailure(Object result){ // not needed debug("on failure: ignoreNotSyncedFile"); ignoreNotSyncedFile(events); } }); getMetaDataNative(i, callback, getId()); } } public void onFailure(Object result){ // not needed debug("parent -- on failure"); } }); listLocalFilesNative(callbackParent); } @Override public void upload(final Material mat) { int callback = addNewCallback(new MyCallback() { public void onSuccess(Object result){ mat.setBase64((String) result); doUpload(mat); } public void onFailure(Object result){ // not needed } }); getBase64(getFileKey(mat), callback); } @Override protected void updateFile(final String key, final long modified, final Material material) { debug("update file: "+material.getTitle()); material.setModified(modified); if (key == null){ debug("key is null"); // save as a new local file String base64 = material.getBase64(); material.setBase64(""); createFileFromTubeNative(getTitleWithoutReservedCharacters(material.getTitle()), base64, material.toJson().toString()); } else { material.setLocalID(MaterialsManager.getIDFromKey(key)); String newKey = MaterialsManager.createKeyString(material.getLocalID(), material.getTitle()); if (key.equals(newKey)) { debug("key == newKey"); // re-save file and meta data String base64 = material.getBase64(); material.setBase64(""); updateFileFromTubeNative(key, base64, material.toJson().toString()); } else { String newTitle = material.getTitle(); material.setTitle(MaterialsManager.getTitleFromKey(key)); material.setSyncStamp(material.getModified()); debug("key != newKey"); // save and rename debug("rename: "+newTitle); rename(newTitle, material); } } } private native void createFileFromTubeNative(String title, String base64, String metaDatas) /*-{ if ($wnd.android) { $wnd.android.createFileFromTube(title, base64, metaDatas); } }-*/; private native void updateFileFromTubeNative(String title, String base64, String metaDatas) /*-{ if ($wnd.android) { $wnd.android.updateFileFromTube(title, base64, metaDatas); } }-*/; @Override public void open(String url, String name, String features){ openUrlInBrowser(url, name, features); } @Override public void open(String url){ openUrlInBrowser(url, "", ""); } private native void openUrlInBrowser(String url, String name, String features) /*-{ if ($wnd.android) { $wnd.android.openUrlInBrowser(url, name, features); } }-*/; @Override public void rename(final String newTitle, final Material mat, final Runnable callback) { final String newKey = MaterialsManager.createKeyString(mat.getLocalID(), newTitle); final String oldKey = getFileKey(mat); mat.setBase64(""); mat.setTitle(newTitle); int callback1 = addNewCallback(new MyCallback() { @Override public void onSuccess(Object result) { if (callback != null){ debug("rename callback"); callback.run(); } } @Override public void onFailure(Object reason) { // not needed } }); renameNative(oldKey, newKey, mat.toJson().toString(), callback1); } private native void renameNative(String oldKey, String newKey, String metaData, int callback) /*-{ if ($wnd.android) { $wnd.android.rename(oldKey, newKey, metaData, callback); } }-*/; /** * this method is called through js (see exportJavascriptMethods()) */ public void catchRename(int callback) { runCallback(callback, true, null); } @Override public void delete(final Material mat, boolean permanent, final Runnable onSuccess) { if (!permanent) { mat.setDeleted(true); mat.setBase64(""); overwriteMetaDataNative(getFileKey(mat), mat.toJson().toString(), NO_CALLBACK); return; } int callback = addNewCallback(new MyCallback() { public void onSuccess(Object result){ debug("delete, onSuccess"); removeFile(mat); onSuccess.run(); } public void onFailure(Object result){ // not needed debug("delete, onFailure"); } }); deleteNative(getFileKey(mat), callback); } private native void overwriteMetaDataNative(String key, String metaData, int callback)/*-{ if ($wnd.android) { $wnd.android.overwriteMetaData(key, metaData, callback); } }-*/; /** * this method is called through js (see exportJavascriptMethods()) */ public void catchOverwriteMetaData(int callback) { runCallback(callback, true, null); } private native void deleteNative(String key, int callback) /*-{ if ($wnd.android) { $wnd.android.deleteGgb(key, callback); } }-*/; /** * this method is called through js (see exportJavascriptMethods()) */ public void catchDeleteResult(String result, int callback) { runCallback(callback, result != null && !"0".equals(result), result); } @Override public void setTubeID(String localID, Material mat) { mat.setBase64(""); overwriteMetaDataNative(localID, mat.toJson().toString(), NO_CALLBACK); } private native void exportJavascriptMethods() /*-{ var that = this; $wnd.tabletFileManager_catchListLocalFiles = $entry(function(length, callback) { that.@org.geogebra.web.tablet.TabletFileManager::catchListLocalFiles(II)(length, callback); }); $wnd.tabletFileManager_catchMetaData = $entry(function(name, data, callback) { that.@org.geogebra.web.tablet.TabletFileManager::catchMetaData(Ljava/lang/String;Ljava/lang/String;I)(name, data, callback); }); $wnd.tabletFileManager_catchMetaDataError = $entry(function(callback) { that.@org.geogebra.web.tablet.TabletFileManager::catchMetaDataError(I)(callback); }); $wnd.tabletFileManager_catchBase64 = $entry(function(data, callback) { that.@org.geogebra.web.tablet.TabletFileManager::catchBase64(Ljava/lang/String;I)(data, callback); }); $wnd.tabletFileManager_catchSaveFileResult = $entry(function(result, callback) { that.@org.geogebra.web.tablet.TabletFileManager::catchSaveFileResult(II)(result, callback); }); $wnd.tabletFileManager_catchDeleteResult = $entry(function(data, callback) { that.@org.geogebra.web.tablet.TabletFileManager::catchDeleteResult(Ljava/lang/String;I)(data, callback); }); $wnd.tabletFileManager_catchRename = $entry(function(callback) { that.@org.geogebra.web.tablet.TabletFileManager::catchRename(I)(callback); }); $wnd.tabletFileManager_catchOverwriteMetaData = $entry(function( callback) { that.@org.geogebra.web.tablet.TabletFileManager::catchOverwriteMetaData(I)(callback); }); }-*/; private native void debugNative(String s) /*-{ if ($wnd.android) { $wnd.android.debug(s); } }-*/; protected void debug(String s){ Log.debug(s); debugNative(s); } }