For some time I've been trying to implement the functionality of sending an audio file from my app through WhatsApp. When debugging everything seems to work correctly in the application, the audio file is generated and saved correctly in the external storage of the device, the WhatsApp window opens and allows me to select the chat to which I want to send the audio. The problem is that when I press the send button, WhatsApp returns the message "Failed to share. Please try again" (I leave a screenshot of the error so that it can be better viewed, in addition to the code used to add said functionality).
Capture of the error shown on the screen by WhatsApp when trying to share the audio:
https://i.stack.imgur.com/gZuLt.jpg
Code used:
//////Boton//////
btn1.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
try {
String mediaPath = copyFiletoExternalStorage(R.raw.audio1, "audio1.mp3");
File myFile = new File(mediaPath);
Uri newUri = getUriForFile(wspActivity.this, "com.restart.shareaudiofiles.fileprovider", myFile);
Intent compartirAudio = new Intent(android.content.Intent.ACTION_SEND);
compartirAudio.setType("com.whatsapp");
compartirAudio.setType("audio/mp3");
compartirAudio.putExtra(Intent.EXTRA_STREAM,newUri);
startActivity(Intent.createChooser(compartirAudio, "Compartir vía"));
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Whatsapp no se encuentra instalado", Toast.LENGTH_LONG).show();
}
}
});
/////funcion auxiliar/////
private String copyFiletoExternalStorage(int resourceId, String resourceName){
String pathi= Environment.getExternalStorageDirectory() + "/Android/data/myProject/";
boolean exists = (new File(pathi)).exists();
if (!exists) {
new File(pathi).mkdirs();
}
String pathSDCard = Environment.getExternalStorageDirectory() + "/Android/data/TeLoResumoBotonera/" + resourceName;
try{
InputStream in = getResources().openRawResource(resourceId);
FileOutputStream out = null;
out = new FileOutputStream(pathSDCard);
byte[] buff = new byte[1024];
int read = 0;
try {
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
} finally {
in.close();
out.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return pathSDCard;
}
/////Elementos agregados al manifest/////
<queries>
<package android:name="com.whatsapp" />
<package android:name="com.whatsapp.w4b" />
</queries>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.restart.shareaudiofiles.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
I researched and implemented various resources to the manifest such as the fileprovider and queries with specific packages for WhatsApp (as can be seen in the code). However, the app still doesn't work. This makes me think that the problem could be in the mobile device that I am using to test the application (I would like to use another one to rule out this option, but I don't have an extra one). If the device has nothing to do with it, then clearly there is a bug in my code. Due to this, in case someone manages to identify the improvement that could make the application work correctly, I would be very grateful if you can share it with me, or at least give me an idea of where to address the problem.
Related
I am trying to send an attached json file to an email, however for some reason the json file is not being attached when the email is sent/created.
Note: I do NOT want the user to select the file to attach, I want it to be fixed/set automatically.
I have the following permissions in my AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
and the code
private void backupJsonToEmail(String jsonString) {
// create file
if(!getFilesDir().exists()){
getFilesDir().mkdir();
}
String filePath = getFilesDir() + File.separator + BACKUP_NAME;
System.out.println("file path: " + filePath);
// /data/user/0/com.my.stuff/files/backup.json
try {
FileOutputStream fos = new FileOutputStream(filePath);
DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(fos));
outStream.writeBytes(jsonString);
outStream.close();
// send to email
try {
File file = new File(filePath);
long fileKbSize = file.length() / 1024;
System.out.println("FILE SIZE IS: " + fileKbSize + " kb"); // 69 kb...
Uri uri = Uri.fromFile(file);
String to[] = {"test#yahoo.com"};
Intent originalIntent = ShareCompat.IntentBuilder.from(this)
.setType("application/json")
.setEmailTo(to)
.setStream(uri)
.setSubject("test")
.setText("here is the attached json")
.getIntent();
originalIntent.setData(Uri.parse("mailto:"));
originalIntent.setAction(Intent.ACTION_SENDTO);
Intent finalIntent = Intent.createChooser(originalIntent, "choose an email application");
startActivity(finalIntent);
} catch (Throwable t) {
Toast.makeText(this, "Request failed try again: " + t.toString(), Toast.LENGTH_LONG).show();
}
} catch (IOException e) {
e.printStackTrace();
}
}
EDIT1:
after making the suggestion changes that #piyushpk suggested, I now get the following errors when I choose an email app:
for Yahoo Mail: "The attachment is too big to send"
for Gmail: "Permission denied for the attachment"
however the file size is only 69 kb, according to my print statement...
I think you are trying to use Inbuilt email application to send a json file and yahoo is complaining about size and Gmail is denying it as json is considered not a safe extension.
Instead use Some SMTP Email api like send grid etc. to send file without any inbuilt android application.
https://github.com/sendgrid/sendgrid-java
Mail Gun Sendgrid are quite good option
I've created an app that records data usage of an app and writes it to a file on the phones sd card. Now I'm trying to allow this process to run in the background through the use of adb. I want to be able to send a signal/broadcast to the app to write the current data usage. And later be able to send a second signal that writes the data usage and the new time. So that one can look at how much data the app has used.
So far I've tried using adb and sending broadcasts to the app and it seems to be working however I am not able to save the file to the sd card through the use of mediascanner.
This is the function that is run when I send "adb -d shell am broadcast -n com.axel.datatracking/.IntentReceiver --es --start 'com.linku.android.mobile_emergency.app.activity' " to the app.
public void startLog(Context context, SimplifiedAppInfo selectedApp) {
int i = 0;
String name = "dataFile.csv";
Log.d("update", "sort of works maybe");
// make the file if it already exists increment the name by 1
try {
testFile = new File(context.getExternalFilesDir(null), name);
while(testFile.exists()) {
i++;
name = this.makeFileName(i);
testFile = new File(context.getExternalFilesDir(null), name);
}
Log.d("filename", name);
testFile.createNewFile();
} catch (IOException e) {
Log.d("broke", "Unable to write my dood");
}
// try to write to the file
try {
fOut = new FileOutputStream(testFile);
BufferedWriter writer = new BufferedWriter(new FileWriter(testFile, true));
startingDown = selectedApp.getDownbytes();
startingUp = selectedApp.getUpbyts();
startingTime = System.currentTimeMillis();
writer.write("data,up,down\n");
writer.write("Initial,"+selectedApp.getUpbyts()+","+selectedApp.getDownbytes()+"\n");
writer.close();
// refresh the data file
//MediaScannerConnection.scanFile(context, new String[]{this.testFile.toString()}, null, null);
} catch (IOException e) {
Log.d("broke", "cant write to the file");
}
}
This is the function to write the end results that is run by "adb -d shell am broadcast -n com.axel.datatracking/.IntentReceiver --es --end 'com.linku.android.mobile_emergency.app.activity' "
public void endLog(Context context, SimplifiedAppInfo selectedApp) {
// write end results to file
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(testFile, true));
writer.write("End,"+selectedApp.getUpbyts()+","+selectedApp.getDownbytes()+"\n");
float effectiveDown = selectedApp.getDownbytes() - startingDown;
float effectiveUp = selectedApp.getUpbyts() - startingUp;
writer.write("Effective,"+effectiveUp+","+effectiveDown+"\n");
float timePassed = ((float) ((System.currentTimeMillis() - startingTime)/1000));
float avgUp = effectiveUp/timePassed;
float avgDown = effectiveDown/timePassed;
writer.write("Average bytes/sec,"+avgUp+","+avgDown+"\n");
writer.close();
fOut.close();
//MediaScannerConnection.scanFile(context, new String[]{this.testFile.toString()},null, null);
} catch (IOException e) {
Log.d("broke", "the write dont work");
}
}
Broadcast receivers components are not allowed to bind to services.
Edit: I'm open to other solutions besides using a broadcast receiver I just need to be able to log the data while outside of the app and focusing on another app, from the terminal.
Register receiver in manifest and then make some Receiver class, example:
public class CustomReceiver extends BroadcastReceiver {
#Override
public final void onReceive(Context context, Intent intent){
//// here you can start your own service or do logic here
}
here is how to register receiver in Manifest just pick different intent filter:
<receiver android:name="com.example.example.CustomReceiver ">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
And onReceive going to be called whenever phone action will be triggered.
I would advice to read documentation about Intents because Android has a lot of restrictions, per
Sorry for the title, I was not sure how to word it, my problem with my app is taking a photo and getting its Uri content to write to a new file using input/output stream. So far I have this
Intent take_photo_intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (take_photo_intent.resolveActivity(getActivity().getPackageManager()) != null)
{
try
{
// create file from a template
image_file = createFileForImage();
}
catch (IOException e)
{
e.printStackTrace();
}
// check if file is null
if(image_file != null)
{
// create uri using file and applying a provider
image_uri = FileProvider.getUriForFile(getContext(), PROVIDER, image_file);
take_photo_intent.putExtra(MediaStore.EXTRA_OUTPUT, image_uri);
image_path = image_uri.getPath();
// start activity
startActivityForResult(take_photo_intent, TAKE_PHOTO);
}
}
in this case, the return bitmap is what I need to display the thumbnail quality image in my app
my issue comes with trying to get the full scaled image and save it to a custom folder in the android devices storage.
I been following this tutorial
https://developer.android.com/training/camera/photobasics.html#Save%20the%20Full-size%20Photo
I am not 100% sure I set up the provider correctly, the app compiles and runes, my issue is with the the Uri.
if you go back to this chunk of my app
image_uri = FileProvider.getUriForFile(getContext(), PROVIDER, image_file);
take_photo_intent.putExtra(MediaStore.EXTRA_OUTPUT, image_uri);
image_path = image_uri.getPath();
the image_file is a File object I created in a folder in the external storage, that part is fine since i used that path for other things and can verify it in a file manager. this is not used until later.
The provider "seems to work" since the app runs
my issue is the image_uri. My thoughts is, i have a empty file saved in that dir i created, now I need to read the contents of image_uri into that file, so I did this in the onActivityResult
// this will hold the Uri data
InputStream in_file = null;
// this will be used to write the input stream to the file
OutputStream out_file = null;
// check if Uri is null
if(image_uri != null) {
try {
// get the Uri data into an input stream
in_file = getContext().getContentResolver().openInputStream(image_uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// check for nulls
if (in_file != null && image_file != null) {
// create output stream linked to new file location
try {
out_file = new FileOutputStream(image_file);
Log.i("IMAGE", "open outsream");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// create temp byte array
byte[] image_bytes = null;
try {
// use apache tools to write bytes to that file using the outputstream
image_bytes = IOUtils.toByteArray(in_file);
} catch (IOException e) {
e.printStackTrace();
}
if (image_bytes != null && out_file != null) {
try {
out_file.write(image_bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Now im thinking, ok, I have the picture just taken which the provider set up, I create an external file onto sd card which I know works, and I have an input stream to the uri and an outputstream to the file, and used IOUtils.toByteArray to write tho.
However, my problem is the uri data is all blank. by that i mean the bytes are all 0's or size of array after that IOUtils.toByteArray call is 0. so I am guessing it must be a problem with the provider. I am still not sure how it works, going off the tutorial, I have this
AndroidManifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.myapp.main.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
res/xml/file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="my_images" path="Android/data/com.myapp/files/Pictures" />
As I said, I am not sure how this works, is /files/Pictures a folder in my apps internal storage by default? do I have to make it?
when logging out some stuff, this is what I got
image_uri = FileProvider.getUriForFile(getContext(), PROVIDER, image_file);
Log.i("IMAGE", "DEBUG--->: " + image_uri.getPath());
I/IMAGE: DEBUG--->: /my_images/camera_shots/20160808_190857-1642881770.jpg
and I never created that folder, so I am confused on how this all works.
I've been trying to encrypt files and write those files back on to the same place. But I got the error message saying "java.io.FileNotFoundException: /storage/emulated/0/New file.txt: open failed: EACCES (Permission denied)".
My Manifest file is this
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.tdk.mytestapplication2">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
I think I have provided correct permission there. And the code I am using to encrypt files is this.
public static void encrypt(SecretKey secretKey, String filePath){
try {
// Here you read the cleartext.
FileInputStream fis = new FileInputStream(filePath);
// This stream write the encrypted text. This stream will be wrapped by another stream.
FileOutputStream fos = new FileOutputStream(filePath);
// Create cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
// Wrap the output stream
CipherOutputStream cos = new CipherOutputStream(fos, cipher);
// Write bytes
int b;
byte[] d = new byte[8];
while ((b = fis.read(d)) != -1) {
cos.write(d, 0, b);
}
// Flush and close streams.
cos.flush();
cos.close();
fis.close();
}catch(IOException e){
e.printStackTrace();
}catch (NoSuchAlgorithmException e){
e.printStackTrace();
}catch(NoSuchPaddingException e){
e.printStackTrace();
}catch(InvalidKeyException e){
e.printStackTrace();
}
}
And I used this method inside a button
Button btnEncrypt = (Button) findViewById(R.id.btnEnc);
btnEncrypt.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
aesKey = EncAndDec.generateKey();
String filePath = editText.getText().toString();
//Generating the file hash
String md5Hash = MD5Hash.getMD5(filePath);
System.out.println(aesKey.toString());
System.out.println(filePath);
System.out.println(md5Hash);
//Encrypting the file
for(int i=1; i<100; i++) {
EncAndDec.encrypt(aesKey, filePath);
}
}
});
Still I couldn't configure this error. Please someone help!
If you are running in Android 29 then you have to use scoped storage or for now, you can bypass this issue by using:
android:requestLegacyExternalStorage="true"
in manifest in the application tag.
I suspect you are running Android 6.0 Marshmallow (API 23) or later. If this is the case, you must implement runtime permissions before you try to read/write external storage.
Implement runtime permission for running your app on Android 6.0 Marshmallow (API 23) or later.
or you can manually enable the storage permission-
goto settings>apps> "your_app_name" >click on it >then click permissions> then enable the storage. Thats it.
But i suggest go the for first one which is, Implement runtime permissions in your code.
On Android 11, apps can no longer access files in any other app's dedicated, app-specific directory within external storage.
To protect user privacy, on devices that run Android 11 or higher, the system further restricts your app's access to other apps' private directories.
Request MANAGE_EXTERNAL_STORAGE permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage"/>
Request External Storage Permissions
ActivityCompat.requestPermissions( this,
new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE
}, 1
);
Checking permission of MANAGE_EXTERNAL_STORAGE
// If you have access to the external storage, do whatever you need
if (Environment.isExternalStorageManager()){
// If you don't have access, launch a new activity to show the user the system's dialog
// to allow access to the external storage
}else{
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", this.getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
For SDK 29 :
String str1 = "";
folder1 = new File(String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)));
if (folder1.exists()) {str1 = folder1.toString() + File.separator;}
public static void createTextFile(String sBody, String FileName, String Where) {
try {
File gpxfile = new File(Where, FileName);
FileWriter writer = new FileWriter(gpxfile);
writer.append(sBody);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Then you can save your file like this :
createTextFile("This is Content","file.txt",str1);
In my app I am adding a Share button through the ShareActionProvider class. I am trying to share a PNG which I pull from the file system. The problem is I get the following error thrown at me when I try to share it with the stock messaging app
com.google.android.mms.MmsException: /data/data/com.frostbytedev.wifiqr/files/QRCode.png: open failed: EACCES (Permission denied)
At first I thought it was my permissions but I have the following permissions in my Manifest.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
The place where I try to get it from the file system is here:
Uri uri = Uri.fromFile(new File(getFilesDir(), "/QRCode.png"));
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM,uri);
provider.setShareIntent(intent);
If you were wondering, he is the code where I save the image
String fileName = getFilesDir() + "/QRCode.png";
etSSID.setText(fileName);
OutputStream stream = null;
try {
stream = new FileOutputStream(fileName);
bmp.compress(Bitmap.CompressFormat.PNG, 80, stream);
stream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
How can I solve this issue?
if /data/data/com.frostbytedev.wifiqr is your app's private directory then yes, your app has permission to read that file. You don't even need the WRITE_EXTERNAL_STORAGE permission because it's "your" directory.
But once you share it with another app, that app needs permission to read the file as well. And that's per default not the case with files inside your app private directory. The error you get is from the MMS app having no access.
A simple way to fix the problem is to save the file to a place that can be read by every app. Essentially everything in Environment.getExternalStorageDirectory().
The next possibility is to make that file readable for other apps but keep it where you have it. File#setReadable(true, false) should do that.
Context also has nice methods to simplify creating files in readable mode.
String fileName = getFileStreamPath("QRCode.png").getPath();
etSSID.setText(fileName);
OutputStream stream = null;
try {
stream = openFileOutput("QRCode.png", Context.MODE_WORLD_READABLE);
bmp.compress(Bitmap.CompressFormat.PNG, 80, stream);
stream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
...
Uri uri = Uri.fromFile(getFileStreamPath("QRCode.png"));
.. share