PageRenderTime 56ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

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

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