Android studio - struggling to share image / audio / video file from app assets - java

I've now tried since a long time to make a cross app sharing intent from assets folder for different types of files, but there is always a problem working. I went on a lot of different types of solutions such as:
Try to use a custom content provider (here, or here, ..)
Try to save the file on local storage (example here)
and others but as it didn't worked I don't have links anymore..
Right now, here is my working solution for sharing an audio file to whatsapp and messenger only, all other app are failing).
My function to create a new file on app storage:
public String getNewPathFromSbElem(SbElem sbElem, String fileName) {
AssetManager assetManager = getAssets();
String path = sbElem.soundPath;
String newName = fileName;
String newPath = getExternalFilesDir(null).getAbsolutePath() + File.separator + newName;
InputStream in = null;
OutputStream out = null;
File outFile;
File deleteFile = new File(newPath);
if(deleteFile.exists()) {
deleteFile.delete();
}
try {
in = assetManager.open(path);
outFile = new File(getExternalFilesDir(null), newName);
out = new FileOutputStream(outFile);
copyFile(in, out);
} catch (IOException ex) {
ex.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
return newPath;
}
My 2 functions to share on whatsapp or messenger:
public void shareOnWhatsApp(SbElem sbElem) {
final String newPath = getNewPathFromSbElem(sbElem, "_share_temp_file.mp3");
Intent whatsappIntent = new Intent(Intent.ACTION_SEND);
whatsappIntent.setPackage("com.whatsapp");
whatsappIntent.setType("audio/mp3");
whatsappIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(newPath));
whatsappIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
startActivity(whatsappIntent);
} catch (android.content.ActivityNotFoundException ex) {
Log.d("error", "WhatsApp not installed");
}
}
public void shareOnMessenger (SbElem sbElem) {
final String newPath = getNewPathFromSbElem(sbElem, "_share_temp_file.mp3");
final File newFile = new File(newPath);
final Uri newUri = FileProvider.getUriForFile(this, getString(R.string.file_provider_authority), newFile);
final Integer SHARE_TO_MESSENGER_REQUEST_CODE = 1;
String mimeType = "audio/*";
ShareToMessengerParams shareToMessengerParams = ShareToMessengerParams.newBuilder(newUri, mimeType).build();
MessengerUtils.shareToMessenger(this, SHARE_TO_MESSENGER_REQUEST_CODE, shareToMessengerParams);
}
The thing is, I would like to be able to share from my asset folder a .mp3, a .jpg, a .png, to gmail, to whatsapp, to slack, or any type of app that supports this extension...
So as almost all the 1000 questions / articles online regarding sharing assets are answered by using a custom content provider, I tried the following sharing function:
public void shareBasic () {
// I added the test.jpg in the root of my asset folder
// Tried with content / file / 2 or 3 '/', with package name and with assets / ...
Uri theUri = Uri.parse("content:///com.MY_PACKAGE_NAME/test.jpg");
//Uri theUri = Uri.parse("content:///assets/test.jpg");
//Uri theUri = Uri.parse("file:///com.MY_PACKAGE_NAME/test.jpg");
//Uri theUri = Uri.parse("file:///asset.jpg");
Intent theIntent = new Intent(Intent.ACTION_SEND);
// Tried with jpeg / png / jpg ...
theIntent.setType("image/*");
theIntent.putExtra(Intent.EXTRA_STREAM, theUri);
theIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(theIntent);
}
And here is my Android manifest:
<provider
android:name="MY_PACKAGE_NAME.MyContentProvider"
android:authorities="MY_PACKAGE_NAME"
android:grantUriPermissions="true"
android:exported="true" />
And the file provider (almost the same on every tutorial)
public class MyContentProvider extends ContentProvider {
#Override
public boolean onCreate() {
return true;
}
#Override
public Cursor query(#NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
#Override
public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal )
{
// TODO: Implement this method
return super.query( uri, projection, selection, selectionArgs, sortOrder, cancellationSignal );
}
#Nullable
#Override
public String getType(#NonNull Uri uri) {
return null;
}
#Nullable
#Override
public Uri insert(#NonNull Uri uri, ContentValues values) {
return null;
}
#Override
public int delete(#NonNull Uri uri, String selection, String[] selectionArgs) {
return 0;
}
#Override
public int update(#NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
#Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
AssetManager am = getContext().getAssets();
String fileName = uri.getLastPathSegment();
if(fileName == null)
throw new FileNotFoundException();
AssetFileDescriptor fileDescriptor = null;
try {
fileDescriptor = am.openFd(fileName);
} catch (IOException e) {
e.printStackTrace();
}
return fileDescriptor;
}
}
I guess I'm doing huge mistakes as sharing an file from asset folder shouldn't be such a struggle, any ideas ?

For your ContentProvider solution:
You need to have your query() function support the OpenableColumns
You need to have your getType() function return the actual MIME type
Your Intent needs to use a concrete MIME type, not a wildcard
See this old sample project for a demonstration.

Related

Android CRUD MediaStore API 26+

I have an application for API26+
With Android 10 and above (API29+) should be used MediaStore to access files, instead of Environment.getExternalStoragePublicDirectory
normally making of new approach would be maked in creation of if-block
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
// deprecated code
} else {
// new approach
}
But:
MediaStorage use Uri to request a file. API28 and below works with File or String as path to file
With MediaStore manually creation of a folder is not needed. API 28 and below should create a folder if not exists.
Deleting of Files requires User interaction with MediaStore.
CRUD:
Create: To write data in File for any API is OutputStream required. That could be reached with if else
Read: How to create general approach to read a File, where API 29+ needs Uri, and API28- needs File|String to access a file?
Update: first Read to check if exists, then Create to get OutputStream
Delete: How to create a batch confirmation with MediaStore?
Is it possible to work with MediaStorage with API26+?
If yes, how? Many properties are added first in API29
After some own research and testing i have found the way to work with MediaStore and on old devices in same time.
First of all some helper classes needed:
With FileType we can support different file types in application at same time.
public enum FileType {
File(Environment.DIRECTORY_DOCUMENTS, Constants.VERSION_29_ABOVE ? MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL) : null, "text/plain"),
Download(Environment.DIRECTORY_DOWNLOADS, Constants.VERSION_29_ABOVE ? MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL) : null, "text/plain"),
Image(Environment.DIRECTORY_PICTURES, Constants.VERSION_29_ABOVE ? MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL) : null, "image/jpeg"),
Audio(Environment.DIRECTORY_MUSIC, Constants.VERSION_29_ABOVE ? MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL) : null, "audio/mpeg"),
Video(Environment.DIRECTORY_MOVIES, Constants.VERSION_29_ABOVE ? MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL) : null, "video/mpeg");
private final String directory;
private final Uri contentUri;
private String mimeType;
FileType(String directory, Uri contentUri, String mimeType) {
this.directory = directory;
this.contentUri = contentUri;
this.mimeType = mimeType;
}
public String getDirectory() {
return directory;
}
public Uri getContentUri() {
return contentUri;
}
public String getMimeType() {
return mimeType;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
}
with setMimeType we can change current file extension and still use other settings
And we need simple callback class to get different results
public interface ObjectCallback<T> {
void result(T object);
}
When we want write some data in a file, we need an OutputStream
With internal storage, the way to get a file has not changed
/**
* opens OutputStream to write data to file
*
* #param context activity context
* #param fileName relative file name
* #param fileType file type to get folder specific values for access
* #param fileStream callback with file output stream to requested file
* #return true if output stream successful opened, false otherwise
*/
public boolean openOutputStream(Context context, String fileName, FileType fileType, ObjectCallback<OutputStream> fileStream) {
File internalFolder = context.getExternalFilesDir(fileType.getDirectory());
File absFileName = new File(internalFolder, fileName);
try {
FileOutputStream fOut = new FileOutputStream(absFileName);
fileStream.result(fOut);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
By external storage, the way to access a file on API 28- and API 29+ is completely different.
On API 28- we could just use a file for it.
On API 29+ we shoould first check, if file already exists. If it (say text.txt) exists and we would make ContentResolver.insert, it would create text-1.txt and in worst case (endless loop) in could quick fill whole smartphone storage.
/**
* #param context application context
* #param fileName relative file name
* #return uri to file or null if file not exist
*/
public Uri getFileUri(Context context, String fileName) {
// remove folders from file name
fileName = fileName.contains("/") ? fileName.substring(fileName.lastIndexOf("/") + 1) : fileName;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
// Deprecated in API 29
File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), Constants.PUBLIC_STORAGE_FOLDER);
File file = new File(storageDir, fileName);
return file.exists() ? Uri.fromFile(file) : null;
} else {
String folderName = Environment.DIRECTORY_PICTURES + File.separator + Constants.PUBLIC_STORAGE_FOLDER + File.separator;
// get content resolver that can interact with public storage
ContentResolver resolver = context.getContentResolver();
String selection = MediaStore.MediaColumns.RELATIVE_PATH + "=?";
String[] selectionArgs = new String[]{folderName};
Cursor cursor = resolver.query(MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL), null, selection, selectionArgs, null);
Uri uri = null;
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
String itemFileName = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
if (itemFileName.equals(fileName)) {
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
uri = ContentUris.withAppendedId(MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL), id);
break;
}
}
}
cursor.close();
return uri;
}
}
/**
* opens OutputStream to write data to file
*
* #param context activity context
* #param fileName relative file name
* #param fileType file type to get folder specific values for access
* #param fileStream callback with file output stream to requested file
* #return true if output stream successful opened, false otherwise
*/
public boolean openOutputStream(Context context, String fileName, FileType fileType, ObjectCallback<OutputStream> fileStream) {
// remove folders from file name
fileName = fileName.contains("/") ? fileName.substring(fileName.lastIndexOf("/") + 1) : fileName;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), Constants.PUBLIC_STORAGE_FOLDER);
if (!storageDir.exists() && !storageDir.mkdir()) {
// directory for file not exists and not created. return false
return false;
}
File file = new File(storageDir, fileName);
try {
FileOutputStream fOut = new FileOutputStream(file);
fileStream.result(fOut);
} catch (Exception e) {
e.printStackTrace();
return false;
}
} else {
// get content resolver that can interact with public storage
ContentResolver resolver = context.getContentResolver();
// always check first if file already exist
Uri existFile = getFileUri(context, fileName);
if (existFile == null) {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.MIME_TYPE, fileType.getMimeType());
// absolute folder name
values.put(MediaStore.MediaColumns.RELATIVE_PATH, fileType.getDirectory() + File.separator + Constants.PUBLIC_STORAGE_FOLDER);
// file name
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
// create uri to the file. If folder not exists, it would be created automatically
Uri uri = resolver.insert(fileType.getContentUri(), values);
// open stream from uri and write data to file
try (OutputStream outputStream = resolver.openOutputStream(uri)) {
fileStream.result(outputStream);
// end changing of file
// when this point reached, no exception was thrown and file write was successful
values.put(MediaStore.MediaColumns.IS_PENDING, false);
resolver.update(uri, values, null, null);
} catch (Exception e) {
e.printStackTrace();
if (uri != null) {
// Don't leave an orphan entry in the MediaStore
resolver.delete(uri, null, null);
}
return false;
}
} else {
// open stream from uri and write data to file
try (OutputStream outputStream = resolver.openOutputStream(existFile)) {
fileStream.result(outputStream);
} catch (Exception e) {
e.printStackTrace();
// Don't leave an orphan entry in the MediaStore
resolver.delete(existFile, null, null);
return false;
}
}
}
return true;
}
In same way could be opened InputStream on all devices and different storage.
To delete file:
internal could be still deleted with file.delete on every API
external storage with API29+ requires user interaction to delete some file
FileProvider
To use Intent for showing some picture, crop picture or whatever, where we use setDataAndType
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(photoUri, "image/*");
// allow to read the file
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
we need in some cases the FileProvider. Specially:
To show external file created with the app, the photoUri would look this way
Uri photoUri = Constants.VERSION_29_ABOVE ? uriToFile :
FileProvider.getUriForFile(context,
context.getApplicationContext().getPackageName() + ".provider",
new File(uriToFile.getPath()));

How can i get PDF Real path from Download Folder in Android? [duplicate]

This question already has answers here:
Android Kotlin: Getting a FileNotFoundException with filename chosen from file picker?
(5 answers)
Android - Get real path of a .txt file selected from the file explorer
(1 answer)
Closed 2 years ago.
I want to get the real path of PDF from URI of the downloaded pdf from the download folder.
I am already following this link. But in this, I do not get the real path and getting exception. like below:
URI
content://com.android.providers.downloads.documents/document/msf%3A26
Lunch Intent:
Intent intent = new Intent();
intent.setType("application/pdf");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select PDF"), PICK_PDF_REQUEST);
OnActivityResult:
if (requestCode == PICK_PDF_REQUEST) {
if (resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
String path = null;
try {
path = getPDFPath(uri);
LogUtils.logE(TAG, "Document : " + path);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
private String getPDFPath(Uri uri) {
// DocumentProvider
if (DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if ("com.android.externalstorage.documents".equals(uri.getAuthority())) {
String documentId = DocumentsContract.getDocumentId(uri);
String[] split = documentId.split(":");
String type = split[0];
return Environment.getExternalStorageDirectory().toString() + "/" + split[1];
} else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
String decodedURI = Uri.decode(uri.toString());
if (decodedURI.contains("raw:")) {
return decodedURI.substring(decodedURI.indexOf("raw:") + 4);
}
String id = DocumentsContract.getDocumentId(Uri.parse(decodedURI));
Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id));
return getDataColumn(contentUri, null, null);
}
}
return null;
}
public String getDataColumn(Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
Exception
2020-05-04 14:47:09.747 17908-17908/ W/System.err: java.lang.NumberFormatException: For input string: "msf:26"
2020-05-04 14:47:09.748 17908-17908/W/System.err: at java.lang.Long.parseLong(Long.java:594)
2020-05-04 14:47:09.748 17908-17908/ W/System.err: at java.lang.Long.valueOf(Long.java:808)
I want Real path of PDF so I can use it for further use.
Thanks in Advance!!!
No need to get a 'real' path.
There is no need for it.
Just use the uri as is.
We all do like this these days.
Join us.

Google Cloud Endpoint Bucket Downloader

I am very new to the GC platform and am trying to create an API in Java with two methods: one which returns a list of all of the files in a specific bucket and another which retrieves a specified file from that bucket. The goal is to be able to iterate the file list in order to download every file from the bucket. Essentially, I am wanting to mirror the contents of the bucket on an Android device, so the API will be called from a generated client library in an Android app.
My getFileList() method returns a ListResult object. How do I extract a list of files from this?
#ApiMethod(name = "getFileList", path = "getFileList", httpMethod = ApiMethod.HttpMethod.GET)
public ListResult getFileList(#Named("bucketName") String bucketName) {
GcsService gcsService = GcsServiceFactory.createGcsService(RetryParams.getDefaultInstance());
ListResult result = null;
try {
result = gcsService.list(bucketName, ListOptions.DEFAULT);
return result;
} catch (IOException e) {
return null; // Handle this properly.
}
}
Also, I am struggling to determine what the return type of my getFile() API method should be. I can’t use a byte array since return types cannot be simple types as I understand it. This is where I am with it:
#ApiMethod(name = "getFile", path = "getFile", httpMethod = ApiMethod.HttpMethod.GET)
public byte[] getFile(#Named("bucketName") String bucketName, ListItem file) {
GcsService gcsService = GcsServiceFactory.createGcsService();
GcsFilename gcsFilename = new GcsFilename(bucketName, file.getName());
ByteBuffer byteBuffer;
try {
int fileSize = (int) gcsService.getMetadata(gcsFilename).getLength();
byteBuffer = ByteBuffer.allocate(fileSize);
GcsInputChannel gcsInputChannel = gcsService.openReadChannel(gcsFilename, 0);
gcsInputChannel.read(byteBuffer);
return byteBuffer.array();
} catch (IOException e) {
return null; // Handle this properly.
}
}
I am lost in the Google documentation for this stuff and am concerned that I am coming at it from completely the wrong direction since all I am trying to do is securely download a bunch of files!
I can't give you a complete solution because, this is code I wrote for my company, but I can show you some basics. I use the google-cloud-java API.
First you need to create an API key and download this in JSON format. More details can be found here.
I have - amongst other - these two fields in my class:
protected final Object storageInitLock = new Object();
protected Storage storage;
First you will need a method to initialize a com.google.cloud.storage.Storage object, something like (set your project-id and path to json api key):
protected final Storage getStorage() {
synchronized (storageInitLock) {
if (null == storage) {
try {
storage = StorageOptions.newBuilder()
.setProjectId(PROJECTID)
.setCredentials(ServiceAccountCredentials.fromStream(new FileInputStream(pathToJsonKey)))
.build()
.getService();
} catch (IOException e) {
throw new MyCustomException("Error reading auth file " + pathToJsonKey, e);
} catch (StorageException e) {
throw new MyCustomException("Error initializing storage", e);
}
}
return storage;
}
}
to get all entries you could use something like:
protected final Iterator<Blob> getAllEntries() {
try {
return getStorage().list(bucketName).iterateAll();
} catch (StorageException e) {
throw new MyCustomException("error retrieving entries", e);
}
}
list files in a directory:
public final Optional<Page<Blob>> listFilesInDirectory(#NotNull String directory) {
try {
return Optional.ofNullable(getStorage().list(getBucketName(), Storage.BlobListOption.currentDirectory(),
Storage.BlobListOption.prefix(directory)));
} catch (Exception e) {
return Optional.empty();
}
}
get info about a file:
public final Optional<Blob> getFileInfo(#NotNull String bucketFilename) {
try {
return Optional.ofNullable(getStorage().get(BlobId.of(getBucketName(), bucketFilename)));
} catch (Exception e) {
return Optional.empty();
}
}
adding a file:
public final void addFile(#NotNull String localFilename, #NotNull String bucketFilename,
#Nullable ContentType contentType) {
final BlobInfo.Builder builder = BlobInfo.newBuilder(BlobId.of(bucketName, bucketFilename));
if (null != contentType) {
builder.setContentType(contentType.getsValue());
}
final BlobInfo blobInfo = builder.build();
try (final RandomAccessFile raf = new RandomAccessFile(localFilename, "r");
final FileChannel channel = raf.getChannel();
final WriteChannel writer = getStorage().writer(blobInfo)) {
writer.write(channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()));
} catch (Exception e) {
throw new MyCustomException(MessageFormat.format("Error storing {0} to {1}", localFilename,
bucketFilename), e);
}
}
I hope these code snippets and the referenced documentation will get you going, actulally it's not too hard.

Android Share Intent Chooser EMail Client Only

I am trying to create a option for the user to send the file from my application via email only. The file is internal to the application and is accessible via the FileProvider.
This is the contentURI looks like content://packagename.files/files/somefile.ext
Here as you can see that I am giving the user to share the file to PicsArt, Google Drive, OneDrive and EMail.
I am able to share the content to the first three clients successfully as they very specific applications. But when it comes to Email, I need to user to pick the client from the applications he has installed in his mobile.
Here are 2 set of codes I have created:
Code Option 1:
Intent EMail = ShareCompat.IntentBuilder.from(this)
.setType("message/rfc822")
.setSubject("Emailing: File Attached")
.setText("Hello")
.setStream(contentUri)
.setChooserTitle("Send via EMail").getIntent();
startActivity(Intent.createChooser(EMail, "Send via EMail"));
Above code shows me a chooser where there are many applications which can handle the files as shown in the image below.
This one works fine if I select any email client application or any other application.
But the problem with this is that there is an option for the user to select any application, which is not the desired behavior of the application. So, I modified the code as below:
final Intent _Intent = new Intent(Intent.ACTION_SENDTO);
_Intent.setType("text/html");
_Intent.setData(Uri.parse("mailto:"));
_Intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
_Intent.putExtra(Intent.EXTRA_STREAM, contentUri);
_Intent.putExtra(android.content.Intent.EXTRA_SUBJECT,
"Emailing: File Attached");
_Intent.putExtra(android.content.Intent.EXTRA_TEXT,
"Hello");
startActivity(Intent.createChooser(_Intent, "Send via EMail"));
Here is the outcome of the code:
But, now the problem here is that I am not able to send the file from the content provider (FileProvider). The email client shows the message as below after selecting:
It is simply not attaching the file to the email in any client in the above list.
I will be greatfull, if anyone can help me out here. I think that, I have tried all the possible scenarios here, by changing the mime-type, setting content in different manner setting data setting stream etc, but not able to get the desired outcome.
Please let me know in case you need any other details on this.
Thanks again in advance.
You need to write the ContentProvider which will provide the InputStream to the client to which you have passed the ContentUri or may be you can directly provide the file path if its present in the SdCard or the Internal storage because you will need to handle the uri's and pass the InputStream . Note: ExtraStream is best for files that are not in device that is which is to be accessed from internet.
public class SampleContentProvider extends ContentProvider implements ContentProvider.PipeDataWriter<InputStream> {
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//Uri matcher for different
}
/**
* Database specific constant declarations
*/
private SQLiteDatabase db;
#Override
public boolean onCreate() {
return true;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
throw new SQLException("Insert operation not supported for " + uri);
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//condition just for files. You can try something else
if (uri.toString().contains("files")) {
//you get the file name
String lastSegment = uri.getLastPathSegment();
if (projection == null) {
projection = new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE};
}
File file = //Code to read the file as u have the directory, just get the file from the file name obtained from the uri
if (null == file) {
throw new IllegalArgumentException("Unknown File for Uri " + uri);
}
String[] cols = new String[projection.length];
Object[] values = new Object[projection.length];
int i = 0;
for (String col : projection) {
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
cols[i] = OpenableColumns.DISPLAY_NAME;
values[i++] = //file name;
} else if (OpenableColumns.SIZE.equals(col)) {
cols[i] = OpenableColumns.SIZE;
values[i++] = //file size;
}
}
cols = copyOf(cols, i);
values = copyOf(values, i);
final MatrixCursor cursor = new MatrixCursor(cols, 1);
cursor.addRow(values);
return cursor;
}
return super.query(uri, projection, selection, selectionArgs, sortOrder);
}
#Override
public String getType(Uri uri) {
return null;
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return super.delete(uri, selection, selectionArgs);
}
private static String[] copyOf(String[] original, int newLength) {
final String[] result = new String[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
private static Object[] copyOf(Object[] original, int newLength) {
final Object[] result = new Object[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
#Nullable
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File file = //read the file
if (file != null) {
try {
StrictMode.ThreadPolicy tp = StrictMode.ThreadPolicy.LAX;
StrictMode.setThreadPolicy(tp);
InputStream in = //Code to get the inputstream;
// Start a new thread that pipes the stream data back to the caller.
return openPipeHelper(uri, null, null, in, this);
} catch (IOException e) {
FileNotFoundException fnf = new FileNotFoundException("Unable to open " + uri);
throw fnf;
}
}
throw new IllegalArgumentException("Unknown URI " + uri);
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return super.update(uri, values, selection, selectionArgs);
}
#Override
public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
Bundle opts, InputStream args) {
// Transfer data from the asset to the pipe the client is reading.
byte[] buffer = new byte[8192];
int n;
FileOutputStream fout = new FileOutputStream(output.getFileDescriptor());
try {
while ((n = args.read(buffer)) >= 0) {
fout.write(buffer, 0, n);
}
} catch (IOException e) {
} finally {
try {
args.close();
} catch (IOException e) {
}
try {
fout.close();
} catch (IOException e) {
}
}
}
}
I have decided to copy the file from internal app storage to external app storage (not external public storage) and sharing the file from there. I am bit surprised though as the FileProvider is capable of sharing the files from the internal file storage with anything and every thing in the system but fails to do so when I want to filter the Intents which are email clients only.
It was kind of difficult for me to implement a custom provider at beginner's level.
try this snippet.
Intent testIntent = new Intent(Intent.ACTION_VIEW);
Uri data = Uri.parse("mailto:?subject=" + "Feedback" + "&body=" + "Write Feedback here....." + "&to=" + "someone#example.com");
testIntent.setData(data);
startActivity(testIntent);

Send a pdf on an default Email Client - Android Aplication [duplicate]

This question already has an answer here:
Android send mail with PDF file
(1 answer)
Closed 7 years ago.
I need send a PDF file attach on a message, I have a button that calls a function that open a Intent with message, email address and subject filled, but I need that the PDF file has been attached too.
This is my code and I can not find my error, someone can help me please?
public void initializeWebView() {
// Initialize the webview
webView.setResourceClient(new XWalkResourceClient(webView) {
#Override
public boolean shouldOverrideUrlLoading(XWalkView view, String stringUrl) {
if(stringUrl.equals(baseUrl)) {
return false;
}
// mailto links will be handled by the OS.
if (stringUrl.startsWith("mailto:")) {
Uri uri = Uri.parse(stringUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
String fileName = "bouhnik.pdf";
String filePath = (Configuration.getMagazineAssetPath()).toString()+ File.separator + fileName;
Context c = getActivity().getApplicationContext();
File file = null;
FileOutputStream fos = null;
try {
InputStream is = c.getAssets().open(filePath);
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
fos = new FileOutputStream(file);
fos.write(buffer);
fos.close();
} catch (IOException e) {
Log.i("Ferrou",e.toString());
e.printStackTrace();
}
if (!file.exists() || !file.canRead()) {
return false;
}
intent.putExtra(intent.EXTRA_STREAM, file.getPath());
intent.setClassName("com.android.email", "com.android.mail.compose.ComposeActivity");
intent .putExtra(Intent.EXTRA_SUBJECT, "Subject");
WebViewFragment.this.startActivity(Intent.createChooser(intent, "Send email..."));
} else {
try {
URL url = new URL(stringUrl);
// We try to remove the referrer string to avoid passing it to the server in case the URL is an external link.
String referrer = "";
if (url.getQuery() != null) {
Map<String, String> variables = Configuration.splitUrlQueryString(url);
String finalQueryString = "";
for (Map.Entry<String, String> entry : variables.entrySet()) {
if (entry.getKey().equals("referrer")) {
referrer = entry.getValue();
} else {
finalQueryString += entry.getKey() + "=" + entry.getValue() + "&";
}
}
if (!finalQueryString.isEmpty()) {
finalQueryString = "?" + finalQueryString.substring(0, finalQueryString.length() - 1);
}
stringUrl = stringUrl.replace("?" + url.getQuery(), finalQueryString);
}
// Remove referrer from query string
if (!url.getProtocol().equals("file")) {
if (referrer.equals(WebViewFragment.this.getActivity().getString(R.string.url_external_referrer))) {
Uri uri = Uri.parse(stringUrl);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
WebViewFragment.this.startActivity(intent);
} else if (referrer.toLowerCase().equals(WebViewFragment.this.getActivity().getString(R.string.url_baker_referrer))) {
((IssueActivity) WebViewFragment.this.getActivity()).openLinkInModal(stringUrl);
return true;
} else {
return false;
}
} else {
stringUrl = url.getPath().substring(url.getPath().lastIndexOf("/") + 1);
int index = ((IssueActivity) WebViewFragment.this.getActivity()).getJsonBook().getContents().indexOf(stringUrl);
if (index != -1) {
Log.d(this.getClass().toString(), "Index to load: " + index + ", page: " + stringUrl);
((IssueActivity) WebViewFragment.this.getActivity()).getViewPager().setCurrentItem(index);
view.setVisibility(View.GONE);
} else {
// If the file DOES NOT exist, we won't load it.
File htmlFile = new File(url.getPath());
if (htmlFile.exists()) {
return false;
}
}
}
} catch (MalformedURLException | UnsupportedEncodingException ex) {
Log.d(">>>URL_DATA", ex.getMessage());
}
}
return true;
}
});
// Set UI Client (Start stop animations)
webView.setUIClient(new XWalkUIClient(webView) {
#Override
public void onPageLoadStopped(XWalkView view, String url, LoadStatus status) {
if(!url.isEmpty() && status == LoadStatus.FINISHED) {
if(isUserVisible) {
webView.resumeTimers();
}else{
webView.pauseTimers();
}
}
}
});
webView.load(baseUrl, null);
}
Thank's so much for everyone!!
I solve my problem change the type of Intent to:
Intent emailIntent = new Intent(Intent.ACTION_SEND);
Because this is better to email commands, and I define a emailUri where:
emailUri = Uri.fromFile(file.getAbsoluteFile());
because this get a absolute path with a file inside, and when the email client open, it open this file, not a path.
I add a type at my intent but I select the type of my attachment, so I define:
emailIntent.setType("application/pdf");
And finally:
emailIntent.putExtra(Intent.EXTRA_STREAM, uriMail);
startActivity(emailIntent);
It's works now!! Thanks :D
It looks like something is might be going wrong with your file path. Double check it. Then
1 - You need to add the package name of your application with context.getPackageName()
private String path = Environment.getExternalStorageDirectory().getPath() + context.getPackageName() + "books/"+fileName;
2 - Declare the permission inside your AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Categories