In my app I have a menu item that I want to open up the user's preferred media player, only interested in audio. Ideally the first time the user chooses this item it would choose the only media player on the phone if they only have one installed, or present them with a list if they have more than one. I would then save off their choice so next time it would open that one.
As I understand it, Android has no default media player. I have the original Droid and it has a media player, but I understand other carriers use their own, or the user can uninstall the standard one.
I have tried a few things but can't get anything to work.
I tried this code, which is supposed to get a list of packages that support the intent. It works for some things like "application/pdf" and "video/*". When I try it with "audio/*" I get an empty list, even though I have both the stock Android media player and MixZing installed. Have also tried it with "media/*" and get nothing.
PackageManager packageManager = getPackageManager();
Intent testIntent = new Intent(Intent.ACTION_VIEW);
testIntent.setType("audio/*");
List list = packageManager.queryIntentActivities(testIntent, 0);
I have seen this code which works and opens an audio file with the default player, however I don't want to open a file, I just want to open up the audio app as if the user opened it from the application drawer.
Intent i = new Intent(Intent.ACTION_VIEW);
Uri u = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"1");
i.setData(u);
startActivity(i);
The only other thing I can think to do is go out and get the package names of the most popular media players and hard code them in and search the phone to see which ones are installed, but this doesn't seem like the best approach. I don't understand why the first bit of code doesn't work. Maybe the intent filters aren't properly set for those apps or I am using the wrong code or mime types.
OK here is how I ask the OS for a list of installed audio players.
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"1");
intent.setData(uri);
List<ResolveInfo> playerList;
playerList = packageManager.queryIntentActivities(intent, 0);
playerList will then be populated with ResolveInfo objects for every app on the phone that claims to handle audio. From the ResolveInfo you can get things like package name to launch the activity, you can get icons and app names. I use it to populate a custom dialog that looks and acts like the stock Android dialog for choosing an activity to launch. I also have the option for the user to set the chosen app as default.
This isn't perfect. You are at the mercy of the OS and the app's intent filters. I have tried this with a few phones and the following apps show up properly: Stock media player for original Droid, stock media player for HTC phones, doubleTwist and MixZing. However, I installed Zimly and it does not show up. I suspect their intent filters aren't set up for audio.
I was also worried there may be cases when MediaStore.Audio.Media.INTERNAL_CONTENT_URI does not have any audio. I have tried this code immediately following a reboot and it works. I have not tried it on a phone with no user media installed.
/*
For my samsung galaxy s4 running android version 5.0, none of the solutions above works but i found another solution. Works perfectly. Here is the code for someone who might need help.
*/
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File("content://media/internal/audio/media")), "audio/*");
List activities = this.getPackageManager().queryIntentActivities(intent, 0);
List activities; activities = getPackageManager().queryIntentActivities(intent, 0); int count = activities.size(); String a = Integer.toString(count);
//set alert for executing the task
AlertDialog.Builder builder1 = new AlertDialog.Builder(this);
builder1.setMessage("Handle: " + a);
builder1.setCancelable(true);
AlertDialog alert11 = builder1.create();
alert11.show();
Intent resolveIntent = new Intent(Intent.ACTION_VIEW);
if (type.startsWith("audio/*")) {
Uri uri = Uri.withAppendedPath(
MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "1");
resolveIntent.setDataAndType(uri, type);
} else if (type.startsWith("video/*")) {
Uri uri = Uri.withAppendedPath(
MediaStore.Video.Media.INTERNAL_CONTENT_URI, "1");
resolveIntent.setDataAndType(uri, type);
} else if (type.startsWith("application/pdf")) {
resolveIntent.setType(type);
}
List<ResolveInfo> pkgAppsList = mContext.getPackageManager().queryIntentActivities(resolveIntent, PackageManager.GET_RESOLVED_FILTER);
Here is my solution, If you want get a list of "video/" or "audio/" APPs, you should use "setDataAndType"; But if you want to get a list of "text/*" or "pdf" APPs, you can use "setType".
/**
* get apps supporting video playing
* #return
*/
private List<String> queryPlayerPackageNameLst()
{
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(video), "video/*");
PackageManager pm = getPackageManager();
List<ResolveInfo> infos = pm.queryIntentActivities(intent,PackageManager.MATCH_UNINSTALLED_PACKAGES);
List<String> packageNameLst = null;
if (infos != null && infos.size() > 0)
{
packageNameLst = new ArrayList<String>(infos.size());
for(int i=0; i<infos.size(); i++)
{
ResolveInfo info = infos.get(i);
packageNameLst.add(info.activityInfo.packageName);
//info.activityInfo.name
}
}
return packageNameLst;
}
Intent resolveIntent = new Intent(Intent.ACTION_VIEW);
resolveIntent.setDataAndType(Uri.parse("file://"), MIMEType);
List<ResolveInfo> pkgAppsList = mContext.getPackageManager().queryIntentActivities(resolveIntent, PackageManager.MATCH_DEFAULT_ONLY| PackageManager.GET_RESOLVED_FILTER);
I think this is better!
For my Samsung Galaxy S4 running Android version 5.0, none of the solutions above work, but I found another solution. Works perfectly.
Here is the code for someone who might need help.
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File("content://media/internal/audio/media")), "audio/*");
List<ResolveInfo> activities = this.getPackageManager().queryIntentActivities(intent, 0);
// content://media/internal/audio/media
//List<ResolveInfo> activities;
activities = getPackageManager().queryIntentActivities(intent, 0);
int count = activities.size();
String a = Integer.toString(count);
//set alert for executing the task
AlertDialog.Builder builder1 = new AlertDialog.Builder(this);
builder1.setMessage("Handle: " + a);
builder1.setCancelable(true);
AlertDialog alert11 = builder1.create();
alert11.show();
Related
Background
I wish to show the native intent-chooser, while having the ability to customize it a bit.
For this, I've found the next StackOverflow thread:
How to customize share intent in Android?
The problem
Thing is, when I use the suggested code on Android 5.x and below, everything seems to be fine, but when I use it on Android 6.0.1 (tested on Nexus 5 and emulator, when having multiple apps to share content with) , I get empty cells and sometimes even empty app names, as such:
This doesn't appear when using the non-customized intent-chooser:
startActivity(Intent.createChooser(intent, "default chooser"));
The code
Seeing the solutions, I've created the next code:
private void test(Intent shareIntent) {
List<Intent> targetedShareIntents = new ArrayList<>();
final List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(shareIntent, 0);
if (!resInfo.isEmpty()) {
for (ResolveInfo resolveInfo : resInfo) {
Intent targetedShareIntent = new Intent(shareIntent);
targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
targetedShareIntents.add(targetedShareIntent);
}
Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), "Select app to share");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[targetedShareIntents.size()]));
startActivity(chooserIntent);
}
}
private void prepareIntentToShare(Intent intent) {
intent.setAction(android.content.Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM, mUri);
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "title");
intent.putExtra(android.content.Intent.EXTRA_TEXT, "body");
}
And the way to test it:
Intent intent = new Intent();
prepareIntentToShare(intent);
test(intent);
What I've tried
I've tried to change various things in the intents, but without any luck. I've also tried to find out what is the order that the intents are supposed to be in (because maybe it's important), but I didn't find it.
Lastly, I've decided to post about it to Google, assuming this is a bug:
https://code.google.com/p/android/issues/detail?id=202693
The questions
Why does it occur? Can I fix it somehow, while still using the native intent-chooser? How come it occurs only on Android 6 and above?
How can I put the correct name for each item there, as I see "twitter" twice, for example, yet other apps do show the correct name (like the one of the qr-code-scanner)?
Is it possible to keep the native behavior of how to order of apps, as shown using the simple way of showing the intent-chooser? Maybe get the list of apps the way they are supposed to be ordered ?
I spend some time to read ChooserActivity and ResolverActivity and kind of solving thoese problems.
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivities(intent, 0);
if (resolveInfos != null && !resolveInfos.isEmpty()) {
List<Intent> targetIntents = new ArrayList<>();
for (ResolveInfo resolveInfo : resolveInfos) {
ActivityInfo activityInfo = resolveInfo.activityInfo;
// remove activities which packageName contains 'ttt' for example
if (activityInfo.packageName.contains("ttt")) {
continue;
}
Intent targetIntent = new Intent(Intent.ACTION_SEND);
targetIntent.setType("text/plain");
targetIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.setting_share_app_subject));
targetIntent.putExtra(Intent.EXTRA_TEXT, context.getString(R.string.setting_share_app_body));
targetIntent.setPackage(activityInfo.packageName);
targetIntent.setComponent(new ComponentName(activityInfo.packageName, activityInfo.name));
// wrap with LabeledIntent to show correct name and icon
LabeledIntent labeledIntent = new LabeledIntent(targetIntent, activityInfo.packageName, resolveInfo.labelRes, resolveInfo.icon);
// add filtered intent to a list
targetIntents.add(labeledIntent);
}
Intent chooserIntent;
// deal with M list seperate problem
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// create chooser with empty intent in M could fix the empty cells problem
chooserIntent = Intent.createChooser(new Intent(), context.getString(R.string.setting_share_app_title));
} else {
// create chooser with one target intent below M
chooserIntent = Intent.createChooser(targetIntents.remove(0), context.getString(R.string.setting_share_app_title));
}
if (chooserIntent == null) {
return;
}
// add initial intents
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[targetIntents.size()]));
try {
context.startActivity(chooserIntent);
} catch (ActivityNotFoundException e) {
Logger.e(TAG, e, e);
}
}
EDIT: seems on Android Q (10 - API 29) this is broken, and will show just up to 2 items instead of all of them. Asked about this again here.
EDIT: made a sample of choosing which items to exclude from sharing, here, which should work for all Android versions.
I think the question says it all: Is it possible to get a list of all apps which are currently playing sounds? Maybe over the SoundManager app or something else?
You can get your list by using this :
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"1");
intent.setData(uri);
intent.setType("audio/*");
List<ResolveInfo> apps = getPackageManager().queryIntentActivities(intent, 0);
for (ResolveInfo rInfo : apps) {
//process list here
}
I had problems attaching a video file (it's always smaller than 100KB) via mms intent. Though this works perfectly well on karbonn A21 (ICS 4.0.4), the attachment fails on HTC one V (ICS 4.0.3) and lg-p920 (2.2.2). I get a toast like "unable to attach video to message"
This is the code I have
Uri uri = Uri.fromFile(videoFile);
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("video/3gp");
sendIntent.setClassName("com.android.mms", "com.android.mms.ui.ComposeMessageActivity");
sendIntent.putExtra("sms_body", "some text here");
sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(sendIntent);
Any hints/clues/pointers on what I could do would be helpful.
this problem cause because in the video/image need to add to galley:
Read code in
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/2.3.3_r1/com/android/mms/ui/ComposeMessageActivity.java
focus in addAttachment part, I saw
String path = c.getString(c.getColumnIndexOrThrow(Images.Media.DATA));
mSrc = path.substring(path.lastIndexOf('/') + 1);
mContentType = c.getString(c.getColumnIndexOrThrow(
mages.Media.MIME_TYPE));
if (TextUtils.isEmpty(mContentType)) {
throw new MmsException("Type of media is unknown.");
})
We saw the message throwed not clear and cause misunderstand.
To solve this, you need to add the file to gallery, pass the URI get from contentResolver.insert to Intent with key Intent.EXTRA_STREAM
One more experience of my when using MMS, the default Activity class use to send MMS change among devices and manufatories, so the setClass com.android.mms.ui.ComposeMessageActivity not always right, it can cause ActivityNotFoundException. When it happends, you must call setPackge("com.android.mms") and remove setClass call.
Hope it help
My approach so far has been to let the user share the video via gmail, youtube and such along with an option to share via mms
ContentValues content = new ContentValues(4);
content.put(Video.VideoColumns.TITLE, "Cool Video");
content.put(Video.VideoColumns.DATE_ADDED,
System.currentTimeMillis() / 1000);
content.put(Video.Media.MIME_TYPE, "video/3gp");
content.put(MediaStore.Video.Media.DATA, videoFile.getAbsolutePath());
ContentResolver resolver = parentActivity.get().getContentResolver();
//I use two URI's. One for the intent with mms(MMSUri) and the
//other(ShareURi) is for sharing video with other social apps like
//gmail, youtube, facebook etc.
Uri ShareUri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,content);
Uri MMSUri = Uri.fromFile(videoFile);
List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(sendIntent, 0);
if(!resInfo.isEmpty())
{
for (ResolveInfo resolveInfo : resInfo)
{
String packageName = resolveInfo.activityInfo.packageName;
Intent targetIntent = new Intent(Intent.ACTION_SEND);
targetIntent.setType("video/3gp");
targetIntent.setPackage(packageName);
if(packageName.contains("mms"))
{
targetIntent.putExtra("sms_body", "Some text here");
targetIntent.putExtra(Intent.EXTRA_STREAM, MMSUri);
}
else
{
targetIntent.putExtra(Intent.EXTRA_SUBJECT, "I can has videos?");
targetIntent.putExtra(Intent.EXTRA_TITLE, "Some title here");
targetIntent.putExtra(Intent.EXTRA_TEXT,"You have gots to watch this");
targetIntent.putExtra(Intent.EXTRA_STREAM, ShareUri);
}
targetedIntents.add(targetIntent);
}
Intent chooserIntent = Intent.createChooser(targetedIntents.remove(0), "Select app to share");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[]{}));
startActivity(chooserIntent);
return;
}
Toast.makeToast(this, "No intents found for this action", Toast.LENGTH_SHORT, Gravity.CENTER).show();
I try to populate my own target intents for the Intent.createChooser knowing only these would work in attaching/uploading my video
EDIT: I wont be accepting my own answer as the right one. I'm most optimistic there's a better one out there
I want to display an activity chooser that shows all apps that can VIEW and/or EDIT some data. Is there an easy way to do this, or do I have to implement my own activity chooser dialog? Or maybe I can just subclass Intent? Thanks.
I found a partial solution by using EXTRA_INITIAL_INTENTS:
Intent viewIntent = new Intent(Intent.ACTION_VIEW);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
viewIntent.setDataAndType(uri, type);
editIntent.setDataAndType(uri, type);
Intent chooserIntent = Intent.createChooser(editIntent, "Open in...");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { viewIntent });
startActivity(chooserIntent);
I say partial because if an app supports both ACTION_VIEW and ACTION_EDIT it will show up twice in the list, one of which will open the file for viewing and the other for editing, and you wouldn't necessarily know which is which. I think a complete solution would require a custom app chooser, as Tim suggested.
EDIT (Complete Solution!):
I found a solution that doesn't involving writing a custom app chooser. In order to differentiate ACTION_EDIT apps from ACTION_VIEW apps, I found a way to append a "(for editing)" string to the labels for one of them (in my case, ACTION_EDIT) by using the line of code Tim provided. In addition, to ensure the appended string doesn't appear to be a part of the app name, I changed the color of it to cyan:
PackageManager pm = kyoPrint.getPackageManager();
Intent viewIntent = new Intent(Intent.ACTION_VIEW);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
viewIntent.setDataAndType(uri, type);
editIntent.setDataAndType(uri, type);
Intent openInChooser = Intent.createChooser(viewIntent, "Open in...");
// Append " (for editing)" to applicable apps, otherwise they will show up twice identically
Spannable forEditing = new SpannableString(" (for editing)");
forEditing.setSpan(new ForegroundColorSpan(Color.CYAN), 0, forEditing.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
List<ResolveInfo> resInfo = pm.queryIntentActivities(editIntent, 0);
Intent[] extraIntents = new Intent[resInfo.size()];
for (int i = 0; i < resInfo.size(); i++) {
// Extract the label, append it, and repackage it in a LabeledIntent
ResolveInfo ri = resInfo.get(i);
String packageName = ri.activityInfo.packageName;
Intent intent = new Intent();
intent.setComponent(new ComponentName(packageName, ri.activityInfo.name));
intent.setAction(Intent.ACTION_EDIT);
intent.setDataAndType(uri, type);
CharSequence label = TextUtils.concat(ri.loadLabel(pm), forEditing);
extraIntents[i] = new LabeledIntent(intent, packageName, label, ri.icon);
}
openInChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents);
startActivity(openInChooser);
EDIT 2: BUG
If there are no activities found by the first intent, NO activities will be displayed, including any found by the second intent. I ended up writing my own chooser. I just populated an ExpandableListView with headings for each type of intent with their respective activities as children (stored as individual LabeledIntents).
depends on what your data is. But in general using with ACTION_VIEW and some data attached you can use an IntentChoooser to populate the list of choices to the user.
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "some data");
startActivity(Intent.createChooser(intent, "Open with"));
Be sure to set your type correctly so that applications will know that you are wanting to open something that they may be able to handle.
EDIT: I think you would have to use a package manager query to get your two lists then combine them into one and make your own activity / dialog that will pop-up and get populated with the data contained in your combined list.
Here is an example making the query:
List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(intent, 0);
so if you make your two Intents and call this twice, passing in each intent you should be able to combine the resulting lists to get your full set of possibilities. Then it is up to to create an activity or dialog to show them with.
I know Android cannot handle PDFs natively. However, the Nexus One (and possibly other phones) come pre-installed with QuickOffice Viewer. How would I determine whether the user has a PDF viewer installed?
Currently, the code to start the PDF download looks pretty simple:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
After the download, the user clicks on the downloaded file to invoke the viewer.
However, if there is no PDF viewer, Android reports "Cannot download. The content is not supported on the phone."
I want to determine if the user will get this message, and if so, direct them to PDF apps in the Android Market.
I have been testing this and found that the following works. First you download the file independently and store it on the device and then you go do this:
File file = new File("/sdcard/download/somepdf.pdf");
PackageManager packageManager = getPackageManager();
Intent testIntent = new Intent(Intent.ACTION_VIEW);
testIntent.setType("application/pdf");
List list = packageManager.queryIntentActivities(testIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() > 0 && file.isFile()) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "application/pdf");
startActivity(intent);
I have tested this on various emulator and a rooted cyanogen phone as well as a HTC Magic. If no pdf renderer is available the list will return zero and nothing will happen.
It seems to be important to set the data type to the pdf mime type to get the correct behaviour.
If you e.g. install droidreader it will react to the intent and display the pdf.
Of course you could do the check before you download the pdf as well depending on your use case or do things like popping up alerts or redirecting do other intents for download or whatever.
Edit: I have since refactored this out into a separate method ..
public static final String MIME_TYPE_PDF = "application/pdf";
/**
* Check if the supplied context can render PDF files via some installed application that reacts to a intent
* with the pdf mime type and viewing action.
*
* #param context
* #return
*/
public static boolean canDisplayPdf(Context context) {
PackageManager packageManager = context.getPackageManager();
Intent testIntent = new Intent(Intent.ACTION_VIEW);
testIntent.setType(MIME_TYPE_PDF);
if (packageManager.queryIntentActivities(testIntent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0) {
return true;
} else {
return false;
}
}
You can query the PackageManager to see if there's a package that can handle your Intent. Here's an example: http://www.curious-creature.org/2008/12/15/android-can-i-use-this-intent/
You will probably use asynch task to download any file.so simple way is Just add below code in post execute() method of asynch task.it will ask for choice.
FileOpener.open(context, file);
and file should contain info about filepath and filename.Example
File file = new File(Environment
.getExternalStoragePublicDirectory("/MDroid")
+ filepath + fileName );
Hope it helps