Java tutorial
/* * Copyright (C) 2011 The Stanford MobiSocial Laboratory * * This file is part of Musubi, a mobile social network. * * This program 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 2 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package edu.stanford.mobisocial.dungbeetle.feed.objects; import java.util.List; import mobisocial.socialkit.Obj; import mobisocial.socialkit.SignedObj; import mobisocial.socialkit.musubi.DbObj; import mobisocial.socialkit.musubi.Musubi; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ResolveInfo; import android.database.Cursor; import android.net.Uri; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import edu.stanford.mobisocial.dungbeetle.App; import edu.stanford.mobisocial.dungbeetle.DBHelper; import edu.stanford.mobisocial.dungbeetle.DungBeetleContentProvider; import edu.stanford.mobisocial.dungbeetle.R; import edu.stanford.mobisocial.dungbeetle.feed.DbObjects; import edu.stanford.mobisocial.dungbeetle.feed.iface.DbEntryHandler; import edu.stanford.mobisocial.dungbeetle.feed.iface.FeedRenderer; import edu.stanford.mobisocial.dungbeetle.model.AppState; import edu.stanford.mobisocial.dungbeetle.model.Contact; import edu.stanford.mobisocial.dungbeetle.model.DbObject; import edu.stanford.mobisocial.dungbeetle.model.Feed; /** * A snapshot of an application's state. */ public class AppStateObj extends DbEntryHandler implements FeedRenderer { private static final String TAG = "AppStateObj"; private static final boolean DBG = false; public static final String TYPE = "appstate"; public static final String ARG = "arg"; public static final String STATE = "state"; public static final String THUMB_JPG = "b64jpgthumb"; public static final String THUMB_TEXT = "txt"; public static final String THUMB_HTML = "html"; public static final String PACKAGE_NAME = "packageName"; public static final String GROUP_URI = "groupuri"; @Override public String getType() { return TYPE; } public static AppState from(String packageName, String arg, String feedName, String groupUri) { return new AppState(json(packageName, arg, feedName, groupUri)); } public static JSONObject json(String packageName, String arg, String feedName, String groupUri) { JSONObject obj = new JSONObject(); try { obj.put(PACKAGE_NAME, packageName); obj.put(ARG, arg); obj.put(DbObject.CHILD_FEED_NAME, feedName); obj.put(GROUP_URI, groupUri); } catch (JSONException e) { } return obj; } public static JSONObject json(String packageName, String arg, String state, String b64JpgThumb, String thumbText, String feedName, String groupUri) { JSONObject obj = new JSONObject(); try { obj.put(PACKAGE_NAME, packageName); obj.put(ARG, arg); if (state != null) { obj.put(STATE, state); } if (b64JpgThumb != null) { obj.put(THUMB_JPG, b64JpgThumb); } if (thumbText != null) { obj.put(THUMB_TEXT, thumbText); } if (feedName != null) { obj.put(DbObject.CHILD_FEED_NAME, feedName); } if (groupUri != null) { obj.put(GROUP_URI, groupUri); } } catch (JSONException e) { } return obj; } @Override public boolean handleObjFromNetwork(Context context, Contact contact, JSONObject obj) { DBHelper helper = new DBHelper(context); if (obj.has(DbObjects.TARGET_HASH)) { long hashA = obj.optLong(DbObjects.TARGET_HASH); long idA = helper.objIdForHash(hashA); if (idA == -1) { Log.e(TAG, "No objId found for hash " + hashA); } else { helper.updateParentLastModified(idA); } } return true; } public void render(final Context context, final ViewGroup frame, Obj obj, boolean allowInteractions) { JSONObject content = obj.getJson(); // TODO: hack to show object history in app feeds JSONObject appState = getAppState(context, content); if (appState != null) { content = appState; } else { Log.e(TAG, "Missing inner content, probably because of format changes"); } boolean rendered = false; AppState ref = new AppState(content); String thumbnail = ref.getThumbnailImage(); if (thumbnail != null) { rendered = true; ImageView imageView = new ImageView(context); imageView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); App.instance().objectImages.lazyLoadImage(thumbnail.hashCode(), thumbnail, imageView); frame.addView(imageView); } thumbnail = ref.getThumbnailText(); if (thumbnail != null) { rendered = true; TextView valueTV = new TextView(context); valueTV.setText(thumbnail); valueTV.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); valueTV.setGravity(Gravity.TOP | Gravity.LEFT); frame.addView(valueTV); } thumbnail = ref.getThumbnailHtml(); if (thumbnail != null) { rendered = true; WebView webview = new WebView(context); webview.loadData(thumbnail, "text/html", "UTF-8"); webview.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); Object o = frame.getTag(R.id.object_entry); webview.setOnTouchListener(new WebViewClickListener(frame, (Integer) o)); frame.addView(webview); } if (!rendered) { String appName = content.optString(PACKAGE_NAME); if (appName.contains(".")) { appName = appName.substring(appName.lastIndexOf(".") + 1); } String text = "Welcome to " + appName + "!"; TextView valueTV = new TextView(context); valueTV.setText(text); valueTV.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); valueTV.setGravity(Gravity.TOP | Gravity.LEFT); frame.addView(valueTV); } } public static Intent getLaunchIntent(Context context, SignedObj obj) { JSONObject content = obj.getJson(); if (DBG) Log.d(TAG, "Getting launch intent for " + content); Uri appFeed; if (content.has(DbObject.CHILD_FEED_NAME)) { Log.d(TAG, "using child feed"); appFeed = Feed.uriForName(content.optString(DbObject.CHILD_FEED_NAME)); } else { Log.d(TAG, "using obj feed"); appFeed = Feed.uriForName(content.optString(DbObjects.FEED_NAME)); } String arg = content.optString(ARG); String state = content.optString(STATE); String appId = obj.getAppId(); // TODO: Hack for deprecated launch method if (appId.equals(DungBeetleContentProvider.SUPER_APP_ID)) { appId = content.optString(PACKAGE_NAME); } if (DBG) Log.d(TAG, "Preparing launch of " + appId + " on " + appFeed); Intent launch = new Intent(); if (content.has(AppReferenceObj.OBJ_INTENT_ACTION)) { launch.setAction(content.optString(AppReferenceObj.OBJ_INTENT_ACTION)); } else { launch.setAction(Intent.ACTION_MAIN); } if (state != null) { launch.putExtra("mobisocial.db.STATE", state); } launch.addCategory(Intent.CATEGORY_LAUNCHER); launch.putExtra(AppState.EXTRA_FEED_URI, appFeed); // TODO: hack until this obj is available in 'related' query. launch.putExtra("obj", content.toString()); // TODO: this is better. launch.putExtra(Musubi.EXTRA_OBJ_HASH, obj.getHash()); if (arg != null) { launch.putExtra(AppState.EXTRA_APPLICATION_ARGUMENT, arg); } // TODO: optimize! List<ResolveInfo> resolved = context.getPackageManager().queryIntentActivities(launch, 0); for (ResolveInfo r : resolved) { ActivityInfo activity = r.activityInfo; if (activity.packageName.equals(appId)) { launch.setClassName(activity.packageName, activity.name); launch.putExtra("mobisocial.db.PACKAGE", activity.packageName); return launch; } } Intent market = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appId)); return market; } private JSONObject getAppState(Context context, JSONObject appReference) { if (appReference.has(DbObject.CHILD_FEED_NAME)) { String feedName = appReference.optString(DbObject.CHILD_FEED_NAME); Uri feedUri = Feed.uriForName(feedName); String selection = "type in ('" + TYPE + "')"; String[] projection = new String[] { "json" }; String order = "_id desc LIMIT 1"; Cursor c = context.getContentResolver().query(feedUri, projection, selection, null, order); try { if (c.moveToFirst()) { try { return new JSONObject(c.getString(0)); } catch (JSONException e) { Log.e(TAG, "not really json", e); } } } finally { c.close(); } } return null; } public interface Callback { public void onAppSelected(String pkg, String arg, Intent localLaunch); } private class WebViewClickListener implements View.OnTouchListener { private int position; private ViewGroup vg; private ViewGroup frame; private ListView lv; public WebViewClickListener(ViewGroup vg, int position) { this.vg = vg; this.position = position; } public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_CANCEL: return true; case MotionEvent.ACTION_UP: sendClick(); return true; } return false; } public void sendClick() { if (lv == null) { while (!(vg instanceof ListView)) { if (null != vg.getTag(R.id.object_entry)) { frame = vg; } vg = (ViewGroup) vg.getParent(); if (vg.performClick()) { return; } } lv = (ListView) vg; } lv.performItemClick(frame, position, 0); } } @Override public boolean doNotification(Context context, DbObj obj) { JSONObject json = obj.getJson(); if (json == null || !json.has("membership")) { return true; } try { String myId = App.instance().getLocalPersonId(); JSONArray arr = json.getJSONArray("membership"); for (int i = 0; i < arr.length(); i++) { if (myId.equals(arr.getString(i))) { return true; } } } catch (JSONException e) { } return false; } }