Communication between running activities? - java

I'm new to android and is currently developing a quest/tresurehunt application. It is a prototype and is going to be evaluated in the field and i would therefore like to implement some sort of cheat to skip checkpoints if the application should crash during the tests.
My application work the way that when you select a quest a activity will be loaded to handle all checkpoints and the user location(ActivityAdapter.java). This activity will open the diffrent navigation tool activities and pass information to them based on the next checkpont using intent. I have implemented a long press event in the app i would like to activate the skip/cheat. My problem is that i can't figure out how to do this.
Location changed event of ActivityAdapter.java:
public void onLocationChanged(Location location) {
location.distanceBetween(location.getLatitude(), location.getLongitude(), lat, lng, dist);
if (dist[0]<= 10) {
if (cid == checkpoints.size()) {
Intent intent = new Intent(ActivityAdapter.this, SuccessActivity.class);
intent.putExtra("title", checkpoints.get(cid).get("title"));
startActivity(intent);
} else {
new loadAndStartQuest().execute();
}
}
}
loadAndStartQuest() just find the next checkpoint and start the right activity (navigationtool). I have tryed creating a object of the ActivityAdapter and set a variable and add it to the the if statement which did not work. I guess it is because it will create another instance of the activity and not affect the current/running one. So how would you communicate between two running activities?

To send data from Activity1 to running Activity2, you must pass through the next steps:
In Manifest.xml set launch mode "singleTask" for Activity2
<activity android:name="Activity2" android:launchMode="singleTask">
Put extras and start Activity2 from Activity1 (if Activity2 was started before, intent will be sended to existing instance).
Intent intent = new Intent(getContext(), Activity2.class);
intent.putExtra("CHECK_POINT", checkPointData);
startActivity(intent)
Override method onNewIntent() in Activity2
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
onNewCheckPoint();
}
private void onNewCheckPoint(){
Intent intent = getIntent();
Bundle extras = intent.getExtras();
//in this moment, you can process data, like you want.
}

Related

Open Activity on notification button click when app is closed

I'm trying to open the MainActivity when the user clicks a button in my notification, while the app is only running in the background with a service. When the button is clicked, these lines are triggered in the Service class:
Intent openApp = new Intent(this, MainActivity.class);
openApp.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openApp);
I've checked it, and the lines are triggered, so there's no problem in reacting to the button's click, the Activity won't open though.
Any suggestions? Why isn't this working for me and how can I make it work?
Edit
I was asked for some more code, so in my onStartCommand() inside my Service, if it starts with a stop-action within its intent, I call the killService() method, which kills the Service, starts the MainActivity and do some other stuff:
if (action != null && action.equals(ACTION_STOP_SERVICE)) {
killService();
}
To set the Notifications button, I use this code:
Intent stopActionIntent = new Intent(this, TimerService.class);
stopActionIntent.setAction(ACTION_STOP_SERVICE);
PendingIntent stopActionPendingIntent = PendingIntent.getService(this, 1, stopActionIntent, PendingIntent.FLAG_IMMUTABLE);
timerNotificationBuilder.addAction(R.drawable.stop, "Stop", stopActionPendingIntent);
And as I said, the button already reacts to the user clicking on it, so that's not the problem.
You can try to receive the click in a BroadcastReceiver and then open activity from there.
Try this to add a action button o your notification:
timerNotificationBuilder.addAction(createNotificationActionButton("STOP");
Where the createNotificationActionButton method is this:
public NotificationCompat.Action createNotificationActionButton(String text){
Intent intent = new Intent(this, StopwatchNotificationActionReceiver.class);
#SuppressLint("InlinedApi") PendingIntent pendingIntent = PendingIntent.getBroadcast(this, new Random().nextInt(100), intent, PendingIntent.FLAG_IMMUTABLE);
return new NotificationCompat.Action(0, text, pendingIntent);
}
Create a class named StopwatchNotificationActionReceiver and make it extent a BroadcastReceiver`. This is the code for that class:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class StopwatchNotificationActionReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
PrefUtil.setIsRunningInBackground(context, false);
PrefUtil.setTimerSecondsPassed(context, 0);
PrefUtil.setWasTimerRunning(context, false);
context.stopService(MainActivity.serviceIntent);
Intent activityIntent = new Intent(context, MainActivity.class);
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActvity(activityIntent);
}
}
Also you need to register that receiver in your manifest like this:
<receiver android:name="StopwatchNotificationActionReceiver"/>
Where the MainActivity.serviceIntent is a public static variable which looks like this:
public static Intent serviceIntent;
And this intent is only used to start the service like this:
//In onCreate
serviceIntent = new Intent(this, TimerService.class);
//In onPause
PrefUtil.setTimerSecondsPassed(this,seconds);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent);
}
Or you can try the simple method:
if (action != null && action.equals(ACTION_STOP_SERVICE)) {
Context context = this;
Intent activityIntent = new Intent(context, MainActivity.class);
activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActvity(activityIntent);
killService();
}
Edit
Another solution is here. Again. You need to refer to my repo as I have made changes to the files in order to complete your task. In the service class, refer to this method. There, I start the activity if the action is reset(r). Or else, it opens the broadcast receiver. Then, in the activity, I receive that extra in the onResume() method. If the reset button is not clicked, it opens the Receiver class.
And as always, you can view the result of the app from here.
I hope that code will do your work.
I found it! See this answer.
This answer suggests enabling ScreeanOverlay settings because as of Android 10 and later you can no longer open an activity from the background just by calling the lines I've used.
To make it work, you'd have to add this permission through your Manifest.xml:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
And then the user would have to enable the Display over other apps setting.
I searched for an option to get the user to this setting more easily and found this answer.
This answer gives a code that redirects the user to the Display over other apps setting
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 0);
}
and then I guide the user with the notification's content (text) on how to enable the setting.
Once the setting is enabled, The lines I've used before work.\
So, problem solved?
Not Completely Solved
this whole configuration described above works, but only if the app is not killed.
If the app was killed and I try the method listed above, the app joins the recent apps list, but won't open and show up.
A solution that solves this issue as well will be accepted as an answer instead of this one.

Starting an Activity from a Notification and passing data onBackPressed

I have 3 Activities on the app:
MainMenuActivity -> ExecuteTrainingActivity -> ExecuteExerciseActivity.
From MainMenuActivity to ExecuteTrainingActivity, I'm passing an idExecution and an idExercise for ExecuteTrainingActivity query and load the initial data.
ExecuteTrainingActivity onCreate:
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
initialize();
setupRecyclerView(exercises);
}
private void initialize() {
Bundle extras = getIntent().getExtras();
if (extras != null) {
if (extras.containsKey("id_execution")) {
idExecution = extras.getLong("id_execution");
idExercise = extras.getLong("id_exercise");
execution = queryExecution(idExecution);
} else {
insertExecution();
}
}
}
In the 3rd activity, ExecuteExerciseActivity, I have a TimerFragment that, and when TimerCountdown reaches 0, it opens a Notification popup, that when clicked open an fresh ExecuteExerciseActivity.
On this TimerFragment, I'm passing as Extras the same ids, so I can get them in the new fresh ExecuteExerciseActivity:
public class TimerFragment extends Fragment {
//...
private void showNotification(){
Intent intent = new Intent(getActivity(), ExecuteExerciseActivity.class);
intent.putExtra("id_execution", idExecution);
intent.putExtra("id_exercise", idExercise);
intent.putExtra("position", position);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(getActivity());
stackBuilder.addNextIntentWithParentStack(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
/*=== CHECK IF NOTIFICATION CHANNEL IS ACTIVE ===*/
boolean ok = isNotificationChannelEnabled(getActivity(), Constants.CHANNEL_ID);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(requireNonNull(getActivity()), Constants.CHANNEL_ID)
.setSmallIcon(R.drawable.d77)
.setContentTitle("Teste Notificação")
.setContentText("Ababa")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getActivity());
notificationManager.notify(0, mBuilder.build());
}
From this new fresh ExecuteExerciseActivity, I want to make the system maintain the same navigation flow of Activities, but when I backpress from the new ExecuteExerciseActivity to ExecuteTrainingActivity, i can't pass the Id's for the ExecuteTrainingActivity query and load.
Is there a way to pass arguments onBackPress?
Is the best approach override onBackPress creating a new intent and a starting a new Activity?
**My manifest is using parentActivityName correctly.
Save your query and id into a SharedPreferences in onDestroy of your ExecuteExerciseActivity then pull out the query and id again in the old ExecuteTrainingActivity. onBackPressed triggers onDestroy event of an activity's life cycle. Then in the onResume of ExecuteTrainingActivity pull this data back out.
I think you can achieve this by overriding onOptionsItemSelected() method of ExecuteExerciseActivity. Try this:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
Intent intent = new Intent(this, ExecuteExerciseActivity.class);
//Add the extras to the intent
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
In Android, if your Activity starts another Activity and want to pass data to that Activity, you would add those data as Intent extras.
This Activity A to Activity B type of sending data is something you're using already.
But for Activity B back to Activity A, there's actually a built-in solution for this, which is through startActivityForResult(Intent, REQUEST_CODE) instead of startActivity(Intent).
Now in Activity B, you simply have to code:
#Override
public void onBackPressed()
{
Intent resultIntent = getIntent();
resultIntent.putExtra(EXTRA_NAME, extra_value);
setResult(Activity.RESULT_OK, resultIntent);
finish();
}
Basically, in Activity B, you're setting the data you want to send back to the Activity A that started Activity B, since there's a connection between these two Activities.
Next, in your Activity A, just override the onActivityResult() method.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE) {
if (resultCode == RESULT_OK) {
// get data through data.getIntExtra() and etc
}
}
}
The request code needs to match the REQUEST_CODE you used for Activity B, so Activity A knows which Activity to respond to. The result code is just a quick way for you to categorize the type of result you're getting back from Activity B, since there might be more than one type of result being returned from Activity B.
This solution of passing data is better than creating a new Intent and starting a new Activity because you don't need to start a new Activity. Your Activity A already exists, so there's no reason to rebuild the entire layout, reload all the data, and have a new Activity existing on the Activity stack.
Since your only intention is to pass data from Activity B back to the Activity A that started it, use startActivityForResult() and onActivityResult() to handle this type of data sharing.

How to restart activity using intent?

I am making an android app which plays mp3 files. I am launching the mp3 playing activity from within another activity using intent:
Intent intent=new Intent(ListViewA.this,mp3player.class);
intent.putExtra("feed",gh);
i.setFlags(0);
i.setPackage(null);
//intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
Now when the user selects another song from a list, I want to close the previous instance of the mp3player activity and start a new one using the intent code (above). How do I go about doing that? Thanks.
Instead of startActivity use startActivityForResult(intent, PLAY_SONG) then you can call finishActivity(PLAY_SONG) where PLAY_SONG is a constant number.
class member
private static final int PLAY_SONG
And then
finishActivity(PLAY_SONG);
Intent intent=new Intent(ListViewA.this,mp3player.class);
intent.putExtra("feed",gh);
i.setFlags(0);
i.setPackage(null);
startActivityForResult(intent, PLAY_SONG)
Hey its very simple Instead of calling Intent from Mp3Playeractivity call finish() when you are pressing the back button by implementing
#Override
public void onBackPressed(){
finish();
}
which will cause close your MpeplayerActivity

Activity being created twice on android

First of all, I'm new to Android Development so please have patience with me.
I'll start from the UI, I have a button that once you tap it, starts an activity for a result.
public class GUIActivity extends Activity
#Override
public void onClick(....){
Intent intent = new Intent(getApplicationContext(), GetImageActivity.class);
intent.putExtra("action", FROM_CAMERA);
startActivityForResult(intent, GET_IMAGE);
}
#Override
onActivityResult(int requestCode, int resultCode, Intent data){
Log(TAG, "onActivityResult");
//handle result
}
}
The GetImageActivity class is a wrapper for two other activities, one to capture an image from the camera and other to get it from the gallery. It returns and Uri object of the selected image.
public class GetImageActivity extends Activity{
private Uri mediaUri;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
int action = extras.getInt("action");
Log.d(TAG, "onCreate");
switch(action){
case FROM_CAMERA:
mediaUri = Uri.fromFile(new File(....));
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, mediaUri);
Log.d(TAG, "Calling camera activity"
startActivityForResult(intent, action);
break;
case FROM GALLERY:
//...
}
}
#Override
onActivityResult(int requestCode, int resultCode, Intent data){
Log.d(TAG, "onActivityResult");
switch(requestCode){
case FROM_CAMERA:
if(resultCode == Activity.RESULT_OK){
Intent data = new Intent();
data.putExtra("uri", mediaUri);
setResult(Activity.RESULT_OK, data);
finish();
}else{
Log.e(TAG, "Camera activity failed!");
setResult(Activity.RESULT_CANCELED);
finish();
}
break;
case FROM_GALLERY:
//...
}
}
}
This is what is expected to happen when the user clicks on the button:
The camera activity should start.
Once the user takes a picture it should send back its URI to the GUI class.
Sometimes (it's usually a 50% chance) it works at expected, but other times this is what happens:
The camera activity starts.
The user takes a picture.
The camera activity starts AGAIN.
The user can either take another picture or go back.
Either case, the URI that gets back to the GUI class does not exist.
I've added a couple of debug log lines to follow the sequence of events. When I get the bad behaviour this is the output I get:
GetImageActivity - onCreate
GetImageActivity - Calling Camera Activity
The camera opens, and once I've taken a picture it says:
GetImageActivity - onCreate (again)
GetImageActivity - Calling Camera Activity
GetImageActivity - onActivityResult
The camera opens for the second time. The user takes another picture and:
GetImageActivity - onActivityResult
GUIActivity - onActivityResult
So my question is... what could cause the GetImageActivity to be called twice?
The problem is improper handling of the Activity lifecycle.
The second call to onCreate is for handling the result.
Android may choose to destroy an Activity that is waiting for the call to onActivityResult; especially when free memory is running low. Some devices appear more aggressive about destroying Activitys that are on the task stack. I can reliably recreate the issue on a Samsung device set to a debugging mode called "strict mode".
You can verify whether this is your issue by logging calls to onCreate & onDestroy.
In the case of a destroyed activity, when the activity result needs to be processed, Android will recreate the Activity, passing a savedInstanceState to onCreate. So, the remedy is to check the value of savedInstanceState in your GetImageActivity.onCreate. If it is not null then don't make any calls to startActivity because your Activity is being recreated to call onActivityResult.
Optionally, if you need to preserve any state then override onSaveInstanceState(Bundle outState) and put data you need into outState.
Add this to your Activity definition in AndroidManifest.xml:
android:launchMode = "singleTask"
Add this to your Activity definition in AndroidManifest.xml:
android:launchMode = "singleInstance"
When you call startActivityForResult it is telling Android that you want the result to be delivered to the Activity which is making that call. Which explains what you see when you logged GetImageActivity - onCreate (again).
Since you call startActivityForResult in your first activity, that is, GUIActivity than you should be calling setResult in GetImageActivity to properly pass back the result.
So in GetImageActivity.onActivityResult() right before you call finish() you should call setResult so that when you return back to GUIActivity it can handle the expected result.

Android: Passing data between activities - multiple intent.putExtra not working?

Having a very annoying problem with passing data between activities.
This is the code I use to successfully pass the value of a progress bar to a different activity:
public void WhenClicked(View view)
{
view.clearAnimation();
Intent intent = new Intent("com.android.Test.QUESTION");
if (progressBar != null)
{
if (progressBar.getProgress() != 0)
{
intent.putExtra("ProgressBarValue", progressBar.getProgress());
}
}
startActivity(intent);
}
Okay, so that worked. Now, when I change it to this, it blows up:
public void WhenClicked(View view, String category)
{
view.clearAnimation();
Intent intent = new Intent("com.android.Test.QUESTION");
intent.putExtra("Category", category);
if (progressBar != null)
{
if (progressBar.getProgress() != 0)
{
intent.putExtra("ProgressBarValue", progressBar.getProgress());
}
}
startActivity(intent);
}
I don't understand what the problem is. I've even tried sticking it all into a bundle and adding the bundle as an extra - that just made it crash as well. Maybe I'm being stupid and I've just been staring at my code too long, but any help would be great!
This is my first time with Android and it's killing me!
Thanks in advance guys!
first you need to create bundle object(Bundle bnd=new Bundle();) and next bnd.putString("param1", "test");
next create intent:
Intent myIntent = new Intent(current classname.this,nextactivity.class);
myIntent.putExtras(bnd);
startActivityForResult(myIntent, 0);
In 2nd activity u need to get bundel value like :
Bundle bundle = this.getIntent().getExtras();
String _getData=bundle.getString("param1");
Assuming that you have an activity called QUESTION, you might try to put .class on the end as well as "this" for first param:
Intent intent = new Intent(this, QUESTION.class);
if you don't have a QUESTION activity then that is another problem. I'm assuming your activities are in the same app?
I believe, the activity which should handle this intent action com.android.Test.QUESTION does not understand your category i.e intent.putExtra("Category", category);.
You can try fixing it in 2 ways:
If the receiving activity is from your own application, then try using explicit intent i.e Intent intent = new Intent("youCurrentClass","theClassYouWantToCall"); without feeding additional category, this will launch the specified activity. In case of explicit Intent Android system does not do a comparison with the intent-filters to match Intent Object.
Change the category section in relation to intent receiving Activity.
Hope this helps,
sku

Categories