1/*
2 * Copyright (C) 2017 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 */
16package com.android.car.media.drawer;
17
18import android.content.Context;
19import android.graphics.Bitmap;
20import android.media.MediaDescription;
21import android.text.TextUtils;
22import android.view.View;
23
24import com.android.car.apps.common.BitmapDownloader;
25import com.android.car.apps.common.BitmapWorkerOptions;
26import com.android.car.apps.common.UriUtils;
27import com.android.car.media.R;
28
29import androidx.car.drawer.CarDrawerAdapter;
30import androidx.car.drawer.DrawerItemViewHolder;
31
32/**
33 * Component that handles fetching of items for {@link MediaDrawerAdapter}.
34 * <p>
35 * It also handles ViewHolder population and item clicks.
36 */
37interface MediaItemsFetcher {
38    int DONT_SCROLL = -1;
39
40    /**
41     * Used to inform owning {@link MediaDrawerAdapter} that items have changed.
42     */
43    interface ItemsUpdatedCallback {
44        void onItemsUpdated();
45    }
46
47    /**
48     * Kick-off fetching/monitoring of items.
49     *
50     * @param callback Callback that is invoked when items are first loaded ar if they change
51     *                 subsequently.
52     */
53    void start(ItemsUpdatedCallback callback);
54
55    /**
56     * @return Number of items currently fetched.
57     */
58    int getItemCount();
59
60    /**
61     * Used to indicate the kind of layout (small or normal) to use for the views that will display
62     * this item in a {@link CarDrawerAdapter}. See {@link CarDrawerAdapter#usesSmallLayout}
63     *
64     * @param position Adapter position of item
65     * @return Whether to use small (true) or normal layout (false).
66     */
67    boolean usesSmallLayout(int position);
68
69    /**
70     * Used by owning {@link MediaDrawerAdapter} to populate views.
71     *
72     * @param holder View-holder to populate.
73     * @param position Item position.
74     */
75    void populateViewHolder(DrawerItemViewHolder holder, int position);
76
77    /**
78     * Used by owning {@link MediaDrawerAdapter} to handle clicks.
79     *
80     * @param position Item position.
81     */
82    void onItemClick(int position);
83
84    /**
85     * Used when this instance is going to be released. Subclasses should release resources.
86     */
87    void cleanup();
88
89
90    /**
91     * Get the position to scroll to if any.
92     * @return An integer greater than or equal to 0 if there is a position to scroll to, the
93     *         constant {@link DONT_SCROLL} otherwise.
94     */
95    int getScrollPosition();
96
97    /**
98     * Utility method to determine if description can be displayed in a small layout.
99     */
100    static boolean usesSmallLayout(MediaDescription description) {
101        // Small layout is sufficient if there's no sub-title to display for the item.
102        return TextUtils.isEmpty(description.getSubtitle());
103    }
104
105    /**
106     * Utility method to populate {@code holder} with details from {@code description}. It populates
107     * title, text and icon at most.
108     */
109    static void populateViewHolderFrom(DrawerItemViewHolder holder, MediaDescription description) {
110        Context context = holder.itemView.getContext();
111        holder.getTitle().setText(description.getTitle());
112        // If normal layout, populate subtitle.
113        if (!usesSmallLayout(description)) {
114            holder.getText().setText(description.getSubtitle());
115        }
116        Bitmap iconBitmap = description.getIconBitmap();
117        holder.getIcon().setImageBitmap(iconBitmap);    // Ok to set null here for clearing.
118        if (iconBitmap == null) {
119            if (description.getIconUri() != null) {
120                holder.getIcon().setVisibility(View.VISIBLE);
121                int bitmapSize =
122                        context.getResources().getDimensionPixelSize(R.dimen.car_primary_icon_size);
123                // We don't want to cache android resources as they are needed to be refreshed after
124                // configuration changes.
125                int cacheFlag = UriUtils.isAndroidResourceUri(description.getIconUri())
126                        ? (BitmapWorkerOptions.CACHE_FLAG_DISK_DISABLED
127                        | BitmapWorkerOptions.CACHE_FLAG_MEM_DISABLED)
128                        : 0;
129                BitmapWorkerOptions options = new BitmapWorkerOptions.Builder(context)
130                        .resource(description.getIconUri())
131                        .height(bitmapSize)
132                        .width(bitmapSize)
133                        .cacheFlag(cacheFlag)
134                        .build();
135                BitmapDownloader.getInstance(context).loadBitmap(options, holder.getIcon());
136            } else {
137                holder.getIcon().setVisibility(View.GONE);
138            }
139        }
140    }
141}
142