Installing shortcut wihout duplicating native handly created one - java

I want to add a shortcut to my application, but I cannot manage not to duplicate native handly created one (by using drag and drop on application Icon in Application Menu to Home Screen for example).
Here is my code:
public void addShortcut(Context context)
{
this.manageShortcutAction(context, "com.android.launcher.action.INSTALL_SHORTCUT");
}
public void deleteShortcut(Context context)
{
this.manageShortcutAction(context, "com.android.launcher.action.UNINSTALL_SHORTCUT");
}
private void manageShortcutAction(Context context, String intentAction)
{
Context applicationContext = context.getApplicationContext();
Intent shortcut = new Intent(intentAction);
ApplicationInfo appInfo = applicationContext.getApplicationInfo();
PackageManager packageManager= applicationContext.getPackageManager();
String applicationName = (String) packageManager.getApplicationLabel(appInfo);
shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, applicationName); // Shortcut name
shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, packageManager.getLaunchIntentForPackage(appInfo.packageName));// Setup activity should be shortcut object
shortcut.putExtra("duplicate", false); // Just create once
shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(applicationContext, appInfo.icon));// Set shortcut icon
applicationContext.sendBroadcast(shortcut);
}
And my manifest required permissions:
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />
By the way, I had overwritten the Application code which is now MainApplication extending Application.
I have already tried to create a component to create the Intent.EXTRA_SHORTCUT_INTENT, without the expecting result.
If anyone has an idea...

I found out that in order to prevent the app from creating a duplicate of the shortcut created at app installation (or by copying from the menu), I have to create one with the same parameters of the existing one - using the same shortcut name is not enough.
After a lot of testing and thanks to the logcat, I've been able to create an exact replica as follows:
private void installShortcut() {
final Intent shortcutIntent = new Intent(getApplicationContext(), MainActivity.class);
shortcutIntent.setAction(Intent.ACTION_MAIN);
shortcutIntent.addCategory(Intent.CATEGORY_LAUNCHER);
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
final Intent intent = new Intent();
intent.putExtra("duplicate", false);
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.app_name));
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(this, R.drawable.icon));
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
sendBroadcast(intent);
}
The difference with other answers I found on SO is in these 3 lines:
shortcutIntent.addCategory(Intent.CATEGORY_LAUNCHER);
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
which I figured out by looking at the log entry printed when manually launching the app using the system created shortcut
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.myapp/.activities.main.MainActivity bnds=[365,73][475,191] } from pid 279

After having discuss with Sony's developers, there is no way not to duplicate manually created shortcuts...
I found some methods for Samsung but these were not generic.

Related

List of apps doesn't populate on Android 11 using PackageManager

I am using package manager to get a list of apps for an app drawer interface in my launcher. Everything works as it should but on Android 11,the only app that shows is the Android Settings app. What changed to make this not work anymore and/or what should i do to make it work? Are app list now based on user profiles?
Here is my current list code
public static List<ApplicationInfo> getPrimaryApps(Context context) {
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> res = new ArrayList<>();
ArrayList<String> hiddenPackages = new ArrayList<>();
IconPackHelper iconPackHelper = IconPackHelper.getInstance(context);
//All Apps Package Filter
Set<String> filteredPackages = new HashSet<>();
filteredPackages.add("com.android.wallpaper.livepicker");
filteredPackages.add("com.gocalsd.symphlyx");
//All Apps Blacklist
String[] flattenedPackages = SettingsProvider.get(context).getString(SettingsProvider.HIDDEN_APPS, "").split("\\|");
for (String flat : flattenedPackages) {
ComponentName cmp = ComponentName.unflattenFromString(flat);
if (cmp != null) {
hiddenPackages.add(cmp.getPackageName());
}
}
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
List<ResolveInfo> resolveInfoList = pm.queryIntentActivities(intent, 0);
//Sort all apps
Collections.sort(resolveInfoList, new ResolveInfo.DisplayNameComparator(pm));
for (ResolveInfo resolveInfo : resolveInfoList) {
ActivityInfo activityInfo = resolveInfo.activityInfo;
int iconId = IconPackHelper.getInstance(context).getResourceIdForActivityIcon(activityInfo);
if (!filteredPackages.contains(resolveInfo.activityInfo.packageName)) {
if (!hiddenPackages.contains(resolveInfo.activityInfo.packageName)) {
String appName = activityInfo.applicationInfo.loadLabel(pm).toString();
String packageName = activityInfo.packageName;
Drawable icon = null;
int extractedIconColor = 0;
//toggle themed icon
if (iconPackHelper.isIconPackLoaded() && iconPackHelper.getThemedIcon(context, packageName)) {
if (iconId != 0) {
icon = IconPackHelper.getInstance(context).getIconPackResources().getDrawable(iconId);
Bitmap iconBm = ImageUtils.drawableToBitmap(icon);
extractedIconColor = ColorProvider.getDominantColor(iconBm);
}
}
if (icon == null || !IconPackHelper.getInstance(context).isIconPackLoaded()) {
icon = activityInfo.applicationInfo.loadIcon(pm);
Bitmap iconBm = ImageUtils.drawableToBitmap(icon);
extractedIconColor = ColorProvider.getDominantColor(iconBm);
}
res.add(new ApplicationInfo(appName, icon, packageName, extractedIconColor));
}
}
}
return res;
}
In Android 11, we can see a lot of updates that improve privacy. If your app uses the PackageManager methods to get the list of installed apps in the user’s device, you will have to make some changes in your code for devices using Android 11.
Now for your users using Android 11, the code remains the same but it won’t work unless you add some additional elements in the AndroidManifest
There are 3 different ways of querying installed apps
1.Query specific packages
If you already know which apps you want to query just mention the package names inside the <queries> element in the AndroidManifest.
<manifest package="com.nd1010.app">
<queries>
<package android:name="com.fake.app" /> //replace with com.android.wallpaper.livepicker
<package android:name="com.fake.game" /> //replace with com.gocalsd.symphlyx
</queries>
...
</manifest>
2.Query using intent filter
In case you don’t know all the package names of the apps that you want to query but there is a set of apps with similar functionality that you want to query then you can use an intent filter inside the <queries> element according to your requirements like it has been done in the code snippet below.
<manifest package="com.nd1010.app">
<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
</queries>
...
</manifest>
The <intent> element looks like but there are few differences. <intent> element has the following restrictions:
The <intent> element can have only one <action> element.
The <data> element can only have the following attributes : mimeType, scheme and host.
3.Query all the apps
If you want to query all the apps of the user like you were doing earlier, you need to include QUERY_ALL_PACKAGES permission in the AndroidManifest. It is a normal permission and it is granted as soon as the app is installed.
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
Ideally one should request the least amount of packages and respect the user’s privacy. In most cases this permission won’t be required, only for apps like launchers it makes sense to ask the user for permission to query all the installed apps on their phone.
There is one loophole that I noticed while exploring the <queries> element if you add android.intent.action.MAIN as the action element in the intent filter, you can see almost all the apps of the user without adding the permission since almost all apps would have this element in the AndroidManifest.

is there away to find out if an apk is installed and if so open app

hi guys some of my previous questions have been marked down so please be nice.
what i want to know is if there is a bit of code i can use that tell the user of my app that an apk is installed. then to open it within my app.
i have a listview, inside the list view is an list of available apps for download. i have worked out how to find out if the apk is there install instead of download. but i cant seam to figure out the installed bit.
ive tried this
public static boolean isPackageInstalled(Context context, String packageName) {
final PackageManager packageManager = context.getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(packageName);
if (intent == null) {
return false;
}
List<ResolveInfo> list = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
then using
ispackageinstalled();
but this asks for Context, and string
so i tried add this to constructor
Context shb;
private Static String Showbox = "com.tk.Showbox";
then tried
ispackageinstalled(shb,Showbox);
and the app just crashes lol im obviously writing something wrong. also i would like for the selection to turn red if it installed if possible. but getting the app to open would be a great help cheers guys
You need to pass in an actual Context to the method. Your Activity subclass that is hosting the ListView is a Context. Pass the Activity into your ispackageinstalled() method.
found this worked the way i wanted
private boolean isCallable(Intent intent) {
if (intent == null) {
return false;
}
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
then making an Intent with the package details like this
Intent AllcastOpen = getPackageManager().getLaunchIntentForPackage("com.koushikdutta.cast");
then an if statement
if (isCallable(AllcastOpen) == true) {
AllcastInstalled.equals(true);
startActivity(AllcastOpen);
the allcastInstalled is a seperate method that changes the color of the selection if its installed/downloaded depending

why package manager is always is null?

i try to show user location in my android app , but it dosnt work! package manager is always null.
private void openPrefredLocationInMap (){
String location =
PreferenceManager.getDefaultSharedPreferences(this)
.getString(getString(R.string.pref_location_key)
, getString(R.string.pref_defult));
Uri geoLocation = Uri.parse("geo:0,0?").buildUpon()
.appendQueryParameter("q", location).build();
Intent intent= new Intent(Intent.ACTION_VIEW,geoLocation);
if (intent.resolveActivity(getPackageManager()) != null)
startActivity(intent);
else
Log.d("package","couldnt call"+location);
}
Your device does not have an application that could handle such intent. Install an application that can handle that intent or make one yourself. To be more specific, you might not have a map application that would be able to display geoLocation intent.
Try installing Google Maps for example.

How can I start a new android activity using class name in a string?

I'm having a problem with an android application that I'm working on.
My application has several sections and the next screen that loads is based on a string. So, screen 1 of section 1 would be, S1S1.
My question is, how can I start an activity based on a string. I have S1S1 saved in a string, let us call it next activity. Rather than having to type S1S1.class, I need it to come from the string. I've tried everything I can think of and google hasn't helped much.
Some things I've tried are
Intent myIntent = new Intent(nextactivity);
Intent myIntent = new Intent(v.getContext(), getClass().getName().valueOf(nextactivity));
Intent myIntent = new Intent(v.getContext(), Class.forName(nextactivity));
and tried running with
startActivityForResult(myIntent, 0);
but nothing seems to work. Any ideas?
Here is a code by which you can start activity using the name of the activity
String activityToStart = "com.example.MainActivity";
try {
Class<?> c = Class.forName(activityToStart);
Intent intent = new Intent(this, c);
startActivity(intent);
} catch (ClassNotFoundException ignored) {
}
EDIT
Here class name will be full name of the class with the package name.
For example if your package name will be x.y.z and if you have Activity name called A then the full name of the Activity A will be x.y.z.A.
An even better way (and one that is used in the system to launch Browser.apk along with other apps that aren't bundled with AOSP):
Intent intent = new Intent();
intent.setClassName("com.android.browser","com.android.BrowserActivity");
context.startActivity(intent);
Alternatively, if you want to check that you can start the Activity from the command line, you can do something like this from your shell:
adb shell
am start com.android.browser/.BrowserActivity
I am not aware of solution but i have an alternative.. the way similar to div hide and show in web pages.
if your s1s1 is to loaded low content have them in a linearlayout and keep their visibility gone on loading form s1. when you click on s1 to reach s1s1 hide s1 and set the params of visibility to "visible".
By doing this you can avoid creating a separate activity and this way is also easy to navigate back.
Use Enums!
public enum SectionActivity {
S1S1(MyS1Activity.class),
S1S2(S2Activity.class);
private Class<? extends Activity> activityClass;
private SectionActivity(Class<? extends Activity> clazz) {
this.activityClass = clazz;
}
public Class<? extends Activity> getActivity {
return activityClass;
}
}
Then somewhere in your code:
SectionActivity act = SectionActivity.valueOf(string);
Intent intent = new Intent(this, act.getActivity());
startActivity(intent);

Start Android Market from App

I'm developing a lite version for an app on the Android. How can I start an Intent to open the Android Market, preferably with the full version of my app displayed? This is difficult to test on an emulator (which is the closest thing to a device I have), as there seems to be no legal way of installing the Market on it.
That query above works, but when I tried it, it looked like it was bringing up search results based on the name.
If you use something like
intent.setData(Uri.parse("market://details?id=com.wolinlabs.SuperScorepad"));
instead, it will go right to the Android Market page for your app.
I think that's more what you wanted (?)
Found answer in the end:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://search?q=pname:MyApp"));
startActivity(intent);
No way of testing on emulator, though.
Hi I was trying the achieve the same but with one small difference
I DIDN'T WANT TO OPEN IT EMBEDDED ON MY APP
public void start(JSONArray args, CallbackContext callback) {
Intent launchIntent;
String packageName;
String activity;
String uri;
ComponentName comp;
try {
packageName = args.getString(0); //com.android.vending
activity = args.getString(1); //com.google.android.finsky.activities.LaunchUrlHandlerActivity
uri = args.getString(2); //'market://details?id=com.triplingo.enterprise'
launchIntent = this.cordova.getActivity().getPackageManager().getLaunchIntentForPackage(packageName);
comp = new ComponentName(packageName, activity);
launchIntent.setComponent(comp);
launchIntent.setData(Uri.parse(uri));
this.cordova.getActivity().startActivity(launchIntent);
callback.success();
} catch (Exception e) {
callback.error(e.toString());
}
}
THE BIG DIFFERENCE HERE IS THAT YOU START A NEW APP NOT JUST SHOW GOOGLE PLAY IN YOUR
APP
This code is part of a Cordova plugin but is pretty obvious what you need to do to use it natively.
THE IMPORTANT LINES
launchIntent = this.cordova.getActivity().getPackageManager().getLaunchIntentForPackage(packageName);
comp = new ComponentName(packageName, activity);
launchIntent.setComponent(comp);
launchIntent.setData(Uri.parse(uri));
Regards

Categories

Resources