/*
* Copyright (C) 2008 Esmertec AG. Copyright (C) 2008 The Android Open Source
* Project
*
* 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 info.guardianproject.otr.app.im.app;
import info.guardianproject.otr.OtrDataHandler;
import info.guardianproject.otr.app.im.IChatSession;
import info.guardianproject.otr.app.im.IChatSessionManager;
import info.guardianproject.otr.app.im.IContactListManager;
import info.guardianproject.otr.app.im.IImConnection;
import info.guardianproject.otr.app.im.R;
import info.guardianproject.otr.app.im.app.ContactListFilterView.ContactListListener;
import info.guardianproject.otr.app.im.app.adapter.ChatListenerAdapter;
import info.guardianproject.otr.app.im.engine.ImConnection;
import info.guardianproject.otr.app.im.provider.Imps;
import info.guardianproject.otr.app.im.provider.Imps.ProviderSettings.QueryMap;
import info.guardianproject.otr.app.im.service.ImServiceConstants;
import info.guardianproject.util.LogCleaner;
import info.guardianproject.util.SystemServices;
import info.guardianproject.util.SystemServices.FileInfo;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.google.zxing.integration.android.IntentIntegrator;
import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;
public class NewChatActivity extends FragmentActivity implements View.OnCreateContextMenuListener {
private static final String ICICLE_CHAT_PAGER_ADAPTER = "chatPagerAdapter";
private static final String ICICLE_POSITION = "position";
private static final int MENU_RESEND = Menu.FIRST;
private static final int REQUEST_PICK_CONTACTS = RESULT_FIRST_USER + 1;
private static final int REQUEST_SEND_IMAGE = REQUEST_PICK_CONTACTS + 1;
private static final int REQUEST_SEND_FILE = REQUEST_SEND_IMAGE + 1;
private ImApp mApp;
private ViewPager mChatPager;
private ChatViewPagerAdapter mChatPagerAdapter;
private Cursor mCursorChats;
private SimpleAlertHandler mHandler;
private long mAccountId = -1;
private long mLastProviderId = -1;
private MessageContextMenuHandler mMessageContextMenuHandler;
private ContactListFragment mContactList = null;
private static final String TAG = "GB.NewChatActivity";
final static class MyHandler extends SimpleAlertHandler {
public MyHandler(NewChatActivity activity) {
super(activity);
}
@Override
public void handleMessage(Message msg) {
if (msg.what == ImApp.EVENT_SERVICE_CONNECTED) {
((NewChatActivity)mActivity).onServiceConnected();
return;
}
super.handleMessage(msg);
}
}
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mApp = (ImApp)getApplication();
mApp.maybeInit(this);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.chat_pager);
ThemeableActivity.setBackgroundImage(this);
mHandler = new MyHandler(this);
mChatPager = (ViewPager) findViewById(R.id.chatpager);
mChatPager.setSaveEnabled(false);
mChatPager.setOnPageChangeListener(new OnPageChangeListener () {
private int lastPos = -1;
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageSelected(int pos) {
if (pos > 0) {
if (lastPos != -1)
{
ChatViewFragment frag = (ChatViewFragment)mChatPagerAdapter.getItemAt(pos);
// Fragment isn't guaranteed to be initialized yet
if (frag != null)
frag.onDeselected(mApp);
}
ChatViewFragment frag = (ChatViewFragment)mChatPagerAdapter.getItemAt(pos);
// Fragment isn't guaranteed to be initialized yet
if (frag != null)
frag.onSelected(mApp);
lastPos = pos;
}
}
});
mMessageContextMenuHandler = new MessageContextMenuHandler();
initSideBar ();
mChatPagerAdapter = new ChatViewPagerAdapter(getSupportFragmentManager());
mChatPager.setAdapter(mChatPagerAdapter);
if (icicle != null) {
if (icicle.containsKey(ICICLE_CHAT_PAGER_ADAPTER)) {
mChatPagerAdapter.restoreState(icicle.getParcelable(ICICLE_CHAT_PAGER_ADAPTER), getClassLoader());
}
if (icicle.containsKey(ICICLE_POSITION)) {
int position = icicle.getInt(ICICLE_POSITION);
if (position < mChatPagerAdapter.getCount())
mChatPager.setCurrentItem(position);
}
}
mApp.registerForBroadcastEvent(ImApp.EVENT_SERVICE_CONNECTED, mHandler);
}
/*
* We must have been thawed and the service was not previously connected, so our ChatViews are showing nothing.
* Refresh them.
*/
void onServiceConnected() {
if (mChatPagerAdapter != null) {
int size = mChatPagerAdapter.getCount();
for (int i = 1; i < size ; i++) {
ChatViewFragment frag = (ChatViewFragment)mChatPagerAdapter.getItemAt(i);
if (frag != null) {
frag.onServiceConnected();
}
}
}
}
@Override
protected void onDestroy() {
mApp.unregisterForBroadcastEvent(ImApp.EVENT_SERVICE_CONNECTED, mHandler);
mChatPagerAdapter.onDestroy();
mChatPagerAdapter = null;
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(ICICLE_POSITION, mChatPager.getCurrentItem());
outState.putParcelable(ICICLE_CHAT_PAGER_ADAPTER, mChatPagerAdapter.saveState());
}
@Override
protected void onResume() {
super.onResume();
if (getIntent() != null)
{
resolveIntent(getIntent());
setIntent(null);
}
if (menu.isMenuShowing())
menu.toggle();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
@Override
public void onBackPressed() {
if (menu.isMenuShowing()) {
menu.showContent();
return;
}
int currentPos = mChatPager.getCurrentItem();
if (currentPos > 0) {
mChatPager.setCurrentItem(0);
return;
}
super.onBackPressed();
}
private SlidingMenu menu = null;
private void initSideBar ()
{
menu = new SlidingMenu(this);
menu.setMode(SlidingMenu.LEFT);
menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
menu.setShadowWidthRes(R.dimen.shadow_width);
menu.setShadowDrawable(R.drawable.shadow);
menu.setBehindOffsetRes(R.dimen.slidingmenu_offset);
menu.setFadeDegree(0.35f);
menu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT);
menu.setMenu(R.layout.fragment_drawer);
Button btnDrawerAccount = (Button) findViewById(R.id.btnDrawerAccount);
Button btnDrawerSettings = (Button) findViewById(R.id.btnDrawerSettings);
Button btnDrawerPanic = (Button) findViewById(R.id.btnDrawerPanic);
Button btnDrawerGroupChat = (Button) findViewById(R.id.btnDrawerGroupChat);
Button btnDrawerAddContact = (Button) findViewById(R.id.btnDrawerAddContact);
Button btnDrawerFingerprint = (Button) findViewById(R.id.btnDrawerQRCode);
btnDrawerAccount.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
Intent intent = new Intent(NewChatActivity.this, AccountListActivity.class);
startActivity(intent);
finish();//we should clsoe this activity when we go to AccountList, in case we sign out
}
});
btnDrawerSettings.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
Intent sintent = new Intent(NewChatActivity.this, SettingActivity.class);
startActivity(sintent);
}
});
btnDrawerPanic.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
/*
Intent intent = new Intent(NewChatActivity.this, AccountListActivity.class);
intent.putExtra("EXIT", true);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
*/
Uri packageURI = Uri.parse("package:info.guardianproject.otr.app.im");
Intent intent = new Intent(Intent.ACTION_DELETE, packageURI);
startActivity(intent);
}
});
btnDrawerGroupChat.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
showGroupChatDialog();
if (menu.isMenuShowing())
menu.toggle();
}
});
btnDrawerAddContact.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
showInviteContactDialog();
}
});
btnDrawerFingerprint.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
displayQRCode ();
}
});
}
private void showInviteContactDialog ()
{
if (mLastProviderId != -1 && mAccountId != -1)
{
Intent i = new Intent(this, AddContactActivity.class);
i.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, mLastProviderId);
i.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, mAccountId);
// i.putExtra(ImServiceConstants.EXTRA_INTENT_LIST_NAME,
// mContactListView.getSelectedContactList());
startActivity(i);
}
}
private void displayQRCode ()
{
try
{
if ( getCurrentChatSession() != null
&& getCurrentChatSession().getOtrChatSession() != null)
{
String localFingerprint = getCurrentChatSession().getOtrChatSession().getLocalFingerprint();
if (localFingerprint != null)
{
IntentIntegrator.shareText(this, localFingerprint);
return;
}
}
}
catch (RemoteException re)
{}
//did not work
Toast.makeText(this, "Please start a secure conversation before scanning codes", Toast.LENGTH_LONG).show();
}
void resolveIntent(Intent intent) {
if (requireOpenDashboardOnStart(intent)) {
long providerId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1L);
mAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,
-1L);
if (providerId == -1L || mAccountId == -1L) {
finish();
} else {
// mChatSwitcher.open();
}
return;
}
if (ImServiceConstants.ACTION_MANAGE_SUBSCRIPTION.equals(intent.getAction())) {
long providerId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1);
mAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,
-1L);
String from = intent.getStringExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS);
if ((providerId == -1) || (from == null)) {
finish();
} else {
//chatView.bindSubscription(providerId, from);
showSubscriptionDialog (providerId, from);
}
} else {
Uri data = intent.getData();
if (data != null)
{
if (data.getScheme() != null && data.getScheme().equals("immu"))
{
String user = data.getUserInfo();
String host = data.getHost();
String path = null;
if (data.getPathSegments().size() > 0)
path = data.getPathSegments().get(0);
if (host != null && path != null)
{
List<IImConnection> listConns = ((ImApp)getApplication()).getActiveConnections();
if (!listConns.isEmpty())
{
startGroupChat(path, host, listConns.get(0));
setResult(RESULT_OK);
}
}
} else {
String type = getContentResolver().getType(data);
if (Imps.Chats.CONTENT_ITEM_TYPE.equals(type)) {
long requestedContactId = ContentUris.parseId(data);
mCursorChats.moveToPosition(-1);
int posIdx = 1;
boolean foundChatView = false;
while (mCursorChats.moveToNext())
{
long chatId = mCursorChats.getLong(ChatView.CONTACT_ID_COLUMN);
if (chatId == requestedContactId)
{
mChatPager.setCurrentItem(posIdx);
foundChatView = true;
break;
}
posIdx++;
}
if (!foundChatView)
{
Uri.Builder builder = Imps.Contacts.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, requestedContactId);
Cursor cursor = getContentResolver().query(builder.build(), ChatView.CHAT_PROJECTION, null, null, null);
try {
if (cursor.getCount() > 0)
{
cursor.moveToFirst();
startChat(cursor);
}
} finally {
cursor.close();
}
}
} else if (Imps.Invitation.CONTENT_ITEM_TYPE.equals(type)) {
//chatView.bindInvitation(ContentUris.parseId(data));
}
}
}
else
{
mAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,-1L);
if (mContactList != null)
{
mChatPager.setCurrentItem(0);
mContactList.refreshSpinners();
mContactList.initAccount(this, mAccountId);
}
}
}
if (mContactList != null)
{
mContactList.setSpinnerState(this);
}
}
public void showChat (long requestedChatId)
{
mCursorChats.moveToPosition(-1);
int posIdx = 1;
while (mCursorChats.moveToNext())
{
long chatId = mCursorChats.getLong(ChatView.CONTACT_ID_COLUMN);
if (chatId == requestedChatId)
{
mChatPager.setCurrentItem(posIdx);
break;
}
posIdx++;
}
}
public void refreshChatViews ()
{
mChatPagerAdapter.notifyDataSetChanged();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.chat_screen_menu, menu);
return true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
//updateOtrMenuState();
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_send_image:
startImagePicker();
return true;
case R.id.menu_send_file:
startFilePicker();
return true;
case R.id.menu_view_profile:
if (getCurrentChatView() != null)
getCurrentChatView().viewProfile();
return true;
case R.id.menu_end_conversation:
if (getCurrentChatView() != null)
getCurrentChatView().closeChatSession();
return true;
case android.R.id.home:
finish();// close this view and return to account list
return true;
case R.id.menu_view_accounts:
startActivity(new Intent(getBaseContext(), ChooseAccountActivity.class));
// finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
/*
* this behavior doesn't make sense to me. i hit the back key
* and the chat disappears, as opposed to just going back to the message
* list. i think the user should have it to use the 'end chat' button to really clear a chat
* n8fr8 2012/09/26
*
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_DOWN) {
mChatView.closeChatSessionIfInactive();
}*/
return super.dispatchKeyEvent(event);
}
/** Check whether we are asked to open Dashboard on startup. */
private boolean requireOpenDashboardOnStart(Intent intent) {
return intent.getBooleanExtra(ImServiceConstants.EXTRA_INTENT_SHOW_MULTIPLE, false);
}
private void sendCallInvite ()
{
// getChatView().sendMessage("☎ Click to start call <a href=\"https://foo.com\">sip:" + this.mSipAccount + "</a>");
}
/*
public void updateOtrMenuState() {
ChatView chatView = getCurrentChatView ();
if (menuOtr == null || chatView == null)
return;
IOtrChatSession otrChatSession = chatView.getOtrChatSession();
if (otrChatSession != null) {
try {
SessionStatus sessionStatus = SessionStatus.values()[otrChatSession.getChatStatus()];
if (sessionStatus != SessionStatus.PLAINTEXT) {
menuOtr.setTitle(R.string.menu_otr_stop);
menuOtr.setIcon(this.getResources().getDrawable(R.drawable.ic_menu_encrypt));
} else {
menuOtr.setTitle(R.string.menu_otr_start);
menuOtr.setIcon(this.getResources().getDrawable(R.drawable.ic_menu_unencrypt));
}
} catch (RemoteException e) {
Log.d("NewChat", "Error accessing remote service", e);
}
} else {
menuOtr.setTitle(R.string.menu_otr_start);
}
}*/
/*
private void switchChat(int delta) {
ChatView chatView = getCurrentChatView ();
long providerId = chatView.getProviderId();
long accountId = chatView.getAccountId();
String contact = chatView.getUserName();
mChatSwitcher.rotateChat(delta, contact, accountId, providerId);
}*/
/*
private void startContactPicker() {
Uri.Builder builder = Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY.buildUpon();
ContentUris.appendId(builder, getChatView().getProviderId());
ContentUris.appendId(builder, getChatView().getAccountId());
Uri data = builder.build();
try {
Intent i = new Intent(Intent.ACTION_PICK, data);
i.putExtra(ContactsPickerActivity.EXTRA_EXCLUDED_CONTACTS, getChatView()
.getCurrentChatSession().getParticipants());
startActivityForResult(i, REQUEST_PICK_CONTACTS);
} catch (RemoteException e) {
mHandler.showServiceErrorAlert();
}
}*/
void startImagePicker() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_SEND_IMAGE);
}
void startFilePicker() {
Intent selectFile = new Intent(Intent.ACTION_GET_CONTENT);
selectFile.setType("file/*");
startActivityForResult(Intent.createChooser(selectFile, "Select File"), REQUEST_SEND_FILE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent resultIntent) {
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_SEND_IMAGE || requestCode == REQUEST_SEND_FILE) {
Uri uri = resultIntent.getData() ;
if( uri == null ) {
return ;
}
handleSend(uri);
}
/* if (requestCode == REQUEST_PICK_CONTACTS) {
String username = data.getStringExtra(ContactsPickerActivity.EXTRA_RESULT_USERNAME);
try {
IChatSession chatSession = getChatView().getCurrentChatSession();
if (chatSession.isGroupChatSession()) {
chatSession.inviteContact(username);
showInvitationHasSent(username);
} else {
chatSession.convertToGroupChat();
new ContactInvitor(chatSession, username).start();
}
} catch (RemoteException e) {
mHandler.showServiceErrorAlert();
}
}
*/ }
}
private void testSendIntent(Uri uri) {
Intent intent = new Intent(Intent.ACTION_SENDTO);
try {
String url = OtrDataHandler.URI_PREFIX_OTR_IN_BAND + URLEncoder.encode(uri.toString(), "UTF-8");
intent.setData(Uri.parse(url));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
startActivity(intent);
}
IChatSession getCurrentChatSession() {
int currentPos = mChatPager.getCurrentItem();
if (currentPos == 0)
return null;
mCursorChats.moveToPosition(currentPos - 1);
long providerId = mCursorChats.getLong(ChatView.PROVIDER_COLUMN);
String username = mCursorChats.getString(ChatView.USERNAME_COLUMN);
IChatSessionManager sessionMgr = getChatSessionManager(providerId);
if (sessionMgr != null) {
try {
IChatSession session = sessionMgr.getChatSession(username);
if (session == null)
session = sessionMgr.createChatSession(username);
return session;
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "send message error",e);
}
}
return null;
}
private IChatSessionManager getChatSessionManager(long providerId) {
IImConnection conn = mApp.getConnection(providerId);
if (conn != null) {
try {
return conn.getChatSessionManager();
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "send message error",e);
}
}
return null;
}
private void handleSend(Uri uri) {
try {
FileInfo info = SystemServices.getFileInfoFromURI(this, uri);
if (info != null)
{
IChatSession session = getCurrentChatSession();
if (session != null)
session.offerData( info.path, info.type );
}
else
{
Toast.makeText(this, R.string.sorry_we_cannot_share_that_file_type, Toast.LENGTH_LONG).show();
}
} catch (RemoteException e) {
Log.e(ImApp.LOG_TAG,"error sending file",e);
}
}
void showInvitationHasSent(String contact) {
Toast.makeText(NewChatActivity.this, getString(R.string.invitation_sent_prompt, contact),
Toast.LENGTH_SHORT).show();
}
private class ContactInvitor extends ChatListenerAdapter {
private final IChatSession mChatSession;
String mContact;
public ContactInvitor(IChatSession session, String data) {
mChatSession = session;
mContact = data;
}
@Override
public void onConvertedToGroupChat(IChatSession ses) {
try {
final long chatId = mChatSession.getId();
mChatSession.inviteContact(mContact);
mHandler.post(new Runnable() {
public void run() {
ChatView chatView = getCurrentChatView ();
if (chatView != null)
{
chatView.bindChat(chatId);
showInvitationHasSent(mContact);
}
}
});
mChatSession.unregisterChatListener(this);
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "group chat error",e);
}
}
public void start() throws RemoteException {
mChatSession.registerChatListener(this);
}
}
/** Show the context menu on a history item. */
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
ChatView chatView = getCurrentChatView ();
if (chatView != null)
{
AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
mMessageContextMenuHandler.mPosition = info.position;
Cursor cursor = chatView.getMessageAtPosition(info.position);
int type = cursor.getInt(cursor.getColumnIndexOrThrow(Imps.Messages.TYPE));
if (type == Imps.MessageType.OUTGOING) {
menu.add(0, MENU_RESEND, 0, R.string.menu_resend).setOnMenuItemClickListener(
mMessageContextMenuHandler);
}
}
}
final class MessageContextMenuHandler implements OnMenuItemClickListener {
int mPosition;
@Override
public boolean onMenuItemClick(android.view.MenuItem item) {
ChatView chatView = getCurrentChatView ();
if (chatView != null)
{
Cursor c;
c = chatView.getMessageAtPosition(mPosition);
switch (item.getItemId()) {
case MENU_RESEND:
String text = c.getString(c.getColumnIndexOrThrow(Imps.Messages.BODY));
chatView.getComposedMessage().setText(text);
break;
default:
return false;
} return false;
}
else
return false;
}
}
private class MyContentObserver extends ContentObserver {
public MyContentObserver() {
super(mHandler);
}
@Override
public void onChange(boolean selfChange) {
// Although we get this on our main thread, the dispatch is async and can happen after onDestroy
// Ensure we are still alive
if (mCursorChats != null)
refreshChatViews();
else
Log.w(TAG, "got onChange after onDestroy");
}
}
public class ChatViewPagerAdapter extends DynamicPagerAdapter {
private MyContentObserver mCursorObserver;
public ChatViewPagerAdapter(FragmentManager fm) {
super(fm);
mCursorChats = getContentResolver().query(Imps.Contacts.CONTENT_URI_CHAT_CONTACTS, ChatView.CHAT_PROJECTION, null, null, null);
mCursorObserver = new MyContentObserver();
mCursorChats.registerContentObserver(mCursorObserver);
}
public void onDestroy() {
mCursorChats.unregisterContentObserver(mCursorObserver);
mCursorChats.close();
mCursorChats = null;
}
@Override
public void notifyDataSetChanged() {
// In case that onDestroy was called first
// FIXME check if this can actually happen
if (mCursorChats == null)
return;
mCursorChats.unregisterContentObserver(mCursorObserver);
mCursorChats.close();
mCursorChats = getContentResolver().query(Imps.Contacts.CONTENT_URI_CHAT_CONTACTS, ChatView.CHAT_PROJECTION, null, null, null);
mCursorChats.registerContentObserver(mCursorObserver);
super.notifyDataSetChanged();
}
@Override
public int getCount() {
if (mCursorChats != null)
return mCursorChats.getCount() + 1;
else
return 0;
}
@Override
public Fragment getItem(int position) {
if (position == 0)
{
return (mContactList = new ContactListFragment());
}
else
{
int positionMod = position - 1;
mCursorChats.moveToPosition(positionMod);
long contactChatId = mCursorChats.getLong(ChatView.CONTACT_ID_COLUMN);
String contactName = mCursorChats.getString(ChatView.USERNAME_COLUMN);
long providerId = mCursorChats.getLong(ChatView.PROVIDER_COLUMN);
return ChatViewFragment.newInstance(contactChatId, contactName, providerId);
}
}
@Override
public int getItemPosition(Object object) {
if (object instanceof ChatViewFragment)
{
ChatViewFragment cvFrag = (ChatViewFragment)object;
ChatView view = cvFrag.getChatView();
long viewChatId = view.mLastChatId;
int position = PagerAdapter.POSITION_NONE;
// TODO: cache positions so we don't scan the cursor every time
if (mCursorChats.getCount() > 0)
{
mCursorChats.moveToFirst();
int posIdx = 1;
do {
long chatId = mCursorChats.getLong(ChatView.CHAT_ID_COLUMN);
if (chatId == viewChatId)
{
position = posIdx;
break;
}
posIdx++;
}
while (mCursorChats.moveToNext());
}
//` Log.d(TAG, "position of " + cvFrag.getArguments().getString("contactName") + " = " + position);
return position;
}
else if (object instanceof ContactListFragment)
{
return 0;
}
else {
throw new RuntimeException("got asked about an unknown fragment");
}
}
@Override
public CharSequence getPageTitle(int position) {
if (position == 0)
{
return getString(R.string.app_name);
}
else
{
int positionMod = position - 1;
mCursorChats.moveToPosition(positionMod);
if (!mCursorChats.isAfterLast())
return mCursorChats.getString(ChatView.NICKNAME_COLUMN);
else
return "";//unknown title
}
}
@Override
public Object instantiateItem(ViewGroup container, int pos) {
Object item = super.instantiateItem(container, pos);
if (pos > 0) {
ChatViewFragment frag = (ChatViewFragment)item;
}
return item;
}
@Override
public void destroyItem(ViewGroup container, int pos, Object object) {
super.destroyItem(container, pos, object);
}
public ChatView getChatViewAt(int pos) {
if (pos > 0)
{
ChatViewFragment frag = ((ChatViewFragment)getItemAt(pos));
if (frag != null)
return frag.getChatView();
}
throw new RuntimeException("could not get chat view at " + pos);
}
}
public static class ContactListFragment extends Fragment implements ContactListListener, ProviderListItem.SignInManager
{
private static final String[] PROVIDER_PROJECTION = {
Imps.Provider._ID,
Imps.Provider.NAME,
Imps.Provider.FULLNAME,
Imps.Provider.CATEGORY,
Imps.Provider.ACTIVE_ACCOUNT_ID,
Imps.Provider.ACTIVE_ACCOUNT_USERNAME,
Imps.Provider.ACTIVE_ACCOUNT_PW,
Imps.Provider.ACTIVE_ACCOUNT_LOCKED,
Imps.Provider.ACTIVE_ACCOUNT_KEEP_SIGNED_IN,
Imps.Provider.ACCOUNT_PRESENCE_STATUS,
Imps.Provider.ACCOUNT_CONNECTION_STATUS, };
static final int PROVIDER_ID_COLUMN = 0;
static final int PROVIDER_NAME_COLUMN = 1;
static final int PROVIDER_FULLNAME_COLUMN = 2;
static final int PROVIDER_CATEGORY_COLUMN = 3;
static final int ACTIVE_ACCOUNT_ID_COLUMN = 4;
static final int ACTIVE_ACCOUNT_USERNAME_COLUMN = 5;
static final int ACTIVE_ACCOUNT_PW_COLUMN = 6;
static final int ACTIVE_ACCOUNT_LOCKED = 7;
static final int ACTIVE_ACCOUNT_KEEP_SIGNED_IN = 8;
static final int ACCOUNT_PRESENCE_STATUS = 9;
static final int ACCOUNT_CONNECTION_STATUS = 10;
long[] mAccountIds = null;
ContactListFilterView mFilterView = null;
UserPresenceView mPresenceView = null;
Cursor mProviderCursor = null;
Spinner mSpinnerAccounts;
boolean showGrid = true;
ImApp mApp = null;
private Handler mPresenceHandler = new Handler()
{
@Override
public void handleMessage(Message msg) {
mPresenceView.refreshLogginInStatus();
super.handleMessage(msg);
}
};
public ContactListFragment() {
}
/**
* When creating, retrieve this instance's number from its arguments.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
/**
* The Fragment's UI is just a simple text view showing its
* instance number.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (showGrid)
mFilterView = (ContactListFilterView) inflater.inflate(
R.layout.contact_grid_filter_view, null);
else
mFilterView = (ContactListFilterView) inflater.inflate(
R.layout.contact_list_filter_view, null);
mPresenceView = (UserPresenceView) mFilterView.findViewById(R.id.userPresence);
mFilterView.setListener(this);
TextView txtEmpty = (TextView)mFilterView.findViewById(R.id.empty);
txtEmpty.setOnClickListener(new OnClickListener ()
{
@Override
public void onClick(View v) {
((NewChatActivity)getActivity()).showInviteContactDialog();
}
});
((AbsListView)mFilterView.findViewById(R.id.filteredList)).setEmptyView(txtEmpty);
// QueryMap mGlobalSettingMap = new Imps.ProviderSettings.QueryMap(getContext().getContentResolver(), true, mHandler);
// Uri uri = mGlobalSettingMap.getHideOfflineContacts() ? Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY
// : Imps.Contacts.CONTENT_URI_CONTACTS_BY;
// uri = ContentUris.withAppendedId(uri, providerId);
// uri = ContentUris.withAppendedId(uri, accountId);
// mFilterView.doFilter( Imps.Contacts.CONTENT_URI_CONTACTS_BY, null);
setupSpinners(mFilterView);
setSpinnerState(getActivity());
return mFilterView;
}
public void refreshSpinners ()
{
if (mProviderCursor != null && (!mProviderCursor.isClosed()))
mProviderCursor.close();
setupSpinners(mFilterView);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mApp = ((ImApp)activity.getApplication());
mApp.registerForConnEvents(mPresenceHandler);
}
@Override
public void onDetach() {
super.onDetach();
mApp.unregisterForConnEvents(mPresenceHandler);
mApp = null;
}
@Override
public void onDestroyView() {
if (mProviderCursor != null && (!mProviderCursor.isClosed()))
mProviderCursor.close();
mFilterView.setConnection(null);
super.onDestroyView();
}
private void setupSpinners (ContactListFilterView filterView)
{
mProviderCursor = getActivity().getContentResolver().query(Imps.Provider.CONTENT_URI_WITH_ACCOUNT, PROVIDER_PROJECTION,
Imps.Provider.CATEGORY + "=?" + " AND " + Imps.Provider.ACTIVE_ACCOUNT_USERNAME + " NOT NULL",
new String[] { ImApp.IMPS_CATEGORY } /* selection args */,
Imps.Provider.DEFAULT_SORT_ORDER);
if (mProviderCursor == null)
{
getActivity().finish();
return;
}
// + " AND " + Imps.Provider.ACCOUNT_CONNECTION_STATUS + " != 0"
/* selection */
mAccountIds = new long[mProviderCursor.getCount()];
mProviderCursor.moveToFirst();
ProviderAdapter pAdapter = new ProviderAdapter(getActivity(), mProviderCursor);
mSpinnerAccounts = (Spinner)filterView.findViewById(R.id.spinnerAccounts);
mSpinnerAccounts.setAdapter(pAdapter);
mSpinnerAccounts.setOnItemSelectedListener(new OnItemSelectedListener ()
{
@Override
public void onItemSelected(AdapterView<?> parent, View view, int itemPosition, long id) {
// mAccountId = mAccountIds[itemPosition];
//update account list
initAccount(getActivity(),mAccountIds[itemPosition]);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
});
mProviderCursor.moveToFirst();
int activeAccountIdColumn = mProviderCursor.getColumnIndexOrThrow(Imps.Provider.ACTIVE_ACCOUNT_ID);
for (int i = 0; i < mAccountIds.length; i++)
{
mAccountIds[i] = mProviderCursor.getLong(activeAccountIdColumn);
mProviderCursor.moveToNext();
}
}
public void setSpinnerState (Activity activity)
{
NewChatActivity chatActivity = (NewChatActivity)activity;
if (mAccountIds == null)
return;
if (mAccountIds.length == 1) //only one account, hide the spinner
{
mSpinnerAccounts.setVisibility(View.GONE);
initAccount(activity,mAccountIds[0]);
}
else if (chatActivity.getAccountId() != -1) //multiple accounts, so select a spinner based on user input
{
mSpinnerAccounts.setVisibility(View.VISIBLE);
int selIdx = 0;
for (long accountId : mAccountIds)
{
if (accountId == chatActivity.getAccountId())
{
mSpinnerAccounts.setSelection(selIdx);
break;
}
selIdx++;
}
}
else if (getActivity() != null) //nothing from the user, show show an active account
{
List<IImConnection> listConns = ((ImApp)getActivity().getApplication()).getActiveConnections();
for (IImConnection conn : listConns)
{
try
{
long activeAccountId = conn.getAccountId();
int spinnerIdx = -1;
for (long accountId : mAccountIds )
{
spinnerIdx++;
if (accountId == activeAccountId)
{
mSpinnerAccounts.setSelection(spinnerIdx);
break;
}
}
}
catch (Exception e){}
}
}
}
public void initAccount (Activity activity, long accountId)
{
if (accountId == -1)
return;
ContentResolver cr = activity.getContentResolver();
Cursor c = cr.query(ContentUris.withAppendedId(Imps.Account.CONTENT_URI, accountId), null,
null, null, null);
if (c == null) {
// finish();
return;
}
if (!c.moveToFirst()) {
c.close();
// finish();
return;
}
long providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Account.PROVIDER));
((NewChatActivity)activity).setLastProviderId(providerId);
// FIXME doesn't mAccountId need to be set here?
initConnection (activity, accountId, providerId);
c.close();
}
private void initConnection (Activity activity, long accountId, long providerId)
{
IImConnection conn = ((ImApp)activity.getApplication()).getConnection(providerId);
if (conn == null)
{
try {
conn = ((ImApp)getActivity().getApplication()).createConnection(providerId, accountId);
} catch (RemoteException e) {
Log.e(ImApp.LOG_TAG,"error creating connection",e);
}
}
if (conn != null)
{
mFilterView.setConnection(conn);
mPresenceView.setConnection(conn);
try {
mPresenceView.loggingIn(conn.getState() == ImConnection.LOGGING_IN);
} catch (RemoteException e) {
mPresenceView.loggingIn(false);
// mHandler.showServiceErrorAlert();
}
QueryMap settingMap = new Imps.ProviderSettings.QueryMap(activity.getContentResolver(), false, null);
Uri baseUri = settingMap.getHideOfflineContacts() ? Imps.Contacts.CONTENT_URI_ONLINE_CONTACTS_BY
: Imps.Contacts.CONTENT_URI_CONTACTS_BY;
settingMap.close();
Uri.Builder builder = baseUri.buildUpon();
ContentUris.appendId(builder, providerId);
ContentUris.appendId(builder, accountId);
mFilterView.doFilter(builder.build(), null);
}
}
@Override
public void startChat(Cursor c) {
NewChatActivity activity = (NewChatActivity)getActivity();
activity.startChat(c);
}
public void showProfile (Cursor c)
{
if (c != null) {
long chatContactId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts._ID));
long providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts.PROVIDER));
long accountId = c.getLong(c.getColumnIndex(Imps.Contacts.ACCOUNT));
Uri data = ContentUris.withAppendedId(Imps.Contacts.CONTENT_URI, chatContactId);
Intent intent = new Intent(Intent.ACTION_VIEW, data);
intent.putExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, providerId);
intent.putExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, accountId);
startActivity(intent);
}
}
public class ProviderAdapter extends CursorAdapter {
private LayoutInflater mInflater;
@SuppressWarnings("deprecation")
public ProviderAdapter(Context context, Cursor c) {
super(context, c);
mInflater = LayoutInflater.from(context).cloneInContext(context);
mInflater.setFactory(new ProviderListItemFactory());
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
// create a custom view, so we can manage it ourselves. Mainly, we want to
// initialize the widget views (by calling getViewById()) in newView() instead of in
// bindView(), which can be called more often.
ProviderListItem view = (ProviderListItem) mInflater.inflate(R.layout.account_view_small,
parent, false);
view.init(cursor, true);
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
((ProviderListItem) view).bindView(cursor);
}
}
public class ProviderListItemFactory implements LayoutInflater.Factory {
public View onCreateView(String name, Context context, AttributeSet attrs) {
if (name != null && name.equals(ProviderListItem.class.getName())) {
return new ProviderListItem(context, getActivity(), ContactListFragment.this);
}
return null;
}
}
@Override
public void signIn(long accountId) {
throw new UnsupportedOperationException("not implemented");
}
@Override
public void signOut(long accountId) {
throw new UnsupportedOperationException("not implemented");
}
}
private void startChat(Cursor c) {
if (c != null && (! c.isAfterLast())) {
long chatContactId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts._ID));
String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME));
long providerId = mLastProviderId;//FIXME c.getLong(c.getColumnIndexOrThrow(Imps.Contacts.PROVIDER));
IImConnection conn = mApp.getConnection(providerId);
if (conn != null)
{
try {
IChatSessionManager manager = conn.getChatSessionManager();
IChatSession session = manager.getChatSession(username);
if (session == null) {
manager.createChatSession(username);
}
refreshChatViews(); // Refresh early so that we can jump to the chat
showChat(chatContactId);
} catch (RemoteException e) {
// mHandler.showServiceErrorAlert(e.getMessage());
LogCleaner.debug(ImApp.LOG_TAG, "remote exception starting chat");
}
}
else
{
LogCleaner.debug(ImApp.LOG_TAG, "could not start chat as connection was null");
}
}
}
public static class ChatViewFragment extends Fragment {
ChatView mChatView;
/**
* Create a new instance of CountingFragment, providing "num"
* as an argument.
* @param providerId
* @param contactName
*/
static ChatViewFragment newInstance(long chatContactId, String contactName, long providerId) {
ChatViewFragment f = new ChatViewFragment();
// Supply num input as an argument.
Bundle args = new Bundle();
args.putLong("contactChatId", chatContactId);
args.putString("contactName", contactName);
args.putLong("providerId", providerId);
f.setArguments(args);
// Log.d(TAG, "CVF new " + contactName);
return f;
}
public ChatViewFragment() {
// Log.d(TAG, "CVF construct " + super.toString());
}
public String toString() {
return super.toString() + " -> " + getArguments().getString("contactName");
}
public void onSelected(ImApp app) {
app.dismissChatNotification(getArguments().getLong("providerId"), getArguments().getString("contactName"));
if (mChatView != null)
mChatView.setSelected(true);
}
public void onDeselected(ImApp app) {
if (mChatView != null)
mChatView.setSelected(false);
}
/**
* When creating, retrieve this instance's number from its arguments.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Log.d(TAG, "CVF create " + getArguments().getString("contactName"));
}
/**
* The Fragment's UI is just a simple text view showing its
* instance number.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
long chatContactId = getArguments().getLong("contactChatId");
mChatView = (ChatView)inflater.inflate(R.layout.chat_view, container, false);
mChatView.bindChat(chatContactId);
return mChatView;
}
public void onServiceConnected() {
if (isResumed()) {
mChatView.onServiceConnected();
}
}
@Override
public void onResume() {
super.onResume();
mChatView.startListening();
// Log.d(TAG, "CVF resume " + getArguments().getString("contactName") + " " + this);
}
@Override
public void onPause() {
super.onPause();
mChatView.stopListening();
// Log.d(TAG, "CVF pause " + getArguments().getString("contactName") + " " + this);
}
@Override
public void onDestroy() {
mChatView.unbind();
super.onDestroy();
// Log.d(TAG, "CVF destroy " + getArguments().getString("contactName") + " " + this);
}
public ChatView getChatView() {
return mChatView;
}
}
public ChatView getCurrentChatView ()
{
int cItemIdx;
if ((cItemIdx = mChatPager.getCurrentItem()) > 0)
{
return mChatPagerAdapter.getChatViewAt(cItemIdx);
}
else
return null;
}
private void showGroupChatDialog ()
{
ContentResolver cr = getContentResolver();
Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(
cr, mLastProviderId, false /* don't keep updated */, null /* no handler */);
String chatDomain = "conference." + settings.getDomain();
settings.close();
// This example shows how to add a custom layout to an AlertDialog
LayoutInflater factory = LayoutInflater.from(this);
final View textEntryView = factory.inflate(R.layout.alert_dialog_group_chat, null);
final TextView tvServer = (TextView) textEntryView.findViewById(R.id.chat_server);
tvServer.setText(chatDomain);
new AlertDialog.Builder(this)
.setTitle(R.string.create_or_join_group_chat)
.setView(textEntryView)
.setPositiveButton(R.string.connect, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked OK so do some stuff */
String chatRoom = null;
String chatServer = null;
TextView tv = (TextView)textEntryView.findViewById(R.id.chat_room);
chatRoom = tv.getText().toString();
tv = (TextView) textEntryView.findViewById(R.id.chat_server);
chatServer = tv.getText().toString();
startGroupChat (chatRoom, chatServer, ((ImApp)getApplication()).getConnection(mLastProviderId));
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked cancel so do some stuff */
}
})
.create().show();
}
public void startGroupChat (String room, String server, IImConnection conn)
{
String roomAddress = room + '@' + server;
try {
IChatSessionManager manager = conn.getChatSessionManager();
IChatSession session = manager.getChatSession(roomAddress);
if (session == null) {
session = manager.createMultiUserChatSession(roomAddress);
}
if (session != null)
{
long id = session.getId();
showChat(id);
//Uri data = ContentUris.withAppendedId(Imps.Chats.CONTENT_URI, id);
//Intent i = new Intent(Intent.ACTION_VIEW, data);
//i.addCategory(ImApp.IMPS_CATEGORY);
//startActivity(i);
}
else
{
mHandler.showServiceErrorAlert(getString(R.string.unable_to_create_or_join_group_chat));
}
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(getString(R.string.unable_to_create_or_join_group_chat));
}
}
void acceptInvitation(long providerId, long invitationId) {
try {
IImConnection conn = mApp.getConnection(providerId);
if (conn != null) {
conn.acceptInvitation(invitationId);
}
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "accept invite error",e);
}
}
void declineInvitation(long providerId, long invitationId) {
try {
IImConnection conn = mApp.getConnection(providerId);
if (conn != null) {
conn.rejectInvitation(invitationId);
}
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "decline invite error",e);
}
}
void showSubscriptionDialog (final long subProviderId, final String subFrom)
{
new AlertDialog.Builder(this)
.setTitle(getString(R.string.subscriptions))
.setMessage(getString(R.string.subscription_prompt,subFrom))
.setPositiveButton(R.string.approve_subscription, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
approveSubscription(subProviderId, subFrom);
}
})
.setNegativeButton(R.string.decline_subscription, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
declineSubscription(subProviderId, subFrom);
}
})
.create().show();
}
void approveSubscription(long providerId, String userName) {
IImConnection conn = mApp.getConnection(providerId);
try {
IContactListManager manager = conn.getContactListManager();
manager.approveSubscription(userName);
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "approve sub error",e);
}
}
void declineSubscription(long providerId, String userName) {
IImConnection conn = mApp.getConnection(providerId);
if (conn != null)
{
try {
IContactListManager manager = conn.getContactListManager();
manager.declineSubscription(userName);
} catch (RemoteException e) {
mHandler.showServiceErrorAlert(e.getLocalizedMessage());
LogCleaner.error(ImApp.LOG_TAG, "decline sub error",e);
}
}
}
long getAccountId() {
return mAccountId;
}
long getLastProviderId() {
return mLastProviderId;
}
void setAccountId(long mAccountId) {
this.mAccountId = mAccountId;
}
void setLastProviderId(long mLastProviderId) {
this.mLastProviderId = mLastProviderId;
}
}