first
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
package com.esafirm.imagepicker.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import com.esafirm.imagepicker.features.imageloader.ImageLoader;
|
||||
|
||||
public abstract class BaseListAdapter<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> {
|
||||
|
||||
private final Context context;
|
||||
private final LayoutInflater inflater;
|
||||
private final ImageLoader imageLoader;
|
||||
|
||||
public BaseListAdapter(Context context, ImageLoader imageLoader) {
|
||||
this.context = context;
|
||||
this.inflater = LayoutInflater.from(context);
|
||||
this.imageLoader = imageLoader;
|
||||
}
|
||||
|
||||
public ImageLoader getImageLoader() {
|
||||
return imageLoader;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
public LayoutInflater getInflater() {
|
||||
return inflater;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.esafirm.imagepicker.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
import com.esafirm.imagepicker.features.imageloader.ImageLoader;
|
||||
import com.esafirm.imagepicker.features.imageloader.ImageType;
|
||||
import com.esafirm.imagepicker.listeners.OnFolderClickListener;
|
||||
import com.esafirm.imagepicker.model.Folder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FolderPickerAdapter extends BaseListAdapter<FolderPickerAdapter.FolderViewHolder> {
|
||||
|
||||
private final OnFolderClickListener folderClickListener;
|
||||
|
||||
private List<Folder> folders = new ArrayList<>();
|
||||
|
||||
public FolderPickerAdapter(Context context, ImageLoader imageLoader, OnFolderClickListener folderClickListener) {
|
||||
super(context, imageLoader);
|
||||
this.folderClickListener = folderClickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FolderViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new FolderViewHolder(
|
||||
getInflater().inflate(R.layout.ef_imagepicker_item_folder, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final FolderViewHolder holder, int position) {
|
||||
final Folder folder = folders.get(position);
|
||||
|
||||
getImageLoader().loadImage(
|
||||
folder.getImages().get(0).getPath(),
|
||||
holder.image,
|
||||
ImageType.FOLDER
|
||||
);
|
||||
|
||||
holder.name.setText(folders.get(position).getFolderName());
|
||||
holder.number.setText(String.valueOf(folders.get(position).getImages().size()));
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (folderClickListener != null)
|
||||
folderClickListener.onFolderClick(folder);
|
||||
});
|
||||
}
|
||||
|
||||
public void setData(List<Folder> folders) {
|
||||
if (folders != null) {
|
||||
this.folders.clear();
|
||||
this.folders.addAll(folders);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return folders.size();
|
||||
}
|
||||
|
||||
static class FolderViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private ImageView image;
|
||||
private TextView name;
|
||||
private TextView number;
|
||||
|
||||
FolderViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
image = (ImageView) itemView.findViewById(R.id.image);
|
||||
name = (TextView) itemView.findViewById(R.id.tv_name);
|
||||
number = (TextView) itemView.findViewById(R.id.tv_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.esafirm.imagepicker.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
import com.esafirm.imagepicker.features.imageloader.ImageLoader;
|
||||
import com.esafirm.imagepicker.features.imageloader.ImageType;
|
||||
import com.esafirm.imagepicker.helper.ImagePickerUtils;
|
||||
import com.esafirm.imagepicker.listeners.OnImageClickListener;
|
||||
import com.esafirm.imagepicker.listeners.OnImageSelectedListener;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class ImagePickerAdapter extends BaseListAdapter<ImagePickerAdapter.ImageViewHolder> {
|
||||
|
||||
private List<Image> images = new ArrayList<>();
|
||||
private List<Image> selectedImages = new ArrayList<>();
|
||||
|
||||
private OnImageClickListener itemClickListener;
|
||||
private OnImageSelectedListener imageSelectedListener;
|
||||
|
||||
public ImagePickerAdapter(Context context, ImageLoader imageLoader,
|
||||
List<Image> selectedImages, OnImageClickListener itemClickListener) {
|
||||
super(context, imageLoader);
|
||||
this.itemClickListener = itemClickListener;
|
||||
|
||||
if (selectedImages != null && !selectedImages.isEmpty()) {
|
||||
this.selectedImages.addAll(selectedImages);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
return new ImageViewHolder(
|
||||
getInflater().inflate(R.layout.ef_imagepicker_item_image, parent, false)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ImageViewHolder viewHolder, int position) {
|
||||
|
||||
final Image image = images.get(position);
|
||||
final boolean isSelected = isSelected(image);
|
||||
|
||||
getImageLoader().loadImage(
|
||||
image.getPath(),
|
||||
viewHolder.imageView,
|
||||
ImageType.GALLERY
|
||||
);
|
||||
|
||||
boolean showFileTypeIndicator = false;
|
||||
String fileTypeLabel = "";
|
||||
if(ImagePickerUtils.isGifFormat(image)) {
|
||||
fileTypeLabel = getContext().getResources().getString(R.string.ef_gif);
|
||||
showFileTypeIndicator = true;
|
||||
}
|
||||
if(ImagePickerUtils.isVideoFormat(image)) {
|
||||
fileTypeLabel = getContext().getResources().getString(R.string.ef_video);
|
||||
showFileTypeIndicator = true;
|
||||
}
|
||||
viewHolder.fileTypeIndicator.setText(fileTypeLabel);
|
||||
viewHolder.fileTypeIndicator.setVisibility(showFileTypeIndicator
|
||||
? View.VISIBLE
|
||||
: View.GONE);
|
||||
|
||||
viewHolder.alphaView.setAlpha(isSelected
|
||||
? 0.5f
|
||||
: 0f);
|
||||
|
||||
viewHolder.itemView.setOnClickListener(v -> {
|
||||
boolean shouldSelect = itemClickListener.onImageClick(
|
||||
isSelected
|
||||
);
|
||||
|
||||
if (isSelected) {
|
||||
removeSelectedImage(image, position);
|
||||
} else if (shouldSelect) {
|
||||
addSelected(image, position);
|
||||
}
|
||||
});
|
||||
|
||||
viewHolder.container.setForeground(isSelected
|
||||
? ContextCompat.getDrawable(getContext(), R.drawable.ef_ic_done_white)
|
||||
: null);
|
||||
}
|
||||
|
||||
private boolean isSelected(Image image) {
|
||||
for (Image selectedImage : selectedImages) {
|
||||
if (selectedImage.getPath().equals(image.getPath())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return images.size();
|
||||
}
|
||||
|
||||
|
||||
public void setData(List<Image> images) {
|
||||
this.images.clear();
|
||||
this.images.addAll(images);
|
||||
}
|
||||
|
||||
private void addSelected(final Image image, final int position) {
|
||||
mutateSelection(() -> {
|
||||
selectedImages.add(image);
|
||||
notifyItemChanged(position);
|
||||
});
|
||||
}
|
||||
|
||||
private void removeSelectedImage(final Image image, final int position) {
|
||||
mutateSelection(() -> {
|
||||
selectedImages.remove(image);
|
||||
notifyItemChanged(position);
|
||||
});
|
||||
}
|
||||
|
||||
public void removeAllSelectedSingleClick() {
|
||||
mutateSelection(() -> {
|
||||
selectedImages.clear();
|
||||
notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
|
||||
private void mutateSelection(Runnable runnable) {
|
||||
runnable.run();
|
||||
if (imageSelectedListener != null) {
|
||||
imageSelectedListener.onSelectionUpdate(selectedImages);
|
||||
}
|
||||
}
|
||||
|
||||
public void setImageSelectedListener(OnImageSelectedListener imageSelectedListener) {
|
||||
this.imageSelectedListener = imageSelectedListener;
|
||||
}
|
||||
|
||||
public Image getItem(int position) {
|
||||
return images.get(position);
|
||||
}
|
||||
|
||||
public List<Image> getSelectedImages() {
|
||||
return selectedImages;
|
||||
}
|
||||
|
||||
static class ImageViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private ImageView imageView;
|
||||
private View alphaView;
|
||||
private TextView fileTypeIndicator;
|
||||
private FrameLayout container;
|
||||
|
||||
ImageViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
container = (FrameLayout) itemView;
|
||||
imageView = itemView.findViewById(R.id.image_view);
|
||||
alphaView = itemView.findViewById(R.id.view_alpha);
|
||||
fileTypeIndicator = itemView.findViewById(R.id.ef_item_file_type_indicator);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class ClickUtils {
|
||||
|
||||
/**
|
||||
* Android优雅地处理按钮重复点击的几种方法
|
||||
* add by wangqianzhou
|
||||
*/
|
||||
private static long mLastClickTime = 0;
|
||||
public static final long TIME_INTERVAL = 3000L;
|
||||
|
||||
|
||||
public static boolean onclickTimes() {
|
||||
long nowTime = System.currentTimeMillis();
|
||||
if (nowTime - mLastClickTime > TIME_INTERVAL) {
|
||||
// do something
|
||||
Log.e("TAGTIME_INTERVAL", "nowTime: " + nowTime);
|
||||
Log.e("TAGTIME_INTERVAL", "mLastClickTime: " + mLastClickTime);
|
||||
Log.e("TAGTIME_INTERVAL", nowTime - mLastClickTime + "");
|
||||
Log.e("TAGTIME_INTERVAL", "onclickTime: " + "11111111111");
|
||||
mLastClickTime = nowTime;
|
||||
return true;
|
||||
} else {
|
||||
Log.e("TAGTIME_INTERVAL", "onclickTime: " + "11111111111222");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.esafirm.imagepicker.features.common.ImageLoaderListener;
|
||||
import com.esafirm.imagepicker.helper.ImagePickerUtils;
|
||||
import com.esafirm.imagepicker.model.Folder;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class ImageFileLoader {
|
||||
|
||||
private Context context;
|
||||
private ExecutorService executorService;
|
||||
|
||||
public ImageFileLoader(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
private final String[] projection = new String[]{
|
||||
MediaStore.Images.Media._ID,
|
||||
MediaStore.Images.Media.DISPLAY_NAME,
|
||||
MediaStore.Images.Media.DATA,
|
||||
MediaStore.Images.Media.BUCKET_DISPLAY_NAME
|
||||
};
|
||||
|
||||
public void loadDeviceImages(final boolean isFolderMode, final boolean includeVideo, final boolean includeAnimation, final ArrayList<File> excludedImages, final ImageLoaderListener listener) {
|
||||
getExecutorService().execute(new ImageLoadRunnable(isFolderMode, includeVideo, includeAnimation, excludedImages, listener));
|
||||
}
|
||||
|
||||
public void abortLoadImages() {
|
||||
if (executorService != null) {
|
||||
executorService.shutdown();
|
||||
executorService = null;
|
||||
}
|
||||
}
|
||||
|
||||
private ExecutorService getExecutorService() {
|
||||
if (executorService == null) {
|
||||
executorService = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
return executorService;
|
||||
}
|
||||
|
||||
private class ImageLoadRunnable implements Runnable {
|
||||
|
||||
private boolean isFolderMode;
|
||||
private boolean includeVideo;
|
||||
private boolean includeAnimation;
|
||||
private ArrayList<File> exlucedImages;
|
||||
private ImageLoaderListener listener;
|
||||
|
||||
public ImageLoadRunnable(boolean isFolderMode, boolean includeVideo, boolean includeAnimation, ArrayList<File> excludedImages, ImageLoaderListener listener) {
|
||||
this.isFolderMode = isFolderMode;
|
||||
this.includeVideo = includeVideo;
|
||||
this.includeAnimation = includeAnimation;
|
||||
this.exlucedImages = excludedImages;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Cursor cursor;
|
||||
if (includeVideo) {
|
||||
String selection = MediaStore.Files.FileColumns.MEDIA_TYPE + "="
|
||||
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE + " OR "
|
||||
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "="
|
||||
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
|
||||
|
||||
cursor = context.getContentResolver().query(MediaStore.Files.getContentUri("external"), projection,
|
||||
selection, null, MediaStore.Images.Media.DATE_ADDED);
|
||||
} else {
|
||||
cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,
|
||||
null, null, MediaStore.Images.Media.DATE_ADDED);
|
||||
}
|
||||
|
||||
if (cursor == null) {
|
||||
listener.onFailed(new NullPointerException());
|
||||
return;
|
||||
}
|
||||
|
||||
List<Image> temp = new ArrayList<>();
|
||||
Map<String, Folder> folderMap = null;
|
||||
if (isFolderMode) {
|
||||
folderMap = new HashMap<>();
|
||||
}
|
||||
|
||||
if (cursor.moveToLast()) {
|
||||
do {
|
||||
long id = cursor.getLong(cursor.getColumnIndex(projection[0]));
|
||||
String name = cursor.getString(cursor.getColumnIndex(projection[1]));
|
||||
String path = cursor.getString(cursor.getColumnIndex(projection[2]));
|
||||
String bucket = cursor.getString(cursor.getColumnIndex(projection[3]));
|
||||
|
||||
File file = makeSafeFile(path);
|
||||
if (file != null) {
|
||||
if (exlucedImages != null && exlucedImages.contains(file))
|
||||
continue;
|
||||
|
||||
Image image = new Image(id, name, path);
|
||||
|
||||
if (!includeAnimation) {
|
||||
if (ImagePickerUtils.isGifFormat(image))
|
||||
continue;
|
||||
}
|
||||
|
||||
temp.add(image);
|
||||
|
||||
if (folderMap != null) {
|
||||
Folder folder = folderMap.get(bucket);
|
||||
if (folder == null) {
|
||||
folder = new Folder(bucket);
|
||||
folderMap.put(bucket, folder);
|
||||
}
|
||||
folder.getImages().add(image);
|
||||
}
|
||||
}
|
||||
|
||||
} while (cursor.moveToPrevious());
|
||||
}
|
||||
cursor.close();
|
||||
|
||||
/* Convert HashMap to ArrayList if not null */
|
||||
List<Folder> folders = null;
|
||||
if (folderMap != null) {
|
||||
folders = new ArrayList<>(folderMap.values());
|
||||
}
|
||||
|
||||
listener.onImageLoaded(temp, folders);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static File makeSafeFile(String path) {
|
||||
if (path == null || path.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new File(path);
|
||||
} catch (Exception ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.esafirm.imagepicker.features.cameraonly.ImagePickerCameraOnly;
|
||||
import com.esafirm.imagepicker.features.imageloader.ImageLoader;
|
||||
import com.esafirm.imagepicker.helper.ConfigUtils;
|
||||
import com.esafirm.imagepicker.helper.IpLogger;
|
||||
import com.esafirm.imagepicker.helper.LocaleManager;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public abstract class ImagePicker {
|
||||
|
||||
private ImagePickerConfig config;
|
||||
|
||||
public abstract void start();
|
||||
|
||||
public abstract void start(int requestCode);
|
||||
|
||||
public static class ImagePickerWithActivity extends ImagePicker {
|
||||
|
||||
private Activity activity;
|
||||
|
||||
public ImagePickerWithActivity(Activity activity) {
|
||||
this.activity = activity;
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(int requestCode) {
|
||||
activity.startActivityForResult(getIntent(activity), requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
activity.startActivityForResult(getIntent(activity), IpCons.RC_IMAGE_PICKER);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ImagePickerWithFragment extends ImagePicker {
|
||||
|
||||
private Fragment fragment;
|
||||
|
||||
public ImagePickerWithFragment(Fragment fragment) {
|
||||
this.fragment = fragment;
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(int requestCode) {
|
||||
fragment.startActivityForResult(getIntent(fragment.getActivity()), requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
fragment.startActivityForResult(getIntent(fragment.getActivity()), IpCons.RC_IMAGE_PICKER);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
/* > Stater */
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
public void init() {
|
||||
config = ImagePickerConfigFactory.createDefault();
|
||||
}
|
||||
|
||||
public static ImagePickerWithActivity create(Activity activity) {
|
||||
return new ImagePickerWithActivity(activity);
|
||||
}
|
||||
|
||||
public static ImagePickerWithFragment create(Fragment fragment) {
|
||||
return new ImagePickerWithFragment(fragment);
|
||||
}
|
||||
|
||||
public static ImagePickerCameraOnly cameraOnly() {
|
||||
return new ImagePickerCameraOnly();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
/* > Builder */
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
public ImagePicker single() {
|
||||
config.setMode(IpCons.MODE_SINGLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker multi() {
|
||||
config.setMode(IpCons.MODE_MULTIPLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker returnMode(@NonNull ReturnMode returnMode) {
|
||||
config.setReturnMode(returnMode);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker limit(int count) {
|
||||
config.setLimit(count);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker showCamera(boolean show) {
|
||||
config.setShowCamera(show);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker toolbarArrowColor(@ColorInt int color) {
|
||||
config.setArrowColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker toolbarFolderTitle(String title) {
|
||||
config.setFolderTitle(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker toolbarImageTitle(String title) {
|
||||
config.setImageTitle(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker toolbarDoneButtonText(String text) {
|
||||
config.setDoneButtonText(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker origin(ArrayList<Image> images) {
|
||||
config.setSelectedImages(images);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker exclude(ArrayList<Image> images) {
|
||||
config.setExcludedImages(images);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker excludeFiles(ArrayList<File> files) {
|
||||
config.setExcludedImageFiles(files);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker folderMode(boolean folderMode) {
|
||||
config.setFolderMode(folderMode);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public ImagePicker includeVideo(boolean includeVideo) {
|
||||
config.setIncludeVideo(includeVideo);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker includeAnimation(boolean includeAnimation) {
|
||||
config.setIncludeAnimation(includeAnimation);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker imageDirectory(String directory) {
|
||||
config.setImageDirectory(directory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker imageFullDirectory(String fullPath) {
|
||||
config.setImageFullDirectory(fullPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker theme(@StyleRes int theme) {
|
||||
config.setTheme(theme);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker imageLoader(ImageLoader imageLoader) {
|
||||
config.setImageLoader(imageLoader);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker enableLog(boolean isEnable) {
|
||||
IpLogger.getInstance().setEnable(isEnable);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePicker language(String language) {
|
||||
config.setLanguage(language);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePickerConfig getConfig() {
|
||||
LocaleManager.setLanguange(config.getLanguage());
|
||||
return ConfigUtils.checkConfig(config);
|
||||
}
|
||||
|
||||
public Intent getIntent(Context context) {
|
||||
ImagePickerConfig config = getConfig();
|
||||
Intent intent = new Intent(context, ImagePickerActivity.class);
|
||||
intent.putExtra(ImagePickerConfig.class.getSimpleName(), config);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
/* > Helper */
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
public static boolean shouldHandle(int requestCode, int resultCode, Intent data) {
|
||||
return resultCode == Activity.RESULT_OK
|
||||
&& requestCode == IpCons.RC_IMAGE_PICKER
|
||||
&& data != null;
|
||||
}
|
||||
|
||||
public static List<Image> getImages(Intent intent) {
|
||||
if (intent == null) {
|
||||
return null;
|
||||
}
|
||||
return intent.getParcelableArrayListExtra(IpCons.EXTRA_SELECTED_IMAGES);
|
||||
}
|
||||
|
||||
public static Image getFirstImageOrNull(Intent intent) {
|
||||
List<Image> images = getImages(intent);
|
||||
if (images == null || images.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return images.get(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
import com.esafirm.imagepicker.features.cameraonly.CameraOnlyConfig;
|
||||
import com.esafirm.imagepicker.helper.ConfigUtils;
|
||||
import com.esafirm.imagepicker.helper.IpLogger;
|
||||
import com.esafirm.imagepicker.helper.LocaleManager;
|
||||
import com.esafirm.imagepicker.helper.ViewUtils;
|
||||
import com.esafirm.imagepicker.model.Folder;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
public class ImagePickerActivity extends AppCompatActivity implements ImagePickerInteractionListener, ImagePickerView {
|
||||
|
||||
private ActionBar actionBar;
|
||||
private ImagePickerFragment imagePickerFragment;
|
||||
|
||||
private ImagePickerConfig config;
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
super.attachBaseContext(LocaleManager.updateResources(newBase));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setResult(RESULT_CANCELED);
|
||||
|
||||
/* This should not happen */
|
||||
Intent intent = getIntent();
|
||||
if (intent == null || intent.getExtras() == null) {
|
||||
IpLogger.getInstance().e("This should not happen. Please open an issue!");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
config = getIntent().getExtras().getParcelable(ImagePickerConfig.class.getSimpleName());
|
||||
CameraOnlyConfig cameraOnlyConfig = getIntent().getExtras().getParcelable(CameraOnlyConfig.class.getSimpleName());
|
||||
|
||||
boolean isCameraOnly = cameraOnlyConfig != null;
|
||||
|
||||
// TODO extract camera only function so we don't have to rely to Fragment
|
||||
if (!isCameraOnly) {
|
||||
setTheme(config.getTheme());
|
||||
setContentView(R.layout.ef_activity_image_picker);
|
||||
setupView();
|
||||
} else {
|
||||
setContentView(createCameraLayout());
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
// The fragment has been restored.
|
||||
imagePickerFragment = (ImagePickerFragment) getSupportFragmentManager().findFragmentById(R.id.ef_imagepicker_fragment_placeholder);
|
||||
} else {
|
||||
imagePickerFragment = ImagePickerFragment.newInstance(config, cameraOnlyConfig);
|
||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
ft.replace(R.id.ef_imagepicker_fragment_placeholder, imagePickerFragment);
|
||||
ft.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private FrameLayout createCameraLayout() {
|
||||
FrameLayout frameLayout = new FrameLayout(this);
|
||||
frameLayout.setId(R.id.ef_imagepicker_fragment_placeholder);
|
||||
return frameLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create option menus.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.ef_image_picker_menu_main, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
MenuItem menuCamera = menu.findItem(R.id.menu_camera);
|
||||
if (menuCamera != null) {
|
||||
if (config != null) {
|
||||
menuCamera.setVisible(config.isShowCamera());
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem menuDone = menu.findItem(R.id.menu_done);
|
||||
if (menuDone != null) {
|
||||
menuDone.setTitle(ConfigUtils.getDoneButtonText(this, config));
|
||||
menuDone.setVisible(imagePickerFragment.isShowDoneButton());
|
||||
}
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle option menu's click event
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
if (id == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
if (id == R.id.menu_done) {
|
||||
imagePickerFragment.onDone();
|
||||
return true;
|
||||
}
|
||||
if (id == R.id.menu_camera) {
|
||||
imagePickerFragment.captureImageWithPermission();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!imagePickerFragment.handleBack()) {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupView() {
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
actionBar = getSupportActionBar();
|
||||
|
||||
if (actionBar != null) {
|
||||
final Drawable arrowDrawable = ViewUtils.getArrowIcon(this);
|
||||
final int arrowColor = config.getArrowColor();
|
||||
if (arrowColor != ImagePickerConfig.NO_COLOR && arrowDrawable != null) {
|
||||
arrowDrawable.setColorFilter(arrowColor, PorterDuff.Mode.SRC_ATOP);
|
||||
}
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setHomeAsUpIndicator(arrowDrawable);
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
/* > ImagePickerInteractionListener Methods */
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
actionBar.setTitle(title);
|
||||
supportInvalidateOptionsMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectionChanged(List<Image> imageList) {
|
||||
// Do nothing when the selection changes.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishPickImages(Intent result) {
|
||||
setResult(RESULT_OK, result);
|
||||
finish();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
/* > View Methods */
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
@Override
|
||||
public void showLoading(boolean isLoading) {
|
||||
imagePickerFragment.showLoading(isLoading);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFetchCompleted(List<Image> images, List<Folder> folders) {
|
||||
imagePickerFragment.showFetchCompleted(images, folders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(Throwable throwable) {
|
||||
imagePickerFragment.showError(throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEmpty() {
|
||||
imagePickerFragment.showEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showCapturedImage() {
|
||||
imagePickerFragment.showCapturedImage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishPickImages(List<Image> images) {
|
||||
imagePickerFragment.finishPickImages(images);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import androidx.annotation.StyleRes;
|
||||
|
||||
import com.esafirm.imagepicker.features.common.BaseConfig;
|
||||
import com.esafirm.imagepicker.features.imageloader.ImageLoader;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ImagePickerConfig extends BaseConfig implements Parcelable {
|
||||
|
||||
public static final int NO_COLOR = -1;
|
||||
|
||||
private ArrayList<Image> selectedImages;
|
||||
private ArrayList<File> excludedImages;
|
||||
|
||||
private String folderTitle;
|
||||
private String imageTitle;
|
||||
private String doneButtonText;
|
||||
private int arrowColor = NO_COLOR;
|
||||
|
||||
private int mode;
|
||||
private int limit;
|
||||
private int theme;
|
||||
|
||||
private boolean folderMode;
|
||||
private boolean includeVideo;
|
||||
private boolean includeAnimation;
|
||||
private boolean showCamera;
|
||||
|
||||
private ImageLoader imageLoader;
|
||||
|
||||
private transient String language;
|
||||
|
||||
public ImagePickerConfig() {
|
||||
}
|
||||
|
||||
public int getArrowColor() {
|
||||
return arrowColor;
|
||||
}
|
||||
|
||||
public void setArrowColor(int arrowColor) {
|
||||
this.arrowColor = arrowColor;
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public void setMode(int mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public void setLimit(int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public boolean isShowCamera() {
|
||||
return showCamera;
|
||||
}
|
||||
|
||||
public void setShowCamera(boolean showCamera) {
|
||||
this.showCamera = showCamera;
|
||||
}
|
||||
|
||||
public boolean isIncludeVideo() {
|
||||
return includeVideo;
|
||||
}
|
||||
|
||||
public void setIncludeVideo(boolean includeVideo) {
|
||||
this.includeVideo = includeVideo;
|
||||
}
|
||||
|
||||
public boolean isIncludeAnimation() {
|
||||
return includeAnimation;
|
||||
}
|
||||
|
||||
public void setIncludeAnimation(boolean includeAnimation) {
|
||||
this.includeAnimation = includeAnimation;
|
||||
}
|
||||
|
||||
public String getFolderTitle() {
|
||||
return folderTitle;
|
||||
}
|
||||
|
||||
public void setFolderTitle(String folderTitle) {
|
||||
this.folderTitle = folderTitle;
|
||||
}
|
||||
|
||||
public String getImageTitle() {
|
||||
return imageTitle;
|
||||
}
|
||||
|
||||
public void setImageTitle(String imageTitle) {
|
||||
this.imageTitle = imageTitle;
|
||||
}
|
||||
|
||||
public String getDoneButtonText() {
|
||||
return doneButtonText;
|
||||
}
|
||||
|
||||
public void setDoneButtonText(String doneButtonText) {
|
||||
this.doneButtonText = doneButtonText;
|
||||
}
|
||||
|
||||
public ArrayList<Image> getSelectedImages() {
|
||||
return selectedImages;
|
||||
}
|
||||
|
||||
public void setSelectedImages(ArrayList<Image> selectedImages) {
|
||||
this.selectedImages = selectedImages;
|
||||
}
|
||||
|
||||
public ArrayList<File> getExcludedImages() {
|
||||
return excludedImages;
|
||||
}
|
||||
|
||||
public void setExcludedImages(ArrayList<Image> excludedImages) {
|
||||
if (excludedImages != null && !excludedImages.isEmpty()) {
|
||||
this.excludedImages = new ArrayList<>();
|
||||
for (Image image : excludedImages) {
|
||||
this.excludedImages.add(new File(image.getPath()));
|
||||
}
|
||||
} else {
|
||||
this.excludedImages = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setExcludedImageFiles(ArrayList<File> excludedImages) {
|
||||
this.excludedImages = excludedImages;
|
||||
}
|
||||
|
||||
public boolean isFolderMode() {
|
||||
return folderMode;
|
||||
}
|
||||
|
||||
public void setFolderMode(boolean folderMode) {
|
||||
this.folderMode = folderMode;
|
||||
}
|
||||
|
||||
public void setTheme(@StyleRes int theme) {
|
||||
this.theme = theme;
|
||||
}
|
||||
|
||||
public int getTheme() {
|
||||
return theme;
|
||||
}
|
||||
|
||||
public void setImageLoader(ImageLoader imageLoader) {
|
||||
this.imageLoader = imageLoader;
|
||||
}
|
||||
|
||||
public ImageLoader getImageLoader() {
|
||||
return imageLoader;
|
||||
}
|
||||
|
||||
public void setLanguage(String language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
/* > Parcelable */
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeTypedList(this.selectedImages);
|
||||
|
||||
dest.writeByte((byte) (excludedImages != null ? 1 : 0));
|
||||
if (excludedImages != null) {
|
||||
dest.writeList(this.excludedImages);
|
||||
}
|
||||
|
||||
dest.writeString(this.folderTitle);
|
||||
dest.writeString(this.imageTitle);
|
||||
dest.writeString(this.doneButtonText);
|
||||
dest.writeInt(this.arrowColor);
|
||||
dest.writeInt(this.mode);
|
||||
dest.writeInt(this.limit);
|
||||
dest.writeInt(this.theme);
|
||||
dest.writeByte(this.folderMode ? (byte) 1 : (byte) 0);
|
||||
dest.writeByte(this.includeVideo ? (byte) 1 : (byte) 0);
|
||||
dest.writeByte(this.includeAnimation ? (byte) 1: (byte) 0);
|
||||
dest.writeByte(this.showCamera ? (byte) 1 : (byte) 0);
|
||||
dest.writeSerializable(this.imageLoader);
|
||||
}
|
||||
|
||||
protected ImagePickerConfig(Parcel in) {
|
||||
super(in);
|
||||
this.selectedImages = in.createTypedArrayList(Image.CREATOR);
|
||||
|
||||
boolean isPresent = in.readByte() != 0;
|
||||
if (isPresent) {
|
||||
this.excludedImages = new ArrayList<>();
|
||||
in.readList(this.excludedImages, File.class.getClassLoader());
|
||||
}
|
||||
|
||||
this.folderTitle = in.readString();
|
||||
this.imageTitle = in.readString();
|
||||
this.doneButtonText = in.readString();
|
||||
this.arrowColor = in.readInt();
|
||||
this.mode = in.readInt();
|
||||
this.limit = in.readInt();
|
||||
this.theme = in.readInt();
|
||||
this.folderMode = in.readByte() != 0;
|
||||
this.includeVideo = in.readByte() != 0;
|
||||
this.includeAnimation = in.readByte() != 0;
|
||||
this.showCamera = in.readByte() != 0;
|
||||
this.imageLoader = (ImageLoader) in.readSerializable();
|
||||
}
|
||||
|
||||
public static final Creator<ImagePickerConfig> CREATOR = new Creator<ImagePickerConfig>() {
|
||||
@Override
|
||||
public ImagePickerConfig createFromParcel(Parcel source) {
|
||||
return new ImagePickerConfig(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImagePickerConfig[] newArray(int size) {
|
||||
return new ImagePickerConfig[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import com.esafirm.imagepicker.features.cameraonly.CameraOnlyConfig;
|
||||
import com.esafirm.imagepicker.features.imageloader.DefaultImageLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ImagePickerConfigFactory {
|
||||
|
||||
public static CameraOnlyConfig createCameraDefault() {
|
||||
CameraOnlyConfig config = new CameraOnlyConfig();
|
||||
config.setSavePath(ImagePickerSavePath.DEFAULT);
|
||||
config.setReturnMode(ReturnMode.ALL);
|
||||
return config;
|
||||
}
|
||||
|
||||
public static ImagePickerConfig createDefault() {
|
||||
ImagePickerConfig config = new ImagePickerConfig();
|
||||
config.setMode(IpCons.MODE_MULTIPLE);
|
||||
config.setLimit(IpCons.MAX_LIMIT);
|
||||
config.setShowCamera(true);
|
||||
config.setFolderMode(false);
|
||||
config.setSelectedImages(new ArrayList<>());
|
||||
config.setSavePath(ImagePickerSavePath.DEFAULT);
|
||||
config.setReturnMode(ReturnMode.NONE);
|
||||
config.setImageLoader(new DefaultImageLoader());
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,566 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcelable;
|
||||
import android.provider.MediaStore;
|
||||
import android.provider.Settings;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
import com.esafirm.imagepicker.features.camera.CameraHelper;
|
||||
import com.esafirm.imagepicker.features.camera.DefaultCameraModule;
|
||||
import com.esafirm.imagepicker.features.cameraonly.CameraOnlyConfig;
|
||||
import com.esafirm.imagepicker.features.common.BaseConfig;
|
||||
import com.esafirm.imagepicker.features.recyclers.RecyclerViewManager;
|
||||
import com.esafirm.imagepicker.helper.ConfigUtils;
|
||||
import com.esafirm.imagepicker.helper.ImagePickerPreferences;
|
||||
import com.esafirm.imagepicker.helper.IpCrasher;
|
||||
import com.esafirm.imagepicker.helper.IpLogger;
|
||||
import com.esafirm.imagepicker.model.Folder;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
import com.esafirm.imagepicker.view.SnackBarView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import static android.app.Activity.RESULT_CANCELED;
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static com.esafirm.imagepicker.helper.ImagePickerPreferences.PREF_WRITE_EXTERNAL_STORAGE_REQUESTED;
|
||||
|
||||
public class ImagePickerFragment extends Fragment implements ImagePickerView {
|
||||
private static final String STATE_KEY_CAMERA_MODULE = "Key.CameraModule";
|
||||
private static final String STATE_KEY_RECYCLER = "Key.Recycler";
|
||||
private static final String STATE_KEY_SELECTED_IMAGES = "Key.SelectedImages";
|
||||
|
||||
private static final int RC_CAPTURE = 2000;
|
||||
|
||||
private static final int RC_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 23;
|
||||
private static final int RC_PERMISSION_REQUEST_CAMERA = 24;
|
||||
|
||||
private IpLogger logger = IpLogger.getInstance();
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private SnackBarView snackBarView;
|
||||
private ProgressBar progressBar;
|
||||
private TextView emptyTextView;
|
||||
|
||||
private RecyclerViewManager recyclerViewManager;
|
||||
|
||||
private ImagePickerPresenter presenter;
|
||||
private ImagePickerPreferences preferences;
|
||||
private ImagePickerConfig config;
|
||||
private ImagePickerInteractionListener interactionListener;
|
||||
|
||||
private Handler handler;
|
||||
private ContentObserver observer;
|
||||
|
||||
private boolean isCameraOnly;
|
||||
|
||||
|
||||
public ImagePickerFragment() {
|
||||
// Required empty public constructor.
|
||||
}
|
||||
|
||||
public static ImagePickerFragment newInstance(@Nullable ImagePickerConfig config,
|
||||
@Nullable CameraOnlyConfig cameraOnlyConfig) {
|
||||
ImagePickerFragment fragment = new ImagePickerFragment();
|
||||
Bundle args = new Bundle();
|
||||
if (config != null) {
|
||||
args.putParcelable(ImagePickerConfig.class.getSimpleName(), config);
|
||||
}
|
||||
if (cameraOnlyConfig != null) {
|
||||
args.putParcelable(CameraOnlyConfig.class.getSimpleName(), cameraOnlyConfig);
|
||||
}
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
setupComponents();
|
||||
|
||||
if (interactionListener == null) {
|
||||
throw new RuntimeException("ImagePickerFragment needs an " +
|
||||
"ImagePickerInteractionListener. This will be set automatically if the " +
|
||||
"activity implements ImagePickerInteractionListener, and can be set manually " +
|
||||
"with fragment.setInteractionListener(listener).");
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
presenter.setCameraModule((DefaultCameraModule) savedInstanceState.getSerializable(STATE_KEY_CAMERA_MODULE));
|
||||
}
|
||||
|
||||
if (isCameraOnly) {
|
||||
if (savedInstanceState == null) {
|
||||
captureImageWithPermission();
|
||||
}
|
||||
} else {
|
||||
ImagePickerConfig config = getImagePickerConfig();
|
||||
if (config == null) {
|
||||
IpCrasher.openIssue();
|
||||
}
|
||||
// clone the inflater using the ContextThemeWrapper
|
||||
LayoutInflater localInflater = inflater.cloneInContext(new ContextThemeWrapper(getActivity(), config.getTheme()));
|
||||
|
||||
// inflate the layout using the cloned inflater, not default inflater
|
||||
View result = localInflater.inflate(R.layout.ef_fragment_image_picker, container, false);
|
||||
setupView(result);
|
||||
if (savedInstanceState == null) {
|
||||
setupRecyclerView(config, config.getSelectedImages());
|
||||
} else {
|
||||
setupRecyclerView(config, savedInstanceState.getParcelableArrayList(STATE_KEY_SELECTED_IMAGES));
|
||||
recyclerViewManager.onRestoreState(savedInstanceState.getParcelable(STATE_KEY_RECYCLER));
|
||||
}
|
||||
interactionListener.selectionChanged(recyclerViewManager.getSelectedImages());
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
isCameraOnly = getArguments().containsKey(CameraOnlyConfig.class.getSimpleName());
|
||||
startContentObserver();
|
||||
}
|
||||
|
||||
private BaseConfig getBaseConfig() {
|
||||
return isCameraOnly
|
||||
? getCameraOnlyConfig()
|
||||
: getImagePickerConfig();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ImagePickerConfig getImagePickerConfig() {
|
||||
if (config == null) {
|
||||
Bundle bundle = getArguments();
|
||||
if (bundle == null) {
|
||||
IpCrasher.openIssue();
|
||||
}
|
||||
boolean hasImagePickerConfig = bundle.containsKey(ImagePickerConfig.class.getSimpleName());
|
||||
boolean hasCameraOnlyConfig = bundle.containsKey(ImagePickerConfig.class.getSimpleName());
|
||||
|
||||
if (!hasCameraOnlyConfig && !hasImagePickerConfig) {
|
||||
IpCrasher.openIssue();
|
||||
}
|
||||
config = bundle.getParcelable(ImagePickerConfig.class.getSimpleName());
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private CameraOnlyConfig getCameraOnlyConfig() {
|
||||
return getArguments().getParcelable(CameraOnlyConfig.class.getSimpleName());
|
||||
}
|
||||
|
||||
private void setupView(View rootView) {
|
||||
progressBar = rootView.findViewById(R.id.progress_bar);
|
||||
emptyTextView = rootView.findViewById(R.id.tv_empty_images);
|
||||
recyclerView = rootView.findViewById(R.id.recyclerView);
|
||||
snackBarView = rootView.findViewById(R.id.ef_snackbar);
|
||||
}
|
||||
|
||||
private void setupRecyclerView(ImagePickerConfig config, ArrayList<Image> selectedImages) {
|
||||
recyclerViewManager = new RecyclerViewManager(
|
||||
recyclerView,
|
||||
config,
|
||||
getResources().getConfiguration().orientation
|
||||
);
|
||||
|
||||
recyclerViewManager.setupAdapters(selectedImages, (isSelected) -> recyclerViewManager.selectImage(isSelected)
|
||||
, bucket -> setImageAdapter(bucket.getImages()));
|
||||
|
||||
recyclerViewManager.setImageSelectedListener(selectedImage -> {
|
||||
updateTitle();
|
||||
interactionListener.selectionChanged(recyclerViewManager.getSelectedImages());
|
||||
if (ConfigUtils.shouldReturn(config, false) && !selectedImage.isEmpty()) {
|
||||
onDone();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void setupComponents() {
|
||||
preferences = new ImagePickerPreferences(getActivity());
|
||||
presenter = new ImagePickerPresenter(new ImageFileLoader(getActivity()));
|
||||
presenter.attachView(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!isCameraOnly) {
|
||||
getDataWithPermission();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putSerializable(STATE_KEY_CAMERA_MODULE, presenter.getCameraModule());
|
||||
|
||||
if (!isCameraOnly) {
|
||||
outState.putParcelable(STATE_KEY_RECYCLER, recyclerViewManager.getRecyclerState());
|
||||
outState.putParcelableArrayList(STATE_KEY_SELECTED_IMAGES, (ArrayList<? extends Parcelable>)
|
||||
recyclerViewManager.getSelectedImages());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set image adapter
|
||||
* 1. Set new data
|
||||
* 2. Update item decoration
|
||||
* 3. Update title
|
||||
*/
|
||||
void setImageAdapter(List<Image> images) {
|
||||
recyclerViewManager.setImageAdapter(images);
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
void setFolderAdapter(List<Folder> folders) {
|
||||
recyclerViewManager.setFolderAdapter(folders);
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
private void updateTitle() {
|
||||
interactionListener.setTitle(recyclerViewManager.getTitle());
|
||||
}
|
||||
|
||||
/**
|
||||
* On finish selected image
|
||||
* Get all selected images then return image to caller activity
|
||||
*/
|
||||
public void onDone() {
|
||||
presenter.onDoneSelectImages(recyclerViewManager.getSelectedImages());
|
||||
}
|
||||
|
||||
/**
|
||||
* Config recyclerView when configuration changed
|
||||
*/
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (recyclerViewManager != null) {
|
||||
// recyclerViewManager can be null here if we use cameraOnly mode
|
||||
recyclerViewManager.changeOrientation(newConfig.orientation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permission
|
||||
*/
|
||||
private void getDataWithPermission() {
|
||||
int rc = ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
if (rc == PackageManager.PERMISSION_GRANTED) {
|
||||
getData();
|
||||
} else {
|
||||
requestWriteExternalPermission();
|
||||
}
|
||||
}
|
||||
|
||||
private void getData() {
|
||||
presenter.abortLoad();
|
||||
ImagePickerConfig config = getImagePickerConfig();
|
||||
if (config != null) {
|
||||
presenter.loadImages(config);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request for permission
|
||||
* If permission denied or app is first launched, request for permission
|
||||
* If permission denied and user choose 'Never Ask Again', show snackbar with an action that navigate to app settings
|
||||
*/
|
||||
private void requestWriteExternalPermission() {
|
||||
logger.w("Write External permission is not granted. Requesting permission");
|
||||
|
||||
final String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
|
||||
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
requestPermissions(permissions, RC_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE);
|
||||
} else {
|
||||
final String permission = PREF_WRITE_EXTERNAL_STORAGE_REQUESTED;
|
||||
if (!preferences.isPermissionRequested(permission)) {
|
||||
preferences.setPermissionRequested(permission);
|
||||
requestPermissions(permissions, RC_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE);
|
||||
} else {
|
||||
snackBarView.show(R.string.ef_msg_no_write_external_permission, v -> openAppSettings());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void requestCameraPermissions() {
|
||||
logger.w("Write External permission is not granted. Requesting permission");
|
||||
|
||||
ArrayList<String> permissions = new ArrayList<>(2);
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
permissions.add(Manifest.permission.CAMERA);
|
||||
}
|
||||
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
|
||||
if (checkForRationale(permissions)) {
|
||||
requestPermissions(permissions.toArray(new String[permissions.size()]), RC_PERMISSION_REQUEST_CAMERA);
|
||||
} else {
|
||||
final String permission = ImagePickerPreferences.PREF_CAMERA_REQUESTED;
|
||||
if (!preferences.isPermissionRequested(permission)) {
|
||||
preferences.setPermissionRequested(permission);
|
||||
requestPermissions(permissions.toArray(new String[permissions.size()]), RC_PERMISSION_REQUEST_CAMERA);
|
||||
} else {
|
||||
if (isCameraOnly) {
|
||||
Toast.makeText(getActivity().getApplicationContext(),
|
||||
getString(R.string.ef_msg_no_camera_permission), Toast.LENGTH_SHORT).show();
|
||||
interactionListener.cancel();
|
||||
} else {
|
||||
snackBarView.show(R.string.ef_msg_no_camera_permission, v -> openAppSettings());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkForRationale(List<String> permissions) {
|
||||
for (int i = 0, size = permissions.size(); i < size; i++) {
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), permissions.get(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle permission results
|
||||
*/
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case RC_PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE: {
|
||||
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
logger.d("Write External permission granted");
|
||||
getData();
|
||||
return;
|
||||
}
|
||||
logger.e("Permission not granted: results len = " + grantResults.length +
|
||||
" Result code = " + (grantResults.length > 0 ? grantResults[0] : "(empty)"));
|
||||
interactionListener.cancel();
|
||||
}
|
||||
break;
|
||||
case RC_PERMISSION_REQUEST_CAMERA: {
|
||||
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
logger.d("Camera permission granted");
|
||||
captureImage();
|
||||
return;
|
||||
}
|
||||
logger.e("Permission not granted: results len = " + grantResults.length +
|
||||
" Result code = " + (grantResults.length > 0 ? grantResults[0] : "(empty)"));
|
||||
interactionListener.cancel();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
logger.d("Got unexpected permission result: " + requestCode);
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open app settings screen
|
||||
*/
|
||||
private void openAppSettings() {
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||
Uri.fromParts("package", getActivity().getPackageName(), null));
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the captured image is stored successfully
|
||||
* Then reload data
|
||||
*/
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == RC_CAPTURE) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
presenter.finishCaptureImage(getActivity(), data, getBaseConfig());
|
||||
} else if (resultCode == RESULT_CANCELED && isCameraOnly) {
|
||||
presenter.abortCaptureImage();
|
||||
interactionListener.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Request for camera permission
|
||||
*/
|
||||
public void captureImageWithPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
final boolean isCameraGranted = ActivityCompat
|
||||
.checkSelfPermission(getActivity(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
|
||||
final boolean isWriteGranted = ActivityCompat
|
||||
.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||
if (isCameraGranted && isWriteGranted) {
|
||||
captureImage();
|
||||
} else {
|
||||
logger.w("Camera permission is not granted. Requesting permission");
|
||||
requestCameraPermissions();
|
||||
}
|
||||
} else {
|
||||
captureImage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start camera intent
|
||||
* Create a temporary file and pass file Uri to camera intent
|
||||
*/
|
||||
private void captureImage() {
|
||||
if (!CameraHelper.checkCameraAvailability(getActivity())) {
|
||||
return;
|
||||
}
|
||||
presenter.captureImage(this, getBaseConfig(), RC_CAPTURE);
|
||||
}
|
||||
|
||||
private void startContentObserver() {
|
||||
if (isCameraOnly) {
|
||||
return;
|
||||
}
|
||||
if (handler == null) {
|
||||
handler = new Handler();
|
||||
}
|
||||
observer = new ContentObserver(handler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
getData();
|
||||
}
|
||||
};
|
||||
|
||||
getActivity().getContentResolver()
|
||||
.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, observer);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (presenter != null) {
|
||||
presenter.abortLoad();
|
||||
presenter.detachView();
|
||||
}
|
||||
|
||||
if (observer != null) {
|
||||
getActivity().getContentResolver().unregisterContentObserver(observer);
|
||||
observer = null;
|
||||
}
|
||||
|
||||
if (handler != null) {
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
handler = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if this Fragment can do anything with a "back" event, such as the containing
|
||||
// Activity receiving onBackPressed(). Returns false if the containing Activity should handle
|
||||
// it.
|
||||
// This Fragment might handle a "back" event by, for example, going back to the list of folders.
|
||||
// Or it might have no "back" to go, and return false.
|
||||
public boolean handleBack() {
|
||||
if (isCameraOnly) {
|
||||
return false;
|
||||
}
|
||||
if (recyclerViewManager.handleBack()) {
|
||||
// Handled.
|
||||
updateTitle();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isShowDoneButton() {
|
||||
return recyclerViewManager.isShowDoneButton();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof ImagePickerInteractionListener) {
|
||||
interactionListener = (ImagePickerInteractionListener) context;
|
||||
}
|
||||
}
|
||||
|
||||
public void setInteractionListener(ImagePickerInteractionListener listener) {
|
||||
interactionListener = listener;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
/* > View Methods */
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
@Override
|
||||
public void finishPickImages(List<Image> images) {
|
||||
Intent data = new Intent();
|
||||
data.putParcelableArrayListExtra(IpCons.EXTRA_SELECTED_IMAGES, (ArrayList<? extends Parcelable>) images);
|
||||
interactionListener.finishPickImages(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showCapturedImage() {
|
||||
getDataWithPermission();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showFetchCompleted(List<Image> images, List<Folder> folders) {
|
||||
ImagePickerConfig config = getImagePickerConfig();
|
||||
if (config != null && config.isFolderMode()) {
|
||||
setFolderAdapter(folders);
|
||||
} else {
|
||||
setImageAdapter(images);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(Throwable throwable) {
|
||||
String message = "Unknown Error";
|
||||
if (throwable != null && throwable instanceof NullPointerException) {
|
||||
message = "Images do not exist";
|
||||
}
|
||||
Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showLoading(boolean isLoading) {
|
||||
progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE);
|
||||
recyclerView.setVisibility(isLoading ? View.GONE : View.VISIBLE);
|
||||
emptyTextView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showEmpty() {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
emptyTextView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ImagePickerInteractionListener {
|
||||
void setTitle(String title);
|
||||
void cancel();
|
||||
// Get this callback by calling an ImagePickerFragment's finishPickImages() method. It
|
||||
// removes Images whose files no longer exist.
|
||||
void finishPickImages(Intent result);
|
||||
|
||||
/**
|
||||
* Called when the user selects or deselects sn image. Also called in onCreateView.
|
||||
* May include Images whose files no longer exist.
|
||||
*/
|
||||
void selectionChanged(List<Image> imageList);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
import com.esafirm.imagepicker.features.camera.DefaultCameraModule;
|
||||
import com.esafirm.imagepicker.features.common.BaseConfig;
|
||||
import com.esafirm.imagepicker.features.common.BasePresenter;
|
||||
import com.esafirm.imagepicker.features.common.ImageLoaderListener;
|
||||
import com.esafirm.imagepicker.helper.ConfigUtils;
|
||||
import com.esafirm.imagepicker.model.Folder;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class ImagePickerPresenter extends BasePresenter<ImagePickerView> {
|
||||
|
||||
private ImageFileLoader imageLoader;
|
||||
private DefaultCameraModule cameraModule;
|
||||
private Handler main = new Handler(Looper.getMainLooper());
|
||||
|
||||
ImagePickerPresenter(ImageFileLoader imageLoader) {
|
||||
this.imageLoader = imageLoader;
|
||||
}
|
||||
|
||||
DefaultCameraModule getCameraModule() {
|
||||
if (cameraModule == null) {
|
||||
cameraModule = new DefaultCameraModule();
|
||||
}
|
||||
return cameraModule;
|
||||
}
|
||||
|
||||
/* Set the camera module in onRestoreInstance */
|
||||
void setCameraModule(DefaultCameraModule cameraModule) {
|
||||
this.cameraModule = cameraModule;
|
||||
}
|
||||
|
||||
void abortLoad() {
|
||||
imageLoader.abortLoadImages();
|
||||
}
|
||||
|
||||
void loadImages(ImagePickerConfig config) {
|
||||
if (!isViewAttached()) return;
|
||||
|
||||
boolean isFolder = config.isFolderMode();
|
||||
boolean includeVideo = config.isIncludeVideo();
|
||||
boolean includeAnimation = config.isIncludeAnimation();
|
||||
ArrayList<File> excludedImages = config.getExcludedImages();
|
||||
|
||||
runOnUiIfAvailable(() -> getView().showLoading(true));
|
||||
|
||||
imageLoader.loadDeviceImages(isFolder, includeVideo, includeAnimation, excludedImages, new ImageLoaderListener() {
|
||||
@Override
|
||||
public void onImageLoaded(final List<Image> images, final List<Folder> folders) {
|
||||
runOnUiIfAvailable(() -> {
|
||||
getView().showFetchCompleted(images, folders);
|
||||
|
||||
final boolean isEmpty = folders != null
|
||||
? folders.isEmpty()
|
||||
: images.isEmpty();
|
||||
|
||||
if (isEmpty) {
|
||||
getView().showEmpty();
|
||||
} else {
|
||||
getView().showLoading(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(final Throwable throwable) {
|
||||
runOnUiIfAvailable(() -> getView().showError(throwable));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void onDoneSelectImages(List<Image> selectedImages) {
|
||||
if (selectedImages != null && selectedImages.size() > 0) {
|
||||
|
||||
/* Scan selected images which not existed */
|
||||
for (int i = 0; i < selectedImages.size(); i++) {
|
||||
Image image = selectedImages.get(i);
|
||||
File file = new File(image.getPath());
|
||||
if (!file.exists()) {
|
||||
selectedImages.remove(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
getView().finishPickImages(selectedImages);
|
||||
}
|
||||
}
|
||||
|
||||
void captureImage(Fragment fragment, BaseConfig config, int requestCode) {
|
||||
Context context = fragment.getActivity().getApplicationContext();
|
||||
Intent intent = getCameraModule().getCameraIntent(fragment.getActivity(), config);
|
||||
if (intent == null) {
|
||||
Toast.makeText(context, context.getString(R.string.ef_error_create_image_file), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
fragment.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
void finishCaptureImage(Context context, Intent data, final BaseConfig config) {
|
||||
getCameraModule().getImage(context, data, images -> {
|
||||
if (ConfigUtils.shouldReturn(config, true)) {
|
||||
getView().finishPickImages(images);
|
||||
} else {
|
||||
getView().showCapturedImage();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void abortCaptureImage() {
|
||||
getCameraModule().removeImage();
|
||||
}
|
||||
|
||||
private void runOnUiIfAvailable(Runnable runnable) {
|
||||
main.post(() -> {
|
||||
if (isViewAttached()) {
|
||||
runnable.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class ImagePickerSavePath implements Parcelable {
|
||||
|
||||
public static final ImagePickerSavePath DEFAULT = new ImagePickerSavePath("Camera", false);
|
||||
|
||||
private final String path;
|
||||
private final boolean isFullPath;
|
||||
|
||||
public ImagePickerSavePath(String path, boolean isFullPath) {
|
||||
this.path = path;
|
||||
this.isFullPath = isFullPath;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public boolean isFullPath() {
|
||||
return isFullPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(this.path);
|
||||
dest.writeByte(this.isFullPath ? (byte) 1 : (byte) 0);
|
||||
}
|
||||
|
||||
protected ImagePickerSavePath(Parcel in) {
|
||||
this.path = in.readString();
|
||||
this.isFullPath = in.readByte() != 0;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ImagePickerSavePath> CREATOR = new Parcelable.Creator<ImagePickerSavePath>() {
|
||||
@Override
|
||||
public ImagePickerSavePath createFromParcel(Parcel source) {
|
||||
return new ImagePickerSavePath(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImagePickerSavePath[] newArray(int size) {
|
||||
return new ImagePickerSavePath[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
import com.esafirm.imagepicker.features.common.MvpView;
|
||||
import com.esafirm.imagepicker.model.Folder;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ImagePickerView extends MvpView {
|
||||
void showLoading(boolean isLoading);
|
||||
void showFetchCompleted(List<Image> images, List<Folder> folders);
|
||||
void showError(Throwable throwable);
|
||||
void showEmpty();
|
||||
void showCapturedImage();
|
||||
void finishPickImages(List<Image> images);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
public class IpCons {
|
||||
|
||||
public static final int MODE_SINGLE = 1;
|
||||
public static final int MODE_MULTIPLE = 2;
|
||||
public static final int MAX_LIMIT = 999;
|
||||
|
||||
public static final int RC_IMAGE_PICKER = 0x229;
|
||||
|
||||
static final String EXTRA_SELECTED_IMAGES = "selectedImages";
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.esafirm.imagepicker.features;
|
||||
|
||||
/**
|
||||
* Define the ImagePicker return behaviour
|
||||
* NONE -> When image is picked, ImagePickerActivity will not dismissed even in Single Mode
|
||||
* ALL -> When image is picked dismiss then deliver result
|
||||
* CAMERA_ONLY -> When image is picked with Camera, dismiss then deliver the result
|
||||
* GALLERY_ONLY -> Same as CAMERA_ONLY but with Gallery
|
||||
*/
|
||||
public enum ReturnMode {
|
||||
NONE,
|
||||
ALL,
|
||||
CAMERA_ONLY,
|
||||
GALLERY_ONLY
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.esafirm.imagepicker.features.camera;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.provider.MediaStore;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
|
||||
public class CameraHelper {
|
||||
public static boolean checkCameraAvailability(Context context) {
|
||||
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
boolean isAvailable = intent.resolveActivity(context.getPackageManager()) != null;
|
||||
|
||||
if (!isAvailable) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
Toast.makeText(appContext,
|
||||
appContext.getString(R.string.ef_error_no_camera), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
return isAvailable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.esafirm.imagepicker.features.camera;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.esafirm.imagepicker.features.common.BaseConfig;
|
||||
|
||||
public interface CameraModule {
|
||||
Intent getCameraIntent(Context context, BaseConfig config);
|
||||
void getImage(Context context, Intent intent, OnImageReadyListener imageReadyListener);
|
||||
void removeImage();
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.esafirm.imagepicker.features.camera;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import com.esafirm.imagepicker.features.ImagePickerConfigFactory;
|
||||
import com.esafirm.imagepicker.features.common.BaseConfig;
|
||||
import com.esafirm.imagepicker.helper.ImagePickerUtils;
|
||||
import com.esafirm.imagepicker.helper.IpLogger;
|
||||
import com.esafirm.imagepicker.model.ImageFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
|
||||
public class DefaultCameraModule implements CameraModule, Serializable {
|
||||
|
||||
private String currentImagePath;
|
||||
|
||||
public Intent getCameraIntent(Context context) {
|
||||
return getCameraIntent(context, ImagePickerConfigFactory.createDefault());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent getCameraIntent(Context context, BaseConfig config) {
|
||||
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
File imageFile = ImagePickerUtils.createImageFile(config.getImageDirectory());
|
||||
if (imageFile != null) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
String providerName = String.format(Locale.ENGLISH, "%s%s", appContext.getPackageName(), ".imagepicker.provider");
|
||||
Uri uri = FileProvider.getUriForFile(appContext, providerName, imageFile);
|
||||
currentImagePath = "file:" + imageFile.getAbsolutePath();
|
||||
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
|
||||
|
||||
ImagePickerUtils.grantAppPermission(context, intent, uri);
|
||||
|
||||
return intent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getImage(final Context context, Intent intent, final OnImageReadyListener imageReadyListener) {
|
||||
if (imageReadyListener == null) {
|
||||
throw new IllegalStateException("OnImageReadyListener must not be null");
|
||||
}
|
||||
|
||||
if (currentImagePath == null) {
|
||||
IpLogger.getInstance().w("currentImagePath null. " +
|
||||
"This happen if you haven't call #getCameraIntent() or the activity is being recreated");
|
||||
imageReadyListener.onImageReady(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final Uri imageUri = Uri.parse(currentImagePath);
|
||||
if (imageUri != null) {
|
||||
MediaScannerConnection.scanFile(context.getApplicationContext(),
|
||||
new String[]{imageUri.getPath()}, null, (path, uri) -> {
|
||||
|
||||
IpLogger.getInstance().d("File " + path + " was scanned successfully: " + uri);
|
||||
|
||||
if (path == null) {
|
||||
IpLogger.getInstance().d("This should not happen, go back to Immediate implemenation");
|
||||
path = currentImagePath;
|
||||
}
|
||||
|
||||
imageReadyListener.onImageReady(ImageFactory.singleListFromPath(path));
|
||||
ImagePickerUtils.revokeAppPermission(context, imageUri);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeImage() {
|
||||
if (currentImagePath != null) {
|
||||
File file = new File(currentImagePath);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.esafirm.imagepicker.features.camera;
|
||||
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OnImageReadyListener {
|
||||
void onImageReady(List<Image> image);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.esafirm.imagepicker.features.cameraonly;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
import com.esafirm.imagepicker.features.common.BaseConfig;
|
||||
|
||||
public class CameraOnlyConfig extends BaseConfig {
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
}
|
||||
|
||||
public CameraOnlyConfig() {
|
||||
}
|
||||
|
||||
private CameraOnlyConfig(Parcel in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public static final Creator<CameraOnlyConfig> CREATOR = new Creator<CameraOnlyConfig>() {
|
||||
@Override
|
||||
public CameraOnlyConfig createFromParcel(Parcel source) {
|
||||
return new CameraOnlyConfig(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraOnlyConfig[] newArray(int size) {
|
||||
return new CameraOnlyConfig[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.esafirm.imagepicker.features.cameraonly;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.esafirm.imagepicker.features.ImagePickerActivity;
|
||||
import com.esafirm.imagepicker.features.ImagePickerConfigFactory;
|
||||
import com.esafirm.imagepicker.features.IpCons;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public class ImagePickerCameraOnly {
|
||||
|
||||
private CameraOnlyConfig config = ImagePickerConfigFactory.createCameraDefault();
|
||||
|
||||
public ImagePickerCameraOnly imageDirectory(String directory) {
|
||||
config.setImageDirectory(directory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ImagePickerCameraOnly imageFullDirectory(String fullPath) {
|
||||
config.setImageFullDirectory(fullPath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void start(Activity activity) {
|
||||
start(activity, IpCons.RC_IMAGE_PICKER);
|
||||
}
|
||||
|
||||
public void start(Activity activity, int requestCode) {
|
||||
activity.startActivityForResult(getIntent(activity), requestCode);
|
||||
}
|
||||
|
||||
public void start(Fragment fragment) {
|
||||
start(fragment, IpCons.RC_IMAGE_PICKER);
|
||||
}
|
||||
|
||||
public void start(Fragment fragment, int requestCode) {
|
||||
fragment.startActivityForResult(getIntent(fragment.getActivity()), requestCode);
|
||||
}
|
||||
|
||||
public void start(android.app.Fragment fragment) {
|
||||
start(fragment, IpCons.RC_IMAGE_PICKER);
|
||||
}
|
||||
|
||||
public void start(android.app.Fragment fragment, int requestCode) {
|
||||
fragment.startActivityForResult(getIntent(fragment.getActivity()), requestCode);
|
||||
}
|
||||
|
||||
public Intent getIntent(Context context) {
|
||||
Intent intent = new Intent(context, ImagePickerActivity.class);
|
||||
intent.putExtra(CameraOnlyConfig.class.getSimpleName(), config);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.esafirm.imagepicker.features.common;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.esafirm.imagepicker.features.ImagePickerSavePath;
|
||||
import com.esafirm.imagepicker.features.ReturnMode;
|
||||
|
||||
public class BaseConfig implements Parcelable {
|
||||
|
||||
private ImagePickerSavePath savePath;
|
||||
private ReturnMode returnMode;
|
||||
|
||||
public ReturnMode getReturnMode() {
|
||||
return returnMode;
|
||||
}
|
||||
|
||||
public ImagePickerSavePath getImageDirectory() {
|
||||
return savePath;
|
||||
}
|
||||
|
||||
public void setSavePath(ImagePickerSavePath savePath) {
|
||||
this.savePath = savePath;
|
||||
}
|
||||
|
||||
public void setImageDirectory(String dirName) {
|
||||
savePath = new ImagePickerSavePath(dirName, false);
|
||||
}
|
||||
|
||||
public void setImageFullDirectory(String path) {
|
||||
savePath = new ImagePickerSavePath(path, true);
|
||||
}
|
||||
|
||||
public void setReturnMode(ReturnMode returnMode) {
|
||||
this.returnMode = returnMode;
|
||||
}
|
||||
|
||||
public BaseConfig() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeParcelable(this.savePath, flags);
|
||||
dest.writeInt(this.returnMode == null ? -1 : this.returnMode.ordinal());
|
||||
}
|
||||
|
||||
protected BaseConfig(Parcel in) {
|
||||
this.savePath = in.readParcelable(ImagePickerSavePath.class.getClassLoader());
|
||||
int tmpReturnMode = in.readInt();
|
||||
this.returnMode = tmpReturnMode == -1 ? null : ReturnMode.values()[tmpReturnMode];
|
||||
}
|
||||
|
||||
public static final Creator<BaseConfig> CREATOR = new Creator<BaseConfig>() {
|
||||
@Override
|
||||
public BaseConfig createFromParcel(Parcel source) {
|
||||
return new BaseConfig(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseConfig[] newArray(int size) {
|
||||
return new BaseConfig[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.esafirm.imagepicker.features.common;
|
||||
|
||||
public class BasePresenter<T extends MvpView> {
|
||||
|
||||
private T view;
|
||||
|
||||
public void attachView(T view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public T getView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
public void detachView() {
|
||||
view = null;
|
||||
}
|
||||
|
||||
protected boolean isViewAttached() {
|
||||
return view != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.esafirm.imagepicker.features.common;
|
||||
|
||||
import com.esafirm.imagepicker.model.Folder;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ImageLoaderListener {
|
||||
void onImageLoaded(List<Image> images, List<Folder> folders);
|
||||
void onFailed(Throwable throwable);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.esafirm.imagepicker.features.common;
|
||||
|
||||
public interface MvpView {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.esafirm.imagepicker.features.imageloader;
|
||||
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.esafirm.imagepicker.R;
|
||||
|
||||
public class DefaultImageLoader implements ImageLoader {
|
||||
|
||||
@Override
|
||||
public void loadImage(String path, ImageView imageView, ImageType imageType) {
|
||||
Glide.with(imageView.getContext())
|
||||
.load(path)
|
||||
.apply(RequestOptions
|
||||
.placeholderOf(imageType == ImageType.FOLDER
|
||||
? R.drawable.ef_folder_placeholder
|
||||
: R.drawable.ef_image_placeholder)
|
||||
.error(imageType == ImageType.FOLDER
|
||||
? R.drawable.ef_folder_placeholder
|
||||
: R.drawable.ef_image_placeholder)
|
||||
)
|
||||
.transition(DrawableTransitionOptions.withCrossFade())
|
||||
.into(imageView);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.esafirm.imagepicker.features.imageloader;
|
||||
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public interface ImageLoader extends Serializable {
|
||||
void loadImage(String path, ImageView imageView, ImageType imageType);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.esafirm.imagepicker.features.imageloader;
|
||||
|
||||
public enum ImageType {
|
||||
FOLDER,
|
||||
GALLERY
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package com.esafirm.imagepicker.features.recyclers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Parcelable;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
import com.esafirm.imagepicker.adapter.FolderPickerAdapter;
|
||||
import com.esafirm.imagepicker.adapter.ImagePickerAdapter;
|
||||
import com.esafirm.imagepicker.features.ClickUtils;
|
||||
import com.esafirm.imagepicker.features.ImagePickerConfig;
|
||||
import com.esafirm.imagepicker.features.ReturnMode;
|
||||
import com.esafirm.imagepicker.features.imageloader.ImageLoader;
|
||||
import com.esafirm.imagepicker.helper.ConfigUtils;
|
||||
import com.esafirm.imagepicker.helper.ImagePickerUtils;
|
||||
import com.esafirm.imagepicker.listeners.OnFolderClickListener;
|
||||
import com.esafirm.imagepicker.listeners.OnImageClickListener;
|
||||
import com.esafirm.imagepicker.listeners.OnImageSelectedListener;
|
||||
import com.esafirm.imagepicker.model.Folder;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
import com.esafirm.imagepicker.view.GridSpacingItemDecoration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import static com.esafirm.imagepicker.features.IpCons.MAX_LIMIT;
|
||||
import static com.esafirm.imagepicker.features.IpCons.MODE_MULTIPLE;
|
||||
import static com.esafirm.imagepicker.features.IpCons.MODE_SINGLE;
|
||||
|
||||
public class RecyclerViewManager {
|
||||
|
||||
private final Context context;
|
||||
private final RecyclerView recyclerView;
|
||||
private final ImagePickerConfig config;
|
||||
|
||||
private GridLayoutManager layoutManager;
|
||||
private GridSpacingItemDecoration itemOffsetDecoration;
|
||||
|
||||
private ImagePickerAdapter imageAdapter;
|
||||
private FolderPickerAdapter folderAdapter;
|
||||
|
||||
private Parcelable foldersState;
|
||||
|
||||
private int imageColumns;
|
||||
private int folderColumns;
|
||||
|
||||
public RecyclerViewManager(RecyclerView recyclerView, ImagePickerConfig config, int orientation) {
|
||||
this.recyclerView = recyclerView;
|
||||
this.config = config;
|
||||
this.context = recyclerView.getContext();
|
||||
changeOrientation(orientation);
|
||||
}
|
||||
|
||||
public void onRestoreState(Parcelable recyclerState) {
|
||||
layoutManager.onRestoreInstanceState(recyclerState);
|
||||
}
|
||||
|
||||
public Parcelable getRecyclerState() {
|
||||
return layoutManager.onSaveInstanceState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item size, column size base on the screen orientation
|
||||
*/
|
||||
public void changeOrientation(int orientation) {
|
||||
imageColumns = orientation == Configuration.ORIENTATION_PORTRAIT ? 3 : 5;
|
||||
folderColumns = orientation == Configuration.ORIENTATION_PORTRAIT ? 2 : 4;
|
||||
|
||||
boolean shouldShowFolder = config.isFolderMode() && isDisplayingFolderView();
|
||||
int columns = shouldShowFolder ? folderColumns : imageColumns;
|
||||
layoutManager = new GridLayoutManager(context, columns);
|
||||
recyclerView.setLayoutManager(layoutManager);
|
||||
recyclerView.setHasFixedSize(true);
|
||||
setItemDecoration(columns);
|
||||
}
|
||||
|
||||
public void setupAdapters(ArrayList<Image> selectedImages, OnImageClickListener onImageClickListener, OnFolderClickListener onFolderClickListener) {
|
||||
if (config.getMode() == MODE_SINGLE && selectedImages != null && selectedImages.size() > 1) {
|
||||
selectedImages = null;
|
||||
}
|
||||
/* Init folder and image adapter */
|
||||
final ImageLoader imageLoader = config.getImageLoader();
|
||||
imageAdapter = new ImagePickerAdapter(context, imageLoader, selectedImages, onImageClickListener);
|
||||
folderAdapter = new FolderPickerAdapter(context, imageLoader, bucket -> {
|
||||
foldersState = recyclerView.getLayoutManager().onSaveInstanceState();
|
||||
onFolderClickListener.onFolderClick(bucket);
|
||||
});
|
||||
}
|
||||
|
||||
private void setItemDecoration(int columns) {
|
||||
if (itemOffsetDecoration != null) {
|
||||
recyclerView.removeItemDecoration(itemOffsetDecoration);
|
||||
}
|
||||
itemOffsetDecoration = new GridSpacingItemDecoration(
|
||||
columns,
|
||||
context.getResources().getDimensionPixelSize(R.dimen.ef_item_padding),
|
||||
false
|
||||
);
|
||||
recyclerView.addItemDecoration(itemOffsetDecoration);
|
||||
|
||||
layoutManager.setSpanCount(columns);
|
||||
}
|
||||
|
||||
// Returns true if a back action was handled by going back a folder; false otherwise.
|
||||
public boolean handleBack() {
|
||||
if (config.isFolderMode() && !isDisplayingFolderView()) {
|
||||
setFolderAdapter(null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isDisplayingFolderView() {
|
||||
return recyclerView.getAdapter() == null || recyclerView.getAdapter() instanceof FolderPickerAdapter;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
if (isDisplayingFolderView()) {
|
||||
return ConfigUtils.getFolderTitle(context, config);
|
||||
}
|
||||
|
||||
if (config.getMode() == MODE_SINGLE) {
|
||||
return ConfigUtils.getImageTitle(context, config);
|
||||
}
|
||||
|
||||
final int imageSize = imageAdapter.getSelectedImages().size();
|
||||
final boolean useDefaultTitle = !ImagePickerUtils.isStringEmpty(config.getImageTitle()) && imageSize == 0;
|
||||
|
||||
if (useDefaultTitle) {
|
||||
return ConfigUtils.getImageTitle(context, config);
|
||||
}
|
||||
return config.getLimit() == MAX_LIMIT
|
||||
? String.format(context.getString(R.string.ef_selected), imageSize)
|
||||
: String.format(context.getString(R.string.ef_selected_with_limit), imageSize, config.getLimit());
|
||||
}
|
||||
|
||||
public void setImageAdapter(List<Image> images) {
|
||||
imageAdapter.setData(images);
|
||||
setItemDecoration(imageColumns);
|
||||
recyclerView.setAdapter(imageAdapter);
|
||||
}
|
||||
|
||||
public void setFolderAdapter(List<Folder> folders) {
|
||||
folderAdapter.setData(folders);
|
||||
setItemDecoration(folderColumns);
|
||||
recyclerView.setAdapter(folderAdapter);
|
||||
|
||||
if (foldersState != null) {
|
||||
layoutManager.setSpanCount(folderColumns);
|
||||
recyclerView.getLayoutManager().onRestoreInstanceState(foldersState);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
/* > Images */
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
private void checkAdapterIsInitialized() {
|
||||
if (imageAdapter == null) {
|
||||
throw new IllegalStateException("Must call setupAdapters first!");
|
||||
}
|
||||
}
|
||||
|
||||
public List<Image> getSelectedImages() {
|
||||
checkAdapterIsInitialized();
|
||||
return imageAdapter.getSelectedImages();
|
||||
}
|
||||
|
||||
public void setImageSelectedListener(OnImageSelectedListener listener) {
|
||||
checkAdapterIsInitialized();
|
||||
imageAdapter.setImageSelectedListener(listener);
|
||||
}
|
||||
|
||||
public boolean selectImage(boolean isSelected) {
|
||||
if (config.getMode() == MODE_MULTIPLE) {
|
||||
if (imageAdapter.getSelectedImages().size() >= config.getLimit() && !isSelected) {
|
||||
if(ClickUtils.onclickTimes()){
|
||||
Toast.makeText(context, "已达到最多选择照片数量", Toast.LENGTH_SHORT).show();
|
||||
return false;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (config.getMode() == MODE_SINGLE) {
|
||||
if (imageAdapter.getSelectedImages().size() > 0) {
|
||||
imageAdapter.removeAllSelectedSingleClick();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isShowDoneButton() {
|
||||
return !isDisplayingFolderView()
|
||||
&& !imageAdapter.getSelectedImages().isEmpty()
|
||||
&& (config.getReturnMode() != ReturnMode.ALL && config.getReturnMode() != ReturnMode.GALLERY_ONLY);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.esafirm.imagepicker.helper;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
import com.esafirm.imagepicker.features.ImagePickerConfig;
|
||||
import com.esafirm.imagepicker.features.IpCons;
|
||||
import com.esafirm.imagepicker.features.ReturnMode;
|
||||
import com.esafirm.imagepicker.features.common.BaseConfig;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class ConfigUtils {
|
||||
|
||||
public static ImagePickerConfig checkConfig(ImagePickerConfig config) {
|
||||
if (config == null) {
|
||||
throw new IllegalStateException("ImagePickerConfig cannot be null");
|
||||
}
|
||||
if (config.getMode() != IpCons.MODE_SINGLE
|
||||
&& (config.getReturnMode() == ReturnMode.GALLERY_ONLY
|
||||
|| config.getReturnMode() == ReturnMode.ALL)) {
|
||||
throw new IllegalStateException("ReturnMode.GALLERY_ONLY and ReturnMode.ALL is only applicable in Single Mode!");
|
||||
}
|
||||
if (config.getImageLoader() != null && !(config.getImageLoader() instanceof Serializable)) {
|
||||
throw new IllegalStateException("Custom image loader must be a class that implement ImageLoader." +
|
||||
" This limitation due to Serializeable");
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
public static boolean shouldReturn(BaseConfig config, boolean isCamera) {
|
||||
ReturnMode mode = config.getReturnMode();
|
||||
if (isCamera) {
|
||||
return mode == ReturnMode.ALL || mode == ReturnMode.CAMERA_ONLY;
|
||||
} else {
|
||||
return mode == ReturnMode.ALL || mode == ReturnMode.GALLERY_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFolderTitle(Context context, ImagePickerConfig config) {
|
||||
final String folderTitle = config.getFolderTitle();
|
||||
return ImagePickerUtils.isStringEmpty(folderTitle)
|
||||
? context.getString(R.string.ef_title_folder)
|
||||
: folderTitle;
|
||||
}
|
||||
|
||||
public static String getImageTitle(Context context, ImagePickerConfig config) {
|
||||
final String configImageTitle = config.getImageTitle();
|
||||
return ImagePickerUtils.isStringEmpty(configImageTitle)
|
||||
? context.getString(R.string.ef_title_select_image)
|
||||
: configImageTitle;
|
||||
}
|
||||
|
||||
public static String getDoneButtonText(Context context, ImagePickerConfig config) {
|
||||
final String doneButtonText = config.getDoneButtonText();
|
||||
return ImagePickerUtils.isStringEmpty(doneButtonText)
|
||||
? context.getString(R.string.ef_done)
|
||||
: doneButtonText;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.esafirm.imagepicker.helper;
|
||||
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
public class ImagePickerFileProvider extends FileProvider {
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.esafirm.imagepicker.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class ImagePickerPreferences {
|
||||
|
||||
public static final String PREF_WRITE_EXTERNAL_STORAGE_REQUESTED = "writeExternalRequested";
|
||||
public static final String PREF_CAMERA_REQUESTED = "cameraRequested";
|
||||
|
||||
private Context context;
|
||||
|
||||
public ImagePickerPreferences(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a permission is requested
|
||||
*/
|
||||
public void setPermissionRequested(String permission) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putBoolean(permission, true);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a permission is requestted or not (false by default)
|
||||
*/
|
||||
public boolean isPermissionRequested(String permission) {
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return preferences.getBoolean(permission, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.esafirm.imagepicker.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.esafirm.imagepicker.features.ImagePickerSavePath;
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URLConnection;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class ImagePickerUtils {
|
||||
|
||||
public static boolean isStringEmpty(@Nullable String str) {
|
||||
return str == null || str.length() == 0;
|
||||
}
|
||||
|
||||
public static File createImageFile(ImagePickerSavePath savePath) {
|
||||
// External sdcard location
|
||||
final String path = savePath.getPath();
|
||||
File mediaStorageDir = savePath.isFullPath()
|
||||
? new File(path)
|
||||
: new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), path);
|
||||
|
||||
// Create the storage directory if it does not exist
|
||||
if (!mediaStorageDir.exists()) {
|
||||
if (!mediaStorageDir.mkdirs()) {
|
||||
IpLogger.getInstance().d("Oops! Failed create " + path);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a media file name
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
|
||||
String imageFileName = "IMG_" + timeStamp;
|
||||
|
||||
File imageFile = null;
|
||||
try {
|
||||
imageFile = File.createTempFile(imageFileName, ".jpg", mediaStorageDir);
|
||||
} catch (IOException e) {
|
||||
IpLogger.getInstance().d("Oops! Failed create " + imageFileName + " file");
|
||||
}
|
||||
return imageFile;
|
||||
}
|
||||
|
||||
public static String getNameFromFilePath(String path) {
|
||||
if (path.contains(File.separator)) {
|
||||
return path.substring(path.lastIndexOf(File.separator) + 1);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static void grantAppPermission(Context context, Intent intent, Uri fileUri) {
|
||||
List<ResolveInfo> resolvedIntentActivities = context.getPackageManager()
|
||||
.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
|
||||
for (ResolveInfo resolvedIntentInfo : resolvedIntentActivities) {
|
||||
String packageName = resolvedIntentInfo.activityInfo.packageName;
|
||||
context.grantUriPermission(packageName, fileUri,
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
public static void revokeAppPermission(Context context, Uri fileUri) {
|
||||
context.revokeUriPermission(fileUri,
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
|
||||
public static boolean isGifFormat(Image image) {
|
||||
String extension = getExtension(image.getPath());
|
||||
return extension.equalsIgnoreCase("gif");
|
||||
}
|
||||
|
||||
public static boolean isVideoFormat(Image image) {
|
||||
String extension = getExtension(image.getPath());
|
||||
String mimeType = TextUtils.isEmpty(extension)
|
||||
? URLConnection.guessContentTypeFromName(image.getPath())
|
||||
: MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
return mimeType != null && mimeType.startsWith("video");
|
||||
|
||||
}
|
||||
|
||||
private static String getExtension(String path) {
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(path);
|
||||
if (!TextUtils.isEmpty(extension)) {
|
||||
return extension;
|
||||
}
|
||||
if (path.contains(".")) {
|
||||
return path.substring(path.lastIndexOf(".") + 1, path.length());
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.esafirm.imagepicker.helper;
|
||||
|
||||
public class IpCrasher {
|
||||
public static void openIssue() {
|
||||
throw new IllegalStateException("This should not happen. Please open an issue!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.esafirm.imagepicker.helper;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class IpLogger {
|
||||
|
||||
private static final String TAG = "ImagePicker";
|
||||
|
||||
private static IpLogger INSTANCE;
|
||||
|
||||
private boolean isEnable = true;
|
||||
|
||||
public static IpLogger getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new IpLogger();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private IpLogger() {
|
||||
}
|
||||
|
||||
public void setEnable(boolean enable) {
|
||||
isEnable = enable;
|
||||
}
|
||||
|
||||
public void d(String message) {
|
||||
if (isEnable) {
|
||||
Log.d(TAG, message);
|
||||
}
|
||||
}
|
||||
|
||||
public void e(String message) {
|
||||
if (isEnable) {
|
||||
Log.e(TAG, message);
|
||||
}
|
||||
}
|
||||
|
||||
public void w(String message) {
|
||||
if (isEnable) {
|
||||
Log.w(TAG, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.esafirm.imagepicker.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class LocaleManager {
|
||||
|
||||
private static String language;
|
||||
|
||||
public static void setLanguange(String newLanguage) {
|
||||
language = newLanguage;
|
||||
}
|
||||
|
||||
private static String getLanguage() {
|
||||
return language != null && !language.isEmpty()
|
||||
? language
|
||||
: Locale.getDefault().getLanguage();
|
||||
}
|
||||
|
||||
public static Context updateResources(Context context) {
|
||||
Locale locale = new Locale(getLanguage());
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Resources res = context.getResources();
|
||||
Configuration config = new Configuration(res.getConfiguration());
|
||||
if (Build.VERSION.SDK_INT >= 17) {
|
||||
config.setLocale(locale);
|
||||
context = context.createConfigurationContext(config);
|
||||
} else {
|
||||
config.locale = locale;
|
||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.esafirm.imagepicker.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
|
||||
public class ViewUtils {
|
||||
|
||||
public static Drawable getArrowIcon(Context context) {
|
||||
final int backResourceId;
|
||||
if (Build.VERSION.SDK_INT >= 17 && context.getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
|
||||
// For right-to-left layouts, pick the drawable that points to the right (forward).
|
||||
backResourceId = R.drawable.ef_ic_arrow_forward;
|
||||
} else {
|
||||
// For left-to-right layouts, pick the drawable that points to the left (back).
|
||||
backResourceId = R.drawable.ef_ic_arrow_back;
|
||||
}
|
||||
return ContextCompat.getDrawable(context.getApplicationContext(), backResourceId);
|
||||
}
|
||||
|
||||
public static void onPreDraw(final View view, final Runnable runnable) {
|
||||
view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
view.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
runnable.run();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.esafirm.imagepicker.listeners;
|
||||
|
||||
import com.esafirm.imagepicker.model.Folder;
|
||||
|
||||
public interface OnFolderClickListener {
|
||||
void onFolderClick(Folder bucket);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.esafirm.imagepicker.listeners;
|
||||
|
||||
public interface OnImageClickListener {
|
||||
boolean onImageClick(boolean isSelected);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.esafirm.imagepicker.listeners;
|
||||
|
||||
import com.esafirm.imagepicker.model.Image;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OnImageSelectedListener {
|
||||
void onSelectionUpdate(List<Image> selectedImage);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.esafirm.imagepicker.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Created by boss1088 on 8/22/16.
|
||||
*/
|
||||
public class Folder {
|
||||
|
||||
private String folderName;
|
||||
private ArrayList<Image> images;
|
||||
|
||||
public Folder(String bucket) {
|
||||
folderName = bucket;
|
||||
images = new ArrayList<>();
|
||||
}
|
||||
|
||||
public String getFolderName() {
|
||||
return folderName;
|
||||
}
|
||||
|
||||
public void setFolderName(String folderName) {
|
||||
this.folderName = folderName;
|
||||
}
|
||||
|
||||
public ArrayList<Image> getImages() {
|
||||
return images;
|
||||
}
|
||||
|
||||
public void setImages(ArrayList<Image> images) {
|
||||
this.images = images;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.esafirm.imagepicker.model;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class Image implements Parcelable {
|
||||
|
||||
private long id;
|
||||
private String name;
|
||||
private String path;
|
||||
|
||||
public Image(long id, String name, String path) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Image image = (Image) o;
|
||||
return image.getPath().equalsIgnoreCase(getPath());
|
||||
}
|
||||
|
||||
/* --------------------------------------------------- */
|
||||
/* > Parcelable */
|
||||
/* --------------------------------------------------- */
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(this.id);
|
||||
dest.writeString(this.name);
|
||||
dest.writeString(this.path);
|
||||
}
|
||||
|
||||
protected Image(Parcel in) {
|
||||
this.id = in.readLong();
|
||||
this.name = in.readString();
|
||||
this.path = in.readString();
|
||||
}
|
||||
|
||||
public static final Creator<Image> CREATOR = new Creator<Image>() {
|
||||
@Override
|
||||
public Image createFromParcel(Parcel source) {
|
||||
return new Image(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Image[] newArray(int size) {
|
||||
return new Image[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.esafirm.imagepicker.model;
|
||||
|
||||
import com.esafirm.imagepicker.helper.ImagePickerUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ImageFactory {
|
||||
|
||||
public static List<Image> singleListFromPath(String path) {
|
||||
List<Image> images = new ArrayList<>();
|
||||
images.add(new Image(0, ImagePickerUtils.getNameFromFilePath(path), path));
|
||||
return images;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.esafirm.imagepicker.view;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Created by hoanglam on 9/5/16.
|
||||
*/
|
||||
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private int spanCount;
|
||||
private int spacing;
|
||||
private boolean includeEdge;
|
||||
|
||||
public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
|
||||
this.spanCount = spanCount;
|
||||
this.spacing = spacing;
|
||||
this.includeEdge = includeEdge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||
int position = parent.getChildAdapterPosition(view);
|
||||
int column = position % spanCount;
|
||||
|
||||
if (includeEdge) {
|
||||
outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
|
||||
outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
|
||||
|
||||
if (position < spanCount) {
|
||||
outRect.top = spacing;
|
||||
}
|
||||
outRect.bottom = spacing;
|
||||
} else {
|
||||
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
|
||||
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
|
||||
if (position >= spanCount) {
|
||||
outRect.top = spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.esafirm.imagepicker.view;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.esafirm.imagepicker.R;
|
||||
|
||||
public class SnackBarView extends RelativeLayout {
|
||||
|
||||
private static final int ANIM_DURATION = 200;
|
||||
|
||||
private static final Interpolator INTERPOLATOR = new FastOutLinearInInterpolator();
|
||||
|
||||
private TextView txtCaption;
|
||||
private Button btnAction;
|
||||
|
||||
public SnackBarView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public SnackBarView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SnackBarView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
View.inflate(getContext(), R.layout.ef_imagepikcer_snackbar, this);
|
||||
if (isInEditMode()) {
|
||||
return;
|
||||
}
|
||||
int height = getContext().getResources().getDimensionPixelSize(R.dimen.ef_height_snackbar);
|
||||
ViewCompat.setTranslationY(this, height);
|
||||
ViewCompat.setAlpha(this, 0f);
|
||||
|
||||
int padding = getContext().getResources().getDimensionPixelSize(R.dimen.ef_spacing_double);
|
||||
setPadding(padding, 0, padding, 0);
|
||||
|
||||
txtCaption = (TextView) findViewById(R.id.ef_snackbar_txt_bottom_caption);
|
||||
btnAction = (Button) findViewById(R.id.ef_snackbar_btn_action);
|
||||
}
|
||||
|
||||
public void setText(@StringRes int textResId) {
|
||||
txtCaption.setText(textResId);
|
||||
}
|
||||
|
||||
public void setOnActionClickListener(@StringRes int textId, final OnClickListener onClickListener) {
|
||||
if (textId == 0) {
|
||||
textId = R.string.ef_ok;
|
||||
}
|
||||
|
||||
btnAction.setText(textId);
|
||||
btnAction.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(final View v) {
|
||||
hide(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
onClickListener.onClick(v);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void show(@StringRes int textResId, OnClickListener onClickListener) {
|
||||
setText(textResId);
|
||||
setOnActionClickListener(0, onClickListener);
|
||||
|
||||
ViewCompat.animate(this)
|
||||
.translationY(0f)
|
||||
.setDuration(ANIM_DURATION)
|
||||
.setInterpolator(INTERPOLATOR)
|
||||
.alpha(1f);
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
hide(null);
|
||||
}
|
||||
|
||||
private void hide(Runnable runnable) {
|
||||
ViewCompat.animate(this)
|
||||
.translationY(getHeight())
|
||||
.setDuration(ANIM_DURATION)
|
||||
.alpha(0.5f)
|
||||
.withEndAction(runnable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.esafirm.imagepicker.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
/**
|
||||
* Created by hoanglam on 9/5/16.
|
||||
*/
|
||||
public class SquareFrameLayout extends FrameLayout {
|
||||
public SquareFrameLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SquareFrameLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user