/* Copyright (c) 2015 Magnet Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.magnet.demo.mmx.starter;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.media.RingtoneManager;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.magnet.max.android.User;
import com.magnet.max.android.ApiCallback;
import com.magnet.max.android.ApiError;
import com.magnet.max.android.auth.model.UserRegistrationInfo;
import com.magnet.max.android.Max;
import com.magnet.mmx.client.api.MMXMessage;
import com.magnet.mmx.client.api.MMX;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* This is the primary activity for this application. It establishes a
* connection to the MMX server, registers a global listener
* <code>MyMMXListener</code> to receive incoming messages from this connection,
* and shows received messages in a List view. The global listener
* <code>MyMMXListener</code> extending from AbstractMMXListener automatically
* dispatches the incoming messages to this activity registered as a local
* listener. It also saves the messages to an in-memory
* <code>MyMessageStore</code> and posts a notification to the Status Bar in
* case this activity is not available to show the incoming message. Furthermore,
* user can also send a message to self or one of the two bots from this
* activity.
*/
public class MyActivity extends Activity {
static final String QUICKSTART_USERNAME = "QuickstartUser1";
static final byte[] QUICKSTART_PASSWORD = "QuickstartUser1".getBytes();
static final String KEY_MESSAGE_TEXT = "textContent";
private static final String TAG = MyActivity.class.getSimpleName();
private static final String[] TO_LIST = {QUICKSTART_USERNAME, "amazing_bot", "echo_bot"};
private static final User[] TO_USERS = {null, null, null};
private TextView mStatus = null;
private ImageButton mSendButton = null;
private EditText mSendText = null;
private ListView mMessageListView = null;
private MessageListAdapter mMessageListAdapter = null;
private User mToUser = null;
private int mNoteId = 0;
private MMX.EventListener mEventListener =
new MMX.EventListener() {
public boolean onMessageReceived(MMXMessage mmxMessage) {
MyMessageStore.addMessage(mmxMessage, null,
new Date(), true);
doNotify(mmxMessage);
updateViewState();
return false;
}
@Override
public boolean onMessageAcknowledgementReceived(User user, String messageId) {
return false;
}
@Override
public boolean onLoginRequired(MMX.LoginReason reason) {
Log.d(TAG, "onLoginRequired() reason="+reason);
updateViewState();
return false;
}
};
/**
* On creating this activity, register this activity as a local listener to
* the global listener, establish a connection to the MMX server and register
* the global listener to the connection.
* @see #onDestroy()
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MMX.registerListener(mEventListener);
// Register this activity as a listener to receive and show incoming
// messages. See #onDestroy for the unregister call.
User.register(new UserRegistrationInfo.Builder()
.userName(QUICKSTART_USERNAME)
.firstName(QUICKSTART_USERNAME)
.password(new String(QUICKSTART_PASSWORD))
.build(), new ApiCallback<User>() {
@Override
public void success(User user) {
Log.d(TAG, "register user succeeded");
loginHelper();
}
@Override
public void failure(ApiError apiError) {
Log.d(TAG, "register user failed because: " + apiError);
loginHelper();
}
});
setContentView(R.layout.activity_my_activity);
//Setup the views
mStatus = (TextView) findViewById(R.id.status_field);
mSendText = (EditText) findViewById(R.id.message_text);
mSendButton = (ImageButton) findViewById(R.id.btn_send);
mMessageListView = (ListView) findViewById(R.id.message_list_view);
mMessageListAdapter = new MessageListAdapter(this, MyMessageStore.getMessageList(), QUICKSTART_USERNAME);
mMessageListView.setAdapter(mMessageListAdapter);
mSendText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (event != null
&& event.getKeyCode() == KeyEvent.KEYCODE_ENTER
|| actionId == EditorInfo.IME_ACTION_DONE) {
doSendMessage(v);
}
return false;
}
});
updateViewState();
}
private void loginHelper() {
User.login(QUICKSTART_USERNAME, new String(QUICKSTART_PASSWORD), false, new ApiCallback<Boolean>() {
public void success(Boolean aBoolean) {
Log.d(TAG, "login(): success! boolean=" + aBoolean);
Max.initModule(MMX.getModule(), new ApiCallback<Boolean>() {
public void success(Boolean aBoolean) {
MMX.start();
loadUsers();
mToUser = MMX.getCurrentUser();
}
public void failure(ApiError apiError) {
Toast.makeText(MyActivity.this, "Unable to initialize MMX: " + apiError, Toast.LENGTH_LONG).show();
}
});
updateViewState();
}
public void failure(ApiError apiError) {
Log.d(TAG, "login(): failure! error=" + apiError);
updateViewState();
}
});
}
/**
* On destroying of this activity, unregister this activity as a listener
* so it won't process any incoming messages.
*/
@Override
public void onDestroy() {
MMX.unregisterListener(mEventListener);
super.onDestroy();
}
/**
* On resuming of this activity, just update the view state.
*/
@Override
public void onResume() {
updateViewState();
super.onResume();
}
/**
* This can be called from anywhere to make sure that the view is updated.
*/
private void updateViewState() {
runOnUiThread(new Runnable() {
public void run() {
User user = User.getCurrentUser();
if (user != null) {
String username = user.getUserName();
String status = getString(R.string.status_authenticated) +
(username != null ? " as " + username : " " + getString(R.string.user_anonymously));
mStatus.setText(status);
mSendButton.setEnabled(true);
} else {
mStatus.setText(R.string.status_unavailable);
mSendButton.setEnabled(false);
}
List<MyMessageStore.Message> messageList = MyMessageStore.getMessageList();
mMessageListAdapter.setMessageList(messageList);
mMessageListView.smoothScrollToPosition(mMessageListAdapter.getCount());
}
});
}
private static class MessageListAdapter extends BaseAdapter {
private static final int[] COLOR_IDS = {R.color.chat_1, R.color.chat_2, R.color.chat_3, R.color.chat_4, R.color.chat_5, R.color.chat_6};
private static final int TYPE_ME = 0;
private static final int TYPE_THEM = 1;
private String mUsername;
private List<MyMessageStore.Message> mMessageList = null;
private LayoutInflater mInflater;
private DateFormat mFormatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT);
public MessageListAdapter(Context context, List<MyMessageStore.Message> messageList, String username) {
mUsername = username;
mMessageList = messageList;
mInflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);
}
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
int colorResId = 0;
String datePostedStr = null;
String messageStr = null;
MyMessageStore.Message message = (MyMessageStore.Message)getItem(position);
switch (type) {
case TYPE_ME:
if (convertView == null) {
convertView = mInflater.inflate(R.layout.message_list_item_me, null);
}
colorResId = R.color.chat_me;
datePostedStr = mFormatter.format(message.getTimestamp());
messageStr = message.getSentText();
break;
case TYPE_THEM:
if (convertView == null) {
convertView = mInflater.inflate(R.layout.message_list_item_them, null);
}
//set author and color
MMXMessage msg = message.getMessage();
String authorStr = msg.getSender().getUserName();
for (int i=TO_LIST.length; --i >= 0;) {
if (TO_LIST[i].equalsIgnoreCase(authorStr)) {
colorResId = COLOR_IDS[i];
break;
}
}
if (colorResId == 0) {
colorResId = COLOR_IDS[Math.abs(authorStr.hashCode() % COLOR_IDS.length)];
}
TextView author = (TextView) convertView.findViewById(R.id.author);
author.setText(authorStr + " - ");
datePostedStr = mFormatter.format(msg.getTimestamp());
Object textObj = msg.getContent().get(KEY_MESSAGE_TEXT);
messageStr = textObj != null ? textObj.toString() : "<no text>";
break;
}
TextView datePosted = (TextView) convertView.findViewById(R.id.datePosted);
datePosted.setText(datePostedStr);
TextView messageText = (TextView) convertView.findViewById(R.id.messageText);
messageText.setBackgroundResource(colorResId);
messageText.setText(messageStr);
return convertView;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
MyMessageStore.Message message = (MyMessageStore.Message)getItem(position);
if (!message.isIncoming()) {
//me
return TYPE_ME;
} else {
//them
return TYPE_THEM;
}
}
private void setMessageList(List<MyMessageStore.Message> messageList) {
mMessageList = messageList;
notifyDataSetChanged();
}
@Override
public int getCount() {
return mMessageList.size();
}
@Override
public Object getItem(int position) {
return mMessageList.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
}
public void doSendMessage(View view) {
if (mToUser == null) {
Toast.makeText(this, "Unable to send. Could not resolve user.", Toast.LENGTH_LONG).show();
return;
}
String messageText = mSendText.getText().toString();
if (messageText.isEmpty()) {
//don't send an empty message
return;
}
HashMap<String, String> content = new HashMap<String, String>();
content.put(KEY_MESSAGE_TEXT, messageText);
HashSet<User> recipients = new HashSet<User>();
recipients.add(mToUser);
String messageID = new MMXMessage.Builder()
.content(content)
.recipients(recipients)
.build()
.send(new MMXMessage.OnFinishedListener<String>() {
public void onSuccess(String s) {
Toast.makeText(MyActivity.this, "Message sent.", Toast.LENGTH_LONG).show();
updateViewState();
}
public void onFailure(MMXMessage.FailureCode failureCode, final Throwable e) {
Log.e(TAG, "doSendMessage() failure: " + failureCode, e);
Toast.makeText(MyActivity.this, "Exception: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
MyMessageStore.addMessage(null, messageText, new Date(), false);
mSendText.setText(null);
}
public void showToDialog(View view) {
AlertDialog toDialog = new AlertDialog.Builder(this, android.R.style.Theme_DeviceDefault_Light_Dialog)
.setTitle(R.string.title_send_to)
.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
android.R.id.text1, TO_LIST),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mToUser = TO_USERS[which];
}
})
.create();
toDialog.show();
}
private void loadUsers() {
User.getUsersByUserNames(Arrays.asList(TO_LIST), new ApiCallback<List<User>>() {
public void success(List<User> users) {
Log.d(TAG, "loadUsers() successfully loaded " + users.size() + " users.");
HashMap<String, User> userMap = new HashMap<String, User>();
for (User user : users) {
userMap.put(user.getUserName().toLowerCase(), user);
}
for (int i = TO_LIST.length; --i >= 0; ) {
TO_USERS[i] = userMap.get(TO_LIST[i].toLowerCase());
}
}
public void failure(ApiError apiError) {
Log.e(TAG, "loadUsers() failed: " + apiError.getMessage(), apiError.getCause());
}
});
}
private void doNotify(com.magnet.mmx.client.api.MMXMessage message) {
Object textObj = message.getContent().get(MyActivity.KEY_MESSAGE_TEXT);
if (textObj != null) {
String messageText = textObj.toString();
User from = message.getSender();
NotificationManager noteMgr = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Notification note = new Notification.Builder(this).setAutoCancel(true)
.setSmallIcon(R.drawable.ic_launcher).setWhen(System.currentTimeMillis())
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setContentTitle("Message from " + from.getUserName()).setContentText(messageText).build();
noteMgr.notify(mNoteId++, note);
}
}
}