mobisocial.bento.ebento.io.EventManager.java Source code

Java tutorial

Introduction

Here is the source code for mobisocial.bento.ebento.io.EventManager.java

Source

/*
 * Copyright (C) 2012 Kazuya (Kaz) Yokoyama <kazuya.yokoyama@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package mobisocial.bento.ebento.io;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import mobisocial.bento.ebento.ui.EventListItem;
import mobisocial.bento.ebento.ui.PeopleListItem;
import mobisocial.bento.ebento.util.BitmapHelper;
import mobisocial.bento.ebento.util.UIUtils;
import mobisocial.socialkit.Obj;
import mobisocial.socialkit.musubi.DbFeed;
import mobisocial.socialkit.musubi.DbIdentity;
import mobisocial.socialkit.musubi.DbObj;
import mobisocial.socialkit.musubi.FeedObserver;
import mobisocial.socialkit.musubi.Musubi;
import mobisocial.socialkit.musubi.Musubi.DbThing;
import mobisocial.socialkit.musubi.multiplayer.FeedRenderable;
import mobisocial.socialkit.obj.MemObj;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Handler;
import android.util.Base64;
import android.util.Log;

public class EventManager {
    public interface OnStateUpdatedListener {
        public void onStateUpdated();
    }

    public interface OnInitialEventListener {
        public void onEventInitialized();
    }

    public static final String ANDROID_PACKAGE_NAME = "android_pkg";
    public static final String ANDROID_CLASS_NAME = "android_cls";
    public static final String ANDROID_ACTION = "android_action";
    public static final String TYPE_EBENTO = "ebento";
    public static final String TYPE_APPSTATE = "appstate";

    // root > state
    public static final String STATE = "state";
    // root > state > event
    public static final String VERSION_CODE = "version_code";
    public static final String EVENT = "event";
    public static final String EVENT_UUID = "uuid";
    public static final String EVENT_TITLE = "title";
    public static final String EVENT_PLACE = "place";
    public static final String EVENT_DETAILS = "details";
    public static final String EVENT_DATE_START = "date_start";
    public static final String EVENT_DATE_END = "date_end";
    public static final String EVENT_DATE_YEAR = "year";
    public static final String EVENT_DATE_MONTH = "month";
    public static final String EVENT_DATE_DAY = "day";
    public static final String EVENT_DATE_ALLDAY = "allday";
    public static final String EVENT_TIME_START = "time_start";
    public static final String EVENT_TIME_END = "time_end";
    public static final String EVENT_TIME_HOUR = "hour";
    public static final String EVENT_TIME_MINUTE = "minute";
    public static final String EVENT_HAS_IMAGE = "has_image";
    public static final String EVENT_CRE_DATE = "cre_date";
    public static final String EVENT_MOD_DATE = "mod_date";
    public static final String EVENT_CRE_CONTACT_ID = "cre_contact_id";
    public static final String EVENT_MOD_CONTACT_ID = "mod_contact_id";
    // root > state > people
    public static final String PEOPLE_LIST = "people_list";
    public static final String PEOPLE_CONTACT_ID = "contact_id";
    public static final String PEOPLE_STATE = "state";
    // root > event_image
    public static final String EVENT_IMAGE = "event_image";
    public static final String EVENT_IMAGE_UUID = "uuid";
    public static final String B64JPGTHUMB = FeedRenderable.OBJ_B64_JPEG;
    // root > initial
    public static final String EVENT_INIT = "event_init";

    private class LatestObj {
        public JSONObject json = null;
        public int intKey = 0;
    };

    private static final Boolean DEBUG = UIUtils.isDebugMode();
    private static final String TAG = "EventManager";
    private static EventManager sInstance = null;
    private Musubi mMusubi = null;
    private Uri mCurrentUri = null;
    private String mLocalContactId = null;
    private String mLocalName = null;
    private Integer mLastInt = 0;
    private int mVersionCode = 0;
    private boolean mbFromMusubi = false;

    private boolean mbInitialEvent = false;
    private OnInitialEventListener mInitEventListener = null;

    private Event mEvent = null;
    private ArrayList<EventListItem> mEventList = new ArrayList<EventListItem>();
    private ArrayList<PeopleListItem> mPeopleList = null;
    private Map<Long, ArrayList<String>> mMemberNameCache = new HashMap<Long, ArrayList<String>>();
    private ArrayList<OnStateUpdatedListener> mListenerList = new ArrayList<OnStateUpdatedListener>();

    // ----------------------------------------------------------
    // Instance
    // ----------------------------------------------------------
    private EventManager() {
        initData();
    }

    public static EventManager getInstance() {
        if (sInstance == null) {
            sInstance = new EventManager();
        }

        return sInstance;
    }

    public void init(Musubi musubi) {
        if (DEBUG)
            Log.d(TAG, "init()");

        // remove previous data
        if (mCurrentUri != null) {
            musubi.objForUri(mCurrentUri).getSubfeed().unregisterStateObserver(mStateObserver);
        }
        mCurrentUri = null;
        initData();
    }

    public void setMusubi(Musubi musubi, int versionCode) {
        if (DEBUG)
            Log.d(TAG, "setMusubi()");

        mMusubi = musubi;
        mVersionCode = versionCode;

        // start new one
        if (mMusubi.getObj() != null && mMusubi.getObj().getSubfeed() != null) {
            setEventObjUri(mMusubi.getObj().getUri());
        }
    }

    // ----------------------------------------------------------
    // Get / Retrieve
    // ----------------------------------------------------------
    synchronized public boolean hasEvent() {
        return (mEvent == null ? false : true);
    }

    synchronized public Event getEvent() {
        return mEvent;
    }

    synchronized public PeopleListItem getPeopleListItem(int position) {
        return mPeopleList.get(position);
    }

    synchronized public int getPeopleListCount() {
        return mPeopleList.size();
    }

    synchronized public String getLocalContactId() {
        return mLocalContactId;
    }

    synchronized public String getLocalName() {
        return mLocalName;
    }

    synchronized public int getLocalState() {
        int state = People.STATE_UNKNOWN;
        if (mPeopleList != null) {
            for (int i = 0; i < mPeopleList.size(); i++) {
                PeopleListItem item = mPeopleList.get(i);

                // skip header
                if (item.header.bHeader) {
                    continue;
                }

                if (mLocalContactId.equals(item.people.contactId)) {
                    state = item.people.state;
                    break;
                }
            }
        }
        return state;
    }

    synchronized public int getNumberOfState(int state) {
        int number = 0;
        for (int i = 0; i < mPeopleList.size(); i++) {
            PeopleListItem item = mPeopleList.get(i);
            if (item.header.state == state) {
                number = item.header.numberInHeader;
                break;
            }
        }
        return number;
    }

    synchronized public boolean isCreator() {
        boolean ret = true;
        if (mEvent != null) {
            ret = mLocalContactId.equals(mEvent.creContactId);
        }
        return ret;
    }

    // Event List
    synchronized public void loadEventList() {
        long prevFeedId = -1;
        String[] projection = new String[] { DbObj.COL_ID, DbObj.COL_FEED_ID };
        Uri uri = Musubi.uriForDir(DbThing.OBJECT);
        String selection = "type = ?";
        String[] selectionArgs = new String[] { TYPE_EBENTO };
        String sortOrder = DbObj.COL_FEED_ID + " asc, " + DbObj.COL_LAST_MODIFIED_TIMESTAMP + " asc";

        Cursor c = mMusubi.getContext().getContentResolver().query(uri, projection, selection, selectionArgs,
                sortOrder);

        ArrayList<EventListItem> tmpList = new ArrayList<EventListItem>();
        if (c != null && c.moveToFirst()) {
            mEventList = new ArrayList<EventListItem>();

            for (int i = 0; i < c.getCount(); i++) {
                EventListItem item = new EventListItem();
                item.event = new Event();

                DbObj dbObj = mMusubi.objForCursor(c);
                LatestObj latestObj = null;

                latestObj = fetchLatestObj(c.getLong(0));
                if (latestObj != null && latestObj.json.has(STATE)) {
                    JSONObject stateObj = latestObj.json.optJSONObject(STATE);
                    if (fetchEventObj(dbObj.getUri(), stateObj, item.event)) {
                        item.objUri = dbObj.getUri();
                        item.feedId = c.getLong(1);
                        if (DEBUG) {
                            Log.d(TAG, item.objUri.toString());
                            Log.d(TAG, item.feedId + "");
                        }
                        tmpList.add(0, item);

                        // load members
                        fetchMemberNames(item.feedId);
                    }
                }
                c.moveToNext();
            }
        }
        c.close();

        // insert dividers
        if (tmpList.size() > 0) {
            for (int j = 0; j < tmpList.size(); j++) {
                EventListItem item = tmpList.get(j);
                if (prevFeedId != item.feedId) {
                    EventListItem divider = new EventListItem();
                    divider.enabled = false;
                    divider.feedId = item.feedId;
                    mEventList.add(divider);

                    prevFeedId = item.feedId;
                }
                mEventList.add(item);
            }
        }

        if (DEBUG) {
            Log.d(TAG, "tmpList:" + tmpList.size() + " mEventList:" + mEventList.size());
        }
    }

    private LatestObj fetchLatestObj(long localId) {
        Uri uri = Musubi.uriForDir(DbThing.OBJECT);
        String[] projection = new String[] { DbObj.COL_JSON, DbObj.COL_INT_KEY };
        String selection = DbObj.COL_PARENT_ID + "=? and type= ?";
        String[] selectionArgs = new String[] { Long.toString(localId), TYPE_APPSTATE };
        String sortOrder = DbObj.COL_INT_KEY + " desc limit 1";
        Cursor c = mMusubi.getContext().getContentResolver().query(uri, projection, selection, selectionArgs,
                sortOrder);
        try {
            if (c.moveToFirst()) {
                LatestObj latestObj = new LatestObj();
                try {
                    latestObj.json = new JSONObject(c.getString(0));
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                latestObj.intKey = c.getInt(1);
                return latestObj;
            } else {
                c.close();
                selection = DbObj.COL_ID + "=?";
                selectionArgs = new String[] { Long.toString(localId) };
                c = mMusubi.getContext().getContentResolver().query(uri, projection, selection, selectionArgs,
                        sortOrder);
                if (c.moveToFirst()) {
                    LatestObj latestObj = new LatestObj();
                    try {
                        latestObj.json = new JSONObject(c.getString(0));
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    latestObj.intKey = c.getInt(1);
                    return latestObj;
                } else {
                    return null;
                }
            }
        } finally {
            c.close();
        }
    }

    synchronized public EventListItem getEventListItem(int position) {
        return mEventList.get(position);
    }

    synchronized public int getEventListCount() {
        return mEventList.size();
    }

    public ArrayList<String> getMemberNames(long feedId) {
        if (mMemberNameCache == null || !mMemberNameCache.containsKey(feedId)) {
            fetchMemberNames(feedId);
        }
        return mMemberNameCache.get(feedId);
    }

    private void fetchMemberNames(long feedId) {
        if (mMemberNameCache == null) {
            mMemberNameCache = new HashMap<Long, ArrayList<String>>();
        }

        if (!mMemberNameCache.containsKey(feedId)) {
            ArrayList<String> names = new ArrayList<String>();
            Uri feedUri = Musubi.uriForItem(DbThing.FEED, feedId);

            List<DbIdentity> members = mMusubi.getFeed(feedUri).getMembers();
            for (int i = 0; i < members.size(); i++) {
                DbIdentity id = members.get(i);
                if (id != null) {
                    if (!id.isOwned()) {
                        // skip me
                        names.add(id.getName());
                    }
                }
            }

            mMemberNameCache.put(feedId, names);
        }
    }

    // ----------------------------------------------------------
    // Update
    // ----------------------------------------------------------
    synchronized public void initEvent(Event event, String msg, OnInitialEventListener listener) {
        mbInitialEvent = true;
        mInitEventListener = listener;

        newData();
        updateEvent(event, msg, true);
    }

    synchronized public void createEvent(Event event, String msg) {
        updateEvent(event, msg, false);
    }

    synchronized public void updateEvent(Event event, String msg, boolean bFirst) {
        mEvent = event;

        String uuid = null;
        String data = null;
        if (mEvent.image != null) {
            uuid = mEvent.uuid;
            data = Base64.encodeToString(BitmapHelper.bitmapToBytes(mEvent.image), Base64.DEFAULT);
        }
        pushUpdate(msg, uuid, data, bFirst);
    }

    synchronized public void updatePeople(String contactId, int state, String msg) {
        PeopleListItem item = new PeopleListItem();
        item.people.contactId = contactId;
        item.people.state = state;

        updateToPeopleListWithState(contactId, state);

        pushUpdate(msg, false);
    }

    // ----------------------------------------------------------
    // Listener
    // ----------------------------------------------------------
    public void addListener(OnStateUpdatedListener listener) {
        mListenerList.add(listener);
    }

    public void removeListener(OnStateUpdatedListener listener) {
        mListenerList.remove(listener);
    }

    // ----------------------------------------------------------
    // Musubi
    // ----------------------------------------------------------
    public void setFromMusubi(boolean bFromMusubi) {
        mbFromMusubi = bFromMusubi;
    }

    public boolean isFromMusubi() {
        return mbFromMusubi;
    }

    public void setFeedUri(Uri feedUri) {
        mMusubi.setFeed(mMusubi.getFeed(feedUri));
    }

    public void pushUpdate(String msg, boolean bFirst) {
        pushUpdate(msg, null, null, bFirst);
    }

    public void pushUpdate(String msg, String eventUuid, String data, boolean bFirst) {
        try {
            JSONObject rootObj = new JSONObject();
            rootObj.put(Obj.FIELD_RENDER_TYPE, Obj.RENDER_LATEST);
            rootObj.put(STATE, getStateObj());
            if (DEBUG)
                Log.d(TAG, "pushUpdate - getStateObj():" + getStateObj().toString());

            JSONObject out = new JSONObject(rootObj.toString());

            // ignore data when initial posting
            if (!bFirst && eventUuid != null && data != null) {
                JSONObject eventImageObj = new JSONObject();
                eventImageObj.put(EVENT_IMAGE_UUID, eventUuid);
                out.put(EVENT_IMAGE, eventImageObj);
                out.put(B64JPGTHUMB, data);
            }

            // initial post
            if (bFirst) {
                out.put(EVENT_INIT, 1);
            }

            FeedRenderable renderable = FeedRenderable.fromText(msg);
            renderable.addToJson(out);

            if (bFirst) {
                Obj obj = new MemObj(TYPE_EBENTO, out, null);
                Uri eventUri = mMusubi.getFeed().insert(obj);
                setEventObjUri(eventUri);
            } else {
                Obj obj = new MemObj(TYPE_APPSTATE, out, null, ++mLastInt);
                mMusubi.objForUri(mCurrentUri).getSubfeed().postObj(obj);
            }
        } catch (JSONException e) {
            Log.e(TAG, "Failed to post JSON", e);
        }
    }

    public void setEventObjUri(Uri objUri) {
        // previous uri
        if (mCurrentUri != null) {
            mMusubi.objForUri(mCurrentUri).getSubfeed().unregisterStateObserver(mStateObserver);
        }

        // new uri
        mCurrentUri = objUri;

        DbObj dbObj = mMusubi.objForUri(objUri);
        Long localId = dbObj.getLocalId();

        LatestObj latestObj = null;
        latestObj = fetchLatestObj(localId);
        if (latestObj != null && latestObj.json.has(STATE)) {
            JSONObject stateObj = latestObj.json.optJSONObject(STATE);

            if (stateObj == null) {
                initData();
                mLastInt = 0;
            } else {
                setNewStateObj(stateObj);
                mLastInt = latestObj.intKey;
            }
        }

        DbFeed dbFeed = mMusubi.objForUri(mCurrentUri).getContainingFeed();
        List<DbIdentity> members = dbFeed.getMembers();
        for (DbIdentity member : members) {
            if (member.isOwned()) {
                mLocalContactId = member.getId();
                mLocalName = member.getName();
            }
        }
        if (DEBUG) {
            Log.d(TAG, "setEventObjUri");
            Log.d(TAG, "  mLocalContactId=" + mLocalContactId);
            Log.d(TAG, "  mLocalName=" + mLocalName);
        }

        mMusubi.objForUri(mCurrentUri).getSubfeed().registerStateObserver(mStateObserver);
    }

    private final FeedObserver mStateObserver = new FeedObserver() {
        @Override
        synchronized public void onUpdate(DbObj obj) {
            if (DEBUG)
                Log.d(TAG, "onUpdate:" + obj.toString());

            // ignore
            if (obj == null || obj.getJson() == null || !obj.getJson().has(STATE)) {
                if (DEBUG)
                    Log.d(TAG, "onUpdate: ignore-1");
                return;
            }

            // notify refresh (always)
            Handler handler = new Handler();
            handler.post(new Runnable() {
                public void run() {
                    for (OnStateUpdatedListener listener : mListenerList) {
                        listener.onStateUpdated();
                    }
                }
            });

            if (mEvent == null) {
                if (DEBUG)
                    Log.d(TAG, "onUpdate: ignore-2");
                return;
            }

            JSONObject stateObj = null;
            stateObj = obj.getJson().optJSONObject(STATE);
            try {
                if (!isValidEvent(stateObj.getJSONObject(EVENT).optString(EVENT_UUID))) {
                    if (DEBUG)
                        Log.d(TAG, "onUpdate: ignore-3");
                    return;
                }
            } catch (JSONException e) {
                Log.e(TAG, "Failed to get JSON", e);
                return;
            }

            // set new state
            setNewStateObj(stateObj);

            mLastInt = (obj.getIntKey() == null) ? 0 : obj.getIntKey();
            if (DEBUG)
                Log.d(TAG, "onUpdate - mLastInt: " + mLastInt);

            // initial post
            if (mbInitialEvent && obj.getJson().has(EVENT_INIT)) {
                mInitEventListener.onEventInitialized();
                mbInitialEvent = false;
            }
        }
    };

    // ----------------------------------------------------------
    // Private
    // ----------------------------------------------------------
    private void initData() {
        mEvent = null;
        mPeopleList = null;
        mMemberNameCache = null;
        mCurrentUri = null;
        mLocalContactId = null;
        mLocalName = null;
        mbInitialEvent = false;
        mInitEventListener = null;
    }

    private void newData() {
        mEvent = new Event();

        mPeopleList = new ArrayList<PeopleListItem>();
        mPeopleList.add(new PeopleListItem(People.STATE_YES));
        mPeopleList.add(new PeopleListItem(People.STATE_MAYBE));
        mPeopleList.add(new PeopleListItem(People.STATE_NO));
    }

    private void setNewStateObj(JSONObject stateObj) {
        newData();
        setNewEventObj(stateObj);
        setNewPeopleListObj(stateObj);
    }

    private void setNewEventObj(JSONObject stateObj) {
        fetchEventObj(mCurrentUri, stateObj, mEvent);
    }

    private boolean fetchEventObj(Uri objUri, JSONObject stateObj, Event event) {
        boolean ret = false;
        try {
            JSONObject eventObj = stateObj.getJSONObject(EVENT);
            event.uuid = eventObj.optString(EVENT_UUID);
            event.title = eventObj.optString(EVENT_TITLE);
            event.place = eventObj.optString(EVENT_PLACE);
            event.details = eventObj.optString(EVENT_DETAILS);

            JSONObject startDateObj = eventObj.getJSONObject(EVENT_DATE_START);
            event.startDate.year = startDateObj.optInt(EVENT_DATE_YEAR);
            event.startDate.month = startDateObj.optInt(EVENT_DATE_MONTH);
            event.startDate.day = startDateObj.optInt(EVENT_DATE_DAY);
            event.startDate.bAllDay = startDateObj.optBoolean(EVENT_DATE_ALLDAY);

            JSONObject endDateObj = eventObj.getJSONObject(EVENT_DATE_END);
            event.endDate.year = endDateObj.optInt(EVENT_DATE_YEAR);
            event.endDate.month = endDateObj.optInt(EVENT_DATE_MONTH);
            event.endDate.day = endDateObj.optInt(EVENT_DATE_DAY);
            event.endDate.bAllDay = endDateObj.optBoolean(EVENT_DATE_ALLDAY);

            JSONObject startTimeObj = eventObj.getJSONObject(EVENT_TIME_START);
            event.startTime.hour = startTimeObj.optInt(EVENT_TIME_HOUR);
            event.startTime.minute = startTimeObj.optInt(EVENT_TIME_MINUTE);

            JSONObject endTimeObj = eventObj.getJSONObject(EVENT_TIME_END);
            event.endTime.hour = endTimeObj.optInt(EVENT_TIME_HOUR);
            event.endTime.minute = endTimeObj.optInt(EVENT_TIME_MINUTE);

            event.creDateMillis = eventObj.optLong(EVENT_CRE_DATE);
            event.modDateMillis = eventObj.optLong(EVENT_MOD_DATE);
            event.creContactId = eventObj.optString(EVENT_CRE_CONTACT_ID);
            event.modContactId = eventObj.optString(EVENT_MOD_CONTACT_ID);
            DbFeed dbFeed = mMusubi.objForUri(objUri).getSubfeed();
            try {
                DbIdentity id = dbFeed.userForGlobalId(event.creContactId);
                event.creatorName = (id != null ? id.getName() : "");
            } catch (Exception e) {
                event.creatorName = "";
            }

            boolean bImage = eventObj.optBoolean(EVENT_HAS_IMAGE);
            if (bImage) {
                event.image = getEventImage(objUri, event.uuid);
            } else {
                event.image = null;
            }

            ret = true;
        } catch (JSONException e) {
            Log.e(TAG, "Failed to get JSON", e);
        }

        return ret;
    }

    private void setNewPeopleListObj(JSONObject stateObj) {
        try {
            JSONArray peopleListArray = stateObj.optJSONArray(PEOPLE_LIST);

            if (peopleListArray != null) {
                for (int i = 0; i < peopleListArray.length(); i++) {
                    JSONObject peopleObj = peopleListArray.getJSONObject(i);
                    People people = new People();
                    people.contactId = peopleObj.optString(PEOPLE_CONTACT_ID);
                    people.state = peopleObj.optInt(PEOPLE_STATE);

                    PeopleListItem item = new PeopleListItem();
                    item.people = people;
                    DbFeed dbFeed = mMusubi.objForUri(mCurrentUri).getSubfeed();
                    DbIdentity id = dbFeed.userForGlobalId(people.contactId);
                    item.image = id.getPicture();
                    item.name = id.getName();

                    insertToPeopleListWithState(item);
                }
            }

        } catch (JSONException e) {
            Log.e(TAG, "Failed to get JSON", e);
        }
    }

    private void insertToPeopleListWithState(PeopleListItem addItem) {
        int insertIndex = 0;
        for (int i = 0; i < mPeopleList.size(); i++) {
            PeopleListItem item = mPeopleList.get(i);
            if (item.header.state == addItem.people.state) {
                insertIndex = i + 1;
                item.header.numberInHeader++;
                break;
            }
        }
        mPeopleList.add(insertIndex, addItem);
    }

    private void updateToPeopleListWithState(String contactId, int state) {
        boolean bRemoved = false;
        boolean bFound = false;
        int headerIndex = 0;

        for (int i = 0; i < mPeopleList.size(); i++) {
            PeopleListItem item = mPeopleList.get(i);

            // skip header
            if (item.header.bHeader) {
                headerIndex = i;
                continue;
            }

            // remove
            if (contactId.equals(item.people.contactId)) {
                if (item.people.state != state) {
                    mPeopleList.remove(i);
                    mPeopleList.get(headerIndex).header.numberInHeader--;
                    bRemoved = true;
                }

                bFound = true;
                break;
            }
        }

        // removed and new face
        if (bRemoved | !bFound) {
            PeopleListItem item = new PeopleListItem();
            item.people.contactId = contactId;
            item.people.state = state;
            insertToPeopleListWithState(item);
        }
    }

    private Bitmap getEventImage(Uri objUri, String eventUuid) {
        Bitmap bitmap = null;
        int targetWidth = BitmapHelper.MAX_IMAGE_WIDTH;
        int targetHeight = BitmapHelper.MAX_IMAGE_HEIGHT;
        float degrees = 0;

        DbFeed dbFeed = mMusubi.objForUri(objUri).getSubfeed();

        Cursor c = dbFeed.query();
        int numberOfObjInSubFeed = c.getCount();
        c.moveToFirst();
        for (int i = 0; i < c.getCount(); i++) {
            Obj object = mMusubi.objForCursor(c);
            bitmap = parseEventImage(object, eventUuid);
            if (bitmap != null)
                break;
            c.moveToNext();
        }
        c.close();

        // if no images in subfeed
        if (numberOfObjInSubFeed == 0 && bitmap == null) {
            bitmap = parseEventImage(mMusubi.objForUri(objUri), eventUuid);
        }

        // dummy
        if (bitmap == null) {
            bitmap = BitmapHelper.getDummyBitmap(targetWidth, targetHeight);
        } else {
            bitmap = BitmapHelper.getResizedBitmap(bitmap, targetWidth, targetHeight, degrees);
        }

        return bitmap;
    }

    private Bitmap parseEventImage(Obj object, String eventUuid) {
        Bitmap bitmap = null;
        if (object != null && object.getJson() != null && object.getJson().has(EVENT_IMAGE)) {
            JSONObject imageObj = object.getJson().optJSONObject(EVENT_IMAGE);
            if (eventUuid.equals(imageObj.optString(EVENT_IMAGE_UUID))) {
                byte[] byteArray = Base64.decode(object.getJson().optString(B64JPGTHUMB), Base64.DEFAULT);
                bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
            }
        }
        return bitmap;
    }

    private JSONObject getEventObj() {
        JSONObject eventObj = new JSONObject();
        try {
            eventObj.put(EVENT_UUID, mEvent.uuid);
            eventObj.put(EVENT_TITLE, mEvent.title);
            eventObj.put(EVENT_PLACE, mEvent.place);
            eventObj.put(EVENT_DETAILS, mEvent.details);

            JSONObject startDateObj = new JSONObject();
            startDateObj.put(EVENT_DATE_YEAR, mEvent.startDate.year);
            startDateObj.put(EVENT_DATE_MONTH, mEvent.startDate.month);
            startDateObj.put(EVENT_DATE_DAY, mEvent.startDate.day);
            startDateObj.put(EVENT_DATE_ALLDAY, mEvent.startDate.bAllDay);
            eventObj.put(EVENT_DATE_START, startDateObj);

            JSONObject endDateObj = new JSONObject();
            endDateObj.put(EVENT_DATE_YEAR, mEvent.endDate.year);
            endDateObj.put(EVENT_DATE_MONTH, mEvent.endDate.month);
            endDateObj.put(EVENT_DATE_DAY, mEvent.endDate.day);
            endDateObj.put(EVENT_DATE_ALLDAY, mEvent.endDate.bAllDay);
            eventObj.put(EVENT_DATE_END, endDateObj);

            JSONObject startTimeObj = new JSONObject();
            startTimeObj.put(EVENT_TIME_HOUR, mEvent.startTime.hour);
            startTimeObj.put(EVENT_TIME_MINUTE, mEvent.startTime.minute);
            eventObj.put(EVENT_TIME_START, startTimeObj);

            JSONObject endTimeObj = new JSONObject();
            endTimeObj.put(EVENT_TIME_HOUR, mEvent.endTime.hour);
            endTimeObj.put(EVENT_TIME_MINUTE, mEvent.endTime.minute);
            eventObj.put(EVENT_TIME_END, endTimeObj);

            eventObj.put(EVENT_CRE_DATE, mEvent.creDateMillis);
            eventObj.put(EVENT_MOD_DATE, mEvent.modDateMillis);
            eventObj.put(EVENT_CRE_CONTACT_ID, mEvent.creContactId);
            eventObj.put(EVENT_MOD_CONTACT_ID, mEvent.modContactId);

            eventObj.put(EVENT_HAS_IMAGE, (mEvent.image == null ? false : true));

        } catch (JSONException e) {
            Log.e(TAG, "Failed to put JSON", e);
        }

        return eventObj;
    }

    private JSONArray getPeopleListArray() {
        JSONArray peopleListArray = new JSONArray();

        try {

            for (int i = 0; i < mPeopleList.size(); i++) {
                PeopleListItem item = mPeopleList.get(i);

                // skip header
                if (item.header.bHeader) {
                    continue;
                }

                JSONObject peopleObj = new JSONObject();
                peopleObj.put(PEOPLE_CONTACT_ID, item.people.contactId);
                peopleObj.put(PEOPLE_STATE, item.people.state);

                peopleListArray.put(peopleObj);
            }

        } catch (JSONException e) {
            Log.e(TAG, "Failed to put JSON", e);
        }

        return peopleListArray;
    }

    private JSONObject getStateObj() {
        JSONObject stateObj = new JSONObject();
        try {
            stateObj.put(VERSION_CODE, mVersionCode);
            JSONObject eventObj = getEventObj();
            stateObj.put(EVENT, eventObj);
            JSONArray peopleListArray = getPeopleListArray();
            stateObj.put(PEOPLE_LIST, peopleListArray);
        } catch (JSONException e) {
            Log.e(TAG, "Failed to put JSON", e);
        }
        return stateObj;
    }

    private boolean isValidEvent(String uuid) {
        return (mEvent != null && mEvent.uuid != null && uuid != null && mEvent.uuid.equals(uuid));
    }

}