I know this is a big topic, as seen here and here, so I just wanted to post how I solved both the issue of receiving incoming MMS and SMS messages and the issue of grabbing data from those MMS and SMS messages on Android 9.0 version 28+ using Xamarin.Forms. This code can easily be translated to Java. Here is the completed Android app so you can try it yourself. It also shows how to do some Azure machine learning if you're interested in that.
For Broadcast Receivers:
Classes, registering class instances , permissions needed.
Note that the broadcast receivers were added dynamically, they can be added statically using Xamarin's intent-filter decorator , or (if you're not using Xamarin) the AndroidManifest.xml file.
Here is a code snippet to show how to parse incoming SMS data with a Broadcast Receiver:
public override void OnReceive(Context context, Intent intent)
{
Log.Info(TAG, "Intent action received: " + intent.Action);
// Retrieve message from the intent and analyze it.
SmsMessage msg = Android.Provider.Telephony.Sms.Intents.GetMessagesFromIntent(intent)[0];
string message = msg.DisplayMessageBody;
(string, bool) result = MMSReceiver.CleanUpMessage(message);
// If there were one or more rooster words.
if (result.Item2)
{
string title = "Rooster Text Received From: " + msg.DisplayOriginatingAddress;
DependencyService.Get<INotificationManager>().ScheduleNotification(title, result.Item1);
}
}
And here is a code snippet to show how to parse incoming MMS data with a Broadcast Receiver:
public override void OnReceive(Context context, Intent intent)
{
Log.Info(TAG, "Intent action received: " + intent.Action);
// Get the MMS ID. Adapted from: https://stackoverflow.com/questions/10065249/how-to-get-mms-id-android-application
ContentResolver contentResolver = AndroidApp.Context.ContentResolver;
Android.Net.Uri mmsInboxUri = Android.Net.Uri.Parse("content://mms");
Android.Database.ICursor mmsInboxCursor = contentResolver.Query(mmsInboxUri, new string[]
{"_id","msg_box","ct_t","date"}, "msg_box=1 or msg_box=2", null, null);
int id = -1;
if (mmsInboxCursor != null)
{
try
{
if (mmsInboxCursor.MoveToFirst())
{
id = Int32.Parse(mmsInboxCursor.GetString(0));
Log.Info(TAG, "Id is this: " + mmsInboxCursor.GetString(0));
}
}
catch (System.Exception error)
{
Log.Error(TAG, "Error requesting the MMS ID: " + error.Message);
}
}// if (mmsInboxCursor != null)
// Get text and picture from MMS message. Adapted from: https://stackoverflow.com/questions/3012287/how-to-read-mms-data-in-android
string message = ""; // text
Android.Graphics.Bitmap bitmap = null; // picture
string selectionPart = "mid=" + id;
Android.Net.Uri mmsTextUri = Android.Net.Uri.Parse("content://mms/part");
Android.Database.ICursor cursor = contentResolver.Query(mmsTextUri, null,
selectionPart, null, null);
if (cursor.MoveToFirst())
{
do
{
string partId = cursor.GetString(cursor.GetColumnIndex("_id"));
string type = cursor.GetString(cursor.GetColumnIndex("ct"));
// Get text.
if ("text/plain".Equals(type))
{
string data = cursor.GetString(cursor.GetColumnIndex("_data"));
if (data != null)
{
message = GetMmsText(partId);
Log.Info(TAG, "Body is this: " + message);
}
else
{
message = cursor.GetString(cursor.GetColumnIndex("text"));
Log.Info(TAG, "Body is this: " + message);
}
}
//Get picture.
if ("image/jpeg".Equals(type) || "image/bmp".Equals(type) ||
"image/gif".Equals(type) || "image/jpg".Equals(type) ||
"image/png".Equals(type))
{
bitmap = GetMmsImage(partId);
}
} while (cursor.MoveToNext());
}// if (cursor.MoveToFirst())
}
Related
following the steps gluonfx attach service between graalvm and android , I was able to do the messaging defined under
public class AndroidSmsService implements SmsService { ...
#Override
public void sendSms(Message message) {
if (message != null) {
_logger.log(Level.INFO, "AndroidSmsService#sendSms phone: {0}, sms : {1} ", new Object[]{message.getPhone(), message.getSms()});
String phone = message.getPhone();
String sms = message.getSms();
if (phone != null && phone.length() > 0 && sms != null && sms.length() > 0) {
sendSms(phone, sms);
}
}
}
private native void sendSms(String phone, String sms);
}
through the JNI Interface, I am able to call sendSms(String phone,String message) defined under
public class DalvikSmsService { ...
private void sendSms(String phone, String message) {
try {
String formattedNumber = PhoneNumberUtils.formatNumber(phone);
Log.i(TAG, "sendSMS phone : " + formattedNumber + ", sms : " + message);
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(formattedNumber, null, message, null, null);
} catch (Exception exc) {
exc.printStackTrace();
Log.i(TAG, "sendSMS sending failed, please try again");
}
}
}
my AndroidManifest.xml file
...
<uses-permission android:name="android.permission.SEND_SMS"/>
<application>
<service android:name="com.gluonhq.helloandroid.DalvikSmsService"
android:permission="android.permission.SEND_SMS"
android:exported="true"/>
<activity android:name=".PermissionRequestActivity"/>
...
the logs clearly show the method is called (with the phone number edited out)
01-13 08:45:56.760: D/GraalGluon(4546): ATTACH_DALVIK, tid = 4587, existed? 1, dalvikEnv at 0x799ff67ca470
01-13 08:45:56.763: I/DalvikSmsService(4546): sendSMS phone : 256-701-XYZXYZ, sms : Hello JavaFx Message
01-13 08:45:56.780: D/SmsNumberUtils(936): enter filterDestAddr. destAddr="[dH0eoa3ufMriilJ2cZIb3LD2V7g]"
01-13 08:45:56.782: D/SmsNumberUtils(936): destAddr is not formatted.
01-13 08:45:56.782: D/SmsNumberUtils(936): leave filterDestAddr, new destAddr="[dH0eoa3ufMriilJ2cZIb3LD2V7g]"
01-13 08:45:56.798: D/GraalGluon(4546): DETACH_DALVIK, tid = 4587, existed = 1, env at 0x799ff67ca470
BUT this message is never delivered !!!!. what could I be doing wrongly???
Please guide me how to access whatsapp status folder in android 11?
I have seen status saver apps in play store that doesn't ask for any special permission but are still able to show statuses?
Tell me how can I access WhatsApp/Media/.Statuses folder in android 11?
You can solve it using the DocumentTreeIntent if you make it allow permission for the WhatsApp folder explicitly for android 11 here is how you can do that.
if (Constants.isPackageInstalled(getActivity(), "com.whatsapp")) {
Intent intent;
StorageManager sm = (StorageManager) getActivity().getSystemService(STORAGE_SERVICE);
String statusDir = getWhatsupFolder();
String str = "android.provider.extra.INITIAL_URI";
if (Build.VERSION.SDK_INT >= 29) {
intent = sm.getPrimaryStorageVolume().createOpenDocumentTreeIntent();
String scheme = ((Uri) intent.getParcelableExtra(str)).toString().replace("/root/", "/document/");
String stringBuilder = scheme +
"%3A" +
statusDir;
intent.putExtra(str, Uri.parse(stringBuilder));
} else {
intent = new Intent("android.intent.action.OPEN_DOCUMENT_TREE");
intent.putExtra(str, Uri.parse(statusDir));
}
intent.addFlags(2);
intent.addFlags(1);
intent.addFlags(128);
intent.addFlags(64);
startActivityForResult(intent, REQUEST_ACTION_OPEN_DOCUMENT_TREE);
return;
}
before using this code you must check if WhatsApp is installed or not so the first check is for that here is the code for that.
try {
context.getPackageManager().getPackageInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
after granting permission you must retrieve files via this code
private DocumentFile[] getFromSdcard() {
DocumentFile fromTreeUri = DocumentFile.fromTreeUri(requireContext().getApplicationContext(), Uri.parse(namedataprefs));
if (fromTreeUri != null && fromTreeUri.exists() && fromTreeUri.isDirectory() && fromTreeUri.canRead() && fromTreeUri.canWrite()) {
return fromTreeUri.listFiles();
}
return null;
}
DocumentFile[] allFiles = getFromSdcard();
//to get signal file path
String path = allFiles[0].getUri().toString();
you can get further details about document tree intent from the below code its just for understanding purposes
Ref : How to check which StorageVolume we have access to, and which we don't?
requestAccessButton.setOnClickListener {
storageVolumes = storageManager.storageVolumes
val primaryVolume = storageManager.primaryStorageVolume
val intent = primaryVolume.createOpenDocumentTreeIntent()
startActivityForResult(intent, 1)
}
}
private fun checkAccessToStorageVolumes() {
val storageVolumePathsWeHaveAccessTo = HashSet<String>()
val persistedUriPermissions = contentResolver.persistedUriPermissions
persistedUriPermissions.forEach {
storageVolumePathsWeHaveAccessTo.add(it.uri.toString())
}
val storageManager = getSystemService(Context.STORAGE_SERVICE) as StorageManager
val storageVolumes = storageManager.storageVolumes
for (storageVolume in storageVolumes) {
val uuid = if (storageVolume.isPrimary) {
// Primary storage doesn't get a UUID here.
"primary"
} else {
storageVolume.uuid
}
val volumeUri = uuid?.let { buildVolumeUriFromUuid(it) }
when {
uuid == null ->
Log.d("AppLog", "UUID is null for ${storageVolume.getDescription(this)}!")
storageVolumePathsWeHaveAccessTo.contains(volumeUri) ->
Log.d("AppLog", "Have access to $uuid")
else -> Log.d("AppLog", "Don't have access to $uuid")
}
}
}
private fun buildVolumeUriFromUuid(uuid: String): String {
return DocumentsContract.buildTreeDocumentUri(
"com.android.externalstorage.documents",
"$uuid:"
).toString()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
Log.d("AppLog", "resultCode:$resultCode")
val uri = data?.data ?: return
val takeFlags =
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(uri, takeFlags)
Log.d("AppLog", "granted uri: ${uri.path}")
}
For Fetch Whatsapp Status in Android 11 You have to Fetch this status from this path.
which is written below:-
Android->media->com.whatsapp->WhatsApp->Media->.Statues->"You can see all statues which was show by user"
You can try this Path. it may be helpful for you.
For Android-10 and above
File(Environment.getExternalStorageDirectory() + File.separator + "Android/media/com.whatsapp/WhatsApp/Media/.Statuses")
Below Android-10 Version
File(Environment.getExternalStorageDirectory() + File.separator + "WhatsApp/Media/.Statuses")
I'm am fairly new to Android, and I'm trying to develop an app for a small project of mine using an example Bluetooth Chat App and Spotify Media Notifications. I could not provide the links because stack overflow did not let me but they are available online with a quick google search. I was wondering why I was getting this exception and is there any solution to this?
java.lang.NullPointerException
The line numbers correspond to each of the pieces of code below:
Line 269 if (message.length() > 0)
Line 56 public class BluetoothChatFragment extends Fragment
Line 131 sendMessage(artistName);
How my code is set up:
I have a static broadcast receiver set up exactly like the Spotify link below. I grab the song name and artist and package them into strings. Then I have another broadcast receiver in my Bluetooth fragment. I used another one because I did not know how to get the string into the Bluetooth fragment because I needed to call sendMessage() a private method in the fragment class. It seems like the exception is throwing when I call sendMessage(string) in my fragment.
Static Spotify Broadcast Receiver
package com.example.android.bluetoothchat;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.example.android.common.logger.Log;
public class MyBroadcastReceiver extends BroadcastReceiver {
static final class BroadcastTypes {
static final String SPOTIFY_PACKAGE = "com.spotify.music";
static final String PLAYBACK_STATE_CHANGED = SPOTIFY_PACKAGE + ".playbackstatechanged";
static final String QUEUE_CHANGED = SPOTIFY_PACKAGE + ".queuechanged";
static final String METADATA_CHANGED = SPOTIFY_PACKAGE + ".metadatachanged";
}
#Override
public void onReceive(Context context, Intent intent) {
// This is sent with all broadcasts, regardless of type. The value is taken from
// System.currentTimeMillis(), which you can compare to in order to determine how
// old the event is.
long timeSentInMs = intent.getLongExtra("timeSent", 0L);
Intent sendBack = new Intent("Received");
String action = intent.getAction();
String artistName;
String trackName;
if (action.equals(BroadcastTypes.METADATA_CHANGED)) {
artistName = intent.getStringExtra("artist");
trackName = intent.getStringExtra("track");
String msg = "S a " + artistName + '\0';
String msg2 = "S n " + trackName + '\0';
Log.d("Song", msg);
Log.d("Artist", msg2);
sendBack.putExtra("Song", msg);
sendBack.putExtra("Artist", msg2);
} else if (action.equals(BroadcastTypes.PLAYBACK_STATE_CHANGED)) {
boolean playing = intent.getBooleanExtra("playing", false);
// Do something with extracted information
if(playing) {
Log.d("Playing", "S p\0");
sendBack.putExtra("Playing", "S p\0");
}
else {
Log.d("Playing", "S s\0");
sendBack.putExtra("Playing", "S s\0");
}
}
context.sendBroadcast(sendBack);
}
}
Broadcast Receiver in Fragment:
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String artistName = intent.getStringExtra("Song");
String trackName = intent.getStringExtra("Artist");
String playing = intent.getStringExtra("Playing");
Log.d("Please", artistName);
Log.d("Please", trackName);
Log.d("Please", playing);
sendMessage(artistName);
sendMessage(trackName);
sendMessage(playing);
}
};
sendMessage Method():
private void sendMessage(String message) {
// Check that we're actually connected before trying anything
if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
Toast.makeText(getActivity(), R.string.not_connected, Toast.LENGTH_SHORT).show();
return;
}
// Check that there's actually something to send
if (message.length() > 0) {
// Get the message bytes and tell the BluetoothChatService to write
byte[] send = message.getBytes();
mChatService.write(send);
// Reset out string buffer to zero and clear the edit text field
mOutStringBuffer.setLength(0);
mOutEditText.setText(mOutStringBuffer);
}
}
Any help would be much appreciated!
http://developer.android.com/samples/BluetoothChat/index.html
To me it looks like sendMessage gets called with null instead of an actual string. Note that intent.getStringExtra(some_string) can return null, if some_string cannot be found.
I am implementing Facebook friends invite in my android app and I need to get to how many friends user sent app request so that I can award him some points.
What I have done so far is as below
WebDialog requestsDialog = (new WebDialog.RequestsDialogBuilder(this,
sessiob, params)).setOnCompleteListener(
new OnCompleteListener() {
#Override
public void onComplete(Bundle values,FacebookException error) {
if (error != null) {
} else {
final String requestId = values.getString("request");
final String[] requestArr1 = values.getStringArray("to");
if (requestId != null) {
Log.e("RequestId1",requestId + "\n" + values.toString());
else {
Toast.makeText(Login.this,"Request cancelled", Toast.LENGTH_SHORT).show();
}
}
}
}).build();
And the Bundle value I am getting is Bundle[{to[0]=808411111111111,to[1]=151584774222222, request=879734911111111}]
While my above code final String requestId = values.getString("request"); working fine however values.getStringArray("to"); giving me Null.
I want to know value "to" inside the Bundle is a StringArray or not and if yes then what's wrong in my extraction process.
Check out this example you will find your all answers
Simple facebook
I want to send MMS from my application to a specific number. I've searched and found this code but I have no idea if this code what I need or not.
My Questions is :
-can anyone explain this code to me.i am beginner in MMS.
-also, i thought this code is let the user send MMS from my application without move it to the native Messaging inbox (and this is what i want) Am i right?
-also i have a problem ,i do not know how can i put this code in my project.
this is what i found
MMS is just a http-post request. You should perform the request using extra network feature :
final ConnectivityManager connMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
final int result = connMgr.startUsingNetworkFeature( ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_MMS);
If you get back the result with Phone.APN_REQUEST_STARTED value, you have to wait for proper state. Register BroadCastReciver and wait until Phone.APN_ALREADY_ACTIVE appears:
final IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(reciver, filter);
If background connection is ready, then build content and perform request. If you want to do that using android's internal code, please use this:
final SendReq sendRequest = new SendReq();
final EncodedStringValue[] sub = EncodedStringValue.extract(subject);
if (sub != null && sub.length > 0) {
sendRequest.setSubject(sub[0]);
}
final EncodedStringValue[] phoneNumbers = EncodedStringValue.extract(recipient);
if (phoneNumbers != null && phoneNumbers.length > 0) {
sendRequest.addTo(phoneNumbers[0]);
}
final PduBody pduBody = new PduBody();
if (parts != null) {
for (MMSPart part : parts) {
final PduPart partPdu = new PduPart();
partPdu.setName(part.Name.getBytes());
partPdu.setContentType(part.MimeType.getBytes());
partPdu.setData(part.Data);
pduBody.addPart(partPdu);
}
}
sendRequest.setBody(pduBody);
final PduComposer composer = new PduComposer(this.context, sendRequest);
final byte[] bytesToSend = composer.make();
HttpUtils.httpConnection(context, 4444L, MMSCenterUrl, bytesToSendFromPDU, HttpUtils.HTTP_POST_METHOD, !TextUtils.isEmpty(MMSProxy), MMSProxy, port);
MMSCenterUrl: url from MMS-APNs,
MMSProxy: proxy from MMS-APNs,
port: port from MMS-APNs
Note that some classes are from internal packages. Download from android git is required.
The request should be done with url from user's apn-space code:
public class APNHelper {
public class APN {
public String MMSCenterUrl = "";
public String MMSPort = "";
public String MMSProxy = "";
}
public APNHelper(final Context context) {
this.context = context;
}
public List<APN> getMMSApns() {
final Cursor apnCursor = this.context.getContentResolver().query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"), null, null, null, null);
if ( apnCursor == null ) {
return Collections.EMPTY_LIST;
} else {
final List<APN> results = new ArrayList<APN>();
while ( apnCursor.moveToNext() ) {
final String type = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.TYPE));
if ( !TextUtils.isEmpty(type) && ( type.equalsIgnoreCase(Phone.APN_TYPE_ALL) || type.equalsIgnoreCase(Phone.APN_TYPE_MMS) ) ) {
final String mmsc = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.MMSC));
final String mmsProxy = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.MMSPROXY));
final String port = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.MMSPORT));
final APN apn = new APN();
apn.MMSCenterUrl = mmsc;
apn.MMSProxy = mmsProxy;
apn.MMSPort = port;
results.add(apn);
}
}
apnCursor.close();
return results;
}
Please help me
why don't you use the android system functions:
Please have a look on
https://developer.android.com/guide/components/intents-common.html
public void composeMmsMessage(String message, Uri attachment) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setData(Uri.parse("smsto:")); // This ensures only SMS apps respond
intent.putExtra("sms_body", message);
intent.putExtra(Intent.EXTRA_STREAM, attachment);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent); }
}
Cheers
Tom
I found a link in an other thread to a github project that works 100% https://github.com/klinker41/android-smsmms
Notice, that obligatory settings are only
Settings sendSettings = new Settings();
sendSettings.setMmsc(mmsc);
sendSettings.setProxy(proxy);
sendSettings.setPort(port);
you can get them something like (found at Set APN programmatically on Android - answear by vincent091):
Cursor cursor = null;
if (Utils.hasICS()){
cursor =SqliteWrapper.query(activity, activity.getContentResolver(),
Uri.withAppendedPath(Carriers.CONTENT_URI, "current"), APN_PROJECTION, null, null, null);
} else {
cursor = activity.getContentResolver().query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"),
null, null, null, null);
}
cursor.moveToLast();
String type = cursor.getString(cursor.getColumnIndex(Telephony.Carriers.TYPE));
String mmsc = cursor.getString(cursor.getColumnIndex(Telephony.Carriers.MMSC));
String proxy = cursor.getString(cursor.getColumnIndex(Telephony.Carriers.MMSPROXY));
String port = cursor.getString(cursor.getColumnIndex(Telephony.Carriers.MMSPORT));