1b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake/*
2b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * Copyright (C) 2017 The Android Open Source Project
3b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake *
4b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * Licensed under the Apache License, Version 2.0 (the "License");
5b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * you may not use this file except in compliance with the License.
6b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * You may obtain a copy of the License at
7b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake *
8b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake *      http://www.apache.org/licenses/LICENSE-2.0
9b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake *
10b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * Unless required by applicable law or agreed to in writing, software
11b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * distributed under the License is distributed on an "AS IS" BASIS,
12b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * See the License for the specific language governing permissions and
14b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * limitations under the License.
15b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake */
16b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
179187ef81d7c051c5e829e8589182d93828ca4a19Ian Lakepackage androidx.navigation.ui;
18b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
19b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.animation.ObjectAnimator;
20b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.animation.ValueAnimator;
21b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lakeimport android.support.annotation.IdRes;
22b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.support.annotation.NonNull;
239187ef81d7c051c5e829e8589182d93828ca4a19Ian Lakeimport android.support.annotation.Nullable;
24b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.support.design.widget.BottomNavigationView;
25b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.support.design.widget.NavigationView;
26b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.support.v4.view.GravityCompat;
27b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.support.v4.widget.DrawerLayout;
28b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.support.v7.app.ActionBar;
29b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.support.v7.app.ActionBarDrawerToggle;
30b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.support.v7.app.AppCompatActivity;
31b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.support.v7.graphics.drawable.DrawerArrowDrawable;
32b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.text.TextUtils;
33b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.view.Menu;
34b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.view.MenuItem;
35b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lakeimport android.view.ViewParent;
36b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
371503d52153986fdcfe7e744795010708b7410892Ian Lakeimport androidx.navigation.NavController;
381503d52153986fdcfe7e744795010708b7410892Ian Lakeimport androidx.navigation.NavDestination;
39b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lakeimport androidx.navigation.NavGraph;
401503d52153986fdcfe7e744795010708b7410892Ian Lakeimport androidx.navigation.NavOptions;
411503d52153986fdcfe7e744795010708b7410892Ian Lake
42b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake/**
43b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * Class which hooks up elements typically in the 'chrome' of your application such as global
44b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake * navigation patterns like a navigation drawer or bottom nav bar with your {@link NavController}.
45b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake */
4655565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lakepublic class NavigationUI {
47b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
48b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    // No instances. Static utilities only.
4955565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake    private NavigationUI() {
50b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    }
51b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
52b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    /**
53b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * Attempt to navigate to the {@link NavDestination} associated with the given MenuItem. This
54b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * MenuItem should have been added via one of the helper methods in this class.
55b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *
56ad7324a8dee986a702d6455158c1d7014243f4b1Ian Lake     * <p>Importantly, it assumes the {@link MenuItem#getItemId() menu item id} matches a valid
57ad7324a8dee986a702d6455158c1d7014243f4b1Ian Lake     * {@link NavDestination#getAction(int) action id} or
58b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * {@link NavDestination#getId() destination id} to be navigated to.</p>
59b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *
60b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * @param item The selected MenuItem.
6155565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * @param navController The NavController that hosts the destination.
62b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * @return True if the {@link NavController} was able to navigate to the destination
63b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * associated with the given MenuItem.
64b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     */
6555565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake    public static boolean onNavDestinationSelected(@NonNull MenuItem item,
6655565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake            @NonNull NavController navController) {
673b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets        return onNavDestinationSelected(item, navController, false);
683b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets    }
693b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets
703b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets    private static boolean onNavDestinationSelected(@NonNull MenuItem item,
713b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets            @NonNull NavController navController, boolean popUp) {
723b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets        NavOptions.Builder builder = new NavOptions.Builder()
733b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets                .setLaunchSingleTop(true)
743b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets                .setEnterAnim(R.anim.nav_default_enter_anim)
753b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets                .setExitAnim(R.anim.nav_default_exit_anim)
763b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets                .setPopEnterAnim(R.anim.nav_default_pop_enter_anim)
773b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets                .setPopExitAnim(R.anim.nav_default_pop_exit_anim);
783b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets        if (popUp) {
79b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake            builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
803b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets        }
813b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets        NavOptions options = builder.build();
82b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        try {
833b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets            //TODO provide proper API instead of using Exceptions as Control-Flow.
843b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets            navController.navigate(item.getItemId(), null, options);
85b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            return true;
86b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        } catch (IllegalArgumentException e) {
87b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            return false;
88b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        }
89b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    }
90b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
91b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    /**
92e4870a3767a88a0644ababb597f87e99bc77648eIan Lake     * Handles the Up button by delegating its behavior to the given NavController. This should
93e4870a3767a88a0644ababb597f87e99bc77648eIan Lake     * generally be called from {@link AppCompatActivity#onSupportNavigateUp()}.
9455565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * <p>If you do not have a {@link DrawerLayout}, you should call
9555565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * {@link NavController#navigateUp()} directly.
96e4870a3767a88a0644ababb597f87e99bc77648eIan Lake     *
97e4870a3767a88a0644ababb597f87e99bc77648eIan Lake     * @param drawerLayout The DrawerLayout that should be opened if you are on the topmost level
98e4870a3767a88a0644ababb597f87e99bc77648eIan Lake     *                     of the app.
9955565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * @param navController The NavController that hosts your content.
100e4870a3767a88a0644ababb597f87e99bc77648eIan Lake     * @return True if the {@link NavController} was able to navigate up.
101e4870a3767a88a0644ababb597f87e99bc77648eIan Lake     */
10255565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake    public static boolean navigateUp(@Nullable DrawerLayout drawerLayout,
10355565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake            @NonNull NavController navController) {
104b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake        if (drawerLayout != null && navController.getCurrentDestination()
105b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake                == findStartDestination(navController.getGraph())) {
106e4870a3767a88a0644ababb597f87e99bc77648eIan Lake            drawerLayout.openDrawer(GravityCompat.START);
107e4870a3767a88a0644ababb597f87e99bc77648eIan Lake            return true;
108e4870a3767a88a0644ababb597f87e99bc77648eIan Lake        } else {
109e4870a3767a88a0644ababb597f87e99bc77648eIan Lake            return navController.navigateUp();
110e4870a3767a88a0644ababb597f87e99bc77648eIan Lake        }
111e4870a3767a88a0644ababb597f87e99bc77648eIan Lake    }
112e4870a3767a88a0644ababb597f87e99bc77648eIan Lake
113e4870a3767a88a0644ababb597f87e99bc77648eIan Lake    /**
114b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
115b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * with a {@link NavController}.
116b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *
117b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * <p>By calling this method, the title in the action bar will automatically be updated when
118b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
119b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *
120b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * <p>The action bar will also display the Up button when you are on a non-root destination.
12155565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * Call {@link #navigateUp(DrawerLayout, NavController)} to handle the Up button.
122b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *
123b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * @param activity The activity hosting the action bar that should be kept in sync with changes
124b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *                 to the NavController.
12555565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * @param navController The NavController that supplies the secondary menu. Navigation actions
12655565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake *                      on this NavController will be reflected in the title of the action bar.
127b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     */
12855565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake    public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
12955565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake            @NonNull NavController navController) {
13055565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake        setupActionBarWithNavController(activity, navController, null);
131b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    }
132b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
133b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    /**
134b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * Sets up the ActionBar returned by {@link AppCompatActivity#getSupportActionBar()} for use
135b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * with a {@link NavController}.
136b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *
137b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * <p>By calling this method, the title in the action bar will automatically be updated when
138b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * the destination changes (assuming there is a valid {@link NavDestination#getLabel label}).
139b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *
140b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * <p>The action bar will also display the Up button when you are on a non-root destination and
141b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * the drawer icon when on the root destination, automatically animating between them.
14255565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * Call {@link #navigateUp(DrawerLayout, NavController)} to handle the Up button.
14355565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     *  @param activity The activity hosting the action bar that should be kept in sync with changes
14455565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     *                 to the NavController.
145e4870a3767a88a0644ababb597f87e99bc77648eIan Lake     * @param navController The NavController whose navigation actions will be reflected
146e4870a3767a88a0644ababb597f87e99bc77648eIan Lake     *                      in the title of the action bar.
147b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * @param drawerLayout The DrawerLayout that should be toggled from the home button
148b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     */
14955565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake    public static void setupActionBarWithNavController(@NonNull AppCompatActivity activity,
15055565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake            @NonNull NavController navController,
15155565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake            @Nullable DrawerLayout drawerLayout) {
1520730cdb7a158e81e3ff5212bbcf964f4b8e7b03bIan Lake        navController.addOnNavigatedListener(
1530730cdb7a158e81e3ff5212bbcf964f4b8e7b03bIan Lake                new ActionBarOnNavigatedListener(activity, drawerLayout));
154b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    }
155b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
156b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    /**
157cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake     * Sets up a {@link NavigationView} for use with a {@link NavController}. This will call
15855565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * {@link #onNavDestinationSelected(MenuItem, NavController)} when a menu item is selected.
159cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake     * The selected item in the NavigationView will automatically be updated when the destination
160cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake     * changes.
161b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *
162b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * @param navigationView The NavigationView that should be kept in sync with changes to the
163b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *                       NavController.
16455565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * @param navController The NavController that supplies the primary and secondary menu.
16555565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake *                      Navigation actions on this NavController will be reflected in the
16655565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake *                      selected item in the NavigationView.
167b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     */
16855565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake    public static void setupWithNavController(@NonNull final NavigationView navigationView,
16955565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake            @NonNull final NavController navController) {
170cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake        navigationView.setNavigationItemSelectedListener(
171cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake                new NavigationView.OnNavigationItemSelectedListener() {
172b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                    @Override
173cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
1743b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets                        boolean handled = onNavDestinationSelected(item, navController, true);
175b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                        if (handled) {
176b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                            ViewParent parent = navigationView.getParent();
177b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                            if (parent instanceof DrawerLayout) {
178b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                                ((DrawerLayout) parent).closeDrawer(navigationView);
179b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                            }
180b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                        }
181e4870a3767a88a0644ababb597f87e99bc77648eIan Lake                        return handled;
182b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                    }
183b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                });
184b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        navController.addOnNavigatedListener(new NavController.OnNavigatedListener() {
185b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            @Override
1869187ef81d7c051c5e829e8589182d93828ca4a19Ian Lake            public void onNavigated(@NonNull NavController controller,
1879187ef81d7c051c5e829e8589182d93828ca4a19Ian Lake                    @NonNull NavDestination destination) {
188b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                Menu menu = navigationView.getMenu();
189b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                for (int h = 0, size = menu.size(); h < size; h++) {
190b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                    MenuItem item = menu.getItem(h);
191b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake                    item.setChecked(matchDestination(destination, item.getItemId()));
192b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                }
193b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            }
194b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        });
195b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    }
196b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
197b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    /**
198cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake     * Sets up a {@link BottomNavigationView} for use with a {@link NavController}. This will call
19955565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * {@link #onNavDestinationSelected(MenuItem, NavController)} when a menu item is selected. The
200cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake     * selected item in the BottomNavigationView will automatically be updated when the destination
201cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake     * changes.
202b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *
203b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * @param bottomNavigationView The BottomNavigationView that should be kept in sync with
204b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     *                             changes to the NavController.
20555565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake     * @param navController The NavController that supplies the primary menu.
20655565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake *                      Navigation actions on this NavController will be reflected in the
20755565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake *                      selected item in the BottomNavigationView.
208b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     */
20955565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake    public static void setupWithNavController(
21055565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake            @NonNull final BottomNavigationView bottomNavigationView,
21155565dc7a0172c95ab6a0d9ceaec0752ef54b940Ian Lake            @NonNull final NavController navController) {
212cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake        bottomNavigationView.setOnNavigationItemSelectedListener(
213cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake                new BottomNavigationView.OnNavigationItemSelectedListener() {
214cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake                    @Override
215cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake                    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
2163b2023c498cd9dfe14244bc1b7f0ac673d039f4bSergey Vasilinets                        return onNavDestinationSelected(item, navController, true);
217cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake                    }
218cdeea78c311ec40b2e870d8de8275c25de9e5be4Ian Lake                });
219b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        navController.addOnNavigatedListener(new NavController.OnNavigatedListener() {
220b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            @Override
2219187ef81d7c051c5e829e8589182d93828ca4a19Ian Lake            public void onNavigated(@NonNull NavController controller,
2229187ef81d7c051c5e829e8589182d93828ca4a19Ian Lake                    @NonNull NavDestination destination) {
223b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                Menu menu = bottomNavigationView.getMenu();
224b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                for (int h = 0, size = menu.size(); h < size; h++) {
225b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                    MenuItem item = menu.getItem(h);
226b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake                    if (matchDestination(destination, item.getItemId())) {
227b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                        item.setChecked(true);
228b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                    }
229b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                }
230b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            }
231b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        });
232b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    }
233b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
234b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    /**
235b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake     * Determines whether the given <code>destId</code> matches the NavDestination. This handles
236b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake     * both the default case (the destination's id matches the given id) and the nested case where
237b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake     * the given id is a parent/grandparent/etc of the destination.
238b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake     */
239b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake    private static boolean matchDestination(@NonNull NavDestination destination,
240b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake            @IdRes int destId) {
241b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake        NavDestination currentDestination = destination;
242b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake        while (currentDestination.getId() != destId && currentDestination.getParent() != null) {
243b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake            currentDestination = currentDestination.getParent();
244b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake        }
245b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake        return currentDestination.getId() == destId;
246b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake    }
247b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake
248b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake    /**
249b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake     * Finds the actual start destination of the graph, handling cases where the graph's starting
250b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake     * destination is itself a NavGraph.
251b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake     */
252b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake    private static NavDestination findStartDestination(@NonNull NavGraph graph) {
253b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake        NavDestination startDestination = graph;
254b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake        while (startDestination instanceof NavGraph) {
255b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake            NavGraph parent = (NavGraph) startDestination;
256b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake            startDestination = parent.findNode(parent.getStartDestination());
257b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake        }
258b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake        return startDestination;
259b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake    }
260b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake
261b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake    /**
262b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * The OnNavigatedListener specifically for keeping the ActionBar updated. This handles both
263b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     * updating the title and updating the Up Indicator transitioning between the
264b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake     */
265b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    private static class ActionBarOnNavigatedListener implements NavController.OnNavigatedListener {
266b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        private final AppCompatActivity mActivity;
2679187ef81d7c051c5e829e8589182d93828ca4a19Ian Lake        @Nullable
268b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        private final DrawerLayout mDrawerLayout;
269b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        private DrawerArrowDrawable mArrowDrawable;
270b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        private ValueAnimator mAnimator;
271b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
272b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        ActionBarOnNavigatedListener(
2739187ef81d7c051c5e829e8589182d93828ca4a19Ian Lake                @NonNull AppCompatActivity activity, @Nullable DrawerLayout drawerLayout) {
274b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            mActivity = activity;
275b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            mDrawerLayout = drawerLayout;
276b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        }
277b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
278b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        @Override
2799187ef81d7c051c5e829e8589182d93828ca4a19Ian Lake        public void onNavigated(@NonNull NavController controller,
2809187ef81d7c051c5e829e8589182d93828ca4a19Ian Lake                @NonNull NavDestination destination) {
281b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            ActionBar actionBar = mActivity.getSupportActionBar();
282b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            CharSequence title = destination.getLabel();
283b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            if (!TextUtils.isEmpty(title)) {
284b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                actionBar.setTitle(title);
285b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            }
286b0cd4b0bbd6ec1aa3958afc7f3be74d949ff3a1eIan Lake            boolean isStartDestination = findStartDestination(controller.getGraph()) == destination;
287b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            actionBar.setDisplayHomeAsUpEnabled(mDrawerLayout != null || !isStartDestination);
2880730cdb7a158e81e3ff5212bbcf964f4b8e7b03bIan Lake            setActionBarUpIndicator(mDrawerLayout != null && isStartDestination);
289b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        }
290b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake
2910730cdb7a158e81e3ff5212bbcf964f4b8e7b03bIan Lake        void setActionBarUpIndicator(boolean showAsDrawerIndicator) {
292b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            ActionBarDrawerToggle.Delegate delegate = mActivity.getDrawerToggleDelegate();
2930730cdb7a158e81e3ff5212bbcf964f4b8e7b03bIan Lake            boolean animate = true;
294b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            if (mArrowDrawable == null) {
295b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                mArrowDrawable = new DrawerArrowDrawable(
296b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                        delegate.getActionBarThemedContext());
297b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                delegate.setActionBarUpIndicator(mArrowDrawable, 0);
2980730cdb7a158e81e3ff5212bbcf964f4b8e7b03bIan Lake                // We're setting the initial state, so skip the animation
2990730cdb7a158e81e3ff5212bbcf964f4b8e7b03bIan Lake                animate = false;
300b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            }
301b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            float endValue = showAsDrawerIndicator ? 0f : 1f;
302b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            if (animate) {
303b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                float startValue = mArrowDrawable.getProgress();
304b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                if (mAnimator != null) {
305b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                    mAnimator.cancel();
306b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                }
307b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                mAnimator = ObjectAnimator.ofFloat(mArrowDrawable, "progress",
308b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                        startValue, endValue);
309b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                mAnimator.start();
310b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            } else {
311b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake                mArrowDrawable.setProgress(endValue);
312b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake            }
313b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake        }
314b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake    }
315b6e6df6f03047d67829ee5e99e7b9c31886f863fIan Lake}
316