Dynamic Shortcuts to open Fragments in Android - 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
}

Related

Avoid caching Intent Extras after App is closed with back press [duplicate]

I have issue in intent of my launcher activity.Scenerio is:
1. Send intents form notification service to my launcher activity
PendingIntent contentIntent = PendingIntent.getActivity(this, TripLoggerConstants.PENDING_TRIPS_NOTIFICATION_ID, new Intent(this, MainActivity.class).putExtra("is_log", true), Intent.FLAG_ACTIVITY_CLEAR_TOP);
2. In my MainActivity i getting this intent. code is:
if(this.getIntent().getExtras()!=null){
boolean isLogNewTripScreen = (boolean)this.getIntent().getExtras().getBoolean("is_log");
}
}
3. this work fine but when i come from notification service,but when i launch from not notification service ,that data in intentis still there.How can i remove that data from intent.
EDIT: I've created a sample application to test this problem and possible solutions. Here are my findings:
If you launch your app from a notification with extras and then later return to your app by selecting it from the list of recent tasks, Android will launch the app again the same way it was launched from the notification (ie: with the extras). This is either a bug or a feature, depending on who you ask.
You'll need to add additional code to deal with this situation. I can offer 2 suggestions:
1. Use FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
When you create your notification, set the flag Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS in the Intent. Then, when the user selects the notification and launches the app from the notification, this will not create an entry for this task in the list of recent tasks. Also, if there was an entry in the list of recent tasks for this application, that entry will also be removed. In this case, it will not be possible for the user to return to this task from the list of recent tasks. This solves your problem by removing the possibility that the user launches the app from the list of recent tasks (but only when the app has been launched from the notification).
2. Detect FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
When the user launches your app from the list of recent tasks, Android sets the flag Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY in the Intent that is passed to onCreate() of your launch activity. You can detect the presence of this flag in onCreate() and then you know that the app has been launched from the recent tasks list and not from the notification. In this case, you can just ignore the fact that the extras in the Intent still contain data.
Choose the solution that best suits the workflow for your application. And thanks for the question, this was an interesting challenge to solve :-)
Additional information:
You are creating the PendingIntent incorrectly. You are calling
PendingIntent contentIntent = PendingIntent.getActivity(this,
TripLoggerConstants.PENDING_TRIPS_NOTIFICATION_ID,
new Intent(this, MainActivity.class).putExtra("is_log", true),
Intent.FLAG_ACTIVITY_CLEAR_TOP);
You are passing Intent.FLAG_ACTIVITY_CLEAR_TOP as the 4th parameter to getActivity(). However, that parameter should be PendingIntent flags. If you want to set FLAG_ACTIVITY_CLEAR_TOP on the Intent, you need to do it this way:
PendingIntent contentIntent = PendingIntent.getActivity(this,
TripLoggerConstants.PENDING_TRIPS_NOTIFICATION_ID,
new Intent(this, MainActivity.class).putExtra("is_log", true)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), 0);
I noticed that using fragments. I read a QR Code in Activity A that opens fragment 1, send its content to a webservice and if goes right, replace it with fragment 2. When user press back, the onBackPressed in Activity A call finish. If user select the app again in the list, it was opening fragment 1 instead of fragment 2.
I solved that checking in onBackPressed if extra contains a field indicating that fragment 2 was already opened. If true, moveTaskToBack(true) is called instead of finish()
Activity A
#Override
public void onBackPressed() {
Bundle extras = getIntent().getExtras();
if(extras.containsKey(Constants.TICKET_DONT_SHOW_QRCODE_SCREEN)){
moveTaskToBack(true);
}else {
finish();
}
}
Fragment 2
Intent mainIntent = getActivity().getIntent();
mainIntent.putExtra(Constants.TICKET_DONT_SHOW_QRCODE_SCREEN, true);
getActivity().setIntent(mainIntent);
I've tested all the answers of stackoverflow with no luck, what worked for me was this. Create a helper class to check the activity flags. Or a function, it does not matter.
object FlagHelper {
fun notLaunchedFromNotification(activity: AppCompatActivity): Boolean {
return activity.intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
}
}
Then use as the following code. It returns a boolean so you can check the intent extras when it's false
val notLaunchedFromNotification = FlagHelper.notLaunchedFromNotification(this)
Add android:launchMode="singleInstance" to your launcher activity
and then Use flag Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS while starting your activity

Launch an activity of an application from a different application on Android

I need to launch an activity (not the main activity) of an application from an application I have made. The activity I want to launch is proprietary, hence, I cannot make any changes to its code(or manifest).
For example: I want to launch somebody's Facebook profile from my own application. A normal intent to facebook from my app would open the 'newsfeed'(which I don't want). I want to know how to access any other activity.
Thanks in advance!
The little code I have:
String PACKAGE="com.facebook.katana";
Intent launchIntent = getPackageManager()
.getLaunchIntentForPackage(PACKAGE);
startActivity(launchIntent);
To launch specific activity you need to use explicit intent. Or use implicit intent with action if you know what action that activity answers to.
To use explicit intent you can do the following (provided you call it from the activity):
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.package.name", "com.package.name.ActivityName"));
if(getPackageManager().resolveActivity(intent, 0) != null) {
startActivity(intent);
} else {
Toast.makeText(this, "No app installed that can perform this action", Toast.LENGTH_SHORT).show();
}
You can also add flags to the intent, add actions and categories. As long as the intent can be resolved as viable intent by the PackageManager, it will launch the activity.
Now...
The question about facebook profile, is a different one.
Perhaps, the best way to achieve that would be to use intent with action VIEW and povide Intent.setData with uri to the profile page. That should also be checked for possibility of being resolved correctly. And then will launch the chooser of all supported activities to open it, which should include facebook application. It is then up to user to open the intent using Facebook app or launcher.

"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.

How to catch new intent instead of the old one?

I have my activity registered as Intent filter (plaintext) and have the issue with receiving multiple intents. This is my situation:
I open email client -> mark some text -> press "Share button" and choose my activity which is correctly listed (as it is registered as intent filter for such action)
My activity opens and I can clearly see that intent has correct values in its EXTRA_TEXT (marked text from email)
So far so good...but now, if I press HOME button (which closes my Activity and seems to call onStop()) -> then I open email client again -> mark some DIFFERENT text -> press "Share button", then suddenly my activity opens (I would expect the list of available activities to choose from again) and received intent contains the ORIGINAL text and not the newly marked one in its EXTRA_TEXT.
What am I missing here? How to receive new content via intent this way? Do I need to somehow invalidate previous Intent?
I have to say that using "BACK" instead of "HOME" button works correctly and new intent contains newly marked text as expected. What's the difference here?
I get my Intent this way:
intent = getIntent();
intentAction = intent.getAction();
if (intentAction.equals(Intent.ACTION_SEND) && intent.hasExtra(Intent.EXTRA_TEXT)) {
Bundle bundle = intent.getExtras();
Thanks
Try setting your Activity launchMode to "singleTop" then implement...
#Override
protected void onNewIntent (Intent intent) {
...
}
The Intent passed in to that method should be the new one.
See the docs for onNewIntent
EDIT: For future readers - based on the comments below, it seems the documentation may be incorrect and launchMode should be "singleTask" and not "singleTop".

Launching activity through intents

I am implementing a simple app. I need to start an activity based on the state of the Activity. Lets take i am using a button to start the activity.
1. If the activity is not started, I need to start XYZ activity.
2. If the XYZ activity is on focus, then i need to close the activity on the button press.
3. If the XYZ activity is not in focus (like onPause) state then, I need to change the button state.
Can you please help me in the flags that i need to use for starting the intent.
Is it possible to get the state of activity before I start that activity?
Try this
Intent intent = new Intent(currentActivity.this, callingActivity.class);
startActivity(intent);
You can use intent like this to call an activity
The button press will need to be captured by each activity separately, so just code the different responses into each different activity.
First off create a MAIN.java activity that is going to house your other activities. Like others have said you're going to have to code the button captures yourself because that should be common sense if you're trying to deal with intents. When you get that together though, you can start a new activity through intent like so:
// allocate new intent, initialized to the activity you wish to launch
Intent i = new Intent(this, ActivityToBeLaunched.class);
// put information into intent
i.putExtras("KeyName", value); // where "KeyName" is simply a reference string
// and 'value' can be anything from boolean - string.
// launch activity and wait for response
startActivityForResult(i, REQUEST_CODE);
Then within your ActivityToBeLaunched.java class you'll have an oncreate that will pull information from the intent like such:
// get intent
Intent i = this.getIntent();
// get information from intent
booleanVariable = i.getExtras().getBoolean("KeyName");
When you're done with this activity simply use;
// create intent
Intent i = new Intent();
// put information into result to send back to parent
i.putExtras("KeyName", value);
// set the result to be returned
setResult(i, ResultCode);
// finish child, return to parent with results
finish();

Categories

Resources