package com.openfeint.internal.ui;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonParser;
import org.json.JSONArray;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.openfeint.api.OpenFeintDelegate;
import com.openfeint.api.R;
import com.openfeint.api.resource.Score;
import com.openfeint.api.resource.User;
import com.openfeint.api.ui.Dashboard;
import com.openfeint.internal.ImagePicker;
import com.openfeint.internal.JsonResourceParser;
import com.openfeint.internal.OpenFeintInternal;
import com.openfeint.internal.Util;
import com.openfeint.internal.request.IRawRequestDelegate;
import com.openfeint.internal.resource.ScoreBlobDelegate;
public class WebNav extends Activity {
// This is necessary because when we ditch out to the image picker,
// the game might get evicted.
protected void onSaveInstanceState(Bundle outState) {
OpenFeintInternal.saveInstanceState(outState);
}
// This is necessary because when we ditch out to the image picker,
// the game might get evicted.
protected void onRestoreInstanceState(Bundle inState) {
OpenFeintInternal.restoreInstanceState(inState);
}
protected static final String TAG = "WebUI";
// sub-activity request codes go here. make sure they don't collide in derived classes
protected static final int REQUEST_CODE_NATIVE_BROWSER = 25565;
private WebView mWebView;
private WebNavClient mWebViewClient;
ActionHandler mActionHandler;
public ActionHandler getActionHandler() { return mActionHandler; }
Dialog mLaunchLoadingView;
public Dialog getLaunchLoadingView() { return mLaunchLoadingView; }
boolean mIsFrameworkLoaded = false;
protected void setFrameworkLoaded(boolean value) { mIsFrameworkLoaded = value; }
protected int pageStackCount;
boolean mIsVisible = false;
private boolean mShouldRefreshOnResume = true;
protected ArrayList<String> mPreloadConsoleOutput = new ArrayList<String>();
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Util.setOrientation(this);
setContentView(R.layout.of_webnav);
OpenFeintInternal.log(TAG, "--- WebUI Bootup ---");
// Setup helper views
pageStackCount = 0;
// Setup WebView
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setPluginsEnabled(false);
mWebView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
// Setup native loader to show while we chew on stuff
mLaunchLoadingView = new Dialog(this, R.style.OFLoading);
mLaunchLoadingView.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel (DialogInterface dialog) {
WebNav.this.finish();
}
});
mLaunchLoadingView.setCancelable(true);
mLaunchLoadingView.setContentView(R.layout.of_native_loader);
ProgressBar progress = (ProgressBar)mLaunchLoadingView.findViewById(R.id.progress);
progress.setIndeterminate(true);
progress.setIndeterminateDrawable(
OpenFeintInternal.getInstance().getContext().getResources().getDrawable(
R.drawable.of_native_loader_progress)
);
mLaunchLoadingView.show();
// Setup the WebViewClient and its ActionHandler
mActionHandler = createActionHandler(this);
mWebViewClient = new WebNavClient(mActionHandler);
mWebView.setWebViewClient(mWebViewClient);
mWebView.setWebChromeClient(new WebNavChromeClient());
// Setup the JavaScript bridge
mWebView.addJavascriptInterface(new Object() {
@SuppressWarnings("unused")
public void action(final String actionUri) {
runOnUiThread(new Runnable() {
public void run() {
getActionHandler().dispatch(Uri.parse(actionUri));
}
});
}
@SuppressWarnings("unused")
public void frameworkLoaded() {
WebNav.this.setFrameworkLoaded(true);
}
}, "NativeInterface");
String path = initialContentPath();
if (path.contains("?")) path = path.split("\\?")[0];
if (!path.endsWith(".json")) path += ".json";
WebViewCache.prioritize(path);
load(false);
}
protected String rootPage() {
return "index.html";
}
private void load(final boolean reload) {
WebViewCache.trackPath(rootPage(), new WebViewCacheCallback() {
public void pathLoaded(String itemPath) {
if (mWebView != null) {
String url = WebViewCache.getItemUri(itemPath);
OpenFeintInternal.log(TAG, "Loading URL: " + url);
if (reload) {
mWebView.reload();
} else {
mWebView.loadUrl(url);
}
}
}
@Override
public void failLoaded() {
closeForDiskError();
}
});
}
private void closeForDiskError() {
// THIS is a hack, since we may rewrite the web cache in next version
// let's just hack it here.
this.runOnUiThread(new Runnable() {
public void run() {
dismissDialog();
String place = (Util.sdcardReady(WebNav.this))? OpenFeintInternal.getRString(R.string.of_sdcard):
OpenFeintInternal.getRString(R.string.of_device);
new AlertDialog.Builder(WebNav.this)
.setMessage(String.format(OpenFeintInternal.getRString(R.string.of_nodisk), place))
.setPositiveButton(OpenFeintInternal.getRString(R.string.of_no), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
WebNav.this.finish();
}
}).show();
}
});
}
private static final String jsQuotedStringLiteral(String unquotedString) {
if (unquotedString == null) return "''";
return "'" + unquotedString.replace("\\", "\\\\").replace("'", "\\'") + "'";
}
/**
* Ensure the current user data is up to date in case it changed since this
* activity was last in the foreground.
*/
@Override
public void onResume() {
super.onResume();
User localUser = OpenFeintInternal.getInstance().getCurrentUser();
if (localUser != null && mIsFrameworkLoaded) {
executeJavascript(
String.format("if (OF.user) { OF.user.name = %s; OF.user.id = '%s'; }",
jsQuotedStringLiteral(localUser.name),
localUser.resourceID()));
if (mShouldRefreshOnResume) {
executeJavascript("if (OF.page) OF.refresh();");
}
}
mShouldRefreshOnResume = true;
}
@Override
public void onStop() {
super.onStop();
dismissDialog();
}
private void dismissDialog() {
if (mLaunchLoadingView.isShowing())
mLaunchLoadingView.dismiss();
}
private void showDialog() {
if (!mLaunchLoadingView.isShowing())
mLaunchLoadingView.show();
}
/**
* Tell the WebNav about an orientation change
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
String orientationString = null;
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
orientationString = "landscape";
} else {
orientationString = "portrait";
}
executeJavascript(String.format("OF.setOrientation('%s');", orientationString));
}
/**
* After the page context is ready, load the first page of content.
*/
public void loadInitialContent() {
String path = initialContentPath();
if (path.contains("?")) path = path.split("\\?")[0];
if (!path.endsWith(".json")) path += ".json";
WebViewCache.trackPath(path, new WebViewCacheCallback() {
public void pathLoaded(String itemPath) {
executeJavascript("OF.navigateToUrl('" + initialContentPath() + "')");
}
@Override
public void failLoaded() {
closeForDiskError();
}
});
}
/**
* Create the native action handler for this flow. Override in subclasses to
* add new actions.
*
* @param webNav
* the WebNav instance from which actions will be called.
* @return an ActionHandler instance on which to call native actions.
*/
protected ActionHandler createActionHandler(WebNav webNav) {
return new ActionHandler(webNav);
}
/**
* Override in subclasses to set what template path is loaded as the first
* content.
*
* @return the template path to load when this WebNav appears.
*/
protected String initialContentPath() {
String contentPath = getIntent().getStringExtra("content_path");
if (contentPath == null) {
throw new RuntimeException("WebNav intent requires extra value 'content_path'");
}
return contentPath;
}
/**
* Intercept the back button. If there is history to go back to in the
* WebNav, then go back. Otherwise, finish the Activity and return.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
executeJavascript(String.format("OF.menu('%s')", "search"));
return true;
}
if (keyCode == KeyEvent.KEYCODE_BACK && pageStackCount > 1) {
executeJavascript("OF.goBack()");
return true;
}
return super.onKeyDown(keyCode, event);
}
/**
* Run some JavaScript in the WebView.
*
* @param js
* the JavaScript to execute.
*/
public void executeJavascript(String js) {
if (mWebView != null) {
mWebView.loadUrl("javascript:" + js);
}
}
/**
* Fades the webview in or out
*
* @param toVisible
* true for fade in, false for fade out.
*/
public void fade(boolean toVisible) {
if (mWebView != null) {
if (mIsVisible != toVisible) {
mIsVisible = toVisible;
AlphaAnimation anim = new AlphaAnimation(toVisible ? 0.0f : 1.0f,
toVisible ? 1.0f : 0.0f);
anim.setDuration(toVisible ? 200 : 0);
anim.setFillAfter(true);
mWebView.startAnimation(anim);
if (mWebView.getVisibility() == View.INVISIBLE)
mWebView.setVisibility(View.VISIBLE);
}
}
}
/**
* Class to handle action URI navigation events as messages.
*
* @author Alex Wayne
*/
private class WebNavClient extends WebViewClient {
ActionHandler mActionHandler;
/**
* Constructor
*
* @param anActionHandler
* ActionHandler instance to use with the WebNav
*/
public WebNavClient(ActionHandler anActionHandler) {
mActionHandler = anActionHandler;
}
/**
* Intercept URL loading and inspect for messages being passed to native
* code.
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String stringUrl) {
Uri uri = Uri.parse(stringUrl);
if (uri.getScheme().equals("http")
|| uri.getScheme().equals("https")) {
view.loadUrl(stringUrl);
} else if (uri.getScheme().equals("openfeint")) {
mActionHandler.dispatch(uri);
} else {
OpenFeintInternal.log(TAG, "UNHANDLED PROTOCOL: " + uri.getScheme());
}
return true;
}
/**
* Make sure the loading is hidden on generic failures so interaction is
* not blocked
*/
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
mActionHandler.hideLoader(null);
}
@Override
public void onPageFinished(WebView view, String url) {
if (mIsFrameworkLoaded) {
loadInitialContent();
} else {
// call onPageFinished after restore
if (WebViewCache.recover()) {
load(true);
new AlertDialog.Builder(view.getContext())
.setMessage(OpenFeintInternal.getRString(R.string.of_crash_report_query))
.setNegativeButton(OpenFeintInternal.getRString(R.string.of_no), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
WebNav.this.finish();
}
})
.setPositiveButton(OpenFeintInternal.getRString(R.string.of_yes), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
WebNavClient.this.submitCrashReport();
}
})
.show();
} else if (!WebViewCache.isDiskError()) {
finish();
}
}
}
private void submitCrashReport() {
Map<String, Object> crashReport = new HashMap<String, Object>();
crashReport.put("console", new JSONArray(mPreloadConsoleOutput));
JSONObject json = new JSONObject(crashReport);
Map<String, Object> params = new HashMap<String, Object>();
params.put("crash_report", json.toString());
OpenFeintInternal.genericRequest("/webui/crash_report","POST", params, null, null);
}
/**
* Initial HTML/JS framework is loaded, so inject the client
* configuration. Then load the initial content
*/
public void loadInitialContent() {
OpenFeintInternal of = OpenFeintInternal.getInstance();
User localUser = of.getCurrentUser();
int orientation = getResources().getConfiguration().orientation;
HashMap<String, Object> user = new HashMap<String, Object>();
if (localUser != null) {
user.put("id", localUser.resourceID());
user.put("name", localUser.name);
}
HashMap<String, Object> game = new HashMap<String, Object>();
game.put("id", of.getAppID());
game.put("name", of.getAppName());
game.put("version", of.getAppVersion());
Map<String, Object> device = OpenFeintInternal.getInstance().getDeviceParams();
HashMap<String, Object> config = new HashMap<String, Object>();
config.put("platform", "android");
config.put("clientVersion", of.getOFVersion());
config.put("hasNativeInterface", true);
config.put("dpi", Util.getDpiName(WebNav.this));
config.put("locale", getResources().getConfiguration().locale.toString());
config.put("user", new JSONObject(user));
config.put("game", new JSONObject(game));
config.put("device", new JSONObject(device));
config.put("actions", new JSONArray(getActionHandler().getActionList()));
config.put("orientation",
orientation == Configuration.ORIENTATION_LANDSCAPE ?
"landscape" : "portrait");
config.put("serverUrl", of.getServerUrl());
JSONObject json = new JSONObject(config);
// Run the environment variables setup JavaScript
executeJavascript(String.format("OF.init.clientBoot(%s)", json.toString()));
// Load the first content
mActionHandler.mWebNav.loadInitialContent();
}
}
/**
* Class to handle browser chrome tasks, like alerts
*
* @author Alex Wayne
*/
private class WebNavChromeClient extends WebChromeClient {
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
new AlertDialog.Builder(view.getContext())
.setMessage(message)
.setNegativeButton(OpenFeintInternal.getRString(R.string.of_ok), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
}).setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
result.cancel();
}
}).show();
return true;
}
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
new AlertDialog.Builder(view.getContext()).setMessage(message)
.setPositiveButton(OpenFeintInternal.getRString(R.string.of_ok), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
}).setNegativeButton(OpenFeintInternal.getRString(R.string.of_cancel), new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
}).setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
result.cancel();
}
}).show();
return true;
}
public void onConsoleMessage (String message, int lineNumber, String sourceID) {
if(!mIsFrameworkLoaded) {
String line = String.format("%s at %s:%d)", message, sourceID, lineNumber);
mPreloadConsoleOutput.add(line);
}
}
}
/**
* Class to handle native code actions from OF.sendAction("someAction", {options: ... })
*
* @author Alex Wayne
*/
protected class ActionHandler extends Object {
WebNav mWebNav;
List<String> mActionList;
protected List<String> getActionList() {
return mActionList;
}
/**
* Constructor
*
* @param webNav
* WebNav calling the actions
*/
public ActionHandler(WebNav webNav) {
mWebNav = webNav;
mActionList = new ArrayList<String>();
populateActionList(mActionList);
}
/**
* Add action names to the supported actionList. Override this to
* support additional actions. Every action you add MUST have a
* corresponding method named identically to handle that action.
*
* @param actionList
* list of actions supported by the superclass. Add your
* custom actions to this list.
*/
protected void populateActionList(List<String> actionList) {
// Available in v1.0 forward
actionList.add("log");
actionList.add("apiRequest");
actionList.add("contentLoaded");
actionList.add("startLoading");
actionList.add("back");
actionList.add("showLoader");
actionList.add("hideLoader");
actionList.add("alert");
actionList.add("dismiss");
actionList.add("openMarket");
actionList.add("isApplicationInstalled");
actionList.add("openYoutubePlayer");
actionList.add("profilePicture");
// Available in v1.5 forward
actionList.add("openBrowser");
actionList.add("downloadBlob");
// Available in v1.6 forward
actionList.add("dashboard");
// Available in v1.8 forward
actionList.add("readSetting");
actionList.add("writeSetting");
}
/**
* Dispatch an action URI to an identically named method to handle it
*
* @param uri
* the action URI to parse
*/
public void dispatch(Uri uri) {
if (uri.getHost().equals("action")) {
// Prepare action values
Map<String, Object> options = parseQueryString(uri);
String actionName = uri.getPath().replaceFirst("/", "");
// Log the action unless this is an explicit log action
if (!actionName.equals("log")) {
Map<String,Object> escapedOptions = new HashMap<String,Object>(options);
String params = (String)options.get("params");
if (params != null && params.contains("password")) {
escapedOptions.put("params", "---FILTERED---");
}
OpenFeintInternal.log(TAG, "ACTION: " + actionName + " " + escapedOptions.toString());
}
// Find the native method to call
if (mActionList.contains(actionName)) {
try {
getClass().getMethod(actionName, Map.class).invoke( this, options);
} catch (NoSuchMethodException e) {
OpenFeintInternal.log(TAG, "mActionList contains this method, but it is not implemented: "+ actionName);
} catch (Exception e) {
OpenFeintInternal.log(TAG, "Unhandled Exception: " + e.toString() +" "+ e.getCause());
}
} else {
OpenFeintInternal.log(TAG, "UNHANDLED ACTION: " + actionName);
}
} else {
OpenFeintInternal.log(TAG, "UNHANDLED MESSAGE TYPE: " + uri.getHost());
}
}
/**
* Utility method to turn a URI's query string string into a Map of keys
* and values
*
* @param uri
* Action URI that contains the query string to parse
* @return a Map of Strings to Strings containing the parsed query
* string.
*/
private Map<String, Object> parseQueryString(Uri uri) {
return parseQueryString(uri.getEncodedQuery());
}
/**
* Utility method to turn query string string into a Map of keys and
* values
*
* @param queryString
* The query string to parse
* @return a Map of Strings to Strings containing the parsed query
* string.
*/
private Map<String, Object> parseQueryString(String queryString) {
Map<String, Object> options = new HashMap<String, Object>();
if (queryString != null) {
String[] pairs = queryString.split("&");
for (String stringPair : pairs) {
String[] pair = stringPair.split("=");
if (pair.length == 2) {
options.put(pair[0], Uri.decode(pair[1]));
} else {
options.put(pair[0], null);
}
}
}
return options;
}
// ---
// --- Action Methods ---
// ---
/**
* Make a signed and authenticate generic API request. Return the result
* into the WebNav with the status code and response body.
*/
public void apiRequest(Map<String, String> options) {
final String requestID = options.get("request_id");
Map<String, Object> params = parseQueryString(options.get("params"));
Map<String, Object> httpParams = parseQueryString(options.get("httpParams"));
OpenFeintInternal.genericRequest(
options.get("path"),
options.get("method"), params, httpParams, new IRawRequestDelegate() {
public void onResponse(int statusCode, String responseBody) {
String response = responseBody.trim();
if (response.length() == 0) response = "{}";
String js = String
.format("OF.api.completeRequest(\"%s\", \"%d\", %s)",
requestID, statusCode, response);
mWebNav.executeJavascript(js);
}
}
);
}
public void contentLoaded(Map<String, String> options) {
if (!(options.get("keepLoader") != null && options.get("keepLoader").equals("true"))) {
hideLoader(null);
setTitle(options.get("title"));
}
mWebNav.fade(true);
dismissDialog();
}
public void startLoading(Map<String, String> options) {
mWebNav.fade(false);
showLoader(null);
WebViewCache.trackPath(options.get("path"),
new WebViewCacheCallback() {
public void pathLoaded(String itemPath) {
executeJavascript("OF.navigateToUrlCallback()");
}
@Override
public void failLoaded() {
closeForDiskError();
}
public void onTrackingNeeded() {
showDialog();
}
});
mWebNav.pageStackCount++;
}
public void back(Map<String, String> options) {
mWebNav.fade(false);
String root = options.get("root");
if (root != null && !root.equals("false")) {
mWebNav.pageStackCount = 1;
}
if (mWebNav.pageStackCount > 1) {
mWebNav.pageStackCount--;
}
}
public void showLoader(Map<String, String> options) {
// mWebNav.getLoadingView().setVisibility(View.VISIBLE);
}
public void hideLoader(Map<String, String> options) {
// mWebNav.getLoadingView().setVisibility(View.GONE);
}
public void log(Map<String, String> options) {
String message = options.get("message");
if (message != null)
OpenFeintInternal.log(TAG, "WEBLOG: " + options.get("message"));
}
public void alert(Map<String, String> options) {
AlertDialog.Builder builder = new AlertDialog.Builder(mWebNav);
builder.setTitle(options.get("title"));
builder.setMessage(options.get("message"));
builder.setNegativeButton(OpenFeintInternal.getRString(R.string.of_ok), null);
builder.show();
}
public void dismiss(Map<String, String> options) {
finish();
}
public void openMarket(final Map<String, String> options) {
String packageName = options.get("package_name");
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("market://details?id=" + packageName));
// Intent intent = new Intent(Intent.ACTION_VIEW,
// Uri.parse("http://market.android.com/details?id=" +
// packageName));
mWebNav.startActivity(intent);
}
public void isApplicationInstalled(final Map<String, String> options) {
boolean installed = false;
PackageManager manager = mWebNav.getPackageManager();
List<ApplicationInfo> installedApps = manager
.getInstalledApplications(0);
String searchString = options.get("package_name");
for (ApplicationInfo info : installedApps) {
if (info.packageName.equals(searchString)) {
installed = true;
}
}
executeJavascript(String.format("%s(%s)",
options.get("callback"),
installed ? "true" : "false"));
}
public void openYoutubePlayer(Map<String, String> options) {
final String videoID = options.get("video_id");
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("vnd.youtube:" + videoID));
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() == 0){
Toast.makeText(mWebNav, OpenFeintInternal.getRString(R.string.of_no_video), Toast.LENGTH_SHORT).show();
} else {
mWebNav.startActivity(intent);
}
}
final public void profilePicture(final Map<String,String> options) {
ImagePicker.show(WebNav.this);
}
public void openBrowser(Map<String, String> options) {
final Intent browserIntent = new Intent(mWebNav, NativeBrowser.class);
mNativeBrowserParameters = new HashMap<String, String>();
for(String arg : new String[] {"src", "callback", "on_cancel", "on_failure", "timeout"}) {
final String val = options.get(arg);
if (null != val) {
// save it for the return...
mNativeBrowserParameters.put(arg, val);
// ... and send it to the browser.
browserIntent.putExtra(NativeBrowser.INTENT_ARG_PREFIX + arg, val);
}
}
startActivityForResult(browserIntent, REQUEST_CODE_NATIVE_BROWSER);
}
public void downloadBlob(Map<String, String> options) {
final String scoreJSON = options.get("score");
final String onError = options.get("onError");
final String onSuccess = options.get("onSuccess");
try {
JsonFactory jsonFactory = new JsonFactory(); // for thread safety, we make our own.
JsonParser jp = jsonFactory.createJsonParser(new StringReader(scoreJSON));
JsonResourceParser jrp = new JsonResourceParser(jp);
Object scoreObject = jrp.parse();
if (scoreObject != null && scoreObject instanceof Score) {
final Score score = (Score)scoreObject;
score.downloadBlob(new Score.DownloadBlobCB() {
@Override public void onSuccess() {
if (onSuccess != null) {
executeJavascript(String.format("%s()", onSuccess));
}
ScoreBlobDelegate.notifyBlobDownloaded(score);
}
@Override public void onFailure(String exceptionMessage) {
if (onError != null) {
executeJavascript(String.format("%s(%s)", onError, exceptionMessage));
}
}
});
}
} catch (Exception e) { // => JsonParseException, IOException
if (onError != null) {
executeJavascript(String.format("%s(%s)", onError, e.getLocalizedMessage()));
}
}
}
public void dashboard(Map<String, String> options) {
Dashboard.openFromSpotlight();
}
private static final String WEBUI_PREFS = "OFWebUI";
private static final String WEBUI_SETTING_PREFIX = "OFWebUISetting_";
public void readSetting(final Map<String, String> options) {
final String k = options.get("key");
final String cb = options.get("callback");
if (cb != null) {
final String key = (k != null ? (WEBUI_SETTING_PREFIX + k) : null);
final SharedPreferences prefs = OpenFeintInternal.getInstance().getContext().getSharedPreferences(WEBUI_PREFS, Context.MODE_PRIVATE);
String val = prefs.getString(key, null);
executeJavascript(String.format("%s(%s)", cb, val != null ? jsQuotedStringLiteral(val) : "null"));
}
}
public void writeSetting(final Map<String, String> options) {
final String k = options.get("key");
final String v = options.get("value");
if (k != null && v != null) {
final String key = WEBUI_SETTING_PREFIX + k;
final SharedPreferences.Editor editor = OpenFeintInternal.getInstance().getContext().getSharedPreferences(WEBUI_PREFS, Context.MODE_PRIVATE).edit();
editor.putString(key, v);
editor.commit();
}
}
}
private Map<String, String> mNativeBrowserParameters = null;
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (mNativeBrowserParameters != null && requestCode == REQUEST_CODE_NATIVE_BROWSER) {
if (resultCode != Activity.RESULT_CANCELED) {
// Temporarily, we don't want to refresh.
mShouldRefreshOnResume = false;
if (data.getBooleanExtra(NativeBrowser.INTENT_ARG_PREFIX + "failed", false)) {
final String cb = mNativeBrowserParameters.get("on_failure");
if (cb != null) {
final int code = data.getIntExtra(NativeBrowser.INTENT_ARG_PREFIX + "failure_code", 0);
final String desc = data.getStringExtra(NativeBrowser.INTENT_ARG_PREFIX + "failure_desc");
executeJavascript(String.format("%s(%d, %s)", cb, code, jsQuotedStringLiteral(desc)));
}
}
else
{
final String cb = mNativeBrowserParameters.get("callback");
if (cb != null) {
final String rv = data.getStringExtra(NativeBrowser.INTENT_ARG_PREFIX + "result");
executeJavascript(String.format("%s(%s)", cb, (rv != null ? rv : "")));
}
}
} else {
final String cb = mNativeBrowserParameters.get("on_cancel");
if (cb != null) {
executeJavascript(String.format("%s()", cb));
}
}
mNativeBrowserParameters = null;
} else if (ImagePicker.isImagePickerActivityResult(requestCode)) {
Bitmap image = ImagePicker.onImagePickerActivityResult(WebNav.this, resultCode, 152, data);
if (image != null) {
String apiPath = "/xp/users/"+ OpenFeintInternal.getInstance().getCurrentUser().resourceID() +"/profile_picture";
ImagePicker.compressAndUpload(image, apiPath, new OpenFeintInternal.IUploadDelegate() {
public void fileUploadedTo(String url, boolean success) {
if (success) {
// OF might not be there, the function might not be there, etc
// so we try/catch.
WebNav.this.executeJavascript("try { OF.page.onProfilePictureChanged('" + url + "'); } catch (e) {}");
}
}
});
}
} else {
// hopefully a subclass handled it
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
OpenFeintDelegate d = OpenFeintInternal.getInstance().getDelegate();
if (d != null) {
if (hasFocus) {
d.onDashboardAppear();
} else {
d.onDashboardDisappear();
}
}
}
@Override
public void onDestroy() {
mWebView.destroy();
mWebView = null;
super.onDestroy();
}
}