mobisocial.musubi.ui.FeedListActivity.java Source code

Java tutorial

Introduction

Here is the source code for mobisocial.musubi.ui.FeedListActivity.java

Source

/*
 * Copyright 2012 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.musubi.ui;

import java.util.BitSet;
import java.util.List;

import mobisocial.crypto.IBHashedIdentity.Authority;
import mobisocial.musubi.App;
import mobisocial.musubi.R;
import mobisocial.musubi.model.MIdentity;
import mobisocial.musubi.model.helpers.IdentitiesManager;
import mobisocial.musubi.objects.AppObj;
import mobisocial.musubi.provider.MusubiContentProvider;
import mobisocial.musubi.provider.MusubiContentProvider.Provided;
import mobisocial.musubi.service.WizardStepHandler;
import mobisocial.musubi.ui.fragments.FeedListFragment;
import mobisocial.musubi.ui.fragments.FeedViewFragment;
import mobisocial.musubi.util.LessSpammyContentObserver;
import mobisocial.nfc.NdefFactory;
import mobisocial.nfc.NdefHandler;
import mobisocial.nfc.Nfc;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.nfc.FormatException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.Menu;
import android.support.v4.view.MenuItem;
import android.util.Log;
import android.view.MenuInflater;
import android.widget.Toast;

/**
 * Displays a list of all user-accessible threads (feeds).
 */
public class FeedListActivity extends MusubiBaseActivity implements FeedListFragment.OnFeedSelectedListener {

    public static final boolean DBG = true;
    public static final String TAG = "DungBeetleActivity";
    public static final String SHARE_SCHEME = "db-share-contact";
    public static final String GROUP_SESSION_SCHEME = "dungbeetle-group-session";
    public static final String GROUP_SCHEME = "dungbeetle-group";
    public static final String AUTO_UPDATE_URL_BASE = "http://mobisocial.stanford.edu/files";
    public static final String AUTO_UPDATE_METADATA_FILE = "dungbeetle_version.json";
    public static final String AUTO_UPDATE_APK_FILE = "dungbeetle-debug.apk";

    public static final int DIALOG_PLZ_LINK_ACCCOUNT = 1;

    private Nfc mNfc;
    private FeedListFragment mFeedListFragment;
    private static final String FRAGMENT_FEED_ACTIONS = "feedActions";
    private boolean mLoaded = false;
    private boolean mDualPane;
    private IdentitiesManager mIdentitiesManager;
    private LessSpammyContentObserver mObserver;
    private Menu mMenu;
    private boolean mSavedInstance;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SQLiteOpenHelper databaseSource = App.getDatabaseSource(this);
        mIdentitiesManager = new IdentitiesManager(databaseSource);
        // TODO: NfcFragment
        mNfc = new Nfc(this);

        SharedPreferences p = getSharedPreferences(WizardStepHandler.WIZARD_PREFS_NAME, 0);
        boolean doRestore = p.getBoolean(WizardStepHandler.DO_RESTORE, WizardStepHandler.DO_RESTORE_DEFAULT);
        if (doRestore) {
            WizardStepHandler.restoreStepsFromDatabase(this);
        }

        // TODO: Hack.
        try {
            if (getIntent().hasExtra(AppObj.EXTRA_APPLICATION_ARGUMENT)) {
                getIntent().setData(Uri.parse(getIntent().getStringExtra(AppObj.EXTRA_APPLICATION_ARGUMENT)));
            }
        } catch (ClassCastException e) {
        }

        setContentView(R.layout.activity_feed_list);
        mFeedListFragment = new FeedListFragment();
        mDualPane = (null != findViewById(R.id.feed_view));
        if (mDualPane) {
            Bundle b = new Bundle();
            b.putBoolean(FeedListFragment.DUAL_PANE, true);
            mFeedListFragment.setArguments(b);
        }
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction().replace(R.id.feed_list, mFeedListFragment).commit();
        }

        // TODO: Combine doHandleInput calls in onNewIntent.
        doHandleInput(getIntent().getData());
        mNfc.addNdefHandler(new NdefHandler() {
            public int handleNdef(final NdefMessage[] messages) {
                FeedListActivity.this.runOnUiThread(new Runnable() {
                    public void run() {
                        Log.d(TAG, "Handling ndef uri: " + uriFromNdef(messages));
                        doHandleInput(uriFromNdef(messages));
                    }
                });
                return NDEF_CONSUME;
            }
        });

        mNfc.setOnTagWriteListener(new Nfc.OnTagWriteListener() {
            public void onTagWrite(final int status) {
                FeedListActivity.this.runOnUiThread(new Runnable() {
                    public void run() {
                        if (status == WRITE_OK) {
                            Toast.makeText(FeedListActivity.this, "Wrote successfully!", Toast.LENGTH_SHORT).show();
                        } else if (status == WRITE_ERROR_READ_ONLY) {
                            Toast.makeText(FeedListActivity.this, "Can't write read-only tag!", Toast.LENGTH_SHORT)
                                    .show();
                        } else {
                            Toast.makeText(FeedListActivity.this, "Failed to write!", Toast.LENGTH_SHORT).show();
                        }
                        pushContactInfoViaNfc();
                    }
                });
            }
        });

        mObserver = new LessSpammyContentObserver(new Handler(getMainLooper())) {
            @Override
            public void lessSpammyOnChange(boolean arg0) {
                fillInCounter();
            }
        };
        //rerun it whenever something about a feed changes, because feed creation/modification implies graylist alteration
        getContentResolver().registerContentObserver(MusubiContentProvider.uriForDir(Provided.FEEDS), false,
                mObserver);

        if (mDualPane && getIntent() != null && getIntent().getData() != null) {
            Uri data = getIntent().getData();
            if (data.toString().startsWith(MusubiContentProvider.uriForDir(Provided.FEEDS_ID).toString())) {
                onFeedSelected(data);
            }
        }
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case DIALOG_PLZ_LINK_ACCCOUNT:
            final Activity activity = FeedListActivity.this;
            AlertDialog.Builder builder = new AlertDialog.Builder(activity);
            //name = cursor.getString(cursor.getColumnIndexOrThrow(Contact.NAME));
            builder.setMessage(
                    "Before you can send any messages to your friends, you'll need to link to your Google or Facebook account. Would you like to do that now?")
                    .setCancelable(true).setPositiveButton("Link Account", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            Intent intent = new Intent(activity, SettingsActivity.class);
                            intent.putExtra(SettingsActivity.ACTION,
                                    SettingsActivity.SettingsAction.ACCOUNT.toString());
                            startActivity(intent);
                        }
                    }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });
            return builder.create();
        }
        return null;
    }

    /* Creates the menu items */
    public boolean onCreateOptionsMenu(android.support.v4.view.Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.feed_list_activity, menu);
        mMenu = menu;
        fillInCounter();
        return true;
    }

    class FillInCounter extends AsyncTask<Void, Void, Void> {
        int mCount;
        MenuItem mRelationshipItem;

        public FillInCounter() {
            //grab it now to avoid threading woes
            mRelationshipItem = (MenuItem) mMenu.findItem(R.id.menu_relationships);
        }

        @Override
        protected Void doInBackground(Void... params) {
            mCount = mIdentitiesManager.getPendingGraylistCount();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.ic_menu_allfriends);
            icon.setDensity(Bitmap.DENSITY_NONE);
            Bitmap dn = icon.copy(icon.getConfig(), true);
            if (mCount > 0) {
                Canvas c = new Canvas(dn);

                Paint p = new Paint();
                p.setAntiAlias(true);
                p.setStrokeWidth(1);
                p.setStyle(Style.FILL_AND_STROKE);
                p.setColor(Color.rgb(180, 0, 1));

                RectF bubble = new RectF(dn.getWidth() / 2, dn.getHeight() / 2, dn.getWidth(), dn.getHeight());
                c.drawRoundRect(bubble, 5, 5, p);
                p.setColor(Color.rgb(255, 255, 255));
                p.setTextAlign(Align.CENTER);
                p.setTextSize(bubble.height() * 2 / 3);
                c.drawText("" + mCount, bubble.centerX(), bubble.centerY() + bubble.height() * 1 / 4, p);
            }
            mRelationshipItem.setIcon(new BitmapDrawable(dn));
        }
    }

    private void fillInCounter() {
        if (mMenu == null)
            return;
        new FillInCounter().execute();
    }

    /* Handles item selections */
    public boolean onOptionsItemSelected(android.support.v4.view.MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_settings:
            Intent intent = new Intent(this, SettingsActivity.class);
            startActivity(intent);
            return true;
        case R.id.menu_relationships:
            List<MIdentity> owned = mIdentitiesManager.getOwnedIdentities();
            for (MIdentity ident : owned) {
                if (ident.type_ != Authority.Local) {
                    intent = new Intent(this, RelationshipsActivity.class);
                    if (mIdentitiesManager.getPendingGraylistCount() > 0) {
                        intent.putExtra(RelationshipsActivity.EXTRA_SHOW_REQUESTS, true);
                    }
                    startActivity(intent);
                    return true;
                }
            }
            showDialog(DIALOG_PLZ_LINK_ACCCOUNT);
            return true;
        }
        return false;
    }

    public Uri uriFromNdef(NdefMessage... messages) {
        if (messages.length == 0 || messages[0].getRecords().length == 0) {
            return null;
        }

        try {
            return NdefFactory.parseUri(messages[0].getRecords()[0]);
        } catch (FormatException e) {
            return null;
        }
    }

    protected void doHandleInput(Uri uri) {
        if (DBG)
            Log.d(TAG, "Handling input uri " + uri);
        if (uri == null) {
            return;
        }

        if (uri.getScheme() == null) {
            Log.w(TAG, "Null uri scheme for " + uri);
            return;
        }

        //TODO: if there are URI's that are opened with the FeedListActivity that we want to 
        //handle, e.g. an NFC url, then we put that code here.  We used to have friend invites
        //but now we don't have to worry about that. 

        //If we want to share a URL that opens a specific Musubi feed, then we need to create a 
        //URL that links to it without giving away the secret feed capability.
        if (uri.getScheme().equals("content")) {
            if (uri.getAuthority().equals("vnd.mobisocial.db")) {
                if (uri.getPath().startsWith("/feed")) {
                    //                    Intent view = new Intent(Intent.ACTION_VIEW, uri);
                    //                    view.addCategory(Intent.CATEGORY_DEFAULT);
                    //                    // TODO: fix in AndroidManifest.
                    //                    //view.setClass(this, FeedActivity.class);
                    //                    view.setClass(this, FeedHomeActivity.class);
                    //                    startActivity(view);
                    //                    finish();
                    //                    return;
                }
            }
        }

        // Re-push the contact info ndef
        pushContactInfoViaNfc();
    }

    public void writeGroupToTag(Uri uri) {
        NdefRecord urlRecord = new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, NdefRecord.RTD_URI, new byte[] {},
                uri.toString().getBytes());
        NdefMessage ndef = new NdefMessage(new NdefRecord[] { urlRecord });
        mNfc.enableTagWriteMode(ndef);
        Toast.makeText(this, "Touch a tag to write the group...", Toast.LENGTH_SHORT).show();
    }

    public static byte[] toByteArray(BitSet bits) {
        byte[] bytes = new byte[bits.length() / 8 + 1];
        for (int i = 0; i < bits.length(); i++) {
            if (bits.get(i)) {
                bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
            }
        }
        return bytes;
    }

    public void pushGroupInfoViaNfc(Uri uri) {
        mNfc.share(NdefFactory.fromUri(uri));
    }

    public void pushContactInfoViaNfc() {
        //for now just share musubi over NFC
        mNfc.share(NdefFactory.fromUri("https://market.android.com/details?id=mobisocial.musubi"));
    }

    @Override
    public void onPause() {
        super.onPause();
        mNfc.onPause(this);
        getContentResolver().unregisterContentObserver(mObserver);
    }

    @Override
    public void onResume() {
        super.onResume();
        mSavedInstance = false;
        mNfc.onResume(this);
        pushContactInfoViaNfc();
        fillInCounter();
        getContentResolver().registerContentObserver(MusubiContentProvider.uriForDir(Provided.FEEDS), false,
                mObserver);
        mObserver.resetTimeout();
        mObserver.dispatchChange(false);
    }

    @Override
    public void onNewIntent(Intent intent) {
        if (mNfc.onNewIntent(this, intent)) {
            return;
        }
        setIntent(intent);
        getContentResolver().registerContentObserver(MusubiContentProvider.uriForDir(Provided.FEEDS), false,
                mObserver);
        if (mDualPane && getIntent() != null && getIntent().getData() != null) {
            Uri data = getIntent().getData();
            if (data.toString().startsWith(MusubiContentProvider.uriForDir(Provided.FEEDS_ID).toString())) {
                onFeedSelected(data);
            }
        }
    }

    @Override
    public void onDestroy() {
        getContentResolver().unregisterContentObserver(mObserver);
        super.onDestroy();
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        finish();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        mSavedInstance = true;
    }

    @Override
    public void onFeedSelected(Uri feedUri) {
        if (mDualPane) {
            if (mSavedInstance) {
                return;
            }
            Bundle args = new Bundle();
            args.putParcelable(FeedViewFragment.ARG_FEED_URI, feedUri);
            args.putBoolean(FeedViewFragment.ARG_DUAL_PANE, mDualPane);
            Fragment feedView = new FeedViewFragment();
            feedView.setArguments(args);
            Fragment oldSelector = getSupportFragmentManager().findFragmentByTag(FRAGMENT_FEED_ACTIONS);
            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            if (oldSelector != null) {
                ft.remove(oldSelector);
            }
            ft.replace(R.id.feed_view, feedView);
            ft.commit();
        } else {
            Intent panner = new Intent(this, FeedPannerActivity.class);
            panner.setData(feedUri);
            startActivity(panner);
        }
    }
}