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.sidebar;
18 
19 import android.content.Context;
20 import android.graphics.drawable.Drawable;
21 import android.provider.DocumentsProvider;
22 import android.text.TextUtils;
23 import android.text.format.Formatter;
24 import android.view.DragEvent;
25 import android.view.Menu;
26 import android.view.MenuInflater;
27 import android.view.View;
28 import android.widget.ImageView;
29 import android.widget.TextView;
30 
31 import androidx.annotation.Nullable;
32 
33 import com.android.documentsui.ActionHandler;
34 import com.android.documentsui.IconUtils;
35 import com.android.documentsui.MenuManager;
36 import com.android.documentsui.R;
37 import com.android.documentsui.base.DocumentInfo;
38 import com.android.documentsui.base.RootInfo;
39 import com.android.documentsui.base.UserId;
40 
41 import java.util.Objects;
42 
43 /**
44  * An {@link Item} for each root provided by {@link DocumentsProvider}s.
45  */
46 public class RootItem extends Item {
47     private static final String TAG = "RootItem";
48     private static final String STRING_ID_FORMAT = "RootItem{%s/%s}";
49 
50     public final RootInfo root;
51     public @Nullable DocumentInfo docInfo;
52 
53     protected final ActionHandler mActionHandler;
54     protected final boolean mMaybeShowBadge;
55     private final String mPackageName;
56 
RootItem(RootInfo root, ActionHandler actionHandler, boolean maybeShowBadge)57     public RootItem(RootInfo root, ActionHandler actionHandler, boolean maybeShowBadge) {
58         this(root, actionHandler, "" /* packageName */, maybeShowBadge);
59     }
60 
RootItem(RootInfo root, ActionHandler actionHandler, String packageName, boolean maybeShowBadge)61     public RootItem(RootInfo root, ActionHandler actionHandler, String packageName,
62             boolean maybeShowBadge) {
63         super(R.layout.item_root, root.title, getStringId(root), root.userId);
64         this.root = root;
65         mActionHandler = actionHandler;
66         mPackageName = packageName;
67         mMaybeShowBadge = maybeShowBadge;
68     }
69 
getStringId(RootInfo root)70     private static String getStringId(RootInfo root) {
71         // Empty URI authority is invalid, so we can use empty string if root.authority is null.
72         // Directly passing null to String.format() will write "null" which can be a valid URI
73         // authority.
74         String authority = (root.authority == null ? "" : root.authority);
75         return String.format(STRING_ID_FORMAT, authority, root.rootId);
76     }
77 
78     @Override
bindView(View convertView)79     public void bindView(View convertView) {
80         final Context context = convertView.getContext();
81         if (root.supportsEject()) {
82             bindAction(convertView, View.VISIBLE, R.drawable.ic_eject,
83                     context.getResources().getString(R.string.menu_eject_root));
84         } else {
85             bindAction(convertView, View.GONE, -1 /* iconResource */, null /* description */);
86         }
87         // Show available space if no summary
88         String summaryText = root.summary;
89         if (TextUtils.isEmpty(summaryText) && root.availableBytes >= 0) {
90             summaryText = context.getString(R.string.root_available_bytes,
91                     Formatter.formatFileSize(context, root.availableBytes));
92         }
93 
94         bindIconAndTitle(convertView);
95         bindSummary(convertView, summaryText);
96     }
97 
bindAction(View view, int visibility, int iconId, String description)98     protected final void bindAction(View view, int visibility, int iconId, String description) {
99         final ImageView actionIcon = (ImageView) view.findViewById(R.id.action_icon);
100         final View verticalDivider = view.findViewById(R.id.vertical_divider);
101         final View actionIconArea = view.findViewById(R.id.action_icon_area);
102 
103         verticalDivider.setVisibility(visibility);
104         actionIconArea.setVisibility(visibility);
105         actionIconArea.setOnClickListener(visibility == View.VISIBLE ? this::onActionClick : null);
106         if (description != null) {
107             actionIconArea.setContentDescription(description);
108         }
109         if (iconId > 0) {
110             actionIcon.setImageDrawable(IconUtils.applyTintColor(view.getContext(), iconId,
111                     R.color.item_action_icon));
112         }
113     }
114 
onActionClick(View view)115     protected void onActionClick(View view) {
116         RootsFragment.ejectClicked(view, root, mActionHandler);
117     }
118 
bindIconAndTitle(View view)119     protected final void bindIconAndTitle(View view) {
120         bindIcon(view, root.loadDrawerIcon(view.getContext(), mMaybeShowBadge));
121         bindTitle(view);
122     }
123 
bindSummary(View view, String summary)124     protected void bindSummary(View view, String summary) {
125         final TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
126         summaryView.setText(summary);
127         summaryView.setVisibility(TextUtils.isEmpty(summary) ? View.GONE : View.VISIBLE);
128     }
129 
bindIcon(View view, Drawable drawable)130     private void bindIcon(View view, Drawable drawable) {
131         final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
132         icon.setImageDrawable(drawable);
133     }
134 
bindTitle(View view)135     private void bindTitle(View view) {
136         final TextView titleView = (TextView) view.findViewById(android.R.id.title);
137         titleView.setText(title);
138     }
139 
140     @Override
isRoot()141     boolean isRoot() {
142         return true;
143     }
144 
145     @Override
open()146     void open() {
147         mActionHandler.openRoot(root);
148     }
149 
150     @Override
getPackageName()151     public String getPackageName() {
152         return mPackageName;
153     }
154 
155     @Override
getSummary()156     public String getSummary() {
157         return root.summary;
158     }
159 
160     @Override
isDropTarget()161     boolean isDropTarget() {
162         return root.supportsCreate();
163     }
164 
165     @Override
dropOn(DragEvent event)166     boolean dropOn(DragEvent event) {
167         return mActionHandler.dropOn(event, root);
168     }
169 
170     @Override
createContextMenu(Menu menu, MenuInflater inflater, MenuManager menuManager)171     void createContextMenu(Menu menu, MenuInflater inflater, MenuManager menuManager) {
172         inflater.inflate(R.menu.root_context_menu, menu);
173         menuManager.updateRootContextMenu(menu, root, docInfo);
174     }
175 
176     @Override
toString()177     public String toString() {
178         return "RootItem{"
179                 + "id=" + stringId
180                 + ", userId=" + userId
181                 + ", root=" + root
182                 + ", docInfo=" + docInfo
183                 + "}";
184     }
185 
186     @Override
equals(Object o)187     public boolean equals(Object o) {
188         if (o == null) {
189             return false;
190         }
191 
192         if (this == o) {
193             return true;
194         }
195 
196         if (o instanceof RootItem) {
197             RootItem other = (RootItem) o;
198             return Objects.equals(root, other.root)
199                     && Objects.equals(docInfo, other.docInfo)
200                     && Objects.equals(mActionHandler, other.mActionHandler)
201                     && Objects.equals(mPackageName, other.mPackageName);
202         }
203 
204         return false;
205     }
206 
207     @Override
hashCode()208     public int hashCode() {
209         return Objects.hash(root, docInfo, mActionHandler, mPackageName);
210     }
211 
212     /**
213      * Creates a stub root item for a user. A stub root item is used as a place holder when
214      * there is no such root available. We can therefore show the item on the UI.
215      */
createStubItem(RootItem item, UserId targetUser)216     public static RootItem createStubItem(RootItem item, UserId targetUser) {
217         RootInfo stubRootInfo = RootInfo.copyRootInfo(item.root);
218         stubRootInfo.userId = targetUser;
219         RootItem stub = new RootItem(stubRootInfo, item.mActionHandler, item.mMaybeShowBadge);
220         return stub;
221     }
222 }
223