I have the following activity path:
Main -> A0 -> A1 -> A2 -> B
or
Main -> B
On Main, the user can select to either show something on B, or to create something new with the A series.
After completing the A series, it goes to B.
When the user gets to B via the A route, I want the back button to go to Main. But when the user is in the A series, I want the back button to go to the previous A (or Main from the first A).
I tried using FLAG_ACTIVITY_NO_HISTORY when I create the intents, but that just makes everything go back to Main.
Is there a way to mark the activities for removal once I hit a threshold?
You could add a BroadcastReceiver in all activities you want to close (A0, A1, A2):
public class MyActivity extends Activity {
private FinishReceiver finishReceiver;
private static final String ACTION_FINISH =
"com.mypackage.MyActivity.ACTION_FINISH";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
finishReceiver= new FinishReceiver();
registerReceiver(finishReceiver, new IntentFilter(ACTION_FINISH));
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(finishReceiver);
}
private final class FinishReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_FINISH))
finish();
}
}
}
To close these activities trigger the following broadcast from Activity B.
sendBroadcast(new Intent(ACTION_FINISH));
Here is the github example project for the same.
Define the logic in this very way
In Series of Activity A define the logic in this very way
public static Activity A=null;
//in onCreate();
A=this;
//Same way in B
public static Activity B=null;
B=this
now wherever you want the back condition to be checked override the OnbackPressed.
#Override
public void onBackPressed()
{
if(Activity_A.A!=null)
{
finish();
return;
}
if(Activity_B.B!=null){
//your condition
finish();
}
}
You can fire a new intent for Main activity with SINGLE_TOP and CLEAR_TOP flags in onBackPressed of B. So no matter how you landed at B, pressing back takes you to Main with all history cleared. No need to mess with A series activities.
Related
I am new in Android Development. I have been trying to figure out how to display a screen in android studio only for 5 seconds and then get transfered into a new activity.
For example:
Activity A -> Activity B (Shown for 5 seconds) -> Activity C
Also I want to make sure that when a user clicks on the back button while he is in Activity B nothing happens (It doesnt go back to Activity A).
What is the easiest way to do that?
I know I have to use Intent.
try this. I have commented it out, but if you have any questions about it feel free to ask.
public class ClassB extends AppCompatActivity {
//Handler allows you to send and process Runnable Objects (Classes in this case)
private Handler mHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_classb);
//postDelayed method, Causes the Runnable r (in this case Class B) to be added to the message queue, to be run
// after the specified amount of time elapses.
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
//Create a new Intent to go from Class B to Class C and start the new Activity.
Intent intent = new Intent(ClassB.this, ClassC.class);
startActivity(intent);
finish()
}
//Here after the comma you specify the amount of time you want the screen to be delayed. 5000 is for 5 seconds.
}, 5000);
}
//Override onBackPressed method and give it no functionality. This way when the user clicks the back button he will not go back.
public void onBackPressed() {
} }
In Kotlin you can do:
Handler().postDelayed({
// Start activity
startActivity(Intent(this, YourTargetActivity::class.java))
// terminate this activity(optional)
finish()
}, 5000)
I have two activities my MainActivity and some other one called DetailActivity. When the app is first started it opens MainActivity and there it binds DataService and makes a call to fetch some data and populate a list view.
From that list view user has a button to open so called detail view for every item in the list. Opening that detail view means starting the second activity (DetailActivity).
Its done like this:
final Intent intent = new Intent(getContext(), DetailActivity.class);
intent.putExtra("data", dto);
getContext().startActivity(intent);
When second one is opened user is able to go back either by using back button (one left of home button on android) or by clicking back arrow in the header.
Everything works as expected except that when user comes back to MainActivity DataService is binded again and call to fetch the data is made and the list is updated. So if user is somewhere at item no. 205 he will be returned back to the start item.
Is there a way to hold the data or the state of MainActivity when user comes back to it that its not refreshed ?
Service is bonded like this
#Override
protected void onStart() {
super.onStart();
bind(DataService.class);
}
#Override
protected void onDestroy() {
super.onDestroy();
unbindService(connection);
}
private void bind(final Class... toBind) {
for (final Class clazz : toBind) {
bindService(new Intent(this, clazz), connection, BIND_AUTO_CREATE);
}
}
private ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(final ComponentName name, final IBinder service) {
if (service instanceof DataService.LocalBinder) {
dataService = ((DataService.LocalBinder) service).getInstance();
dataService.readData();
}
}
#Override
public void onServiceDisconnected(final ComponentName name) {
// Empty By Default
}
};
You can achieve this by binding the service in onCreate() instead of onStart().
You should also have a look at the symmetry of your life cycle. Currently you are binding in start and unbinding in destroy. If you bind in onStart you should probably unbind in onStop. When you move the binding to onCreate you can keep the unbinding in onDestroy
Explanation: onCreate() is called when the activity is created first. onStart() is called every time your activity becomes visible.
Managed to solve it like this
#Override
public boolean onSupportNavigateUp() {
onBackPressed(); // one inherited from android.support.v4.app.FragmentActivity
return false;
}
Doing it this way seamed to do the trick. I would return to the place where I left from and there is no need to load any data since everything is already there
I have a, what I thought was a, simple task. I have a list of achievements in a recyclerView in my MainActivity. Clicking on one of the achievements launches the AchievementDetailActivity. You do an action in the AchievementDetailActivity and it completes the achievement. When that happens, I need the thumbnail on the MainActivity to be the achievement icon and not the locked icon. What I thought was I could just use the LocalBroadcastManager to send and receive messages from one activity to another, but that isn't working. From everything I have read you are supposed to unregister the listener in your activity onPause and onStop lifecycle methods, however, if you unregister the listener in the onPause of MainActivity, it gets unregistered when the AchievementDetailActivity starts and nothing gets delivered to the MainActivity. I don't understand how you can use LocalBroadCastManager with receivers to send information between activities when they get unregistered as soon as you start a new activity. The following example shows it unregisters as soon as the second activity is started, so the first activity will never get the broadcast...
public class MainActivity extends AppCompatActivity {
public static final String ACTION = "update";
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("MAIN ACTIVITY", "RECEIVED EVENT");
}
}
public void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(this).registerReceiver(receiver), new IntentFilter(ACTION));
}
public void onPause() {
Log.d("MAIN ACTIVITY", "REMOVING LISTENER");
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
}
//.. The rest of MainActivity
}
public class SecondActivity extends AppCompatActivity {
#Override
public void onStart() {
super.onStart();
//initialize view and such
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
Intent intent = new Intent(MainActivity.ACTION);
intent.putExtra("Something", "somewhere");
manager.sendBroadcast(intent);
}
)};
}
If you run the above, obviously with the buttons and such, you will get a message that shows it unregisters the listener as soon as SecondActivity starts and the MainActivity will never get the message it is supposed to get when you click a button on the second activity.
Is there a way to send information from activity to activity, or because of the lifecycle events is that not possible? Everything I read said LocalBroadcastManager was the right way to do it, but it gets unregistered so, how can it? Please help, this is driving me nuts. Thank you.
If you want to use your LocalBroadcastManager to fetch results, do not unregister in onPause, in which case must to unregister in onDestroy().
If you startAvtivity only to fetch some results, it is a good way to startActivityForResult.
Its not a great idea to use LocalBroadcastReceiver for this purpose. Use startActivityForResult instead. Follow this Official doc for implementation details.
The reason its not wokring is:
You are registering the receiver in onStart and unregistering it in onPause(). When your second activity is shown, onPause () will be called as a result the BroadcastReceiver is unregistered.
Its not a great idea to keep it registered since it would lead to memory leaks.
I have a service that is, among other things, downloading images from internet. When this is done I want to show this image in a custom Activity that has a dialog theme. But I only want to use this pop up if the app is running, otherwise I just want to create a notification.
But I get an exception when I try to start an activity from my service and i feel that maybe this isn't the right way to do it?
It says:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
So my question is if this is the right way to do this by setting that flag or how should I get my downloaded image from my service to an activity. Can I in some way tell an activity to start a new activity from my service class?
I think using Broadcast Receiver is better option for you.
Add Below Method in Service and call this method when image Download Complete.
private void updateMyActivity(Context context) {
if(MainActivity.activityStatusFlag){
//update the activity if activityStatusFlag=true;
Intent intent = new Intent("mUpdateActivity");
context.sendBroadcast(intent);
}else{
//display notification if activityStatusFlag=false;
}
}
In Activity Add Following Code.
public class MainActivity extends Activity{
public static boolean activityStatusFlag= false;
//define this variable to check if activity is running or not.
#Override
protected void onResume() {
super.onResume();
activityStatusFlag = true;
this.getApplicationContext().
registerReceiver(mMessageReceiver,new IntentFilter("mUpdateActivity"));
}
#Override
protected void onPause() {
super.onPause();
activityStatusFlag = false;
this.getApplicationContext().unregisterReceiver(mMessageReceiver);
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Display Popup or update Activity
}
};
}
I have a rather commonly occurring situation in Android, which has to do with the previous asynctask updating the activity, whilst the activity has been lost because of a change in orientation.
I have an activity, Activity A.
Activity A implements OnDownloadCompleteListener {
public void sync()
{
new SyncAttestationInfoTask(this).execute();
}
#Override
public void onComplete()
{
loadAttestationInfo();
}
}
Here is my asynctask shortened:
package com.evento.mofa.backgroundtasks;
import java.util.ArrayList;
import java.util.List;
/**
* #author Ahmed
*
*/
public class SyncAttestationInfoTask extends AsyncTask<Void, Void,Void> {
/*TIP*/
//TO SPEED UP THIS OPERATION WE CAN USE THE EXECUTEONEXECUTOR .
private ProgressDialog pd;
private OnDownloadComplete parentActivityContext;
EntityConvert convert = new EntityConvert();
private AttestationDao aDao = new AttestationDao();
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
if (Locale.getDefault().getLanguage().equals("ar"))
{
/*EMPTY ALL THE TABLES THEN START PROCESSING*/
aDao.flushTables(Locale.getDefault().getLanguage());
syncAttestLocations(Webservices.ATTEST_LOCATION_AR,1);
syncDocumentTypes(Webservices.DOCUMENT_TYPES_AR,1);
syncAttestationInfo(Webservices.ATTESTATION_INFO_AR,1);
} else {
/*EMPTY ALL THE TABLES THEN START PROCESSING*/
aDao.flushTables(Locale.getDefault().getLanguage());
syncAttestLocations(Webservices.ATTEST_LOCATION,0);
syncDocumentTypes(Webservices.DOCUMENT_TYPES,0);
syncAttestationInfo(Webservices.ATTESTATION_INFO,0);
}
return null;
}
public SyncAttestationInfoTask(OnDownloadComplete context) {
parentActivityContext = context;
}
#Override
protected void onPreExecute() {
pd = new ProgressDialog((Context)parentActivityContext);
pd.setTitle("Loading...");
pd.setMessage("Updating Data.");
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();
}
#Override
protected void onPostExecute(Void result) {
pd.dismiss();
parentActivityContext.onComplete();
// findViewById(R.id.the_button).setEnabled(true);
}
}
There is something strange with my Activity.
I put a breakpoint on the onComplete callback inside my activity
I start a progress dialog inside the sync async task.
As soon as the progress dialog displays on the screen I landscape my device.
The dialog box vanishes, and pd.dismiss() raises a "View not attached" error (I understand that the activity that it was attached to no longer exists).
The above means that parentActivityContext().oncomplete should also throw the same error, however it does not.
I commented the pd.Dismiss(), and found out that the breakpoint on onComplete() is invoked? Isn't this strange given the fact that the reference to the activity has been lost at this point?
Please give me insight into this.
I would do this
Add this line to your Manifest.xml file, this will prevent of calling onCreate() when screen rotates.
<activity android:name=".yourActivity" android:configChanges="keyboardHidden|orientation">
Version above Android 3.2, you also need to add "screenSize":
<activity android:name=".yourActivity" android:configChanges="keyboardHidden|orientation|screenSize">
This will prevent activity from restarting on orientation change, and you should not have any problems (except maybe some layout fixes)
The previous Activty is being referenced by the AsyncTask via a strong reference.
AsyncTask constructed with Activity 1 (parentActivityContex =
Activity 1)
Activity 1 "destroyed" and Activity 2 comes into foreground
AsyncTask completes, calls parentActivityContext (Activity 1) onComplete
Activity 2 just sits there doing nothing
Activity 1 no longer has active references pointing to it, is collected
You could try doing your task in a Service and having whatever Activity is in the foreground receive a broadcast, or you could try having your AsyncTask reference a Fragment with setRetainInstance(true). Below is an example of the second case. Note that you might want to handle the case where the AsyncTask completes while the Fragment is detached from one Activity and not yet attached to the next Activity
public class ExampleDownloadFragment extends Fragment {
#Override
public void onCreate(final Bundle savedInstanceState) {
setRetainInstance(true);
new SyncAttestationInfoTask(this).execute();
}
public void onComplete() {
final Activity activity = getActivity();
if (activity != null && activity instanceof A) {
((A) activity).onComplete();
}
}
}