Home > database >  Trying to change the starting fragment causes the navigation drawer to malfunction
Trying to change the starting fragment causes the navigation drawer to malfunction

Time:10-03

I am having a hard time understanding what I am doing wrong, so I hope you'll be able to help me.

Context :

I am building an Android application using Android Studio to control and monitor my IoT devices in my house. I started with the Navigation Drawer Activity example, so I have 3 fragments and a navigation drawer :

  • Home fragment ("Accueil") : first fragment to appear with two tests button to create notifications
  • Motion fragment ("Camera de surveillance") : fragment displaying a webview for camera stream
  • Dashboard fragment ("Tableau de bord") : fragment displaying another webview

Navigation drawer
Home fragment

I am currently working with a notification that opens my app directly into the Motion fragment. To do this, I am using an extra with the pendingIntent sent by the notification. Depending on its value, inside the onCreate function of the MainActivity, the navController is forced to display the asked fragment. In my case, I want the notification to display the Motion fragment ("Camera de surveillance").

Notification

Intent intent = getIntent();
String fragmentName = intent.getStringExtra(FRAGMENTNAME);
if (fragmentName == null) fragmentName = "";
switch (fragmentName) {
    case "motion":
        navController.navigate(R.id.nav_motion);
        break;
    case "dashboard":
        navController.navigate(R.id.nav_dashboard);
        break;
    case "home":
    default:
        navController.navigate(R.id.nav_home);
        break;
}

So then, the test : I open my app, click on the test button "Button 1" to display the notification, close my app, click on the notification, and Bingo! The Motion fragment opens first! This works perfectly... or so I thought.

The issue :
By doing so, I get a weird behavior when the app has been opened from the notification: I can't get to the Home fragment.
In the navigation drawer, clicking on the "Accueil" menu to open the Home fragment opens the Motion fragment ("Camera de surveillance"). To see the Home fragment, I need to exit the application and open it again.
I am guessing that clicking on the "Accueil" button on the navigation drawer to display the Home fragment resets the MainActivity and calls the onCreate function, but that doesn't make sense to me as this function is the first callback when the application is created and is not called afterward, is it? Has anyone got an idea of what is really happening and how I can get the wanted behavior?

Edit : After some debugging, using a notification as an indicator, I found that the onCreate function of MainActivity is being called only once at start-up. Moreover, when the navigation drawer is in its "weird behavior" state, clicking the "back" navigation button brings back the Home fragment and corrects the "weird behavior" of the navigation drawer.
What is happening is that the function I use to change the first fragment is the "navigate" function from the navigation controller. It keeps in memory that from the Home fragment (the true first fragment to be displayed), I navigated to the Motion fragment. So as long as I don't go back the navigation backstack by pressing the "Back" button, it keeps in memory that, when I click on the Home fragment, I navigated to the Motion fragment.
I need to either find a way to delete that navigation history, or find another way to display the Motion fragment at the beginning.

Full file source :
MainActivity.java

public class MainActivity extends AppCompatActivity {
    public static final String FRAGMENTNAME = "com.mrxana91dev.maisonpaul.fragmentstartname";

    public NotificationManagerCompat notificationManagerCompat;

    private AppBarConfiguration mAppBarConfiguration;
    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        setSupportActionBar(binding.appBarMain.toolbar);
        binding.appBarMain.fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        DrawerLayout drawer = binding.drawerLayout;
        NavigationView navigationView = binding.navView;
        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        mAppBarConfiguration = new AppBarConfiguration.Builder(
                R.id.nav_home, R.id.nav_motion, R.id.nav_dashboard)
                .setOpenableLayout(drawer)
                .build();
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
        NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
        NavigationUI.setupWithNavController(navigationView, navController);

        this.notificationManagerCompat = NotificationManagerCompat.from(this);

        // ADDED CODE TO CHANGE FIRST FRAGMENT DEPENDING ON FRAGMENTNAME EXTRA
        Intent intent = getIntent();
        String fragmentName = intent.getStringExtra(FRAGMENTNAME);
        if (fragmentName == null) fragmentName = "";
        switch (fragmentName) {
            case "motion":
                navController.navigate(R.id.nav_motion);
                break;
            case "dashboard":
                navController.navigate(R.id.nav_dashboard);
                break;
            case "home":
            default:
                navController.navigate(R.id.nav_home);
                break;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onSupportNavigateUp() {
        NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
        return NavigationUI.navigateUp(navController, mAppBarConfiguration)
                || super.onSupportNavigateUp();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle item selection
        switch (item.getItemId()) {
            case R.id.action_settings:
                Intent intent = new Intent(this, SettingsActivity.class);
                startActivity(intent);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

HomeFragment.java

public class HomeFragment extends Fragment {
    public static final String EXTRA_FRAGMENT = "com.mrxana91dev.maisonpaul.fragmentstartname";

    private FragmentHomeBinding binding;

    public View onCreateView(@NonNull LayoutInflater inflater,
                             ViewGroup container, Bundle savedInstanceState) {
        HomeViewModel homeViewModel =
                new ViewModelProvider(this).get(HomeViewModel.class);

        binding = FragmentHomeBinding.inflate(inflater, container, false);
        View root = binding.getRoot();

        final TextView textView = binding.textHome;
        final TextView textView2 = binding.textView2;
        homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
        homeViewModel.getText2().observe(getViewLifecycleOwner(), textView2::setText);

        final Button testButton1 = binding.testButton1;
        final Button testButton2 = binding.testButton2;
        testButton1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sendOnChannel1();
            }
        });
        testButton2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sendOnChannel2();
            }
        });
        return root;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }

    private void sendOnChannel1()  {
        String title = "Maison Paul - Test notification";
        String message = "Mouvement repéré !";

        MainActivity mainActivity = (MainActivity) getActivity();

        Intent intent = new Intent(mainActivity, MainActivity.class);
        intent.putExtra(EXTRA_FRAGMENT, "motion");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(mainActivity, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);

        Notification notification = new NotificationCompat.Builder(mainActivity, NotificationApp.CHANNEL_1_ID)
                .setSmallIcon(R.drawable.ic_baseline_home_24)
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setCategory(NotificationCompat.CATEGORY_MESSAGE)
                .setContentIntent(pendingIntent)
                .setAutoCancel(true)
                .build();

        int notificationId = 1;
        mainActivity.notificationManagerCompat.notify(notificationId, notification);
    }

    private void sendOnChannel2()  {
        String title = "Maison Paul - Test notification";
        String message = "Notification from channel 2 !";

        MainActivity mainActivity = (MainActivity) getActivity();

        Notification notification = new NotificationCompat.Builder(mainActivity, NotificationApp.CHANNEL_2_ID)
                .setSmallIcon(R.drawable.ic_menu_camera)
                .setContentTitle(title)
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setCategory(NotificationCompat.CATEGORY_PROMO) // Promotion.
                .build();

        int notificationId = 2;
        mainActivity.notificationManagerCompat.notify(notificationId, notification);
    }
}

CodePudding user response:

Alright! I figured it out.
I had to change some things inside the navigation menu (mobile_navigation.xml) by creating two actions :

  • One that navigates from the Home fragment to the Motion fragment
  • One that navigates from the Home fragment to the Dashboard fragment
    Each of those two actions are setup with popUpTo and popUpToInclusive.

mobile_navigation.xml (extract)

<action
    android:id="@ id/action_nav_home_to_nav_dashboard"
    app:destination="@id/nav_dashboard"
    app:launchSingleTop="false"
    app:popUpTo="@id/nav_home"
    app:popUpToInclusive="true" />

Then, instead of directly using the id of the navigation fragment as argument for the "navigate" function, I use the corresponding actions.

MainActivity.java - onCreate (extract)

        Intent intent = getIntent();
        String fragmentName = intent.getStringExtra(FRAGMENTNAME);
        if (fragmentName == null) fragmentName = "";
        switch (fragmentName) {
            case "motion":
                navController.navigate(R.id.action_nav_home_to_nav_motion);
                break;
            case "dashboard":
                navController.navigate(R.id.action_nav_home_to_nav_dashboard);
                break;
            case "home":
            default:
                // No action needed requesting the Home fragment as it's already the starting fragment
                break;
        }

After all those changes, clicking the notification still displays the Motion fragment as intended, and clicking the "Accueil" button in the navigation drawer displays the Home fragment as wanted.
Solved!

  • Related