How to get to another application's specific page - android

I have a widget application, which I have made clickable to reach to another application. However, I am stuck on how to get to a specific fragment in my application. My code is making me open the application, but I want to open a specific page. For that, I tried to put the package of the class I want to be displayed but I got a null pointer exception on the category launcher line. Is it possible? This is my code so far. TIA
Intent in = new Intent(Intent.ACTION_MAIN);
PackageManager manager = context.getPackageManager();
in = manager.getLaunchIntentForPackage("com.playup.android");
in.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent pendingIntent = PendingIntent.getActivity(context,0 /* no requestCode */, in, 0 /* no flags */);

Related

How to programmatically resume an Activity from a notification?

I've been reading a lot of answers on the topic but none of them resolve this issue.
The app is running a "foreground service" so a notification is required.
This is how the Intent is created.
Intent intent = new Intent(context, notificationClass);
return PendingIntent.getActivity(context, 0, intent, 0);
Note that the Activity class to launch from the notification is known at runtime (notificationClass). For extra context, there's a library that exposes a View which when inflated creates the Service which creates the Notification and because any Activity could include the View, the Class is requested so that when the user clicks the notification resumes the correct Activity.
Then, the intent is added to the notificationBuilder (NotificationCompat.Builder).
notificationBuilder.setContentIntent(intent);
Implementing this when the app goes to the background and then the notification is clicked, it creates a fresh copy of the Activity instead of resuming it.
For testing purposes, I got the expected behavior (the Activity is resumed after clicking the notification) by adding the launchMode of the Activity (know beforehand) to singleTop in the AndroidManifest.xml file. But I wasn't able to get it working in any other way.
With these constraints I'm wondering if it's possible to get the same behavior programmatically when the Notification is created. I've tried a bunch of Intent flags combinations (also addFlags vs setFlags) without luck.
Is there any possibility of creating an Intent to behave as explained?
Thanks a lot!
cc'ing https://stackoverflow.com/users/769265/david-wasser that has answered a lot of similar Intent-related questions
Not sure if I completely understand, but if you just want to bring the existing task to the foreground (which is the same as selecting a task from the list of recent tasks), then try this:
PackageManager pm = getPackageManager();
Intent intent = pm.getLaunchIntentForPackage(packageName);
return PendingIntent.getActivity(context, 0, intent, 0);
the packageName is the name of the package in the AndroidManifest.xml.
This "launches" the root Activity for the task, which will not actually launch anything but just brings the existing task to the foreground.
After some trial and error testing, I got it working using the following combination of methods
PackageManager pm = context.getPackageManager();
Intent intent = pm.getLaunchIntentForPackage(context.getPackageName());
intent.setPackage(null);
return PendingIntent.getActivity(context, 0, intent, 0);enter code here
The key part here was intent.setPackage(null);
/**
* (Usually optional) Set an explicit application package name that limits
* the components this Intent will resolve to. If left to the default
* value of null, all components in all applications will considered.
* If non-null, the Intent can only match the components in the given
* application package.
*
* #param packageName The name of the application package to handle the
* intent, or null to allow any application package.
*
* #return Returns the same Intent object, for chaining multiple calls
* into a single statement.
*
* #see #getPackage
* #see #resolveActivity
*/
So apparently, pm.getLaunchIntentForPackage(context.getPackageName()); returns an Intent limited to the components in context.getPackageName() and setting the package explicitly to null is needed afterwards so that all components are considered.
I'm not completely sure about ^ though. FWIW adding it, solved the issue ¯\_(ツ)_/¯

Dynamic Shortcuts to open Fragments in Android

Hi I'm developing an application that has 2 types of user: Admin and normal user; The admin obviously has more actions than the normal user, so I need to use dynamic Shortcuts, depending on the user the Shortcuts are created.
At this point everything is already codified and working. However, at the time of assigning an Intent, even to the MainActivity I receive an error.
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.motusk.Monit", "com.motusk.Monit.activity.MainActivity"));
shortcut.add(new ShortcutInfo.Builder(this, "ub")
.setShortLabel("Location")
.setLongLabel("Location")
.setIcon(Icon.createWithResource(this, R.drawable.bg))
.setIntent(intent)
.build());
I also tried with:
Intent intent = new Intent(this, MainActivity.class);
But, in both I get the error:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.motusk.Monit/com.motusk.Monit.activity.MainActivity}: java.lang.NullPointerException: intent's action must be set
In what I need help with is:
1) How to create a correct Intent to the MainActivity?
2) How to know which shortcut was pressed? (The process of depending on which shortcut was pressed to open a certain Fragment will be performed in the MainActivity, that's why I need to know which Shortcut was pressed).
As per the error message, your Intent must set an action with setAction:
intent.setAction("LOCATION_SHORTCUT");
You can then check the action in your Activity:
String action = getIntent() != null ? getIntent().getAction() : null;
if ("LOCATION_SHORTCUT".equals(action)) {
// Show the correct fragment
}

How to not show app chooser Android

I have an intent that dials a number as follows:
Intent intent = new Intent(isTelephonyEnabled() ? Intent.ACTION_CALL : Intent.ACTION_VIEW);
intent.setData(Uri.parse(number));
cordova.getActivity().startActivity(intent);
callbackContext.success();
The function isTelephoneEnabled() just makes sure that a Telephony Manager exists:
private boolean isTelephonyEnabled(){
TelephonyManager tm = (TelephonyManager)cordova.getActivity().getSystemService(Context.TELEPHONY_SERVICE);
return tm != null && tm.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
}
The issue is, an app chooser opens up and asks for instance if I want to dial using Skype or through the normal Phone app. How do I prevent the app chooser from opening up? I want it to go straight to the default option everytime (the normal phone).
I just give you an idea to do what you want.
You can get the list of all the activities that may receive your intent. Use PackageManager.queryIntentActivities for it. This method returns a list of ResolveInfo objects. And you may retrieve an Activity name out of ResolveInfo
Then you should choose one activity, that you want to open, and send your intent to this activity (use Intent.setComponent for instance).

"smart" android activation from a push notification

I have a simple task - activate an app from the push on a particular activity (not on the start activity)
Imaging I have 3 activities in the app:
A (splash)
B (items list)
C (selected item details)
Some pre-requirements:
With push I'm getting the id of item to select.
On the splash I'm forcing an authentication.
One of the conditions - I couldn't move authentication let's say to another activity or to application service for example.
Now I could create several statements. When I tap on push to activate the app:
When push is arrived the PushIntentService generates a notification which specifies item id in intent extras If the app was terminated I should start the app from the activity A (to force authentication)
If the app was backgrounded (works in background) I should re-activate it at the same place (to skip re-authentication)
Once the app is activate I will navigate to Activity C with item id fetched from extras.
Right now I'm using the following code to generate the notification (item 1, Xamarin.Android syntax):
var resultIntent = new Intent(Application.Context, typeof(SplashScreen));
resultIntent.AddFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
if (extras.ContainsKey("ItemId"))
{
var itemId = extras["ItemId"];
resultIntent.PutExtra("ItemId", itemId);
}
var resultPendingIntent = PendingIntent.GetActivity(Application.Context, 0, resultIntent, 0);
builder.SetContentIntent(resultPendingIntent);
var notification = builder.Build();
This notification works absolutely fine in all cases but I see here one issue.
I'm restarting the app from the very beginning every time I'm tapping on a notification.
What I want is when the app is backgrounded I need just to activate it (like iOS does) and navigate to required page (faster activation and avoid re-authentication).
How can I achieve this and modify the code above?
Create a new Activity for this. The notification should start that Activity (without any flags).
In onCreate() of this new Activity, do something like this:
super.onCreate(...);
if (!isTaskRoot() && alreadyAuthenticated) {
// Go directly to details page
Intent redirectIntent = new Intent(this, Details.class)
redirect.putExtra("id", itemId);
startActivity(redirectIntent);
else {
// This means the app was not running, so redirect to Splash
Intent redirectIntent = new Intent(this, Splash.class)
startActivity(redirectIntent);
}
finish();
isTaskRoot() will return true if the app was not running when the user clicked on the Notification. If the app was already running, it should return false.
To test if you are already authenticated, you could call a static method or check a static variable or maybe you have some other method of doing this. Depending on what you want the Activity stack to look like if the user was already in the "item details" or "item list" activites, you may want to add SINGLE_TOP and/or CLEAR_TOP flags when redirecting to the item details Activity.
Hopefully you get the point.
I found the answer on how to simulate launcher icon tap intent. I'm using it to create pending intent for my push:
var launchIntent = PackageManager.GetLaunchIntentForPackage(PackageName);
This is exactly what I wanted and it works perfectly fine in my case.

When is a Home not a Home (Launcher related)

I am running into a very strange situation whereby I am getting a list of all the installed packages that have a CATEGORY_HOME intent.
My intention is to manually launch the native Home application (which is currently NOT the Default native Home application, because my application has that role).
So, the method I'm using (beneath) correctly identifies that there are two apps that are set up as CATEGORY_HOME.
When I try to launch the one that is mine (fetching the Launcher activity) it works fine. However, when I try to fetch the Launcher activity for the default one, it comes back as null.
So... I'm stumped. How do I determine what I should actually be launching when the package name of the stock home app returns null when I try and pull the relevant Launch activity via getLaunchIntentForPackag from it?
Here's what I'm doing (with some comments to avoid confusion), and for the record I know that not all of them will have the expression "android" in the name space, but I'm trying to get this to work initially on a device that does come back with that string so that part of it isn't the issue.
//get a list of all apps that set themselves up as CATEGORY_HOME
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
final List<ResolveInfo> list = ((PackageManager)getPackageManager()).queryIntentActivities(intent, 0);
String packageName = null;
//look for the one that has the word android in the package name
for(ResolveInfo ri : list){
if(ri.activityInfo.packageName.indexOf("android") != -1)
//this does get set correctly and looks like "com.sec.android.app.launcher"
packageName = ri.activityInfo.packageName;
}
PackageManager pm = SlidePlayer.this
.getPackageManager();
Intent it = pm.getLaunchIntentForPackage(packageName);
//it is NULL so this doesn't work
startActivity(it);
***EDIT
Trying out the following methodology based on CommonsWare's advice...
String packageName = null;
String className = null;
for(ResolveInfo ri : list){
//L.d("HOME PACK = " + ri.);
if(ri.activityInfo.packageName.indexOf("android") != -1){
className = ri.activityInfo.applicationInfo.className;
packageName = ri.activityInfo.applicationInfo.packageName;
}
}
//PackageManager pm = SlidePlayer.this
//.getPackageManager();
Intent it = new Intent();//pm.getLaunchIntentForPackage(packageName);
//both packageName and className appear to be set correctly
//packageName = "com.sec.android.app.launcher"
//className = "com.android.launcher2.LauncherApplication"
it.setClassName(packageName, className);
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(it);
EDIT 2*
semi huzzah...
On one device (Samsung Note II) the key is to set it via Component like so...
Intent it = new Intent();//pm.getLaunchIntentForPackage(packageName);
ComponentName cn = new ComponentName(packageName, className);
it.setComponent(cn);
//it.setClassName(packageName, className);
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(it);
This doesn't work on the Samsung Tab 10" or the Motorola M (which are the only other 2 devices I have thus far tested with this methodology).
getLaucnIntentForPackage looks specifically for CATEGORY_INFO or CATEGORY_LAUNCHER - not CATEGORY_HOME, in which case it will return null. From the Documentation:
public abstract Intent getLaunchIntentForPackage (String packageName)
Added in API level 3 Return a "good" intent to launch a front-door
activity in a package, for use for example to implement an "open"
button when browsing through packages. The current implementation will
look first for a main activity in the category CATEGORY_INFO, next for
a main activity in the category CATEGORY_LAUNCHER, or return null if
neither are found.
However, when I try to fetch the Launcher activity for the default one, it comes back as null.
That's because most firmware home screens are not designed to be launched by home screens, and so probably do not include a launcher activity.
How do I determine what I should actually be launching when the package name of the stock home app returns null when I try and pull the relevant Launch activity via getLaunchIntentForPackag from it?
Since it is a home screen, you know that it must have an activity with the same ACTION_MAIN/CATEGORY_HOME as you do. Find that one and start it.

Categories

Resources