I would know one thing about SMS...
I know that is possible read SMS in Android, but I don't know how I can read "Unread SMS", and in particular how to accede to each field like "sender", "SMS", "date", "time", "text".
What that i must do is: when a sms is received, an application "reads" the message and stores all the information in a data structure.
IMPORTANT:
Another question: is it possible read "Unread SMS" in WhatsApp or other IM application (for example facebook messenger) ?
And in this case how I can accede to each field ?
Regards
Reading Phone SMS
Check this out How can I read SMS messages from the device programmatically in Android?
Reading Facebook messages
For facebook you need to implement their API. See this Read Messages in Facebook
Reading Whatsapp messages
Option 1
Whatsapp did not publish any official APIs.
There's this open source API for communicating with whatsapp, it's not official and might stop working if Whatsapp update their protocols.
https://github.com/venomous0x/WhatsAPI
Regarding the legality of using this or other non-official API, it depends on the service agreement that you agreed to with Whatsapp. Read it and see if they frown upon using their communication protocols with clients other than theirs. My guess would be they do not allow it.
Option 2
WhatsApp makes a chat backup everyday at 4 AM on your SD Card. This is a single database encrypted with an AES key. Since it is on the external storage, your app can read it if you have access to the external storage.
You can easily decrypt this database (there is a paper available on this online).
However, this will only give you updated chats once every 24 hours.
If you want more realtime updates, your device has to be rooted, and you will need a completely new parser, as on the internal storage the chats are stored in more than one database.
Option 3
Read this SO question Get all messages from Whatsapp
NOTE: I am not sure about the Whatsapp stuff. It's just a compilation of various posts together.
The easiest function
To read the sms I wrote a function that returns a Conversation object:
class Conversation(val number: String, val message: List<Message>)
class Message(val number: String, val body: String, val date: Date)
fun getSmsConversation(context: Context, number: String? = null, completion: (conversations: List<Conversation>?) -> Unit) {
val cursor = context.contentResolver.query(Telephony.Sms.CONTENT_URI, null, null, null, null)
val numbers = ArrayList<String>()
val messages = ArrayList<Message>()
var results = ArrayList<Conversation>()
while (cursor != null && cursor.moveToNext()) {
val smsDate = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.DATE))
val number = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS))
val body = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.BODY))
numbers.add(number)
messages.add(Message(number, body, Date(smsDate.toLong())))
}
cursor?.close()
numbers.forEach { number ->
if (results.find { it.number == number } == null) {
val msg = messages.filter { it.number == number }
results.add(Conversation(number = number, message = msg))
}
}
if (number != null) {
results = results.filter { it.number == number } as ArrayList<Conversation>
}
completion(results)
}
Using:
getSmsConversation(this){ conversations ->
conversations.forEach { conversation ->
println("Number: ${conversation.number}")
println("Message One: ${conversation.message[0].body}")
println("Message Two: ${conversation.message[1].body}")
}
}
Or get only conversation of specific number:
getSmsConversation(this, "+33666494128"){ conversations ->
conversations.forEach { conversation ->
println("Number: ${conversation.number}")
println("Message One: ${conversation.message[0].body}")
println("Message Two: ${conversation.message[1].body}")
}
}
Related
I am using Realm MongoDB for my android app, and I have a problem:
I have different users in my app, and each user has his "cards". The partition of each user's cards is:
"Card=userID".
So, I want to be able to send a card from one user to the other. I do it via a link that includes userID and specific cardID.
So my code looks something like:
Realm.init(this);
mainApp = new App(new AppConfiguration.Builder(APP_ID).defaultSyncErrorHandler((session, error) ->
Log.e("TAG()", "Sync error: ${error.errorMessage}")
).build());
//TEMP CODE
String partition = "Card=611d7n582w36796ce34af106"; //test partition of another user
if(mainApp.currentUser() != null) {
SyncConfiguration config = new SyncConfiguration.Builder(
mainApp.currentUser(),
partition)
.build();
Realm realmLinkCard = Realm.getInstance(config);
Log.d(TAG, "onCreate: cards found- " + realmLinkCard.where(Card.class).findAll().size());
}
The last log always shows 0. I know there are cards for sure because if the user that created the corresponding partition is signed in then it does find the cards.
permissions are set to true for both read and write for the whole sync.
What can the problem be?
You cannot access a Realm by a user who has a different partition.
Instead you can create a mongodb function and call it from your user.
Make your function here:
Check here on How to create a function
And call it by checking here on How to call a function from client
Quick example of a realm function:
exports = async function funcName(partition) {
const cluster = context.services.get('myclustername');
const mycollection = cluster.db('mydbname').collection('mycollectionname');
let result = [];
try {
result = mycollection.findOne({
_partition: partition,
});
} catch (e) {
result.push(e);
return result;
}
return result;
};
To call it, please see above for the documentation as I'm not an Android developper.
override fun Mesibo_onMessage(p0: Mesibo.MessageParams?, p1: ByteArray?): Boolean {
if (userManager.isLogged()) {
Timber.d(" on message : %s", p0?.profile?.name)
Timber.d(" on message : %s", p0?.profile?.address)
p0?.profile?.name?.let { sendNotification(it, String(p1!!, StandardCharsets.UTF_8), applicationContext) }
}
return true
}
**I'm uisng this code to show notification after receiving messages, I don't know why the profile name return the address of user not his name
**
I'm using this code to setup mesibo and setting my profile and it did not work also give me user address in message params profile name
private fun setupMesibo() {
val user = UserManager.getInstance().getUser()
if (UserManager.getInstance().isLogged()) {
Mesibo.getInstance().init(this)
Mesibo.addListener(this)
Mesibo.setRestartListener(this)
Mesibo.setSecureConnection(true)
Mesibo.setAccessToken(user?.mesiboToken)
val myProfile = UserProfile()
myProfile.name = userManager.getUser()?.name
myProfile.address = userManager.getUser()?.mesiboAddress
Mesibo.setSelfProfile(myProfile)
Mesibo.setUserProfile(myProfile, true)
Mesibo.setDatabase("mydb", 0)
MesiboCall.getInstance().init(applicationContext)
Mesibo.setAppInForeground(this, 0, true)
Mesibo.start()
Mesibo.setPushToken(TokenManager.getInstance().getFCMToken())
}
}
mesibo does not know the name unless you set it using user profile APIs. In such cases, it sets the name as the address. You should use mesibo user profile APIs to set the profile for address and save.
https://mesibo.com/documentation/api/real-time-api/profiles/
A profile allows you to associate human readable information such as
name, picture, status, presence information, etc. with a user or a
group.
I want to show all messaging apps installed by user in it's phone. The list I am expecting is like, WhatsApp, Facebook messenger, Viber, Slack, Skype, WeChat etc (If any installed). So, far I have tried getting all apps in Phone through this code:
val pm: PackageManager = context!!.packageManager
val i = Intent(Intent.ACTION_MAIN)
i.addCategory(Intent.CATEGORY_LAUNCHER)
val lst = pm.queryIntentActivities(i, 0)
for (resolveInfo in lst) {
Log.d(
"Test",
"New Launcher Found: " + resolveInfo.activityInfo.packageName
)
This only gives me Slack app but not other messaging apps. I have a feeling it has something to do with MIME types as mentioned in Google docs.
text/*, senders will often send text/plain, text/rtf, text/html, text/json
image/*, senders will often send image/jpg, image/png, image/gif
video/*, senders will often send video/mp4, video/3gp
but I don't know how to use this info. Any help would be appreciated. TIA!
add ACTION_SENDTOas action parameter while creating your intent and you should see the list of apps capable of handling messages/sms etc.
val intent = Intent(Intent.ACTION_SENDTO)
// get list of activities that can handle this type of intent
val lst = pm.queryIntentActivities(intent, 0)
Here pack name refers to - apps package name
fun appinstalled(){
var app_names = mutableListOf<String>()
val app_package = mutableListOf<String>()
val packagelist: MutableList<PackageInfo> = packageManager.getInstalledPackages(0)
var appname : String
var packname : String
for (i in packagelist.indices) {
val packageinfo : PackageInfo= packagelist[i]
appname=packageinfo.applicationInfo.loadLabel(packageManager).toString()
packname=packageinfo.packageName
app.add(appname)
app-package.add(packname)
}
this will the list of app installed in the user device as well as their package name . After this attach these list to the Listview and their adpater and all done.
Can't insert a new conversation to Telephony.Sms.Conversations.CONTENT_URI.
Keep getting a Caused by: java.lang.NullPointerException: Uri must not be null exception.
Even though the uri has a value of "content://sms/conversations".
Situation - logic flow
I receive an SMS message from an unknown number.
I insert a conversation for the unknown number (if one not found).
I insert the message and associate it with the newly created conversation.
Dev setup
For learning purposes, I am creating an Android SMS application with Kotlin.
Android Emulator with Pixel XL API 26.
The application I'm working on is set as the default SMS app.
Can successfully send, receive and insert (code below) individual messages.
createMessage() - works
Below is the working code I wrote to insert a message when the phone receives an SMS.
fun createMessage(
resolver: ContentResolver,
threadId: Number,
body: String,
sentByUs: Boolean
): Message? {
val messageType = if (sentByUs) Telephony.Sms.MESSAGE_TYPE_SENT else Telephony.Sms.MESSAGE_TYPE_INBOX
val values = ContentValues()
values.put(Telephony.Sms.THREAD_ID, threadId.toInt())
values.put(Telephony.Sms.BODY, body)
values.put(Telephony.Sms.TYPE, messageType)
val result = resolver.insert(Telephony.Sms.CONTENT_URI, values)
return this.getMessage(resolver, result)
}
createConversation() - doesn't work
Below is the code I'm working on which tries to insert a new conversation.
fun createConversation(
resolver: ContentResolver,
senderPhoneNumber: String,
latestMessageText: String,
latestMessageTimestamp: Long,
latestMessageIsOurs: Boolean,
latestMessageWasRead: Boolean
): Conversation? {
val wasRead = if (latestMessageWasRead) 1 else 0
val isOurs = if (latestMessageIsOurs) Telephony.Sms.Conversations.MESSAGE_TYPE_SENT else Telephony.Sms.Conversations.MESSAGE_TYPE_INBOX
val values = ContentValues()
values.put(Telephony.Sms.Conversations.ADDRESS, senderPhoneNumber)
values.put(Telephony.Sms.Conversations.BODY, latestMessageText)
values.put(Telephony.Sms.Conversations.DATE, latestMessageTimestamp)
values.put(Telephony.Sms.Conversations.TYPE, isOurs)
values.put(Telephony.Sms.Conversations.READ, wasRead)
// -------------------------------------------------------------------------
// ------ Throws java.lang.NullPointerException: Uri must not be null ------
// -------------------------------------------------------------------------
val result = resolver.insert(Telephony.Sms.Conversations.CONTENT_URI, values)
return this.getConversation(resolver, result)
}
While executing the resolver.insert() the application crashes with the following error message:
Caused by: java.lang.NullPointerException: Uri must not be null
With the debugger attached I can see that the uri does have a value.
Telephony.Sms.Conversations.CONTENT_URI is "content://sms/conversations"
How does Google do it?
Found out that Google open sources its common Android apps.
Here's the code for the Messaging application:
https://android.googlesource.com/platform/packages/apps/Messaging/
While analyzing DatabaseHelper.java I came to the conclusion that they create a whole separate database from scratch.
And then work with that troughout the lifetime of the application.
Which confused me even more - why don't they use resolver.insert()?
I may be wrong, the program was overwhelming for a new guy like me.
Question
If Telephony.Sms.Conversations.CONTENT_URI has a value of "content://sms/conversations", why do I get the exception?
To investigate such issue you need to turn off logcat filtering for your app. Then you'll find an SQLiteException that will help find out what's actually wrong.
The NPE refers to the fact that the insert failed and return a null Uri instead of the appropriate Uri path of the new element.
I'm trying to get data from a monitor to an Android application and I've took the IHE - PCD-01 transaction as a model.
The scheme is simple, is based on achieve the interconnection between the monitor and the tablet, where the monitor sends constantly information and the application is listening.
But what I don't understand is if I need an ACK or not after every message. Does anyone can help me with this?
TL;DR yes, nothing special here, support the usual HL7 ACK/NACK driven by MSH-15, MSH-16 fields. ACK-ing everything by default is "better safe then sorry"
The document "IHE Patient Care Device (PCD), Technical Framework, Volume 2 (PCD TF-2) Transactions, Revision 1.0 - Final Text, August 12, 2011" available at http://www.ihe.net/technical_framework/upload/ihe_pcd_tf_vol2_ft_2011-08-12.pdf says
..The common static definition of the HL7 acknowledgement (ACK) message is described in Appendix G, "HL7 Implementation Notes"..
which says
G.1 Network Guidelines
The HL7 2.6 standard does not define a network communications protocol. Beginning with HL7 2.2, the definitions of lower layer protocols were moved to the Implementation Guide, but are not HL7 requirements. The IHE Framework makes these recommendations:
Applications shall use the Minimal Lower Layer Protocol defined in Appendix C of the HL7 Implementation Guide.
An application that wants to send a message (initiate a transaction) will initiate a network connection to start the transaction. The receiver application will respond with an acknowledgement or response to query but will not initiate new transactions on this network connection
G.1.1 Acknowledgment Modes
ACKNOWLEDGMENT MESSAGES
Acknowledgment messages may be defined on an application basis. However the simple general acknowledgment message (ACK) may be used where the application does not define a special message (application level acknowledgment) and in other cases as described in Section 2.9, "Message Processing Rules".
The IHE PCD transaction PCD-03 supports „enhanced mode‟ acknowledgements. See discussion under PCD-03 Transactions as well as in B.1 MSH – Message Header Segment and B.2 MSA – Message Acknowledgement Segment
and document "Health Level Seven, Version 2.6 © 2007, Chapter 2: Control" coming from the "HL7 Messaging Standard Version 2.6" package which can be downloaded from http://www.hl7.org/implement/standards/product_brief.cfm?product_id=185 describes the accept and validate behavior in
2.9.2 Message response using the original processing rules
..too long to quote..
2.9.3 Response using enhanced acknowledgement
..too long to quote..
depending on the values of MSH-15 Accept Acknowledgement Type and MSH-16 Application Acknowledgment Type fields in the HL7 message
The above chapters from the HL7 standard contain what you want to read and implement/support.
EDIT:
Simply put, in HL7 protocol in every message sent the sender may request an ACK receipt by flagging appropriate fields in the message header segment. IHE does not remove this rule and does not enforce any other but enables any other convention to be defined on an application basis. Correct expected behavior is defined by the HL7 specification and in order to get it right and create a conforming implementation (without hidden surprises for your 3rd parties) you may need to read it several times (see also Stack Overflow: How can I make my system HL7 certified?)
For example this is how HAPI library handles the ACKing, snippet comes from http://sourceforge.net/p/hl7api/code/764/tree/tags/Root_REL_1_2/hapi-mvn/hapi-base/src/main/java/ca/uhn/hl7v2/protocol/impl/ProcessorImpl.java
/**
* #see ca.uhn.hl7v2.protocol.Processor#cycle(boolean)
*/
public void cycle(boolean expectingAck) throws HL7Exception {
log.debug("In cycle({})", expectingAck);
cleanReservations();
cleanAcceptAcks();
cleanReservedMessages();
Transportable in = null;
try {
if (expectingAck) {
in = tryReceive(myContext.getLocallyDrivenTransportLayer());
} else {
in = tryReceive(myContext.getRemotelyDrivenTransportLayer());
}
} catch (TransportException e) {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {}
throw e;
}
// log
if (in != null) {
log.debug("Received message: {}", in.getMessage());
} else {
log.debug("Received no message");
}
// If we have a message, handle it
if (in != null) {
String acceptAckNeeded = null;
// String appAckNeeded = null;
String ackCode = null;
String ackId = null;
try {
String[] fieldPaths = {"MSH-15", "MSH-16", "MSA-1", "MSA-2"};
String[] fields = PreParser.getFields(in.getMessage(), fieldPaths);
acceptAckNeeded = fields[0];
// appAckNeeded = fields[1];
ackCode = fields[2];
ackId = fields[3];
} catch (HL7Exception e) {
log.warn("Failed to parse accept ack fields in incoming message", e);
}
if (ackId != null && ackCode != null && ackCode.startsWith("C")) {
long expiryTime = System.currentTimeMillis() + 1000 * 60;
myAcceptAcks.put(ackId, new ExpiringTransportable(in, expiryTime));
} else {
AcceptAcknowledger.AcceptACK ack = AcceptAcknowledger.validate(getContext(), in);
if ((acceptAckNeeded != null && acceptAckNeeded.equals(AL))
|| (acceptAckNeeded != null && acceptAckNeeded.equals(ER) && !ack.isAcceptable())
|| (acceptAckNeeded != null && acceptAckNeeded.equals(SU) && ack.isAcceptable())) {
trySend(myContext.getRemotelyDrivenTransportLayer(), ack.getMessage());
}
if (ack.isAcceptable()) {
if (isReserved(ackId)) {
log.debug("Received expected ACK message with ACK ID: {}", ackId);
removeReservation(ackId);
long expiryTime = System.currentTimeMillis() + 1000 * 60 * 5;
myAvailableMessages.put(ackId, new ExpiringTransportable(in, expiryTime));
} else {
log.debug("Sending message to router");
Transportable out = myContext.getRouter().processMessage(in);
sendAppResponse(out);
}
} else {
// TODO: should we do something more here? Might be nice to
// allow a configurable handler for this situation
log.warn("Incoming message was not acceptable");
}
}
} else {
String transport = expectingAck ? " Locally driven " : "Remotely driven";
log.debug("{} TransportLayer.receive() returned null.", transport);
}
sleepIfNeeded();
log.debug("Exiting cycle()");
}
Thanks for your answer :)
of course that it is better to use an ACK to make sure if the receiver is getting the message but what I wanted to know if it was mandatory or not using the PCD-01 transaction.
I've read your documents and what I've understood is that the use of ACK depends on the MSH-15 and MSH-16 fields content, but with the following information:
An application that wants to send a message (initiate a transaction) will initiate a network connection to start the transaction. The receiver application will respond with an acknowledgement or response to query but will not initiate new transactions on this network connection
I understand that the ACK is only at the beginning of the connection not after every message, is it right?