Share image in Facebook programmatically from Android app via Intent - java

I have this code working well on Android 4.0.4.
// Create the new Intent using the 'Send' action.
Intent share = new Intent(Intent.ACTION_SEND);
// Set the MIME type
share.setType(type);
// Create the URI from the media
java.io.File media = new java.io.File(mediaPath);
Uri uri = Uri.fromFile(media);
// Add the URI and the caption to the Intent.
share.putExtra(Intent.EXTRA_STREAM, uri);
share.putExtra(Intent.EXTRA_TEXT, caption);
// Broadcast the Intent.
mActivity.startActivity(Intent.createChooser(share, "Share to"));
But on Android 4.4.2 it crashes the Facebook app. Facebook app opens, the image is not shown and the FB app is dead.
In log dump I've noticed this message:
E/JHEAD ( 5850): can't open '/data/data/cz.volten.brili.android.free/files/product_preview_shared.jpg'
V/ContextImpl( 5850): ----- packageName = com.facebook.katana is NOT LOCKED -----
Could the reason be some security restrictions, e.g. The FB app does not have rights to access the image in the application folder even though it is invoked from an intent?
If so, what would be a proper location for an image shared between the apps?
Shall I use something like this: how to share image to facebook via intent

Could the reason be some security restrictions, e.g. The FB app does not have rights to access the image in the application folder even though it is invoked from an intent?
Correct. That image is on internal storage for your app, which is private to your app.
If so, what would be a proper location for an image shared between the apps?
You can stick with internal storage, though you will need to use a FileProvider, perhaps with my LegacyCompatCursorWrapper, to serve the file. This sample app demonstrates this, albeit with a PDF rather than an image.
Or, put the file on external storage.
Shall I use something like this: how to share image to facebook via intent
You could, though that would seem to be overkill, compared to using FileProvider.

This is what I usually use
private void initShareIntent(String type) {
boolean found = false;
Intent share = new Intent(android.content.Intent.ACTION_SEND);
share.setType("image/jpeg");
// gets the list of intents that can be loaded.
List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(share, 0);
if (!resInfo.isEmpty()) {
for (ResolveInfo info : resInfo) {
if (info.activityInfo.packageName.toLowerCase().contains(type) ||
info.activityInfo.name.toLowerCase().contains(type)) {
share.putExtra(Intent.EXTRA_TEXT, "Elevator Express");
share.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(imagePath))); // Optional, just if you wanna share an image.
share.setPackage(info.activityInfo.packageName);
found = true;
break;
}
}
if (!found) {
Toast.makeText(getApplicationContext(), "Facebook does not exist", Toast.LENGTH_SHORT).show();
return;
}
startActivity(Intent.createChooser(share, "Select"));
}
}
and call it like this :
iniShareIntent("face");

This code works for me.....here "updateImage" is my image location.
if (isFacebookExist()) {
if (hashClick.isChecked()) {
SharePhoto sharePhoto = new SharePhoto.Builder()
.setBitmap(updateImage)
.build();
if (ShareDialog.canShow(SharePhotoContent.class)) {
SharePhotoContent content = new SharePhotoContent.Builder()
.addPhoto(sharePhoto)
.setShareHashtag(new ShareHashtag.Builder()
.setHashtag("#HashTag")
.build())
.build();
shareDialog.show(content);
}
} else {
SharePhoto sharePhoto = new SharePhoto.Builder()
.setBitmap(updateImage)
.build();
if (ShareDialog.canShow(SharePhotoContent.class)) {
SharePhotoContent content = new SharePhotoContent.Builder()
.addPhoto(sharePhoto)
.build();
shareDialog.show(content);
}
}
} else {
showToast(" Facebook is not install.");
}
private boolean isFacebookExist() {
PackageManager pm = getPackageManager();
try {
PackageInfo info = pm.getPackageInfo("com.facebook.katana", PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
return true;
}

Related

Get value from deeplink parameter rather than dynamic link in android java

I made an application in which users can send links to their friends. With the link, I added some parameters, So when the user clicks on the link the page with the given parameter will open.
The deep link user sends to his friends is:
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
String shareBody = "https://shareemotion.page.link/mood?mood=Sad&bottomneg=2";
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Subject Here");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody);
context.startActivity(Intent.createChooser(sharingIntent, "Share via"));
While the dynamic link in firebase is:
https://www.myapp.com/?mood=Happy&bottomneg=2
And the code for getting data from link is :
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
#Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
// Get deep link from result (may be null if no link is found)
Uri deepLink = null;
if (pendingDynamicLinkData != null) {
deepLink = pendingDynamicLinkData.getLink();
}
if(deepLink!=null)
{
String mood = deepLink.getQueryParameter("mood");
String bottomId = deepLink.getQueryParameter("bottomneg");
NewActivity(mood, bottomId);
}
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.e( "getDynamicLink", " "+e);
}
});
So Now when I share the link with mood=Sad the app run but at the fetching point it fetches "Happy"(which is present in the dynamic link) but I want to fetch mood=Sad from the link that sends to another user.
Note
mood=Sad will replace by variable, for now, it uses for testing purposes.

Android sharing Intent choser - Filter apps based on accepted file size

In my application I use the following code to share multiple video files to an application of the users choice. This works fine but I would like to dynamically limit the choices to apps that can handle the filesize. For instance gmail limits it attachments to 25MB on my device, while google drive does not.
Is it possible to do this?
If it helps here is my code.
ArrayList<Uri> uris = new ArrayList<>();
for(File f : files.getFiles()){ //files is an array of the files to share
Uri mp4Uri = FileProvider.getUriForFile(context, "com.myfileproviderauthority", f);
uris.add(mp4Uri);
}
sharingIntent.setType("video/mp4");
sharingIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
sharingIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { "toMe#company.com" });
sharingIntent.putExtra(Intent.EXTRA_SUBJECT, "Video");
sharingIntent.putExtra(Intent.EXTRA_TEXT, "Checkout the video!");
sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Intent chooser = Intent.createChooser(sharingIntent, "Share video clip");
List<ResolveInfo> resInfoList =
context.getPackageManager().queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
for(Uri uri : uris){
//TODO: Caution: Calling setFlags() is the only way to securely grant access to your files using temporary access permissions.
// Avoid calling Context.grantUriPermission() method for a file's content URI, since this method grants access that you can only revoke by calling Context.revokeUriPermission().
context.grantUriPermission(packageName, uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
context.startActivity(chooser);

How to programmatically grant the "draw over other apps" permission in android?

How can I programmatically grant the permission in Settings -> Apps -> Draw over other apps in Android? I want to use system alert window but unable to in Android Marshmallow without forcing the user to go through the Settings app and grant the permission first.
You can check and ask for overlay permission to draw over other apps using this
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 0);
}
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 0);
}
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Here's the code for automatic granting the SYSTEM_ALERT_WINDOW permission to the package. To run this code, your Android application must be system (signed by platform keys).
This method is based on the following Android source code files: AppOpsManager.java and DrawOverlayDetails.java, see the method DrawOverlayDetails.setCanDrawOverlay(boolean newState).
#TargetApi(Build.VERSION_CODES.KITKAT)
public static void autoSetOverlayPermission(Context context, String packageName) {
PackageManager packageManager = context.getPackageManager();
int uid = 0;
try {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
uid = applicationInfo.uid;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return;
}
AppOpsManager appOpsManager = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
final int OP_SYSTEM_ALERT_WINDOW = 24;
try {
Class clazz = AppOpsManager.class;
Method method = clazz.getDeclaredMethod("setMode", int.class, int.class, String.class, int.class);
method.invoke(appOpsManager, OP_SYSTEM_ALERT_WINDOW, uid, packageName, AppOpsManager.MODE_ALLOWED);
Log.d(Const.LOG_TAG, "Overlay permission granted to " + packageName);
} catch (Exception e) {
Log.e(Const.LOG_TAG, Log.getStackTraceString(e));
}
}
}
The code has been tested in Headwind MDM project, it successfully grants "Draw over other apps" permission without any user consent to the Headwind Remote application (disclaimer: I'm the project owner of Headwind MDM and Headwind Remote), when Headwind MDM application is signed by platform keys. The code has been tested on Android 10 (LineageOS 17).
Check this question and the answer:
SYSTEM_ALERT_WINDOW - How to get this permission automatically on Android 6.0 and targetSdkVersion 23
"Every app that requests the SYSTEM_ALERT_WINDOW permission and that is installed through the Play Store (version 6.0.5 or higher is required), will have granted the permission automatically."

Start intent "silently"

Following up on this question, is there a way to start an intent in android without prompting the user for anything?
Right now, I am retrieving the image like this:
public void changeImage(View view) {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(
Intent.createChooser(intent, getResources().getString(R.string.select_picture)),
PICK_IMAGE);
}
Then I store the Uri, and when necessary display the image (I actually resize it first, but that doesn't matter):
Uri _uri = Uri.parse(_path);
InputStream imageStream = null;
try {
imageStream = getContentResolver().openInputStream(_uri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Bitmap b = BitmapFactory.decodeStream(imageStream);
iv.setImageBitmap(b);
I would like to retrieve the image data given its Uri by "silently" invoking the intent so as to get the relevant permission. So I would need something like:
Edit:
I tried the setPackage() method. This code has the following behavior:
If the ACTION_VIEW intent is used, the gallery opens and shows the specific image.
If the ACTION_GET_CONTENT intent is used, I get prompted to pick an image from the gallery, even though I supply the specific Uri.
>
Uri _uri = Uri.parse(_path);
InputStream imageStream = null;
Bitmap b = null;
try {
imageStream = getContentResolver().openInputStream(_uri);
b = BitmapFactory.decodeStream(imageStream);
ImageView iv = (ImageView) findViewById(R.id.playerImage);
iv.setImageBitmap(b);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
Intent dummyIntent = new Intent(Intent.ACTION_GET_CONTENT);
//Intent dummyIntent = new Intent(Intent.ACTION_VIEW);
dummyIntent.setDataAndType(_uri,"image/*");
dummyIntent.setPackage("com.google.android.gallery3d");
startActivityForResult(dummyIntent, PICK_IMAGE);
}
Any ideas?
You have different types of Intents, the silent ones are the broadcast and service intents. And by silent I mean that there's nothing visible going on for the user by default (no activity is launched).
An intent will just ask the system: is there something capable of handling it?
If there is, it will pass it to that something (or in case of multiple options will ask the user). And you have no control after that unless what's handling it is your code.
Your problem is actually with Picasa.
When the user pick an image from Picasa you get an Uri. You can use that Uri with the ContentResolver to get the image.
What's actually happening when you do is that a ContentProvider from Picasa will process the Uri and return you the InputStream for the image.
From the other question I understood that you do not immediately get the image, instead you save the Uri in the database and later process it.
Since Picasa use a permission mechanism to provide you the image and since that permission is expired when you try to use the Uri you want to obtain the Uri (along with the permission) again from Picasa without asking the user again.
The problem is that Picasa is in control on that permission and so if Picasa do not provide you with an intent to obtain the permission by knowing the Uri there's nothing you can do.
This is not even a bug in my opinion: you have the permission to access the image when the user decide so, you do not automatically get the permission to access every image from Picasa. A service that "silently" provide you with the permission to access every Picasa image by knowing its Uri would just let you get any image from the user without him knowing. This is probably not what the Picasa developers wants.
The best suggestion I can give you is the same one that you got in the other question: when the user chose the image immediately obtain it and save locally, then use the local Uri to access the image.
Instead of trying to send a "silent" intent, a possible solution could be to save the URI of the file using "Shared Preferences" -- you could save the user and URI as a key/value pair (for example).
When you want to open the file later, it could then be opened without sending an intent.
You should be able to get the "real" uri (one that won't change) in the following way:
This is done for audio file, so use "MediaStore.Images.Media.EXTERNAL_CONTENT_URI" and change other details to match your case.
Pre kit-kat:
String songName = "";
Cursor cursor = getContext().getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Audio.Media.TITLE}, MediaStore.MediaColumns.DATA + " =?", new String[]{uri.toString()}, null);
if (cursor != null && cursor.moveToNext()) {
songName = cursor.getString(0);
}
if (cursor != null) {
cursor.close();
}
return songName;
After Kitkat, there is the change due to the document provider, this code should do the trick:
Cursor cursor = null;
String songName = "";
try {
String[] column = new String[]{MediaStore.Audio.Media.TITLE};
String wholeID = DocumentsContract.getDocumentId(uri);
// Split at colon, use second item in the array
String id = wholeID.split(":")[1];
// where id is equal to
String sel = MediaStore.Images.Media._ID + " =?";
cursor = getContext().getContentResolver().
query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, column, sel, new String[]{id}, null);
if (cursor != null && cursor.moveToNext()) {
songName = cursor.getString(0);
}
}
catch (Exception e){
Logger.log(e);
}
finally {
if (cursor != null) {
cursor.close();
}
}
return songName;
Again, change the details to match your case and save the real uri taken from the cursor you get (probably get this field in your projections : MediaStore.Images.Media.DATA).

How to get the network uri of a file being downloaded from the Download Manager in Android

I am writing an application wherein I want to detect if a download has started and retrieve the URI of the file being downloaded and then cancel the download from the Download Manager. I am doing this so that I can send this URI somewhere else.
The trouble is that I can detect when a download begins by querying the Download Manager, but is there a method or a constant variable in Download Manager from which I can also get the URL of the file being downloaded
Ok its weird answering your own question, but I finally figured out how to do this. There is a DownloadManager class in android.app, which stores a list of all http downloads initiated and their statuses. These can be filtered out based on whether the download is 'RUNNING', 'PENDING', 'PAUSED' and so on.
This list can be read into a cursor and one of the columns of the result is 'COLUMN_URI', which is the url from where the file is being downloaded. A sample code where I have used it is as given below:
public void readDownloadManager() {
DownloadManager.Query query = null;
DownloadManager downloadManager = null;
Cursor c = null;
try {
query = new DownloadManager.Query();
downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);
//Just for testing I initiated my own download from this url. When an http
// reuest for this url is made, since download is taking place, it gets saved in
// the download manager.
Request request = new Request(Uri.parse("http://ocw.mit.edu/courses" +
"/aeronautics-and-astronautics/16-100-aerodynamics-fall-2005" +
"/lecture-notes/16100lectre1_kvm.pdf"));
downloadManager.enqueue(request);
query.setFilterByStatus(DownloadManager.STATUS_PENDING);
c = downloadManager.query(query);
if(true){
int statusColumnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
int urlColumnIndex = c.getColumnIndex(DownloadManager.COLUMN_URI);
long downloadProcessIdColumnNo = c.getColumnIndex(DownloadManager.COLUMN_ID);
Log.d("Column Count", ((Integer)c.getCount()).toString());
if(c.getCount() > 0){
String url="";
c.moveToLast();
if(c.isLast()){
url = c.getString(urlColumnIndex);
downloadManager.remove(downloadProcessIdColumnNo);
Log.d("Count after remove", ((Integer)c.getCount()).toString());
}
Log.d("After", "Stopped Working");
//Here I am sending the url to another activity, where I can work with it.
Intent intent = new Intent(EasyUploadMainMenu.this, EasyUploadActivity.class);
Bundle b = new Bundle();
b.putString("url", url);
intent.putExtras(b);
startActivity(intent);
Log.d("url:", url);
}
}
} catch (NullPointerException ex) {
ex.printStackTrace();
}
}

Categories

Resources