1 /*
2  * Copyright (C) 2014 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.example.androidx.graphics;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.database.Cursor;
22 import android.graphics.Bitmap;
23 import android.net.Uri;
24 import android.os.Bundle;
25 import android.provider.MediaStore;
26 import android.view.Menu;
27 import android.view.MenuInflater;
28 import android.view.MenuItem;
29 import android.view.View;
30 import android.widget.ImageView;
31 import android.widget.ListView;
32 
33 import androidx.appcompat.app.AppCompatActivity;
34 import androidx.cursoradapter.widget.ResourceCursorAdapter;
35 import androidx.fragment.app.ListFragment;
36 import androidx.loader.app.LoaderManager;
37 import androidx.loader.content.CursorLoader;
38 import androidx.loader.content.Loader;
39 import androidx.palette.graphics.Palette;
40 
41 import com.example.androidx.R;
42 
43 import org.jspecify.annotations.NonNull;
44 
45 /**
46  * Activity which displays the images from the device's {@link MediaStore}, alongside the generated
47  * {@link androidx.palette.graphics.Palette} results.
48  *
49  * Allows the customization of the number of colors used in the palette generation, to demonstrate
50  * the difference in results for different types of images.
51  */
52 public class PaletteActivity extends AppCompatActivity {
53 
54     @Override
onCreate(Bundle savedInstanceState)55     protected void onCreate(Bundle savedInstanceState) {
56         super.onCreate(savedInstanceState);
57 
58         getSupportFragmentManager()
59                 .beginTransaction()
60                 .replace(android.R.id.content, new PaletteMediaStoreListFragment())
61                 .commit();
62     }
63 
64     /**
65      * The {@link androidx.fragment.app.ListFragment} which does all of the hard work.
66      */
67     public static class PaletteMediaStoreListFragment extends ListFragment
68             implements LoaderManager.LoaderCallbacks<Cursor> {
69 
70         /**
71          * Projection used for querying the {@link android.provider.MediaStore}.
72          */
73         static final String[] PROJECTION = {
74                 MediaStore.Images.ImageColumns._ID,
75                 MediaStore.Images.ImageColumns.DATE_ADDED
76         };
77 
78         private PhotosCursorAdapter mAdapter;
79 
80         @Override
onCreate(Bundle savedInstanceState)81         public void onCreate(Bundle savedInstanceState) {
82             super.onCreate(savedInstanceState);
83             setHasOptionsMenu(true);
84         }
85 
86         @Override
onViewCreated(View view, Bundle savedInstanceState)87         public void onViewCreated(View view, Bundle savedInstanceState) {
88             super.onViewCreated(view, savedInstanceState);
89 
90             // Enable fast scroll to make it easier to navigate large number of images
91             getListView().setFastScrollEnabled(true);
92         }
93 
94         @Override
onActivityCreated(Bundle savedInstanceState)95         public void onActivityCreated(Bundle savedInstanceState) {
96             super.onActivityCreated(savedInstanceState);
97 
98             // Create an Adapter and use a new Adapter
99             mAdapter = new PhotosCursorAdapter(getActivity(), null);
100             mAdapter.setNumColors(16);
101             setListAdapter(mAdapter);
102 
103             // Start the loader manager to create our CursorLoader
104             getLoaderManager().initLoader(0, null, this);
105         }
106 
107         @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)108         public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
109             inflater.inflate(R.menu.sample_palette_actions, menu);
110         }
111 
112         @Override
onOptionsItemSelected(MenuItem item)113         public boolean onOptionsItemSelected(MenuItem item) {
114             int itemId = item.getItemId();
115             if (itemId == R.id.menu_num_colors_8) {
116                 mAdapter.setNumColors(8);
117                 item.setChecked(true);
118                 return true;
119             } else if (itemId == R.id.menu_num_colors_12) {
120                 mAdapter.setNumColors(12);
121                 item.setChecked(true);
122                 return true;
123             } else if (itemId == R.id.menu_num_colors_16) {
124                 mAdapter.setNumColors(16);
125                 item.setChecked(true);
126                 return true;
127             } else if (itemId == R.id.menu_num_colors_24) {
128                 mAdapter.setNumColors(24);
129                 item.setChecked(true);
130                 return true;
131             } else if (itemId == R.id.menu_num_colors_32) {
132                 mAdapter.setNumColors(32);
133                 item.setChecked(true);
134                 return true;
135             }
136 
137             return super.onOptionsItemSelected(item);
138         }
139 
140         @Override
onListItemClick(ListView l, View v, int position, long id)141         public void onListItemClick(ListView l, View v, int position, long id) {
142             final Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
143                     .appendEncodedPath(String.valueOf(id)).build();
144 
145             // Start the Detail Activity
146             Intent intent = new Intent(getActivity(), PaletteDetailActivity.class);
147             intent.setData(uri);
148             startActivity(intent);
149         }
150 
151         @Override
onCreateLoader(int id, Bundle bundle)152         public @NonNull Loader<Cursor> onCreateLoader(int id, Bundle bundle) {
153             return new CursorLoader(
154                     getActivity(),
155                     MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
156                     PROJECTION,
157                     null,
158                     null,
159                     MediaStore.Images.ImageColumns.DATE_ADDED + " DESC");
160         }
161 
162         @Override
onLoadFinished(@onNull Loader<Cursor> cursorLoader, Cursor cursor)163         public void onLoadFinished(@NonNull Loader<Cursor> cursorLoader, Cursor cursor) {
164             mAdapter.swapCursor(cursor);
165         }
166 
167         @Override
onLoaderReset(@onNull Loader<Cursor> cursorLoader)168         public void onLoaderReset(@NonNull Loader<Cursor> cursorLoader) {
169             mAdapter.swapCursor(null);
170         }
171 
172         private static class PhotosCursorAdapter extends ResourceCursorAdapter {
173 
174             private int mNumColors;
175 
PhotosCursorAdapter(Context context, Cursor c)176             public PhotosCursorAdapter(Context context, Cursor c) {
177                 super(context, R.layout.palette_list_item, c, false);
178             }
179 
180             /**
181              * Set the number of colors used for {@link Palette} generation.
182              */
setNumColors(int numColors)183             void setNumColors(int numColors) {
184                 mNumColors = numColors;
185                 notifyDataSetChanged();
186             }
187 
188             @Override
bindView(final View view, Context context, Cursor cursor)189             public void bindView(final View view, Context context, Cursor cursor) {
190                 // Let's reset the view, clearing the ImageView and resetting the background colors
191                 // of the Palette UI
192                 ImageView imageView = (ImageView) view.findViewById(R.id.image);
193                 imageView.setImageDrawable(null);
194 
195                 view.findViewById(R.id.text_vibrant).setBackground(null);
196                 view.findViewById(R.id.text_muted).setBackground(null);
197                 view.findViewById(R.id.text_light_vibrant).setBackground(null);
198                 view.findViewById(R.id.text_light_muted).setBackground(null);
199                 view.findViewById(R.id.text_dark_vibrant).setBackground(null);
200                 view.findViewById(R.id.text_dark_muted).setBackground(null);
201 
202                 final long id = cursor.getLong(
203                         cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID));
204 
205                 ImageLoader.loadMediaStoreThumbnail(imageView, id, new ImageLoader.Listener() {
206                     @SuppressWarnings("deprecation")
207                     @Override
208                     public void onImageLoaded(Bitmap bitmap) {
209                         new Palette.Builder(bitmap).maximumColorCount(mNumColors).generate(
210                                 new Palette.PaletteAsyncListener() {
211                                     @Override
212                                     public void onGenerated(Palette palette) {
213                                         setBackgroundColor(
214                                                 view.findViewById(R.id.text_vibrant),
215                                                 palette.getVibrantSwatch());
216                                         setBackgroundColor(
217                                                 view.findViewById(R.id.text_muted),
218                                                 palette.getMutedSwatch());
219                                         setBackgroundColor(
220                                                 view.findViewById(R.id.text_light_vibrant),
221                                                 palette.getLightVibrantSwatch());
222                                         setBackgroundColor(
223                                                 view.findViewById(R.id.text_light_muted),
224                                                 palette.getLightMutedSwatch());
225                                         setBackgroundColor(
226                                                 view.findViewById(R.id.text_dark_vibrant),
227                                                 palette.getDarkVibrantSwatch());
228                                         setBackgroundColor(
229                                                 view.findViewById(R.id.text_dark_muted),
230                                                 palette.getDarkMutedSwatch());
231                                     }
232                                 });
233                     }
234                 });
235             }
236         }
237 
setBackgroundColor(View view, Palette.Swatch swatch)238         static void setBackgroundColor(View view, Palette.Swatch swatch) {
239             if (view != null && swatch != null) {
240                 view.setBackgroundColor(swatch.getRgb());
241             }
242         }
243 
244     }
245 
246 }
247