• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.providers.media.photopicker.ui;
18 
19 import android.view.View;
20 import android.view.ViewGroup;
21 import android.widget.TextView;
22 
23 import androidx.annotation.NonNull;
24 import androidx.annotation.VisibleForTesting;
25 import androidx.lifecycle.LifecycleOwner;
26 import androidx.lifecycle.LiveData;
27 import androidx.recyclerview.widget.RecyclerView;
28 
29 import com.android.providers.media.R;
30 import com.android.providers.media.photopicker.data.Selection;
31 import com.android.providers.media.photopicker.data.model.Item;
32 import com.android.providers.media.photopicker.util.DateTimeUtils;
33 
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.List;
37 
38 /**
39  * Adapts from model to something RecyclerView understands.
40  */
41 class PhotosTabAdapter extends TabAdapter {
42 
43     private static final int RECENT_MINIMUM_COUNT = 12;
44 
45     private final boolean mShowRecentSection;
46     private final View.OnClickListener mOnMediaItemClickListener;
47     private final View.OnLongClickListener mOnMediaItemLongClickListener;
48     private final Selection mSelection;
49 
PhotosTabAdapter(boolean showRecentSection, @NonNull Selection selection, @NonNull ImageLoader imageLoader, @NonNull View.OnClickListener onMediaItemClickListener, @NonNull View.OnLongClickListener onMediaItemLongClickListener, @NonNull LifecycleOwner lifecycleOwner, @NonNull LiveData<String> cloudMediaProviderAppTitle, @NonNull LiveData<String> cloudMediaAccountName, @NonNull LiveData<Boolean> shouldShowChooseAppBanner, @NonNull LiveData<Boolean> shouldShowCloudMediaAvailableBanner, @NonNull LiveData<Boolean> shouldShowAccountUpdatedBanner, @NonNull LiveData<Boolean> shouldShowChooseAccountBanner, @NonNull OnBannerEventListener onChooseAppBannerEventListener, @NonNull OnBannerEventListener onCloudMediaAvailableBannerEventListener, @NonNull OnBannerEventListener onAccountUpdatedBannerEventListener, @NonNull OnBannerEventListener onChooseAccountBannerEventListener)50     PhotosTabAdapter(boolean showRecentSection,
51             @NonNull Selection selection,
52             @NonNull ImageLoader imageLoader,
53             @NonNull View.OnClickListener onMediaItemClickListener,
54             @NonNull View.OnLongClickListener onMediaItemLongClickListener,
55             @NonNull LifecycleOwner lifecycleOwner,
56             @NonNull LiveData<String> cloudMediaProviderAppTitle,
57             @NonNull LiveData<String> cloudMediaAccountName,
58             @NonNull LiveData<Boolean> shouldShowChooseAppBanner,
59             @NonNull LiveData<Boolean> shouldShowCloudMediaAvailableBanner,
60             @NonNull LiveData<Boolean> shouldShowAccountUpdatedBanner,
61             @NonNull LiveData<Boolean> shouldShowChooseAccountBanner,
62             @NonNull OnBannerEventListener onChooseAppBannerEventListener,
63             @NonNull OnBannerEventListener onCloudMediaAvailableBannerEventListener,
64             @NonNull OnBannerEventListener onAccountUpdatedBannerEventListener,
65             @NonNull OnBannerEventListener onChooseAccountBannerEventListener) {
66         super(imageLoader, lifecycleOwner, cloudMediaProviderAppTitle, cloudMediaAccountName,
67                 shouldShowChooseAppBanner, shouldShowCloudMediaAvailableBanner,
68                 shouldShowAccountUpdatedBanner, shouldShowChooseAccountBanner,
69                 onChooseAppBannerEventListener, onCloudMediaAvailableBannerEventListener,
70                 onAccountUpdatedBannerEventListener, onChooseAccountBannerEventListener);
71         mShowRecentSection = showRecentSection;
72         mSelection = selection;
73         mOnMediaItemClickListener = onMediaItemClickListener;
74         mOnMediaItemLongClickListener = onMediaItemLongClickListener;
75     }
76 
77     @NonNull
78     @Override
createSectionViewHolder(@onNull ViewGroup viewGroup)79     RecyclerView.ViewHolder createSectionViewHolder(@NonNull ViewGroup viewGroup) {
80         final View view = getView(viewGroup, R.layout.item_date_header);
81         return new DateHeaderViewHolder(view);
82     }
83 
84     @NonNull
85     @Override
createMediaItemViewHolder(@onNull ViewGroup viewGroup)86     RecyclerView.ViewHolder createMediaItemViewHolder(@NonNull ViewGroup viewGroup) {
87         final View view = getView(viewGroup, R.layout.item_photo_grid);
88         view.setOnClickListener(mOnMediaItemClickListener);
89         view.setOnLongClickListener(mOnMediaItemLongClickListener);
90 
91         return new MediaItemGridViewHolder(view, mImageLoader, mSelection.canSelectMultiple());
92     }
93 
94     @Override
onBindSectionViewHolder(@onNull RecyclerView.ViewHolder viewHolder, int position)95     void onBindSectionViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
96         final DateHeader dateHeader = (DateHeader) getAdapterItem(position);
97         final DateHeaderViewHolder dateHeaderVH = (DateHeaderViewHolder) viewHolder;
98 
99         dateHeaderVH.bind(dateHeader);
100     }
101 
102     @Override
onBindMediaItemViewHolder(@onNull RecyclerView.ViewHolder viewHolder, int position)103     void onBindMediaItemViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
104         final Item item = (Item) getAdapterItem(position);
105         final MediaItemGridViewHolder mediaItemVH  = (MediaItemGridViewHolder) viewHolder;
106 
107         final boolean isSelected = mSelection.canSelectMultiple()
108                 && mSelection.isItemSelected(item);
109         mediaItemVH.bind(item, isSelected);
110 
111         // We also need to set Item as a tag so that OnClick/OnLongClickListeners can then
112         // retrieve it.
113         mediaItemVH.itemView.setTag(item);
114     }
115 
116     @Override
isItemTypeSection(int position)117     boolean isItemTypeSection(int position) {
118         return getAdapterItem(position) instanceof DateHeader;
119     }
120 
121     @Override
isItemTypeMediaItem(int position)122     boolean isItemTypeMediaItem(int position) {
123         return getAdapterItem(position) instanceof Item;
124     }
125 
setMediaItems(@onNull List<Item> mediaItems)126     void setMediaItems(@NonNull List<Item> mediaItems) {
127         final List<Object> mediaItemsWithDateHeaders;
128         if (!mediaItems.isEmpty()) {
129             // We'll have at least one section
130             mediaItemsWithDateHeaders = new ArrayList<>(mediaItems.size() + 1);
131 
132             // First: show "Recent" section header if needed.
133             if (mShowRecentSection) {
134                 mediaItemsWithDateHeaders.add(new DateHeader(DateHeader.RECENT));
135             }
136 
137             int recentItemsCount = 0;
138             long prevItemDate = -1;
139             for (Item mediaItem : mediaItems) {
140                 final long itemDate = mediaItem.getDateTaken();
141 
142                 if (mShowRecentSection && recentItemsCount < RECENT_MINIMUM_COUNT) {
143                     // The minimum count of items in "Recent" section is not reached yet.
144                     recentItemsCount++;
145                 } else if (!DateTimeUtils.isSameDate(prevItemDate, itemDate)) {
146                     // The dateTaken of these two images are not on the same day: add a new date
147                     // header
148                     mediaItemsWithDateHeaders.add(new DateHeader(itemDate));
149                 }
150 
151                 mediaItemsWithDateHeaders.add(mediaItem);
152 
153                 prevItemDate = itemDate;
154             }
155         } else {
156             mediaItemsWithDateHeaders = Collections.emptyList();
157         }
158         setAllItems(mediaItemsWithDateHeaders);
159 
160         notifyDataSetChanged();
161     }
162 
163     @VisibleForTesting
164     static class DateHeader {
165         static final int RECENT = -1;
166         final long timestamp;
167 
DateHeader(long timestamp)168         DateHeader(long timestamp) {
169             this.timestamp = timestamp;
170         }
171     }
172 
173     private static class DateHeaderViewHolder extends RecyclerView.ViewHolder {
174         final TextView title;
175 
DateHeaderViewHolder(@onNull View itemView)176         DateHeaderViewHolder(@NonNull View itemView) {
177             super(itemView);
178             title = itemView.findViewById(R.id.date_header_title);
179         }
180 
bind(@onNull DateHeader dateHeader)181         void bind(@NonNull DateHeader dateHeader) {
182             if (dateHeader.timestamp == DateHeader.RECENT) {
183                 title.setText(R.string.recent);
184             } else {
185                 title.setText(DateTimeUtils.getDateHeaderString(dateHeader.timestamp));
186             }
187         }
188     }
189 }
190