package com.whatscloud.logic.sync.manager;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
import com.fasterxml.jackson.core.type.TypeReference;
import com.whatscloud.R;
import com.whatscloud.config.debug.Logging;
import com.whatscloud.config.app.WhatsCloud;
import com.whatscloud.config.functionality.Sync;
import com.whatscloud.logic.auth.User;
import com.whatscloud.logic.sync.integration.WhatsApp;
import com.whatscloud.logic.ui.Events;
import com.whatscloud.model.Chat;
import com.whatscloud.model.ChatMessage;
import com.whatscloud.logic.security.AES;
import com.whatscloud.utils.strings.StringUtils;
import com.whatscloud.utils.networking.HTTP;
import com.whatscloud.utils.objects.Singleton;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import java.util.ArrayList;
import java.util.List;
public class SyncManager
{
Context mContext;
WhatsApp mWhatsApp;
boolean mIsScheduledSync;
public SyncManager( Context context, boolean isScheduledSync )
{
//--------------------------------
// Save context instance
//--------------------------------
this.mContext = context;
//--------------------------------
// Save sync type
//--------------------------------
this.mIsScheduledSync = isScheduledSync;
//--------------------------------
// Create database connector
//--------------------------------
this.mWhatsApp = new WhatsApp( context );
}
void syncUnread() throws Exception
{
//--------------------------------
// Get last synced unread count
//--------------------------------
int lastUnreadCount = getLastUnreadCount(mContext);
//--------------------------------
// Get current unread count
//--------------------------------
int unreadCount = mWhatsApp.getTotalUnreadCount();
//--------------------------------
// Did it change?
//--------------------------------
if ( lastUnreadCount != unreadCount )
{
//--------------------------------
// Are there no more unread?
//--------------------------------
if ( unreadCount == 0 )
{
//----------------------------
// Sync it with server
//----------------------------
saveUnreadCount(unreadCount);
}
else
{
//--------------------------------
// Save last unread count
//--------------------------------
saveLastUnreadCount(mContext, unreadCount);
}
}
}
public void sync() throws Exception
{
//----------------------------
// Sync unread count
//----------------------------
syncUnread();
//----------------------------
// Sync WhatsApp chats
//----------------------------
syncChats();
//----------------------------
// Sync WhatsApp messages
//----------------------------
syncMessages();
}
public static int getLastSyncedMessageID(Context context)
{
//----------------------------
// Query SharedPreferences
//----------------------------
return Singleton.getSettings(context).getInt("last_message", 0);
}
public static int getLastSyncedChatID(Context context)
{
//----------------------------
// Query SharedPreferences
//----------------------------
return Singleton.getSettings(context).getInt("last_chat", 0);
}
public static int getLastUnreadCount(Context context)
{
//----------------------------
// Query SharedPreferences
//----------------------------
return Singleton.getSettings(context).getInt("last_unread", 0);
}
void initialMessagesSync() throws Exception
{
//--------------------------------
// Prepare last id temp variable
//--------------------------------
int maxID = 0, i = 0;
//--------------------------------
// Send the request
//--------------------------------
String responseJSON = HTTP.get(WhatsCloud.API_URL + "/sync?do=reset&key=" + User.getAPIKey(mContext));
//--------------------------------
// No success? Stop right there!
//--------------------------------
if ( ! responseJSON.contains( "success" ) )
{
//---------------------------------
// Return sync failed
//---------------------------------
throw new Exception(responseJSON);
}
//--------------------------------
// Get all chats (from wa_contacts)
//--------------------------------
List<Chat> chats = mWhatsApp.getChatList();
//--------------------------------
// No chats? Halt!
//--------------------------------
if ( chats.size() == 0 )
{
throw new Exception(mContext.getString(R.string.retrieveChatsError));
}
//--------------------------------
// Log the chats
//--------------------------------
Log.d(Logging.TAG_NAME, "Got " + chats.size() + " chats");
//--------------------------------
// Temporary list of messages
// ready for syncing
//--------------------------------
List<ChatMessage> syncMessages = new ArrayList<ChatMessage>();
//--------------------------------
// For each chat, query last
// X messages and sync them
//--------------------------------
for ( Chat chat : chats )
{
//----------------------------
// Increment counter
//----------------------------
i++;
//--------------------------------
// Get last 20 messages
//--------------------------------
List<ChatMessage> lastMessages = mWhatsApp.getMessages(0, chat.jid, Sync.MAX_MESSAGES_PER_CHAT_INITIAL_SYNC);
//--------------------------------
// No messages returned? Continue
//--------------------------------
if ( lastMessages.size() == 0 )
{
continue;
}
//--------------------------------
// Log the chats
//--------------------------------
Log.d(Logging.TAG_NAME, "Got " + lastMessages.size() + " messages for " + chat.jid);
//--------------------------------
// Loop over messages
// to find the max id
//--------------------------------
for ( ChatMessage message : lastMessages )
{
//--------------------------------
// Is it bigger than MaxID?
//--------------------------------
if ( message.id > maxID )
{
//--------------------------------
// Update MaxID with new value
//--------------------------------
maxID = message.id;
}
}
//--------------------------------
// Add messages to sync list
//--------------------------------
syncMessages.addAll(lastMessages);
//--------------------------------
// Time to sync?
//--------------------------------
if ( syncMessages.size() >= Sync.MAX_MESSAGES_PER_SYNC)
{
//--------------------------------
// Sync the list to server
//--------------------------------
syncMessagesToServer(syncMessages);
//--------------------------------
// Clear the list
//--------------------------------
syncMessages.clear();
}
//----------------------------
// Update progress
//----------------------------
saveSyncProgress(mContext.getString(R.string.syncingMessages) + " (" + (int) (100 * (double) i / chats.size()) + "%)");
}
//--------------------------------
// Left over messages?
//--------------------------------
if ( syncMessages.size() > 0 )
{
//--------------------------------
// Sync the list to server
//--------------------------------
syncMessagesToServer(syncMessages);
//--------------------------------
// Clear the list
//--------------------------------
syncMessages.clear();
}
//---------------------------------
// Save last synced message id
//---------------------------------
saveLastMessageID(mContext, maxID);
}
void syncMessages() throws Exception
{
//--------------------------------
// Initial sync? Special algorithm
//--------------------------------
if ( ! User.isInitialSyncComplete(mContext) )
{
initialMessagesSync();
}
else
{
//----------------------------
// Sync until we've synced
// all device messages!
//----------------------------
while ( syncMessagesBulk() > 0 );
}
}
void syncChats() throws Exception
{
//----------------------------
// Sync until we've synced
// all WhatsApp chats!
//----------------------------
while ( syncChatsBulk() > 0 );
}
public void saveSyncProgress(String message)
{
//----------------------------------
// Get shared preferences editor
//----------------------------------
SharedPreferences.Editor editor = Singleton.getSettings(mContext).edit();
//---------------------------------
// Store in shared preferences
//---------------------------------
editor.putString("sync_message", message);
//---------------------------------
// Save preferences
//---------------------------------
editor.commit();
//---------------------------------
// Broadcast event
//---------------------------------
Events.broadcastEvent(mContext, "SyncProgress");
}
public static void saveLastMessageID(Context context, int lastMessageID)
{
//----------------------------------
// Get shared preferences editor
//----------------------------------
SharedPreferences.Editor editor = Singleton.getSettings(context).edit();
//---------------------------------
// Store in shared preferences
//---------------------------------
editor.putInt("last_message", lastMessageID);
//---------------------------------
// Save preferences
//---------------------------------
editor.commit();
}
public static void saveLastUnreadCount(Context context, int lastUnreadCount)
{
//----------------------------------
// Get shared preferences editor
//----------------------------------
SharedPreferences.Editor editor = Singleton.getSettings(context).edit();
//---------------------------------
// Store in shared preferences
//---------------------------------
editor.putInt("last_unread", lastUnreadCount);
//---------------------------------
// Save preferences
//---------------------------------
editor.commit();
}
public static void saveLastChatID(Context context, int lastChatID)
{
//----------------------------------
// Get shared preferences editor
//----------------------------------
SharedPreferences.Editor editor = Singleton.getSettings(context).edit();
//---------------------------------
// Store in shared preferences
//---------------------------------
editor.putInt("last_chat", lastChatID);
//---------------------------------
// Save preferences
//---------------------------------
editor.commit();
}
public int syncMessagesBulk() throws Exception
{
//--------------------------------
// Get last synced message id
//--------------------------------
int lastMessageID = getLastSyncedMessageID(mContext);
//--------------------------------
// Get new messages
//--------------------------------
List<ChatMessage> newMessages = mWhatsApp.getMessages(lastMessageID, null, Sync.MAX_MESSAGES_PER_SYNC);
//----------------------------
// Sync messages to server
//----------------------------
syncMessagesToServer(newMessages);
//----------------------------
// Return synced messages
//----------------------------
return newMessages.size();
}
public int syncChatsBulk() throws Exception
{
//--------------------------------
// Get last synced chat id
//--------------------------------
int lastSyncedChatID = getLastSyncedChatID(mContext);
//--------------------------------
// Get total chats
//--------------------------------
int lastChatID = mWhatsApp.getLastChatID();
//--------------------------------
// Get new chats
//--------------------------------
List<Chat> newChats = mWhatsApp.getChats(lastSyncedChatID);
//--------------------------------
// Got new chats?
//--------------------------------
if ( newChats.size() > 0 )
{
//----------------------------
// Save them!
//----------------------------
saveChats(newChats);
//---------------------------------
// Update last synced chat id
//---------------------------------
lastSyncedChatID = newChats.get(newChats.size() - 1).id;
//---------------------------------
// Save last synced chat id
//---------------------------------
saveLastChatID(mContext, lastSyncedChatID);
}
//----------------------------
// Update progress
//----------------------------
saveSyncProgress(mContext.getString(R.string.syncingChats) + " (" + (int) (100 * (double) lastSyncedChatID / lastChatID) + "%)");
//----------------------------
// Return synced chats
//----------------------------
return newChats.size();
}
public void syncMessagesToServer(List<ChatMessage> messages) throws Exception
{
//----------------------------
// No messages?
//----------------------------
if ( messages.size() == 0 )
{
return;
}
//----------------------------
// Traverse messages
//----------------------------
for ( ChatMessage message : messages )
{
//----------------------------
// Encrypt message data
//----------------------------
message.data = AES.encrypt(message.data, mContext);
//----------------------------
// Encrypt media URL
//----------------------------
message.mediaURL = AES.encrypt(message.mediaURL, mContext);
}
//----------------------------
// Prepare post data
//----------------------------
List<NameValuePair> postData = new ArrayList<NameValuePair>();
//----------------------------
// Convert messages into JSON
//----------------------------
postData.add(new BasicNameValuePair("messages", Singleton.getJackson().writeValueAsString(messages)));
//--------------------------------
// Add user's authentication key
//--------------------------------
postData.add(new BasicNameValuePair("key", User.getAPIKey(mContext)));
//--------------------------------
// Add encryption flag
//--------------------------------
postData.add(new BasicNameValuePair("encrypted", "1"));
//--------------------------------
// Scheduled sync - for unread
//--------------------------------
if (mIsScheduledSync)
{
postData.add(new BasicNameValuePair("scheduled", "1"));
}
//--------------------------------
// Send the request
//--------------------------------
String responseJSON = HTTP.post(WhatsCloud.API_URL + "/sync?do=messages", postData);
//--------------------------------
// No Internet?
//--------------------------------
if ( StringUtils.stringIsNullOrEmpty(responseJSON))
{
throw new Exception(mContext.getString(R.string.noInternetDesc));
}
//---------------------------------
// Get last synced message id
//---------------------------------
int lastMessageID = messages.get(messages.size() - 1).id;
//---------------------------------
// Save it
//---------------------------------
saveLastMessageID(mContext, lastMessageID);
//--------------------------------
// Send the pending messages
//--------------------------------
sendPendingMessages(responseJSON);
//--------------------------------
// Log the sync
//--------------------------------
Log.d(Logging.TAG_NAME, "Synced " + messages.size() + " messages");
}
public void saveUnreadCount(int unread) throws Exception
{
//--------------------------------
// Send the request
//--------------------------------
String responseJSON = HTTP.get(WhatsCloud.API_URL + "/sync?do=unread&key=" + User.getAPIKey(mContext));
//--------------------------------
// No success? Stop right there!
//--------------------------------
if ( ! responseJSON.contains( "success" ) )
{
//---------------------------------
// Return sync failed
//---------------------------------
throw new Exception(responseJSON);
}
//--------------------------------
// Save last synced unread count
//--------------------------------
saveLastUnreadCount(mContext, unread);
//--------------------------------
// Log the sync
//--------------------------------
Log.d(Logging.TAG_NAME, "Synced unread count");
}
public void resetTotalUnreadCount() throws Exception
{
//--------------------------------
// Do it via WhatsApp class
//--------------------------------
mWhatsApp.resetTotalUnreadCount();
//--------------------------------
// Save last synced unread count
// To prevent extra request to API
//--------------------------------
saveLastUnreadCount(mContext, 0);
}
public void sendPendingMessages(String responseJSON) throws Exception
{
//--------------------------------
// No Internet?
//--------------------------------
if ( StringUtils.stringIsNullOrEmpty(responseJSON))
{
throw new Exception(mContext.getString(R.string.noInternetDesc));
}
//----------------------------------
// Convert JSON to object
//----------------------------------
List<ChatMessage> pendingMessages = Singleton.getJackson().readValue( responseJSON, new TypeReference<List<ChatMessage>>(){} );
//--------------------------------
// Server returned error? Parse it
//--------------------------------
if ( pendingMessages == null )
{
//---------------------------------
// Return sync failed
//---------------------------------
throw new Exception(responseJSON);
}
//---------------------------------
// Traverse messages
//---------------------------------
for ( ChatMessage message : pendingMessages )
{
//---------------------------------
// Decrypt each message
//---------------------------------
message.data = AES.decrypt(message.data, mContext);
}
//----------------------------------
// Got any messages?
//----------------------------------
if ( pendingMessages.size() > 0 )
{
//----------------------------------
// Send them!
//----------------------------------
mWhatsApp.sendMessages(pendingMessages);
//----------------------------------
// Sync them back to server
// (to populate the MIDs)
//----------------------------------
syncMessages();
}
}
public void saveChats(List<Chat> chats) throws Exception
{
//----------------------------
// Traverse chats
//----------------------------
for ( Chat chat : chats )
{
//----------------------------
// Encrypt chat name
//----------------------------
chat.name = AES.encrypt(chat.name, mContext);
//----------------------------
// Encrypt chat status
//----------------------------
chat.status = AES.encrypt(chat.status, mContext);
}
//----------------------------
// Prepare post data
//----------------------------
List<NameValuePair> postData = new ArrayList<NameValuePair>();
//----------------------------
// Convert chats into JSON
//----------------------------
postData.add(new BasicNameValuePair("chats", Singleton.getJackson().writeValueAsString(chats)));
//--------------------------------
// Add user's authentication key
//--------------------------------
postData.add(new BasicNameValuePair("key", User.getAPIKey(mContext)));
//--------------------------------
// Add encryption flag
//--------------------------------
postData.add(new BasicNameValuePair("encrypted", "1"));
//--------------------------------
// Send the request
//--------------------------------
String responseJSON = HTTP.post(WhatsCloud.API_URL + "/sync?do=chats", postData);
//--------------------------------
// No Internet?
//--------------------------------
if ( StringUtils.stringIsNullOrEmpty(responseJSON))
{
throw new Exception(mContext.getString(R.string.noInternetDesc));
}
//--------------------------------
// Server returned error? Parse it
//--------------------------------
if ( responseJSON.contains( "error" ) )
{
//---------------------------------
// Return sync failed
//---------------------------------
throw new Exception(responseJSON);
}
}
}