package me.nereo.multi_image_selector; import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Color; import android.graphics.Point; import android.graphics.drawable.ColorDrawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.ContextCompat; import android.support.v4.content.CursorLoader; import android.support.v4.content.FileProvider; import android.support.v4.content.Loader; import android.support.v7.app.AlertDialog; import android.support.v7.widget.ListPopupWindow; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.GridView; import android.widget.TextView; import android.widget.Toast; import com.squareup.picasso.Picasso; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import me.nereo.multi_image_selector.adapter.FolderAdapter; import me.nereo.multi_image_selector.adapter.ImageGridAdapter; import me.nereo.multi_image_selector.bean.Folder; import me.nereo.multi_image_selector.bean.Image; import me.nereo.multi_image_selector.utils.FileUtils; import me.nereo.multi_image_selector.utils.ScreenUtils; /** * Multi image selector Fragment * Created by Nereo on 2015/4/7. * Updated by nereo on 2016/5/18. */ public class MultiImageSelectorFragment extends Fragment { public static final String TAG = "MultiImageSelectorFragment"; private static final int REQUEST_STORAGE_WRITE_ACCESS_PERMISSION = 110; private static final int REQUEST_CAMERA = 100; private static final String KEY_TEMP_FILE = "key_temp_file"; // Single choice public static final int MODE_SINGLE = 0; // Multi choice public static final int MODE_MULTI = 1; /** Max image size,int,*/ public static final String EXTRA_SELECT_COUNT = "max_select_count"; /** Select mode,{@link #MODE_MULTI} by default */ public static final String EXTRA_SELECT_MODE = "select_count_mode"; /** Whether show camera,true by default */ public static final String EXTRA_SHOW_CAMERA = "show_camera"; /** Original data set */ public static final String EXTRA_DEFAULT_SELECTED_LIST = "default_list"; // loaders private static final int LOADER_ALL = 0; private static final int LOADER_CATEGORY = 1; // image result data set private ArrayList resultList = new ArrayList<>(); // folder result data set private ArrayList mResultFolder = new ArrayList<>(); private GridView mGridView; private Callback mCallback; private ImageGridAdapter mImageAdapter; private FolderAdapter mFolderAdapter; private ListPopupWindow mFolderPopupWindow; private TextView mCategoryText; private View mPopupAnchorView; private boolean hasFolderGened = false; private File mTmpFile; @Override public void onAttach(Context context) { super.onAttach(context); try { mCallback = (Callback) getActivity(); }catch (ClassCastException e){ throw new ClassCastException("The Activity must implement MultiImageSelectorFragment.Callback interface..."); } } @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.mis_fragment_multi_image, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); final int mode = selectMode(); if(mode == MODE_MULTI) { ArrayList tmp = getArguments().getStringArrayList(EXTRA_DEFAULT_SELECTED_LIST); if(tmp != null && tmp.size()>0) { resultList = tmp; } } mImageAdapter = new ImageGridAdapter(getActivity(), showCamera(), 3); mImageAdapter.showSelectIndicator(mode == MODE_MULTI); mPopupAnchorView = view.findViewById(R.id.footer); mCategoryText = (TextView) view.findViewById(R.id.category_btn); mCategoryText.setText(R.string.mis_folder_all); mCategoryText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(mFolderPopupWindow == null){ createPopupFolderList(); } if (mFolderPopupWindow.isShowing()) { mFolderPopupWindow.dismiss(); } else { mFolderPopupWindow.show(); int index = mFolderAdapter.getSelectIndex(); index = index == 0 ? index : index - 1; mFolderPopupWindow.getListView().setSelection(index); } } }); mGridView = (GridView) view.findViewById(R.id.grid); mGridView.setAdapter(mImageAdapter); mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { if (mImageAdapter.isShowCamera()) { if (i == 0) { showCameraAction(); } else { Image image = (Image) adapterView.getAdapter().getItem(i); selectImageFromGrid(image, mode); } } else { Image image = (Image) adapterView.getAdapter().getItem(i); selectImageFromGrid(image, mode); } } }); mGridView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_FLING) { Picasso.get().pauseTag(TAG); } else { Picasso.get().resumeTag(TAG); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); mFolderAdapter = new FolderAdapter(getActivity()); } /** * Create popup ListView */ private void createPopupFolderList() { Point point = ScreenUtils.getScreenSize(getActivity()); int width = point.x; int height = (int) (point.y * (4.5f/8.0f)); mFolderPopupWindow = new ListPopupWindow(getActivity()); mFolderPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.WHITE)); mFolderPopupWindow.setAdapter(mFolderAdapter); mFolderPopupWindow.setContentWidth(width); mFolderPopupWindow.setWidth(width); mFolderPopupWindow.setHeight(height); mFolderPopupWindow.setAnchorView(mPopupAnchorView); mFolderPopupWindow.setModal(true); mFolderPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { mFolderAdapter.setSelectIndex(i); final int index = i; final AdapterView v = adapterView; new Handler().postDelayed(new Runnable() { @Override public void run() { mFolderPopupWindow.dismiss(); if (index == 0) { getActivity().getSupportLoaderManager().restartLoader(LOADER_ALL, null, mLoaderCallback); mCategoryText.setText(R.string.mis_folder_all); if (showCamera()) { mImageAdapter.setShowCamera(true); } else { mImageAdapter.setShowCamera(false); } } else { Folder folder = (Folder) v.getAdapter().getItem(index); if (null != folder) { mImageAdapter.setData(folder.images); mCategoryText.setText(folder.name); if (resultList != null && resultList.size() > 0) { mImageAdapter.setDefaultSelected(resultList); } } mImageAdapter.setShowCamera(false); } mGridView.smoothScrollToPosition(0); } }, 100); } }); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putSerializable(KEY_TEMP_FILE, mTmpFile); } @Override public void onViewStateRestored(@Nullable Bundle savedInstanceState) { super.onViewStateRestored(savedInstanceState); if (savedInstanceState != null) { mTmpFile = (File) savedInstanceState.getSerializable(KEY_TEMP_FILE); } } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // load image data getActivity().getSupportLoaderManager().initLoader(LOADER_ALL, null, mLoaderCallback); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(requestCode == REQUEST_CAMERA){ if(resultCode == Activity.RESULT_OK) { if (mTmpFile != null) { if (mCallback != null) { mCallback.onCameraShot(mTmpFile); } } }else{ // delete tmp file while (mTmpFile != null && mTmpFile.exists()){ boolean success = mTmpFile.delete(); if(success){ mTmpFile = null; } } } } } @Override public void onConfigurationChanged(Configuration newConfig) { if(mFolderPopupWindow != null){ if(mFolderPopupWindow.isShowing()){ mFolderPopupWindow.dismiss(); } } super.onConfigurationChanged(newConfig); } /** * Open camera */ private void showCameraAction() { if(ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, getString(R.string.mis_permission_rationale_write_storage), REQUEST_STORAGE_WRITE_ACCESS_PERMISSION); }else { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (intent.resolveActivity(getActivity().getPackageManager()) != null) { try { mTmpFile = FileUtils.createTmpFile(getActivity()); } catch (IOException e) { e.printStackTrace(); } if (mTmpFile != null && mTmpFile.exists()) { //安卓7.0适配 Uri uri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { uri = FileProvider.getUriForFile(getActivity(), "cn.flightfeather.thirdapp.fileProvider", mTmpFile); } else { uri = Uri.fromFile(mTmpFile); } intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, REQUEST_CAMERA); } else { Toast.makeText(getActivity(), R.string.mis_error_image_not_exist, Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(getActivity(), R.string.mis_msg_no_camera, Toast.LENGTH_SHORT).show(); } } } private void requestPermission(final String permission, String rationale, final int requestCode){ if(shouldShowRequestPermissionRationale(permission)){ new AlertDialog.Builder(getContext()) .setTitle(R.string.mis_permission_dialog_title) .setMessage(rationale) .setPositiveButton(R.string.mis_permission_dialog_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { requestPermissions(new String[]{permission}, requestCode); } }) .setNegativeButton(R.string.mis_permission_dialog_cancel, null) .create().show(); }else{ requestPermissions(new String[]{permission}, requestCode); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if(requestCode == REQUEST_STORAGE_WRITE_ACCESS_PERMISSION){ if(grantResults[0] == PackageManager.PERMISSION_GRANTED){ showCameraAction(); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } /** * notify callback * @param image image data */ private void selectImageFromGrid(Image image, int mode) { if(image != null) { if(mode == MODE_MULTI) { if (resultList.contains(image.path)) { resultList.remove(image.path); if (mCallback != null) { mCallback.onImageUnselected(image.path); } } else { if(selectImageCount() == resultList.size()){ Toast.makeText(getActivity(), R.string.mis_msg_amount_limit, Toast.LENGTH_SHORT).show(); return; } resultList.add(image.path); if (mCallback != null) { mCallback.onImageSelected(image.path); } } mImageAdapter.select(image); }else if(mode == MODE_SINGLE){ if(mCallback != null){ mCallback.onSingleImageSelected(image.path); } } } } private LoaderManager.LoaderCallbacks mLoaderCallback = new LoaderManager.LoaderCallbacks() { private final String[] IMAGE_PROJECTION = { MediaStore.Images.Media.DATA, MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATE_ADDED, MediaStore.Images.Media.MIME_TYPE, MediaStore.Images.Media.SIZE, MediaStore.Images.Media._ID }; @Override public Loader onCreateLoader(int id, Bundle args) { CursorLoader cursorLoader = null; if(id == LOADER_ALL) { cursorLoader = new CursorLoader(getActivity(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION, IMAGE_PROJECTION[4]+">0 AND "+IMAGE_PROJECTION[3]+"=? OR "+IMAGE_PROJECTION[3]+"=? ", new String[]{"image/jpeg", "image/png"}, IMAGE_PROJECTION[2] + " DESC"); }else if(id == LOADER_CATEGORY){ cursorLoader = new CursorLoader(getActivity(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, IMAGE_PROJECTION, IMAGE_PROJECTION[4]+">0 AND "+IMAGE_PROJECTION[0]+" like '%"+args.getString("path")+"%'", null, IMAGE_PROJECTION[2] + " DESC"); } return cursorLoader; } private boolean fileExist(String path){ if(!TextUtils.isEmpty(path)){ return new File(path).exists(); } return false; } @Override public void onLoadFinished(Loader loader, Cursor data) { if (data != null) { if (data.getCount() > 0) { List images = new ArrayList<>(); data.moveToFirst(); do{ String path = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[0])); String name = data.getString(data.getColumnIndexOrThrow(IMAGE_PROJECTION[1])); long dateTime = data.getLong(data.getColumnIndexOrThrow(IMAGE_PROJECTION[2])); if(!fileExist(path)){continue;} Image image = null; if (!TextUtils.isEmpty(name)) { image = new Image(path, name, dateTime); images.add(image); } if( !hasFolderGened ) { // get all folder data File folderFile = new File(path).getParentFile(); if(folderFile != null && folderFile.exists()){ String fp = folderFile.getAbsolutePath(); Folder f = getFolderByPath(fp); if(f == null){ Folder folder = new Folder(); folder.name = folderFile.getName(); folder.path = fp; folder.cover = image; List imageList = new ArrayList<>(); imageList.add(image); folder.images = imageList; mResultFolder.add(folder); }else { f.images.add(image); } } } }while(data.moveToNext()); mImageAdapter.setData(images); if(resultList != null && resultList.size()>0){ mImageAdapter.setDefaultSelected(resultList); } if(!hasFolderGened) { mFolderAdapter.setData(mResultFolder); hasFolderGened = true; } } } } @Override public void onLoaderReset(Loader loader) { } }; private Folder getFolderByPath(String path){ if(mResultFolder != null){ for (Folder folder : mResultFolder) { if(TextUtils.equals(folder.path, path)){ return folder; } } } return null; } private boolean showCamera(){ return getArguments() == null || getArguments().getBoolean(EXTRA_SHOW_CAMERA, true); } private int selectMode(){ return getArguments() == null ? MODE_MULTI : getArguments().getInt(EXTRA_SELECT_MODE); } private int selectImageCount(){ return getArguments() == null ? 9 : getArguments().getInt(EXTRA_SELECT_COUNT); } /** * Callback for host activity */ public interface Callback{ void onSingleImageSelected(String path); void onImageSelected(String path); void onImageUnselected(String path); void onCameraShot(File imageFile); } }