We are implementing a chat in our android application. We have an userlist and of you click on one of the users, you start a chat with that user. We can send messages to the database with the sender and receiver and we can load these messages everytime we open that specific chat. We cannot, however, get the messages that are sent to the user from the database. This is how our Parse chat colums look like
This is our code for loading the conversation list:
private void loadConversationList()
{
final ParseQuery<ParseObject> q = ParseQuery.getQuery("Chat");
if (convList.size() == 0)
{
// load all messages...
ArrayList<String> al = new ArrayList<String>();
al.add(buddy);
al.add(UserList.user.getUsername());
q.whereContainedIn("sender", al);
q.whereContainedIn("receiver", al);
}
else
{
// load only newly received message..
if (li != null && li.size() > 0)
q.whereGreaterThan("createdAt", lastMsgDate);
q.whereEqualTo("sender", buddy);
q.whereEqualTo("receiver", UserList.user.getUsername());
}
q.orderByDescending("createdAt");
q.setLimit(30);
q.findInBackground(new FindCallback<ParseObject>()
{
#Override
public void done(List<ParseObject> li, ParseException e)
{
if (li != null && li.size() > 0)
{
for (int i = li.size() - 1; i >= 0; i--)
{
ParseObject po = li.get(i);
Conversation c = new Conversation(po
.getString("message"), po.getCreatedAt(), po.getString("sender"));
convList.add(c);
f = li.size();
if (lastMsgDate == null
|| lastMsgDate.before(c.getDate()))
lastMsgDate = c.getDate();
adp.notifyDataSetChanged();
}
}
handler.postDelayed(new Runnable() {
#Override
public void run()
{
if (isRunning)
loadConversationList();
}
}, 1000);
}
});
}
The conversation list is used to fill the screen with te messages. In SendMessage we also update the conversation list, only with the send messages of course. The problem is that the conversation list doesn't update the received messages
Related
I have an AsyncTask that sends data to a server. There is 1 call for each record in an SQL database. Once the HTTP call is competed, that record is marked as "uploaded". And I need to do this for all records that are not marked as "uploaded".
public void sync (final Context context, final boolean manualSync, final boolean rosterOnly, final String type) {
if ( unsentScansObjects == null ) {
Log.d(TAG, "sync: Loading Unsent Scans from Database...");
unsentScansObjects = unsentScans();
}
if ( unsentScansObjects.size() != 0 ) {
isUploading = true;
final ScanModel model = (ScanModel) unsentScansObjects.get(0);
UploadTask upload = (UploadTask) new UploadTask(mContext);
upload.completionBlock = new PPCompletionBlock() {
#Override
public void onCompletion(Boolean success, JSONObject object, String error) {
// Scan was marked as "uploaded", remove it from the array
unsentScansObjects.remove(model);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Keep calling sync until unsentScansObjects.size() == 0
sync(context, manualSync, rosterOnly, type);
}
},500);
}
};
upload.execute(model);
}
else {
isUploading = false;
unsentScansObjects = null;
}
}
Is there a more efficient way to upload all scans until there are none left?
I have tried a do { upload(); } while (unsentScansObjects.size() != 0);, but I need to wait until each http call and scan marked as "uploaded" is completed before moving on to the next.
I will note that this method works very well, but it was requested by client to have this as an "EOF", 1 call (not looping) type of function.
In my application I used Socket.io and I want when receive listener from socket I should update UI.
I write below codes, but not update my UI!
For test this i write Log.e when receive socket listener, show this Log, but not update UI!
My Codes:
#Override
public void onSocketUpdateBid(final JSONObject ob) {
Log.e("updateBidCount", "Receive");
super.onSocketUpdateBid(ob);
final int auction_id;
final String remainClick;
final JSONObject data = ob;
try {
UpdateBidResponse updateBidResponse = new Gson().fromJson(data.toString(), UpdateBidResponse.class);
auction_id = updateBidResponse.getAuction_id();
remainClick = updateBidResponse.getRemain_click();
Constants.currentActivity.runOnUiThread(() -> {
if (detail.getId() != null) {
Log.e("RemainClickLog", "" + remainClick);
if (detail.getId().equals(auction_id)) {
if (Integer.parseInt(remainClick) > 0) {
Log.e("updateBidLog", "RemainClick : " + remainClick);
auctionDetail_miniBottomBidTxt.setText(remainClick);
auctionDetail_footerPlusBidBtn.setAlpha(1.0f);
auctionDetail_footerPlusBidBtn.setEnabled(true);
auctionDetail_footerPlusBidBtn.setClickable(true);
Log.e("updateBidLog", "Enable");
auctionDetail_footerBottom.setEnabled(true);
auctionDetail_footerBottom.setClickable(true);
auctionDetail_footerBottom.setAlpha(1.0f);
isShowSendBid = true;
} else if (Integer.parseInt(remainClick) == 0) {
Log.e("updateBidLog", "Disable");
auctionDetail_footerBottom.setEnabled(false);
auctionDetail_footerBottom.setClickable(false);
auctionDetail_footerBottom.setAlpha(0.4f);
auctionDetail_miniBottomBidTxt.setText("0");
isNotEndBids = true;
isShowSendBid = false;
} else {
Log.e("updateBidLog", "Disable");
auctionDetail_footerBottom.setEnabled(false);
auctionDetail_footerBottom.setClickable(false);
auctionDetail_footerBottom.setAlpha(0.4f);
auctionDetail_miniBottomBidTxt.setText("0");
isNotEndBids = true;
isShowSendBid = false;
}
}
}
});
} catch (Exception e) {
Log.e("updateBidCount", "Err : " + e.getMessage());
}
}
When run application and receive socket listener i show Log.e("updateBidLog", "Disable"); into logcat tab.
But not run this code :
auctionDetail_footerBottom.setEnabled(false);
auctionDetail_footerBottom.setClickable(false);
auctionDetail_footerBottom.setAlpha(0.4f);
auctionDetail_miniBottomBidTxt.setText("0");
isNotEndBids = true;
isShowSendBid = false;
and not update UI !
Update : I used this Constants.currentActivity.runOnUiThread for update UI. but not update UI!
How can i fix it?
I am building an android email client app. I managed to display the emails headers/senders in a RecylerView. Next, I want to start a new activity that will display the email content/attachments when the user chooses a specific mail.
I have a hard time doing this through intents. I am able to get the contents of the email (text, inline images, attachments) but I can't figure out a way to display them as close to the original format as possible. I thought of putting the text in a StringBuilder and sending it through an intent in order to display the text, but this way I can't display the inline images in the right place and there are also formatting problems.
Any kind of guidance towards the way I should approach this is much appreciated.
The class that displays the list of the available mails and gets the content of the specific mail to send it another activity for displaying it. I know the code is a little hazardous, I tried many approaches and it is far from the final form.
public class CheckMail extends Activity {
static List<Message> messages = new ArrayList<>();
String[] sender;
String[] date;
String[] subject;
boolean[] seen;
Context context = null;
ListView listView;
Intent intent;
Store store;
StringBuilder content = new StringBuilder();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_check_mail);
if (android.os.Build.VERSION.SDK_INT > 9)
{
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
context = this;
ReadEmails task = new ReadEmails();
task.execute();
}
public void writePart(Part p) throws Exception {
if (p instanceof Message)
this.writeEnvelope((Message) p);
//check if the content is plain text
if (p.isMimeType("text/plain")) {
content.append(p.getContent().toString());
}
//check if the content has attachment
else if (p.isMimeType("multipart/*")) {
System.out.println("This is a Multipart");
System.out.println("---------------------------");
Multipart mp = (Multipart) p.getContent();
int count = mp.getCount();
for (int i = 0; i < count; i++)
writePart(mp.getBodyPart(i));
}
//check if the content is a nested message
else if (p.isMimeType("message/rfc822")) {
System.out.println("This is a Nested Message");
System.out.println("---------------------------");
writePart((Part) p.getContent());
}
/*
//check if the content is an inline image
else if (p.isMimeType("image/jpeg")) {
System.out.println("--------> image/jpeg");
Object o = p.getContent();
InputStream x = (InputStream) o;
// Construct the required byte array
System.out.println("x.length = " + x.available());
while ((i = (int) ((InputStream) x).available()) > 0) {
int result = (int) (((InputStream) x).read(bArray));
if (result == -1)
int i = 0;
byte[] bArray = new byte[x.available()];
break;
}
FileOutputStream f2 = new FileOutputStream("/tmp/image.jpg");
f2.write(bArray);
}
else if (p.getContentType().contains("image/")) {
System.out.println("content type" + p.getContentType());
File f = new File("image" + new Date().getTime() + ".jpg");
DataOutputStream output = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream(f)));
com.sun.mail.util.BASE64DecoderStream test =
(com.sun.mail.util.BASE64DecoderStream) p
.getContent();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = test.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
}
else {
Object o = p.getContent();
if (o instanceof String) {
System.out.println("This is a string");
System.out.println("---------------------------");
System.out.println((String) o);
}
else if (o instanceof InputStream) {
System.out.println("This is just an input stream");
System.out.println("---------------------------");
InputStream is = (InputStream) o;
is = (InputStream) o;
int c;
while ((c = is.read()) != -1)
System.out.write(c);
}
else {
System.out.println("This is an unknown type");
System.out.println("---------------------------");
System.out.println(o.toString());
}
}
*/
}
public void writeEnvelope(Message m) throws Exception {
System.out.println("This is the message envelope");
System.out.println("---------------------------");
Address[] a;
StringBuilder sender = new StringBuilder();
StringBuilder recipients = new StringBuilder();
String subject = "";
// FROM
if ((a = m.getFrom()) != null) {
for (int j = 0; j < a.length; j++)
sender.append(a[j].toString());
}
// TO
if ((a = m.getRecipients(Message.RecipientType.TO)) != null) {
for (int j = 0; j < a.length; j++)
recipients.append(a[j].toString());
}
// SUBJECT
if (m.getSubject() != null)
subject = m.getSubject();
intent.putExtra("Sender", sender.toString());
intent.putExtra("Recipients", recipients.toString());
intent.putExtra("Message", subject);
intent.putExtra("Date", m.getReceivedDate().toString());
}
class ReadEmails extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
// Create all the needed properties - empty!
Properties connectionProperties = new Properties();
// Create the session
Session session = Session.getDefaultInstance(connectionProperties, null);
try {
System.out.print("Connecting to the IMAP server...");
// Connecting to the server
// Set the store depending on the parameter flag value
store = session.getStore("imaps");
// Set the server depending on the parameter flag value
String server = "imap.gmail.com";
store.connect(server, "....#gmail.com", "password");
System.out.println("done!");
// Get the Inbox folder
Folder inbox = store.getFolder("Inbox");
// Set the mode to the read-only mode
inbox.open(Folder.READ_ONLY);
// Get messages
CheckMail.messages = Arrays.asList(inbox.getMessages());
System.out.println("Reading messages...");
sender = new String[messages.size()];
date = new String[messages.size()];
subject = new String[messages.size()];
seen = new boolean[messages.size()];
for (int i = 0; i < messages.size(); i++) {
try {
Address[] froms = messages.get(i).getFrom();
String email = froms == null ? null : ((InternetAddress) froms[0]).getAddress();
sender[i] = email;
date[i] = messages.get(i).getReceivedDate().toString();
subject[i] = messages.get(i).getSubject();
Flags flags = messages.get(i).getFlags();
Flags.Flag[] sf = flags.getSystemFlags();
for (int j = 0; j < sf.length; j++) {
if (sf[j] == Flags.Flag.SEEN)
seen[i] = true;
else
seen[i] = false;
}
} catch (MessagingException e) {
e.printStackTrace();
}
}
System.out.println("Done reading...");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(String result) {
CustomListAdapter whatever = new CustomListAdapter((Activity) context, sender, date, subject, seen);
listView = (ListView) findViewById(R.id.listviewID);
listView.setAdapter(whatever);
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
try {
content.delete(0, content.length());
intent = new Intent(context, OpenMail.class);
writePart(messages.get(position));
intent.putExtra("Content", content.toString());
startActivity(intent);
}
catch (Exception e)
{e.printStackTrace();}
}
});
}
}
}
Since no one answered...I display emails via Javamail in some of my apps.
I think you're on the right track, having separate activities for the list and viewer is a good approach. [Or separate fragments because on a tablet you may want to display the list and the body on the same screen, side by side]
Couple issues that might come up:
I would be cautious about putting the email content in an extra to start the activity and/or committing it to saved instance state in the viewer activity because there are size limits [For example, 1MB for saved instance state on Android 7+]
Downloading the email in a ASyncTask in the activity might not be the best approach. I don't know the full purpose of the app, but I assume that is something that should succeed whether the user waits or not? The ASyncTask will continue to run if they task away, but it will hold on to the activity context causing a so called 'temporary memory leak'. It is probably best to put it in a service and download it in a separate thread. However, doing it in the activity as a proof of concept is perfectly reasonable...
I don't think that walking the message structure in the email list activity is the best approach. In my apps, I download email in a background service and commit the data to an SQL-DB via a ContentProvider. On the message viewer screen the email body is retrieved from the ContentProvider/SQL-DB using a component called the CursorLoader. It handles all the loading in the background so that the UI remains responsive whilst loading large mails. But in any event, I avoid passing the message body between activities.
A lot of emails have HTML parts (multipart/alternative: text/plain & text/html) so the viewer was implemented as a WebView. The WebView produced good looking emails with minimal effort.
Couple miscellaneous gotchas, when retrieving the mail, take care to call setPeek(true). It will stop the the READ flag being set. It is not an issue for GMail, but some IMAP servers will set this flag. Users will complain if any app other than their primary email app changes the READ flag. Also don't assume that any of the headers are present, SPAM emails are notorious for leaving out the message ID, subject and other fields. Finally, it might be worth considering implementing authentication via OAuth2 which will enable your app to connect to a user's GMail account via Javamail without needing their password.
I'm not sure that any of that really helps because it is a pretty big job, but one step at a time...Cheers!
Fairly new to Android/Java development and using the Open Source Parseplatform as my backend server. I've created a class to manage a parse object and this object update's its data from an async call to the parse server as per this code.
public class DeviceObject {
private String objectID, deviceName, status;
private ParseGeoPoint location;
int batLevel;
public DeviceObject(){
objectID = null;
deviceName = null;
location = null;
batLevel = 0;
status = null;
}
public void getDeviceLatestData() {
if (objectID != null) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("DeviceData");
query.whereEqualTo("DeviceObjectID", objectID);
query.orderByDescending("createdAt");
query.setLimit(1);
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> ParseDeviceList, ParseException e) {
if (e == null) {
if (ParseDeviceList.size() == 0) {
Log.d("debg", "Device not found");
} else {
for (ParseObject ParseDevice : ParseDeviceList) {
status = ParseDevice.getString("Status");
batLevel = ParseDevice.getInt("BatteryLevel");
location = ParseDevice.getParseGeoPoint("Location");
Log.d("debg", "Retrieving: " + deviceName);
Log.d("debg", "Status: " + status + " Battery: " + Integer.toString(batLevel));
}
//callback listener to add marker to map
}
} else {
Log.d("debg", "Error: " + e.getMessage());
}
}
});
}
}
So I create my class object in my Main Activity with the following:
DeviceObject userDevice = new DeviceObject();
userDevice.getDeviceLatestData();
What I can't get my head around is how in my MainActivity I can get notified/callback to continue displaying the information which the userDevice class just got off the parse Server.
I've tried creating an interface and adding a listener as what i've seen suggested however I could not add the listener inside the parse's done function.
The definition of my main activity is, note I need the OnMapReadyCallback as i'm using Google Maps
public class MapMainActivity extends AppCompatActivity implements OnMapReadyCallback {
So in summary i'd like to add something to the main activity so that I can process the data when it has been added to the class from the async call.
For something like this, I recommend using an event bus. Here is a link to a popular one I've had success with in the past.
Basically, you will have another class involved, which will be your bus. Your activity will register for a specific event (which you will create, subclassing as appropriate). Your async call will tell the event bus to fire off that event, and the bus will then tell all subscribers, including your main activity, that the event fired off. That is when you'd call getDeviceLatestData. Below are simple code snippets you may use, but read the documentation on that bus to fully understand it.
Your event:
public static class DataReady Event { /* optional properties */ }
Your DeviceObject:
public class DeviceObject {
private String objectID, deviceName, status;
private ParseGeoPoint location;
int batLevel;
public DeviceObject(){
objectID = null;
deviceName = null;
location = null;
batLevel = 0;
status = null;
}
public void getDeviceLatestData() {
if (objectID != null) {
ParseQuery<ParseObject> query = ParseQuery.getQuery("DeviceData");
query.whereEqualTo("DeviceObjectID", objectID);
query.orderByDescending("createdAt");
query.setLimit(1);
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> ParseDeviceList, ParseException e) {
if (e == null) {
if (ParseDeviceList.size() == 0) {
Log.d("debg", "Device not found");
} else {
for (ParseObject ParseDevice : ParseDeviceList) {
status = ParseDevice.getString("Status");
batLevel = ParseDevice.getInt("BatteryLevel");
location = ParseDevice.getParseGeoPoint("Location");
Log.d("debg", "Retrieving: " + deviceName);
Log.d("debg", "Status: " + status + " Battery: " + Integer.toString(batLevel));
}
//callback listener to add marker to map
EventBus.getDefault().post(new DataReadyEvent());
}
} else {
Log.d("debg", "Error: " + e.getMessage());
}
}
});
}
}
Your MainActivity:
public class MainActivity {
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
#Subscribe(threadMode = ThreadMode.MAIN) // Seems like you're updating UI, so use the main thread
public void onDataReady(DataReadyEvent event) {
/* Do whatever it is you need to do - remember you can add properties to your event and pull them off here if you need to*/
};
}
I have this program that has this method.
public List<Message> getMessages(int start, int end, Date earliestDate) throws MessagingException {
Using the Java EWS api how could I get the messages from a folder using these variables. For instance if I wanted to get the 50th-70th message in the inbox or I want to get messages 10-20 starting on a specific date. The date can be null it doesn't really matter to much.
I doubt this is the most efficient method possible but oh well.
public List<Message> getMessages(int start, int end, Date earliestDate) {
if (start < 1 || end < 1 || end < start) {
throw new MessagingException(String.format(Locale.US, "Invalid message set %d %d",
start, end));
}
int length = end - start;
ItemView view = new ItemView(length);
FindItemsResults<Item> findresults = null;
try {
if (earliestDate == null) {
findresults = mService.findItems(mFolder.getId(), view);
}
else{
SearchFilter filter = new SearchFilter.IsGreaterThanOrEqualTo(ItemSchema.DateTimeReceived, earliestDate);
findresults = mService.findItems(mFolder.getId(), filter,view);
}
} catch (Exception e) {
e.printStackTrace();
}
List<Message> messages = new ArrayList<Message>();
int i = 0;
for (Item item : findresults) {
ItemId id = null;
EmailMessage message = null;
try {
id = item.getId();
message = new EmailMessage(mService);
message.bind(mService, id);
} catch (ServiceLocalException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
messages.add(message);
}
return messages;
}