I have a profile fragment that I manage with NavgitaionComponents bottomNavigationView, the thing is that from here I want to logout my user, I do this by doing this
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Glide.with(requireContext()).load(FirebaseAuth.getInstance().currentUser?.photoUrl).centerCrop().placeholder(GlideProgress.setupCircularProgress(requireContext())).into(profile_photo)
txt_profile_name.text = FirebaseAuth.getInstance().currentUser!!.displayName
txt_email.text = FirebaseAuth.getInstance().currentUser!!.email
btn_signout.setOnClickListener {
showSignOutProgress()
AuthUI.getInstance().signOut(requireContext()).addOnSuccessListener {
FirebaseAuth.getInstance().signOut()
startActivity(Intent(requireContext(),MainActivity::class.java))
findNavController().popBackStack()
}.addOnFailureListener {
hideSignOutProgress()
}
}
}
My MainActivity handles what navigation graph to go depending if the user is logged in or not, if is logged in it will go to the main_navigation graph and if not to the login_navigation graph
Now, when I start my mainacivity after succefully signed out, when I do findNacController().popBackStack() I expect the current profile fragment to pop and pop all the backstack fragments, so in my login fragment when I go back I cant return to my profile fragment
but this happends
Unable to pause activity FragmentManager is already executing
transactions
Solved it, I needed to finish the whole activity containing the main navigation instead of just poping the profile fragment
added
requireActivity().finish()
instead of
findNavController().popBackStack()
Related
I'm building an app in Kotlin, Android Studio and Firebase.
My user have a button that have a .setOnClickListener (as I show below) to logout form Firebase. But the app closes when I open the activity/fragment where the button is.
logoutButton.setOnClickListener {
val intent =
Intent(activity, AccountFragment::class.java)
Firebase.firebaseAuth.signOut()
startActivity(intent)
}
It isn't giving me any errors on Logcat...at least that I see.
When i change to the fragment that have the button, the app closes and say that the app stopped.
UPDATE: there is an error on the Logcat. It says:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
This is an image of the logcat when I changed the fragment:
Logcat new image
If the button is inside a fragment, then you have place the setOnClickListener code in onViewCreated instead of onCreateView:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
logoutButton.setOnClickListener {
val intent =
Intent(activity, AccountFragment::class.java)
Firebase.firebaseAuth.signOut()
startActivity(intent)
}
}
I would like the onBackPressedDispatcher to absorb the back button press event. Sometimes but I don't see an option for it. Whats happening is that we are just today trying to upgrade to onBackPressedDispatcher in androidX but we have already overridden onBackPressd in activity. so when our onBackPressedDispatcher calls OnBackPressedCallback afterwards there is also a call to activities onBackPressed override. we don't want that. the onBackpressed should be consumed on the spot. here is what I have so far:
const val TAG = "MyTag"
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_first)
MyTester(this)
}
override fun onBackPressed() {
super.onBackPressed()
Log.v(TAG, "Activities class back button Pressed")
}
inner class MyTester(var activity: AppCompatActivity) {
init {
addBackCB()
}
fun addBackCB() {
var callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Log.v(TAG, "inner class MyTester back button")
}
}
activity.onBackPressedDispatcher.addCallback(activity, callback);
}
}
}
Which prints the following:
V/MyTag: inner class MyTester back button
V/MyTag: Activities class back button Pressed
If I don't call super.onBackPressed() then the dispatcher does not even work. it needs that call.
This is because the OnBackPressedDispatcher is called in ComponentActivity.onBackPressed(), if you look at the source code:
#Override
#MainThread
public void onBackPressed() {
mOnBackPressedDispatcher.onBackPressed();
}
So you'll never reach your callback if you override onBackPressed and never call the super method. I think the whole idea behind OnBackPressedDispatcher is that you shouldn't have to override Activity.onBackPressed if you want your fragments to intercept back presses. This is mentioned in one of the guides.
If you really want your activity to handle the back press, you can try something like this in your override:
if (onBackPressedDispatcher.hasEnabledCallbacks()) {
// There's an active callback; let the fragment handle it
super.onBackPressed()
} else {
// Do your activity's back press handling here
}
Be careful doing this, though. After trying it myself, you might find that hasEnabledCallbacks will return true even when you haven't added any callbacks yourself. This is because your FragmentManager has its own back press callback that's going to be enabled whenever backStackEntryCount > 0. I'm not sure if there's a quick workaround for that.
I am testing my app with "Don't Keep Activities" options from developer options. When I try to background the app and launch it , am getting multiple duplicate icons added to the actionbar.
I am adding the context menu icon from the fragment. I have a string passed in a bundle to the fragment. When I background and launch the app, Android tries to recreate the activity and as setHasOptionsMenu(true); is called multiple time during this process and adds duplicate icons to the actionbar.
The following fixes this issue , but would like to know if this is the best approach
if(savedInstanceState == null) {
setHasOptionsMenu(true);
}
Maybe i'm a little late to answer your question, but it can be useful for future users.
The problem origin:
When you background the app and and launch it, the system will save and restore the state of your fragment. Also onCreate() method will be called and a new fragment will be added to your Activity, and you end up with multiple fragments running in your activity, each one added an item to the menu.
The solution:
(+) The correct fix: simply add a null check in your onCreate() method before adding your fragment (to avoid duplication)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
if (savedInstanceState == null) {
Fragment fragment = ...
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
(+) Or a workaround1: Clear your menu inside onCreateOptionsMenu() method of your fragment
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.your_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
(+) Or a workaround2: Call setHasOptionsMenu(true) only when savedInstanceState == null like you mentioned in OP.
I have my MainActivity and inside that I have a number of fragments. I also have another activity that works as my launcher and does everything to do with the Google Drive section of my app. On start up this activity launches, connects to Drive and then launches the MainActivity. I have a button in one of my fragments that, when pushed, needs to call a method in the DriveActivity. I can't create a new instance of DriveActivity because then googleApiClient will be null. Is this possible and how would I go about doing it? I've already tried using getActivity and casting but I'm assuming that isn't working because DriveActivity isn't the fragments parent.
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//TODO for test only remove
directory = new Directory(SDCARD + LOCAL_STORAGE);
byte[] zippedFile = directory.getZippedFile(SDCARD + STORAGE_LOCATION + directory.getZipFileName());
//Here I need to somehow call DriveActivity.uploadFileToDrive(zippedFile);
//((DriveActivity)getActivity()).uploadFileToDrive(zippedFile);
}
});
Right, so I'm having a bit of difficulty with the heirarchy but I think what you want to do is define a method in the fragment that the activity will be required to override to use.
This will allow you to press the button, and then fire a method whos actual implementation is inside the parent.
public interface Callbacks {
/**
* Callback for when an item has been selected.
*/
public void onItemSelected(String id);
}
example implementation:
private static Callbacks sDummyCallbacks = new Callbacks() {
#Override
public void onItemSelected(String id) {
//Button fired logic
}
};
so in the child you'd do just call:
this.onItemSelected("ID of Class");
EDITED
In retrospect what I believe you need is an activity whos sole purpose is to upload files, not fire off other activities.
Heres an example of a 'create file' activity:Google Demo for creating a file on drive
Heres an example of the 'base upload' activity' Base Service creator
There are dozens of this kind of threads, but i couldnt find and solution for my problem (at least working one).
So i have 3 fragments in my viewpager, my last (3rd) fragment is basically a friendlist, i also have a button with which i open a new fragment, where i handle search/request etc. In that fragment i have "back button", i get back to my "3rd" fragment in a viewpager with getFragmentManager().popBackStack(). How can i pass boolean value or something back to the "3rd" fragment?
I tried with manually calling onPause and onResume methods of the 3rd fragment, but then my list is empty. Also no methods of the 3rd fragment is called when i popbackstack.
This is my code
3rd fragment
This is how i open new fragment
ImageButton friendsButton = (ImageButton) v.findViewById(R.id.friendsButton);
friendsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SearchActivity configDetailSectionFragment = new SearchActivity();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.item_detail_container, configDetailSectionFragment);
transaction.addToBackStack(null);
transaction.commit();
}
});
and this is how i get back to the 3rd fragment
ImageButton backButton=(ImageButton)rootView.findViewById(R.id.backButton);
backButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getFragmentManager().popBackStack();
} });
My methods for filling exlistview work fine, just no methods are called after popbackstack, so i could update my list.
What should i change and when and where should i call listadapter.notifyDataSetChanged()?
Using add fragment method with addBackStack, on poBackStack previous fragment's onResume will not get called. So if you are using addBackStack and popBackStack, and you want to call onResume of your previous fragment then you have to use replace instead of add. So your code just changes like -
transaction.replace(R.id.item_detail_container,configDetailSectionFragment);
once you did this you can use your onResume method to refresh list.