Background
Starting from API 25 of Android, apps can offer extra shortcuts in the launcher, by long clicking on them:
The problem
Thing is, all I've found is how your app can offer those shortcuts to the launcher, but I can't find out how the launcher gets the list of them.
Since it's a rather new API, and most users and developers don't even use it, I can't find much information about it, especially because I want to search of the "other side" of the API usage.
What I've tried
I tried reading the docs (here, for example). I don't see it being mentioned. Only the part of other apps is mentioned, but not of the receiver app (the launcher).
The questions
Given a package name of an app, how can I get a list of all of its "app shortcuts" using the new API?
Is it possible to use it in order to request to create a Pinned Shortcut out of one of them?
You need to make yourself the launcher app. After that you can query the packagemanager to get the shortcutinfo for a particular package:
fun getShortcutFromApp(packageName: String): List<Shortcut> {
val shortcutQuery = LauncherApps.ShortcutQuery()
shortcutQuery.setQueryFlags(FLAG_MATCH_DYNAMIC or FLAG_MATCH_MANIFEST or FLAG_MATCH_PINNED)
shortcutQuery.setPackage(packageName)
return try {
launcherApps.getShortcuts(shortcutQuery, Process.myUserHandle())
.map { Shortcut(it.id, it.`package`, it.shortLabel.toString(), it) }
} catch (e: SecurityException) {
Collections.emptyList()
}
}
A full implementation of this can be found here:
https://android.jlelse.eu/nhandling-shortcuts-when-building-an-android-launcher-5908d0bb50d2
Github link to project:
https://github.com/nongdenchet/Shortcuts
LauncherApps is a class provided by the Android framework:
https://developer.android.com/reference/android/content/pm/LauncherApps.html
"Class for retrieving a list of launchable activities for the current user and any associated managed profiles that are visible to the current user, which can be retrieved with getProfiles(). This is mainly for use by launchers. Apps can be queried for each user profile. Since the PackageManager will not deliver package broadcasts for other profiles, you can register for package changes here."
Great Question this was what i was looking for and I get one that might be useful
(Its all about Intent)
https://github.com/googlesamples/android-AppShortcuts
Note: If your app is static its simple to implement.
Updated:::
Here is a link that will show you the difference of dynamic or static shortcuts and its implementation if you like it please up vote.
https://www.novoda.com/blog/exploring-android-nougat-7-1-app-shortcuts/
Related
I want to show some of the applications installed on device in my app. Like Gmail, yahoo, outlook, what's app, etc.
Is it allowed to show installed app icons in my app. e.g.: "Gmail" app installed on a device, then my app will check if "Gmail" app installed? If yes then it will fetch the icon from "Gmail" app using API:
Drawable icon = getPackageManager().getApplicationIcon("com.google.android.gm");
and will display it in my app. So showing icon in my app is allowed?
I think you can use this code.
Drawable icon = getPackageManager()
.getApplicationIcon("com.example.testnotification");
imageView.setImageDrawable(icon);
Yes it's allowed. All custom launchers are doing this. They just fetch info of all installed apps on a device and then just show this in their own way.
For all installed apps you can use this:
// First specify an intent for which you want to find activities
// available on device.
// In most cases you only need the "starter" activity which have
// action "ACTION_MAIN" and category "CATEGORY_LAUNCHER"
Intent i = new Intent(Intent.ACTION_MAIN, null);
i.addCategory(Intent.CATEGORY_LAUNCHER);
// Then you can "ask" a system to give you all activities that can
// be opened with this intent
List<ResolveInfo> allApps = getPackageManager()
.queryIntentActivities(i, 0);
// Iterate over info of all apps and retrieve info you need
for (ResolveInfo ri : allApps) {
// Name of app
CharSequence label = ri.loadLabel(getPackageManager());
// App package name
CharSequence packageName = ri.activityInfo.packageName;
// App icon
Drawable icon = ri.activityInfo.loadIcon(getPackageManager());
}
Source: https://www.androidauthority.com/make-a-custom-android-launcher-837342-837342/
You say:
So showing (their) icon in my app is allowed?
#JonGoodwin: Yes I am looking from copyright issue, is their any?
Intro
A trademark protects your brand image. Namely, your logo or
tagline. You can file for a registered trademark if you want to keep
the integrity of your app without worrying about other developers
taking your brand and creating a similar logo, name, or tagline.
ref2
Case law is complicated.
Hello I use Android. This line you are reading is illegal!.
Android™ should have a trademark symbol the first time it appears in a creative (i.e. the previous line, in this creative answer).
Their icon
Example:
(a) When using their actual logo, the rules are a bit more
complicated.
(b) You cannot use the Facebook “Facebook” logo without their
permission, so this is a no-no:
(c) You can use the Facebook™ “F”. But only in the context that you want to
prompt the reader/viewer to connect to your Facebook page.
When you're trademarking your app, you're protecting yourself from
anyone else from using a similar or confusing name to yours. The
particular design or functionality of an app may be subject to
copyright or patent protection, but a trademark protects the name
of your app, or, the logo associated with your app.
Your icon
Mobile App Icons: They’re Trademarks and You Should Register Them.
The majority of app icons have not yet been registered as
trademarks.
Once a company’s core trademarks are protected, it should consider
filing a trademark application to cover its actual app icon.
I searched all federal case law and all TTAB(Trademark Trial and
Appeal Board) cases and could not find any involving app icon
trademarks.ref3
Disclaimer:
The information provided herein presents general
information and should not be relied upon as legal advice when
anallyzing and resolving a specific legal issue. If you have specific
questions regarding a particular fact situation, please consult with
competent legal counsel about the facts and laws that apply.
On google awareness API Guides page there is mention about Context types.
Contextual data includes sensor-derived data such as location
(lat/lng), place (home, work, coffee shop)
However, on reference page with Places type Place reference, there is no mention about Home type. Is there a way to find out if user is at home (of course if he has it set up in his google settings)?
A bit late to the party, but now that Google's Awareness API has deprecated Places, you can use the NumberEight SDK as an alternative. One bonus is that it works on iOS too.
It performs a wide variety of context recognition tasks including:
Real-time physical activity detection
Current place categories (including Work and Home, which can trigger even without GPS)
Motion detection
Reachability
Local weather
It can also record user context for reports and analysis via the online portal.
To quickly check when a user is at home in Kotlin, you would write:
val ne = NumberEight()
ne.onPlaceUpdated { glimpse ->
val place = glimpse.mostProbable
if (place.context.home == Knowledge.AtPlaceContext) {
Log.d("MyApp", "User is at home!")
}
}
Here are some iOS and Android example projects.
Disclosure: I'm one of the developers.
I've searched and cannot find this, though mainly a challenge of knowing what to search for, I'm sure it's been asked before.
How does an app deduce whether it is running on a "Google Android" device, or an AOSP device (e.g. Kindle Fire etc)?
You can try to enumerate an existing accounts on device using AccountManager class: getAccountsByType(), passing com.google as desired account type. If there is no accounts of this type, this mean either it is AOSP device or the user didn't create Google account yet.
If it is not enough for you, you can use PackageManager class and query some Google-specific package using getPackageInfo() method. For example, com.android.vending - Google Play app.
But pay attention that no one of these methods can guaranty you that target device is running AOSP.
This is not trivial, but most apps do not need to even care. If you do, then it usually means you need some unique features therefore it's simpler to check if certain feature is present or not, instead of what label is on the device. You may also check for presence of certain packages (like Google Play), but lack of it does not mean automatically it is i.e. Fire.
You can check whether you can open market urls by using an Intent.
Documentation on the intent's extras is here.
The Intent would look like this:
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=com.example.android"));
//you can use any package identifier for the check.
startActivity(intent);
} catch (ActvitiyNotFoundException anfe) {
//There's no market installed.
//So you can guess that you're not on a device with Google experience
}
The downside of this method would be, that the user will be taken to the Play Store if it's available.
Not all phones have Android Market installed, and therefore using intent to open market app fails.
What's the best way to handle this?
Hide this feature if user doesn't have Android Market installed (how would I detect this?).
Handle the possible error, how (and possibly suggest that the user downloads the Android Market)?
The problem with the answer above is that if you just pass a URL the user will be prompted how to handle the Intent.
A more graceful way to do it IMO, expanding upon the 1st answer above, is to test whether the market app is installed, and then if not, pass a URL (which actually you would then want to test to see if something can handle that intent, but if you happen to have a device without both the play store and a browser then I would question why the user would have my app installed in the first place (another story I suppose)....
Perhaps there is a better way, but here's what works for me:
private String getMarketURI(String marketURL) {
String returnURL = "";
PackageManager packageManager = getApplication().getPackageManager();
Uri marketUri = Uri.parse("market://" + marketURL);
Intent marketIntent = new Intent(Intent.ACTION_VIEW).setData(marketUri);
if (marketIntent.resolveActivity(packageManager) != null) {
returnURL = "market://" + marketURL;
} else {
returnURL = "https://play.google.com/store/apps/" + marketURL;
}
return returnURL;
}
And then use it like so:
marketIntent.setData(Uri.parse(getMarketURI("details?id=com.yourapps.packagename")));
If your app is being provided by Android Market, then it does have Android Market installed. :)
Okay that is snide, but there is an important truth -- Google goes to a lot of effort to enforce compatibility guarantees on devices for them to be allowed to ship with Android Market, so that is how you can know that whatever you are running on will behave as it should.
If you are delivering your app from something besides Android Market, you need to get information from whoever is delivering the app about what compatibility guarantees they have.
If they don't have compatibility guarantees (or you are just putting a raw .apk up on a web site or such), then you have a complete crap shoot. The device you are running on could have had its software modified in pretty much any way, and have any kind of differences in behavior you can imagine.
That said, if you want to determine whether there is an activity on the current device to handle a particular Intent, you can use this: PackageManager.resolveActivity
Use the web address as the intent target and then if there is no android market it will open in a browser.
So one of my applications was rejected from the Amazon app store today. The reason was because inside my app, I linked to the paid version of the app on the Android market. Disappointing, but whatever, I guess everyone wants their cut...
So now I'm left having to modify the application to change the Android market link to an Amazon app store link. Not really a big deal except for now if I do that I'm left with a discrepancy when I want to upload a newer version back to the Android market. After all, it would be rather dumb to link someone to the Amazon app store if they purchase the app from the Android market.
Now we all know that it is a pain supporting/managing multiple versions of the same app. Consequently my question becomes how can I link to both at the same time? Is there a way to tell where an app was downloaded from so I can code both links into the app and thus point the user automatically to one or the other? Secondly, is it against the Amazon TOS to give the user a choice (say I pop up a dialog instead and ask the user where to download from)?
Thanks all.
Edit: Direct from Amazon customer service "Re: Link to both markets" (I wish the approval process was as fast as these guys):
For the time being, we need any
linking to point back to the Amazon
Appstore only for market links.
Linking to your website is allowed,
just not other markets.
When pointing to other apps from
within your app, including up-sells,
completion of purchase must be from
the Amazon Appstore.
Good news! Apparently the latest version of the Amazon store finally sets PackageManager.getInstallerPackageName() to "com.amazon.venezia" to contrast with Google Play's "com.android.vending". This will be the easiest way to determine if your app is sideloaded, installed from Amazon, or installed from Google.
Here's what you can do:
Complete the preparation and signing of your application.
Install it on your test device
Use PackageManager.getPackageInfo
How to do this:
public static boolean isMarket(Context context){
boolean isMarketSig = false;
int currentSig = 1;
try {
Signature[] sigs = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
for (Signature sig : sigs)
{
currentSig = sig.hashCode();
Log.i("MyApp", "Signature hashcode : " + sig.hashCode());
// This Log is for first time testing so you can find out what the int value of your signature is.
}
} catch (Exception e){
e.printStackTrace();
}
//-1545485543 was the int I got from the log line above, so I compare the current signature hashCode value with that value to determine if it's market or not.
if (currentSig==-1545485543){
isMarketSig = true;
} else {
isMarketSig = false;
}
return isMarketSig;
}
public static void openStore(Context context){
if (isMarket(context)){
Intent goToMarket = new Intent(Intent.ACTION_VIEW,Uri.parse("market://d" +
"etails?id=com.jakar.myapp"));
goToMarket.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(goToMarket);
} else {
Intent goToAppstore = new Intent(Intent.ACTION_VIEW,Uri.parse("http://www.amazon.com/gp/mas/dl/andro" +
"id?p=com.jakar.myapp"));
goToAppstore.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(goToAppstore);
}
}
Basically, the hashCode() that you get from the app installed on your testing device will be the same one from the market. The hash code from the app store will be different because according to https://developer.amazon.com/help/faq.html, the app store signs the application with a signature specific to your developer account, so that will return a different value that what you actually signed it with.
Note: It works to open the market successfully, but I haven't yet deployed this method on the App Store, so I haven't completely tested it. I am confident it will work, but can make no guarantees, so if you use what I've suggested and it fails, please don't hold me accountable.
You can do the following things:
Check if the device based on its Manufacturer.
For ex: https://developer.amazon.com/sdk/fire/specifications.html
For writing reviews and opening the Amazon App Store use the following intent
amzn://apps/android?p=package_name
where p=Link to the detail page for a specific package name.
Ref: Amazon developer link.
https://developer.amazon.com/sdk/in-app-purchasing/sample-code/deeplink.html
As you said, you could use a boolean and then be forced to build your apps twice, so I bevelive it's not the best way.
The best way is to check if android market is installed and act accordingly: here.
Another even more complex way is to query the name of the installer of your app, using PackageManager.getInstallerPackageName. This needs extra work since the app can be installed by parallel markets even if you are on android device with android market installed, and also you must check if it's installed as debug/development (installer package name is null in this case).
What a hard way to do this. If you just want to open the market url, just check if the intent with the url for android market has any activity that knows how to handle it. If not, open up the amazon appstore with another intent.
/**
* Returns intent that opens app in Google Play or Amazon Appstore
* #param context
* #param packageName
* #return null if no market available, otherwise intent
*/
public static Intent showApp(Activity activity, String packageName)
{
Intent i = new Intent(Intent.ACTION_VIEW);
String url = "market://details?id=" + packageName;
i.setData(Uri.parse(url));
if (isIntentAvailable(activity, i))
{
return i;
}
i.setData(Uri.parse("http://www.amazon.com/gp/mas/dl/android?p=" + packageName));
if (isIntentAvailable(activity, i))
{
return i;
}
return null;
}
public static boolean isIntentAvailable(Context context, Intent intent) {
final PackageManager packageManager = context.getPackageManager();
List<ResolveInfo> list =
packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
return list.size() > 0;
}
Another way is to build using ant. This way you can generate dynamically a java class with constants set to a value that represent the app market, and output different builds with ease. This however takes some learning but when you have it running, it's very easy.
I was also struggling with this, but decided that the instant success I'm seeing with my free app on Amazon warrants the time to create a second set of .apks when I make a new build. I went with the Amazon boolean flag for now, and create one common version, then a version incremented by one for the Amazon market. No other markets demand internal linking, AFAIK.
I eventually intend to code up a market chooser with a slick way to automatically figure out what to do, but there are a lot of variables - not just the market address of one app, but how different markets recognize the company name to find all apps. Some market apps hijack the main Android market protocol (e.g. Appslib and SlideMe if I remember correctly) but don't store the company name the same way. Then you need to decide where upsell links go - to the same market or to a common one (I only submitted my free app to most of the markets I use).
I am waiting for my paid app to be approved, but I am pretty sure it will be worth it to have it available on the Amazon market given how many downloads I've gotten for my free app.
Just refactor most of your project into a Project Library, and then create multiple projects (e.g., for each app store) that have only unique icon and string resources, their own package ID declared in the manifest, and then a main activity that extends the main activity that you have defined in your library.
That way, all the unique URLs can be provided by overriding, in each particular app project's activity, the virtual or abstract methods defined in your library's main activity. The library code that that displays these URLs can obtain them via a polymorphic call to each of those methods.
That way, each such specialized project will be very small, and maintenance will be mainly to your library, as described here:
Multiple Apps with a shared code base