is it possible to add queries in runtime? I know it is possible to change configurations and permissions but e.g if I want my app to interact with data of any app on my phone which is possible by having the packagename. But after API 30 I need to specify Apps I want to interact with as queries in the AndroidManifest.xml, or use the QUERY_ALL_PACKAGES permission which I don't want to use.
Here is an Example how I imagined it:
User enters my app and wants the icon of a specific app on his phone. Then enters the package name and clicks ok and now he can see the icon of a certain app.
is it possible to add queries in runtime?
You appear to be referring to <queries> elements in the manifest. If so, then no, that is not possible.
But after API 30 I need to specify Apps I want to interact with as queries in the AndroidManifest.xml, or use the QUERY_ALL_PACKAGES permission which I don't want to use
Or, you specify Intent patterns of relevance in <queries>.
User enters my app and wants the icon of a specific app on his phone. Then enters the package name and clicks ok and now he can see the icon of a certain app.
First, approximately 0% of your users know package names. A more user-friendly UI would be to present the member with a list to choose from.
Second, it is unclear what you mean by "the icon of a certain app". If you mean "a launcher icon from a certain app", then basically you want similar capabilities to a launcher: show a list (or grid or whatever) of launcher icons for the user to choose from, after which you can do something with the chosen icon.
For that scenario, this should suffice:
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>
</queries>
You can then use queryIntentActivities() to find the launcher activities on the device:
private val LAUNCHER_INTENT =
Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER)
data class MainViewState(
val launcherActivities: List<ResolveInfo>
)
class MainMotor(private val context: Context) : ViewModel() {
private val _states = MutableLiveData<MainViewState>()
val states: LiveData<MainViewState> = _states
fun load(force: Boolean = false) {
if (force || _states.value == null) {
val pm = context.packageManager
_states.value = MainViewState(
launcherActivities = pm.queryIntentActivities(LAUNCHER_INTENT, 0)
)
}
}
}
Hmh... read a bit more about Androidmanifest.xmls and Queries and found out the App i planned to do is exactly sth Google doesnt want to have so....
But still thanks for the answers ^^
Related
Here's the use case: I want to create a statistics tool that lists all incoming push messages on a given phone, their message text, when they arrived and their source. clarification: I need 100% access to the data as I'm creating my own presentation tool for private use. It's not to be a consumer app.
I'm thinking I can achieve this by creating an app that somehow listens to all incoming intents of type com.google.android.c2dm.intent.RECEIVE and then analyzing the contents of those. Now I believe that is not possible with the security and intent model of stock Android. But is there anyway around this either with a stock rooted phone, or do I even have to go further and fork and modify AOSP?
Other options I can think of is to monitor the visual notification center itself, if that has an API (which I doubt).
Any suggestions are welcome. I am not intimidated by difficult tasks. Yet my knowledge is limited to Java Android coding - I know little of AOSP or the NDK, but I'd be willing to learn if this is the path I must take.
(Sorry if this is a very broad question, I'll accept the answer that gives me the easiest path to a solution)
So it turns out it wasn't all that difficult. You need an app with minSdkVersion=18 and then you can implement a NotificationListenerService which listens for created or deleted notifications, or you can poll for the currently visible.
Caveat: You need explicit persmission from the user to read notifications other than your own.
Basically what you do is 1. Extend the NotificationListenerInterface and override the onNotificationPosted like this:
#Override
public void onNotificationPosted(StatusBarNotification notif) {
Bundle extras = notif.getNotification().extras;
String packageName = notif.getPackageName();
if (extras != null) {
String title = "" + extras.getCharSequence(Notification.EXTRA_TITLE);
String description = "" + extras.getCharSequence(Notification.EXTRA_TEXT);
// Do whatever you want to do with the data
}
}
In addition you need to register the service in the manifest:
<application>
<!-- (...) -->
<service android:name=".YourListenerClassName"
android:label="#string/app_name" android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
</application>
Background
According to a new feature on Android M (link here), apps outside your app can offer to give a direct sharing intent to one of its activities, allowing, for example, a chatting app to share the content to an exact contact, so you choose both the chatting-app and the contact at the same time (one step instead of 2) . This can be shown on this image:
Or, at least that's what I've understood from it.
The question
I have 2 questions regarding this new feature:
In the description, they only show what to put in the manifest, and they mention using "ChooserTargetService". What should be done in order to provide the texts and images?
I'd like to know how to do the opposite : how can I query all of those "direct-share" items (images, texts, and intents) and be able to show them on a customized dialog?
I want to do it because I have a customized dialog myself, that allows to choose what to share and how, and not just through which app.
Question 1
In the description, they only show what to put in the manifest, and
they mention using "ChooserTargetService". What should be done in
order to provide the texts and images?
Start by extending ChooserTargetService. You'll need to return a List of ChooserTarget and how you create those targets is entirely up to you.
public class YourChooserTargetService extends ChooserTargetService {
#Override
public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) {
final List<ChooserTarget> targets = new ArrayList<>();
for (int i = 0; i < length; i++) {
// The title of the target
final String title = ...
// The icon to represent the target
final Icon icon = ...
// Ranking score for this target between 0.0f and 1.0f
final float score = ...
// PendingIntent to fill in and send if the user chooses this target
final PendingIntent action = ...
targets.add(new ChooserTarget(title, icon, score, action));
}
return targets;
}
}
AndroidManifest
Now you'll need to declare your ChooserTargetService in your AndroidManifest and do two things:
Bind the Service using the android.permission.BIND_CHOOSER_TARGET_SERVICE permission
Include an IntentFilter with the android.service.chooser.ChooserTargetService action
For example:
<service
android:name=".YourChooserTargetService"
android:label="#string/yourLabel"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
<intent-filter>
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
In the Activity that's going to handle the Intent, you'll need to add the meta-data tag android.service.chooser.chooser_target_service. For example:
<activity android:name=".YourShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<meta-data
android:name="android.service.chooser.chooser_target_service"
android:value=".YourChooserTargetService" />
</activity>
From here, it's mostly a matter of calling Intent.createChooser and then handling the data if the user chooses your application.
final Intent target = new Intent(Intent.ACTION_SEND);
target.setType("text/plain");
target.putExtra(Intent.EXTRA_TITLE, "Your title");
target.putExtra(Intent.EXTRA_TEXT, "Your text");
startActivity(Intent.createChooser(target, "ChooserTargetService Example"));
Results
Things to note
The ranking score for each ChooserTarget is used to sort the targets, but is only used if the UI decides to use it. As per ChooserTarget.getScore
The UI displaying the target may take this score into account when
sorting and merging targets from multiple sources
Also, as far as I know, this feature isn't actually implemented yet in the Android MNC preview. The ChooserActivity contains a TODO for it:
TODO: Maintain sort by ranking scores
When creating a new android.graphics.drawable.Icon, you'll need to use one of the static initializers.
Icon.createWithBitmap();
Icon.createWithContentUri()
Icon.createWithData()
Icon.createWithFilePath()
Icon.createWithResource()
Question 2
I'd like to know how to do the opposite : how can I query all of those
"direct-share" items (images, texts, and intents) and be able to show
them on a customized dialog?
The data supplied to ChooserTargetService.onGetChooserTargets is dynamic. So, there's no direct way to access those items, as far as I know.
I have different understanding of this future.
Until now when user wanted to share something they been asked to choose the application they want to share with, and then this application handled the share.
Now instead of user choosing the application they will choose the content from the application that will handle the share. Each such option is encapsulated in android.service.chooser.ChooserTargetService.
So as you see on the image, it shows some products of ChooserTargetService, the user see some contacts ui without lunching or sharing just yet.
I believe your dialog could be triggered at the same way.
Background
Android M presents a new way to handle selected text (link here), even from outside of your app . Text selection can be handled as such:
I know it's possible to handle the selected text from outside the app, because if I go to the web browser (or any other place that allows text selection), I can see that I can use the "API demos" app to handle the selected text.
The problem
I can't see a lot of information about how to do it.
The question
What should be added in code (and manifest) to be able to handle the selected text from outside the app ?
Is it possible to limit the selection to certain types of texts ? For example, offer to show the app only if the text type is a valid phone number ?
First, to clarify the question: On an M emulator, if you highlight text, you will see the new floating action mode. If you click the overflow icon, you will see "API DEMOS" show up:
Clicking that brings up an activity from the API Demos app, showing the highlighted text:
Replacing the value in the field and clicking the button puts your replacement text in as a replacement for whatever you had highlighted.
WARNING: The following explanation is from inspecting the API Demos code and the M Developer Preview documentation. It is very possible that this will change before M ships for realz. YMMV, unless you use the metric system, in which case YKMV.
The activity in question, that is receiving the text, supports ACTION_PROCESS_TEXT as the Intent action. EXTRA_PROCESS_TEXT will hold some text, or EXTRA_PROCESS_TEXT_READONLY will hold it if the text is read-only. The activity will be invoked via startActivityForResult(). The result Intent can have its own EXTRA_PROCESS_TEXT value, which will be the replacement text.
So, to the specific questions:
What should be added in code (and manifest) to be able to handle the selected text from outside the app ?
See above. Note that the API Demos activity (ProcessText) has this <intent-filter>:
<intent-filter >
<action android:name="android.intent.action.PROCESS_TEXT"/>
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
The documentation does not discuss a MIME type. I have not run any experiments to determine if the MIME type is required, and what else we might get (text/html for stuff that has spans?).
Is it possible to limit the selection to certain types of texts ? For example, offer to show the app only if the text type is a valid phone number ?
That wouldn't seem to be possible given the documentation. That being said, it's certainly a reasonable idea (e.g., advertise a regex, or multiple regexes, via metadata in the manifest that the text must match).
This article on Android Developers Blog may be relevant, it describes how Google Translate option can be added to overflow text selection menu.
Android apps that use Android text selection behavior will already
have this feature enabled, so no extra steps need to be taken.
Developers who created custom text selection behavior for their apps
can easily implement this feature by following the below steps:
Scan via the PackageManager through all packages that have the
PROCESS_TEXT intent filter (for example:
com.google.android.apps.translate - if it installed) and add them as
MenuItems into TextView selections for your app
To query the package manager, first build an intent with the action
Intent.ACTION_PROCESS_TEXT, then retrieve the supported activities
and add an item for each retrieved activity and attach an intent to it
to launch the action
public void onInitializeMenu(Menu menu) {
// Start with a menu Item order value that is high enough
// so that your "PROCESS_TEXT" menu items appear after the
// standard selection menu items like Cut, Copy, Paste.
int menuItemOrder = 100;
for (ResolveInfo resolveInfo : getSupportedActivities()) {
menu.add(Menu.NONE, Menu.NONE,
menuItemOrder++,
getLabel(resolveInfo))
.setIntent(createProcessTextIntentForResolveInfo(resolveInfo))
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
}
The situation:
You have an extensive mobile website, m.somewhere.com
On Google Play you have an Android App that duplicates the key features of m.somewhere.com, but not all of them.
Your Client/Employer/Investor has asked you to implement deep-linking for those urls that can be handled by the app.
TL;DR - how do you implement this?
My Approach So Far:
First instinct: match only certain urls and launch for them. Problem: paucity of expression in the AndroidManifest intent-filter prevents this (e.g. http://weiyang.wordpress.ncsu.edu/2013/04/11/a-limitation-in-intent-filter-of-android-application/).
As a subset of the problem, suppose the server at m.somewhere.com knows that any url that ends in a number goes to a certain page on the site, and the marketing guys are constantly futzing with the seo, so e.g.
I want to launch the app for:
http://m.somewhere.com/find/abc-12345
https://m.somewhere.com/shop/xyz-45678928492
But not for
http://m.somewhere.com/find/abc-12345-xyz
https://m.somewhere.com/about-us
no combination of path, pathPrefix, or pathPattern will handle this.
Best practice on stackoverflow (Match URIs with <data> like http://example.com/something in AndroidManifest) seems to be to catch everything, and then handle the situation when you get to onCreate() and realize you shouldn't have handled this particular url:
Android Manifest:
...
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http"
android:host="m.somewhere.com"
android:pathPattern=".*"/>
</intent-filter>
...
Activity onCreate():
Intent i = getIntent()
String action = i.getAction();
Uri uri = i.getData();
if (Intent.ACTION_VIEW.equals(action) && cantHandleUrl(uri)) {
// TODO - fallback to browser.
}
I have programmed something similar to the above that is working, but it leads to a very bad end-user experience:
While browsing m.somewhere.com, there is a hiccup on every url click
while the app is launched and then falls back.
There is a nasty habit for a Chooser screen to popup for each and every link click on m.somewhere.com, asking the user which they would like to use (and the Android App is listed along with the browsers, but clicking on the Android App just launches the chooser screen again). If I'm not careful I get in an infinite relaunch loop for my app (if the user selects "Always"), and even if I am careful, it appears to the user that their "Always" selection is being ignored.
What can be done?
(EDIT: Displaying the site in a WebView in the app for unhandled pages is NOT an option).
Late answer, but for future readers: if you're supporting a minimum of API level 15 then there's a more direct (less hacky) way of falling back to a browser for URLs you realize you don't want to handle, without resorting to disabling/re-enabling URL catching components.
nbarraille's answer is creative and possibly your only option if you need to support APIs lower than 15, but if you don't then you can make use of Intent.makeMainSelectorActivity() to directly launch the user's default browser, allowing you to bypass Android's ResolverActivity app selection dialog.
Don't do this
So instead of re-broadcasting the URL Intent the typical way like this:
// The URL your Activity intercepted
String data = "example.com/someurl"
Intent webIntent = new Intent(Intent.ACTION_VIEW, data);
webIntent.addCategory(Intent.CATEGORY_BROWSABLE);
startActivity(webIntent);
Do this
You would broadcast this Intent instead:
Intent defaultBrowser = Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_BROWSER);
defaultBrowser.setData(data);
startActivity(defaultBrowser);
This will tell Android to load the browser app and data URL. This should bypass the chooser dialog even if they have more than one browser app installed. And without the chooser dialog you don't have to worry about the app falling into an infinite loop of intercepting/re-broadcasting the same Intent.
Caveat
You have to be okay with opening the URL (the one you didn't want to handle) in the user's browser. If you wanted to give other non-browser apps a chance to open the link as well, this solution wouldn't work since there is no chooser dialog.
Pitfalls
As far as I can tell, the only quirk from using this solution is that when the user clicks one of your deep links, they'll get to choose to open in your app or their browser, etc. When they choose your app and your internal app logic realizes it's a URL it doesn't want to intercept, the user gets shown the browser right away. So they choose your app but get shown the browser instead.
NOTE: when I say "broadcast" in this answer, I mean the general term, not the actual Android system feature.
There is a somewhat hacky way of doing this:
In the manifest, create an intent-filter for m.somewhere.com, to open a specific deeplink handler activity.
In that Activity, figure out if your app supports that URL or not.
If it does, just open whatever activity
If it doesn't, send a non-resolved ACTION_VIEW intent to be opened by your browser. The problem here, is that your app will also catch this intent, and this will create an infinite loop if your app is selected as the default handler for that URL. The solution is to use PackageManager.setComponentEnabledSetting() to disable your deeplink handler Activity before you send that intent, and re-enable it after.
Some example code:
public class DeepLinkHandlerActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Uri uri = intent.getData();
Intent intent = makeInternallySupportedIntent(uri);
if (intent == null) {
final PackageManager pm = getPackageManager();
final ComponentName component = new ComponentName(context, DeepLinkHandlerActivity.class);
pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
Intent webIntent = new Intent(Intent.ACTION_VIEW);
webIntent.setData(uri);
context.startActivity(webIntent);
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void[] params) {
SystemClock.sleep(2000);
pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
return null;
}
};
task.execute();
} else {
startActivity(intent);
}
finish();
}
}
Hope that helps.
Note: It looks like you need to delay the re-enabling by a couple of seconds for this to work.
Note 2: For a better experience, using a Transparent theme for your activity will make it look like your app didn't even open.
Note 3: If for some reason your app crashes or gets killed before the component re-registers, you're loosing deep link support forever (or until next update/reinstall), so I would also do the component re-enabling in App.onCreate() just in case.
URX provides a free tool (urxlinks.js) that automatically redirects mobile web users into an app if the app is installed. The documentation is available here: http://developers.urx.com/deeplinks/urx-links.html#using-urx-links-js
If two apps are using same scheme then the chooser screen will be popped as android wont know which app the link is intended for. Using custom scheme for your app might solve this issue. But still you can't be sure no one else will use that scheme.
It sounds like you're trying to treat your mobile app and mobile website as extensions of the same experience. That's good practice, generally speaking, but at this point the two are simply not at parity. At least until they reach parity I would not recommend automatically pushing the end user into your mobile app because users who are deliberately using the mobile site in order to find the content your app is missing will find this incredibly frustrating.
Instead, it might make sense to use a smart banner to encourage users on the mobile website pages that do have an in-app equivalent to open the app instead. Those banners would be your deeplinks. You could create them yourself or integrate a tool like Branch ( https://branch.io/universal-app-banner/ ) that handles deep linking and smart banners both.
That last part of your question has to do with where to place the deep links. One advantage to using smart banners instead of redirects is that you can embed them into the appropriate templates on your CMS instead of needing to rely on url detection.
Good luck!
This was my solution to your second problem. PackageManager.queryIntentActivities() will give you the list of apps/activities that would appear in the chooser. Iterate through the list (which should at least include the browser) and find an activity whose package name doesn't match the current app, and set the intent class name to it, then launch an Activity with that intent and call finish();
public Intent getNotMeIntent(Uri uri) {
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
PackageManager manager = context.getPackageManager();
List<ResolveInfo> infos = manager.queryIntentActivities(intent, 0);
for (int i = 0; i < infos.size(); i++) {
ResolveInfo info = infos.get(i);
// Find a handler for this url that isn't us
if (!info.activityInfo.packageName.equals(context.getPackageName())) {
intent.setComponent(null);
intent.setClassName(info.activityInfo.packageName, info.activityInfo.name);
return intent;
}
}
// They have no browser
return null;
}
The Transparent theme (mentioned above) should be a good solution for the first problem.
In destination activity in onCreate set this code for Kotlin:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleIntent(intent)
}
private fun handleIntent(intent: Intent?) {
val appLinkAction: String? = intent?.action
val appLinkData: Uri? = intent?.data
showDeepLinkData(appLinkAction, appLinkData)
}
private fun showDeepLinkData(appLinkAction: String?, appLinkData: Uri?) {
if (Intent.ACTION_VIEW == appLinkAction && appLinkData != null) {
val promotionCode = appLinkData.getQueryParameter("exampleQueryString")
Log.e("TAG", "Uri is: $appLinkData")
}
}
The documentation says you can specify a custom category.
When, why and how would you do it?
What would be the use of it?
The way I understand it, categories are public directives to the android operating system(and other apps) that represent different categories that your app should be a part of.
Example
When the launcher icon is tapped on the home screen, the home application looks through every installed app's manifest for the HOME category -- and if so it displays it in the app drawer.
However, there's more. You can specify categories in your applications manifest that lets the system know that you application can handle the intent category. For example, by putting a ALTERNATIVE category, other apps in the system know that your app can handle that category without specifically knowing the action name! In the following example, custom intent categories are passed through this intent, which is filtered and the corresponding object gets edited(taken from the Notes example app):
<intent-filter android:label="#string/resolve_title">
<action android:name="com.android.notepad.action.EDIT_TITLE" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.ALTERNATIVE" />
<category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
<data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
</intent-filter>
By registering this intent filter in an <activity /> tag, you can edit a "note". The intent data would contain the note, and the intent would get routed to the activity that this filter is registered in.
In Conclusion:
There isn't really a reason you'd use a custom category. They are for Android, and thus don't really make sense in application use. But, if you choose to use them, they can be used in the methods described above. "They provide some specific semantic rules, and if those rules are useful to you then feel free to use them"(Hackbod).
http://developer.android.com/guide/topics/intents/intents-filters.html
Scroll down a bit and you should see a section under "Intent Objects"
They basically describe certain special properties of an activity. for example, adding
<category android:name="android.intent.category.HOME" />
means that the app can be started on the phone's bootup
I'm kinda a noob to Android still, although I have programming experience otherwise.. It says a custom category in your own namespace. I'm guessing that if you are programming multiple apps and you want one app to run another app, you could use a custom category for your intent to force the phone to find your other app to catch the intent with?
When you do not want to use the default category then use the custom category.
Custom categories should use the package name as a prefix, to ensure that they are unique.
Some information is provided on below link:
http://developer.android.com/guide/topics/manifest/category-element.html
Check the below link it has somewhat same question:
Android custom categories