mobisocial.socialkit.musubi.Musubi.java Source code

Java tutorial

Introduction

Here is the source code for mobisocial.socialkit.musubi.Musubi.java

Source

/*
 * Copyright (C) 2011 The Stanford MobiSocial Laboratory
 *
 * 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.socialkit.musubi;

import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;

import mobisocial.socialkit.SQLClauseHelper;

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

import android.app.Activity;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;

/**
 * Use the Musubi APIs in your application.
 */
public class Musubi {
    public static final String TAG = "SocialKit-DB";
    static boolean DBG = true;

    /**
     * The name of the Musubi content provider.
     */
    public static final String AUTHORITY = "org.musubi.db";
    private static final String SUPER_APP_ID = "mobisocial.musubi";
    public static final String EXTRA_FEED_URI = "feedUri";
    public static final String EXTRA_OBJ_URI = "objUri";

    private final Context mContext;
    private final ContentProviderThread mContentProviderThread;
    private DbFeed mFeed;
    private DbObj mObj;

    private static final Uri CONTACTS_URI = Uri.parse("content://" + AUTHORITY + "/identities");
    private static final LinkedHashMap<Long, DbIdentity> sUserCache = new UserCache();
    private final ContentObserver mContactUpdateObserver;

    public static boolean isMusubiInstalled(Context context) {
        try {
            final Intent intent = new Intent(Intent.ACTION_MAIN);
            intent.addCategory(Intent.CATEGORY_LAUNCHER);
            intent.setPackage(SUPER_APP_ID);
            return context.getPackageManager().queryIntentActivities(intent, 0).size() > 0;
        } catch (Throwable t) {
            return false;
        }
    }

    public DbObj objFromIntent(Intent intent) {
        Log.d(TAG, "fetching obj from " + intent);
        if (intent.hasExtra(EXTRA_OBJ_URI)) {
            try {
                Uri objUri = intent.getParcelableExtra(EXTRA_OBJ_URI);
                return objForUri(objUri);
            } catch (Exception e) {
                if (DBG)
                    Log.e(TAG, "couldnt get obj from uri", e);
            }
        }
        if (intent.getType() != null && intent.getType().startsWith("vnd.musubi.obj/")) {
            if (intent.getData() != null) {
                return objForUri(intent.getData());
            }
        }
        if (DBG)
            Log.e(TAG, "no obj found");
        return null;
    }

    public static boolean isMusubiIntent(Intent intent) {
        return intent.hasExtra(EXTRA_FEED_URI) || intent.hasExtra(EXTRA_OBJ_URI);
    }

    public Musubi(Context context) {
        mContext = context.getApplicationContext();
        if (context instanceof Activity) {
            setDataFromIntent(((Activity) context).getIntent());
        }
        mContentProviderThread = new ContentProviderThread();
        mContentProviderThread.start();
        if (mContentProviderThread.mHandler == null) {
            synchronized (this) {
                while (mContentProviderThread.mHandler == null) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        continue;
                    }
                }
            }
        }

        mContactUpdateObserver = new ContentObserver(mContentProviderThread.mHandler) {
            @Override
            public void onChange(boolean selfChange) {
                sUserCache.clear();
            }
        };
        mContext.getContentResolver().registerContentObserver(CONTACTS_URI, true, mContactUpdateObserver);
    }

    ContentProviderThread getContentProviderThread() {
        return mContentProviderThread;
    }

    /**
     * @hide
     */
    public Context getContext() {
        return mContext;
    }

    public static Intent getMarketIntent() {
        Uri marketUri = Uri.parse("market://details?id=" + SUPER_APP_ID);
        return new Intent(Intent.ACTION_VIEW, marketUri);
    }

    public DbFeed getFeed(Uri feedUri) {
        return new DbFeed(this, feedUri);
    }

    public void setDataFromIntent(Intent intent) {
        if (intent.hasExtra(EXTRA_FEED_URI)) {
            mFeed = new DbFeed(this, (Uri) intent.getParcelableExtra(EXTRA_FEED_URI));
        }
        if (mObj == null) {
            if (intent.hasExtra(EXTRA_OBJ_URI)) {
                mObj = objForUri((Uri) intent.getParcelableExtra(EXTRA_OBJ_URI));
            } else {
                mObj = objForUri(intent.getData());
            }
        }
    }

    public static Musubi forIntent(Context context, Intent intent) {
        Musubi m = new Musubi(context);
        if (intent != null) {
            m.setDataFromIntent(intent);
        }
        return m;
    }

    public static Musubi getInstance(Context context) {
        return forIntent(context, null);
    }

    public void setFeed(DbFeed feed) {
        mFeed = feed;
    }

    public DbFeed getFeed() {
        return mFeed;
    }

    public DbObj getObj() {
        return mObj;
    }

    public DbObj objForCursor(Cursor cursor) {
        try {
            long localId = -1;
            String appId = null;
            String type = null;
            String name = null;
            JSONObject json = null;
            long senderId = -1;
            byte[] hash = null;
            long feedId = -1;
            Integer intKey = null;
            long timestamp = -1;
            Long parentId = null;

            try {
                localId = cursor.getLong(cursor.getColumnIndexOrThrow(DbObj.COL_ID));
            } catch (IllegalArgumentException e) {
            }
            try {
                appId = cursor.getString(cursor.getColumnIndexOrThrow(DbObj.COL_APP_ID));
            } catch (IllegalArgumentException e) {
            }
            try {
                type = cursor.getString(cursor.getColumnIndexOrThrow(DbObj.COL_TYPE));
            } catch (IllegalArgumentException e) {
            }
            try {
                name = cursor.getString(cursor.getColumnIndexOrThrow(DbObj.COL_STRING_KEY));
            } catch (IllegalArgumentException e) {
            }
            try {
                json = new JSONObject(cursor.getString(cursor.getColumnIndexOrThrow(DbObj.COL_JSON)));
            } catch (IllegalArgumentException e) {
            }
            try {
                senderId = cursor.getLong(cursor.getColumnIndexOrThrow(DbObj.COL_IDENTITY_ID));
            } catch (IllegalArgumentException e) {
            }
            try {
                hash = cursor.getBlob(cursor.getColumnIndexOrThrow(DbObj.COL_UNIVERSAL_HASH));
            } catch (IllegalArgumentException e) {
            }
            try {
                feedId = cursor.getLong(cursor.getColumnIndexOrThrow(DbObj.COL_FEED_ID));
            } catch (IllegalArgumentException e) {
            }
            try {
                intKey = cursor.getInt(cursor.getColumnIndexOrThrow(DbObj.COL_INT_KEY));
            } catch (IllegalArgumentException e) {
            }
            try {
                timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(DbObj.COL_TIMESTAMP));
            } catch (IllegalArgumentException e) {
            }
            try {
                int pIndex = cursor.getColumnIndexOrThrow(DbObj.COL_PARENT_ID);
                if (!cursor.isNull(pIndex)) {
                    parentId = cursor.getLong(pIndex);
                }
            } catch (IllegalArgumentException e) {
            }

            // Don't require raw field.
            final byte[] raw;
            int rawIndex = cursor.getColumnIndex(DbObj.COL_RAW);
            if (rawIndex == -1 || cursor.isNull(rawIndex)) {
                raw = null;
            } else {
                raw = cursor.getBlob(cursor.getColumnIndexOrThrow(DbObj.COL_RAW));
            }
            return new DbObj(this, appId, feedId, parentId, senderId, localId, type, json, raw, intKey, name,
                    timestamp, hash);
        } catch (JSONException e) {
            Log.e(TAG, "Couldn't parse obj.", e);
            return null;
        }
    }

    public DbObj objForId(long localId) {
        Cursor cursor = mContext.getContentResolver().query(DbObj.OBJ_URI,
                new String[] { DbObj.COL_APP_ID, DbObj.COL_TYPE, DbObj.COL_STRING_KEY, DbObj.COL_JSON,
                        DbObj.COL_RAW, DbObj.COL_IDENTITY_ID, DbObj.COL_UNIVERSAL_HASH, DbObj.COL_FEED_ID,
                        DbObj.COL_INT_KEY, DbObj.COL_TIMESTAMP, DbObj.COL_PARENT_ID },
                DbObj.COL_ID + " = ?", new String[] { String.valueOf(localId) }, null);
        try {
            if (cursor == null || !cursor.moveToFirst()) {
                Log.w(TAG, "Obj " + localId + " not found.");
                return null;
            }

            final String appId = cursor.getString(0);
            final String type = cursor.getString(1);
            final String name = cursor.getString(2);
            final JSONObject json = new JSONObject(cursor.getString(3));
            final byte[] raw = cursor.isNull(4) ? null : cursor.getBlob(4);
            final long senderId = cursor.getLong(5);
            final byte[] hash = cursor.getBlob(6);
            final long feedId = cursor.getLong(7);
            final Integer intKey = cursor.isNull(8) ? null : cursor.getInt(8);
            final long timestamp = cursor.getLong(9);
            Long parentId = cursor.isNull(10) ? null : cursor.getLong(10);

            return new DbObj(this, appId, feedId, parentId, senderId, localId, type, json, raw, intKey, name,
                    timestamp, hash);
        } catch (JSONException e) {
            Log.e(TAG, "Couldn't parse obj.", e);
            return null;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    public DbObj objForUri(Uri objUri) {
        try {
            return objForId(ContentUris.parseId(objUri));
        } catch (Exception e) {
            if (DBG)
                Log.e(TAG, "Bad uri " + objUri);
            return null;
        }
    }

    public DbIdentity userForGlobalId(Uri feedUri, String personId) {
        byte[] idHash = MusubiUtil.convertToByteArray(personId);
        long shortHash = MusubiUtil.shortHash(idHash);

        Uri uri = new Uri.Builder().scheme("content").authority(Musubi.AUTHORITY)
                .appendPath(DbThing.MEMBER.toString()).appendPath(feedUri.getLastPathSegment()).build();
        String selection = DbIdentity.COL_ID_SHORT_HASH + " = ?";
        String[] selectionArgs = new String[] { Long.toString(shortHash) };
        String sortOrder = null;
        Cursor c = mContext.getContentResolver().query(uri, DbIdentity.COLUMNS, selection, selectionArgs,
                sortOrder);
        try {
            while (c != null && c.moveToNext()) {
                DbIdentity mate = DbIdentity.fromStandardCursor(mContext, c);
                if (mate.getId().equals(personId)) {
                    return mate;
                }
            }
            Log.e(TAG, "id not found #" + shortHash);
            return null;
        } finally {
            if (c != null) {
                c.close();
            }
        }
    }

    public DbIdentity userForLocalId(Uri feedUri, long localId) {
        DbIdentity cachedUser = sUserCache.get(localId);
        if (cachedUser != null) {
            return cachedUser;
        }

        String feedName;
        if (feedUri != null) {
            feedName = feedUri.getLastPathSegment();
        } else {
            feedName = "friend";
        }
        Uri uri = uriForItem(DbThing.IDENTITY, localId);
        String[] projection = { DbIdentity.COL_ID_HASH, DbIdentity.COL_NAME };
        String selection = DbIdentity.COL_IDENTITY_ID + " = ?";
        String[] selectionArgs = new String[] { Long.toString(localId) };
        String sortOrder = null;
        Cursor c = mContext.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
        if (c == null) {
            Log.w(Musubi.TAG, "Null cursor for user query " + localId);
            return null;
        }
        try {
            if (!c.moveToFirst()) {
                Log.w(Musubi.TAG, "No user found for " + localId + " in " + feedName, new Throwable());
                return null;
            }

            DbIdentity user = DbIdentity.fromStandardCursor(mContext, c);
            sUserCache.put(localId, user);
            return user;
        } finally {
            c.close();
        }
    }

    /**
     * Returns the DbUser that is currently logged in to this app.
     */
    public DbIdentity userForLocalDevice(Uri feedUri) {
        Uri uri;
        if (feedUri == null) {
            uri = uriForDir(DbThing.IDENTITY);
        } else {
            long feedId = ContentUris.parseId(feedUri);
            uri = uriForItem(DbThing.MEMBER, feedId);
        }
        String selection = DbIdentity.COL_OWNED + " = 1";
        String[] selectionArgs = null;
        String sortOrder = null;
        Cursor c = mContext.getContentResolver().query(uri, DbIdentity.COLUMNS, selection, selectionArgs,
                sortOrder);
        try {
            if (c == null || !c.moveToFirst()) {
                Log.w(TAG, "no local user for feed " + feedUri, new Throwable());
                return null;
            }
            DbIdentity id = DbIdentity.fromStandardCursor(mContext, c);
            return id;
        } finally {
            if (c != null) {
                c.close();
            }
        }
    }

    /**
     * Returns all DbIdentities owned by the current user
     */
    public List<DbIdentity> users(Uri feedUri) {
        Uri uri;
        if (feedUri == null) {
            uri = uriForDir(DbThing.IDENTITY);
        } else {
            long feedId = ContentUris.parseId(feedUri);
            uri = uriForItem(DbThing.MEMBER, feedId);
        }
        String selection = DbIdentity.COL_OWNED + " = 1";
        String[] selectionArgs = null;
        String sortOrder = null;
        Cursor c = mContext.getContentResolver().query(uri, DbIdentity.COLUMNS, selection, selectionArgs,
                sortOrder);
        try {
            List<DbIdentity> identities = new LinkedList<DbIdentity>();
            if (c == null || c.getCount() == 0) {
                Log.w(TAG, "no local user for feed " + feedUri, new Throwable());
                return identities;
            }
            while (c.moveToNext()) {
                DbIdentity id = DbIdentity.fromStandardCursor(mContext, c);
                identities.add(id);
            }
            return identities;
        } finally {
            if (c != null) {
                c.close();
            }
        }
    }

    public Cursor queryAppData(String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        String pkg = mContext.getPackageName();
        selection = SQLClauseHelper.andClauses(selection, DbObj.COL_APP_ID + "=?");
        selectionArgs = SQLClauseHelper.andArguments(selectionArgs, pkg);
        Uri uri = uriForDir(DbThing.OBJECT);
        return mContext.getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);
    }

    class ContentProviderThread extends Thread {
        public Handler mHandler;

        private Handler createHandler() {
            return new Handler() {
                public void handleMessage(Message msg) {
                    Insertion i = (Insertion) msg.obj;
                    mContext.getContentResolver().insert(i.uri, i.cv);
                }
            };
        }

        public void run() {
            Looper.prepare();

            synchronized (Musubi.this) {
                mHandler = createHandler();
                Musubi.this.notify();
            }

            Looper.loop();
        }

        public void insert(Uri uri, ContentValues cv) {
            Insertion i = new Insertion(uri, cv);
            Message m = mHandler.obtainMessage();
            m.obj = i;
            mHandler.sendMessage(m);
        }

        public Uri insertSync(Uri uri, ContentValues cv) {
            return mContext.getContentResolver().insert(uri, cv);
        }

        private class Insertion {
            Uri uri;

            ContentValues cv;

            public Insertion(Uri uri, ContentValues cv) {
                this.uri = uri;
                this.cv = cv;
            }
        }
    }

    @SuppressWarnings("serial")
    private static class UserCache extends LinkedHashMap<Long, DbIdentity> {
        private static final int MAX_ENTRIES = 10;

        public UserCache() {
            super(10, 0.75f, true);
        }

        @Override
        protected boolean removeEldestEntry(java.util.Map.Entry<Long, DbIdentity> eldest) {
            return size() > MAX_ENTRIES;
        }
    }

    /**
     * Connects to a real-time multi-way, globally ordered data stream
     * associated with the given object.
     */
    /*
    public Junction junctionForObj(JunctionActor actor, DbObj obj)
        throws JunctionException {
    String uid = obj.getUri().getLastPathSegment();
    uid = uid.replace("^", "_").replace(":", "_");
    Uri uri = new Uri.Builder().scheme("junction")
            .authority("sb.openjunction.org")
            .appendPath("dbf-" + uid).build();
    return AndroidJunctionMaker.bind(uri, actor);
    }*/

    public static Uri uriForItem(DbThing type, long id) {
        return new Uri.Builder().scheme("content").authority(AUTHORITY).appendPath(type.toString())
                .appendPath("" + id).build();
    }

    public static Uri uriForDir(DbThing type) {
        return new Uri.Builder().scheme("content").authority(AUTHORITY).appendPath(type.toString()).build();
    }

    public static String mimeTypeFor(String musubiType) {
        return "vnd.musubi.obj/" + musubiType;
    }

    public enum DbThing {
        OBJECT, FEED, IDENTITY, MEMBER;

        @Override
        public String toString() {
            switch (this) {
            case OBJECT:
                return "objects";
            case FEED:
                return "feeds";
            case IDENTITY:
                return "identities";
            case MEMBER:
                return "feed_members";
            default:
                return null;
            }
        }
    }
}