/* * 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.fragments; import mobisocial.crypto.IBHashedIdentity.Authority; import mobisocial.musubi.App; import mobisocial.musubi.R; import mobisocial.musubi.model.MFeed; import mobisocial.musubi.model.MIdentity; import mobisocial.musubi.model.MMyAccount; import mobisocial.musubi.model.helpers.FeedManager; import mobisocial.musubi.model.helpers.IdentitiesManager; import mobisocial.musubi.model.helpers.MyAccountManager; import mobisocial.musubi.service.MusubiService; import mobisocial.musubi.ui.EmailInviteActivity; import mobisocial.musubi.ui.ViewProfileActivity; import mobisocial.musubi.ui.util.UiUtil; import mobisocial.musubi.util.LessSpammyContentObserver; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.database.ContentObserver; import android.database.Cursor; import android.database.sqlite.SQLiteOpenHelper; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.support.v4.app.SupportActivity; import android.support.v4.view.MenuItem; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.Button; import android.widget.CursorAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; /** * Displays a list of contacts. If the intent used to create * this activity as Long extra "group_id", contacts are chosen * from this group. Otherwise, lists all known contacts. * */ public class ContactsFragment extends Fragment implements OnItemClickListener { private ContactListCursorAdapter mContacts; public static final String TAG = "ContactsFragment"; public static final int GROUP_ID = 111; private static final int sBlockedColor = Color.parseColor("#66FF3333"); public static enum ContactListType {WHITE_LIST, GRAY_LIST}; public static final String CONTACT_LIST_TYPE = "contact_list_type"; private ContactListType listType; private SQLiteOpenHelper mDatabaseSource; private IdentitiesManager mIdentityManager; private FeedManager mFeedManager; private MyAccountManager mAccountManager; private LessSpammyContentObserver mObserver; private ContentObserver mEditableObserver; private Activity mActivity; public static ContactsFragment newInstance(ContactListType type) { ContactsFragment frag = new ContactsFragment(); Bundle args = new Bundle(); args.putInt(CONTACT_LIST_TYPE, type.ordinal()); frag.setArguments(args); return frag; } @Override public void onAttach(SupportActivity activity) { super.onAttach(activity); mActivity = activity.asActivity(); setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.simple_list, container, false); Log.w(TAG, "returning view"); return v; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mObserver = new LessSpammyContentObserver(new Handler(mActivity.getMainLooper())) { @Override public void lessSpammyOnChange(boolean arg0) { if(mContacts == null || mContacts.getCursor() == null || !isAdded()) return; mContacts.getCursor().requery(); } }; mEditableObserver = new ContentObserver(new Handler(mActivity.getMainLooper())) { @Override public void onChange(boolean arg0) { if(mContacts == null || mContacts.getCursor() == null || !isAdded()) return; mContacts.getCursor().requery(); } }; } @Override public void onResume() { super.onResume(); mActivity.getContentResolver().registerContentObserver(MusubiService.WHITELIST_APPENDED, false, mEditableObserver); mActivity.getContentResolver().registerContentObserver(MusubiService.COLORLIST_CHANGED, false, mEditableObserver); mActivity.getContentResolver().registerContentObserver(MusubiService.PRIMARY_CONTENT_CHANGED, false, mObserver); mObserver.resetTimeout(); mObserver.dispatchChange(false); } @Override public void onPause() { super.onPause(); mActivity.getContentResolver().unregisterContentObserver(mObserver); mActivity.getContentResolver().unregisterContentObserver(mEditableObserver); } @Override public void onDestroy() { super.onDestroy(); } public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); listType = ContactListType.values()[getArguments().getInt(CONTACT_LIST_TYPE)]; mDatabaseSource = App.getDatabaseSource(this.mActivity); mIdentityManager = new IdentitiesManager(mDatabaseSource); mFeedManager = new FeedManager(mDatabaseSource); mAccountManager = new MyAccountManager(mDatabaseSource); Cursor identities; if (listType == ContactListType.GRAY_LIST) { identities = mIdentityManager.getGrayListIdentitiesCursor(); } else { identities = mIdentityManager.getWhiteListIdentitiesCursor(); } mContacts = new ContactListCursorAdapter(this.mActivity, identities); ListView lv = (ListView)getView().findViewById(android.R.id.list); lv.setAdapter(mContacts); lv.setTextFilterEnabled(true); lv.setFastScrollEnabled(true); registerForContextMenu(lv); lv.setOnItemClickListener(this); } @Override public void onItemClick(AdapterView<?> parent, final View view, int position, long id){ final Cursor cursor = (Cursor)mContacts.getItem(position); final long identityId = cursor.getLong(cursor.getColumnIndexOrThrow(MIdentity.COL_ID)); final MIdentity ident = mIdentityManager.getIdentityForId(identityId); assert(ident != null); switch(listType) { case WHITE_LIST: Intent intent = new Intent(mActivity, ViewProfileActivity.class); intent.putExtra(ViewProfileActivity.PROFILE_ID, identityId); mActivity.startActivity(intent); break; case GRAY_LIST: break; } } private void addContact(final MIdentity ident) { /* AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); //name = cursor.getString(cursor.getColumnIndexOrThrow(Contact.NAME)); String name = UiUtil.safeNameForIdentity(ident); builder = new AlertDialog.Builder(mActivity); //name = cursor.getString(cursor.getColumnIndexOrThrow(Contact.NAME)); builder.setMessage("Do you want to add " + name + " to your friends list?") .setCancelable(true) .setPositiveButton("Confirm", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { addToWhitelist(ident); dialog.cancel(); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alert = builder.create(); alert.show();*/ addToWhitelist(ident); } void addToWhitelist(MIdentity ident) { ident.whitelisted_ = true; ident.blocked_ = false; //force a profile exchange to ensure we get an icon after a user action ident.sentProfileVersion_ = 0; mIdentityManager.updateIdentity(ident); mFeedManager.acceptFeedsFromMember(mActivity, ident.id_); //stop sending them broadcasts, e.g. profiles MMyAccount[] accounts = mAccountManager.getMyAccounts(); boolean found_specific_account = false; for(MMyAccount account : accounts) { //TODO: someday you can remove these checks assert(account.identityId_ != null); if(account.identityId_ == null) continue; assert(account.feedId_ != null); if(account.feedId_ == null) continue; //check if this member is on a provisional whitelist, that's the identity we want to put them in if(!account.accountType_.equals(MMyAccount.INTERNAL_ACCOUNT_TYPE)) continue; if(!account.accountName_.equals(MMyAccount.PROVISIONAL_WHITELIST_ACCOUNT)) continue; MMyAccount whitelist_account = mAccountManager.getWhitelistForIdentity(account.identityId_); assert(whitelist_account != null); //should be guaranteed assert(whitelist_account.feedId_ != null); //should be guaranteed mFeedManager.ensureFeedMember(whitelist_account.feedId_, ident.id_); found_specific_account = true; } //put them in an unassociated group which will always use the default identity whatever that is if(!found_specific_account) { mFeedManager.ensureFeedMember(MFeed.NONIDENTITY_SPECIFIC_WHITELIST_ID, ident.id_); } mObserver.resetTimeout(); mActivity.getContentResolver().notifyChange(MusubiService.WHITELIST_APPENDED, null); mActivity.getContentResolver().notifyChange(MusubiService.COLORLIST_CHANGED, null); } public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo; Cursor c = (Cursor)mContacts.getItem(info.position); long identityId = c.getLong(c.getColumnIndexOrThrow(MIdentity.COL_ID)); MIdentity ident = mIdentityManager.getIdentityForId(identityId); String[] menuItems; if (ident.type_ == Authority.Email && !ident.claimed_) { menuItems = new String[] { "Ignore", "Invite" }; } else { menuItems = new String[] { "Ignore" }; } for (int i = 0; i<menuItems.length; i++) { menu.add(GROUP_ID, i, i, menuItems[i]); } } public boolean onContextItemSelected(MenuItem item) { AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); int menuItemIndex = item.getItemId(); Cursor cursor = (Cursor)mContacts.getItem(info.position); final MIdentity ident = mIdentityManager.getIdentityForId(cursor.getLong(cursor.getColumnIndexOrThrow(MIdentity.COL_ID))); switch(menuItemIndex) { case 0: ignoreContact(ident); break; case 1: String email = ident.principal_; if (email == null) { Toast.makeText(getActivity(), "Oops, couldn't set up email.", Toast.LENGTH_SHORT).show(); return true; } Intent intent = EmailInviteActivity.getInviteIntentForEmail(getActivity(), email); startActivity(intent); break; } return true; } private void ignoreContact(final MIdentity ident) { AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); //name = cursor.getString(cursor.getColumnIndexOrThrow(Contact.NAME)); String name = UiUtil.safeNameForIdentity(ident); builder.setMessage("Are you sure you want to ignore " + name + "?") .setCancelable(true) .setPositiveButton("Ignore", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { ident.whitelisted_ = false; ident.blocked_ = true; mIdentityManager.updateIdentity(ident); //stop sending them broadcasts, e.g. profiles MMyAccount[] accounts = mAccountManager.getMyAccounts(); for(MMyAccount account : accounts) { //TODO: someday you can remove these checks assert(account.feedId_ != null); if(account.feedId_ == null) continue; mFeedManager.deleteFeedMember(account.feedId_, ident.id_); } //TODO: hide all the feeds this person is the only other person on? //TODO: hide this persons objects or delete them? ew? mObserver.resetTimeout(); mActivity.getContentResolver().notifyChange(MusubiService.COLORLIST_CHANGED, null); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alert = builder.create(); alert.show(); } private class ContactListCursorAdapter extends CursorAdapter { public ContactListCursorAdapter (Context context, Cursor c) { super(context, c); } @Override public View newView(Context context, Cursor c, ViewGroup parent) { final LayoutInflater inflater = LayoutInflater.from(context); View v = inflater.inflate(R.layout.contacts_item, parent, false); return v; } @Override public void bindView(View v, Context context, Cursor cursor) { final MIdentity ident = mIdentityManager.getIdentityForId(cursor.getLong(0)); v.setTag(ident.id_); TextView nameText = (TextView) v.findViewById(R.id.name_text); nameText.setText(UiUtil.safeNameForIdentity(ident)); TextView principalText = (TextView) v.findViewById(R.id.principal_text); principalText.setText(UiUtil.safePrincipalForIdentity(ident)); TextView statusText = (TextView) v.findViewById(R.id.status_text); statusText.setVisibility(View.GONE); TextView unreadCount = (TextView)v.findViewById(R.id.unread_count); unreadCount.setVisibility(View.GONE); ImageView icon = (ImageView)v.findViewById(R.id.icon); icon.setImageBitmap(UiUtil.safeGetContactThumbnail(context, mIdentityManager, ident)); final ImageView presenceIcon = (ImageView)v.findViewById(R.id.presence_icon); presenceIcon.setVisibility(View.GONE); //presenceIcon.setImageResource(c.currentPresenceResource()); final ImageView nearbyIcon = (ImageView)v.findViewById(R.id.nearby_icon); nearbyIcon.setVisibility(View.GONE); //nearbyIcon.setVisibility(c.nearby ? View.VISIBLE : View.GONE); final ImageView more = (ImageView)v.findViewById(R.id.more); more.setVisibility(View.GONE); if (listType == ContactListType.GRAY_LIST) { final View buttonBar = v.findViewById(R.id.button_bar); buttonBar.setVisibility(View.VISIBLE); Button addButton = (Button) v.findViewById(R.id.add_contact); addButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub addContact(ident); } }); Button ignoreButton = (Button) v.findViewById(R.id.ignore_contact); if(ident.blocked_) { ignoreButton.setVisibility(View.GONE); addButton.setText("Un-ignore"); } else { ignoreButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub ignoreContact(ident); } }); } } ImageView musubiEnabled = (ImageView)v.findViewById(R.id.musubi_enabled); ImageView ignoredOverlay = (ImageView) v.findViewById(R.id.ignored_overlay); if (ident.claimed_) { ignoredOverlay.setVisibility(View.GONE); nameText.setTextColor(Color.BLACK); principalText.setTextColor(Color.BLACK); musubiEnabled.setBackgroundResource(R.drawable.musubi_enabled); } else { ignoredOverlay.setVisibility(View.GONE); nameText.setTextColor(Color.BLACK); principalText.setTextColor(Color.BLACK); musubiEnabled.setBackgroundResource(R.drawable.musubi_disabled); } if(ident.blocked_) { nameText.setTextColor(sBlockedColor); principalText.setTextColor(sBlockedColor); ignoredOverlay.setVisibility(View.VISIBLE); } musubiEnabled.setVisibility(View.VISIBLE); } } }