1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.documentsui;
18 
19 import static com.android.documentsui.base.SharedMinimal.VERBOSE;
20 
21 import android.content.res.Resources;
22 import android.graphics.Outline;
23 import android.graphics.drawable.Drawable;
24 import android.util.Log;
25 import android.view.View;
26 import android.view.ViewOutlineProvider;
27 
28 import androidx.annotation.Nullable;
29 import androidx.appcompat.widget.Toolbar;
30 
31 import com.android.documentsui.base.RootInfo;
32 import com.android.documentsui.base.State;
33 import com.android.documentsui.base.UserId;
34 import com.android.documentsui.dirlist.AnimationView;
35 
36 import com.google.android.material.appbar.AppBarLayout;
37 import com.google.android.material.appbar.CollapsingToolbarLayout;
38 import com.google.android.material.tabs.TabLayout;
39 
40 import java.util.function.IntConsumer;
41 
42 /**
43  * A facade over the portions of the app and drawer toolbars.
44  */
45 public class NavigationViewManager {
46 
47     private static final String TAG = "NavigationViewManager";
48 
49     private final DrawerController mDrawer;
50     private final Toolbar mToolbar;
51     private final State mState;
52     private final NavigationViewManager.Environment mEnv;
53     private final Breadcrumb mBreadcrumb;
54     private final ProfileTabs mProfileTabs;
55     private final View mSearchBarView;
56     private final CollapsingToolbarLayout mCollapsingBarLayout;
57     private final Drawable mDefaultActionBarBackground;
58     private final ViewOutlineProvider mSearchBarOutlineProvider;
59     private final boolean mShowSearchBar;
60 
NavigationViewManager( BaseActivity activity, DrawerController drawer, State state, NavigationViewManager.Environment env, Breadcrumb breadcrumb, View tabLayoutContainer, UserIdManager userIdManager)61     public NavigationViewManager(
62             BaseActivity activity,
63             DrawerController drawer,
64             State state,
65             NavigationViewManager.Environment env,
66             Breadcrumb breadcrumb,
67             View tabLayoutContainer,
68             UserIdManager userIdManager) {
69 
70         mToolbar = activity.findViewById(R.id.toolbar);
71         mDrawer = drawer;
72         mState = state;
73         mEnv = env;
74         mBreadcrumb = breadcrumb;
75         mBreadcrumb.setup(env, state, this::onNavigationItemSelected);
76         mProfileTabs = new ProfileTabs(tabLayoutContainer, mState, userIdManager, mEnv, activity);
77 
78         mToolbar.setNavigationOnClickListener(
79                 new View.OnClickListener() {
80                     @Override
81                     public void onClick(View v) {
82                         onNavigationIconClicked();
83                     }
84                 });
85         mSearchBarView = activity.findViewById(R.id.searchbar_title);
86         mCollapsingBarLayout = activity.findViewById(R.id.collapsing_toolbar);
87         mDefaultActionBarBackground = mToolbar.getBackground();
88         mShowSearchBar = activity.getResources().getBoolean(R.bool.show_search_bar);
89 
90         final Resources resources = mToolbar.getResources();
91         final int radius = resources.getDimensionPixelSize(R.dimen.search_bar_radius);
92         final int marginStart =
93                 resources.getDimensionPixelSize(R.dimen.search_bar_background_margin_start);
94         final int marginEnd =
95                 resources.getDimensionPixelSize(R.dimen.search_bar_background_margin_end);
96         mSearchBarOutlineProvider = new ViewOutlineProvider() {
97             @Override
98             public void getOutline(View view, Outline outline) {
99                 outline.setRoundRect(marginStart, 0,
100                         view.getWidth() - marginEnd, view.getHeight(), radius);
101             }
102         };
103     }
104 
setSearchBarClickListener(View.OnClickListener listener)105     public void setSearchBarClickListener(View.OnClickListener listener) {
106         mSearchBarView.setOnClickListener(listener);
107     }
108 
getProfileTabsAddons()109     public ProfileTabsAddons getProfileTabsAddons() {
110         return mProfileTabs;
111     }
112 
113     /**
114      * Sets a listener to the profile tabs.
115      */
setProfileTabsListener(ProfileTabs.Listener listener)116     public void setProfileTabsListener(ProfileTabs.Listener listener) {
117         mProfileTabs.setListener(listener);
118     }
119 
onNavigationIconClicked()120     private void onNavigationIconClicked() {
121         if (mDrawer.isPresent()) {
122             mDrawer.setOpen(true);
123         }
124     }
125 
onNavigationItemSelected(int position)126     void onNavigationItemSelected(int position) {
127         boolean changed = false;
128         while (mState.stack.size() > position + 1) {
129             changed = true;
130             mState.stack.pop();
131         }
132         if (changed) {
133             mEnv.refreshCurrentRootAndDirectory(AnimationView.ANIM_LEAVE);
134         }
135     }
136 
getSelectedUser()137     public UserId getSelectedUser() {
138         return mProfileTabs.getSelectedUser();
139     }
140 
update()141     public void update() {
142         updateScrollFlag();
143         updateToolbar();
144         mProfileTabs.updateView();
145 
146         // TODO: Looks to me like this block is never getting hit.
147         if (mEnv.isSearchExpanded()) {
148             mToolbar.setTitle(null);
149             mBreadcrumb.show(false);
150             return;
151         }
152 
153         mDrawer.setTitle(mEnv.getDrawerTitle());
154 
155         mToolbar.setNavigationIcon(getActionBarIcon());
156         mToolbar.setNavigationContentDescription(R.string.drawer_open);
157 
158         if (shouldShowSearchBar()) {
159             mBreadcrumb.show(false);
160             mToolbar.setTitle(null);
161             mSearchBarView.setVisibility(View.VISIBLE);
162         } else {
163             mSearchBarView.setVisibility(View.GONE);
164             String title = mState.stack.size() <= 1
165                     ? mEnv.getCurrentRoot().title : mState.stack.getTitle();
166             if (VERBOSE) Log.v(TAG, "New toolbar title is: " + title);
167             mToolbar.setTitle(title);
168             mBreadcrumb.show(true);
169             mBreadcrumb.postUpdate();
170         }
171     }
172 
updateScrollFlag()173     private void updateScrollFlag() {
174         if (mCollapsingBarLayout == null) {
175             return;
176         }
177 
178         AppBarLayout.LayoutParams lp =
179                 (AppBarLayout.LayoutParams) mCollapsingBarLayout.getLayoutParams();
180         if (shouldShowSearchBar()) {
181             lp.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
182                             | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
183                             | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED);
184         } else {
185             lp.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
186                             | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
187         }
188         mCollapsingBarLayout.setLayoutParams(lp);
189     }
190 
updateToolbar()191     private void updateToolbar() {
192         if (shouldShowSearchBar()) {
193             mToolbar.setBackgroundResource(R.drawable.search_bar_background);
194             mToolbar.setOutlineProvider(mSearchBarOutlineProvider);
195         } else {
196             mToolbar.setBackground(mDefaultActionBarBackground);
197             mToolbar.setOutlineProvider(null);
198         }
199 
200         if (mCollapsingBarLayout != null) {
201             View overlayBackground =
202                     mCollapsingBarLayout.findViewById(R.id.toolbar_background_layout);
203             overlayBackground.setVisibility(shouldShowSearchBar() ? View.GONE : View.VISIBLE);
204         }
205     }
206 
shouldShowSearchBar()207     private boolean shouldShowSearchBar() {
208         return mState.stack.isRecents() && !mEnv.isSearchExpanded() && mShowSearchBar;
209     }
210 
211     // Hamburger if drawer is present, else sad nullness.
getActionBarIcon()212     private @Nullable Drawable getActionBarIcon() {
213         if (mDrawer.isPresent()) {
214             return mToolbar.getContext().getDrawable(R.drawable.ic_hamburger);
215         } else {
216             return null;
217         }
218     }
219 
revealRootsDrawer(boolean open)220     void revealRootsDrawer(boolean open) {
221         mDrawer.setOpen(open);
222     }
223 
224     interface Breadcrumb {
setup(Environment env, State state, IntConsumer listener)225         void setup(Environment env, State state, IntConsumer listener);
show(boolean visibility)226         void show(boolean visibility);
postUpdate()227         void postUpdate();
228     }
229 
230     interface Environment {
231         @Deprecated  // Use CommonAddones#getCurrentRoot
getCurrentRoot()232         RootInfo getCurrentRoot();
getDrawerTitle()233         String getDrawerTitle();
234         @Deprecated  // Use CommonAddones#refreshCurrentRootAndDirectory
refreshCurrentRootAndDirectory(int animation)235         void refreshCurrentRootAndDirectory(int animation);
isSearchExpanded()236         boolean isSearchExpanded();
237     }
238 }
239