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.app.Activity;
22 import android.content.res.Resources;
23 import android.graphics.Outline;
24 import android.graphics.drawable.Drawable;
25 import android.util.Log;
26 import android.view.View;
27 import android.view.ViewOutlineProvider;
28 
29 import androidx.annotation.Nullable;
30 import androidx.appcompat.widget.Toolbar;
31 
32 import com.android.documentsui.R;
33 import com.android.documentsui.base.RootInfo;
34 import com.android.documentsui.base.State;
35 import com.android.documentsui.dirlist.AnimationView;
36 
37 import com.google.android.material.appbar.AppBarLayout;
38 import com.google.android.material.appbar.CollapsingToolbarLayout;
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 View mSearchBarView;
55     private final CollapsingToolbarLayout mCollapsingBarLayout;
56     private final Drawable mDefaultActionBarBackground;
57     private final ViewOutlineProvider mSearchBarOutlineProvider;
58     private final boolean mShowSearchBar;
59 
NavigationViewManager( Activity activity, DrawerController drawer, State state, NavigationViewManager.Environment env, Breadcrumb breadcrumb)60     public NavigationViewManager(
61             Activity activity,
62             DrawerController drawer,
63             State state,
64             NavigationViewManager.Environment env,
65             Breadcrumb breadcrumb) {
66 
67         mToolbar = activity.findViewById(R.id.toolbar);
68         mDrawer = drawer;
69         mState = state;
70         mEnv = env;
71         mBreadcrumb = breadcrumb;
72         mBreadcrumb.setup(env, state, this::onNavigationItemSelected);
73 
74         mToolbar.setNavigationOnClickListener(
75                 new View.OnClickListener() {
76                     @Override
77                     public void onClick(View v) {
78                         onNavigationIconClicked();
79                     }
80                 });
81         mSearchBarView = activity.findViewById(R.id.searchbar_title);
82         mCollapsingBarLayout = activity.findViewById(R.id.collapsing_toolbar);
83         mDefaultActionBarBackground = mToolbar.getBackground();
84         mShowSearchBar = activity.getResources().getBoolean(R.bool.show_search_bar);
85 
86         final Resources resources = mToolbar.getResources();
87         final int radius = resources.getDimensionPixelSize(R.dimen.search_bar_radius);
88         final int marginStart =
89                 resources.getDimensionPixelSize(R.dimen.search_bar_background_margin_start);
90         final int marginEnd =
91                 resources.getDimensionPixelSize(R.dimen.search_bar_background_margin_end);
92         mSearchBarOutlineProvider = new ViewOutlineProvider() {
93             @Override
94             public void getOutline(View view, Outline outline) {
95                 outline.setRoundRect(marginStart, 0,
96                         view.getWidth() - marginEnd, view.getHeight(), radius);
97             }
98         };
99     }
100 
setSearchBarClickListener(View.OnClickListener listener)101     public void setSearchBarClickListener(View.OnClickListener listener) {
102         mSearchBarView.setOnClickListener(listener);
103     }
104 
onNavigationIconClicked()105     private void onNavigationIconClicked() {
106         if (mDrawer.isPresent()) {
107             mDrawer.setOpen(true);
108         }
109     }
110 
onNavigationItemSelected(int position)111     void onNavigationItemSelected(int position) {
112         boolean changed = false;
113         while (mState.stack.size() > position + 1) {
114             changed = true;
115             mState.stack.pop();
116         }
117         if (changed) {
118             mEnv.refreshCurrentRootAndDirectory(AnimationView.ANIM_LEAVE);
119         }
120     }
121 
update()122     public void update() {
123         updateScrollFlag();
124         updateToolbar();
125 
126         // TODO: Looks to me like this block is never getting hit.
127         if (mEnv.isSearchExpanded()) {
128             mToolbar.setTitle(null);
129             mBreadcrumb.show(false);
130             return;
131         }
132 
133         mDrawer.setTitle(mEnv.getDrawerTitle());
134 
135         mToolbar.setNavigationIcon(getActionBarIcon());
136         mToolbar.setNavigationContentDescription(R.string.drawer_open);
137 
138         if (shouldShowSearchBar()) {
139             mBreadcrumb.show(false);
140             mToolbar.setTitle(null);
141             mSearchBarView.setVisibility(View.VISIBLE);
142         } else if (mState.stack.size() <= 1) {
143             mBreadcrumb.show(false);
144             mSearchBarView.setVisibility(View.GONE);
145             String title = mEnv.getCurrentRoot().title;
146             if (VERBOSE) Log.v(TAG, "New toolbar title is: " + title);
147             mToolbar.setTitle(title);
148         } else {
149             mBreadcrumb.show(true);
150             mToolbar.setTitle(null);
151             mSearchBarView.setVisibility(View.GONE);
152             mBreadcrumb.postUpdate();
153         }
154     }
155 
updateScrollFlag()156     private void updateScrollFlag() {
157         if (mCollapsingBarLayout == null) {
158             return;
159         }
160 
161         AppBarLayout.LayoutParams lp =
162                 (AppBarLayout.LayoutParams) mCollapsingBarLayout.getLayoutParams();
163         if (shouldShowSearchBar()) {
164             lp.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
165                             | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
166                             | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED);
167         } else {
168             lp.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL
169                             | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED);
170         }
171         mCollapsingBarLayout.setLayoutParams(lp);
172     }
173 
updateToolbar()174     private void updateToolbar() {
175         if (shouldShowSearchBar()) {
176             mToolbar.setBackgroundResource(R.drawable.search_bar_background);
177             mToolbar.setOutlineProvider(mSearchBarOutlineProvider);
178         } else {
179             mToolbar.setBackground(mDefaultActionBarBackground);
180             mToolbar.setOutlineProvider(null);
181         }
182 
183         if (mCollapsingBarLayout != null) {
184             View overlayBackground =
185                     mCollapsingBarLayout.findViewById(R.id.toolbar_background_layout);
186             overlayBackground.setVisibility(shouldShowSearchBar() ? View.GONE : View.VISIBLE);
187         }
188     }
189 
shouldShowSearchBar()190     private boolean shouldShowSearchBar() {
191         return mState.stack.isRecents() && !mEnv.isSearchExpanded() && mShowSearchBar;
192     }
193 
194     // Hamburger if drawer is present, else sad nullness.
getActionBarIcon()195     private @Nullable Drawable getActionBarIcon() {
196         if (mDrawer.isPresent()) {
197             return mToolbar.getContext().getDrawable(R.drawable.ic_hamburger);
198         } else {
199             return null;
200         }
201     }
202 
revealRootsDrawer(boolean open)203     void revealRootsDrawer(boolean open) {
204         mDrawer.setOpen(open);
205     }
206 
207     interface Breadcrumb {
setup(Environment env, State state, IntConsumer listener)208         void setup(Environment env, State state, IntConsumer listener);
show(boolean visibility)209         void show(boolean visibility);
postUpdate()210         void postUpdate();
211     }
212 
213     interface Environment {
214         @Deprecated  // Use CommonAddones#getCurrentRoot
getCurrentRoot()215         RootInfo getCurrentRoot();
getDrawerTitle()216         String getDrawerTitle();
217         @Deprecated  // Use CommonAddones#refreshCurrentRootAndDirectory
refreshCurrentRootAndDirectory(int animation)218         void refreshCurrentRootAndDirectory(int animation);
isSearchExpanded()219         boolean isSearchExpanded();
220     }
221 }
222