/* * 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 gnu.trove.list.array.TLongArrayList; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import mobisocial.musubi.App; import mobisocial.musubi.R; import mobisocial.musubi.model.MFeed; import mobisocial.musubi.model.MIdentity; import mobisocial.musubi.model.helpers.FeedManager; import mobisocial.musubi.model.helpers.IdentitiesManager; import mobisocial.musubi.provider.MusubiContentProvider; import mobisocial.musubi.provider.MusubiContentProvider.Provided; import mobisocial.musubi.ui.util.UiUtil; import mobisocial.musubi.ui.widget.MultiIdentitySelector; import mobisocial.musubi.ui.widget.MultiIdentitySelector.OnIdentitiesUpdatedListener; import mobisocial.musubi.ui.widget.MultiIdentitySelector.OnRequestAddIdentityListener; import mobisocial.musubi.util.SimpleCursorLoader; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.database.MatrixCursor; import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract; import android.provider.ContactsContract.Intents.Insert; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.MarginLayoutParams; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.CursorAdapter; import android.widget.Gallery; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; public class FeedIdentityGrid extends MusubiBaseActivity implements LoaderManager.LoaderCallbacks<Cursor> { static final int LOAD_FEEDS = 0; private static final int REQUEST_ADD_CONTACT = 1; MultiIdentitySelector mIdentitySelector; SQLiteOpenHelper mDatabaseSource; IdentitiesManager mIdentitiesManager; FeedManager mFeedManager; ListView mFeedListView; FeedMembersCursorAdapter mFeedAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //TODO: this begs for being in util, huh. i wonder where else it is used. //check that we aren't going to send a message using the local authority //to our friends. This is similar to the initial hidden state of the //person picker on the feed list mDatabaseSource = App.getDatabaseSource(this); mIdentitiesManager = new IdentitiesManager(mDatabaseSource); mFeedManager = new FeedManager(mDatabaseSource); if(mIdentitiesManager.getOwnedIdentities().size() < 2) { Toast.makeText(this, "You must connect an account in Musubi to be able to share with your contacts", Toast.LENGTH_SHORT).show(); Intent intent = new Intent(this, FeedListActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); return; } setTitle("Share"); RelativeLayout window = new RelativeLayout(this); LayoutParams fill = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); window.setLayoutParams(fill); // Identity multi-select mIdentitySelector = new MultiIdentitySelector(this); mIdentitySelector.setOnIdentitiesUpdatedListener(mIdentitiesUpdatedListener); mIdentitySelector.setOnRequestAddIdentityListener(mOnRequestAddIdentityListener); RelativeLayout.LayoutParams selectorParams = new RelativeLayout.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); selectorParams.addRule(RelativeLayout.ALIGN_PARENT_TOP); mIdentitySelector.setLayoutParams(selectorParams); mIdentitySelector.setId(R.id.people); // Feed list mFeedListView = new ListView(this); RelativeLayout.LayoutParams listParams = new RelativeLayout.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); listParams.addRule(RelativeLayout.BELOW, R.id.people); listParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); mFeedListView.setLayoutParams(listParams); mFeedListView.setOnItemClickListener(mFeedClickListener); // Must be called before setAdapter(): //mFeedListView.addHeaderView(mHeaderView); // Bind to content view window.addView(mIdentitySelector); window.addView(mFeedListView); setContentView(window, fill); getSupportLoaderManager().initLoader(LOAD_FEEDS, null, this); } public void onActivityResult(int requestCode, int resultCode, Intent data) { if(requestCode == REQUEST_ADD_CONTACT) { if(resultCode == Activity.RESULT_OK) { UiUtil.addedContact(this, data, mIdentitySelector); } } }; private OnRequestAddIdentityListener mOnRequestAddIdentityListener = new OnRequestAddIdentityListener() { @Override public void onRequestAddIdentity(String enteredText) { Intent i = new Intent(Intent.ACTION_INSERT_OR_EDIT); i.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); if(enteredText != null) { Pattern emailPattern = Pattern.compile("\\b[A-Z0-9._%-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}\\b", Pattern.CASE_INSENSITIVE); if (emailPattern.matcher(enteredText).matches()) { i.putExtra(Insert.EMAIL, enteredText); } else { i.putExtra(Insert.NAME, enteredText); } } startActivityForResult(i, REQUEST_ADD_CONTACT); } }; String getCallerAppId() { ActivityManager manager = (ActivityManager)getSystemService(ACTIVITY_SERVICE); List<RecentTaskInfo> infos = manager.getRecentTasks(1, 0); if (infos.size() == 0) { Log.w(TAG, "couldn't get info"); return null; } Intent base = infos.get(0).baseIntent; return base.getComponent().getPackageName(); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { long[] ids; if (args == null) { ids = new long[0]; } else { ids = args.getLongArray("ids"); } switch (id) { case LOAD_FEEDS: return new FeedsWithMembersLoader(this, mFeedManager, ids); } return null; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { if (mFeedAdapter == null) { mFeedAdapter = new FeedMembersCursorAdapter(this, data); mFeedListView.setAdapter(mFeedAdapter); } else { mFeedAdapter.changeCursor(data); } } @Override public void onLoaderReset(Loader<Cursor> loader) { mFeedAdapter.changeCursor(null); } OnIdentitiesUpdatedListener mIdentitiesUpdatedListener = new OnIdentitiesUpdatedListener() { public void onIdentitiesUpdated() { Set<MIdentity> idSet = mIdentitySelector.getSelectedIdentities(); long[] ids = new long[idSet.size()]; int i = 0; for (MIdentity id : idSet) { ids[i++] = id.id_; } Bundle args = new Bundle(); args.putLongArray("ids", ids); getSupportLoaderManager().restartLoader(LOAD_FEEDS, args, FeedIdentityGrid.this); } }; OnItemClickListener mFeedClickListener = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { long feedId = (Long)view.getTag(R.id.feed_label); Uri feedUri = MusubiContentProvider.uriForItem(Provided.FEEDS, feedId); MFeed mfeed = new MFeed(); mfeed.id_ = feedId; MIdentity[] list = mFeedManager.getFeedMembers(mfeed); String text = "bye"; Toast.makeText(FeedIdentityGrid.this, text, Toast.LENGTH_SHORT).show(); finish(); } }; public static class FeedsWithMembersLoader extends SimpleCursorLoader { final String[] mColumns = new String[] { MFeed.COL_ID, MFeed.COL_NUM_UNREAD, MFeed.COL_NAME, MFeed.COL_LATEST_RENDERABLE_OBJ_ID }; final long[] mIdentityIds; final FeedManager mFeedManager; public FeedsWithMembersLoader(Context context, FeedManager feedManager, long[] idIds) { super(context); mIdentityIds = idIds; mFeedManager = feedManager; } @Override public Cursor loadInBackground() { if (mIdentityIds.length == 0) { return allFeedsCursor(); } return identityFilteredCursor(); } Cursor allFeedsCursor() { String selection = FeedManager.VISIBLE_FEED_SELECTION; String sortOrder = MFeed.COL_LATEST_RENDERABLE_OBJ_TIME + " desc"; return getContext().getContentResolver().query(MusubiContentProvider.uriForDir(Provided.FEEDS), mColumns, selection, null, sortOrder); } Cursor identityFilteredCursor() { assert(mIdentityIds.length > 0); long[] feedIds = mFeedManager.getFeedsForIdentityId(mIdentityIds[0]); for (int p = 1; p < mIdentityIds.length; p++) { long[] filter = mFeedManager.getFeedsForIdentityId(mIdentityIds[p]); int i = 0, j = 0; TLongArrayList survivors = new TLongArrayList(feedIds.length); while (i < feedIds.length && j < filter.length) { if (feedIds[i] == filter[j]) { survivors.add(feedIds[i]); i++; j++; } else if (feedIds[i] > filter[j]) { i++; } else { j++; } } feedIds = new long[survivors.size()]; survivors.toArray(feedIds); if (feedIds.length == 0) { break; } } final MatrixCursor cursor = new MatrixCursor(mColumns, feedIds.length); for (long f : feedIds) { MFeed feed = mFeedManager.lookupFeed(f); Object[] values = new Object[mColumns.length]; values[0] = feed.id_; values[1] = feed.numUnread_; values[2] = feed.name_; values[3] = feed.latestRenderableObjId_; cursor.addRow(values); } return cursor; } } public static class FeedMembersCursorAdapter extends CursorAdapter { private FeedManager mFeedManager; private final FeedIdentityGrid mContext; public FeedMembersCursorAdapter(FeedIdentityGrid context, Cursor c) { super(context, c); mContext = context; SQLiteOpenHelper helper = App.getDatabaseSource(context); mFeedManager = new FeedManager(helper); } @Override public View newView(Context context, Cursor c, ViewGroup parent) { LinearLayout membershipView = new LinearLayout(context); membershipView.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.FILL_PARENT, AbsListView.LayoutParams.WRAP_CONTENT)); TextView title = new TextView(context); title.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); Gallery gallery = new Gallery(context); gallery.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); gallery.setId(R.id.people); gallery.setGravity(Gravity.LEFT); gallery.setSpacing(4); gallery.setClickable(false); gallery.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS); hackGalleryInit(gallery); membershipView.addView(gallery); return membershipView; } void hackGalleryInit(Gallery gallery) { DisplayMetrics metrics = new DisplayMetrics(); mContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); MarginLayoutParams mlp = (MarginLayoutParams) gallery.getLayoutParams(); mlp.setMargins(-(metrics.widthPixels/2), mlp.topMargin, mlp.rightMargin, mlp.bottomMargin ); } @Override public void bindView(final View v, final Context context, final Cursor c) { long feedId = c.getLong(c.getColumnIndexOrThrow(MFeed.COL_ID)); v.setTag(R.id.feed_label, feedId); Gallery gallery = (Gallery)v.findViewById(R.id.people); Cursor members = mFeedManager.getKnownProfileFeedMembersCursor(c.getLong(0)); gallery.setAdapter(new MembersCursorAdapter(context, members)); /* TextView title = (TextView)v.findViewById(R.id.text); MFeed feed = new MFeed(); feed.id_ = feedId; title.setText(UiUtil.getFeedNameFromMembersList(mFeedManager, feed));*/ } } static class MembersCursorAdapter extends CursorAdapter { final IdentitiesManager idm; public MembersCursorAdapter(Context context, Cursor members) { super(context, members); idm = new IdentitiesManager(App.getDatabaseSource(context)); } @Override public void bindView(View view, Context context, Cursor cursor) { MIdentity identity = new MIdentity(); identity.id_ = cursor.getLong(0); Bitmap cached = UiUtil.safeGetContactThumbnail(context, idm, identity); ((ImageView)view).setImageBitmap(cached); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { // TODO: Render nam under icon ImageView icon = new ImageView(context); int DP = (int)context.getResources().getDisplayMetrics().density * 75; icon.setLayoutParams(new Gallery.LayoutParams(DP, DP)); icon.setPadding(8, 8, 8, 8); return icon; } } }