apps choice when sharing files - android

When sharing a file (text file in this case), is it possible to specify an attribute that limits the number of apps to select?
For example, I run an ACTION_SEND by Intent to share a text file through FileProvider interposed. Android then proposes 10 or 12 apps, whereas only one or two manage to exploit the text file: Gmail, to send an email, X-plore or Total Commander to copy it, but not Chomp to send an sms with attachment, nor such or such notepad...
For example we would need an attribute type: "mail" + "storage" and what else!?
My code, to illustrate and that works well on an external file:
File file = new File(Environment.getExternalStorageDirectory() +
File.separator + filepath, filename);
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("text/*");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] {" "});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, »Bla « bla bla);
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,"[" +
file.getName() + "]");
Uri fileUri = FileProvider.getUriForFile(this
, getString(R.string.fileProvAuthorities)
, file);
emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
startActivity(Intent.createChooser(emailIntent, "To share"));

There's nothing like that. But you could create your own chooser showing only the apps you want.

You can do that easily via Flipboard BottomSheet library
Here is a sample:
First, create a string array includes the package names of apps to be shared with, into your strings.xml file like that:
<string-array name="share_apps">
<item>com.facebook.katana</item>
<item>com.facebook.orca</item>
<item>com.twitter.android</item>
<item>com.whatsapp</item>
<item>com.google.android.apps.messaging</item>
<item>com.google.android.talk</item>
<item>com.google.android.gm</item>
<item>com.google.android.apps.plus</item>
<item>com.pinterest</item>
<item>com.instagram.android</item>
<item>com.android.email</item>
<item>com.microsoft.office.outlook</item>
<item>com.yahoo.mobile.client.android.mail</item>
<item>ru.yandex.mail</item>
</string-array>
You can add or remove whatever you want in the list. Then, you can use the bottomsheet like below
IntentPickerSheetView intentPickerSheet = new IntentPickerSheetView(MainActivity.this, shareIntent, "Share with...", new IntentPickerSheetView.OnIntentPickedListener() {
#Override
public void onIntentPicked(IntentPickerSheetView.ActivityInfo activityInfo) {
bottomSheet.dismissSheet();
startActivity(activityInfo.getConcreteIntent(shareIntent));
}
});
// Filter out built in sharing options such as bluetooth and beam.
intentPickerSheet.setFilter(new IntentPickerSheetView.Filter() {
#Override
public boolean include(IntentPickerSheetView.ActivityInfo info) {
String packageName = info.componentName.getPackageName();
ArrayList<String> apps = new ArrayList<String>(Arrays.asList(getResources().getStringArray(R.array.share_apps)));
return apps.contains(new String(packageName));
}
});
// Sort activities in reverse order for no good reason
intentPickerSheet.setSortMethod(new Comparator<IntentPickerSheetView.ActivityInfo>() {
#Override
public int compare(IntentPickerSheetView.ActivityInfo lhs, IntentPickerSheetView.ActivityInfo rhs) {
return rhs.label.compareTo(lhs.label);
}
});
bottomSheet.showWithSheetView(intentPickerSheet);

Related

How to delete a file using the path

I'm building an app that allows the user to save the bitmap or share it without saving it. The 2nd functionality doesn't quite work. I understand that the app needs to save the file to the device before sharing it on a social media app so my idea was, immediately after the file was successfully shared, to automatically delete the file from the device. I've build a delete method trying 2 different approaches and neither have worked:
First approach:
public void deleteFile(String path){
File file = new File(path);
try {
file.getCanonicalFile().delete();
} catch (IOException e) {
e.printStackTrace();
}
}
Second approach:
public void deleteFile(String path){
File file = new File(path);
boolean deleted = file.delete();
}
And I'm calling deleteFile(String) from the sharing method:
public void shareMeme(Bitmap bitmap) {
String path = MediaStore.Images.Media.insertImage(Objects.requireNonNull(getContext()).getContentResolver(), bitmap, "Meme", null);
Uri uri = Uri.parse(path);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.putExtra(Intent.EXTRA_TEXT, "This is my Meme");
getContext().startActivity(Intent.createChooser(share, "Share Your Meme!"));
deleteFile(path);
}
With respect to your stated problem, insertImage() returns a string representation of a Uri. That Uri is not a file. Calling getPath() on it is pointless, and you cannot delete anything based on that path.
More broadly, if your intention is to delete the content right away:
Do not put it in the MediaStore
Do not share it, as you will be deleting it before the other app has a chance to do anything with it
If you want to share it, but then delete it:
Do not put it in the MediaStore
Delete it the next day, or in a few hours, or something, as you have no good way of knowing when the other app is done with the content
To share an image with another app without using the MediaStore:
Save the image to a file in getCacheDir() (call that on a Context, such as an Activity or Service)
Use FileProvider to make that file available to other apps
Beyond that:
Do not use wildcard MIME types in ACTION_SEND. You are the one who is supplying the content to send. You know the actual MIME type. Use it.
Note that there is no requirement for an ACTION_SEND activity to honor both EXTRA_TEXT and EXTRA_STREAM. Most seem to do so, but that behavior is outside of the ACTION_SEND specification.
Note that insertImage() is deprecated on Android Q.
First, you need to check if your file exists, (maybe you set the wrong path?). Then delete the file
File file = new File(path);
if (file.exists()){
if (file.delete()) {
Toast.makeText(this, "file Deleted :" + path, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "file not Deleted :" + path, Toast.LENGTH_SHORT).show();
}
}

Open image from external public directory through intent

Recently I am developing a file sharing application and I created a GridView, where the downloaded files are being shown. From this View, I would like to be able to open the default application through an intent, to open the whole file. Currently I am testing the app with only image files. All the files are downloaded to the external public directory this way:
File externalFolder = new File(Environment.getExternalStorageDirectory(), "My application");
if(!externalFolder.exists()){
externalFolder.mkdir();
}
...
File folder = new File(externalFolder, "Images");
if(!folder.exists()){
folder.mkdir();
}
...
String filename = folder.getAbsolutePath() + "/" + fileToDownload.getName() + "." + fileToDownload.getExtension();
FileOutputStream fos = new FileOutputStream(filename);
When the file is downloaded, I scan it with MediaScannerConnection.scanFile. The scan is successful, the picture is visible among other files in Photos app.
After the file is downloaded, I am able to extract a thumbnail in the adapter of the GridView, so I surely have a valid path to the file.
And where the fun begins: I tried to set an onClickListener to the GridView items in the adapter to be able to open the picture in Photos app this way:
listItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_VIEW);
if(new File(current.getPath()).exists()){
Log.e("path", "valid");
}else{
Log.e("path", "invalid");
}
Log.e("path", Uri.parse("content://"+current.getPath()).toString());
intent.setDataAndType(Uri.parse("content://"+current.getPath()), "image/*");
getContext().startActivity(intent);
}
});
The Intent is created successfully, I get the following in the log:
E/path: valid
E/path: content:///storage/emulated/0/My application/Images/best_picture_ever.jpeg
I have the option to choose among apps to open. When I select the app, it fails to open the image, like when it does not exist. All the 5 applications.
I tested this on my device with Oreo, and on two emulated devices with Nougat and Lollipop, all of them behaves the same way.
What am I doing wrong?
What am I doing wrong?
You are not creating a valid Uri. You cannot put content:// in front of arbitrary things and have a useful Uri, any more than you can put https:// in front of arbitrary things and have a usable URL.
Use FileProvider to serve up this file.

Permission to my application data

I'm new to android and having a headache trying to do this scenario:
I'm creating a backup of my database, which is exists in a path like this:
String dbPath = "data/data/" + c.getPackageName() + "/databases/" + DatabaseHelper.DATABASE_NAME;
I'm copying current database file to another in same directory with a custom extension, something like this:
String backupName = "data/data/" + a.getBaseContext().getPackageName() + "/databases/MyDB_" + MyTools.GenerateDateNow() + ".mydb";
then i use a recycler view and an adapter to loop through files inside the folder, and get my custom created files
i also have a button inside the recycler view, which will restore the created backup.
everything up till now is alright and works fine.
what i want to do next is the problem:
i need to share these backups with other people ( if user decides to ), and to do that, i have used Intents to send a user selected backup file to all apps which work with files ( gmail, whatsapp, ...):
holder.btnShare.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
Intent myIntent = new Intent(Intent.ACTION_SEND);
myIntent.setType("file/*");
Uri fileUri = Uri.fromFile(file);
myIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
Intent chooserIntent = Intent.createChooser(myIntent, "Share With...");
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
chooserIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
v.getContext().startActivity(chooserIntent);
} catch (Exception e) {
e.printStackTrace();
}
}
});
the selected file will be picked and sent to the chooserIntent, but it seems like the chooserIntent has no permission to access it, i guess it's something simple that I'm missing

Saved file without extension. How to re-gain its original MIME Type?

I have a function in my app where the user can copy images to the app. The app will rename the added files to numbers, counting upwards, but without any file extension.
So basically if the user adds beach.jpg it saves a copy of the file to to the path/filename: "/gallery/1"
I'm saving an entry to the sqlite database which contains the original file name (among some other infos).
I've made a sharing function so the user can select the image from the grid gallery and then share it. The problem I now have is that when I'm reading the "gallery/1" file for sharing the MIME type is null (because there's no file name extension).
So my idea was to read the file and save a copy with the original file name to some temporary folder inside my app and then share that copied file.
The problem there is that the MIME type of the file is "application/octet-stream" and not "image/jpeg".
How can I re-gain the proper MIME type of the file? I could not figure out how to achieve this.
The problem now is that the apps I can select to send the image to is very limited because of the "octet-stream" MIME type. Whatsapp etc. is not selectable from the list.
My current code (snippets) look like this:
private void shareFile() {
ShareCompat.IntentBuilder shareIntent = ShareCompat.IntentBuilder.from(activity);
List<Uri> uris = getSelectedImagesAllGalleries();
Log.d(TAG, "shareFile() uris size: " + uris.size());
for (Uri uri : uris) {
Log.d(TAG, "shareFile() URI: " + uri.getPath());
Log.d(TAG, "shareFile() URI TYPE: " + activity.getContentResolver().getType(uri)); // is now always "application/octet-stream"
shareIntent.setType(activity.getContentResolver().getType(uri));
shareIntent.addStream(uri);
}
startActivity(shareIntent.getIntent());
}
private List<Uri> getSelectedImagesAllGalleries() {
List<Uri> selectedImages = new ArrayList<>();
selectedImages.addAll(getSelectedImages());
return selectedImages;
}
private List<Uri> getSelectedImages() {
List<Uri> uriList = new ArrayList<>();
for (Media media : mediaList) {
Log.d(TAG, "getSelectedImages(): " + media.getOriginalFileName() + " / " + media.isSelected());
// Here I create a copy of the file using its original file name incl. its file extension
File fileToShare = mediaTools.createTemporaryFileForSharing(media, activity);
if (fileToShare.exists() && media.isSelected()) {
Uri contentUri = FileProvider.getUriForFile(activity, activity.getPackageName(), fileToShare);
uriList.add(contentUri);
}
}
return uriList;
}
Thanks for any help in advance.
You would need inspect the file header bytes and compare the Magic Bytes in the header of the file to the known values for images to figure out what type it is.
Here is an answer to another similar question with a method of how to do this: Checking File Signature

How to write database to CSV (Android)

I have worked out how to fire an e-mail and fill it with information from a database query, but I would like to instead write this to a CSV file so I can use excel to format (maybe attached to an e-mail if this is at all possible).
Below is my code which creates the text to send to an e-mail:
private void exportRedRiskItemsToEmail(){
recipient = email;
subject.append(inspectionRef).append(" - Locators Rack Inspection Report (Red Risk Items)");
message.append("Dear ").append(contactFirstName).append(",\r\n");
message.append("\r\n");
message.append("Following todays inspection, below is a list of locations which are noted as Red Risk:\r\n");
final Cursor areasCursor = (Cursor) rmDbHelper.fetchAllAreasForInspection(inspectionId);
if(areasCursor.moveToFirst()){
do{
areaId = areasCursor.getLong(areasCursor.getColumnIndex(RMDbAdapter.AREA_ID));
areaNumber = RMUtilities.notEmpty(areasCursor.getString(areasCursor.getColumnIndex(RMDbAdapter.AREA_NUMBER)), "number unknown");
areaRef = RMUtilities.notEmpty(areasCursor.getString(areasCursor.getColumnIndex(RMDbAdapter.AREA_REF)), "");
if (areaRef != ""){
areaRef = " (" + areaRef + ")";
}
message.append("______________________________________________________").append("\r\n");
message.append("\r\n");
message.append("Area ").append(areaNumber).append(areaRef).append("\r\n");
message.append("\r\n");
}
while (areasCursor.moveToNext());
}
areasCursor.close();
message.append("______________________________________________________").append("\r\n");
message.append("\r\n");
message.append("Please fully off-load above locations (for uprights, please off-load bays either side). \r\n");
message.append("\r\n");
message.append("We will follow up with our full report and quotation for repairs shortly. \r\n");
message.append("\r\n");
message.append("In the meantime, if you have any queries, please give me a call on 07970 088845. \r\n");
message.append("\r\n");
message.append("Kind regards, \r\n");
message.append("\r\n");
message.append(inspector).append("\r\n");
sendEmail(recipient, subject.toString(), message.toString());
}
Here is the code which fires the e-mail:
private void sendEmail(String recipient, String subject, String message) {
try {
final Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("plain/text");
emailIntent.setType("message/rfc822");
// emailIntent.setType("high/priority"); //This didn't work - any way to do this though!?
if (recipient != null) emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]{recipient});
if (subject != null) emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
if (message != null) emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
} catch (ActivityNotFoundException e) {
// cannot send email for some reason
}
}
Any pointers much appreciated.
You may want to look at the OpenCSV library. It is free, open source, and easy to use. It does not support directly dumping from SQLite DBs, but it should be pretty straight forward to write the neccessary code to implement this. OpenCSV does support writing Lists of objects to CSV, so if you can get your data out of the sqlite DB into a List you will be good to go.
Also here is a post on the topic.
OpenCSV will write to a file as well. This file then can be attached to an email, or used however you wish(uploaded to google drive, transfered to computer via usb,etc.)
CSVWriter writer = new CSVWriter(new FileWriter("yourfile.csv"));
// feed in your array (or convert your data to an array)
writer.write(entries);
writer.close();
This is just a snippet and I'm working from memory write now so it may be a bit off. You may also lookup writing files in android. You can either store this on the external memory or in your application memory. If you want to access the file from outside your app you will need to place it in external memory.
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sendIntent.putExtra(Intent.EXTRA_SUBJECT,"subject line");
sendIntent.putExtra(Intent.EXTRA_TEXT,"Body of email");
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File("yourfile.csv")));
sendIntent.setType("vnd.android.cursor.dir/email");
startActivity(Intent.createChooser(sendIntent,"Email:"));

Categories

Resources