I am trying to open the call logs activity from my app using an intent and startActivity(ForResult).
It does work fine, except on some devices.
After investigation, I noticed that when the intent resolve to this class:
ComponentName("com.android.contacts", "com.android.contacts.NonPhoneActivity")
then startActivity (or startActivityForResult) has "no effect" / the CallLogs is NOT displayed.
As per the name 'NonPhoneActivity', this makes sense I guess..
(info about this NonPhoneActivity activity :
https://android.googlesource.com/platform/packages/apps/Contacts/+/master/AndroidManifest.xml )
Here is the code (FYI, with startActivityForResult; I observe the same behavior with startActivity:
additionalButtonsBinding.phoneSelectContactFragmentButtonsMissedCallButton.setOnClickListener(v -> {
Intent showCallLog = new Intent();
showCallLog.setAction(Intent.ACTION_VIEW); // "android.intent.action.VIEW"
showCallLog.setType(CallLog.Calls.CONTENT_TYPE); // "vnd.android.cursor.dir/calls"
showCallLogLauncher.launch(showCallLog);
});
showCallLogLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
// result is always {resultCode=RESULT_CANCELED, data=null}
// may the CallLogs be shown OR not
// so startActivityForResult is useless: just here fore investigation purpose and completeness of my question
Log.d(TAG, result.toString());
}
});
Questions:
Why does the CallLogs intent resolve to ComponentName("com.android.contacts", "com.android.contacts.NonPhoneActivity")... even on a Phone!
On emulator, the intent resolve to
"com.android.dialer",com.android.dialer.main.impl.MainActivity",
which is able to display CallLogs.
Since ("com.android.contacts", "com.android.contacts.NonPhoneActivity") is sometimes/often the only component (*) able to handle Intent { act=android.intent.action.VIEW typ=vnd.android.cursor.dir/calls } , should I use another intent (another action) to display CallLogs?Which other Intent should I use to display CallLogs?
*: as per result PackageManager.queryIntentActivities(showCallLog, PackageManager.MATCH_ALL) , which is a list with only one item, being "com.android.contacts", "com.android.contacts.NonPhoneActivity"
Nailed it (almost):
When Google Dialer (Google Phone) app is installed, the following intent action is available: com.android.phone.action.RECENT_CALLS:
Intent googleDialerShowRecent = new Intent("com.android.phone.action.RECENT_CALLS");
if (context.getPackageManager().queryIntentActivities(googleDialerShowRecent, PackageManager.MATCH_ALL).size() > 0) {
showCallLogLauncher.launch(googleDialerShowRecent);
}
I just do not knwow how to force the Google Phone app to display the 'History'/'Recent calls' tab on launch.
Google Phone's manifest excerpt:
<activity
android:name="com.android.dialer.main.impl.MainActivity"
...>
<intent-filter android:label="#ref/0x7f150123">
<action android:name="com.android.phone.action.RECENT_CALLS" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.TAB" /> TAB HERE - Could I send some EXTRA in INTENT to force Google Dialer to display the 'recent call' TAB?
</intent-filter>
Question:
In manifest we can see this: <category android:name="android.intent.category.TAB" />
How could I use this to force Google Phone app to display the 'recent call' TAB? Adding some EXTRA in the Intent? which one?
(I know, we do not have source code of Google Phone app, but, hopefully they use some standard system / naming convention for EXTRA's in Intent)..
Anyone?
Related
Our app needs to become the default dialer app (also known as: "default phone handler", "default calling app") so it'll be able to make calls under Android's new permissions policy.
We use the following intent to show a system dialog to ask the user to make our app the default:
Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, getPackageName());
startActivityForResult(intent, RC_DEFAULT_PHONE);
This works well on all our test devices, and apparently for most users, but doesn't show any dialog and immediately returns a RESULT_CANCELED result code for some devices.
By looking at the reports, it seems like the majority if not all reports are coming from Huawei devices (and from Huawei's brand - Honor).
Any idea how to display the default call app dialog on those devices?
Any other intent we can run to help the user manually set our app to be the default calling app?
some googling returned that honor devices require you to change the default dialer app in settings, maybe launch intent to that settings page directly? as for the particular settings page argument, you will have to check on a honor device
startActivityForResult(new Intent(android.provider.Settings.ACTION_SETTINGS), 0);
then you can handle the result in your application(by checking if your dialer is default now) else tell user about failure
I had the same problem while building dialer app
Starting from the release Q android os
must but this code to ask default dialer in phone >= Q android
private static final int CHANGE_DEFAULT_DIALER_CODE =25 ;
private void offerReplacingDefaultDialer() {
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME,
this.getPackageName());
startActivity(intent);
}
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
startActivityForResult(intent, CHANGE_DEFAULT_DIALER_CODE);
}
}
Maybe adding following <intent-filter> solve the problem:
<intent-filter>
<action android:name="android.intent.action.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="tel"/>
</intent-filter>
I have 2 apps that are used in conjunction with one another. I want to create a button that launches app A from app B so I am creating an intent like so in app B:
Intent intent = new Intent("com.org.orgmobile.android.action.ACTION_CUSTOM");
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setData(Uri.parse(url));
startActivity(intent);
where url is https://org.org.com/mobile/Support/action/org/ActionEnumValue/?a=123
the intent filter I have declared in app A's manifest is like so:
<intent-filter>
<action android:name="com.org.orgmobile.android.action.ACTION_CUSTOM"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https"/>
<data android:host="org.org.com"/>
<data android:pathPrefix="/mobile/Support/action/${manifestplaceholderattr}.*/ActionEnumValue/*"/>
</intent-filter>
and manifestplaceholderattr is an empty string.
I am getting
android.content.ActivityNotFoundException: No Activity found to handle Intent { act=com.org.orgmobile.android.action.ACTION_CUSTOM dat=https://org.org.com/... flg=0x20000000 }
at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1936)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1615)
at android.app.Activity.startActivityForResult(Activity.java:4472)
at android.app.Activity.startActivityForResult(Activity.java:4430)
at android.app.Activity.startActivity(Activity.java:4791)
at android.app.Activity.startActivity(Activity.java:4759)
What am I overlooking here? I have tried using pathPattern instead of path prefix with no success.
Edit: to provide more detail, I don't just want to launch that specific activity, I want to match the specific intent associated with that activity to launch the activity. I don't want to launch the activity directly because in the case that the user's app is out of date because it won't have the desired behavior.
Best way to do this through Package manager as below.
PackageManager manager = getPackageManager();
try {
Intent intent = manager.getLaunchIntentForPackage("app B package name here");
if (intent == null)
throw new PackageManager.NameNotFoundException();
intent.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
Package manager checks if app with the given package name is installed, if exists it launches the app else it throws NameNotFoundException
You need to use pathPattern instead of pathPrefix. Also you should use .* instead of * at the end of the pattern:
<data android:pathPattern="/mobile/Support/action/${manifestplaceholderattr}.*/ActionEnumValue/.*"/>
This may also not work. If I recall, pathPattern isn't a real regular expression and you may not be able to match the URL like this. Try it and see. If this doesn't work, you'll probably need to rely on just the first part of the path. Let me know how it goes.
See the documentation on path, pathPattern and pathPrefix.
You need to use code as below to launch other App using custom action.
Intent intent = new Intent();
intent.setAction("com.org.orgmobile.android.action.ACTION_CUSTOM");
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setData(Uri.parse(url));
startActivity(intent);
Also make sure both of your application is installed. Also it is better to have check as below to make sure application exists on the phone to handle such Intent.
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
I am trying to test how implicit intent can be used to invoke a component within the same app (I am using Android 4.4).
I have a MainActivity and a Activity2. In the AndroidManifest.xml,
<activity
android:name="com.android.intenttest.Activity2"
android:label="#string/title_activity_activity2" >
<intent-filter>
<action android:name="com.android.intesttest.Activity2Action"/>
</intent-filter>
</activity>
In the MainActivity, I tried using:
Intent intentObj = new Intent();
intentObj.setAction("com.android.intesttest.Activity2Action");
if(intentObj.resolveActivity(getPackageManager()) != null){
startActivity(intentObj);
}
else{
Toast.makeText(getApplicationContext(), "No matching activity found", Toast.LENGTH_SHORT).show();
}
It's unable to invoke Activity2 (I can see the Toast). Can someone please point out what I am doing wrong ?
Thanks.
Edit :
I modified the scenario a bit and can't explain what I observe. I have two apps : App 1 and App 2.
App 1 has :
Activity 2 : intent filter with action 'com.android.intenttest.testAction' and category DEFAULT
Activity 3 : no intent filter
App 2 has :
Activity 2 : intent filter with action 'com.android.intenttest.testAction' and category DEFAULT
In App 1's Activity 3, I have :
Intent intentObj = new Intent();
intentObj.setAction("com.android.intesttest.testAction");
if(intentObj.resolveActivity(getPackageManager()) != null){
startActivity(intentObj);
}
else{
Toast.makeText(getApplicationContext(), "No matching activity found", Toast.LENGTH_SHORT).show();
}
I would expect a chooser dialog to pop up and ask to choose between App 1's Activity2 and App 2's Activity2.
But it always displays Activity2 from App2. Can someone explain why ?
Thanks.
For first part of the question, the intent filter needs to be as follows :
<activity
android:name="com.android.intenttest.Activity2"
android:label="#string/title_activity_activity2" >
<intent-filter>
<action android:name="com.android.intesttest.Activity2Action"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
From Android's documentation :
In order to receive implicit intents, you must include the CATEGORY_DEFAULT
category in the intent filter. The methods startActivity() and
startActivityForResult() treat all intents as if they declared the
CATEGORY_DEFAULT category. If you do not declare this category in your intent
filter, no implicit intents will resolve to your activity.
I am looking to start an activity in my app using a custom action. I have found a few answers but everything I try it throws java.lang.RuntimeException saying No Activity found to handle Intent { act=com.example.foo.bar.YOUR_ACTION }.
This is the activity in my manifest file:
<activity
android:name=".FeedbackActivity" >
<intent-filter>
<action android:name="com.example.foo.bar.YOUR_ACTION" />
</intent-filter>
</activity>
And this is how I'm starting the activity:
Intent intent = new Intent("com.example.foo.bar.YOUR_ACTION");
startActivity(intent);
Any help would be greatly appreciated.
I think what you need is to add a default category to your intent-filter,
eg.
<activity
android:name=".FeedbackActivity" >
<intent-filter>
<action android:name="com.example.foo.bar.YOUR_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
see this answer for more info.
I think you are creating your intent wrong. Try like this:
String CUSTOM_ACTION = "com.example.foo.bar.YOUR_ACTION";
//Intent i = new Intent(this, FeedBackActivity.class); // <--- You might need to do it this way.
Intent i = new Intent();
i.setAction(CUSTOM_ACTION);
startActivity(i);
Just add and intent-filter category as Default.
Implicit intent works perfectly and in many cases its better to use a implicit intent with Intent-action to call a service/Activity than using class-name.
Before startActivty() / startService() with proper context you cane use this method 'queryIntentActivities(Intent intent, int flags)' from package manager class.
It helps the ActivityManager (responsible for launching activities) to check whether the Android system is getting any match with you Intent.
If it doesn't it returns a list size 0 or else >0.
By this you can also check if your app is getting the call,and in this case even if your app is not installed / has got some problem, it will not crash but will throw a warning in Log. Users will face no big trouble apart from app not being launched.
(users will never forgive you if tour app crashes).
Hope this will help !!!
Happy Coding. :)
I faced the same problem when trying to launch the activity residing in the dynamic feature module and starting through action String as the Activity is not resolvable by name at compile time.
So I set the action but the activity crashes every time (No Activity found to handle intent bla bla.. ) until I set the correct package name.
Context c = getApplicationContext();// flag would be require Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag
Intent i = new Intent(action_string);
i.setPackage(context.getPackageName());//this did the trick actually
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
In the Manifest : add catrgory default to the intent filters
from google docs:
<category android:name="android.intent.category.DEFAULT"/>
Note: In order to receive implicit intents, you must include the CATEGORY_DEFAULT category in the intent filter. The methods startActivity() and startActivityForResult() treat all intents as if they declared the CATEGORY_DEFAULT category. If you do not declare it in your intent filter, no implicit intents will resolve to your activity.
In my application I have an option to start navigation to selected POI. Basically what I want is to launch a turn-by-turn navigator from my application. The thing is I don't know which (if any) navigator is installed.
So, the question is how to start an intent by showing a list of suitable activities for navigation to the user first, letting him choose which one he would like to use? Also would be nice to find a way to pass extra parameters to selected activity (this sounds like an issue to me, since different navigation apps use different names for their extras, I guess).
In case it's not clear: I'm looking for a way to DISPLAY A LIST OF SUITABLE APPLICATIONS FOR NAVIGATION WITH THE OPTION TO MAKE ONE DEFAULT.
EDIT: Find here the implementation http://datamoil.blogspot.com/2011/04/android-universal-intent-to-start.html
The bad news is, there isn't a standard Intent URI for navigation.
Yes, google.navigation URIs exist, and an app may choose to support it.
The best solution I can think of is to:
Explicitly check for known apps
Implicitly check for apps hooking google.navigation: and perhaps geo: (but then you also get map apps)
You can enumerate the possible implicit targets using PackageManage.queryIntentActivities
Try:
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse("google.navigation:q=New+York+NY"));
startActivity(intent);
First I used in my onTap method inside the button's listener:
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:q=New+York+NY"));
mContext.startActivity(i);
Then in manifest simply use:
<activity android:name=".LaunchGPS" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
This will open any Navigator App your phone has such as VZ Navigagtor, Google or whatever the phone is loaded with. Worked for me the first time perfectly. Hope this solves your problem.
I found more advantages using this code (just for to show available navigator apps)
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("geo:"));
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivityForResult(Intent.createChooser(intent, "Continues with:", CHOOSE_NAVIGATOR_ID);
} else {
// Handle failure...
}