I have Google Play, Amazon, Samsung, etc stores on a phone. I want to give the user the choice of which market to go to. If I use market, it is going to the Google Play app by default. I want a list of them to appear. Do I have to manually check which are installed and have a custom choice screen? Or can I adjust my current code:
startActivity(new Intent(Intent.ACTION_VIEW,Uri.parse("market://details?id=" + appName)));
I have an example:
// get app which can open fill
public List<Map<String, Object>> getAppOpeners(Intent intent) {
if (intent == null) {
return null;
}
List<Map<String, Object>> appinfos = new ArrayList<Map<String, Object>>();
ResolveInfo app = null;
List<ResolveInfo> mApps = new ArrayList<ResolveInfo>();
PackageManager pm = mContext.getPackageManager();
mApps = pm.queryIntentActivities(intent,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);// get app which can open fill
Iterator<ResolveInfo> it = mApps.iterator();
while (it.hasNext()) {
Map<String, Object> item = new HashMap<String, Object>();
app = it.next();
if (true) {// mContext.getPackageManager().getLaunchIntentForPackage(app.activityInfo.packageName)
// != null
item.put("packname", app.activityInfo.packageName);// packname of app
item.put("appname", app.loadLabel(mContext.getPackageManager()));// name of app
item.put("icon", app.loadIcon(mContext.getPackageManager()));// icon of app
item.put("intent", intent);
appinfos.add(item);
}
}
//appinfos = StringSortUtil.sortString(appinfos, "appname");
return appinfos;
}
The codes is used for screening apps which can open file by mimetype,.It is maybe usefull for you
I am sorry for my bad english
Related
I'm targeting my app to support 30 (R).
I've notice that some apps are missing to choose when calling this:
baseActivity.startActivity(Intent(MediaStore.ACTION_IMAGE_CAPTURE))
When targeting to 29, this code shows several apps to choose before taking the picture:
Native camera app
B612 Camera app
After targeting to 30, the camera app is being opened directly (no option to choose).
I looked in the android 11 changes but didn't see anything special.
Is there anything that needs to be change in my side?
Thanks for reading/helping
Once your targetSdkVersion reaches 30, ACTION_IMAGE_CAPTURE will only display pre-installed camera apps, not user-installed apps.
I've found a workaround;
TL;DR: Read the AndroidManifest.xml's of the apps yourself to find the camera apps.
Note: This may result in your app being banned from the store.
Step 1:
Using the PackageManager, create a list of all apps that have the Camera-permission granted.
public static List<PackageInfo> getPackageInfosWithCameraPermission(Context context){
//Get a list of compatible apps
PackageManager pm = context.getPackageManager();
List<PackageInfo> installedPackages = pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
ArrayList<PackageInfo> cameraPermissionPackages = new ArrayList<PackageInfo>();
//filter out only camera apps
for (PackageInfo somePackage : installedPackages) {
//- A camera app should have the Camera permission
boolean hasCameraPermission = false;
if (somePackage.requestedPermissions == null || somePackage.requestedPermissions.length == 0) {
continue;
}
for (String requestPermission : somePackage.requestedPermissions) {
if (requestPermission.equals(Manifest.permission.CAMERA)) {
//Ask for Camera permission, now see if it's granted.
if (pm.checkPermission(Manifest.permission.CAMERA, somePackage.packageName) == PackageManager.PERMISSION_GRANTED) {
hasCameraPermission = true;
break;
}
}
}
if (hasCameraPermission) {
cameraPermissionPackages.add(somePackage);
}
}
return cameraPermissionPackages;
}
Step 2: Get the AndroidManifest from the APK-file (from PackageInfo)
public static Document readAndroidManifestFromPackageInfo(PackageInfo packageInfo) {
File publicSourceDir = new File(packageInfo.applicationInfo.publicSourceDir);
//Get AndroidManifest.xml from APK
ZipFile apkZipFile = new ZipFile(apkFile, ZipFile.OPEN_READ);
ZipEntry manifestEntry = apkZipFile.getEntry("AndroidManifest.xml");
InputStream manifestInputStream = apkZipFile.getInputStream(manifestEntry);
try {
Document doc = new CompressedXmlParser().parseDOM(manifestInputStream);
return doc;
} catch (Exception e) {
throw new IOException("Error reading AndroidManifest", e);
}
}
Step 3: Read the AndroidManifest to find the Activities with the correct IntentFilter(s)
public static List<ComponentName> getCameraComponentNamesFromDocument(Document doc) {
#SuppressLint("InlinedApi")
String[] correctActions = {MediaStore.ACTION_IMAGE_CAPTURE, MediaStore.ACTION_IMAGE_CAPTURE_SECURE, MediaStore.ACTION_VIDEO_CAPTURE};
ArrayList<ComponentName> componentNames = new ArrayList<ComponentName>();
Element manifestElement = (Element) doc.getElementsByTagName("manifest").item(0);
String packageName = manifestElement.getAttribute("package");
Element applicationElement = (Element) manifestElement.getElementsByTagName("application").item(0);
NodeList activities = applicationElement.getElementsByTagName("activity");
for (int i = 0; i < activities.getLength(); i++) {
Element activityElement = (Element) activities.item(i);
String activityName = activityElement.getAttribute("android:name");
NodeList intentFiltersList = activityElement.getElementsByTagName("intent-filter");
for (int j = 0; j < intentFiltersList.getLength(); j++) {
Element intentFilterElement = (Element) intentFiltersList.item(j);
NodeList actionsList = intentFilterElement.getElementsByTagName("action");
for (int k = 0; k < actionsList.getLength(); k++) {
Element actionElement = (Element) actionsList.item(k);
String actionName = actionElement.getAttribute("android:name");
for (String correctAction : correctActions) {
if (actionName.equals(correctAction)) {
//this activity has an intent filter with a correct action, add this to the list.
componentNames.add(new ComponentName(packageName, activityName));
}
}
}
}
}
return componentNames;
}
Step 4: Create a list of all Camera Apps
List<> cameraApps = new ArrayList<>();
for (PackageInfo somePackage : cameraPermissionPackages) {
Document doc = readAndroidManifestFromPackageInfo(somePackage);
List<ComponentName> componentNames = getCameraComponentNamesFromDocument(doc);
if (componentNames.size() == 0) {
continue; //This is not a Camera app
}
cameraApps.add(cameraApp);
}
Step 5: Present list of Camera Apps to the user.
Just create a dialog or something.
I've worked it out into a library:
https://github.com/frankkienl/Camera11
What I do
I'm retrieving a list of all installed apps like following (for a launcher like app):
PackageManager pm = context.getPackageManager();
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null)
.addCategory(Intent.CATEGORY_LAUNCHER);
// flag PackageManager.GET_RESOLVED_FILTER will set ResolveInfo.filter (not documented, but tested)
// many examples use flag == 0, but then the filter is always null
List<ResolveInfo> ril = pm.queryIntentActivities(mainIntent, PackageManager.GET_RESOLVED_FILTER);
if (ril != null) {
for (ResolveInfo ri : ril) {
// I need this info!
// ri.isDefault is always false...
boolean isDefault = ri.isDefault || ril.filter.hasCategory(Intent.CATEGORY_DEFAULT);
}
}
So far, this works. But some apps like samsungs phone app do not respect that they should only flag one activity with Intent.CATEGORY_DEFAULT. Here's what the categories for the two activities in samsung's app look like:
ActivityInfo{cd007eb com.android.contacts.activities.PeopleActivity}
filter:
0 = "android.intent.category.DEFAULT"
1 = "android.intent.category.LAUNCHER"
2 = "android.intent.category.BROWSABLE"
3 = "android.intent.category.APP_CONTACTS"
ActivityInfo{3e9ba8d com.android.dialer.DialtactsActivity}
filter:
0 = "android.intent.category.DEFAULT"
1 = "android.intent.category.LAUNCHER"
2 = "android.intent.category.BROWSABLE"
Requirements
I need a list of all activities a launcher like app like mine can launch
I need the info if a ResolveInfo is the one that is launched if I use the apps package name only like if I use the intent retrieved by following command: pm.getLaunchIntentForPackage(packageName). This one is what I call the default launch intent in this post
Question
How can I find out reliably if a ResolveInfo belongs to the default launch intent?
You can use the queryIntentActivities as follows:
PackageManager pm = context.getPackageManager();
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null)
.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> ril = pm.queryIntentActivities(mainIntent, PackageManager.MATCH_DEFAULT_ONLY);
Based on the documentation for MATCH_DEFAULT_ONLY:
Resolution and querying flag: if set, only filters that support the
CATEGORY_DEFAULT will be considered for matching. This is a synonym
for including the CATEGORY_DEFAULT in your supplied Intent.
SOLUTION
This solution is useable as long as you assume that only some apps register multiple default activities.
get all ResolveInfos like in my question
create a HashMap and insert package names and number of entries in list
use the HashMap to determine if you even need to check if the ResolveInfo belongs to the default activity or not
if you need to check, make the slow check for this ResolveInfo
Code
List<ResolveInfo> ril = ...;
HashMap<String, Integer> packageCountMap = getPackageCountMap(uniqueRil);
for (ResolveInfo ri :: ril) {
boolean isDefault = isDefaultIntentForPhoneApp(packageCountMap, ri);
// use this info...
}
Helper function:
public static HashMap<String, Integer> getPackageCountMap(List<ResolveInfo> ril) {
HashMap<String, Integer> map = new HashMap<>();
ResolveInfo ri;
Integer count;
if (ril != null) {
for (int i = 0; i < ril.size(); i++) {
ri = ril.get(i);
count = map.get(ri.activityInfo.packageName);
if (count == null) {
count = 1;
} else {
count++;
}
map.put(ri.activityInfo.packageName, count);
}
}
return map;
}
public static boolean isDefaultIntentForPhoneApp(PackageManager pm, HashMap<String, Integer> packageCountMap, ResolveInfo ri) {
boolean isDefault;
// count how often the package name exists => 1x => then the activity must be the default one, no need to do any check
Integer count = packageCountMap.get(ri.activityInfo.packageName);
if (count == null || count <= 1) {
isDefault = true;
}
// otherwise get default activity and compare it
else {
Intent i = pm.getLaunchIntentForPackage(packageName);
ComponentName cn = i.resolveActivity(pm);
isDefault = cn != null && cn.getClassName().equals(ri.activityInfo.name);
}
return isDefault;
}
After starting default calculator and hitting back navigation, the user is returned to a different activity rather than the calling activity. When the user clicks the calculator button, the devices default calculator opens. Upon invoking onBackPressed, the user is returned to a different activity within the app that they should not be going to. How can I make sure that the user is returned to the calling activity? Here's my code and what I have tried:
public static void openByName(Context context, String name){
ArrayList<HashMap<String,Object>> items =new ArrayList<HashMap<String,Object>>();
final PackageManager pm = context.getPackageManager();
List<PackageInfo> packs = pm.getInstalledPackages(0);
for (PackageInfo pi : packs) {
if( pi.packageName.toLowerCase().contains(name)){
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("appName", pi.applicationInfo.loadLabel(pm));
map.put("packageName", pi.packageName);
items.add(map);
}
}
if(items.size()>=1){
String packageName = (String) items.get(0).get("packageName");
Log.d(TAG, "PackageName: " + packageName);
Intent i = pm.getLaunchIntentForPackage(packageName);
if (i != null)
context.startActivity(i);
}
else{
// Application not found
Toaster.make(context, name + " not found");
}
}
After different method attempts, I had to use this way because it otherwise wouldn't work on some devices.
I programmed an app that can send a message to twitter with an image attached. It works! I tested it on several devices and asked other people to do the same. It even works for a Direct Message when a twitter friend is selected. However, it does not work when "Direct Message" is selected. This forces the user to select a friend directly instead of selecting him via "Direct Message" (which is really strange) otherwise the picture is not attached. Just have a look at the screenshot:
Here is my Xamarin Android programming code. Let me know how to fix it. Currently, all options work, even selecting my friend but not "Direct Message". I also need to tell that I do not have any issue with the twitter text I expect to see in the tweet.
public bool TweetImage(Bitmap imageToTweet)
{
var messageIntent = context.FindMessageIntent(this.twitterConstants.PackageName);
if (messageIntent == null)
{
return false;
}
string outputFileBMP = SaveBitmap(imageToTweet);
context.Tweet(messageIntent, outputFileBMP, this.twitterConstants.DefaultTwitterText, this.twitterConstants.ChooserMessage);
return true;
}
and
public static Intent FindMessageIntent(this ContextWrapper contextWrapper, params string[] packageNames)
{
Intent wantedIntent = new Intent();
wantedIntent.SetType("text/plain");
var resolveInfos = contextWrapper.PackageManager.QueryIntentActivities(wantedIntent, PackageInfoFlags.MatchDefaultOnly);
var result = (from r in resolveInfos
from p in packageNames
where p == r.ActivityInfo.PackageName
select p).FirstOrDefault();
if (result != null)
{
wantedIntent.SetPackage(result);
return wantedIntent;
}
return null;
}
and
public static void Tweet(this ContextWrapper contextWrapper, Intent messageIntent, string filePath = null, string message = null, string chooserMessage = null)
{
if (filePath != null)
{
using (var file = new Java.IO.File(filePath))
{
messageIntent.PutExtra(Intent.ExtraStream, Android.Net.Uri.FromFile(file));
}
}
if (message != null)
{
messageIntent.PutExtra(Intent.ExtraText, message);
}
if (chooserMessage != null)
{
using (var chooser = Intent.CreateChooser(messageIntent, chooserMessage))
{
contextWrapper.StartActivity(chooser);
}
return;
}
contextWrapper.StartActivity(messageIntent);
}
Please note that I am using Android and need a solution based on Android (intent based).
Sadly, Twitter don't provide API access for uploading images via DM.
If you are able to use Twitter's private API, you should be able to attach a media_id to your DM. But other than that, you're out of luck.
Sorry.
I am working in an application where I am fetch all the application installed my device using this code :
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory("com.myapp.MY_CATEGORY");
final List<ResolveInfo> pkgAppsList =getPackageManager().queryIntentActivities( mainIntent, 0);
Now I want to categories the apps differently According to their Categories. Please Suggest me.
To know the category of an application you need to get the data from google play. you can check android-market-api. it is a third party api. according to their info
You can browse market with any carrier or locale you want.
Search for apps using keywords or package name.
Retrieve an app info using an app ID.
Retrieve comments using an app ID.
Get PNG screenshots and icon
So you better check if you can parse the category info using this api.
I realized an AsyncTask to collect categories for some apps, using these libraries:
android-market-api-0.6
com.google.protobuf 2.4.1
you can find them on this link:
https://code.google.com/archive/p/android-market-api/downloads
http://mvnrepository.com/artifact/com.google.protobuf/protobuf-java
Here's the code in doInBackground() Method:
final ArrayList<MarketApplication> results = new ArrayList<>();
AccountManager am = AccountManager.get(MainActivity.this.getBaseContext());
Account[] accounts = am.getAccountsByType("com.google");
if (accounts.length > 0) {
try {
AccountManagerFuture<Bundle> accountManagerFuture =
am.getAuthToken(accounts[0], "android", null, MainActivity.this, null,
null);
Bundle authTokenBundle = accountManagerFuture.getResult();
String authToken =
authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN).toString();
MarketSession session = new MarketSession();
session.setAuthSubToken(authToken);
Market.AppsRequest appsRequest = Market.AppsRequest.newBuilder()
.setQuery(params[0])
.setStartIndex(0).setEntriesCount(10)
.setWithExtendedInfo(true)
.build();
session.append(appsRequest, new MarketSession.Callback<Market.AppsResponse>() {
public void onResult(Market.ResponseContext context, Market.AppsResponse
response) {
for (int i = 0; i < response.getEntriesCount(); i++) {
MarketApplication marketApplication = new MarketApplication();
Market.App app = response.getApp(i);
marketApplication.setName(app.getTitle());
Market.App.ExtendedInfo extendedInfo = app.getExtendedInfo();
marketApplication.setCategory(extendedInfo.getCategory());
results.add(marketApplication);
}
}
});
session.flush();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
return results;
Category information, got from ExtendedInfo, and name of application are added to a custom class MarketApplication. params[0] is a query String, like app name of interest.
There is a wiki page that helps developer to make a specific query:
https://code.google.com/archive/p/android-market-api/wikis/HowToSearchApps.wiki
take notice that this service requires to add these permissions in the Android manifest:
<uses-permission android:name="android.permission.GET_ACCOUNTS"></uses-permission>
<uses-permission android:name="android.permission.USE_CREDENTIALS"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>