Skip to content

Commit a106f0a

Browse files
finnurbrekiCommit bot
authored andcommitted
Photo Picker Dialog: Recursively traverse the photo directories.
(Only visible change, since the picker is not decoding yet, is that it will show as many gray squares as there are photos on disk). BUG=656015 Review-Url: https://codereview.chromium.org/2810773002 Cr-Commit-Position: refs/heads/master@{#464641}
1 parent 5be07ac commit a106f0a

File tree

5 files changed

+260
-22
lines changed

5 files changed

+260
-22
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright 2017 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package org.chromium.chrome.browser.photo_picker;
6+
7+
import android.os.AsyncTask;
8+
import android.os.Environment;
9+
10+
import org.chromium.base.ThreadUtils;
11+
12+
import java.io.File;
13+
import java.util.ArrayList;
14+
import java.util.Collections;
15+
import java.util.List;
16+
17+
/**
18+
* A worker task to enumerate image files on disk.
19+
*/
20+
class FileEnumWorkerTask extends AsyncTask<Void, Void, List<PickerBitmap>> {
21+
/**
22+
* An interface to use to communicate back the results to the client.
23+
*/
24+
public interface FilesEnumeratedCallback {
25+
/**
26+
* A callback to define to receive the list of all images on disk.
27+
* @param files The list of images.
28+
*/
29+
void filesEnumeratedCallback(List<PickerBitmap> files);
30+
}
31+
32+
// The callback to use to communicate the results.
33+
private FilesEnumeratedCallback mCallback;
34+
35+
// The filter to apply to the list.
36+
private MimeTypeFileFilter mFilter;
37+
38+
// The camera directory undir DCIM.
39+
private static final String SAMPLE_DCIM_SOURCE_SUB_DIRECTORY = "Camera";
40+
41+
/**
42+
* A FileEnumWorkerTask constructor.
43+
* @param callback The callback to use to communicate back the results.
44+
* @param filter The file filter to apply to the list.
45+
*/
46+
public FileEnumWorkerTask(FilesEnumeratedCallback callback, MimeTypeFileFilter filter) {
47+
mCallback = callback;
48+
mFilter = filter;
49+
}
50+
51+
/**
52+
* Retrieves the DCIM/camera directory.
53+
*/
54+
private File getCameraDirectory() {
55+
return new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
56+
SAMPLE_DCIM_SOURCE_SUB_DIRECTORY);
57+
}
58+
59+
/**
60+
* Recursively enumerate files in a directory (and subdirectories) and add them to a list.
61+
* @param directory The parent directory to recursively traverse.
62+
* @param pickerBitmaps The list to add the results to.
63+
* @return True if traversing can continue, false if traversing was aborted and should stop.
64+
*/
65+
private boolean traverseDir(File directory, List<PickerBitmap> pickerBitmaps) {
66+
File[] files = directory.listFiles(mFilter);
67+
if (files == null) return true;
68+
69+
for (File file : files) {
70+
if (isCancelled()) return false;
71+
72+
if (file.isDirectory()) {
73+
if (!traverseDir(file, pickerBitmaps)) return false;
74+
} else {
75+
pickerBitmaps.add(new PickerBitmap(
76+
file.getPath(), file.lastModified(), PickerBitmap.PICTURE));
77+
}
78+
}
79+
80+
return true;
81+
}
82+
83+
/**
84+
* Enumerates (in the background) the image files on disk. Called on a non-UI thread
85+
* @param params Ignored, do not use.
86+
* @return A sorted list of images (by last-modified first).
87+
*/
88+
@Override
89+
protected List<PickerBitmap> doInBackground(Void... params) {
90+
assert !ThreadUtils.runningOnUiThread();
91+
92+
if (isCancelled()) return null;
93+
94+
List<PickerBitmap> pickerBitmaps = new ArrayList<>();
95+
96+
// TODO(finnur): Figure out which directories to scan and stop hard coding "Camera" above.
97+
File[] sourceDirs = new File[] {
98+
getCameraDirectory(),
99+
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
100+
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
101+
};
102+
103+
for (File directory : sourceDirs) {
104+
if (!traverseDir(directory, pickerBitmaps)) return null;
105+
}
106+
107+
Collections.sort(pickerBitmaps);
108+
109+
pickerBitmaps.add(0, new PickerBitmap("", 0, PickerBitmap.GALLERY));
110+
pickerBitmaps.add(0, new PickerBitmap("", 0, PickerBitmap.CAMERA));
111+
112+
return pickerBitmaps;
113+
}
114+
115+
/**
116+
* Communicates the results back to the client. Called on the UI thread.
117+
* @param files The resulting list of files on disk.
118+
*/
119+
@Override
120+
protected void onPostExecute(List<PickerBitmap> files) {
121+
if (isCancelled()) {
122+
return;
123+
}
124+
125+
mCallback.filesEnumeratedCallback(files);
126+
}
127+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright 2017 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package org.chromium.chrome.browser.photo_picker;
6+
7+
import android.support.annotation.NonNull;
8+
import android.webkit.MimeTypeMap;
9+
10+
import java.io.File;
11+
import java.io.FileFilter;
12+
import java.util.HashSet;
13+
import java.util.Locale;
14+
15+
/**
16+
* A file filter for handling extensions mapping to MIME types (such as images/jpeg and images/*).
17+
*/
18+
class MimeTypeFileFilter implements FileFilter {
19+
private HashSet<String> mExtensions = new HashSet<>();
20+
private HashSet<String> mMimeTypes = new HashSet<>();
21+
private HashSet<String> mMimeSupertypes = new HashSet<>();
22+
private MimeTypeMap mMimeTypeMap;
23+
24+
/**
25+
* Contructs a MimeTypeFileFilter object.
26+
* @param acceptAttr A comma seperated list of MIME types this filter accepts.
27+
* For example: images/gif, video/*.
28+
*/
29+
// TODO(finnur): Convert param to List.
30+
public MimeTypeFileFilter(@NonNull String acceptAttr) {
31+
for (String field : acceptAttr.toLowerCase(Locale.US).split(",")) {
32+
field = field.trim();
33+
if (field.startsWith(".")) {
34+
mExtensions.add(field.substring(1));
35+
} else if (field.endsWith("/*")) {
36+
mMimeSupertypes.add(field.substring(0, field.length() - 2));
37+
} else if (field.contains("/")) {
38+
mMimeTypes.add(field);
39+
} else {
40+
// Throw exception?
41+
}
42+
}
43+
44+
mMimeTypeMap = MimeTypeMap.getSingleton();
45+
}
46+
47+
@Override
48+
public boolean accept(@NonNull File file) {
49+
if (file.isDirectory()) {
50+
return true;
51+
}
52+
53+
String uri = file.toURI().toString();
54+
String ext = MimeTypeMap.getFileExtensionFromUrl(uri).toLowerCase(Locale.US);
55+
if (mExtensions.contains(ext)) {
56+
return true;
57+
}
58+
59+
String mimeType = getMimeTypeFromExtension(ext);
60+
if (mimeType != null) {
61+
if (mMimeTypes.contains(mimeType)
62+
|| mMimeSupertypes.contains(getMimeSupertype(mimeType))) {
63+
return true;
64+
}
65+
}
66+
67+
return false;
68+
}
69+
70+
private HashSet<String> getAcceptedSupertypes() {
71+
HashSet<String> supertypes = new HashSet<>();
72+
supertypes.addAll(mMimeSupertypes);
73+
for (String mimeType : mMimeTypes) {
74+
supertypes.add(getMimeSupertype(mimeType));
75+
}
76+
for (String ext : mExtensions) {
77+
String mimeType = getMimeTypeFromExtension(ext);
78+
if (mimeType != null) {
79+
supertypes.add(getMimeSupertype(mimeType));
80+
}
81+
}
82+
return supertypes;
83+
}
84+
85+
private String getMimeTypeFromExtension(@NonNull String ext) {
86+
String mimeType = mMimeTypeMap.getMimeTypeFromExtension(ext);
87+
return (mimeType != null) ? mimeType.toLowerCase(Locale.US) : null;
88+
}
89+
90+
@NonNull
91+
private String getMimeSupertype(@NonNull String mimeType) {
92+
return mimeType.split("/", 2)[0];
93+
}
94+
}

chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,10 @@ protected void onCreate(Bundle savedInstanceState) {
4646
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
4747
WindowManager.LayoutParams.FLAG_FULLSCREEN);
4848
}
49+
50+
@Override
51+
public void dismiss() {
52+
super.dismiss();
53+
mCategoryView.onDialogDismissed();
54+
}
4955
}

chrome/android/java/src/org/chromium/chrome/browser/photo_picker/PickerCategoryView.java

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import android.support.v7.widget.GridLayoutManager;
1111
import android.support.v7.widget.RecyclerView;
1212
import android.support.v7.widget.Toolbar.OnMenuItemClickListener;
13-
import android.util.AttributeSet;
1413
import android.view.LayoutInflater;
1514
import android.view.MenuItem;
1615
import android.view.View;
@@ -21,14 +20,14 @@
2120
import org.chromium.chrome.browser.widget.selection.SelectionDelegate;
2221
import org.chromium.ui.PhotoPickerListener;
2322

24-
import java.util.ArrayList;
2523
import java.util.List;
2624

2725
/**
2826
* A class for keeping track of common data associated with showing photos in
2927
* the photo picker, for example the RecyclerView and the bitmap caches.
3028
*/
31-
public class PickerCategoryView extends RelativeLayout implements OnMenuItemClickListener {
29+
public class PickerCategoryView extends RelativeLayout
30+
implements FileEnumWorkerTask.FilesEnumeratedCallback, OnMenuItemClickListener {
3231
// The dialog that owns us.
3332
private PhotoPickerDialog mDialog;
3433

@@ -69,21 +68,14 @@ public class PickerCategoryView extends RelativeLayout implements OnMenuItemClic
6968
// The size of the bitmaps (equal length for width and height).
7069
private int mImageSize;
7170

71+
// A worker task for asynchronously enumerating files off the main thread.
72+
private FileEnumWorkerTask mWorkerTask;
73+
7274
public PickerCategoryView(Context context) {
7375
super(context);
7476
postConstruction(context);
7577
}
7678

77-
public PickerCategoryView(Context context, AttributeSet attrs) {
78-
super(context, attrs);
79-
postConstruction(context);
80-
}
81-
82-
public PickerCategoryView(Context context, AttributeSet attrs, int defStyle) {
83-
super(context, attrs, defStyle);
84-
postConstruction(context);
85-
}
86-
8779
/**
8880
* A helper function for initializing the PickerCategoryView.
8981
* @param context The context to use.
@@ -119,6 +111,16 @@ private void postConstruction(Context context) {
119111
prepareBitmaps();
120112
}
121113

114+
/**
115+
* Cancels any outstanding requests.
116+
*/
117+
public void onDialogDismissed() {
118+
if (mWorkerTask != null) {
119+
mWorkerTask.cancel(true);
120+
mWorkerTask = null;
121+
}
122+
}
123+
122124
/**
123125
* Initializes the PickerCategoryView object.
124126
* @param dialog The dialog showing us.
@@ -134,6 +136,16 @@ public void initialize(
134136
mListener = listener;
135137
}
136138

139+
// FileEnumWorkerTask.FilesEnumeratedCallback:
140+
141+
@Override
142+
public void filesEnumeratedCallback(List<PickerBitmap> files) {
143+
mPickerBitmaps = files;
144+
if (files != null && files.size() > 0) {
145+
mPickerAdapter.notifyDataSetChanged();
146+
}
147+
}
148+
137149
// OnMenuItemClickListener:
138150

139151
@Override
@@ -203,15 +215,12 @@ private void calculateGridMetrics(int width) {
203215
* Prepares bitmaps for loading.
204216
*/
205217
private void prepareBitmaps() {
206-
// TODO(finnur): Use worker thread to fetch bitmaps instead of hard-coding.
207-
mPickerBitmaps = new ArrayList<>();
208-
mPickerBitmaps.add(0, new PickerBitmap("", 0, PickerBitmap.GALLERY));
209-
mPickerBitmaps.add(0, new PickerBitmap("", 0, PickerBitmap.CAMERA));
210-
mPickerBitmaps.add(new PickerBitmap("foo/bar1.jpg", 1, PickerBitmap.PICTURE));
211-
mPickerBitmaps.add(new PickerBitmap("foo/bar2.jpg", 2, PickerBitmap.PICTURE));
212-
mPickerBitmaps.add(new PickerBitmap("foo/bar3.jpg", 3, PickerBitmap.PICTURE));
213-
mPickerBitmaps.add(new PickerBitmap("foo/bar4.jpg", 4, PickerBitmap.PICTURE));
214-
mPickerAdapter.notifyDataSetChanged();
218+
if (mWorkerTask != null) {
219+
mWorkerTask.cancel(true);
220+
}
221+
222+
mWorkerTask = new FileEnumWorkerTask(this, new MimeTypeFileFilter("image/*"));
223+
mWorkerTask.execute();
215224
}
216225

217226
/**

chrome/android/java_sources.gni

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -771,6 +771,8 @@ chrome_java_sources = [
771771
"java/src/org/chromium/chrome/browser/permissions/PermissionDialogController.java",
772772
"java/src/org/chromium/chrome/browser/permissions/PermissionDialogDelegate.java",
773773
"java/src/org/chromium/chrome/browser/physicalweb/BitmapHttpRequest.java",
774+
"java/src/org/chromium/chrome/browser/photo_picker/FileEnumWorkerTask.java",
775+
"java/src/org/chromium/chrome/browser/photo_picker/MimeTypeFileFilter.java",
774776
"java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerDialog.java",
775777
"java/src/org/chromium/chrome/browser/photo_picker/PhotoPickerToolbar.java",
776778
"java/src/org/chromium/chrome/browser/photo_picker/PickerAdapter.java",

0 commit comments

Comments
 (0)