/* * 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.iocipher.VirtualFileSystem; import info.guardianproject.otr.IOtrChatSession; import info.guardianproject.otr.OtrAndroidKeyManagerImpl; import info.guardianproject.otr.OtrChatManager; import info.guardianproject.otr.app.im.IChatSession; import info.guardianproject.otr.app.im.IChatSessionManager; import info.guardianproject.otr.app.im.IContactList; import info.guardianproject.otr.app.im.IContactListListener; import info.guardianproject.otr.app.im.IContactListManager; import info.guardianproject.otr.app.im.IImConnection; import info.guardianproject.otr.app.im.ISubscriptionListener; import info.guardianproject.otr.app.im.R; import info.guardianproject.otr.app.im.app.ContactListFilterView.ContactListListener; import info.guardianproject.otr.app.im.engine.Contact; import info.guardianproject.otr.app.im.engine.ImConnection; import info.guardianproject.otr.app.im.engine.ImErrorInfo; import info.guardianproject.otr.app.im.plugin.xmpp.XmppAddress; import info.guardianproject.otr.app.im.provider.Imps; import info.guardianproject.otr.app.im.service.ImServiceConstants; import info.guardianproject.otr.app.im.ui.SecureCameraActivity; import info.guardianproject.util.LogCleaner; import info.guardianproject.util.SystemServices; import info.guardianproject.util.SystemServices.FileInfo; import info.guardianproject.util.XmppUriHelper; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.UUID; import net.java.otr4j.OtrPolicy; import net.java.otr4j.session.SessionStatus; import org.ironrabbit.type.CustomTypefaceManager; import org.jivesoftware.smackx.muc.MultiUserChat; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.Cursor; import android.graphics.drawable.Drawable; import android.media.MediaScannerConnection; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.provider.MediaStore; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.view.GravityCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.SimpleOnPageChangeListener; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar.OnMenuItemClickListener; import android.text.SpannableString; import android.text.style.ImageSpan; 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.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; 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.SimpleCursorAdapter; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; 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 static final int REQUEST_SEND_AUDIO = REQUEST_SEND_FILE + 1; private static final int REQUEST_TAKE_PICTURE = REQUEST_SEND_AUDIO + 1; private static final int REQUEST_SETTINGS = REQUEST_TAKE_PICTURE + 1; private static final int REQUEST_TAKE_PICTURE_SECURE = REQUEST_SETTINGS + 1; private static final int REQUEST_ADD_CONTACT = REQUEST_TAKE_PICTURE_SECURE + 1; private static final int CONTACT_LIST_LOADER_ID = 4444; private static final int CHAT_LIST_LOADER_ID = CONTACT_LIST_LOADER_ID+1; private static final int CHAT_PAGE_LOADER_ID = CONTACT_LIST_LOADER_ID+2; private ImApp mApp; private ViewPager mChatPager; private ChatViewPagerAdapter mChatPagerAdapter; private Toolbar mToolbar; private DrawerLayout mDrawer; private ActionBarDrawerToggle mDrawerToggle; private int mLastPagePosition = -1; private SimpleAlertHandler mHandler; private long mLastAccountId = -1; private long mLastProviderId = -1; private boolean mShowChatsOnly = true; private MessageContextMenuHandler mMessageContextMenuHandler; private ContactListFragment mContactList = null; private Imps.ProviderSettings.QueryMap mGlobalSettings = null; 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); checkCustomFont (); mApp = (ImApp)getApplication(); mApp.maybeInit(this); setContentView(R.layout.chat_pager); mToolbar = (Toolbar) findViewById(R.id.toolbar); mApp.setAppTheme(this,mToolbar); ThemeableActivity.setBackgroundImage(this); mToolbar.inflateMenu(R.menu.new_chat_menu); setupMenu (); setTitle(R.string.app_name); mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle( this, mDrawer, mToolbar, R.string.ok, R.string.cancel ); // Set the drawer toggle as the DrawerListener mDrawer.setDrawerListener(mDrawerToggle); mDrawerToggle.setDrawerIndicatorEnabled(true); mDrawerToggle.syncState(); mDrawerToggle.setToolbarNavigationClickListener(new OnClickListener () { @Override public void onClick(View v) { int currentPos = mChatPager.getCurrentItem(); if (currentPos > 0) { mChatPager.setCurrentItem(0); } } }); mHandler = new MyHandler(this); mRequestedChatId = -1; ((Button)findViewById(R.id.btnAddAccount)).setOnClickListener (new OnClickListener () { @Override public void onClick(View v) { // TODO Auto-generated method stub startAccountSetup (); } }); mChatPager = (ViewPager) findViewById(R.id.chatpager); //mChatPager.setSaveEnabled(false); //mChatPager.setOffscreenPageLimit(3); //mChatPager.setDrawingCacheEnabled(true); //mChatPager.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_AUTO); mChatPager.setOnPageChangeListener(new SimpleOnPageChangeListener () { @Override public void onPageSelected(int pos) { if (pos > 0) { if (mLastPagePosition != -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); mLastPagePosition = pos; if (mMenu != null) { mMenu.setGroupVisible(R.id.menu_group_chats, true); mMenu.setGroupVisible(R.id.menu_group_contacts, false); } mDrawerToggle.setHomeAsUpIndicator(R.drawable.abc_ic_ab_back_mtrl_am_alpha); mDrawerToggle.setDrawerIndicatorEnabled(false); mDrawerToggle.syncState(); } else { if (mMenu != null) { mMenu.setGroupVisible(R.id.menu_group_chats, false); mMenu.setGroupVisible(R.id.menu_group_contacts, true); mMenu.setGroupVisible(R.id.menu_group_otr_verified,false); mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false); mMenu.setGroupVisible(R.id.menu_group_otr_off,false); } setTitle(R.string.app_name); mDrawerToggle.setHomeAsUpIndicator(null); mDrawerToggle.setDrawerIndicatorEnabled(true); mDrawerToggle.syncState(); } } }); 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); initConnections(); } private void initChats () { LoaderManager lMgr =getSupportLoaderManager(); lMgr.initLoader(CHAT_LIST_LOADER_ID, null, new LoaderManager.LoaderCallbacks<Cursor> () { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { CursorLoader loader = new CursorLoader(NewChatActivity.this, Imps.Contacts.CONTENT_URI_CHAT_CONTACTS, ChatView.CHAT_PROJECTION, null, null, null); loader.setUpdateThrottle(100L); return loader; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor newCursor) { mChatPagerAdapter.swapCursor(newCursor); updateChatList(); if (getIntent() != null) resolveIntent(getIntent()); if (mRequestedChatId >= 0) { if (showChat(mRequestedChatId)) { mRequestedChatId = -1; } } } @Override public void onLoaderReset(Loader<Cursor> loader) { mChatPagerAdapter.swapCursor(null); } }); } @Override public void setTitle(CharSequence title) { mToolbar.setTitle(title); // mToolbar.setLogo(null); } public void setTitle(CharSequence title, Drawable icon) { mToolbar.setTitle(title); // mToolbar.setLogo(icon); } private void checkCustomFont () { if (CustomTypefaceManager.getCurrentTypeface(this)==null) { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); List<InputMethodInfo> mInputMethodProperties = imm.getEnabledInputMethodList(); final int N = mInputMethodProperties.size(); for (int i = 0; i < N; i++) { InputMethodInfo imi = mInputMethodProperties.get(i); //imi contains the information about the keyboard you are using if (imi.getPackageName().equals("org.ironrabbit.bhoboard")) { CustomTypefaceManager.loadFromKeyboard(this); break; } } } } /* * 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() { unregisterSubListeners (); if (mGlobalSettings != null) { mGlobalSettings.close(); mGlobalSettings = null; } mApp.unregisterForBroadcastEvent(ImApp.EVENT_SERVICE_CONNECTED, mHandler); mChatPagerAdapter.swapCursor(null); //mAdapter.swapCursor(null); super.onDestroy(); mChatPagerAdapter = null; // mAdapter = null; } @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(); mApp.getTrustManager().bindDisplayActivity(this); mApp.checkForCrashes(this); //if VFS is not mounted, then send to WelcomeActivity if (!VirtualFileSystem.get().isMounted()) { finish(); startActivity(new Intent(this,WelcomeActivity.class)); } } @Override protected void onPause() { super.onPause(); mApp.getTrustManager().unbindDisplayActivity(this); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); resolveIntent(intent); } @Override public void onBackPressed() { int currentPos = mChatPager.getCurrentItem(); if (currentPos > 0) { mChatPager.setCurrentItem(0); return; } super.onBackPressed(); } private void showInviteContactDialog () { Intent i = new Intent(this, AddContactActivity.class); startActivityForResult(i,REQUEST_ADD_CONTACT); } private IOtrChatSession getCurrentOtrChatSession() throws RemoteException { IChatSession chatSession = getCurrentChatSession(); if (chatSession == null) return null; else return chatSession.getOtrChatSession(); } private void displayQRCode () { try { IOtrChatSession iOtr = getCurrentOtrChatSession(); if (iOtr != null) { String localUser = iOtr.getLocalUserId(); String localFingerprint = iOtr.getLocalFingerprint(); if (localFingerprint != null) { String otrKeyURI = XmppUriHelper.getUri(localUser, localFingerprint); new IntentIntegrator(this).shareText(otrKeyURI); return; } } } catch (RemoteException re) { } //did not work Toast.makeText(this, R.string.please_start_a_secure_conversation_before_scanning_codes, Toast.LENGTH_LONG).show(); } private void resolveIntent(Intent intent) { doResolveIntent(intent); setIntent(null); } private IImConnection findConnectionForGroupChat (String user, String host) { Collection<IImConnection> connActive = mApp.getActiveConnections(); ContentResolver cr = this.getContentResolver(); IImConnection result = null; for (IImConnection conn : connActive) { try { Cursor pCursor = cr.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString( conn.getProviderId())},null); Imps.ProviderSettings.QueryMap settings = new Imps.ProviderSettings.QueryMap(pCursor, cr, conn.getProviderId(), false /* keep updated */, mHandler /* no handler */); if (host.contains(settings.getDomain())) { if (conn.getState() == ImConnection.LOGGED_IN) { result = conn; settings.close(); pCursor.close(); break; } } settings.close(); pCursor.close(); } catch (RemoteException e){//nothing to do here } } return result; } private void doResolveIntent(Intent intent) { if (requireOpenDashboardOnStart(intent)) { long providerId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1L); mLastAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, -1L); if (providerId == -1L || mLastAccountId == -1L) { finish(); } else { // mChatSwitcher.open(); } return; } if (ImServiceConstants.ACTION_MANAGE_SUBSCRIPTION.equals(intent.getAction())) { long providerId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_PROVIDER_ID, -1); mLastAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID, -1L); String from = intent.getStringExtra(ImServiceConstants.EXTRA_INTENT_FROM_ADDRESS); if ((providerId == -1) || (from == null)) { finish(); } else { showSubscriptionDialog (providerId, from); } } else if (intent != null) { Uri data = intent.getData(); if (intent.getBooleanExtra("showaccounts", false)) mDrawer.openDrawer(GravityCompat.START); 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) { IImConnection connMUC = findConnectionForGroupChat(user, host); if (connMUC != null) { startGroupChat (path, host, user, connMUC); setResult(RESULT_OK); } else { mHandler.showAlert("Connection Error", "Unable to find a connection to join a group chat from. Please sign in and try again."); setResult(Activity.RESULT_CANCELED); finish(); } } } else { String type = getContentResolver().getType(data); if (Imps.Chats.CONTENT_ITEM_TYPE.equals(type)) { long requestedContactId = ContentUris.parseId(data); Cursor cursorChats = mChatPagerAdapter.getCursor(); if (cursorChats != null) { cursorChats.moveToPosition(-1); int posIdx = 1; boolean foundChatView = false; while (cursorChats.moveToNext()) { long chatId = cursorChats.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(); openExistingChat(cursor); } } finally { cursor.close(); } } } } else if (Imps.Invitation.CONTENT_ITEM_TYPE.equals(type)) { //chatView.bindInvitation(ContentUris.parseId(data)); } } } else if (intent.hasExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID)) { //set the current account id mLastAccountId = intent.getLongExtra(ImServiceConstants.EXTRA_INTENT_ACCOUNT_ID,-1L); //move the pager back to the first page if (mChatPager != null) mChatPager.setCurrentItem(0); } else { // refreshConnections(); } } } public boolean showChat (long requestedChatId) { Cursor cursorChats = mChatPagerAdapter.getCursor(); if (cursorChats == null) return false; cursorChats.moveToPosition(-1); int posIdx = 1; while (cursorChats.moveToNext()) { long chatId = cursorChats.getLong(ChatView.CONTACT_ID_COLUMN); if (chatId == requestedChatId) { mChatPager.setCurrentItem(posIdx); return true; } posIdx++; } // Was not found return false; } public void refreshChatViews () { mChatPagerAdapter.notifyDataSetChanged(); } private Menu mMenu; // private AccountAdapter mAdapter; protected Long[][] mAccountIds; private long mRequestedChatId; public void updateEncryptionMenuState () { ChatView cView = getCurrentChatView(); if (cView == null) return; if (mChatPager != null && mMenu != null) { if (mChatPager.getCurrentItem() > 0) { // phoenix-nz - a group chat should not be shown as 'unverified' as it (currently) // cannot be verified. Thus, show as neutral. if (cView.isGroupChat()) { mMenu.setGroupVisible(R.id.menu_group_otr_verified,false); mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false); mMenu.setGroupVisible(R.id.menu_group_otr_off,false); } else if (cView.getOtrSessionStatus() == SessionStatus.ENCRYPTED && cView.isOtrSessionVerified()) { mMenu.setGroupVisible(R.id.menu_group_otr_verified,true); mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false); mMenu.setGroupVisible(R.id.menu_group_otr_off,false); } else if (cView.getOtrSessionStatus() == SessionStatus.ENCRYPTED) { mMenu.setGroupVisible(R.id.menu_group_otr_unverified,true); mMenu.setGroupVisible(R.id.menu_group_otr_verified,false); mMenu.setGroupVisible(R.id.menu_group_otr_off,false); } else if (cView.getOtrSessionStatus() == SessionStatus.FINISHED) { mMenu.setGroupVisible(R.id.menu_group_otr_unverified,true); mMenu.setGroupVisible(R.id.menu_group_otr_verified,false); mMenu.setGroupVisible(R.id.menu_group_otr_off,false); } else { mMenu.setGroupVisible(R.id.menu_group_otr_off,true); mMenu.setGroupVisible(R.id.menu_group_otr_verified,false); mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false); } } } } private void setupMenu () { mMenu = mToolbar.getMenu(); if (mMenu != null) { if (mChatPager != null && mChatPager.getCurrentItem() > 0) { mMenu.setGroupVisible(R.id.menu_group_chats, true); mMenu.setGroupVisible(R.id.menu_group_contacts, false); } else { mMenu.setGroupVisible(R.id.menu_group_chats, false); mMenu.setGroupVisible(R.id.menu_group_contacts, true); mMenu.setGroupVisible(R.id.menu_group_otr_verified,false); mMenu.setGroupVisible(R.id.menu_group_otr_unverified,false); mMenu.setGroupVisible(R.id.menu_group_otr_off,false); } } mToolbar.setOnMenuItemClickListener(new OnMenuItemClickListener () { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { /* case R.id.menu_send_image: if (getCurrentChatView() != null && getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED) { startImagePicker(); } else { mHandler.showServiceErrorAlert(getString(R.string.please_enable_chat_encryption_to_share_files)); } return true; case R.id.menu_take_picture: if (getCurrentChatView() != null && getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED) { startPhotoTaker(); } else { mHandler.showServiceErrorAlert(getString(R.string.please_enable_chat_encryption_to_share_files)); } return true; case R.id.menu_send_file: if (getCurrentChatView() != null && getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED) { startFilePicker(); } else { mHandler.showServiceErrorAlert(getString(R.string.please_enable_chat_encryption_to_share_files)); } return true; case R.id.menu_send_audio: if (getCurrentChatView() != null && getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED) { startAudioPicker(); } else { mHandler.showServiceErrorAlert(getString(R.string.please_enable_chat_encryption_to_share_files)); } return true; */ case R.id.menu_verify_or_view: case R.id.menu_view_profile_verified: if (getCurrentChatView() != null) getCurrentChatView().showVerifyDialog(); return true; case R.id.menu_show_qr: displayQRCode(); return true; case R.id.menu_end_conversation: try { endCurrentChatPrompt( getCurrentSessionId()); } catch (Exception e) { Toast.makeText(NewChatActivity.this, "Error:" + e.getMessage(), Toast.LENGTH_LONG).show(); // TODO i18n e.printStackTrace(); } return true; /* case R.id.menu_delete_conversation: if (getCurrentChatView() != null) getCurrentChatView().closeChatSession(true); return true; */ case R.id.menu_settings: Intent sintent = new Intent(NewChatActivity.this, SettingActivity.class); startActivityForResult(sintent,REQUEST_SETTINGS); return true; case R.id.menu_otr: case R.id.menu_otr_stop: case R.id.menu_otr_stop_verified: if (getCurrentChatView() != null) { boolean isEnc = (getCurrentChatView().getOtrSessionStatus() == SessionStatus.ENCRYPTED || getCurrentChatView().getOtrSessionStatus() == SessionStatus.FINISHED ); getCurrentChatView().setOTRState(!isEnc); } return true; case android.R.id.home: int currentPos = mChatPager.getCurrentItem(); if (currentPos > 0) { mChatPager.setCurrentItem(0); } return true; case R.id.menu_view_accounts: startAccountSetup(); return true; case R.id.menu_new_chat: startContactPicker(); return true; case R.id.menu_exit: WelcomeActivity.shutdownAndLock(NewChatActivity.this); ExitActivity.exitAndRemoveFromRecentApps(NewChatActivity.this); return true; case R.id.menu_add_contact: showInviteContactDialog(); return true; case R.id.menu_group_chat: showGroupChatDialog(); return true; case R.id.menu_import_keys: importKeyStore(); return true; } return false; } }); } private void startAccountSetup () { startActivity(new Intent(getBaseContext(), ChooseAccountActivity.class)); } private void importKeyStore () { OtrAndroidKeyManagerImpl.checkForKeyImport(getIntent(), this); } private void endCurrentChatPrompt( final String sessionId ) { OtrChatManager otrChatManager = OtrChatManager.getInstance(); if (otrChatManager != null) { try { IOtrChatSession otrSession = getCurrentOtrChatSession(); otrSession.stopChatEncryption(); } catch (RemoteException e) { } } // if no files to delete - just end the session if (!ChatFileStore.sessionExists(sessionId)) { endCurrentChat(); return; } new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert) .setTitle(getString(R.string.end_chat_title)) .setMessage(getString(R.string.end_chat_summary)) .setPositiveButton(getString(R.string.end_chat_and_delete), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { endCurrentChat(); } }) .setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { return; } }) .show(); } private void endCurrentChat() { if (getCurrentChatView() != null) { try { // delete the chat session's files if any deleteSessionVfs( getCurrentSessionId() ); } catch (Exception e) { // TODO error e.printStackTrace(); } getCurrentChatView().closeChatSession(true); } updateChatList(); mChatPager.setCurrentItem(0); } private void deleteSessionVfs( final String sessionId ) throws Exception { // if no files to delete - bail if (!ChatFileStore.sessionExists(sessionId)) { return; } // delete ChatFileStore.deleteSession(sessionId); } private void startContactPicker() { Uri.Builder builder = Imps.Contacts.CONTENT_URI_CONTACTS_BY.buildUpon(); Uri data = builder.build(); Intent i = new Intent(Intent.ACTION_PICK, data); i.putExtra("invitations", false); startActivityForResult(i, REQUEST_PICK_CONTACTS); } @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); } void startImagePicker() { Intent intent = new Intent(Intent.ACTION_PICK); intent.setType("image/*"); startActivityForResult(intent, REQUEST_SEND_IMAGE); } Uri mLastPhoto = null; void startPhotoTaker() { // create Intent to take a picture and return control to the calling application Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); File photo = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "cs_" + new Date().getTime() + ".jpg"); mLastPhoto = Uri.fromFile(photo); intent.putExtra(MediaStore.EXTRA_OUTPUT, mLastPhoto); // start the image capture Intent startActivityForResult(intent, REQUEST_TAKE_PICTURE); } void startPhotoTakerSecure() { // create Intent to take a picture and return control to the calling application Intent intent = new Intent(this, SecureCameraActivity.class); String time = ""+new Date().getTime(); String filename = "/" + Environment.DIRECTORY_DCIM + "/" + "cs_" + time + ".jpg"; String thumbnail = "/" + Environment.DIRECTORY_DCIM + "/" + "cs_" + time + "_thumb.jpg"; intent.putExtra(SecureCameraActivity.FILENAME, filename ) ; intent.putExtra(SecureCameraActivity.THUMBNAIL, thumbnail ) ; // start the secure image capture Intent startActivityForResult(intent, REQUEST_TAKE_PICTURE_SECURE); } void startFilePicker() { Intent selectFile = new Intent(Intent.ACTION_GET_CONTENT); selectFile.setType("file/*"); Intent intentChooser = Intent.createChooser(selectFile, "Select File"); if (intentChooser != null) startActivityForResult(Intent.createChooser(selectFile, "Select File"), REQUEST_SEND_FILE); } void startAudioPicker() { Intent intent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION); if (!isCallable(intent)) { intent = new Intent("android.provider.MediaStore.RECORD_SOUND"); intent.addCategory("android.intent.category.DEFAULT"); if (!isCallable(intent)) { intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("audio/*"); if (!isCallable(intent)) return; } } startActivityForResult(intent, REQUEST_SEND_AUDIO); // intent and requestCode of 1 } private boolean isCallable(Intent intent) { List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY); return list.size() > 0; } private void handleSendDelete( Uri contentUri, boolean delete, boolean resizeImage) { try { // import FileInfo info = SystemServices.getFileInfoFromURI(this, contentUri); String sessionId = getCurrentSessionId(); Uri vfsUri; if (resizeImage) vfsUri = ChatFileStore.resizeAndImportImage(this, sessionId, contentUri, info.type); else vfsUri = ChatFileStore.importContent(sessionId, info.path); // send boolean sent = handleSend(vfsUri, info.type); if (!sent) { // not deleting if not sent return; } // autu delete if (delete) { boolean deleted = delete(contentUri); if (!deleted) { throw new IOException("Error deleting " + contentUri); } } } catch (Exception e) { // Toast.makeText(this, "Error sending file", Toast.LENGTH_LONG).show(); // TODO i18n Log.e(ImApp.LOG_TAG,"error sending file",e); } } private boolean delete(Uri uri) { if (uri.getScheme().equals("content")) { int deleted = getContentResolver().delete(uri,null,null); return deleted == 1; } if (uri.getScheme().equals("file")) { java.io.File file = new java.io.File(uri.toString().substring(5)); return file.delete(); } return false; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent resultIntent) { if (resultCode == RESULT_OK) { if (requestCode == REQUEST_SEND_IMAGE || requestCode == REQUEST_SEND_FILE || requestCode == REQUEST_SEND_AUDIO) { Uri uri = resultIntent.getData() ; if( uri == null ) { return ; } boolean deleteAudioFile = (requestCode == REQUEST_SEND_AUDIO); boolean resizeImage = requestCode == REQUEST_SEND_IMAGE; //resize if is an image, not shared as "file" handleSendDelete(uri, deleteAudioFile, resizeImage); } else if (requestCode == REQUEST_TAKE_PICTURE) { File file = new File(getRealPathFromURI(mLastPhoto)); final Handler handler = new Handler(); MediaScannerConnection.scanFile( this, new String[] { file.toString() }, null, new MediaScannerConnection.OnScanCompletedListener() { @Override public void onScanCompleted(String path, final Uri uri) { handler.post( new Runnable() { @Override public void run() { handleSendDelete(mLastPhoto, true, true); } }); } }); } else if (requestCode == REQUEST_TAKE_PICTURE_SECURE) { String filename = resultIntent.getStringExtra(SecureCameraActivity.FILENAME); String mimeType = resultIntent.getStringExtra(SecureCameraActivity.MIMETYPE); Uri uri = Uri.parse("file:" + filename); handleSend(uri,mimeType); } else if (requestCode == REQUEST_SETTINGS) { try { mApp.getRemoteImService().updateStateFromSettings(); } catch (Exception e) { Log.e(ImApp.LOG_TAG,"unable to update service settings",e); } finish(); Intent intent = new Intent(getApplicationContext(), NewChatActivity.class); startActivity(intent); } else if (requestCode == REQUEST_PICK_CONTACTS || requestCode == REQUEST_ADD_CONTACT) { String username = resultIntent.getStringExtra(ContactsPickerActivity.EXTRA_RESULT_USERNAME); long providerId = resultIntent.getLongExtra(ContactsPickerActivity.EXTRA_RESULT_PROVIDER,-1); //String message = resultIntent.getStringExtra(ContactsPickerActivity.EXTRA_RESULT_MESSAGE); try { IChatSession chatSession = this.getCurrentChatSession(); if (chatSession != null && chatSession.isGroupChatSession()) { chatSession.inviteContact(username); showInvitationHasSent(username); } else { startChat(providerId, username,Imps.ContactsColumns.TYPE_NORMAL,true, null); } } catch (RemoteException e) { mHandler.showServiceErrorAlert("Error picking contacts"); Log.d(ImApp.LOG_TAG,"error picking contact",e); } } IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, resultIntent); if (scanResult != null) { String scannedString = scanResult.getContents(); if (scannedString.startsWith("xmpp")) { String result = XmppUriHelper.getOtrFingerprint(scannedString); if (getCurrentChatView()!=null && result != null) getCurrentChatView().verifyScannedFingerprint(result); } else { OtrAndroidKeyManagerImpl.handleKeyScanResult(scannedString, this); } } } } IChatSession getCurrentChatSession() { int currentPos = mChatPager.getCurrentItem(); if (currentPos == 0) return null; Cursor cursorChats = mChatPagerAdapter.getCursor(); cursorChats.moveToPosition(currentPos - 1); long providerId = cursorChats.getLong(ChatView.PROVIDER_COLUMN); String username = cursorChats.getString(ChatView.USERNAME_COLUMN); IChatSessionManager sessionMgr = getChatSessionManager(providerId); if (sessionMgr != null) { try { IChatSession session = sessionMgr.getChatSession(username); if (session == null) session = sessionMgr.createChatSession(username, false); return session; } catch (RemoteException e) { mHandler.showServiceErrorAlert(e.getLocalizedMessage()); LogCleaner.error(ImApp.LOG_TAG, "send message error",e); } } return null; } private String getCurrentSessionId() throws Exception { return ""+getCurrentChatSession().getId(); } 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; } //---------------------------------------- /** * This method is used to get real path of file from from uri * * @param contentUri * @return String */ //---------------------------------------- public String getRealPathFromURI(Uri contentUri) { try { String[] proj = {MediaStore.Images.Media.DATA}; Cursor cursor = managedQuery(contentUri, proj, null, null, null); int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); return cursor.getString(column_index); } catch (Exception e) { return contentUri.getPath(); } } private boolean handleSend(Uri uri, String mimeType) { try { FileInfo info = SystemServices.getFileInfoFromURI(this, uri); if (mimeType != null) info.type = mimeType; if (info != null && info.path != null && ChatFileStore.exists(info.path)) { IChatSession session = getCurrentChatSession(); if (session != null) { if (info.type == null) if (mimeType != null) info.type = mimeType; else info.type = "application/octet-stream"; String offerId = UUID.randomUUID().toString(); session.offerData(offerId, info.path, info.type ); ChatView cView = getCurrentChatView(); int type = cView.isOtrSessionVerified() ? Imps.MessageType.OUTGOING_ENCRYPTED_VERIFIED : Imps.MessageType.OUTGOING_ENCRYPTED; Imps.insertMessageInDb( getContentResolver(), false, session.getId(), true, null, uri.toString(), System.currentTimeMillis(), type, 0, offerId, info.type); return true; // sent } } 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); } return false; // was not sent } void showInvitationHasSent(String contact) { Toast.makeText(NewChatActivity.this, getString(R.string.invitation_sent_prompt, contact), Toast.LENGTH_SHORT).show(); } /** 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) { android.view.MenuItem mi = menu.add(0, MENU_RESEND, 0, R.string.menu_resend); mi.setOnMenuItemClickListener( mMessageContextMenuHandler); } } } final class MessageContextMenuHandler implements android.view.MenuItem.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; } } public class ChatViewPagerAdapter extends DynamicPagerAdapter { Cursor mCursor; boolean mDataValid; public ChatViewPagerAdapter(FragmentManager fm) { super(fm); } public Cursor getCursor() { return mCursor; } public Cursor swapCursor(Cursor newCursor) { if (newCursor == mCursor) { return null; } Cursor oldCursor = mCursor; mCursor = newCursor; if (newCursor != null) { mDataValid = true; } else { mDataValid = false; } notifyDataSetChanged(); // notify the observers about the new cursor refreshChatViews(); return oldCursor; } @Override public int getCount() { if (mCursor != null) return mCursor.getCount() + 1; else return 0; } @Override public Fragment getItem(int position) { if (position == 0) { if (mContactList == null) mContactList = new ContactListFragment(); return mContactList; } else { int positionMod = position - 1; mCursor.moveToPosition(positionMod); long contactChatId = mCursor.getLong(ChatView.CONTACT_ID_COLUMN); String contactName = mCursor.getString(ChatView.USERNAME_COLUMN); long providerId = mCursor.getLong(ChatView.PROVIDER_COLUMN); int chatType = mCursor.getInt(ChatView.TYPE_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 (mCursor != null && mCursor.getCount() > 0) { mCursor.moveToFirst(); int posIdx = 1; do { long chatId = mCursor.getLong(ChatView.CHAT_ID_COLUMN); if (chatId == viewChatId) { position = posIdx; break; } posIdx++; } while (mCursor.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 || mCursor == null) { if (mShowChatsOnly) return getString(R.string.title_chats); else return getString(R.string.contacts); } else { int positionMod = position - 1; mCursor.moveToPosition(positionMod); if (!mCursor.isAfterLast()) { String nickname = mCursor.getString(ChatView.NICKNAME_COLUMN); int presence = mCursor.getInt(ChatView.PRESENCE_STATUS_COLUMN); int type = mCursor.getInt(ChatView.TYPE_COLUMN); BrandingResources brandingRes = mApp.getBrandingResource(mCursor.getInt(ChatView.PROVIDER_COLUMN)); SpannableString s = null; Drawable statusIcon = null; if (Imps.Contacts.TYPE_GROUP == type) { s = new SpannableString(nickname); } else { s = new SpannableString("+ " + nickname); statusIcon = brandingRes.getDrawable(PresenceUtils.getStatusIconId(presence)); statusIcon.setBounds(0, 0, statusIcon.getIntrinsicWidth(), statusIcon.getIntrinsicHeight()); s.setSpan(new ImageSpan(statusIcon), 0, 1, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); } return s; } else return "";//unknown title } } @Override public Object instantiateItem(ViewGroup container, int pos) { Object item = super.instantiateItem(container, pos); 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(); } return null; //this can happen if the user is quickly closing chats; just return null and swallow the event //throw new RuntimeException("could not get chat view at " + pos); } } private void initConnections () { getSupportLoaderManager().initLoader(CHAT_PAGE_LOADER_ID, null, new LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { CursorLoader loader = new CursorLoader(NewChatActivity.this, Imps.Provider.CONTENT_URI_WITH_ACCOUNT, ContactListFragment.PROVIDER_PROJECTION, Imps.Provider.CATEGORY + "=?" + " AND " + Imps.Provider.ACTIVE_ACCOUNT_USERNAME + " NOT NULL", new String[] { ImApp.IMPS_CATEGORY } , Imps.Provider.DEFAULT_SORT_ORDER); loader.setUpdateThrottle(50L); return loader; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor newCursor) { if (newCursor != null && newCursor.getCount() > 0) { mAccountIds = new Long[newCursor.getCount()][2]; newCursor.moveToFirst(); int activeAccountIdColumn = 4; int activeProviderIdColumn = 0; for (int i = 0; i < mAccountIds.length; i++) { mAccountIds[i][0] = newCursor.getLong(activeAccountIdColumn); mAccountIds[i][1] = newCursor.getLong(activeProviderIdColumn); newCursor.moveToNext(); } for (int i = 0; i < mAccountIds.length; i++) initConnection(mAccountIds[i][0],mAccountIds[i][1]); mLastAccountId = mAccountIds[0][0]; mLastProviderId = mAccountIds[0][1]; newCursor.moveToFirst(); initChats(); } else { //no configured accounts, prompt to setup Intent intent = new Intent(NewChatActivity.this, AccountWizardActivity.class); startActivity(intent); finish(); } } @Override public void onLoaderReset(Loader<Cursor> loader) { mAccountIds = null; } }); } public void unregisterSubListeners () { if (mAccountIds != null) for (int i = 0; i < mAccountIds.length; i++) { IImConnection conn = initConnection(mAccountIds[i][0],mAccountIds[i][1]); if (conn != null) { try { conn.getContactListManager().unregisterSubscriptionListener(mSubscriptionListener); } catch (RemoteException e1) { Log.e(ImApp.LOG_TAG,"error registering listener",e1); } } } } public IImConnection initConnection (long accountId, long providerId) { IImConnection conn = ((ImApp)getApplication()).getConnection(providerId); if (conn == null) { try { conn = ((ImApp)getApplication()).createConnection(providerId, accountId); } catch (RemoteException e) { Log.e(ImApp.LOG_TAG,"error creating connection",e); } if (conn != null) { try { conn.getContactListManager().registerSubscriptionListener(mSubscriptionListener); // conn.getContactListManager().registerContactListListener(mContactListListener); } catch (RemoteException e1) { Log.e(ImApp.LOG_TAG,"error registering listener",e1); } } } return conn; } public void updateChatList () { if (mContactList != null && mContactList.mFilterView != null) { mLastPagePosition = -1; Uri baseUri = Imps.Contacts.CONTENT_URI_CHAT_CONTACTS_BY; Uri.Builder builder = baseUri.buildUpon(); mContactList.mFilterView.doFilter(builder.build(), null); } mChatPager.invalidate(); } public static class ContactListFragment extends Fragment implements ContactListListener { 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; ContactListFilterView mFilterView = null; ImApp mApp = null; private Handler mPresenceHandler = new Handler() { @Override public void handleMessage(Message msg) { // mPresenceView.refreshLogginInStatus(); super.handleMessage(msg); } }; /** * The Fragment's UI is just a simple text view showing its * instance number. */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mFilterView = (ContactListFilterView) inflater.inflate( R.layout.contact_list_filter_view, null); mFilterView.setListener(this); mFilterView.setLoaderManager(getLoaderManager(), CONTACT_LIST_LOADER_ID); TextView txtEmpty = (TextView)mFilterView.findViewById(R.id.empty); txtEmpty.setOnClickListener(new OnClickListener () { @Override public void onClick(View v) { ((NewChatActivity)getActivity()).startContactPicker(); } }); ((AbsListView)mFilterView.findViewById(R.id.filteredList)).setEmptyView(txtEmpty); Uri baseUri = Imps.Contacts.CONTENT_URI_CHAT_CONTACTS_BY; Uri.Builder builder = baseUri.buildUpon(); mFilterView.doFilter(builder.build(), null); return 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() { super.onDestroyView(); } @Override public void openChat(Cursor c) { NewChatActivity activity = (NewChatActivity)getActivity(); activity.openExistingChat(c); } @Override 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); } } } private void openExistingChat(Cursor c) { if (c != null && (! c.isAfterLast())) { int type = c.getInt(c.getColumnIndexOrThrow(Imps.Contacts.TYPE)); String username = c.getString(c.getColumnIndexOrThrow(Imps.Contacts.USERNAME)); long providerId = c.getLong(c.getColumnIndexOrThrow(Imps.Contacts.PROVIDER)); startChat(providerId,username,type, false, null); } else updateChatList(); } private void startChat (long providerId, String address,int userType, boolean isNewChat, String message) { IImConnection conn = mApp.getConnection(providerId); if (conn != null) { try { IChatSessionManager manager = conn.getChatSessionManager(); IChatSession session = manager.getChatSession(address); if (session == null && manager != null) { // Create session. Stash requested contact ID for when we get called back. if (userType == Imps.ContactsColumns.TYPE_GROUP) session = manager.createMultiUserChatSession(address, null, isNewChat); else session = manager.createChatSession(address, isNewChat); if (session != null) { mRequestedChatId = session.getId(); if (!showChat(session.getId())) { // We have a session, but it's not in the cursor yet mRequestedChatId = session.getId(); session.reInit(); } else { mRequestedChatId = -1;//we showed the chat, so set this to -1; } if (message != null) session.sendMessage(message); } } else { // Already have session if (!showChat(session.getId())) { // We have a session, but it's not in the cursor yet mRequestedChatId = session.getId(); session.reInit(); } } updateChatList(); } 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()); } @Override 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(); } @Override public void onPause() { super.onPause(); mChatView.stopListening(); } @Override public void onDestroy() { mChatView.unbind(); super.onDestroy(); } public ChatView getChatView() { return mChatView; } } public ChatView getCurrentChatView () { int cItemIdx; // FIXME why is mChatPagerAdapter null here? Is this called after onDestroy? if (mChatPagerAdapter != null && (cItemIdx = mChatPager.getCurrentItem()) > 0) { return mChatPagerAdapter.getChatViewAt(cItemIdx); } else return null; } View mDialogGroup = null; private void showGroupChatDialog () { // This example shows how to add a custom layout to an AlertDialog LayoutInflater factory = LayoutInflater.from(this); mDialogGroup = factory.inflate(R.layout.alert_dialog_group_chat, null); final Spinner listAccounts = (Spinner) mDialogGroup.findViewById(R.id.choose_list); setupAccountSpinner(listAccounts); new AlertDialog.Builder(this) .setTitle(R.string.create_or_join_group_chat) .setView(mDialogGroup) .setPositiveButton(R.string.connect, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { /* User clicked OK so do some stuff */ String chatRoom = null; String chatServer = null; String nickname = null; TextView tv = (TextView)mDialogGroup.findViewById(R.id.chat_room); chatRoom = tv.getText().toString(); tv = (TextView) mDialogGroup.findViewById(R.id.chat_server); chatServer = tv.getText().toString(); tv = (TextView) mDialogGroup.findViewById(R.id.nickname); nickname = tv.getText().toString(); try { IImConnection conn = mApp.getConnection(mLastProviderId); if (conn.getState() == ImConnection.LOGGED_IN) startGroupChat (chatRoom, chatServer, nickname, conn); else { //can't start group chat mHandler.showAlert("Group Chat","Please enable your account to join a group chat"); } } catch (RemoteException re) { } dialog.dismiss(); } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { /* User clicked cancel so do some stuff */ dialog.dismiss(); } }) .create().show(); } private void setupAccountSpinner (Spinner spinner) { final Uri uri = Imps.Provider.CONTENT_URI_WITH_ACCOUNT; final Cursor cursorProviders = managedQuery(uri, PROVIDER_PROJECTION, Imps.Provider.CATEGORY + "=?" + " AND " + Imps.Provider.ACTIVE_ACCOUNT_USERNAME + " NOT NULL" /* selection */, new String[] { ImApp.IMPS_CATEGORY } /* selection args */, Imps.Provider.DEFAULT_SORT_ORDER); SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_spinner_dropdown_item, cursorProviders, new String[] { Imps.Provider.ACTIVE_ACCOUNT_USERNAME}, new int[] { android.R.id.text1 }); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); if (cursorProviders.getCount() > 0) { cursorProviders.moveToFirst(); mLastProviderId = cursorProviders.getLong(PROVIDER_ID_COLUMN); mLastAccountId = cursorProviders.getLong(ACTIVE_ACCOUNT_ID_COLUMN); spinner.setAdapter(adapter); spinner.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { cursorProviders.moveToPosition(arg2); mLastProviderId = cursorProviders.getLong(PROVIDER_ID_COLUMN); mLastAccountId = cursorProviders.getLong(ACTIVE_ACCOUNT_ID_COLUMN); mHandler.post(new Runnable() { public void run () { TextView tvServer = (TextView) mDialogGroup.findViewById(R.id.chat_server); IChatSessionManager manager; try { IImConnection conn = mApp.getConnection(mLastProviderId); manager = conn.getChatSessionManager(); String server = manager.getDefaultMultiUserChatServer(); if (server != null) tvServer.setText(server); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } @Override public void onNothingSelected(AdapterView<?> arg0) { // TODO Auto-generated method stub } }); } else { spinner.setVisibility(View.GONE); } } private IImConnection mLastConnGroup = null; public void startGroupChat (String room, String server, String nickname, IImConnection conn) { mLastConnGroup = conn; new AsyncTask<String, Long, String>() { private ProgressDialog dialog; @Override protected void onPreExecute() { dialog = new ProgressDialog(NewChatActivity.this); dialog.setMessage(getString(R.string.connecting_to_group_chat_)); dialog.setCancelable(true); dialog.show(); } @Override protected String doInBackground(String... params) { String roomAddress = (params[0] + '@' + params[1]).toLowerCase(Locale.US).replace(' ', '_'); String nickname = params[2]; try { IChatSessionManager manager = mLastConnGroup.getChatSessionManager(); IChatSession session = manager.getChatSession(roomAddress); if (session == null) { session = manager.createMultiUserChatSession(roomAddress, nickname, true); if (session != null) { mRequestedChatId = session.getId(); publishProgress(mRequestedChatId); } else { return getString(R.string.unable_to_create_or_join_group_chat); } } else { mRequestedChatId = session.getId(); publishProgress(mRequestedChatId); } return null; } catch (RemoteException e) { return e.toString(); } } @Override protected void onProgressUpdate(Long... showChatId) { showChat(showChatId[0]); } @Override protected void onPostExecute(String result) { super.onPostExecute(result); if (dialog.isShowing()) { dialog.dismiss(); } if (result != null) { mHandler.showServiceErrorAlert(result); } } }.execute(room, server, nickname); } 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) { if (! ((Activity) this).isFinishing()) { mHandler.postDelayed(new Runnable() { @Override public void run () { new AlertDialog.Builder(NewChatActivity.this) .setTitle(getString(R.string.subscriptions)) .setMessage(getString(R.string.subscription_prompt,subFrom)) .setPositiveButton(R.string.approve_subscription, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { approveSubscription(subProviderId, subFrom); dialog.dismiss(); } }) .setNegativeButton(R.string.decline_subscription, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { declineSubscription(subProviderId, subFrom); dialog.dismiss(); } }) .create().show(); } },500); } } void approveSubscription(long providerId, String userName) { IImConnection conn = mApp.getConnection(providerId); if (conn != null) { try { IContactListManager manager = conn.getContactListManager(); manager.approveSubscription(new Contact(new XmppAddress(userName),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(new Contact(new XmppAddress(userName),userName)); } catch (RemoteException e) { mHandler.showServiceErrorAlert(e.getLocalizedMessage()); LogCleaner.error(ImApp.LOG_TAG, "decline sub error",e); } } } long getLastAccountId() { return mLastAccountId; } long getLastProviderId() { return mLastProviderId; } void setLastProviderId(long mLastProviderId) { this.mLastProviderId = mLastProviderId; } public class ProviderListItemFactory implements LayoutInflater.Factory { @Override public View onCreateView(String name, Context context, AttributeSet attrs) { if (name != null && name.equals(ProviderListItem.class.getName())) { return new ProviderListItem(context, NewChatActivity.this, null); } return null; } } private final ISubscriptionListener.Stub mSubscriptionListener = new ISubscriptionListener.Stub() { @Override public void onSubScriptionRequest(Contact from, long providerId, long accountId) { showSubscriptionDialog (providerId, from.getAddress().getAddress()); } @Override public void onSubscriptionApproved(Contact contact, long providerId, long accountId) { } @Override public void onSubscriptionDeclined(Contact contact, long providerId, long accountId) { } }; private final IContactListListener.Stub mContactListListener = new IContactListListener.Stub () { @Override public IBinder asBinder() { return null; } @Override public void onContactChange(int type, IContactList list, Contact contact) throws RemoteException { } @Override public void onAllContactListsLoaded() throws RemoteException { Log.d(ImApp.LOG_TAG, "onAllContactListsLoaded"); } @Override public void onContactsPresenceUpdate(Contact[] contacts) throws RemoteException { } @Override public void onContactError(int errorType, ImErrorInfo error, String listName, Contact contact) throws RemoteException { } }; private synchronized Imps.ProviderSettings.QueryMap getGlobalSettings() { if (mGlobalSettings == null) { ContentResolver contentResolver = getContentResolver(); Cursor cursor = contentResolver.query(Imps.ProviderSettings.CONTENT_URI,new String[] {Imps.ProviderSettings.NAME, Imps.ProviderSettings.VALUE},Imps.ProviderSettings.PROVIDER + "=?",new String[] { Long.toString(Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS)},null); if (cursor == null) return null; mGlobalSettings = new Imps.ProviderSettings.QueryMap(cursor, contentResolver, Imps.ProviderSettings.PROVIDER_ID_FOR_GLOBAL_SETTINGS, true, mHandler); } return mGlobalSettings; } public int getOtrPolicy() { int otrPolicy = OtrPolicy.OPPORTUNISTIC; String otrModeSelect = getGlobalSettings().getOtrMode(); if (otrModeSelect.equals("auto")) { otrPolicy = OtrPolicy.OPPORTUNISTIC; } else if (otrModeSelect.equals("disabled")) { otrPolicy = OtrPolicy.NEVER; } else if (otrModeSelect.equals("force")) { otrPolicy = OtrPolicy.OTRL_POLICY_ALWAYS; } else if (otrModeSelect.equals("requested")) { otrPolicy = OtrPolicy.OTRL_POLICY_MANUAL; } return otrPolicy; } 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; }