PageRenderTime 74ms CodeModel.GetById 35ms RepoModel.GetById 1ms app.codeStats 0ms

/resources/ExDataset/codechanges/208/old/ChatController.java

https://bitbucket.org/tamnguyenthe/exassist_repo
Java | 2453 lines | 1777 code | 464 blank | 212 comment | 400 complexity | 0a10c9c7ee65359a08d3808dfeb153bc MD5 | raw file
  1. package com.twofours.surespot.chat;
  2. import io.socket.IOAcknowledge;
  3. import io.socket.IOCallback;
  4. import io.socket.SocketIO;
  5. import io.socket.SocketIOException;
  6. import java.io.BufferedInputStream;
  7. import java.io.File;
  8. import java.io.FileInputStream;
  9. import java.io.FileNotFoundException;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.io.InterruptedIOException;
  13. import java.io.PipedInputStream;
  14. import java.io.PipedOutputStream;
  15. import java.net.URI;
  16. import java.net.URISyntaxException;
  17. import java.util.ArrayList;
  18. import java.util.Date;
  19. import java.util.HashMap;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import java.util.Map.Entry;
  23. import java.util.Timer;
  24. import java.util.TimerTask;
  25. import java.util.concurrent.ConcurrentLinkedQueue;
  26. import org.json.JSONArray;
  27. import org.json.JSONException;
  28. import org.json.JSONObject;
  29. import android.app.Activity;
  30. import android.app.NotificationManager;
  31. import android.content.BroadcastReceiver;
  32. import android.content.Context;
  33. import android.content.Intent;
  34. import android.content.IntentFilter;
  35. import android.graphics.Bitmap;
  36. import android.net.ConnectivityManager;
  37. import android.net.NetworkInfo;
  38. import android.os.AsyncTask;
  39. import android.os.Handler;
  40. import android.os.Looper;
  41. import android.support.v4.app.FragmentManager;
  42. import android.support.v4.view.ViewPager;
  43. import android.text.TextUtils;
  44. import ch.boye.httpclientandroidlib.Header;
  45. import ch.boye.httpclientandroidlib.HttpStatus;
  46. import ch.boye.httpclientandroidlib.HttpVersion;
  47. import ch.boye.httpclientandroidlib.StatusLine;
  48. import ch.boye.httpclientandroidlib.client.cache.HttpCacheEntry;
  49. import ch.boye.httpclientandroidlib.cookie.Cookie;
  50. import ch.boye.httpclientandroidlib.impl.client.cache.HeapResource;
  51. import ch.boye.httpclientandroidlib.impl.cookie.DateUtils;
  52. import ch.boye.httpclientandroidlib.message.BasicHeader;
  53. import ch.boye.httpclientandroidlib.message.BasicStatusLine;
  54. import com.actionbarsherlock.view.MenuItem;
  55. import com.loopj.android.http.AsyncHttpResponseHandler;
  56. import com.loopj.android.http.JsonHttpResponseHandler;
  57. import com.twofours.surespot.R;
  58. import com.twofours.surespot.StateController;
  59. import com.twofours.surespot.StateController.FriendState;
  60. import com.twofours.surespot.SurespotApplication;
  61. import com.twofours.surespot.activities.MainActivity;
  62. import com.twofours.surespot.common.SurespotConfiguration;
  63. import com.twofours.surespot.common.SurespotConstants;
  64. import com.twofours.surespot.common.SurespotLog;
  65. import com.twofours.surespot.common.Utils;
  66. import com.twofours.surespot.encryption.EncryptionController;
  67. import com.twofours.surespot.friends.AutoInviteData;
  68. import com.twofours.surespot.friends.Friend;
  69. import com.twofours.surespot.friends.FriendAdapter;
  70. import com.twofours.surespot.identity.IdentityController;
  71. import com.twofours.surespot.images.MessageImageDownloader;
  72. import com.twofours.surespot.network.IAsyncCallback;
  73. import com.twofours.surespot.network.IAsyncCallbackTuple;
  74. import com.twofours.surespot.network.NetworkController;
  75. import com.viewpagerindicator.TitlePageIndicator;
  76. public class ChatController {
  77. private static final String TAG = "ChatController";
  78. private static final int STATE_CONNECTING = 0;
  79. private static final int STATE_CONNECTED = 1;
  80. private static final int STATE_DISCONNECTED = 2;
  81. private static final int MAX_RETRIES = 16;
  82. private final StatusLine mImageStatusLine = new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "");
  83. private SocketIO socket;
  84. private int mRetries = 0;
  85. private Timer mBackgroundTimer;
  86. private IOCallback mSocketCallback;
  87. private ConcurrentLinkedQueue<SurespotMessage> mSendBuffer = new ConcurrentLinkedQueue<SurespotMessage>();
  88. private ConcurrentLinkedQueue<SurespotMessage> mResendBuffer = new ConcurrentLinkedQueue<SurespotMessage>();
  89. private int mConnectionState;
  90. private boolean mOnWifi;
  91. private NotificationManager mNotificationManager;
  92. private BroadcastReceiver mConnectivityReceiver;
  93. private HashMap<String, ChatAdapter> mChatAdapters;
  94. private HashMap<String, Integer> mEarliestMessage;
  95. private FriendAdapter mFriendAdapter;
  96. private ChatPagerAdapter mChatPagerAdapter;
  97. private ViewPager mViewPager;
  98. private TitlePageIndicator mIndicator;
  99. private FragmentManager mFragmentManager;
  100. private int mLatestUserControlId;
  101. private ArrayList<MenuItem> mMenuItems;
  102. private HashMap<String, LatestIdPair> mPreConnectIds;
  103. private static String mCurrentChat;
  104. private static boolean mPaused = true;
  105. private NetworkController mNetworkController;
  106. private Context mContext;
  107. public static final int MODE_NORMAL = 0;
  108. public static final int MODE_SELECT = 1;
  109. private int mMode = MODE_NORMAL;
  110. private IAsyncCallbackTuple<String, Boolean> mCallback401;
  111. private IAsyncCallback<Boolean> mProgressCallback;
  112. private IAsyncCallback<Void> mSendIntentCallback;
  113. private IAsyncCallback<Friend> mTabShowingCallback;
  114. private AutoInviteData mAutoInviteData;
  115. public ChatController(Context context, NetworkController networkController, FragmentManager fm, IAsyncCallbackTuple<String, Boolean> m401Handler,
  116. IAsyncCallback<Boolean> progressCallback, IAsyncCallback<Void> sendIntentCallback, IAsyncCallback<Friend> tabShowingCallback) {
  117. SurespotLog.v(TAG, "constructor: " + this);
  118. mContext = context;
  119. mNetworkController = networkController;
  120. mCallback401 = m401Handler;
  121. mProgressCallback = progressCallback;
  122. mSendIntentCallback = sendIntentCallback;
  123. mTabShowingCallback = tabShowingCallback;
  124. mEarliestMessage = new HashMap<String, Integer>();
  125. mChatAdapters = new HashMap<String, ChatAdapter>();
  126. mFriendAdapter = new FriendAdapter(mContext);
  127. mPreConnectIds = new HashMap<String, ChatController.LatestIdPair>();
  128. loadState();
  129. mFragmentManager = fm;
  130. mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
  131. setOnWifi();
  132. // mViewPager.setOffscreenPageLimit(2);
  133. mSocketCallback = new IOCallback() {
  134. @Override
  135. public void onMessage(JSONObject json, IOAcknowledge ack) {
  136. try {
  137. SurespotLog.v(TAG, "JSON Server said: %s", json.toString(2));
  138. }
  139. catch (JSONException e) {
  140. SurespotLog.w(TAG, "onMessage", e);
  141. }
  142. }
  143. @Override
  144. public void onMessage(String data, IOAcknowledge ack) {
  145. SurespotLog.v(TAG, "Server said: %s", data);
  146. }
  147. @Override
  148. public synchronized void onError(SocketIOException socketIOException) {
  149. // socket.io returns 403 for can't login
  150. if (socketIOException.getHttpStatus() == 403) {
  151. socket = null;
  152. logout();
  153. mCallback401.handleResponse(mContext.getString(R.string.could_not_login_to_server), false);
  154. return;
  155. }
  156. SurespotLog.i(TAG, socketIOException, "an Error occured, attempting reconnect with exponential backoff, retries: %d", mRetries);
  157. setOnWifi();
  158. // kick off another task
  159. if (mRetries < MAX_RETRIES) {
  160. if (mReconnectTask != null) {
  161. mReconnectTask.cancel();
  162. }
  163. int timerInterval = (int) (Math.pow(2, mRetries++) * 1000);
  164. SurespotLog.v(TAG, "Starting another task in: " + timerInterval);
  165. mReconnectTask = new ReconnectTask();
  166. if (mBackgroundTimer == null) {
  167. mBackgroundTimer = new Timer("backgroundTimer");
  168. }
  169. mBackgroundTimer.schedule(mReconnectTask, timerInterval);
  170. }
  171. else {
  172. // TODO tell user
  173. SurespotLog.i(TAG, "Socket.io reconnect retries exhausted, giving up.");
  174. mCallback401.handleResponse(mContext.getString(R.string.could_not_connect_to_server), true);
  175. }
  176. }
  177. @Override
  178. public void onDisconnect() {
  179. SurespotLog.v(TAG, "Connection terminated.");
  180. // socket = null;
  181. }
  182. @Override
  183. public void onConnect() {
  184. SurespotLog.v(TAG, "socket.io connection established");
  185. setState(STATE_CONNECTED);
  186. setOnWifi();
  187. mRetries = 0;
  188. if (mBackgroundTimer != null) {
  189. mBackgroundTimer.cancel();
  190. mBackgroundTimer = null;
  191. }
  192. if (mReconnectTask != null && mReconnectTask.cancel()) {
  193. SurespotLog.v(TAG, "Cancelled reconnect timer.");
  194. mReconnectTask = null;
  195. }
  196. connected();
  197. }
  198. @Override
  199. public void on(String event, IOAcknowledge ack, Object... args) {
  200. SurespotLog.v(TAG, "Server triggered event '" + event + "'");
  201. if (event.equals("control")) {
  202. try {
  203. SurespotControlMessage message = SurespotControlMessage.toSurespotControlMessage(new JSONObject((String) args[0]));
  204. handleControlMessage(null, message, true, false);
  205. }
  206. catch (JSONException e) {
  207. SurespotLog.w(TAG, "on control", e);
  208. }
  209. }
  210. else
  211. if (event.equals("message")) {
  212. try {
  213. JSONObject jsonMessage = new JSONObject((String) args[0]);
  214. SurespotLog.v(TAG, "received message: " + jsonMessage.toString());
  215. SurespotMessage message = SurespotMessage.toSurespotMessage(jsonMessage);
  216. handleMessage(message);
  217. checkAndSendNextMessage(message);
  218. // see if we have deletes
  219. String sDeleteControlMessages = jsonMessage.optString("deleteControlMessages", null);
  220. if (sDeleteControlMessages != null) {
  221. JSONArray deleteControlMessages = new JSONArray(sDeleteControlMessages);
  222. if (deleteControlMessages.length() > 0) {
  223. for (int i = 0; i < deleteControlMessages.length(); i++) {
  224. try {
  225. SurespotControlMessage dMessage = SurespotControlMessage.toSurespotControlMessage(new JSONObject(
  226. deleteControlMessages.getString(i)));
  227. handleControlMessage(null, dMessage, true, false);
  228. }
  229. catch (JSONException e) {
  230. SurespotLog.w(TAG, "on control", e);
  231. }
  232. }
  233. }
  234. }
  235. }
  236. catch (JSONException e) {
  237. SurespotLog.w(TAG, "on message", e);
  238. }
  239. }
  240. else
  241. if (event.equals("messageError")) {
  242. try {
  243. JSONObject jsonMessage = (JSONObject) args[0];
  244. SurespotLog.v(TAG, "received messageError: " + jsonMessage.toString());
  245. SurespotErrorMessage errorMessage = SurespotErrorMessage.toSurespotErrorMessage(jsonMessage);
  246. handleErrorMessage(errorMessage);
  247. }
  248. catch (JSONException e) {
  249. SurespotLog.w(TAG, "on messageError", e);
  250. }
  251. }
  252. }
  253. };
  254. mConnectivityReceiver = new BroadcastReceiver() {
  255. @Override
  256. public void onReceive(Context context, Intent intent) {
  257. SurespotLog.v(TAG, "Connectivity Action");
  258. ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  259. NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
  260. if (networkInfo != null) {
  261. SurespotLog.v(TAG, "isconnected: " + networkInfo.isConnected());
  262. SurespotLog.v(TAG, "failover: " + networkInfo.isFailover());
  263. SurespotLog.v(TAG, "reason: " + networkInfo.getReason());
  264. SurespotLog.v(TAG, "type: " + networkInfo.getTypeName());
  265. // if it's not a failover and wifi is now active then initiate reconnect
  266. if (!networkInfo.isFailover() && (networkInfo.getType() == ConnectivityManager.TYPE_WIFI && networkInfo.isConnected())) {
  267. synchronized (ChatController.this) {
  268. // if we're not connecting, connect
  269. if (getState() != STATE_CONNECTING && !mOnWifi) {
  270. SurespotLog.v(TAG, "Network switch, Reconnecting...");
  271. setState(STATE_CONNECTING);
  272. mOnWifi = true;
  273. disconnect();
  274. connect();
  275. }
  276. }
  277. }
  278. }
  279. else {
  280. SurespotLog.v(TAG, "networkinfo null");
  281. }
  282. }
  283. };
  284. }
  285. // this has to be done outside of the contructor as it creates fragments, which need chat controller instance
  286. public void init(ViewPager viewPager, TitlePageIndicator pageIndicator, ArrayList<MenuItem> menuItems, AutoInviteData autoInviteData) {
  287. mChatPagerAdapter = new ChatPagerAdapter(mContext, mFragmentManager);
  288. mMenuItems = menuItems;
  289. mAutoInviteData = autoInviteData;
  290. mViewPager = viewPager;
  291. mViewPager.setAdapter(mChatPagerAdapter);
  292. mIndicator = pageIndicator;
  293. mIndicator.setViewPager(mViewPager);
  294. mIndicator.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
  295. @Override
  296. public void onPageSelected(int position) {
  297. if (mChatPagerAdapter != null) {
  298. SurespotLog.v(TAG, "onPageSelected, position: " + position);
  299. String name = mChatPagerAdapter.getChatName(position);
  300. setCurrentChat(name);
  301. }
  302. }
  303. });
  304. mChatPagerAdapter.setChatNames(mFriendAdapter.getActiveChats());
  305. onResume();
  306. }
  307. private void connect() {
  308. SurespotLog.v(TAG, "connect, socket: " + socket + ", connected: " + (socket != null ? socket.isConnected() : false) + ", state: " + mConnectionState);
  309. // copy the latest ids so that we don't miss any if we receive new messages during the time we request messages and when the
  310. // connection completes (if they
  311. // are received out of order for some reason)
  312. //
  313. mPreConnectIds.clear();
  314. for (Entry<String, ChatAdapter> entry : mChatAdapters.entrySet()) {
  315. String username = entry.getKey();
  316. LatestIdPair idPair = new LatestIdPair();
  317. idPair.latestMessageId = getLatestMessageId(username);
  318. idPair.latestControlMessageId = getLatestMessageControlId(username);
  319. SurespotLog.v(TAG, "setting preconnectids for: " + username + ", latest message id: " + idPair.latestMessageId + ", latestcontrolid: "
  320. + idPair.latestControlMessageId);
  321. mPreConnectIds.put(username, idPair);
  322. }
  323. Cookie cookie = IdentityController.getCookie();
  324. if (cookie == null) {
  325. return;
  326. }
  327. try {
  328. HashMap<String, String> headers = new HashMap<String, String>();
  329. headers.put("cookie", cookie.getName() + "=" + cookie.getValue());
  330. socket = new SocketIO(SurespotConfiguration.getBaseUrl(), headers);
  331. socket.connect(mSocketCallback);
  332. }
  333. catch (Exception e) {
  334. SurespotLog.w(TAG, "connect", e);
  335. }
  336. }
  337. private void disconnect() {
  338. SurespotLog.v(TAG, "disconnect.");
  339. setState(STATE_DISCONNECTED);
  340. if (socket != null) {
  341. socket.disconnect();
  342. socket = null;
  343. }
  344. }
  345. private void connected() {
  346. getFriendsAndIds();
  347. resendMessages();
  348. // if we need to invite someone then do it
  349. if (mAutoInviteData != null) {
  350. if (mFriendAdapter.getFriend(mAutoInviteData.getUsername()) == null) {
  351. mNetworkController.invite(mAutoInviteData.getUsername(), mAutoInviteData.getSource(), new AsyncHttpResponseHandler() {
  352. @Override
  353. public void onSuccess(int statusCode, String arg0) {
  354. getFriendAdapter().addFriendInvited(mAutoInviteData.getUsername());
  355. mAutoInviteData = null;
  356. }
  357. });
  358. }
  359. else {
  360. Utils.makeToast(mContext, mContext.getString(R.string.autoinvite_user_exists, mAutoInviteData.getUsername()));
  361. mAutoInviteData = null;
  362. }
  363. }
  364. }
  365. private void resendMessages() {
  366. // get the resend messages
  367. SurespotMessage[] resendMessages = getResendMessages();
  368. JSONArray sMessageList = new JSONArray();
  369. for (int i = 0; i < resendMessages.length; i++) {
  370. SurespotMessage message = resendMessages[i];
  371. // if it has an id don't send it again
  372. if (message.getId() != null) {
  373. mResendBuffer.remove(message);
  374. continue;
  375. }
  376. // set the last received id so the server knows which messages to check
  377. String otherUser = message.getOtherUser();
  378. // String username = message.getFrom();
  379. Integer lastMessageID = 0;
  380. // ideally get the last id from the fragment's chat adapter
  381. ChatAdapter chatAdapter = mChatAdapters.get(otherUser);
  382. if (chatAdapter != null) {
  383. SurespotMessage lastMessage = chatAdapter.getLastMessageWithId();
  384. if (lastMessage != null) {
  385. lastMessageID = lastMessage.getId();
  386. }
  387. }
  388. // failing that use the last viewed id
  389. if (lastMessageID == null) {
  390. mFriendAdapter.getFriend(otherUser).getLastViewedMessageId();
  391. }
  392. SurespotLog.v(TAG, "setting resendId, otheruser: " + otherUser + ", id: " + lastMessageID);
  393. message.setResendId(lastMessageID);
  394. // String sMessage = message.toJSONObject().toString();
  395. sMessageList.put(message.toJSONObject());
  396. // enqueueMessage(message);
  397. // sendMessages();
  398. }
  399. socket.send(sMessageList.toString());
  400. }
  401. private void setOnWifi() {
  402. // get the initial state...sometimes when the app starts it says "hey i'm on wifi" which creates a reconnect
  403. ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
  404. NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
  405. if (networkInfo != null) {
  406. mOnWifi = (networkInfo.getType() == ConnectivityManager.TYPE_WIFI);
  407. }
  408. }
  409. private void checkAndSendNextMessage(SurespotMessage message) {
  410. sendMessages();
  411. if (mResendBuffer.size() > 0) {
  412. if (mResendBuffer.remove(message)) {
  413. SurespotLog.v(TAG, "Received and removed message from resend buffer: " + message);
  414. }
  415. }
  416. }
  417. private SurespotMessage[] getResendMessages() {
  418. SurespotMessage[] messages = mResendBuffer.toArray(new SurespotMessage[0]);
  419. // mResendBuffer.clear();
  420. return messages;
  421. }
  422. private void enqueueMessage(SurespotMessage message) {
  423. mSendBuffer.add(message);
  424. }
  425. private synchronized void sendMessages() {
  426. if (mBackgroundTimer == null) {
  427. mBackgroundTimer = new Timer("backgroundTimer");
  428. }
  429. SurespotLog.v(TAG, "Sending: " + mSendBuffer.size() + " messages.");
  430. Iterator<SurespotMessage> iterator = mSendBuffer.iterator();
  431. while (iterator.hasNext()) {
  432. SurespotMessage message = iterator.next();
  433. if (isMessageReadyToSend(message)) {
  434. iterator.remove();
  435. sendMessage(message);
  436. }
  437. else {
  438. break;
  439. }
  440. }
  441. }
  442. private boolean isMessageReadyToSend(SurespotMessage message) {
  443. return !TextUtils.isEmpty(message.getData()) && !TextUtils.isEmpty(message.getFromVersion()) && !TextUtils.isEmpty(message.getToVersion());
  444. }
  445. private void sendMessage(final SurespotMessage message) {
  446. SurespotLog.v(TAG, "sendmessage adding message to ResendBuffer, text: %s, iv: %s", message.getPlainData(), message.getIv());
  447. mResendBuffer.add(message);
  448. if (getState() == STATE_CONNECTED) {
  449. SurespotLog.v(TAG, "sendmessage, socket: %s", socket);
  450. JSONObject json = message.toJSONObject();
  451. SurespotLog.v(TAG, "sendmessage, json: %s", json);
  452. String s = json.toString();
  453. SurespotLog.v(TAG, "sendmessage, message string: %s", s);
  454. if (socket != null) {
  455. socket.send(s);
  456. }
  457. }
  458. }
  459. private int getState() {
  460. return mConnectionState;
  461. }
  462. private synchronized void setState(int state) {
  463. mConnectionState = state;
  464. }
  465. private ReconnectTask mReconnectTask;
  466. private class ReconnectTask extends TimerTask {
  467. @Override
  468. public void run() {
  469. SurespotLog.v(TAG, "Reconnect task run.");
  470. connect();
  471. }
  472. }
  473. private void handleMessage(final SurespotMessage message) {
  474. SurespotLog.v(TAG, "handleMessage %s", message);
  475. final String otherUser = message.getOtherUser();
  476. final ChatAdapter chatAdapter = mChatAdapters.get(otherUser);
  477. // if the adapter is open add the message
  478. if (chatAdapter != null) {
  479. // decrypt the message before adding it so the size is set properly
  480. new AsyncTask<Void, Void, Void>() {
  481. @Override
  482. protected Void doInBackground(Void... params) {
  483. if (message.getMimeType().equals(SurespotConstants.MimeTypes.TEXT)) {
  484. // decrypt it before adding
  485. final String plainText = EncryptionController.symmetricDecrypt(message.getOurVersion(), message.getOtherUser(),
  486. message.getTheirVersion(), message.getIv(), message.getData());
  487. // substitute emoji
  488. if (plainText != null) {
  489. EmojiParser parser = EmojiParser.getInstance();
  490. message.setPlainData(parser.addEmojiSpans(plainText));
  491. }
  492. }
  493. else {
  494. if (message.getMimeType().equals(SurespotConstants.MimeTypes.IMAGE)) {
  495. // if it's an image that i sent
  496. // get the local message
  497. if (ChatUtils.isMyMessage(message)) {
  498. handleCachedFile(chatAdapter, message);
  499. }
  500. else {
  501. InputStream imageStream = MainActivity.getNetworkController().getFileStream(MainActivity.getContext(), message.getData());
  502. Bitmap bitmap = null;
  503. PipedOutputStream out = new PipedOutputStream();
  504. PipedInputStream inputStream;
  505. try {
  506. inputStream = new PipedInputStream(out);
  507. EncryptionController.runDecryptTask(message.getOurVersion(), message.getOtherUser(), message.getTheirVersion(),
  508. message.getIv(), new BufferedInputStream(imageStream), out);
  509. byte[] bytes = Utils.inputStreamToBytes(inputStream);
  510. bitmap = ChatUtils.getSampledImage(bytes);
  511. }
  512. catch (InterruptedIOException ioe) {
  513. SurespotLog.w(TAG, ioe, "handleMessage");
  514. }
  515. catch (IOException e) {
  516. SurespotLog.w(TAG, e, "handleMessage");
  517. }
  518. if (bitmap != null) {
  519. MessageImageDownloader.addBitmapToCache(message.getData(), bitmap);
  520. }
  521. }
  522. }
  523. else {
  524. if (message.getMimeType().equals(SurespotConstants.MimeTypes.M4A)) {
  525. if (ChatUtils.isMyMessage(message)) {
  526. handleCachedFile(chatAdapter, message);
  527. }
  528. else {
  529. InputStream encryptedVoiceStream = MainActivity.getNetworkController().getFileStream(MainActivity.getContext(),
  530. message.getData());
  531. PipedOutputStream out = new PipedOutputStream();
  532. PipedInputStream inputStream;
  533. try {
  534. inputStream = new PipedInputStream(out);
  535. EncryptionController.runDecryptTask(message.getOurVersion(), message.getOtherUser(), message.getTheirVersion(),
  536. message.getIv(), new BufferedInputStream(encryptedVoiceStream), out);
  537. byte[] bytes = Utils.inputStreamToBytes(inputStream);
  538. message.setPlainBinaryData(bytes);
  539. }
  540. catch (InterruptedIOException ioe) {
  541. SurespotLog.w(TAG, ioe, "handleMessage");
  542. }
  543. catch (IOException e) {
  544. SurespotLog.w(TAG, e, "handleMessage");
  545. }
  546. }
  547. }
  548. else {
  549. message.setPlainData("unknown message mime type");
  550. }
  551. }
  552. }
  553. return null;
  554. }
  555. protected void onPostExecute(Void result) {
  556. try {
  557. boolean added = applyControlMessages(chatAdapter, message, false, false, true);
  558. scrollToEnd(otherUser);
  559. Friend friend = mFriendAdapter.getFriend(otherUser);
  560. if (friend != null) {
  561. int messageId = message.getId();
  562. // always update the available id
  563. friend.setAvailableMessageId(messageId);
  564. // if the chat is showing set the last viewed id the id of the message we just received
  565. if (otherUser.equals(mCurrentChat)) {
  566. friend.setLastViewedMessageId(messageId);
  567. // if it was a voice message from the other user set play flag
  568. // TODO wrap in preference
  569. if (!ChatUtils.isMyMessage(message) && message.getMimeType().equals(SurespotConstants.MimeTypes.M4A)) {
  570. message.setPlayMedia(true);
  571. }
  572. }
  573. // chat not showing
  574. else {
  575. // if it's my message increment the count by one to account for it as I may have unread messages from the
  576. // other user; we
  577. // can't just set the last viewed to the latest message
  578. if (ChatUtils.isMyMessage(message) && added) {
  579. int adjustedLastViewedId = friend.getLastViewedMessageId() + 1;
  580. if (adjustedLastViewedId < messageId) {
  581. friend.setLastViewedMessageId(adjustedLastViewedId);
  582. }
  583. else {
  584. friend.setLastViewedMessageId(messageId);
  585. }
  586. }
  587. }
  588. mFriendAdapter.sort();
  589. mFriendAdapter.notifyDataSetChanged();
  590. }
  591. }
  592. catch (SurespotMessageSequenceException e) {
  593. SurespotLog.v(TAG, "handleMessage: %s", e.getMessage());
  594. getLatestMessagesAndControls(otherUser, e.getMessageId());
  595. }
  596. };
  597. }.execute();
  598. }
  599. else {
  600. Friend friend = mFriendAdapter.getFriend(otherUser);
  601. if (friend != null) {
  602. int messageId = message.getId();
  603. // always update the available id
  604. friend.setAvailableMessageId(messageId);
  605. mFriendAdapter.sort();
  606. mFriendAdapter.notifyDataSetChanged();
  607. }
  608. }
  609. }
  610. private boolean applyControlMessages(ChatAdapter chatAdapter, SurespotMessage message, boolean checkSequence, boolean sort, boolean notify) throws SurespotMessageSequenceException {
  611. // see if we have applicable control messages and apply them if necessary
  612. ArrayList<SurespotControlMessage> controlMessages = chatAdapter.getControlMessages();
  613. ArrayList<SurespotControlMessage> applicableControlMessages = new ArrayList<SurespotControlMessage>();
  614. for (SurespotControlMessage controlMessage : controlMessages) {
  615. int messageId = Integer.parseInt(controlMessage.getMoreData());
  616. if (message.getId() == messageId) {
  617. applicableControlMessages.add(controlMessage);
  618. }
  619. }
  620. boolean added = false;
  621. if (applicableControlMessages.size() == 0) {
  622. added = chatAdapter.addOrUpdateMessage(message, checkSequence, sort, notify);
  623. }
  624. else {
  625. added = chatAdapter.addOrUpdateMessage(message, checkSequence, false, false);
  626. for (SurespotControlMessage controlMessage : applicableControlMessages) {
  627. SurespotLog.v(TAG, "applying control message %s: to message %s", controlMessage, message);
  628. handleControlMessage(chatAdapter, controlMessage, false, true);
  629. }
  630. if (notify) {
  631. chatAdapter.notifyDataSetChanged();
  632. }
  633. }
  634. return added;
  635. }
  636. // add entry to http cache for image we sent so we don't download it again
  637. private void handleCachedFile(ChatAdapter chatAdapter, SurespotMessage message) {
  638. SurespotLog.v(TAG, "handleCachedFile");
  639. SurespotMessage localMessage = chatAdapter.getMessageByIv(message.getIv());
  640. // if the data is different we haven't updated the url to point externally
  641. if (localMessage != null && localMessage.getId() == null && !localMessage.getData().equals(message.getData())) {
  642. // add the remote cache entry for the new url
  643. String localUri = localMessage.getData();
  644. String remoteUri = message.getData();
  645. FileInputStream fis;
  646. try {
  647. fis = new FileInputStream(new File(new URI(localUri)));
  648. byte[] imageData = Utils.inputStreamToBytes(fis);
  649. HeapResource resource = new HeapResource(imageData);
  650. Date date = new Date();
  651. String sDate = DateUtils.formatDate(date);
  652. Header[] cacheHeaders = new Header[3];
  653. // create fake cache entry
  654. cacheHeaders[0] = new BasicHeader("Last-Modified", sDate);
  655. cacheHeaders[1] = new BasicHeader("Cache-Control", "public, max-age=31557600");
  656. cacheHeaders[2] = new BasicHeader("Date", sDate);
  657. HttpCacheEntry cacheEntry = new HttpCacheEntry(date, date, mImageStatusLine, cacheHeaders, resource);
  658. SurespotLog.v(TAG, "creating http cache entry for: %s", remoteUri);
  659. mNetworkController.addCacheEntry(remoteUri, cacheEntry);
  660. // update image cache
  661. if (message.getMimeType().equals(SurespotConstants.MimeTypes.IMAGE)) {
  662. MessageImageDownloader.copyAndRemoveCacheEntry(localUri, remoteUri);
  663. }
  664. }
  665. catch (FileNotFoundException e1) {
  666. SurespotLog.w(TAG, e1, "onMessage");
  667. }
  668. catch (URISyntaxException e1) {
  669. SurespotLog.w(TAG, e1, "onMessage");
  670. }
  671. catch (IOException e) {
  672. SurespotLog.w(TAG, e, "onMessage");
  673. }
  674. // delete the file
  675. try {
  676. SurespotLog.v(TAG, "handleCachedImage deleting local file: %s", localUri);
  677. File file = new File(new URI(localUri));
  678. file.delete();
  679. }
  680. catch (URISyntaxException e) {
  681. SurespotLog.w(TAG, e, "handleMessage");
  682. }
  683. // update message to point to real location
  684. localMessage.setData(remoteUri);
  685. }
  686. }
  687. // private void handleLocalData(ChatAdapter chatAdapter, SurespotMessage message) {
  688. // SurespotLog.v(TAG, "handleLocalData");
  689. // SurespotMessage localMessage = chatAdapter.getMessageByIv(message.getIv());
  690. //
  691. // // if the data is different we haven't updated the http cache with data we sent
  692. // if (localMessage != null && localMessage.getId() == null && !localMessage.getData().equals(message.getData()) && localMessage.getInlineData() != null) {
  693. // // add the remote cache entry for the new url
  694. //
  695. // byte[] imageData = localMessage.getInlineData();
  696. //
  697. // String remoteUri = message.getData();
  698. // HeapResource resource = new HeapResource(imageData);
  699. // Date date = new Date();
  700. // String sDate = DateUtils.formatDate(date);
  701. //
  702. // Header[] cacheHeaders = new Header[3];
  703. //
  704. // // create fake cache entry
  705. // cacheHeaders[0] = new BasicHeader("Last-Modified", sDate);
  706. // cacheHeaders[1] = new BasicHeader("Cache-Control", "public, max-age=31557600");
  707. // cacheHeaders[2] = new BasicHeader("Date", sDate);
  708. //
  709. // HttpCacheEntry cacheEntry = new HttpCacheEntry(date, date, mImageStatusLine, cacheHeaders, resource);
  710. //
  711. // SurespotLog.v(TAG, "creating http cache entry for: %s", remoteUri);
  712. // mNetworkController.addCacheEntry(remoteUri, cacheEntry);
  713. //
  714. // // update message to point to real location
  715. // localMessage.setData(remoteUri);
  716. //
  717. // // clear out the inline data as we should still have the decrypted plain data
  718. // localMessage.setInlineData(null);
  719. //
  720. // }
  721. // }
  722. // message handling shiznit
  723. void loadEarlierMessages(final String username, final IAsyncCallback<Boolean> callback) {
  724. // mLoading = true;
  725. // get the list of messages
  726. Integer firstMessageId = mEarliestMessage.get(username);
  727. if (firstMessageId == null) {
  728. firstMessageId = getEarliestMessageId(username);
  729. mEarliestMessage.put(username, firstMessageId);
  730. }
  731. // else {
  732. // firstMessageId -= 60;
  733. // if (firstMessageId < 1) {
  734. // firstMessageId = 1;
  735. // }
  736. // }
  737. if (firstMessageId != null) {
  738. if (firstMessageId > 1) {
  739. SurespotLog.v(TAG, username + ": asking server for messages before messageId: " + firstMessageId);
  740. // final int fMessageId = firstMessageId;
  741. final ChatAdapter chatAdapter = mChatAdapters.get(username);
  742. mNetworkController.getEarlierMessages(username, firstMessageId, new JsonHttpResponseHandler() {
  743. @Override
  744. public void onSuccess(final JSONArray jsonArray) {
  745. // if (getActivity() != null) {
  746. SurespotMessage message = null;
  747. try {
  748. for (int i = jsonArray.length() - 1; i >= 0; i--) {
  749. JSONObject jsonMessage = new JSONObject(jsonArray.getString(i));
  750. message = SurespotMessage.toSurespotMessage(jsonMessage);
  751. chatAdapter.insertMessage(message, false);
  752. }
  753. }
  754. catch (JSONException e) {
  755. SurespotLog.e(TAG, e, "%s: error creating chat message", username);
  756. }
  757. SurespotLog.v(TAG, "%s: loaded: %d earlier messages from the server.", username, jsonArray.length());
  758. if (message != null) {
  759. mEarliestMessage.put(username, message.getId());
  760. // chatAdapter.notifyDataSetChanged();
  761. }
  762. // chatAdapter.setLoading(false);
  763. callback.handleResponse(jsonArray.length() > 0);
  764. }
  765. @Override
  766. public void onFailure(Throwable error, String content) {
  767. SurespotLog.i(TAG, error, "%s: getEarlierMessages", username);
  768. // chatAdapter.setLoading(false);
  769. callback.handleResponse(false);
  770. }
  771. });
  772. }
  773. else {
  774. SurespotLog.v(TAG, "%s: getEarlierMessages: no more messages.", username);
  775. callback.handleResponse(false);
  776. // ChatFragment.this.mNoEarlierMessages = true;
  777. }
  778. }
  779. }
  780. private void getLatestIds() {
  781. SurespotLog.v(TAG, "getLatestIds");
  782. // setMessagesLoading(true);
  783. mNetworkController.getLatestIds(mLatestUserControlId, new JsonHttpResponseHandler() {
  784. @Override
  785. public void onSuccess(int statusCode, final JSONObject jsonResponse) {
  786. SurespotLog.v(TAG, "getlatestIds success, response: %s, statusCode: %d", jsonResponse, statusCode);
  787. JSONArray conversationIds = jsonResponse.optJSONArray("conversationIds");
  788. Friend friend = null;
  789. if (conversationIds != null) {
  790. for (int i = 0; i < conversationIds.length(); i++) {
  791. try {
  792. JSONObject jsonObject = conversationIds.getJSONObject(i);
  793. String spot = jsonObject.getString("conversation");
  794. Integer availableId = jsonObject.getInt("id");
  795. String user = ChatUtils.getOtherSpotUser(spot, IdentityController.getLoggedInUser());
  796. // update available ids
  797. friend = mFriendAdapter.getFriend(user);
  798. if (friend != null) {
  799. friend.setAvailableMessageId(availableId);
  800. }
  801. }
  802. catch (JSONException e) {
  803. SurespotLog.w(TAG, "getlatestIds", e);
  804. }
  805. }
  806. }
  807. JSONArray controlIds = jsonResponse.optJSONArray("controlIds");
  808. if (controlIds != null) {
  809. for (int i = 0; i < controlIds.length(); i++) {
  810. try {
  811. JSONObject jsonObject = controlIds.getJSONObject(i);
  812. String spot = jsonObject.getString("conversation");
  813. Integer availableId = jsonObject.getInt("id");
  814. String user = ChatUtils.getOtherSpotUser(spot, IdentityController.getLoggedInUser());
  815. // update available ids
  816. friend = mFriendAdapter.getFriend(user);
  817. if (friend != null) {
  818. friend.setAvailableMessageControlId(availableId);
  819. }
  820. }
  821. catch (JSONException e) {
  822. SurespotLog.w(TAG, "getlatestIds", e);
  823. }
  824. }
  825. }
  826. JSONArray userControlMessages = jsonResponse.optJSONArray("userControlMessages");
  827. if (userControlMessages != null) {
  828. handleControlMessages(IdentityController.getLoggedInUser(), userControlMessages);
  829. }
  830. if (friend != null) {
  831. mFriendAdapter.sort();
  832. mFriendAdapter.notifyDataSetChanged();
  833. }
  834. getLatestMessagesAndControls();
  835. }
  836. @Override
  837. public void onFailure(Throwable error, String content) {
  838. // setMessagesLoading(false);
  839. SurespotLog.i(TAG, error, "loading latest messages failed");
  840. Utils.makeToast(mContext, mContext.getString(R.string.loading_latest_messages_failed));
  841. setProgress(null, false);
  842. }
  843. });
  844. }
  845. private class LatestIdPair {
  846. public int latestMessageId;
  847. public int latestControlMessageId;
  848. }
  849. private void getLatestMessagesAndControls() {
  850. for (Entry<String, ChatAdapter> entry : mChatAdapters.entrySet()) {
  851. getLatestMessagesAndControls(entry.getKey());
  852. }
  853. // done with "global" updates
  854. setProgress(null, false);
  855. }
  856. private LatestIdPair getLatestIds(String username) {
  857. Friend friend = getFriendAdapter().getFriend(username);
  858. LatestIdPair idPair = mPreConnectIds.get(username);
  859. Integer latestMessageId = idPair.latestMessageId > -1 ? idPair.latestMessageId : 0;
  860. int latestAvailableId = friend.getAvailableMessageId();
  861. int latestControlId = idPair.latestControlMessageId > -1 ? idPair.latestControlMessageId : friend.getLastReceivedMessageControlId();
  862. int latestAvailableControlId = friend.getAvailableMessageControlId();
  863. int fetchMessageId = 0;
  864. if (latestMessageId > 0) {
  865. fetchMessageId = latestAvailableId > latestMessageId ? latestMessageId : -1;
  866. }
  867. int fetchControlMessageId = 0;
  868. if (latestControlId > 0) {
  869. fetchControlMessageId = latestAvailableControlId > latestControlId ? latestControlId : -1;
  870. }
  871. LatestIdPair intPair = new LatestIdPair();
  872. intPair.latestMessageId = fetchMessageId;
  873. intPair.latestControlMessageId = fetchControlMessageId;
  874. return intPair;
  875. }
  876. private void getLatestMessagesAndControls(final String username) {
  877. LatestIdPair ids = getLatestIds(username);
  878. getLatestMessagesAndControls(username, ids.latestMessageId, ids.latestControlMessageId);
  879. }
  880. private void getLatestMessagesAndControls(String username, int messageId) {
  881. getLatestMessagesAndControls(username, messageId, -1);
  882. }
  883. private void getLatestMessagesAndControls(final String username, int fetchMessageId, int fetchControlMessageId) {
  884. SurespotLog.v(TAG, "getLatestMessagesAndControls: name %s, fetchMessageId: %d, fetchControlMessageId: %d", username, fetchMessageId,
  885. fetchControlMessageId);
  886. if (fetchMessageId > -1 || fetchControlMessageId > -1) {
  887. setProgress(username, true);
  888. mNetworkController.getMessageData(username, fetchMessageId, fetchControlMessageId, new JsonHttpResponseHandler() {
  889. @Override
  890. public void onSuccess(int statusCode, JSONObject response) {
  891. JSONArray controlMessages = response.optJSONArray("controlMessages");
  892. String messages = response.optString("messages", null);
  893. if (messages != null) {
  894. handleMessages(username, messages);
  895. }
  896. if (controlMessages != null) {
  897. handleControlMessages(username, controlMessages);
  898. }
  899. setProgress(username, false);
  900. }
  901. });
  902. }
  903. }
  904. private void handleControlMessages(String username, JSONArray jsonArray) {
  905. SurespotLog.v(TAG, "%s: handleControlMessages", username);
  906. final ChatAdapter chatAdapter = mChatAdapters.get(username);
  907. SurespotControlMessage message = null;
  908. boolean messageActivity = false;
  909. boolean userActivity = false;
  910. for (int i = 0; i < jsonArray.length(); i++) {
  911. try {
  912. JSONObject jsonMessage = new JSONObject(jsonArray.getString(i));
  913. message = SurespotControlMessage.toSurespotControlMessage(jsonMessage);
  914. handleControlMessage(chatAdapter, message, false, false);
  915. // if it's a system message from another user then check version
  916. if (message.getType().equals("user")) {
  917. userActivity = true;
  918. }
  919. else
  920. if (message.getType().equals("message")) {
  921. messageActivity = true;
  922. }
  923. }
  924. catch (JSONException e) {
  925. SurespotLog.w(TAG, e, "%s: error creating chat message", username);
  926. }
  927. }
  928. if (message != null) {
  929. SurespotLog.v(TAG, "%s: loaded: %d latest control messages from the server.", username, jsonArray.length());
  930. if (messageActivity || userActivity) {
  931. Friend friend = mFriendAdapter.getFriend(username);
  932. if (friend != null) {
  933. if (messageActivity) {
  934. if (chatAdapter != null) {
  935. friend.setLastReceivedMessageControlId(message.getId());
  936. chatAdapter.sort();
  937. chatAdapter.notifyDataSetChanged();
  938. }
  939. friend.setAvailableMessageControlId(message.getId());
  940. mFriendAdapter.notifyDataSetChanged();
  941. }
  942. if (userActivity) {
  943. friend.setLastReceivedUserControlId(message.getId());
  944. saveFriends();
  945. mFriendAdapter.notifyDataSetChanged();
  946. }
  947. }
  948. }
  949. }
  950. // chatAdapter.setLoading(false);
  951. }
  952. private void handleControlMessage(ChatAdapter chatAdapter, SurespotControlMessage message, boolean notify, boolean reApplying) {
  953. // if it's a system message from another user then check version
  954. if (message.getType().equals("user")) {
  955. handleUserControlMessage(message, notify);
  956. }
  957. else
  958. if (message.getType().equals("message")) {
  959. String otherUser = ChatUtils.getOtherSpotUser(message.getData(), IdentityController.getLoggedInUser());
  960. Friend friend = mFriendAdapter.getFriend(otherUser);
  961. if (chatAdapter == null) {
  962. chatAdapter = mChatAdapters.get(otherUser);
  963. }
  964. if (chatAdapter != null) {
  965. // if we're not re applying this control message
  966. if (!reApplying) {
  967. // add control message to check messages against later for this session
  968. chatAdapter.addControlMessage(message);
  969. }
  970. boolean controlFromMe = message.getFrom().equals(IdentityController.getLoggedInUser());
  971. if (message.getAction().equals("delete")) {
  972. int messageId = Integer.parseInt(message.getMoreData());
  973. SurespotMessage dMessage = chatAdapter.getMessageById(messageId);
  974. if (dMessage != null) {
  975. deleteMessageInternal(chatAdapter, dMessage, controlFromMe);
  976. }
  977. }
  978. else {
  979. if (message.getAction().equals("deleteAll")) {
  980. if (message.getMoreData() != null) {
  981. if (controlFromMe) {
  982. chatAdapter.deleteAllMessages(Integer.parseInt(message.getMoreData()));
  983. }
  984. else {
  985. chatAdapter.deleteTheirMessages(Integer.parseInt(message.getMoreData()));
  986. }
  987. }
  988. }
  989. else {
  990. if (message.getAction().equals("shareable") || message.getAction().equals("notshareable")) {
  991. int messageId = Integer.parseInt(message.getMoreData());
  992. SurespotMessage dMessage = chatAdapter.getMessageById(messageId);
  993. if (dMessage != null) {
  994. SurespotLog.v(TAG, "setting message " + message.getAction());
  995. dMessage.setShareable(message.getAction().equals("shareable") ? true : false);
  996. }
  997. }
  998. }
  999. }
  1000. }
  1001. if (notify) {
  1002. if (friend != null) {
  1003. // if the chat adapter is open we will have acted upon the control message
  1004. if (chatAdapter != null) {
  1005. friend.setLastReceivedMessageControlId(message.getId());
  1006. }
  1007. friend.setAvailableMessageControlId(message.getId());
  1008. }
  1009. if (chatAdapter != null) {
  1010. chatAdapter.notifyDataSetChanged();
  1011. }
  1012. }
  1013. }
  1014. }
  1015. private void handleUserControlMessage(SurespotControlMessage message, boolean notify) {
  1016. mLatestUserControlId = message.getId();
  1017. String user = null;
  1018. if (message.getAction().equals("revoke")) {
  1019. IdentityController.updateLatestVersion(mContext, message.getData(), message.getMoreData());
  1020. }
  1021. else
  1022. if (message.getAction().equals("invited")) {
  1023. user = message.getData();
  1024. mFriendAdapter.addFriendInvited(user);
  1025. }
  1026. else
  1027. if (message.getAction().equals("added")) {
  1028. user = message.getData();
  1029. mFriendAdapter.addNewFriend(user);
  1030. ChatAdapter chatAdapter = mChatAdapters.get(user);
  1031. if (chatAdapter != null) {
  1032. chatAdapter.userDeleted(false);
  1033. }
  1034. }
  1035. else
  1036. if (message.getAction().equals("invite")) {
  1037. user = message.getData();
  1038. mFriendAdapter.addFriendInviter(user);
  1039. }
  1040. else
  1041. if (message.getAction().equals("ignore")) {
  1042. String friendName = message.getData();
  1043. Friend friend = mFriendAdapter.getFriend(friendName);
  1044. // if they're not deleted, remove them
  1045. if (friend != null) {
  1046. if (!friend.isDeleted()) {
  1047. mFriendAdapter.removeFriend(friendName);
  1048. }
  1049. else {
  1050. // they've been deleted, just remove the invite flags
  1051. friend.setInviter(false);
  1052. friend.setInvited(false);
  1053. }
  1054. }
  1055. }
  1056. else
  1057. if (message.getAction().equals("delete")) {
  1058. String friendName = message.getData();
  1059. Friend friend = mFriendAdapter.getFriend(friendName);
  1060. if (friend != null) {
  1061. // if it was just a delete of an invite
  1062. if (friend.isInviter() || friend.isInvited()) {
  1063. // if they're not deleted, remove them
  1064. if (!friend.isDeleted()) {
  1065. mFriendAdapter.removeFriend(friendName);
  1066. }
  1067. else {
  1068. // they've been deleted, just remove the invite flags
  1069. friend.setInviter(false);
  1070. friend.setInvited(false);
  1071. }
  1072. // clear any associated invite notification
  1073. String loggedInUser = IdentityController.getLoggedInUser();
  1074. if (loggedInUser != null) {
  1075. mNotificationManager.cancel(loggedInUser + ":" + friendName,
  1076. SurespotConstants.IntentRequestCodes.INVITE_REQUEST_NOTIFICATION);
  1077. }
  1078. }
  1079. // they really deleted us boo hoo
  1080. else {
  1081. handleDeleteUser(friendName, message.getMoreData(), notify);
  1082. }
  1083. }
  1084. }
  1085. if (notify) {
  1086. Friend friend = mFriendAdapter.getFriend(user);
  1087. if (friend != null) {
  1088. friend.setLastReceivedUserControlId(message.getId());
  1089. }
  1090. mFriendAdapter.notifyDataSetChanged();
  1091. saveFriends();
  1092. }
  1093. }
  1094. private void handleDeleteUser(String deletedUser, String deleter, boolean notify) {
  1095. SurespotLog.v(TAG, "handleDeleteUser, deletedUser: %s, deleter: %s", deletedUser, deleter);
  1096. String username = IdentityController.getLoggedInUser();
  1097. Friend friend = mFriendAdapter.getFriend(deletedUser);
  1098. boolean iDidTheDeleting = deleter.equals(username);
  1099. if (iDidTheDeleting) {
  1100. // won't be needing this anymore
  1101. closeTab(deletedUser);
  1102. // blow all the state associated with this user away
  1103. StateController.wipeUserState(mContext, username, deletedUser);
  1104. // clear in memory cached data
  1105. SurespotApplication.getCachingService().clearUserData(deletedUser);
  1106. // clear the http cache
  1107. mNetworkController.clearCache();
  1108. // or you
  1109. mFriendAdapter.removeFriend(deletedUser);
  1110. }
  1111. // you deleted me, you bastard!!
  1112. else {
  1113. ChatAdapter chatAdapter = mChatAdapters.get(deleter);
  1114. // i'll delete all your messages then
  1115. if (chatAdapter != null) {
  1116. chatAdapter.userDeleted(true);
  1117. if (notify) {
  1118. chatAdapter.notifyDataSetChanged();
  1119. }
  1120. }
  1121. // and mark you as deleted until I want to delete you
  1122. friend.setDeleted();
  1123. // force the controls to update
  1124. if (friend != null && mCurrentChat != null && mCurrentChat.equals(deletedUser)) {
  1125. mTabShowingCallback.handleResponse(friend);
  1126. }
  1127. }
  1128. enableMenuItems(friend);
  1129. }
  1130. private void handleErrorMessage(SurespotErrorMessage errorMessage) {
  1131. SurespotMessage message = null;
  1132. Iterator<SurespotMessage> iterator = mResendBuffer.iterator();
  1133. while (iterator.hasNext()) {
  1134. message = iterator.next();
  1135. if (message.getIv().equals(errorMessage.getId())) {
  1136. iterator.remove();
  1137. message.setErrorStatus(errorMessage.getStatus());
  1138. break;
  1139. }
  1140. }
  1141. if (message != null) {
  1142. ChatAdapter chatAdapter = mChatAdapters.get(message.getOtherUser());
  1143. if (chatAdapter != null) {
  1144. chatAdapter.notifyDataSetChanged();
  1145. }
  1146. }
  1147. }
  1148. private void deleteMessageInternal(ChatAdapter chatAdapter, SurespotMessage dMessage, boolean initiatedByMe) {
  1149. // if it's an image blow the http cache entry away
  1150. if (dMessage.getMimeType() != null) {
  1151. if (dMessage.getMimeType().equals(SurespotConstants.MimeTypes.IMAGE) || dMessage.getMimeType().equals(SurespotConstants.MimeTypes.M4A)) {
  1152. mNetworkController.purgeCacheUrl(dMessage.getData());
  1153. }
  1154. boolean myMessage = dMessage.getFrom().equals(IdentityController.getLoggedInUser());
  1155. // if i sent the delete, or it's not my message then delete it
  1156. // (if someone else deleted my message we don't care)
  1157. if (initiatedByMe || !myMessage) {
  1158. SurespotLog.v(TAG, "deleting message");
  1159. chatAdapter.deleteMessageById(dMessage.getId());
  1160. }
  1161. }
  1162. }
  1163. private void handleMessages(String username, String jsonMessageString) {
  1164. SurespotLog.v(TAG, "%s: handleMessages", username);
  1165. final ChatAdapter chatAdapter = mChatAdapters.get(username);
  1166. if (chatAdapter == null) {
  1167. return;
  1168. }
  1169. int sentByMeCount = 0;
  1170. SurespotMessage lastMessage = null;
  1171. try {
  1172. JSONArray jsonUM = new JSONArray(jsonMessageString);
  1173. SurespotLog.v(TAG, "%s: loaded: %d messages from the server: %s", username, jsonUM.length(), jsonMessageString);
  1174. for (int i = 0; i < jsonUM.length(); i++) {
  1175. lastMessage = SurespotMessage.toSurespotMessage(new JSONObject(jsonUM.getString(i)));
  1176. boolean myMessage = lastMessage.getFrom().equals(IdentityController.getLoggedInUser());
  1177. if (myMessage) {
  1178. if (lastMessage.getMimeType().equals(SurespotConstants.MimeTypes.IMAGE)) {
  1179. handleCachedFile(chatAdapter, lastMessage);
  1180. }
  1181. else {
  1182. if (lastMessage.getMimeType().equals(SurespotConstants.MimeTypes.M4A)) {
  1183. handleCachedFile(chatAdapter, lastMessage);
  1184. }
  1185. }
  1186. }
  1187. boolean added = applyControlMessages(chatAdapter, lastMessage, false, false, false);
  1188. mResendBuffer.remove(lastMessage);
  1189. if (added && myMessage) {
  1190. sentByMeCount++;
  1191. }
  1192. }
  1193. }
  1194. catch (JSONException e) {
  1195. SurespotLog.w(TAG, e, "jsonStringsToMessages");
  1196. }
  1197. catch (SurespotMessageSequenceException e) {
  1198. // shouldn't happen
  1199. SurespotLog.w(TAG, e, "handleMessages");
  1200. // getLatestMessagesAndControls(username, e.getMessageId(), -1);
  1201. // setProgress(username, false);
  1202. return;
  1203. }
  1204. if (lastMessage != null) {
  1205. Friend friend = mFriendAdapter.getFriend(username);
  1206. int availableId = lastMessage.getId();
  1207. friend.setAvailableMessageId(availableId);
  1208. int lastViewedId = friend.getLastViewedMessageId();
  1209. // how many new messages total are there
  1210. int delta = availableId - lastViewedId;
  1211. // if the current chat is showing or
  1212. // all the new messages are mine then i've viewed them all
  1213. if (username.equals(mCurrentChat) || sentByMeCount == delta) {
  1214. friend.setLastViewedMessageId(availableId);
  1215. }
  1216. else {
  1217. // set the last viewed id to the difference caused by their messages
  1218. friend.setLastViewedMessageId(availableId - (delta - sentByMeCount));
  1219. }
  1220. chatAdapter.sort();
  1221. chatAdapter.notifyDataSetChanged();
  1222. chatAdapter.doneCheckingSequence();
  1223. mFriendAdapter.sort();
  1224. mFriendAdapter.notifyDataSetChanged();
  1225. scrollToEnd(username);
  1226. }
  1227. // setProgress(username, false);
  1228. }
  1229. private Integer getEarliestMessageId(String username) {
  1230. ChatAdapter chatAdapter = mChatAdapters.get(username);
  1231. Integer firstMessageId = null;
  1232. if (chatAdapter != null) {
  1233. SurespotMessage firstMessage = chatAdapter.getFirstMessageWithId();
  1234. if (firstMessage != null) {
  1235. firstMessageId = firstMessage.getId();
  1236. }
  1237. }
  1238. return firstMessageId;
  1239. }
  1240. private int getLatestMessageId(String username) {
  1241. Integer lastMessageId = 0;
  1242. ChatAdapter chatAdapter = mChatAdapters.get(username);
  1243. if (chatAdapter != null) {
  1244. SurespotMessage lastMessage = chatAdapter.getLastMessageWithId();
  1245. if (lastMessage != null) {
  1246. lastMessageId = lastMessage.getId();
  1247. }
  1248. }
  1249. return lastMessageId;
  1250. }
  1251. private Integer getLatestMessageControlId(String username) {
  1252. Friend friend = mFriendAdapter.getFriend(username);
  1253. Integer lastControlId = null;
  1254. if (friend != null) {
  1255. lastControlId = friend.getLastReceivedMessageControlId();
  1256. }
  1257. return lastControlId == null ? 0 : lastControlId;
  1258. }
  1259. public synchronized void loadMessages(String username, boolean replace) {
  1260. SurespotLog.v(TAG, "loadMessages: " + username);
  1261. String loggedInUser = IdentityController.getLoggedInUser();
  1262. if (!TextUtils.isEmpty(loggedInUser)) {
  1263. String spot = ChatUtils.getSpot(loggedInUser, username);
  1264. ChatAdapter chatAdapter = mChatAdapters.get(username);
  1265. if (replace) {
  1266. chatAdapter.setMessages(SurespotApplication.getStateController().loadMessages(spot));
  1267. }
  1268. else {
  1269. chatAdapter.addOrUpdateMessages(SurespotApplication.getStateController().loadMessages(spot));
  1270. }
  1271. }
  1272. }
  1273. private synchronized void saveMessages() {
  1274. // save last 30? messages
  1275. SurespotLog.v(TAG, "saveMessages");
  1276. if (IdentityController.getLoggedInUser() != null) {
  1277. for (Entry<String, ChatAdapter> entry : mChatAdapters.entrySet()) {
  1278. String them = entry.getKey();
  1279. String spot = ChatUtils.getSpot(IdentityController.getLoggedInUser(), them);
  1280. SurespotApplication.getStateController().saveMessages(spot, entry.getValue().getMessages(), entry.getValue().getCurrentScrollPositionId());
  1281. }
  1282. }
  1283. }
  1284. public synchronized void saveMessages(String username) {
  1285. // save last 30? messages
  1286. SurespotLog.v(TAG, "saveMessages, username: %s", username);
  1287. ChatAdapter chatAdapter = mChatAdapters.get(username);
  1288. if (chatAdapter != null) {
  1289. SurespotApplication.getStateController().saveMessages(ChatUtils.getSpot(IdentityController.getLoggedInUser(), username), chatAdapter.getMessages(),
  1290. chatAdapter.getCurrentScrollPositionId());
  1291. }
  1292. }
  1293. private void saveUnsentMessages() {
  1294. mResendBuffer.addAll(mSendBuffer);
  1295. // SurespotLog.v(TAG, "saving: " + mResendBuffer.size() + " unsent messages.");
  1296. SurespotApplication.getStateController().saveUnsentMessages(mResendBuffer);
  1297. }
  1298. private void loadUnsentMessages() {
  1299. Iterator<SurespotMessage> iterator = SurespotApplication.getStateController().loadUnsentMessages().iterator();
  1300. while (iterator.hasNext()) {
  1301. mResendBuffer.add(iterator.next());
  1302. }
  1303. // SurespotLog.v(TAG, "loaded: " + mSendBuffer.size() + " unsent messages.");
  1304. }
  1305. public synchronized void logout() {
  1306. mCurrentChat = null;
  1307. onPause();
  1308. // mViewPager = null;
  1309. // mCallback401 = null;
  1310. // mChatPagerAdapter = null;
  1311. // mIndicator = null;
  1312. // mFragmentManager = null;
  1313. // mFriendAdapter = null;
  1314. // mMenuItems = null;
  1315. // mSocketCallback = null;
  1316. mChatAdapters.clear();
  1317. // mActiveChats.clear();
  1318. // mReadSinceConnected.clear();
  1319. mResendBuffer.clear();
  1320. mSendBuffer.clear();
  1321. }
  1322. private void saveState(String username) {
  1323. SurespotLog.v(TAG, "saveState");
  1324. if (username == null) {
  1325. saveUnsentMessages();
  1326. saveMessages();
  1327. SurespotLog.v(TAG, "saving last chat: %s", mCurrentChat);
  1328. Utils.putSharedPrefsString(mContext, SurespotConstants.PrefNames.LAST_CHAT, mCurrentChat);
  1329. saveFriends();
  1330. }
  1331. else {
  1332. saveMessages(username);
  1333. }
  1334. }
  1335. private void saveFriends() {
  1336. SurespotApplication.getStateController().saveFriends(mLatestUserControlId, mFriendAdapter.getFriends());
  1337. }
  1338. private void loadState() {
  1339. SurespotLog.v(TAG, "loadState");
  1340. FriendState fs = SurespotApplication.getStateController().loadFriends();
  1341. List<Friend> friends = null;
  1342. if (fs != null) {
  1343. mLatestUserControlId = fs.userControlId;
  1344. friends = fs.friends;
  1345. }
  1346. mFriendAdapter.setFriends(friends);
  1347. mFriendAdapter.setLoading(false);
  1348. loadUnsentMessages();
  1349. }
  1350. private boolean mGlobalProgress;
  1351. private HashMap<String, Boolean> mChatProgress = new HashMap<String, Boolean>();
  1352. private synchronized void setProgress(String key, boolean inProgress) {
  1353. if (key == null) {
  1354. mGlobalProgress = inProgress;
  1355. }
  1356. else {
  1357. if (inProgress) {
  1358. mChatProgress.put(key, true);
  1359. }
  1360. else {
  1361. mChatProgress.remove(key);
  1362. }
  1363. }
  1364. boolean progress = isInProgress();
  1365. SurespotLog.v(TAG, "setProgress, isInProgress(): %b", progress);
  1366. if (mProgressCallback != null) {
  1367. mProgressCallback.handleResponse(progress);
  1368. }
  1369. }
  1370. public synchronized boolean isInProgress() {
  1371. return mGlobalProgress || !mChatProgress.isEmpty();
  1372. }
  1373. public synchronized void onResume() {
  1374. SurespotLog.v(TAG, "onResume, mPaused: %b", mPaused);
  1375. if (mPaused) {
  1376. mPaused = false;
  1377. setProgress(null, true);
  1378. // getFriendsAndIds();
  1379. // load chat messages from disk that may have been added by gcm
  1380. for (Entry<String, ChatAdapter> ca : mChatAdapters.entrySet()) {
  1381. loadMessages(ca.getKey(), false);
  1382. }
  1383. connect();
  1384. mContext.registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
  1385. clearMessageNotification(IdentityController.getLoggedInUser(), mCurrentChat);
  1386. }
  1387. }
  1388. public synchronized void onPause() {
  1389. SurespotLog.v(TAG, "onResume, mPaused: %b", mPaused);
  1390. if (!mPaused) {
  1391. mPaused = true;
  1392. saveState(null);
  1393. }
  1394. disconnect();
  1395. if (mBackgroundTimer != null) {
  1396. mBackgroundTimer.cancel();
  1397. mBackgroundTimer = null;
  1398. }
  1399. if (mReconnectTask != null) {
  1400. boolean cancel = mReconnectTask.cancel();
  1401. mReconnectTask = null;
  1402. SurespotLog.v(TAG, "Cancelled reconnect task: " + cancel);
  1403. }
  1404. // socket = null;
  1405. // workaround unchecked exception: https://code.google.com/p/android/issues/detail?id=18147
  1406. try {
  1407. mContext.unregisterReceiver(mConnectivityReceiver);
  1408. }
  1409. catch (IllegalArgumentException e) {
  1410. if (e.getMessage().contains("Receiver not registered")) {
  1411. // Ignore this exception. This is exactly what is desired
  1412. }
  1413. else {
  1414. // unexpected, re-throw
  1415. throw e;
  1416. }
  1417. }
  1418. // }
  1419. }
  1420. ChatAdapter getChatAdapter(Context context, String username) {
  1421. ChatAdapter chatAdapter = mChatAdapters.get(username);
  1422. if (chatAdapter == null) {
  1423. chatAdapter = new ChatAdapter(context);
  1424. Friend friend = mFriendAdapter.getFriend(username);
  1425. if (friend != null) {
  1426. chatAdapter.userDeleted(friend.isDeleted());
  1427. }
  1428. SurespotLog.v(TAG, "getChatAdapter created chat adapter for: %s", username);
  1429. mChatAdapters.put(username, chatAdapter);
  1430. // load savedmessages
  1431. loadMessages(username, true);
  1432. LatestIdPair idPair = new LatestIdPair();
  1433. idPair.latestMessageId = getLatestMessageId(username);
  1434. idPair.latestControlMessageId = getLatestMessageControlId(username);
  1435. SurespotLog.v(TAG, "setting preconnectids for: %s, latest message id: %d, latestcontrolid: %d", username, idPair.latestMessageId,
  1436. idPair.latestControlMessageId);
  1437. mPreConnectIds.put(username, idPair);
  1438. // get latest messages from server
  1439. getLatestMessagesAndControls(username);
  1440. }
  1441. return chatAdapter;
  1442. }
  1443. public void destroyChatAdapter(String username) {
  1444. SurespotLog.v(TAG, "destroying chat adapter for: %s", username);
  1445. saveState(username);
  1446. mChatAdapters.remove(username);
  1447. }
  1448. public synchronized void setCurrentChat(final String username) {
  1449. SurespotLog.v(TAG, "setCurrentChat: %s", username);
  1450. String loggedInUser = IdentityController.getLoggedInUser();
  1451. if (loggedInUser == null) {
  1452. return;
  1453. }
  1454. Friend friend = null;
  1455. if (username != null) {
  1456. friend = mFriendAdapter.getFriend(username);
  1457. }
  1458. mTabShowingCallback.handleResponse(friend);
  1459. if (friend != null) {
  1460. mCurrentChat = username;
  1461. mChatPagerAdapter.addChatName(username);
  1462. friend.setChatActive(true);
  1463. friend.setLastViewedMessageId(friend.getAvailableMessageId());
  1464. // cancel associated notifications
  1465. clearMessageNotification(loggedInUser, username);
  1466. int wantedPosition = mChatPagerAdapter.getChatFragmentPosition(username);
  1467. if (wantedPosition != mViewPager.getCurrentItem()) {
  1468. mViewPager.setCurrentItem(wantedPosition, true);
  1469. }
  1470. if (mMode == MODE_SELECT) {
  1471. mSendIntentCallback.handleResponse(null);
  1472. setMode(MODE_NORMAL);
  1473. }
  1474. }
  1475. else {
  1476. mCurrentChat = null;
  1477. mViewPager.setCurrentItem(0, true);
  1478. mNotificationManager.cancel(loggedInUser + ":" + username, SurespotConstants.IntentRequestCodes.INVITE_REQUEST_NOTIFICATION);
  1479. mNotificationManager.cancel(loggedInUser, SurespotConstants.IntentRequestCodes.INVITE_RESPONSE_NOTIFICATION);
  1480. }
  1481. mFriendAdapter.sort();
  1482. mFriendAdapter.notifyDataSetChanged();
  1483. // set menu item enable state
  1484. enableMenuItems(friend);
  1485. }
  1486. private void clearMessageNotification(String loggedInUser, String username) {
  1487. if (!TextUtils.isEmpty(loggedInUser) && !TextUtils.isEmpty(username)) {
  1488. mNotificationManager.cancel(loggedInUser + ":" + ChatUtils.getSpot(loggedInUser, username),
  1489. SurespotConstants.IntentRequestCodes.NEW_MESSAGE_NOTIFICATION);
  1490. }
  1491. }
  1492. private ChatFragment getChatFragment(String username) {
  1493. String fragmentTag = Utils.makePagerFragmentName(mViewPager.getId(), username.hashCode());
  1494. SurespotLog.v(TAG, "looking for fragment: %s", fragmentTag);
  1495. ChatFragment chatFragment = (ChatFragment) mFragmentManager.findFragmentByTag(fragmentTag);
  1496. SurespotLog.v(TAG, "fragment: %s", chatFragment);
  1497. return chatFragment;
  1498. }
  1499. public void sendMessage(final String username, final String plainText, final String mimeType) {
  1500. if (plainText.length() > 0) {
  1501. final ChatAdapter chatAdapter = mChatAdapters.get(username);
  1502. if (chatAdapter == null) {
  1503. return;
  1504. }
  1505. // display the message immediately
  1506. final byte[] iv = EncryptionController.getIv();
  1507. // build a message without the encryption values set as they could take a while
  1508. final SurespotMessage chatMessage = ChatUtils.buildPlainMessage(username, mimeType, EmojiParser.getInstance().addEmojiSpans(plainText), new String(
  1509. ChatUtils.base64EncodeNowrap(iv)));
  1510. try {
  1511. chatAdapter.addOrUpdateMessage(chatMessage, false, true, true);
  1512. enqueueMessage(chatMessage);
  1513. }
  1514. catch (SurespotMessageSequenceException e) {
  1515. // not gonna happen
  1516. SurespotLog.v(TAG, e, "sendMessage");
  1517. }
  1518. // do encryption in background
  1519. new AsyncTask<Void, Void, Boolean>() {
  1520. @Override
  1521. protected Boolean doInBackground(Void... arg0) {
  1522. String ourLatestVersion = IdentityController.getOurLatestVersion();
  1523. String theirLatestVersion = IdentityController.getTheirLatestVersion(username);
  1524. String result = EncryptionController.symmetricEncrypt(ourLatestVersion, username, theirLatestVersion, plainText, iv);
  1525. if (result != null) {
  1526. chatMessage.setData(result);
  1527. chatMessage.setFromVersion(ourLatestVersion);
  1528. chatMessage.setToVersion(theirLatestVersion);
  1529. SurespotLog.v(TAG, "sending message to chat controller iv: %s", chatMessage.getIv());
  1530. sendMessages();
  1531. return true;
  1532. }
  1533. else {
  1534. SurespotLog.v(TAG, "could not encrypt message, iv: %s", chatMessage.getIv());
  1535. chatMessage.setErrorStatus(500);
  1536. return false;
  1537. }
  1538. }
  1539. protected void onPostExecute(Boolean success) {
  1540. // if success is false we will have set an error status in the message so notify
  1541. if (!success) {
  1542. chatAdapter.notifyDataSetChanged();
  1543. }
  1544. };
  1545. }.execute();
  1546. }
  1547. }
  1548. public void sendVoiceMessage(final String username, final byte[] plainData, final String mimeType) {
  1549. if (plainData.length > 0) {
  1550. final ChatAdapter chatAdapter = mChatAdapters.get(username);
  1551. if (chatAdapter == null) {
  1552. return;
  1553. }
  1554. // display the message immediately
  1555. final byte[] iv = EncryptionController.getIv();
  1556. // build a message without the encryption values set as they could take a while
  1557. final SurespotMessage chatMessage = ChatUtils.buildPlainBinaryMessage(username, mimeType, plainData, new String(ChatUtils.base64EncodeNowrap(iv)));
  1558. try {
  1559. chatAdapter.addOrUpdateMessage(chatMessage, false, true, true);
  1560. enqueueMessage(chatMessage);
  1561. }
  1562. catch (SurespotMessageSequenceException e) {
  1563. // not gonna happen
  1564. SurespotLog.v(TAG, e, "sendMessage");
  1565. }
  1566. // do encryption in background
  1567. new AsyncTask<Void, Void, Boolean>() {
  1568. @Override
  1569. protected Boolean doInBackground(Void... arg0) {
  1570. String ourLatestVersion = IdentityController.getOurLatestVersion();
  1571. String theirLatestVersion = IdentityController.getTheirLatestVersion(username);
  1572. byte[] result = EncryptionController.symmetricEncrypt(ourLatestVersion, username, theirLatestVersion, plainData, iv);
  1573. if (result != null) {
  1574. // set data for sending
  1575. chatMessage.setData(new String(ChatUtils.base64EncodeNowrap(result)));
  1576. chatMessage.setFromVersion(ourLatestVersion);
  1577. chatMessage.setToVersion(theirLatestVersion);
  1578. SurespotLog.v(TAG, "sending message to chat controller iv: %s", chatMessage.getIv());
  1579. sendMessages();
  1580. return true;
  1581. }
  1582. else {
  1583. SurespotLog.v(TAG, "could not encrypt message, iv: %s", chatMessage.getIv());
  1584. chatMessage.setErrorStatus(500);
  1585. return false;
  1586. }
  1587. }
  1588. protected void onPostExecute(Boolean success) {
  1589. // if success is false we will have set an error status in the message so notify
  1590. if (!success) {
  1591. chatAdapter.notifyDataSetChanged();
  1592. }
  1593. };
  1594. }.execute();
  1595. }
  1596. }
  1597. void addMessage(Activity activity, SurespotMessage message) {
  1598. if (mChatAdapters != null) {
  1599. ChatAdapter chatAdapter = mChatAdapters.get(message.getTo());
  1600. try {
  1601. chatAdapter.addOrUpdateMessage(message, false, true, true);
  1602. scrollToEnd(message.getTo());
  1603. saveState(message.getTo());
  1604. }
  1605. catch (Exception e) {
  1606. SurespotLog.v(TAG, e, "addMessage");
  1607. }
  1608. }
  1609. else {
  1610. Utils.makeToast(activity, activity.getString(R.string.error_message_generic));
  1611. }
  1612. }
  1613. public static String getCurrentChat() {
  1614. return mCurrentChat;
  1615. }
  1616. public static boolean isPaused() {
  1617. return mPaused;
  1618. }
  1619. public boolean hasEarlierMessages(String username) {
  1620. Integer id = mEarliestMessage.get(username);
  1621. if (id == null) {
  1622. id = getEarliestMessageId(username);
  1623. }
  1624. if (id != null && id > 1) {
  1625. return true;
  1626. }
  1627. return false;
  1628. }
  1629. public void deleteMessage(final SurespotMessage message) {
  1630. // if it's on the server, send delete control message otherwise just delete it locally
  1631. if (message.getId() != null) {
  1632. // SurespotControlMessage dmessage = new SurespotControlMessage();
  1633. // String me = IdentityController.getLoggedInUser();
  1634. // dmessage.setFrom(me);
  1635. // dmessage.setType("message");
  1636. // dmessage.setAction("delete");
  1637. // dmessage.setData(ChatUtils.getSpot(message));
  1638. // dmessage.setMoreData(message.getId().toString());
  1639. // dmessage.setLocalId(me + Integer.toString(getLatestMessageControlId(message.getOtherUser()) + 1));
  1640. // sendControlMessage(dmessage);
  1641. // String spot = ChatUtils.getSpot(message);
  1642. final ChatAdapter chatAdapter = mChatAdapters.get(message.getOtherUser());
  1643. setProgress("delete", true);
  1644. if (chatAdapter != null) {
  1645. mNetworkController.deleteMessage(message.getOtherUser(), message.getId(), new AsyncHttpResponseHandler() {
  1646. @Override
  1647. public void onSuccess(int statusCode, String content) {
  1648. // MainActivity.getMainHandler().post(new Runnable() {
  1649. //
  1650. // @Override
  1651. // public void run() {
  1652. deleteMessageInternal(chatAdapter, message, true);
  1653. setProgress("delete", false);
  1654. // }
  1655. // });
  1656. }
  1657. @Override
  1658. public void onFailure(Throwable error, String content) {
  1659. SurespotLog.i(TAG, error, "deleteMessage");
  1660. // MainActivity.getMainHandler().post(new Runnable() {
  1661. //
  1662. // @Override
  1663. // public void run() {
  1664. setProgress("delete", false);
  1665. Utils.makeToast(mContext, mContext.getString(R.string.could_not_delete_message));
  1666. // }
  1667. // });
  1668. }
  1669. });
  1670. }
  1671. }
  1672. else {
  1673. // remove the local message
  1674. String otherUser = message.getOtherUser();
  1675. mResendBuffer.remove(message);
  1676. mSendBuffer.remove(message);
  1677. ChatAdapter chatAdapter = mChatAdapters.get(otherUser);
  1678. chatAdapter.deleteMessageByIv(message.getIv());
  1679. saveState(otherUser);
  1680. // if it's an image, delete the local image file
  1681. if (message.getMimeType().equals(SurespotConstants.MimeTypes.IMAGE)) {
  1682. if (message.getData().startsWith("file")) {
  1683. try {
  1684. new File(new URI(message.getData())).delete();
  1685. }
  1686. catch (URISyntaxException e) {
  1687. SurespotLog.w(TAG, e, "deleteMessage");
  1688. }
  1689. }
  1690. }
  1691. }
  1692. }
  1693. public void deleteMessages(String name) {
  1694. Friend friend = mFriendAdapter.getFriend(name);
  1695. if (friend != null) {
  1696. deleteMessages(friend);
  1697. }
  1698. }
  1699. public void deleteMessages(final Friend friend) {
  1700. // if it's on the server, send delete control message otherwise just delete it locally
  1701. if (friend != null) {
  1702. String username = friend.getName();
  1703. setProgress("deleteMessages", true);
  1704. int lastReceivedMessageId = 0;
  1705. final ChatAdapter chatAdapter = mChatAdapters.get(username);
  1706. if (chatAdapter != null) {
  1707. lastReceivedMessageId = getLatestMessageId(username);
  1708. }
  1709. else {
  1710. lastReceivedMessageId = friend.getLastViewedMessageId();
  1711. }
  1712. final int finalMessageId = lastReceivedMessageId;
  1713. mNetworkController.deleteMessages(username, lastReceivedMessageId, new AsyncHttpResponseHandler() {
  1714. @Override
  1715. public void onSuccess(int statusCode, String content) {
  1716. if (chatAdapter != null) {
  1717. chatAdapter.deleteAllMessages(finalMessageId);
  1718. chatAdapter.notifyDataSetChanged();
  1719. }
  1720. else {
  1721. // tell friend there's a new control message so they get it when the tab is opened
  1722. friend.setAvailableMessageControlId(friend.getAvailableMessageControlId() + 1);
  1723. saveFriends();
  1724. }
  1725. setProgress("deleteMessages", false);
  1726. }
  1727. @Override
  1728. public void onFailure(Throwable error, String content) {
  1729. setProgress("deleteMessages", false);
  1730. Utils.makeToast(mContext, mContext.getString(R.string.could_not_delete_messages));
  1731. }
  1732. });
  1733. }
  1734. }
  1735. public void deleteFriend(Friend friend) {
  1736. if (friend != null) {
  1737. final String username = friend.getName();
  1738. setProgress("deleteFriend", true);
  1739. mNetworkController.deleteFriend(username, new AsyncHttpResponseHandler() {
  1740. @Override
  1741. public void onSuccess(int statusCode, String content) {
  1742. handleDeleteUser(username, IdentityController.getLoggedInUser(), true);
  1743. setProgress("deleteFriend", false);
  1744. }
  1745. @Override
  1746. public void onFailure(Throwable error, String content) {
  1747. SurespotLog.i(TAG, error, "deleteFriend");
  1748. setProgress("deleteFriend", false);
  1749. Utils.makeToast(mContext, mContext.getString(R.string.could_not_delete_friend));
  1750. }
  1751. });
  1752. }
  1753. }
  1754. public void toggleMessageShareable(String to, final String messageIv) {
  1755. final ChatAdapter chatAdapter = mChatAdapters.get(to);
  1756. final SurespotMessage message = chatAdapter.getMessageByIv(messageIv);
  1757. if (message != null && message.getId() > 0) {
  1758. String messageUsername = message.getOtherUser();
  1759. if (!messageUsername.equals(to)) {
  1760. Utils.makeToast(mContext, mContext.getString(R.string.could_not_set_message_lock_state));
  1761. return;
  1762. }
  1763. if (chatAdapter != null) {
  1764. setProgress("shareable", true);
  1765. mNetworkController.setMessageShareable(to, message.getId(), !message.isShareable(), new AsyncHttpResponseHandler() {
  1766. @Override
  1767. public void onSuccess(int statusCode, String content) {
  1768. message.setShareable(!message.isShareable());
  1769. chatAdapter.notifyDataSetChanged();
  1770. setProgress("shareable", false);
  1771. }
  1772. @Override
  1773. public void onFailure(Throwable error, String content) {
  1774. SurespotLog.i(TAG, error, "toggleMessageShareable");
  1775. setProgress("shareable", false);
  1776. Utils.makeToast(mContext, mContext.getString(R.string.could_not_set_message_lock_state));
  1777. }
  1778. });
  1779. }
  1780. }
  1781. }
  1782. public void resendFileMessage(String to, final String messageIv) {
  1783. final ChatAdapter chatAdapter = mChatAdapters.get(to);
  1784. final SurespotMessage message = chatAdapter.getMessageByIv(messageIv);
  1785. message.setErrorStatus(0);
  1786. chatAdapter.notifyDataSetChanged();
  1787. setProgress("resend", true);
  1788. ChatUtils.resendFileMessage(mContext, mNetworkController, message, new IAsyncCallback<Boolean>() {
  1789. @Override
  1790. public void handleResponse(Boolean result) {
  1791. setProgress("resend", false);
  1792. if (!result) {
  1793. message.setErrorStatus(500);
  1794. chatAdapter.notifyDataSetChanged();
  1795. }
  1796. }
  1797. });
  1798. }
  1799. public FriendAdapter getFriendAdapter() {
  1800. return mFriendAdapter;
  1801. }
  1802. public boolean isFriendDeleted(String username) {
  1803. return getFriendAdapter().getFriend(username).isDeleted();
  1804. }
  1805. public boolean isFriendDeleted() {
  1806. return getFriendAdapter().getFriend(mCurrentChat).isDeleted();
  1807. }
  1808. private void getFriendsAndIds() {
  1809. if (mFriendAdapter.getCount() == 0 && mLatestUserControlId == 0) {
  1810. mFriendAdapter.setLoading(true);
  1811. // get the list of friends
  1812. mNetworkController.getFriends(new JsonHttpResponseHandler() {
  1813. @Override
  1814. public void onSuccess(JSONObject jsonObject) {
  1815. SurespotLog.v(TAG, "getFriends success.");
  1816. ArrayList<Friend> friends = new ArrayList<Friend>();
  1817. try {
  1818. mLatestUserControlId = jsonObject.getInt("userControlId");
  1819. JSONArray friendsArray = jsonObject.getJSONArray("friends");
  1820. if (friendsArray != null) {
  1821. for (int i = 0; i < friendsArray.length(); i++) {
  1822. JSONObject jsonFriend = friendsArray.getJSONObject(i);
  1823. // String name = jsonFriend.getString("name");
  1824. // jsonFriend.put("name", name);
  1825. // jsonFriend.put("flags", jsonFriend.getInt("flags"));
  1826. Friend friend = Friend.toFriend(jsonFriend);
  1827. friends.add(friend);
  1828. SurespotLog.v(TAG, "getFriendsAndIds, adding friend: %s", friend);
  1829. }
  1830. }
  1831. }
  1832. catch (JSONException e) {
  1833. SurespotLog.e(TAG, e, "getFriendsAndIds");
  1834. mFriendAdapter.setLoading(false);
  1835. return;
  1836. }
  1837. if (mFriendAdapter != null) {
  1838. mFriendAdapter.addFriends(friends);
  1839. mFriendAdapter.setLoading(false);
  1840. }
  1841. getLatestIds();
  1842. }
  1843. @Override
  1844. public void onFailure(Throwable arg0, String content) {
  1845. // if we didn't get a 401
  1846. if (!mNetworkController.isUnauthorized()) {
  1847. mFriendAdapter.setLoading(false);
  1848. SurespotLog.i(TAG, arg0, "getFriends: %s", content);
  1849. setProgress(null, false);
  1850. }
  1851. }
  1852. });
  1853. }
  1854. else {
  1855. getLatestIds();
  1856. }
  1857. }
  1858. public void closeTab() {
  1859. if (mChatPagerAdapter.getCount() > 0) {
  1860. int position = mViewPager.getCurrentItem();
  1861. if (position > 0) {
  1862. String name = mChatPagerAdapter.getChatName(position);
  1863. if (name != null) {
  1864. SurespotLog.v(TAG, "closeTab, name: %s, position: %d", name, position);
  1865. mChatPagerAdapter.removeChat(mViewPager.getId(), position);
  1866. mFriendAdapter.setChatActive(name, false);
  1867. mEarliestMessage.remove(name);
  1868. destroyChatAdapter(name);
  1869. mIndicator.notifyDataSetChanged();
  1870. position = mViewPager.getCurrentItem();
  1871. setCurrentChat(mChatPagerAdapter.getChatName(position));
  1872. SurespotLog.v(TAG, "closeTab, new tab name: %s, position: %d", mCurrentChat, position);
  1873. }
  1874. }
  1875. }
  1876. }
  1877. /**
  1878. * Called when a user has been deleted
  1879. *
  1880. * @param username
  1881. */
  1882. public void closeTab(String username) {
  1883. if (mChatPagerAdapter.getCount() > 0) {
  1884. int position = mChatPagerAdapter.getChatFragmentPosition(username);
  1885. if (position > 0) {
  1886. String name = mChatPagerAdapter.getChatName(position);
  1887. if (name != null) {
  1888. SurespotLog.v(TAG, "closeTab, name: %s, position: %d", name, position);
  1889. mChatPagerAdapter.removeChat(mViewPager.getId(), position);
  1890. mFriendAdapter.setChatActive(name, false);
  1891. mEarliestMessage.remove(name);
  1892. destroyChatAdapter(name);
  1893. mIndicator.notifyDataSetChanged();
  1894. position = mViewPager.getCurrentItem();
  1895. setCurrentChat(mChatPagerAdapter.getChatName(position));
  1896. SurespotLog.v(TAG, "closeTab, new tab name: %s, position: %d", mCurrentChat, position);
  1897. }
  1898. }
  1899. }
  1900. }
  1901. public synchronized void setMode(int mode) {
  1902. mMode = mode;
  1903. }
  1904. public int getMode() {
  1905. return mMode;
  1906. }
  1907. public void enableMenuItems(Friend friend) {
  1908. boolean enabled = mMode != MODE_SELECT && mCurrentChat != null;
  1909. SurespotLog.v(TAG, "enableMenuItems, enabled: %b", enabled);
  1910. boolean isDeleted = false;
  1911. if (friend != null) {
  1912. isDeleted = friend.isDeleted();
  1913. }
  1914. if (mMenuItems != null) {
  1915. for (MenuItem menuItem : mMenuItems) {
  1916. if (menuItem.getItemId() != R.id.menu_purchase_voice) {
  1917. // deleted users can't have images sent to them
  1918. if (menuItem.getItemId() == R.id.menu_capture_image_bar || menuItem.getItemId() == R.id.menu_send_image_bar) {
  1919. menuItem.setVisible(enabled && !isDeleted);
  1920. }
  1921. else {
  1922. menuItem.setVisible(enabled);
  1923. }
  1924. }
  1925. else {
  1926. boolean voiceEnabled = SurespotApplication.getBillingController().hasVoiceMessaging();
  1927. SurespotLog.v(TAG, "enableMenuItems, setting voice purchase menu visibility: %b", !voiceEnabled);
  1928. menuItem.setVisible(!voiceEnabled);
  1929. }
  1930. }
  1931. }
  1932. }
  1933. public void scrollToEnd(String to) {
  1934. ChatFragment chatFragment = getChatFragment(to);
  1935. if (chatFragment != null) {
  1936. chatFragment.scrollToEnd();
  1937. }
  1938. }
  1939. public void setImageUrl(String name, String url, String version, String iv) {
  1940. Friend friend = mFriendAdapter.getFriend(name);
  1941. if (friend != null) {
  1942. String oldUrl = friend.getImageUrl();
  1943. if (!TextUtils.isEmpty(oldUrl)) {
  1944. mNetworkController.removeCacheEntry(oldUrl);
  1945. }
  1946. friend.setImageUrl(url);
  1947. friend.setImageIv(iv);
  1948. friend.setImageVersion(version);
  1949. saveFriends();
  1950. mFriendAdapter.notifyDataSetChanged();
  1951. }
  1952. }
  1953. public SurespotMessage getLiveMessage(SurespotMessage message) {
  1954. String otherUser = message.getOtherUser();
  1955. ChatAdapter chatAdapter = mChatAdapters.get(otherUser);
  1956. if (chatAdapter != null) {
  1957. return chatAdapter.getMessageByIv(message.getIv());
  1958. }
  1959. return null;
  1960. }
  1961. // called from GCM service
  1962. public boolean addMessageExternal(final SurespotMessage message) {
  1963. // might not be same user so check that to is the currently logged in user
  1964. boolean sameUser = message.getTo().equals(IdentityController.getLoggedInUser());
  1965. if (!sameUser) {
  1966. return false;
  1967. }
  1968. else {
  1969. final ChatAdapter chatAdapter = mChatAdapters.get(message.getFrom());
  1970. if (chatAdapter == null) {
  1971. return false;
  1972. }
  1973. else {
  1974. Handler handler = new Handler(Looper.getMainLooper());
  1975. handler.post(new Runnable() {
  1976. @Override
  1977. public void run() {
  1978. try {
  1979. applyControlMessages(chatAdapter, message, false, true, true);
  1980. }
  1981. catch (SurespotMessageSequenceException e) {
  1982. }
  1983. }
  1984. });
  1985. return true;
  1986. }
  1987. }
  1988. }
  1989. }