/*
* 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.Set;
import java.util.regex.Pattern;
import mobisocial.musubi.App;
import mobisocial.musubi.Helpers;
import mobisocial.musubi.R;
import mobisocial.musubi.model.MFeed;
import mobisocial.musubi.model.MFeedMember;
import mobisocial.musubi.model.MIdentity;
import mobisocial.musubi.model.helpers.FeedManager;
import mobisocial.musubi.model.helpers.IdentitiesManager;
import mobisocial.musubi.model.helpers.MyAccountManager;
import mobisocial.musubi.objects.IntroductionObj;
import mobisocial.musubi.provider.MusubiContentProvider;
import mobisocial.musubi.provider.MusubiContentProvider.Provided;
import mobisocial.musubi.service.MusubiService;
import mobisocial.musubi.ui.fragments.ClipboardKeeper;
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.ui.widget.ObjView;
import mobisocial.musubi.util.ObjFactory;
import mobisocial.musubi.util.SimpleCursorLoader;
import mobisocial.socialkit.Obj;
import mobisocial.socialkit.musubi.DbFeed;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Intents.Insert;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
public class SendContentActivity extends MusubiBaseActivity {
static final int LOAD_FEEDS = 0;
private static final int REQUEST_ADD_CONTACT = 1;
public static final String EXTRA_CALLING_APP = "caller";
Obj mObj;
MultiIdentitySelector mIdentitySelector;
SQLiteDatabase mDatabaseSource;
IdentitiesManager mIdentitiesManager;
FeedManager mFeedManager;
Button mSendButton;
Button mClipboardButton;
RelativeLayout mWindow;
ProgressBar mProgress;
class BuildObjTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
mObj = ObjFactory.objForSendIntent(SendContentActivity.this, getIntent());
return null;
}
@Override
protected void onPostExecute(Void result) {
if (mObj == null) {
Toast.makeText(SendContentActivity.this, "Unsupported content type.", Toast.LENGTH_SHORT).show();
finish();
return;
}
Set<MIdentity> idSet = mIdentitySelector.getSelectedIdentities();
mSendButton.setEnabled(idSet.size() > 0 && mObj != null);
mClipboardButton.setEnabled(mObj != null);
mProgress.setVisibility(View.GONE);
// Preview pane
ObjView preview = new ObjView(SendContentActivity.this, mObj);
preview.setId(R.id.object_entry);
RelativeLayout.LayoutParams previewParams = new RelativeLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
previewParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
previewParams.addRule(RelativeLayout.BELOW, R.id.submit);
preview.setLayoutParams(previewParams);
preview.setPadding(6, 16, 6, 6);
preview.setMinimumHeight(150);
preview.setBackgroundResource(R.drawable.sharebox);
mWindow.addView(preview);
}
}
@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).getWritableDatabase();
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");
mWindow = new RelativeLayout(this);
LayoutParams fill = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
mWindow.setLayoutParams(fill);
// Identity multi-select
mIdentitySelector = new MultiIdentitySelector(this);
mIdentitySelector.setOnIdentitiesUpdatedListener(mIdentitiesUpdatedListener);
mIdentitySelector.setOnRequestAddIdentityListener(mRequestAddIdentityListener);
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);
mWindow.addView(mIdentitySelector);
// Send button
mSendButton = new Button(this);
mSendButton.setText(R.string.send);
RelativeLayout.LayoutParams sendButtonParams = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
mSendButton.setId(R.id.submit);
sendButtonParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
sendButtonParams.addRule(RelativeLayout.BELOW, R.id.people);
mSendButton.setLayoutParams(sendButtonParams);
mSendButton.setOnClickListener(mNewFeedClickListener);
mSendButton.setEnabled(false);
mWindow.addView(mSendButton);
// Clipboard button
mClipboardButton = new Button(this);
mClipboardButton.setEnabled(false);
//mClipboardButton.setImageResource(R.drawable.ic_action_pin);
mClipboardButton.setText("Copy to clipboard");
RelativeLayout.LayoutParams clipboardParams = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
clipboardParams.addRule(RelativeLayout.LEFT_OF, R.id.submit);
clipboardParams.addRule(RelativeLayout.BELOW, R.id.people);
mClipboardButton.setLayoutParams(clipboardParams);
mClipboardButton.setOnClickListener(mClipboardListener);
int dp = (int)(getResources().getDisplayMetrics().density * 10);
mClipboardButton.setPadding(dp, dp, dp, dp);
mWindow.addView(mClipboardButton);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
layoutParams.addRule(RelativeLayout.BELOW, R.id.submit);
layout.setLayoutParams(layoutParams);
layout.setPadding(6, 16, 6, 6);
layout.setGravity(Gravity.CENTER);
layout.setMinimumHeight(150);
layout.setBackgroundResource(R.drawable.sharebox);
mProgress = new ProgressBar(this);
mProgress.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
mProgress.setIndeterminate(true);
layout.addView(mProgress);
mWindow.addView(layout);
setContentView(mWindow, fill);
new BuildObjTask().execute();
}
OnIdentitiesUpdatedListener mIdentitiesUpdatedListener = new OnIdentitiesUpdatedListener() {
public void onIdentitiesUpdated() {
Set<MIdentity> idSet = mIdentitySelector.getSelectedIdentities();
mSendButton.setEnabled(idSet.size() > 0 && mObj != null);
mClipboardButton.setEnabled(mObj != null);
}
};
OnRequestAddIdentityListener mRequestAddIdentityListener = 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);
}
};
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQUEST_ADD_CONTACT) {
//reread the contact list so that its possible for us to fill in what they typed
getContentResolver().notifyChange(MusubiService.FORCE_RESCAN_CONTACTS, null);
}
};
View.OnClickListener mNewFeedClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean ownedId = false;
Set<MIdentity> ids = mIdentitySelector.getSelectedIdentities();
StringBuilder selection = new StringBuilder(FeedManager.VISIBLE_FEED_SELECTION);
for (MIdentity p : ids) {
ownedId |= p.owned_;
selection.append(" AND ").append(MFeed.COL_ID).append(" in (SELECT ")
.append(MFeedMember.COL_FEED_ID).append(" FROM ").append(MFeedMember.TABLE)
.append(" WHERE ").append(MFeedMember.COL_IDENTITY_ID).append("=")
.append(p.id_).append(")");
}
String table = MFeed.TABLE;
String[] projection = new String[] { MFeed.COL_ID };
String sortOrder = MFeed.COL_LATEST_RENDERABLE_OBJ_TIME + " desc";
String[] selectionArgs = null;
String groupBy = null, having = null;
Cursor c = mDatabaseSource.query(table, projection, selection.toString(),
selectionArgs, groupBy, having, sortOrder);
try {
int size = ids.size();
if (!ownedId) size++;
while (c.moveToNext()) {
long feedId = c.getLong(0);
int count = mFeedManager.getFeedMemberCount(feedId);
if (count == size) {
Uri feedUri = MusubiContentProvider.uriForItem(Provided.FEEDS, feedId);
DbFeed feed = App.getMusubi(SendContentActivity.this).getFeed(feedUri);
feed.postObj(mObj);
String text = (ids.size() == 1) ? "Shared with 1 person." :
"Shared with " + ids.size() + " people.";
Toast.makeText(SendContentActivity.this, text, Toast.LENGTH_SHORT).show();
finish();
return;
}
}
} finally {
c.close();
}
MIdentity[] buddies = new MIdentity[ids.size() + 1];
int i = 0;
for (MIdentity id : ids) {
buddies[i++] = id;
}
buddies[i] = mIdentitiesManager.getMyDefaultIdentity();
MFeed f = mFeedManager.createExpandingFeed(buddies);
MyAccountManager am = new MyAccountManager(mDatabaseSource);
UiUtil.addToWhitelistsIfNecessary(mFeedManager, am, mFeedManager.getFeedMembers(f), true);
long feedId = f.id_;
Uri feedUri = MusubiContentProvider.uriForItem(Provided.FEEDS, feedId);
DbFeed feed = App.getMusubi(SendContentActivity.this).getFeed(feedUri);
Obj invitedObj = IntroductionObj.from(mIdentitySelector.getSelectedIdentities(), true);
Helpers.sendToFeed(SendContentActivity.this, invitedObj, feedUri);
feed.postObj(mObj);
String text = (ids.size() == 1) ? "Shared with 1 person." :
"Shared with " + ids.size() + " people.";
Toast.makeText(SendContentActivity.this, text, Toast.LENGTH_SHORT).show();
finish();
}
};
View.OnClickListener mClipboardListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
new ClipboardKeeper(SendContentActivity.this).store(mObj);
Intent home = new Intent(SendContentActivity.this, FeedListActivity.class);
startActivity(home);
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;
}
}
}