package com.magnet.wru;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;
import com.magnet.mmx.client.api.ListResult;
import com.magnet.mmx.client.api.MMX;
import com.magnet.mmx.client.api.MMXChannel;
import com.magnet.mmx.client.api.MMXMessage;
import com.magnet.mmx.client.api.MMXUser;
import com.magnet.mmx.protocol.GeoLoc;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class WRU {
private static final String TAG = WRU.class.getSimpleName();
private static final String SHARED_PREF_NAME = WRU.class.getName();
private static final String SHARED_PREF_KEY_MMX_USERNAME = "mmxUsername";
private static final String SHARED_PREF_KEY_MMX_PASSWORD = "mmxPassword";
private static final String SHARED_PREF_KEY_JOINED_TOPIC_KEY = "joinedTopicKey";
private static final String SHARED_PREF_KEY_JOINED_TOPIC_PASSPHRASE = "joinedTopicPassphrase";
private static final String SHARED_PREF_KEY_JOINED_TOPIC_AS_USERNAME = "joinedTopicUsername";
private static final String SHARED_PREF_KEY_UPDATE_INTERVAL = "joinedTopicUpdateInterval";
private static final String SUFFIX_CHAT = "_chat";
private static WRU sInstance = null;
private Context mContext;
private SQLiteDatabase mDb = null;
private SharedPreferences mSharedPrefs;
private Random mRandom;
private MessageDigest mDigester;
private MMXChannel mJoinedTopic;
private MMXChannel mJoinedTopicChat;
private String mJoinedTopicKey;
private String mJoinedTopicPassphrase;
private long mJoinedTopicUpdateInterval;
private String mUsername;
private AtomicBoolean mLoginSuccess = new AtomicBoolean(false);
private AtomicBoolean mLoggingIn = new AtomicBoolean(false);
private GoogleApiClient mGoogleApiClient = null;
private final AtomicBoolean mGoogleApiInitialized = new AtomicBoolean(false);
private GoogleApiClient.OnConnectionFailedListener mGoogleConnectionFailedListener =
new GoogleApiClient.OnConnectionFailedListener() {
public void onConnectionFailed(ConnectionResult connectionResult) {
synchronized (mGoogleApiInitialized) {
mGoogleApiInitialized.set(false);
mGoogleApiInitialized.notify();
}
}
};
private GoogleApiClient.ConnectionCallbacks mGoogleConnectionCallbacks =
new GoogleApiClient.ConnectionCallbacks() {
public void onConnected(Bundle bundle) {
synchronized (mGoogleApiInitialized) {
mGoogleApiInitialized.set(true);
mGoogleApiInitialized.notify();
}
}
public void onConnectionSuspended(int i) {
synchronized (mGoogleApiInitialized) {
mGoogleApiInitialized.set(false);
mGoogleApiInitialized.notify();
}
}
};
private final ConcurrentHashMap<String, LocationTime> mLocations = new ConcurrentHashMap<>();
private MMX.EventListener mListener = new MMX.EventListener() {
public boolean onMessageReceived(MMXMessage mmxMessage) {
Map<String, String> content = mmxMessage.getContent();
String messageType = content.get(KEY_MESSAGE_TYPE);
boolean handled = false;
if (mmxMessage.getChannel() == null) {
if (MessageType.LOCATION_REQUEST.name().equals(messageType)) {
EventLog.getInstance(mContext).add(EventLog.Type.INFO, "WRU: Location request received.");
LocationUpdaterService.updateLocation(mContext, mUsername, mJoinedTopic);
handled = true;
}
} else {
if (mJoinedTopicChat.getName().equalsIgnoreCase(mmxMessage.getChannel().getName())) {
//this is a chat message
if (!mmxMessage.getSender().equals(MMX.getCurrentUser())) {
MyMessageStore.addMessage(mmxMessage.getId(),
mmxMessage.getContent(), mmxMessage.getTimestamp(), true);
}
handled = true;
} else if (mJoinedTopic.getName().equalsIgnoreCase(mmxMessage.getChannel().getName())) {
if (MessageType.LOCATION_UPDATE.name().equals(messageType)) {
handlePayload(mmxMessage.getSender(), content);
handled = true;
}
}
}
if (!handled) {
Log.e(TAG, "onMessageReceived(): Unhandled message. channel=" + mmxMessage.getChannel() + ", type=" + messageType);
}
return false;
}
};
private WRU(Context context) {
mContext = context.getApplicationContext();
mDb = new WRUHelper().getWritableDatabase();
MMX.registerListener(mListener);
mSharedPrefs = mContext.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
mJoinedTopicKey = mSharedPrefs.getString(SHARED_PREF_KEY_JOINED_TOPIC_KEY, null);
mJoinedTopicPassphrase = mSharedPrefs.getString(SHARED_PREF_KEY_JOINED_TOPIC_PASSPHRASE, null);
mUsername = mSharedPrefs.getString(SHARED_PREF_KEY_JOINED_TOPIC_AS_USERNAME, null);
mJoinedTopicUpdateInterval = mSharedPrefs.getLong(SHARED_PREF_KEY_UPDATE_INTERVAL, -1l);
if (mJoinedTopicKey != null) {
loadLastLocations(encodeTopic(mJoinedTopicKey, mJoinedTopicPassphrase));
}
if (playServicesConnected(mContext)) {
mGoogleApiClient = new GoogleApiClient.Builder(mContext)
.addOnConnectionFailedListener(mGoogleConnectionFailedListener)
.addConnectionCallbacks(mGoogleConnectionCallbacks)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
registerMMXUser();
}
public static synchronized WRU getInstance(final Context context) {
if (sInstance == null) {
sInstance = new WRU(context);
}
sInstance.login();
return sInstance;
}
public synchronized String getUsername() {
return mUsername;
}
public synchronized MMXChannel getJoinedTopic() {
return mJoinedTopic;
}
public synchronized MMXChannel getJoinedTopicChat() {
return mJoinedTopicChat;
}
public synchronized String getJoinedTopicKey() {
return mJoinedTopicKey;
}
public synchronized String getJoinedTopicPassphrase() {
return mJoinedTopicPassphrase;
}
public String generateTopicKey() {
return Long.toString(System.currentTimeMillis(), 36);
}
public void createTopic(String passphrase, final MMXChannel.OnFinishedListener<String> listener) {
final String topicKey = generateTopicKey();
final String topicName = encodeTopic(topicKey, passphrase);
Log.d(TAG, "createTopic(): creating topic " + topicName);
MMXChannel.create(topicName, topicName, true, new MMXChannel.OnFinishedListener<MMXChannel>() {
public void onSuccess(MMXChannel mmxChannel) {
Log.d(TAG, "createTopic(): success");
//at this point it should already be subscribed
final String chatTopicName = topicName + SUFFIX_CHAT;
MMXChannel.create(chatTopicName, chatTopicName, true,
new MMXChannel.OnFinishedListener<MMXChannel>() {
public void onSuccess(MMXChannel mmxChannel) {
Log.d(TAG, "createTopic(): success creating chat topic");
//at this point it should already be subscribed
listener.onSuccess(topicKey);
}
@Override
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "createTopic(): failure creating chat topic: " + failureCode, throwable);
listener.onFailure(failureCode, throwable);
}
});
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "createTopic(): failure: " + failureCode, throwable);
listener.onFailure(failureCode, throwable);
}
});
}
public synchronized void joinTopic(final String topicKey, final String passphrase,
final String username, final long updateInterval,
final MMXChannel.OnFinishedListener<Void> listener) {
Log.d(TAG, "joinTopic(): topicKey: " + topicKey + ", passphrase: " + passphrase);
//clean up old subscriptions
MMXChannel.getAllSubscriptions(new MMXChannel.OnFinishedListener<List<MMXChannel>>() {
public void onSuccess(List<MMXChannel> mmxChannels) {
for (MMXChannel sub : mmxChannels) {
sub.unsubscribe(new MMXChannel.OnFinishedListener<Boolean>() {
public void onSuccess(Boolean aBoolean) {
Log.d(TAG, "joinTopic(): unsubscribe success: " + aBoolean);
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "joinTopic(): unsubscribe failure: " + failureCode, throwable);
}
});
}
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
}
});
final String topicName = encodeTopic(topicKey, passphrase);
MMXChannel.getPublicChannel(topicName, new MMXChannel.OnFinishedListener<MMXChannel>() {
public void onSuccess(MMXChannel mmxChannel) {
mJoinedTopic = mmxChannel;
mJoinedTopicKey = topicKey;
mJoinedTopicPassphrase = passphrase;
mUsername = username;
mSharedPrefs.edit()
.putString(SHARED_PREF_KEY_JOINED_TOPIC_KEY, topicKey)
.putString(SHARED_PREF_KEY_JOINED_TOPIC_PASSPHRASE, passphrase)
.putString(SHARED_PREF_KEY_JOINED_TOPIC_AS_USERNAME, username)
.putLong(SHARED_PREF_KEY_UPDATE_INTERVAL, updateInterval)
.apply();
mJoinedTopic.subscribe(new MMXChannel.OnFinishedListener<String>() {
public void onSuccess(String s) {
Log.d(TAG, "joinTopic(): successfully subscribed to group");
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "joinTopic(): unable to subscribe to group: " + failureCode, throwable);
}
});
LocationUpdaterService.startLocationUpdates(mContext, mUsername, mJoinedTopic, updateInterval);
final String chatTopicName = topicName + SUFFIX_CHAT;
MMXChannel.getPublicChannel(chatTopicName, new MMXChannel.OnFinishedListener<MMXChannel>() {
public void onSuccess(MMXChannel mmxChannel) {
mJoinedTopicChat = mmxChannel;
loadMessages(mJoinedTopicChat);
mJoinedTopicChat.subscribe(new MMXChannel.OnFinishedListener<String>() {
public void onSuccess(String s) {
Log.d(TAG, "joinTopic(): successfully subscribed to group chat");
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "joinTopic(): unable to subscribe to group chat: " + failureCode, throwable);
}
});
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
//for backwards compatibility
MMXChannel.create(chatTopicName, chatTopicName, true, new MMXChannel.OnFinishedListener<MMXChannel>() {
public void onSuccess(MMXChannel mmxChannel) {
mJoinedTopicChat = mmxChannel;
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "Unable to create chat topic: " + failureCode, throwable);
}
});
}
});
listener.onSuccess(null);
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
listener.onFailure(failureCode, throwable);
}
});
}
public synchronized void leaveTopic() {
if (mJoinedTopic != null) {
mSharedPrefs.edit()
.remove(SHARED_PREF_KEY_JOINED_TOPIC_KEY)
.remove(SHARED_PREF_KEY_JOINED_TOPIC_PASSPHRASE)
.apply();
LocationUpdaterService.stopLocationUpdates(mContext, mUsername, mJoinedTopic);
clearLastLocations(mJoinedTopic);
MyMessageStore.clear();
final MMXChannel tempTopic = mJoinedTopic;
mJoinedTopic = null;
mJoinedTopicChat = null;
mJoinedTopicKey = null;
mJoinedTopicPassphrase = null;
mLocations.clear();
tempTopic.unsubscribe(new MMXChannel.OnFinishedListener<Boolean>() {
public void onSuccess(Boolean aBoolean) {
Log.d(TAG, "leaveTopic(): success");
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.d(TAG, "leaveTopic(): failure: " + failureCode, throwable);
}
});
}
}
private synchronized String encodeTopic(String topicKey, String passphrase) {
byte[] digestedPassphrase = digest(passphrase + topicKey);
String encodedDigestedPassphrase = bytesToHex(digestedPassphrase);
return topicKey + '_' + encodedDigestedPassphrase;
}
private byte[] digest(String text) {
if (mDigester == null) {
try {
mDigester = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "encodeTopic(): caught exception", e);
}
}
byte[] topicBytes = text.getBytes();
mDigester.update(topicBytes, 0, topicBytes.length);
byte[] digest = mDigester.digest();
mDigester.reset();
return digest;
}
private enum MessageType {LOCATION_REQUEST, LOCATION_UPDATE}
private static final String KEY_MESSAGE_TYPE = "type";
private static final String KEY_USERNAME = "username";
private static final String KEY_LOCATION = "location";
private static final String KEY_TIMESTAMP = "timestamp";
public static class LocationTime {
public final String username;
public final GeoLoc location;
public final long locationTimestamp;
public final long timestamp;
private LocationTime(String username, GeoLoc location, long locationTimestamp, long timestamp) {
this.username = username;
this.location = location;
this.locationTimestamp = locationTimestamp;
this.timestamp = timestamp;
}
}
public boolean requestLocationUpdates() {
if (MMX.getCurrentUser() != null) {
final HashMap<String, String> content = new HashMap<>();
content.put(KEY_MESSAGE_TYPE, MessageType.LOCATION_REQUEST.name());
Log.d(TAG, "requestLocationUpdates(): requesting location updates from subscribers");
mJoinedTopic.getAllSubscribers(0, 100, new MMXChannel.OnFinishedListener<ListResult<MMXUser>>() {
public void onSuccess(ListResult<MMXUser> mmxUserListResult) {
HashSet<MMXUser> recipients = new HashSet<>(mmxUserListResult.items);
if (recipients.size() > 0) {
MMXMessage message = new MMXMessage.Builder()
.recipients(recipients)
.content(content)
.build();
message.send(new MMXMessage.OnFinishedListener<String>() {
public void onSuccess(String s) {
Log.d(TAG, "requestLocationUpdates(): success");
}
public void onFailure(MMXMessage.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "requestLocationUpdates(): failed: " + failureCode, throwable);
}
});
} else {
Log.d(TAG, "requestLocationUpdates(): no subscribers...");
}
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "requestLocationUpdates(): failed: " + failureCode, throwable);
}
});
return true;
}
return false;
}
public static Map<String, String> buildPayload(String username, GeoLoc geo, Date locationDate) {
HashMap<String, String> content = new HashMap<>();
content.put(KEY_MESSAGE_TYPE, MessageType.LOCATION_UPDATE.name());
content.put(KEY_USERNAME, username);
content.put(KEY_LOCATION, geo.toJson());
content.put(KEY_TIMESTAMP, String.valueOf(locationDate.getTime()));
return content;
}
private void handlePayload(MMXUser from, Map<String, String> content) {
String username = content.get(KEY_USERNAME);
String locationStr = content.get(KEY_LOCATION);
String locationTimeStr = content.get(KEY_TIMESTAMP);
if (locationStr == null || username == null || locationTimeStr == null) {
Log.w(TAG, "handlePayload(): unable to parse payload. ignoring");
return;
}
GeoLoc geo = GeoLoc.fromJson(locationStr);
long locationTime = Long.valueOf(locationTimeStr);
EventLog.getInstance(mContext).add(EventLog.Type.INFO, "WRU: Location update received from: " + username);
//put this in the map
synchronized (mLocations) {
long timestamp = System.currentTimeMillis();
LocationTime newLocationTime = new LocationTime(username, geo, locationTime, timestamp);
mLocations.put(username, newLocationTime);
saveLastLocation(mJoinedTopic, newLocationTime);
notifyLocationListeners(from, newLocationTime);
}
}
public ConcurrentHashMap<String, LocationTime> getLocationTimes() {
return mLocations;
}
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
private final ArrayList<OnLocationReceivedListener> mLocationListeners = new ArrayList<>();
public interface OnLocationReceivedListener {
void onLocationReceived(MMXUser user, LocationTime locationTime);
}
public void registerOnLocationReceivedListener(OnLocationReceivedListener listener) {
synchronized (mLocationListeners) {
for (OnLocationReceivedListener existingListener : mLocationListeners) {
if (existingListener == listener) {
return;
}
}
mLocationListeners.add(listener);
}
}
public void unregisterOnLocationReceivedListener(OnLocationReceivedListener listener) {
synchronized (mLocationListeners) {
mLocationListeners.remove(listener);
}
}
private void notifyLocationListeners(MMXUser user, LocationTime locationTime) {
synchronized (mLocationListeners) {
for (OnLocationReceivedListener listener : mLocationListeners) {
try {
listener.onLocationReceived(user, locationTime);
} catch (Exception ex) {
Log.e(TAG, "notifyLocationListeners(): caught exception, but continuing.", ex);
}
}
}
}
// ==================
// LOGIN HELPER STUFF
// ==================
private final AtomicBoolean mRegistered = new AtomicBoolean(false);
private synchronized void registerMMXUser() {
String username = getMMXUsername();
if (username == null) {
//register a new user
final String newUsername = generateString();
final String newPassword = generateString();
MMXUser user = new MMXUser.Builder().username(newUsername).build();
user.register(newPassword.getBytes(), new MMXUser.OnFinishedListener<Void>() {
public void onSuccess(Void aVoid) {
Log.d(TAG, "register() success: " + newUsername);
mRegistered.set(true);
mSharedPrefs.edit().putString(SHARED_PREF_KEY_MMX_USERNAME, newUsername).apply();
mSharedPrefs.edit().putString(SHARED_PREF_KEY_MMX_PASSWORD, newPassword).apply();
synchronized (mRegistered) {
mRegistered.notify();
}
}
public void onFailure(MMXUser.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "registration() failure: " + newUsername);
Toast.makeText(mContext, "Unable to register user", Toast.LENGTH_LONG).show();
synchronized (mRegistered) {
mRegistered.notify();
}
}
});
} else {
mRegistered.set(true);
synchronized (mRegistered) {
mRegistered.notify();
}
}
}
private synchronized void login() {
if (mLoggingIn.get() || mLoginSuccess.get()) {
//already logging in or logged in
return;
}
mLoggingIn.set(true);
if (!mRegistered.get()) {
synchronized (mRegistered) {
try {
mRegistered.wait(10000);
} catch (InterruptedException e) {
Log.e(TAG, "login(): caught exception", e);
}
}
}
if (!mRegistered.get()) {
Toast.makeText(mContext, "Unable to connect to server.", Toast.LENGTH_LONG).show();
return;
}
MMX.login(getMMXUsername(), getMMXPassword().getBytes(), new MMX.OnFinishedListener<Void>() {
public void onSuccess(Void aVoid) {
mLoggingIn.set(false);
mLoginSuccess.set(true);
MMX.start();
if (mJoinedTopicKey != null && mJoinedTopicPassphrase != null) {
final String topicName = encodeTopic(mJoinedTopicKey, mJoinedTopicPassphrase);
MMXChannel.getPublicChannel(topicName, new MMXChannel.OnFinishedListener<MMXChannel>() {
public void onSuccess(MMXChannel mmxChannel) {
mJoinedTopic = mmxChannel;
final String chatTopicName = topicName + SUFFIX_CHAT;
MMXChannel.getPublicChannel(chatTopicName, new MMXChannel.OnFinishedListener<MMXChannel>() {
public void onSuccess(MMXChannel mmxChannel) {
mJoinedTopicChat = mmxChannel;
loadMessages(mJoinedTopicChat);
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.d(TAG, "Unable to retrieve existing chat topic: " + failureCode, throwable);
//support older app which didn't have chat
MMXChannel.create(chatTopicName, chatTopicName, true, new MMXChannel.OnFinishedListener<MMXChannel>() {
public void onSuccess(MMXChannel mmxChannel) {
mJoinedTopicChat = mmxChannel;
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "Unable to create chat topic: " + failureCode, throwable);
}
});
}
});
requestLocationUpdates();
LocationUpdaterService.startLocationUpdates(mContext, mUsername, mJoinedTopic, mJoinedTopicUpdateInterval);
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "Unable to retrieve existing topic: " + failureCode, throwable);
}
});
}
}
public void onFailure(MMX.FailureCode failureCode, Throwable throwable) {
Log.e(TAG, "login(): failed: " + failureCode, throwable);
mLoggingIn.set(false);
mLoginSuccess.set(false);
Toast.makeText(mContext, "Unable to connect to server.", Toast.LENGTH_LONG).show();
}
});
}
public synchronized String getMMXUsername() {
return mSharedPrefs.getString(SHARED_PREF_KEY_MMX_USERNAME, null);
}
public synchronized String getMMXPassword() {
return mSharedPrefs.getString(SHARED_PREF_KEY_MMX_PASSWORD, null);
}
private synchronized String generateString() {
if (mRandom == null) {
mRandom = new Random();
}
return Long.toString(mRandom.nextLong(), 36) + '_' + Long.toString(System.currentTimeMillis(), 36);
}
// remember recent locations
private class WRUHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "WRU";
private static final int DB_VERSION = 1;
private static final String TABLE_HISTORY = "history";
private static final String COL_USERNAME = "username";
private static final String COL_TOPIC_NAME = "topic";
private static final String COL_LOCATION_TIME = "locationTime";
private static final String COL_TIMESTAMP = "timestamp";
private static final String COL_LOCATION = "location";
private static final String CREATE_HISTORY_TABLE =
"CREATE TABLE " + TABLE_HISTORY + " (" +
COL_USERNAME + " TEXT, " +
COL_TOPIC_NAME + " TEXT, " +
COL_TIMESTAMP + " NUM, " +
COL_LOCATION_TIME + " NUM, " +
COL_LOCATION + " TEXT)";
private WRUHelper() {
super(mContext, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_HISTORY_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
private void saveLastLocation(MMXChannel topic, LocationTime locationTime) {
Log.d(TAG, "saveLastLocation(): " + locationTime.username);
ContentValues values = new ContentValues();
values.put(WRUHelper.COL_LOCATION, locationTime.location.toJson());
values.put(WRUHelper.COL_LOCATION_TIME, locationTime.locationTimestamp);
values.put(WRUHelper.COL_TIMESTAMP, System.currentTimeMillis());
String where = WRUHelper.COL_USERNAME + "=? AND " + WRUHelper.COL_TOPIC_NAME + "=?";
String[] whereArgs = {locationTime.username, topic.getName().toLowerCase()};
int rows = mDb.update(WRUHelper.TABLE_HISTORY, values, where, whereArgs);
Log.d(TAG, "saveLastLocation(): rows updated=" + rows);
if (rows == 0) {
//do an insert instead
values.put(WRUHelper.COL_USERNAME, locationTime.username);
values.put(WRUHelper.COL_TOPIC_NAME, topic.getName());
Log.d(TAG, "saveLastLocation(): inserting new record for " + locationTime.username);
mDb.insert(WRUHelper.TABLE_HISTORY, "", values);
}
}
private void loadLastLocations(String topicName) {
Log.d(TAG, "loadLastLocations(): for topic: " + topicName);
String where = WRUHelper.COL_TOPIC_NAME + "=?";
String[] whereArgs = {topicName.toLowerCase()};
Cursor cursor = null;
try {
cursor = mDb.query(WRUHelper.TABLE_HISTORY, null, where, whereArgs, null, null, null);
int usernameIdx = cursor.getColumnIndex(WRUHelper.COL_USERNAME);
int locationIdx = cursor.getColumnIndex(WRUHelper.COL_LOCATION);
int locationTimeIdx = cursor.getColumnIndex(WRUHelper.COL_LOCATION_TIME);
int timestampIdx = cursor.getColumnIndex(WRUHelper.COL_TIMESTAMP);
synchronized (mLocations) {
while (cursor.moveToNext()) {
String username = cursor.getString(usernameIdx);
GeoLoc geo = GeoLoc.fromJson(cursor.getString(locationIdx));
long locationTimestamp = cursor.getLong(locationTimeIdx);
long timestamp = cursor.getLong(timestampIdx);
mLocations.put(username, new LocationTime(username, geo, locationTimestamp, timestamp));
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
private void loadMessages(MMXChannel topic) {
Log.d(TAG, "loadMessages(): for topic: " + topic.getName());
MyMessageStore.clear();
topic.getMessages(null, null, 0, 20, true, new MMXChannel.OnFinishedListener<ListResult<MMXMessage>>() {
public void onSuccess(ListResult<MMXMessage> mmxMessageListResult) {
Log.d(TAG, "loadMessages(): seeding existing messages");
MMXUser currentUser = MMX.getCurrentUser();
MyMessageStore.sSuppressNotification = true;
for (MMXMessage message : mmxMessageListResult.items) {
MyMessageStore.addMessage(message.getId(), message.getContent(), message.getTimestamp(),
!message.getSender().equals(currentUser));
}
MyMessageStore.sSuppressNotification = false;
}
public void onFailure(MMXChannel.FailureCode failureCode, Throwable throwable) {
Log.w(TAG, "loadMessages() failed: " + failureCode, throwable);
}
});
}
private void clearLastLocations(MMXChannel topic) {
String where = WRUHelper.COL_TOPIC_NAME + "=?";
String[] whereArgs = {topic.getName()};
int count = mDb.delete(WRUHelper.TABLE_HISTORY, where, whereArgs);
Log.d(TAG, "clearLastLocations: deleted " + count + " rows for topic: " + topic.getName());
}
public GoogleApiClient waitForGoogleApi() {
synchronized (mGoogleApiInitialized) {
if (mGoogleApiInitialized.get()) {
return mGoogleApiClient;
} else {
try {
mGoogleApiInitialized.wait();
} catch (InterruptedException e) {
}
if (mGoogleApiInitialized.get()) {
return mGoogleApiClient;
} else {
return null;
}
}
}
}
private static boolean playServicesConnected(Context context) {
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(context);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
Log.d(TAG, "playServicesConnected(): Google Play services is available.");
// Continue
return true;
// Google Play services was not available for some reason.
// resultCode holds the error code.
} else {
// log an error
Log.e(TAG, "playServicesConnected(): Google Play services is NOT AVAILABLE.");
return false;
}
}
}