Java tutorial
/* == This file is part of Tomahawk Player - <http://tomahawk-player.org> === * * Copyright 2015, Enno Gottschalk <mrmaffen@googlemail.com> * * Tomahawk is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Tomahawk is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. */ package org.tomahawk.libtomahawk.resolver; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import org.apache.commons.io.Charsets; import org.apache.commons.io.IOUtils; import org.tomahawk.libtomahawk.database.CollectionDbManager; import org.tomahawk.libtomahawk.resolver.models.ScriptResolverMetaData; import org.tomahawk.libtomahawk.resolver.models.ScriptResolverTrack; import org.tomahawk.libtomahawk.resolver.plugins.ScriptCollectionPluginFactory; import org.tomahawk.libtomahawk.resolver.plugins.ScriptInfoPluginFactory; import org.tomahawk.libtomahawk.resolver.plugins.ScriptResolverPluginFactory; import org.tomahawk.libtomahawk.utils.GsonHelper; import org.tomahawk.libtomahawk.utils.StringEscapeUtils; import org.tomahawk.tomahawk_android.TomahawkApp; import org.tomahawk.tomahawk_android.activities.TomahawkMainActivity; import android.content.Context; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.preference.PreferenceManager; import android.util.Log; import android.webkit.WebSettings; import android.webkit.WebView; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; public class ScriptAccount implements ScriptWebViewClient.WebViewClientReadyListener { private final static String TAG = ScriptAccount.class.getSimpleName(); public final static String SCRIPT_INTERFACE_NAME = "Tomahawk"; public final static String CONFIG = "config"; public final static String ENABLED_KEY = "_enabled_"; private String mPath; private boolean mManuallyInstalled; private String mName; private WebView mWebView; private HashMap<String, ScriptJob> mJobs = new HashMap<>(); private HashMap<String, ScriptObject> mObjects = new HashMap<>(); private ScriptResolverPluginFactory mResolverPluginFactory = new ScriptResolverPluginFactory(); private ScriptCollectionPluginFactory mCollectionPluginFactory = new ScriptCollectionPluginFactory(); private ScriptInfoPluginFactory mInfoPluginFactory = new ScriptInfoPluginFactory(); private boolean mStopped; private ScriptResolver mScriptResolver; private ScriptResolverMetaData mMetaData; private Map<String, FuzzyIndex> mFuzzyIndexMap = new HashMap<>(); public ScriptAccount(String path, boolean manuallyInstalled) { String prefix = manuallyInstalled ? "file://" : "file:///android_asset"; mPath = prefix + path; mManuallyInstalled = manuallyInstalled; String[] parts = mPath.split("/"); mName = parts[parts.length - 1]; try { InputStream inputStream; if (mManuallyInstalled) { File metadataFile = new File(path + File.separator + "content" + File.separator + "metadata.json"); inputStream = new FileInputStream(metadataFile); } else { inputStream = TomahawkApp.getContext().getAssets() .open(path.substring(1) + "/content/metadata.json"); } String metadataString = IOUtils.toString(inputStream, Charsets.UTF_8); mMetaData = GsonHelper.get().fromJson(metadataString, ScriptResolverMetaData.class); if (mMetaData == null) { Log.e(TAG, "Couldn't read metadata.json. Cannot instantiate ScriptAccount."); return; } } catch (IOException e) { Log.e(TAG, "ScriptAccount: " + e.getClass() + ": " + e.getLocalizedMessage()); Log.e(TAG, "Couldn't read metadata.json. Cannot instantiate ScriptAccount."); return; } mWebView = new WebView(TomahawkApp.getContext()); WebSettings settings = mWebView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDatabaseEnabled(true); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2) { //noinspection deprecation settings.setDatabasePath(TomahawkApp.getContext().getDir("databases", Context.MODE_PRIVATE).getPath()); } settings.setDomStorageEnabled(true); mWebView.setWebChromeClient(new TomahawkWebChromeClient()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mWebView.getSettings().setAllowUniversalAccessFromFileURLs(true); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { WebView.setWebContentsDebuggingEnabled(true); } new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { //initalize WebView String data = "<!DOCTYPE html>" + "<html>" + "<head><title>" + mName + "</title></head>" + "<body>" + "<script src=\"file:///android_asset/js/rsvp-latest.min.js" + "\" type=\"text/javascript\"></script>" + "<script src=\"file:///android_asset/js/cryptojs-core.js" + "\" type=\"text/javascript\"></script>"; if (mMetaData.manifest.scripts != null) { for (String scriptPath : mMetaData.manifest.scripts) { data += "<script src=\"" + mPath + "/content/" + scriptPath + "\" type=\"text/javascript\"></script>"; } } try { String[] cryptoJsScripts = TomahawkApp.getContext().getAssets().list("js/cryptojs"); for (String scriptPath : cryptoJsScripts) { data += "<script src=\"file:///android_asset/js/cryptojs/" + scriptPath + "\" type=\"text/javascript\"></script>"; } } catch (IOException e) { Log.e(TAG, "ScriptResolver: " + e.getClass() + ": " + e.getLocalizedMessage()); } data += "<script src=\"file:///android_asset/js/tomahawk_android_pre.js" + "\" type=\"text/javascript\"></script>" + "<script src=\"file:///android_asset/js/tomahawk.js" + "\" type=\"text/javascript\"></script>" + "<script src=\"file:///android_asset/js/tomahawk-infosystem.js" + "\" type=\"text/javascript\"></script>" + "<script src=\"file:///android_asset/js/tomahawk_android_post.js" + "\" type=\"text/javascript\"></script>" + "<script src=\"" + mPath + "/content/" + mMetaData.manifest.main + "\" type=\"text/javascript\"></script>" + "</body></html>"; mWebView.setWebViewClient(new ScriptWebViewClient(ScriptAccount.this)); mWebView.addJavascriptInterface(new ScriptInterface(ScriptAccount.this), SCRIPT_INTERFACE_NAME); mWebView.loadDataWithBaseURL("file:///android_asset/test.html", data, "text/html", null, null); } }); } /** * This method is being called, when the {@link ScriptWebViewClient} has completely loaded the * given .js script. */ @Override public void onWebViewClientReady() { registerPlugin(ScriptObject.TYPE_RESOLVER); PipeLine.get().onPluginLoaded(this); } public ScriptResolver getScriptResolver() { return mScriptResolver; } public void setScriptResolver(ScriptResolver scriptResolver) { mScriptResolver = scriptResolver; } public ScriptResolverMetaData getMetaData() { return mMetaData; } public String getPath() { return mPath; } public String getName() { return mName; } public void setConfig(Map<String, Object> config) { String rawJsonString = GsonHelper.get().toJson(config); PreferenceManager.getDefaultSharedPreferences(TomahawkApp.getContext()).edit() .putString(buildPreferenceKey(), rawJsonString).commit(); mScriptResolver.saveUserConfig(); } /** * @return the Map<String, String> containing the Config information of this resolver */ public Map<String, Object> getConfig() { String rawJsonString = PreferenceManager.getDefaultSharedPreferences(TomahawkApp.getContext()) .getString(buildPreferenceKey(), ""); Map<String, Object> result = GsonHelper.get().fromJson(rawJsonString, Map.class); if (result == null) { result = new HashMap<>(); } return result; } public boolean isManuallyInstalled() { return mManuallyInstalled; } private String buildPreferenceKey() { return mMetaData.pluginName + "_" + CONFIG; } public void registerPlugin(String type) { evaluateJavaScript("Tomahawk.PluginManager.registerPlugin('" + type + "', Tomahawk.resolver.instance);"); } public void unregisterAllPlugins() { for (String objectId : mResolverPluginFactory.getScriptPlugins().keySet()) { evaluateJavaScript("Tomahawk.PluginManager.unregisterPlugin('" + ScriptObject.TYPE_RESOLVER + "', " + objectId + ");"); } for (String objectId : mCollectionPluginFactory.getScriptPlugins().keySet()) { evaluateJavaScript("Tomahawk.PluginManager.unregisterPlugin('" + ScriptObject.TYPE_COLLECTION + "', " + objectId + ");"); } for (String objectId : mInfoPluginFactory.getScriptPlugins().keySet()) { evaluateJavaScript("Tomahawk.PluginManager.unregisterPlugin('" + ScriptObject.TYPE_INFOPLUGIN + "', " + objectId + ");"); } } public void startJob(final ScriptJob job) { final String requestId = TomahawkMainActivity.getSessionUniqueStringId(); mJobs.put(requestId, job); new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { String serializedArgs = GsonHelper.get().toJson(job.getArguments()); serializedArgs = "JSON.parse('" + StringEscapeUtils.escapeJavaScript(serializedArgs) + "')"; evaluateJavaScript("Tomahawk.PluginManager.invoke(" + "'" + requestId + "'," + "'" + job.getScriptObject().getId() + "'," + "'" + job.getMethodName() + "'," + serializedArgs + ")"); } }); } private void evaluateJavaScript(final String code) { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { mWebView.loadUrl("javascript: " + code); } }); } public void reportScriptJobResult(JsonObject result) { JsonElement requestIdNode = result.get("requestId"); String requestId = null; if (requestIdNode != null && requestIdNode.isJsonPrimitive()) { requestId = result.get("requestId").getAsString(); } if (requestId != null && !requestId.isEmpty()) { ScriptJob job = mJobs.get(requestId); if (job != null) { JsonElement errorNode = result.get("error"); if (errorNode == null) { job.reportResults(result.get("data")); } else if (errorNode.isJsonPrimitive()) { job.reportFailure(result.get("error").getAsString()); } else { job.reportFailure("no error message provided"); } } else { Log.e(TAG, "reportScriptJobResult - ScriptAccount:" + mName + ", couldn't find ScriptJob with given requestId"); } } else { Log.e(TAG, "reportScriptJobResult - ScriptAccount:" + mName + ", requestId is null or empty"); } } public void registerScriptPlugin(String type, String objectId) { ScriptObject object = mObjects.get(objectId); if (object == null) { object = new ScriptObject(objectId, this); mObjects.put(objectId, object); } switch (type) { case ScriptObject.TYPE_RESOLVER: mResolverPluginFactory.registerPlugin(object, this); break; case ScriptObject.TYPE_COLLECTION: mCollectionPluginFactory.registerPlugin(object, this); break; case ScriptObject.TYPE_INFOPLUGIN: mInfoPluginFactory.registerPlugin(object, this); break; default: Log.e(TAG, "registerScriptPlugin - ScriptAccount:" + mName + ", ScriptPlugin type not supported!"); } } public void unregisterScriptPlugin(String type, String objectId) { ScriptObject object = mObjects.get(objectId); if (object == null) { Log.e(TAG, "unregisterScriptPlugin - ScriptAccount:" + mName + ", tried to unregister a plugin that was not registered!"); } switch (type) { case ScriptObject.TYPE_RESOLVER: mResolverPluginFactory.unregisterPlugin(object); break; case ScriptObject.TYPE_COLLECTION: mCollectionPluginFactory.unregisterPlugin(object); break; case ScriptObject.TYPE_INFOPLUGIN: mInfoPluginFactory.unregisterPlugin(object); break; default: Log.e(TAG, "unregisterScriptPlugin - ScriptAccount:" + mName + ", ScriptPlugin type not supported!"); } } public boolean isStopped() { return mStopped; } public void nativeAsyncRequestDone(final int requestId, final String responseText, final Map<String, List<String>> responseHeaders, final int status, final String statusText) { final Map<String, String> headers = new HashMap<>(); for (String key : responseHeaders.keySet()) { if (key != null) { String concatenatedValues = ""; for (int i = 0; i < responseHeaders.get(key).size(); i++) { if (i > 0) { concatenatedValues += "\n"; } concatenatedValues += responseHeaders.get(key).get(i); } headers.put(key, concatenatedValues); } } String headersString = GsonHelper.get().toJson(headers); evaluateJavaScript("Tomahawk._nativeAsyncRequestDone(" + requestId + "," + "'" + StringEscapeUtils.escapeJavaScript(responseText) + "'," + "'" + StringEscapeUtils.escapeJavaScript(headersString) + "'," + status + "," + "'" + StringEscapeUtils.escapeJavaScript(statusText) + "');"); } public class CollectionAddTracksResult { String id; ScriptResolverTrack[] tracks; } public class CollectionWipeResult { String id; } public void invokeNativeScriptJob(int requestId, String methodName, String paramsString) { if (methodName.equals("collectionAddTracks")) { CollectionAddTracksResult result = GsonHelper.get().fromJson(paramsString, CollectionAddTracksResult.class); CollectionDbManager.get().getCollectionDb(result.id).addTracks(result.tracks); } else if (methodName.equals("collectionWipe")) { CollectionWipeResult result = GsonHelper.get().fromJson(paramsString, CollectionWipeResult.class); CollectionDbManager.get().getCollectionDb(result.id).wipe(); } evaluateJavaScript("Tomahawk.NativeScriptJobManager.reportNativeScriptJobResult(" + requestId + ");"); } }