mFragment;
+
+ private Matisse(Activity activity) {
+ this(activity, null);
+ }
+
+ private Matisse(Fragment fragment) {
+ this(fragment.getActivity(), fragment);
+ }
+
+ private Matisse(Activity activity, Fragment fragment) {
+ mContext = new WeakReference<>(activity);
+ mFragment = new WeakReference<>(fragment);
+ }
+
+ /**
+ * Start Matisse from an Activity.
+ *
+ * This Activity's {@link Activity#onActivityResult(int, int, Intent)} will be called when user
+ * finishes selecting.
+ *
+ * @param activity Activity instance.
+ * @return Matisse instance.
+ */
+ public static Matisse from(Activity activity) {
+ return new Matisse(activity);
+ }
+
+ /**
+ * Start Matisse from a Fragment.
+ *
+ * This Fragment's {@link Fragment#onActivityResult(int, int, Intent)} will be called when user
+ * finishes selecting.
+ *
+ * @param fragment Fragment instance.
+ * @return Matisse instance.
+ */
+ public static Matisse from(Fragment fragment) {
+ return new Matisse(fragment);
+ }
+
+ /**
+ * Obtain user selected media' {@link Uri} list in the starting Activity or Fragment.
+ *
+ * @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or
+ * {@link Fragment#onActivityResult(int, int, Intent)}.
+ * @return User selected media' {@link Uri} list.
+ */
+ public static List obtainResult(Intent data) {
+ return data.getParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION);
+ }
+
+ /**
+ * Obtain user selected media path list in the starting Activity or Fragment.
+ *
+ * @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or
+ * {@link Fragment#onActivityResult(int, int, Intent)}.
+ * @return User selected media path list.
+ */
+ public static List obtainPathResult(Intent data) {
+ return data.getStringArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION_PATH);
+ }
+
+ /**
+ * Obtain state whether user decide to use selected media in original
+ *
+ * @param data Intent passed by {@link Activity#onActivityResult(int, int, Intent)} or
+ * {@link Fragment#onActivityResult(int, int, Intent)}.
+ * @return Whether use original photo
+ */
+ public static boolean obtainOriginalState(Intent data) {
+ return data.getBooleanExtra(MatisseActivity.EXTRA_RESULT_ORIGINAL_ENABLE, false);
+ }
+
+ /**
+ * MIME types the selection constrains on.
+ *
+ * Types not included in the set will still be shown in the grid but can't be chosen.
+ *
+ * @param mimeTypes MIME types set user can choose from.
+ * @return {@link SelectionCreator} to build select specifications.
+ * @see MimeType
+ * @see SelectionCreator
+ */
+ public SelectionCreator choose(Set mimeTypes) {
+ return this.choose(mimeTypes, true);
+ }
+
+ /**
+ * MIME types the selection constrains on.
+ *
+ * Types not included in the set will still be shown in the grid but can't be chosen.
+ *
+ * @param mimeTypes MIME types set user can choose from.
+ * @param mediaTypeExclusive Whether can choose images and videos at the same time during one single choosing
+ * process. true corresponds to not being able to choose images and videos at the same
+ * time, and false corresponds to being able to do this.
+ * @return {@link SelectionCreator} to build select specifications.
+ * @see MimeType
+ * @see SelectionCreator
+ */
+ public SelectionCreator choose(Set mimeTypes, boolean mediaTypeExclusive) {
+ return new SelectionCreator(this, mimeTypes, mediaTypeExclusive);
+ }
+
+ @Nullable
+ Activity getActivity() {
+ return mContext.get();
+ }
+
+ @Nullable
+ Fragment getFragment() {
+ return mFragment != null ? mFragment.get() : null;
+ }
+
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/MimeType.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/MimeType.java
new file mode 100644
index 0000000..edbcced
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/MimeType.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import androidx.collection.ArraySet;
+
+import android.webkit.MimeTypeMap;
+
+import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * MIME Type enumeration to restrict selectable media on the selection activity. Matisse only supports images and
+ * videos.
+ *
+ * Good example of mime types Android supports:
+ * https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/MediaFile.java
+ */
+@SuppressWarnings("unused")
+public enum MimeType {
+
+ // ============== images ==============
+ JPEG("image/jpeg", arraySetOf(
+ "jpg",
+ "jpeg"
+ )),
+ PNG("image/png", arraySetOf(
+ "png"
+ )),
+ GIF("image/gif", arraySetOf(
+ "gif"
+ )),
+ BMP("image/x-ms-bmp", arraySetOf(
+ "bmp"
+ )),
+ WEBP("image/webp", arraySetOf(
+ "webp"
+ )),
+
+ // ============== videos ==============
+ MPEG("video/mpeg", arraySetOf(
+ "mpeg",
+ "mpg"
+ )),
+ MP4("video/mp4", arraySetOf(
+ "mp4",
+ "m4v"
+ )),
+ QUICKTIME("video/quicktime", arraySetOf(
+ "mov"
+ )),
+ THREEGPP("video/3gpp", arraySetOf(
+ "3gp",
+ "3gpp"
+ )),
+ THREEGPP2("video/3gpp2", arraySetOf(
+ "3g2",
+ "3gpp2"
+ )),
+ MKV("video/x-matroska", arraySetOf(
+ "mkv"
+ )),
+ WEBM("video/webm", arraySetOf(
+ "webm"
+ )),
+ TS("video/mp2ts", arraySetOf(
+ "ts"
+ )),
+ AVI("video/avi", arraySetOf(
+ "avi"
+ ));
+
+ private final String mMimeTypeName;
+ private final Set mExtensions;
+
+ MimeType(String mimeTypeName, Set extensions) {
+ mMimeTypeName = mimeTypeName;
+ mExtensions = extensions;
+ }
+
+ public static Set ofAll() {
+ return EnumSet.allOf(MimeType.class);
+ }
+
+ public static Set of(MimeType type, MimeType... rest) {
+ return EnumSet.of(type, rest);
+ }
+
+ public static Set ofImage() {
+ return EnumSet.of(JPEG, PNG, GIF, BMP, WEBP);
+ }
+
+ public static Set ofImage(boolean onlyGif) {
+ return EnumSet.of(GIF);
+ }
+
+ public static Set ofGif() {
+ return ofImage(true);
+ }
+
+ public static Set ofVideo() {
+ return EnumSet.of(MPEG, MP4, QUICKTIME, THREEGPP, THREEGPP2, MKV, WEBM, TS, AVI);
+ }
+
+ public static boolean isImage(String mimeType) {
+ if (mimeType == null) return false;
+ return mimeType.startsWith("image");
+ }
+
+ public static boolean isVideo(String mimeType) {
+ if (mimeType == null) return false;
+ return mimeType.startsWith("video");
+ }
+
+ public static boolean isGif(String mimeType) {
+ if (mimeType == null) return false;
+ return mimeType.equals(MimeType.GIF.toString());
+ }
+
+ private static Set arraySetOf(String... suffixes) {
+ return new ArraySet<>(Arrays.asList(suffixes));
+ }
+
+ @Override
+ public String toString() {
+ return mMimeTypeName;
+ }
+
+ public boolean checkType(ContentResolver resolver, Uri uri) {
+ MimeTypeMap map = MimeTypeMap.getSingleton();
+ if (uri == null) {
+ return false;
+ }
+ String type = map.getExtensionFromMimeType(resolver.getType(uri));
+ String path = null;
+ // lazy load the path and prevent resolve for multiple times
+ boolean pathParsed = false;
+ for (String extension : mExtensions) {
+ if (extension.equals(type)) {
+ return true;
+ }
+ if (!pathParsed) {
+ // we only resolve the path for one time
+ path = PhotoMetadataUtils.getPath(resolver, uri);
+ if (!TextUtils.isEmpty(path)) {
+ path = path.toLowerCase(Locale.US);
+ }
+ pathParsed = true;
+ }
+ if (path != null && path.endsWith(extension)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/SelectionCreator.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/SelectionCreator.java
new file mode 100644
index 0000000..66aeadf
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/SelectionCreator.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Build;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.StyleRes;
+import androidx.fragment.app.Fragment;
+
+import com.zhihu.matisse.engine.ImageEngine;
+import com.zhihu.matisse.filter.Filter;
+import com.zhihu.matisse.internal.entity.CaptureStrategy;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.listener.OnCheckedListener;
+import com.zhihu.matisse.listener.OnSelectedListener;
+import com.zhihu.matisse.ui.MatisseActivity;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Set;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;
+
+/**
+ * Fluent API for building media select specification.
+ */
+@SuppressWarnings("unused")
+public final class SelectionCreator {
+ private final Matisse mMatisse;
+ private final SelectionSpec mSelectionSpec;
+
+ @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
+ @IntDef({
+ SCREEN_ORIENTATION_UNSPECIFIED,
+ SCREEN_ORIENTATION_LANDSCAPE,
+ SCREEN_ORIENTATION_PORTRAIT,
+ SCREEN_ORIENTATION_USER,
+ SCREEN_ORIENTATION_BEHIND,
+ SCREEN_ORIENTATION_SENSOR,
+ SCREEN_ORIENTATION_NOSENSOR,
+ SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
+ SCREEN_ORIENTATION_SENSOR_PORTRAIT,
+ SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
+ SCREEN_ORIENTATION_REVERSE_PORTRAIT,
+ SCREEN_ORIENTATION_FULL_SENSOR,
+ SCREEN_ORIENTATION_USER_LANDSCAPE,
+ SCREEN_ORIENTATION_USER_PORTRAIT,
+ SCREEN_ORIENTATION_FULL_USER,
+ SCREEN_ORIENTATION_LOCKED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ScreenOrientation {
+ }
+
+ /**
+ * Constructs a new specification builder on the context.
+ *
+ * @param matisse a requester context wrapper.
+ * @param mimeTypes MIME type set to select.
+ */
+ SelectionCreator(Matisse matisse, @NonNull Set mimeTypes, boolean mediaTypeExclusive) {
+ mMatisse = matisse;
+ mSelectionSpec = SelectionSpec.getCleanInstance();
+ mSelectionSpec.mimeTypeSet = mimeTypes;
+ mSelectionSpec.mediaTypeExclusive = mediaTypeExclusive;
+ mSelectionSpec.orientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ /**
+ * Whether to show only one media type if choosing medias are only images or videos.
+ *
+ * @param showSingleMediaType whether to show only one media type, either images or videos.
+ * @return {@link SelectionCreator} for fluent API.
+ * @see SelectionSpec#onlyShowImages()
+ * @see SelectionSpec#onlyShowVideos()
+ */
+ public SelectionCreator showSingleMediaType(boolean showSingleMediaType) {
+ mSelectionSpec.showSingleMediaType = showSingleMediaType;
+ return this;
+ }
+
+ /**
+ * Theme for media selecting Activity.
+ *
+ * There are two built-in themes:
+ * 1. com.zhihu.matisse.R.style.Matisse_Zhihu;
+ * 2. com.zhihu.matisse.R.style.Matisse_Dracula
+ * you can define a custom theme derived from the above ones or other themes.
+ *
+ * @param themeId theme resource id. Default value is com.zhihu.matisse.R.style.Matisse_Zhihu.
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator theme(@StyleRes int themeId) {
+ mSelectionSpec.themeId = themeId;
+ return this;
+ }
+
+ /**
+ * Show a auto-increased number or a check mark when user select media.
+ *
+ * @param countable true for a auto-increased number from 1, false for a check mark. Default
+ * value is false.
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator countable(boolean countable) {
+ mSelectionSpec.countable = countable;
+ return this;
+ }
+
+ /**
+ * Maximum selectable count.
+ *
+ * @param maxSelectable Maximum selectable count. Default value is 1.
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator maxSelectable(int maxSelectable) {
+ if (maxSelectable < 1)
+ throw new IllegalArgumentException("maxSelectable must be greater than or equal to one");
+ if (mSelectionSpec.maxImageSelectable > 0 || mSelectionSpec.maxVideoSelectable > 0)
+ throw new IllegalStateException("already set maxImageSelectable and maxVideoSelectable");
+ mSelectionSpec.maxSelectable = maxSelectable;
+ return this;
+ }
+
+ /**
+ * Only useful when {@link SelectionSpec#mediaTypeExclusive} set true and you want to set different maximum
+ * selectable files for image and video media types.
+ *
+ * @param maxImageSelectable Maximum selectable count for image.
+ * @param maxVideoSelectable Maximum selectable count for video.
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator maxSelectablePerMediaType(int maxImageSelectable, int maxVideoSelectable) {
+ if (maxImageSelectable < 1 || maxVideoSelectable < 1)
+ throw new IllegalArgumentException(("max selectable must be greater than or equal to one"));
+ mSelectionSpec.maxSelectable = -1;
+ mSelectionSpec.maxImageSelectable = maxImageSelectable;
+ mSelectionSpec.maxVideoSelectable = maxVideoSelectable;
+ return this;
+ }
+
+ /**
+ * Add filter to filter each selecting item.
+ *
+ * @param filter {@link Filter}
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator addFilter(@NonNull Filter filter) {
+ if (mSelectionSpec.filters == null) {
+ mSelectionSpec.filters = new ArrayList<>();
+ }
+ if (filter == null) throw new IllegalArgumentException("filter cannot be null");
+ mSelectionSpec.filters.add(filter);
+ return this;
+ }
+
+ /**
+ * Determines whether the photo capturing is enabled or not on the media grid view.
+ *
+ * If this value is set true, photo capturing entry will appear only on All Media's page.
+ *
+ * @param enable Whether to enable capturing or not. Default value is false;
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator capture(boolean enable) {
+ mSelectionSpec.capture = enable;
+ return this;
+ }
+
+ /**
+ * Show a original photo check options.Let users decide whether use original photo after select
+ *
+ * @param enable Whether to enable original photo or not
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator originalEnable(boolean enable) {
+ mSelectionSpec.originalable = enable;
+ return this;
+ }
+
+
+ /**
+ * Determines Whether to hide top and bottom toolbar in PreView mode ,when user tap the picture
+ * @param enable
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator autoHideToolbarOnSingleTap(boolean enable) {
+ mSelectionSpec.autoHideToobar = enable;
+ return this;
+ }
+
+ /**
+ * Maximum original size,the unit is MB. Only useful when {link@originalEnable} set true
+ *
+ * @param size Maximum original size. Default value is Integer.MAX_VALUE
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator maxOriginalSize(int size) {
+ mSelectionSpec.originalMaxSize = size;
+ return this;
+ }
+
+ /**
+ * Capture strategy provided for the location to save photos including internal and external
+ * storage and also a authority for {@link androidx.core.content.FileProvider}.
+ *
+ * @param captureStrategy {@link CaptureStrategy}, needed only when capturing is enabled.
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator captureStrategy(CaptureStrategy captureStrategy) {
+ mSelectionSpec.captureStrategy = captureStrategy;
+ return this;
+ }
+
+ /**
+ * Set the desired orientation of this activity.
+ *
+ * @param orientation An orientation constant as used in {@link ScreenOrientation}.
+ * Default value is {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_PORTRAIT}.
+ * @return {@link SelectionCreator} for fluent API.
+ * @see Activity#setRequestedOrientation(int)
+ */
+ public SelectionCreator restrictOrientation(@ScreenOrientation int orientation) {
+ mSelectionSpec.orientation = orientation;
+ return this;
+ }
+
+ /**
+ * Set a fixed span count for the media grid. Same for different screen orientations.
+ *
+ * This will be ignored when {@link #gridExpectedSize(int)} is set.
+ *
+ * @param spanCount Requested span count.
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator spanCount(int spanCount) {
+ if (spanCount < 1) throw new IllegalArgumentException("spanCount cannot be less than 1");
+ mSelectionSpec.spanCount = spanCount;
+ return this;
+ }
+
+ /**
+ * Set expected size for media grid to adapt to different screen sizes. This won't necessarily
+ * be applied cause the media grid should fill the view container. The measured media grid's
+ * size will be as close to this value as possible.
+ *
+ * @param size Expected media grid size in pixel.
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator gridExpectedSize(int size) {
+ mSelectionSpec.gridExpectedSize = size;
+ return this;
+ }
+
+ /**
+ * Photo thumbnail's scale compared to the View's size. It should be a float value in (0.0,
+ * 1.0].
+ *
+ * @param scale Thumbnail's scale in (0.0, 1.0]. Default value is 0.5.
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator thumbnailScale(float scale) {
+ if (scale <= 0f || scale > 1f)
+ throw new IllegalArgumentException("Thumbnail scale must be between (0.0, 1.0]");
+ mSelectionSpec.thumbnailScale = scale;
+ return this;
+ }
+
+ /**
+ * Provide an image engine.
+ *
+ * There are two built-in image engines:
+ * 1. {@link com.zhihu.matisse.engine.impl.GlideEngine}
+ * 2. {@link com.zhihu.matisse.engine.impl.PicassoEngine}
+ * And you can implement your own image engine.
+ *
+ * @param imageEngine {@link ImageEngine}
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator imageEngine(ImageEngine imageEngine) {
+ mSelectionSpec.imageEngine = imageEngine;
+ return this;
+ }
+
+ /**
+ * Set listener for callback immediately when user select or unselect something.
+ *
+ * It's a redundant API with {@link Matisse#obtainResult(Intent)},
+ * we only suggest you to use this API when you need to do something immediately.
+ *
+ * @param listener {@link OnSelectedListener}
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ @NonNull
+ public SelectionCreator setOnSelectedListener(@Nullable OnSelectedListener listener) {
+ mSelectionSpec.onSelectedListener = listener;
+ return this;
+ }
+
+ /**
+ * Set listener for callback immediately when user check or uncheck original.
+ *
+ * @param listener {@link OnSelectedListener}
+ * @return {@link SelectionCreator} for fluent API.
+ */
+ public SelectionCreator setOnCheckedListener(@Nullable OnCheckedListener listener) {
+ mSelectionSpec.onCheckedListener = listener;
+ return this;
+ }
+
+ /**
+ * Start to select media and wait for result.
+ *
+ * @param requestCode Identity of the request Activity or Fragment.
+ */
+ public void forResult(int requestCode) {
+ Activity activity = mMatisse.getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ Intent intent = new Intent(activity, MatisseActivity.class);
+
+ Fragment fragment = mMatisse.getFragment();
+ if (fragment != null) {
+ fragment.startActivityForResult(intent, requestCode);
+ } else {
+ activity.startActivityForResult(intent, requestCode);
+ }
+ }
+
+ public SelectionCreator showPreview(boolean showPreview) {
+ mSelectionSpec.showPreview = showPreview;
+ return this;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/engine/ImageEngine.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/engine/ImageEngine.java
new file mode 100644
index 0000000..84e5c96
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/engine/ImageEngine.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.engine;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.widget.ImageView;
+
+/**
+ * Image loader interface. There are predefined {@link com.zhihu.matisse.engine.impl.GlideEngine}
+ * and {@link com.zhihu.matisse.engine.impl.PicassoEngine}.
+ */
+@SuppressWarnings("unused")
+public interface ImageEngine {
+
+ /**
+ * Load thumbnail of a static image resource.
+ *
+ * @param context Context
+ * @param resize Desired size of the origin image
+ * @param placeholder Placeholder drawable when image is not loaded yet
+ * @param imageView ImageView widget
+ * @param uri Uri of the loaded image
+ */
+ void loadThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri);
+
+ /**
+ * Load thumbnail of a gif image resource. You don't have to load an animated gif when it's only
+ * a thumbnail tile.
+ *
+ * @param context Context
+ * @param resize Desired size of the origin image
+ * @param placeholder Placeholder drawable when image is not loaded yet
+ * @param imageView ImageView widget
+ * @param uri Uri of the loaded image
+ */
+ void loadGifThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri);
+
+ /**
+ * Load a static image resource.
+ *
+ * @param context Context
+ * @param resizeX Desired x-size of the origin image
+ * @param resizeY Desired y-size of the origin image
+ * @param imageView ImageView widget
+ * @param uri Uri of the loaded image
+ */
+ void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri);
+
+ /**
+ * Load a gif image resource.
+ *
+ * @param context Context
+ * @param resizeX Desired x-size of the origin image
+ * @param resizeY Desired y-size of the origin image
+ * @param imageView ImageView widget
+ * @param uri Uri of the loaded image
+ */
+ void loadGifImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri);
+
+ /**
+ * Whether this implementation supports animated gif.
+ * Just knowledge of it, convenient for users.
+ *
+ * @return true support animated gif, false do not support animated gif.
+ */
+ boolean supportAnimatedGif();
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/engine/impl/GlideEngine.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/engine/impl/GlideEngine.java
new file mode 100644
index 0000000..51157ae
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/engine/impl/GlideEngine.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.engine.impl;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.Priority;
+import com.bumptech.glide.request.RequestOptions;
+import com.zhihu.matisse.engine.ImageEngine;
+
+/**
+ * {@link ImageEngine} implementation using Glide.
+ */
+
+public class GlideEngine implements ImageEngine {
+
+ @Override
+ public void loadThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri) {
+ Glide.with(context)
+ .asBitmap() // some .jpeg files are actually gif
+ .load(uri)
+ .apply(new RequestOptions()
+ .override(resize, resize)
+ .placeholder(placeholder)
+ .centerCrop())
+ .into(imageView);
+ }
+
+ @Override
+ public void loadGifThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView,
+ Uri uri) {
+ Glide.with(context)
+ .asBitmap() // some .jpeg files are actually gif
+ .load(uri)
+ .apply(new RequestOptions()
+ .override(resize, resize)
+ .placeholder(placeholder)
+ .centerCrop())
+ .into(imageView);
+ }
+
+ @Override
+ public void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {
+ Glide.with(context)
+ .load(uri)
+ .apply(new RequestOptions()
+ .override(resizeX, resizeY)
+ .priority(Priority.HIGH)
+ .fitCenter())
+ .into(imageView);
+ }
+
+ @Override
+ public void loadGifImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {
+ Glide.with(context)
+ .asGif()
+ .load(uri)
+ .apply(new RequestOptions()
+ .override(resizeX, resizeY)
+ .priority(Priority.HIGH)
+ .fitCenter())
+ .into(imageView);
+ }
+
+ @Override
+ public boolean supportAnimatedGif() {
+ return true;
+ }
+
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/engine/impl/PicassoEngine.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/engine/impl/PicassoEngine.java
new file mode 100644
index 0000000..4ee33ea
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/engine/impl/PicassoEngine.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.engine.impl;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.widget.ImageView;
+
+import com.squareup.picasso.Picasso;
+import com.zhihu.matisse.engine.ImageEngine;
+
+/**
+ * {@link ImageEngine} implementation using Picasso.
+ */
+
+public class PicassoEngine implements ImageEngine {
+
+ @Override
+ public void loadThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView, Uri uri) {
+ Picasso.with(context).load(uri).placeholder(placeholder)
+ .resize(resize, resize)
+ .centerCrop()
+ .into(imageView);
+ }
+
+ @Override
+ public void loadGifThumbnail(Context context, int resize, Drawable placeholder, ImageView imageView,
+ Uri uri) {
+ loadThumbnail(context, resize, placeholder, imageView, uri);
+ }
+
+ @Override
+ public void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {
+ Picasso.with(context).load(uri).resize(resizeX, resizeY).priority(Picasso.Priority.HIGH)
+ .centerInside().into(imageView);
+ }
+
+ @Override
+ public void loadGifImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) {
+ loadImage(context, resizeX, resizeY, imageView, uri);
+ }
+
+ @Override
+ public boolean supportAnimatedGif() {
+ return false;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/filter/Filter.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/filter/Filter.java
new file mode 100644
index 0000000..0942364
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/filter/Filter.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.filter;
+
+import android.content.Context;
+
+import com.zhihu.matisse.MimeType;
+import com.zhihu.matisse.SelectionCreator;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.IncapableCause;
+
+import java.util.Set;
+
+/**
+ * Filter for choosing a {@link Item}. You can add multiple Filters through
+ * {@link SelectionCreator#addFilter(Filter)}.
+ */
+@SuppressWarnings("unused")
+public abstract class Filter {
+ /**
+ * Convenient constant for a minimum value.
+ */
+ public static final int MIN = 0;
+ /**
+ * Convenient constant for a maximum value.
+ */
+ public static final int MAX = Integer.MAX_VALUE;
+ /**
+ * Convenient constant for 1024.
+ */
+ public static final int K = 1024;
+
+ /**
+ * Against what mime types this filter applies.
+ */
+ protected abstract Set constraintTypes();
+
+ /**
+ * Invoked for filtering each item.
+ *
+ * @return null if selectable, {@link IncapableCause} if not selectable.
+ */
+ public abstract IncapableCause filter(Context context, Item item);
+
+ /**
+ * Whether an {@link Item} need filtering.
+ */
+ protected boolean needFiltering(Context context, Item item) {
+ for (MimeType type : constraintTypes()) {
+ if (type.checkType(context.getContentResolver(), item.getContentUri())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/Album.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/Album.java
new file mode 100644
index 0000000..35490e9
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/Album.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.entity;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.Nullable;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.loader.AlbumLoader;
+
+public class Album implements Parcelable {
+ public static final Creator CREATOR = new Creator() {
+ @Nullable
+ @Override
+ public Album createFromParcel(Parcel source) {
+ return new Album(source);
+ }
+
+ @Override
+ public Album[] newArray(int size) {
+ return new Album[size];
+ }
+ };
+ public static final String ALBUM_ID_ALL = String.valueOf(-1);
+ public static final String ALBUM_NAME_ALL = "All";
+
+ private final String mId;
+ private final Uri mCoverUri;
+ private final String mDisplayName;
+ private long mCount;
+
+ public Album(String id, Uri coverUri, String albumName, long count) {
+ mId = id;
+ mCoverUri = coverUri;
+ mDisplayName = albumName;
+ mCount = count;
+ }
+
+ private Album(Parcel source) {
+ mId = source.readString();
+ mCoverUri = source.readParcelable(Uri.class.getClassLoader());
+ mDisplayName = source.readString();
+ mCount = source.readLong();
+ }
+
+ /**
+ * Constructs a new {@link Album} entity from the {@link Cursor}.
+ * This method is not responsible for managing cursor resource, such as close, iterate, and so on.
+ */
+ public static Album valueOf(Cursor cursor) {
+ String clumn = cursor.getString(cursor.getColumnIndex(AlbumLoader.COLUMN_URI));
+ return new Album(
+ cursor.getString(cursor.getColumnIndex("bucket_id")),
+ Uri.parse(clumn != null ? clumn : ""),
+ cursor.getString(cursor.getColumnIndex("bucket_display_name")),
+ cursor.getLong(cursor.getColumnIndex(AlbumLoader.COLUMN_COUNT)));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mId);
+ dest.writeParcelable(mCoverUri, 0);
+ dest.writeString(mDisplayName);
+ dest.writeLong(mCount);
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public Uri getCoverUri() {
+ return mCoverUri;
+ }
+
+ public long getCount() {
+ return mCount;
+ }
+
+ public void addCaptureCount() {
+ mCount++;
+ }
+
+ public String getDisplayName(Context context) {
+ if (isAll()) {
+ return context.getString(R.string.album_name_all);
+ }
+ return mDisplayName;
+ }
+
+ public boolean isAll() {
+ return ALBUM_ID_ALL.equals(mId);
+ }
+
+ public boolean isEmpty() {
+ return mCount == 0;
+ }
+
+}
\ No newline at end of file
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/CaptureStrategy.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/CaptureStrategy.java
new file mode 100644
index 0000000..c4de7ca
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/CaptureStrategy.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.entity;
+
+public class CaptureStrategy {
+
+ public final boolean isPublic;
+ public final String authority;
+ public final String directory;
+
+ public CaptureStrategy(boolean isPublic, String authority) {
+ this(isPublic, authority, null);
+ }
+
+ public CaptureStrategy(boolean isPublic, String authority, String directory) {
+ this.isPublic = isPublic;
+ this.authority = authority;
+ this.directory = directory;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/IncapableCause.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/IncapableCause.java
new file mode 100644
index 0000000..ff5ec46
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/IncapableCause.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.entity;
+
+import android.content.Context;
+import androidx.annotation.IntDef;
+import androidx.fragment.app.FragmentActivity;
+import android.widget.Toast;
+
+import com.zhihu.matisse.internal.ui.widget.IncapableDialog;
+
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+@SuppressWarnings("unused")
+public class IncapableCause {
+ public static final int TOAST = 0x00;
+ public static final int DIALOG = 0x01;
+ public static final int NONE = 0x02;
+
+ @Retention(SOURCE)
+ @IntDef({TOAST, DIALOG, NONE})
+ public @interface Form {
+ }
+
+ private int mForm = TOAST;
+ private String mTitle;
+ private String mMessage;
+
+ public IncapableCause(String message) {
+ mMessage = message;
+ }
+
+ public IncapableCause(String title, String message) {
+ mTitle = title;
+ mMessage = message;
+ }
+
+ public IncapableCause(@Form int form, String message) {
+ mForm = form;
+ mMessage = message;
+ }
+
+ public IncapableCause(@Form int form, String title, String message) {
+ mForm = form;
+ mTitle = title;
+ mMessage = message;
+ }
+
+ public static void handleCause(Context context, IncapableCause cause) {
+ if (cause == null)
+ return;
+
+ switch (cause.mForm) {
+ case NONE:
+ // do nothing.
+ break;
+ case DIALOG:
+ IncapableDialog incapableDialog = IncapableDialog.newInstance(cause.mTitle, cause.mMessage);
+ incapableDialog.show(((FragmentActivity) context).getSupportFragmentManager(),
+ IncapableDialog.class.getName());
+ break;
+ case TOAST:
+ default:
+ Toast.makeText(context, cause.mMessage, Toast.LENGTH_SHORT).show();
+ break;
+ }
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/Item.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/Item.java
new file mode 100644
index 0000000..eda9bb5
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/Item.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.entity;
+
+import android.content.ContentUris;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.MediaStore;
+import androidx.annotation.Nullable;
+
+import com.zhihu.matisse.MimeType;
+
+public class Item implements Parcelable {
+ public static final Creator- CREATOR = new Creator
- () {
+ @Override
+ @Nullable
+ public Item createFromParcel(Parcel source) {
+ return new Item(source);
+ }
+
+ @Override
+ public Item[] newArray(int size) {
+ return new Item[size];
+ }
+ };
+ public static final long ITEM_ID_CAPTURE = -1;
+ public static final String ITEM_DISPLAY_NAME_CAPTURE = "Capture";
+ public final long id;
+ public final String mimeType;
+ public final Uri uri;
+ public final long size;
+ public final long duration; // only for video, in ms
+
+ private Item(long id, String mimeType, long size, long duration) {
+ this.id = id;
+ this.mimeType = mimeType;
+ Uri contentUri;
+ if (isImage()) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if (isVideo()) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else {
+ // ?
+ contentUri = MediaStore.Files.getContentUri("external");
+ }
+ this.uri = ContentUris.withAppendedId(contentUri, id);
+ this.size = size;
+ this.duration = duration;
+ }
+
+ private Item(Parcel source) {
+ id = source.readLong();
+ mimeType = source.readString();
+ uri = source.readParcelable(Uri.class.getClassLoader());
+ size = source.readLong();
+ duration = source.readLong();
+ }
+
+ public static Item valueOf(Cursor cursor) {
+ return new Item(cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID)),
+ cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE)),
+ cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns.SIZE)),
+ cursor.getLong(cursor.getColumnIndex("duration")));
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(id);
+ dest.writeString(mimeType);
+ dest.writeParcelable(uri, 0);
+ dest.writeLong(size);
+ dest.writeLong(duration);
+ }
+
+ public Uri getContentUri() {
+ return uri;
+ }
+
+ public boolean isCapture() {
+ return id == ITEM_ID_CAPTURE;
+ }
+
+ public boolean isImage() {
+ return MimeType.isImage(mimeType);
+ }
+
+ public boolean isGif() {
+ return MimeType.isGif(mimeType);
+ }
+
+ public boolean isVideo() {
+ return MimeType.isVideo(mimeType);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Item)) {
+ return false;
+ }
+
+ Item other = (Item) obj;
+ return id == other.id
+ && (mimeType != null && mimeType.equals(other.mimeType)
+ || (mimeType == null && other.mimeType == null))
+ && (uri != null && uri.equals(other.uri)
+ || (uri == null && other.uri == null))
+ && size == other.size
+ && duration == other.duration;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result = 31 * result + Long.valueOf(id).hashCode();
+ if (mimeType != null) {
+ result = 31 * result + mimeType.hashCode();
+ }
+ result = 31 * result + uri.hashCode();
+ result = 31 * result + Long.valueOf(size).hashCode();
+ result = 31 * result + Long.valueOf(duration).hashCode();
+ return result;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/SelectionSpec.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/SelectionSpec.java
new file mode 100644
index 0000000..80b7d10
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/entity/SelectionSpec.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.entity;
+
+import android.content.pm.ActivityInfo;
+
+import androidx.annotation.StyleRes;
+
+import com.zhihu.matisse.MimeType;
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.engine.ImageEngine;
+import com.zhihu.matisse.engine.impl.GlideEngine;
+import com.zhihu.matisse.filter.Filter;
+import com.zhihu.matisse.listener.OnCheckedListener;
+import com.zhihu.matisse.listener.OnSelectedListener;
+
+import java.util.List;
+import java.util.Set;
+
+public final class SelectionSpec {
+
+ public Set mimeTypeSet;
+ public boolean mediaTypeExclusive;
+ public boolean showSingleMediaType;
+ @StyleRes
+ public int themeId;
+ public int orientation;
+ public boolean countable;
+ public int maxSelectable;
+ public int maxImageSelectable;
+ public int maxVideoSelectable;
+ public List filters;
+ public boolean capture;
+ public CaptureStrategy captureStrategy;
+ public int spanCount;
+ public int gridExpectedSize;
+ public float thumbnailScale;
+ public ImageEngine imageEngine;
+ public boolean hasInited;
+ public OnSelectedListener onSelectedListener;
+ public boolean originalable;
+ public boolean autoHideToobar;
+ public int originalMaxSize;
+ public OnCheckedListener onCheckedListener;
+ public boolean showPreview;
+
+ private SelectionSpec() {
+ }
+
+ public static SelectionSpec getInstance() {
+ return InstanceHolder.INSTANCE;
+ }
+
+ public static SelectionSpec getCleanInstance() {
+ SelectionSpec selectionSpec = getInstance();
+ selectionSpec.reset();
+ return selectionSpec;
+ }
+
+ private void reset() {
+ mimeTypeSet = null;
+ mediaTypeExclusive = true;
+ showSingleMediaType = false;
+ themeId = R.style.Matisse_Zhihu;
+ orientation = 0;
+ countable = false;
+ maxSelectable = 1;
+ maxImageSelectable = 0;
+ maxVideoSelectable = 0;
+ filters = null;
+ capture = false;
+ captureStrategy = null;
+ spanCount = 3;
+ gridExpectedSize = 0;
+ thumbnailScale = 0.5f;
+ imageEngine = new GlideEngine();
+ hasInited = true;
+ originalable = false;
+ autoHideToobar = false;
+ originalMaxSize = Integer.MAX_VALUE;
+ showPreview = true;
+ }
+
+ public boolean singleSelectionModeEnabled() {
+ return !countable && (maxSelectable == 1 || (maxImageSelectable == 1 && maxVideoSelectable == 1));
+ }
+
+ public boolean needOrientationRestriction() {
+ return orientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
+ public boolean onlyShowImages() {
+ return showSingleMediaType && MimeType.ofImage().containsAll(mimeTypeSet);
+ }
+
+ public boolean onlyShowVideos() {
+ return showSingleMediaType && MimeType.ofVideo().containsAll(mimeTypeSet);
+ }
+
+ public boolean onlyShowGif() {
+ return showSingleMediaType && MimeType.ofGif().equals(mimeTypeSet);
+ }
+
+ private static final class InstanceHolder {
+ private static final SelectionSpec INSTANCE = new SelectionSpec();
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/loader/AlbumLoader.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/loader/AlbumLoader.java
new file mode 100644
index 0000000..157a563
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/loader/AlbumLoader.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.loader;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MergeCursor;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.MediaStore;
+
+import androidx.loader.content.CursorLoader;
+
+import com.zhihu.matisse.MimeType;
+import com.zhihu.matisse.internal.entity.Album;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Load all albums (grouped by bucket_id) into a single cursor.
+ */
+public class AlbumLoader extends CursorLoader {
+
+ private static final String COLUMN_BUCKET_ID = "bucket_id";
+ private static final String COLUMN_BUCKET_DISPLAY_NAME = "bucket_display_name";
+ public static final String COLUMN_URI = "uri";
+ public static final String COLUMN_COUNT = "count";
+ private static final Uri QUERY_URI = MediaStore.Files.getContentUri("external");
+
+ private static final String[] COLUMNS = {
+ MediaStore.Files.FileColumns._ID,
+ COLUMN_BUCKET_ID,
+ COLUMN_BUCKET_DISPLAY_NAME,
+ MediaStore.MediaColumns.MIME_TYPE,
+ COLUMN_URI,
+ COLUMN_COUNT};
+
+ private static final String[] PROJECTION = {
+ MediaStore.Files.FileColumns._ID,
+ COLUMN_BUCKET_ID,
+ COLUMN_BUCKET_DISPLAY_NAME,
+ MediaStore.MediaColumns.MIME_TYPE,
+ "COUNT(*) AS " + COLUMN_COUNT};
+
+ private static final String[] PROJECTION_29 = {
+ MediaStore.Files.FileColumns._ID,
+ COLUMN_BUCKET_ID,
+ COLUMN_BUCKET_DISPLAY_NAME,
+ MediaStore.MediaColumns.MIME_TYPE};
+
+ // === params for showSingleMediaType: false ===
+ private static final String SELECTION =
+ "(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " OR "
+ + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ + ") GROUP BY (bucket_id";
+ private static final String SELECTION_29 =
+ "(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " OR "
+ + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0";
+ private static final String[] SELECTION_ARGS = {
+ String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
+ String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
+ };
+ // =============================================
+
+ // === params for showSingleMediaType: true ===
+ private static final String SELECTION_FOR_SINGLE_MEDIA_TYPE =
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ + ") GROUP BY (bucket_id";
+ private static final String SELECTION_FOR_SINGLE_MEDIA_TYPE_29 =
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0";
+
+ private static String[] getSelectionArgsForSingleMediaType(int mediaType) {
+ return new String[]{String.valueOf(mediaType)};
+ }
+ // =============================================
+
+ // === params for showSingleMediaType: true ===
+ private static final String SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE =
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ + " AND " + MediaStore.MediaColumns.MIME_TYPE + "=?"
+ + ") GROUP BY (bucket_id";
+ private static final String SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE_29 =
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ + " AND " + MediaStore.MediaColumns.MIME_TYPE + "=?";
+
+ private static String[] getSelectionArgsForSingleMediaGifType(int mediaType) {
+ return new String[]{String.valueOf(mediaType), "image/gif"};
+ }
+ // =============================================
+
+ private static final String BUCKET_ORDER_BY = "datetaken DESC";
+
+ private AlbumLoader(Context context, String selection, String[] selectionArgs) {
+ super(
+ context,
+ QUERY_URI,
+ beforeAndroidTen() ? PROJECTION : PROJECTION_29,
+ selection,
+ selectionArgs,
+ BUCKET_ORDER_BY
+ );
+ }
+
+ public static CursorLoader newInstance(Context context) {
+ String selection;
+ String[] selectionArgs;
+ if (SelectionSpec.getInstance().onlyShowGif()) {
+ selection = beforeAndroidTen()
+ ? SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE : SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE_29;
+ selectionArgs = getSelectionArgsForSingleMediaGifType(
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
+ } else if (SelectionSpec.getInstance().onlyShowImages()) {
+ selection = beforeAndroidTen()
+ ? SELECTION_FOR_SINGLE_MEDIA_TYPE : SELECTION_FOR_SINGLE_MEDIA_TYPE_29;
+ selectionArgs = getSelectionArgsForSingleMediaType(
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
+ } else if (SelectionSpec.getInstance().onlyShowVideos()) {
+ selection = beforeAndroidTen()
+ ? SELECTION_FOR_SINGLE_MEDIA_TYPE : SELECTION_FOR_SINGLE_MEDIA_TYPE_29;
+ selectionArgs = getSelectionArgsForSingleMediaType(
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);
+ } else {
+ selection = beforeAndroidTen() ? SELECTION : SELECTION_29;
+ selectionArgs = SELECTION_ARGS;
+ }
+ return new AlbumLoader(context, selection, selectionArgs);
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+ Cursor albums = super.loadInBackground();
+ MatrixCursor allAlbum = new MatrixCursor(COLUMNS);
+
+ if (beforeAndroidTen()) {
+ int totalCount = 0;
+ Uri allAlbumCoverUri = null;
+ MatrixCursor otherAlbums = new MatrixCursor(COLUMNS);
+ if (albums != null) {
+ while (albums.moveToNext()) {
+ long fileId = albums.getLong(
+ albums.getColumnIndex(MediaStore.Files.FileColumns._ID));
+ long bucketId = albums.getLong(
+ albums.getColumnIndex(COLUMN_BUCKET_ID));
+ String bucketDisplayName = albums.getString(
+ albums.getColumnIndex(COLUMN_BUCKET_DISPLAY_NAME));
+ String mimeType = albums.getString(
+ albums.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE));
+ Uri uri = getUri(albums);
+ int count = albums.getInt(albums.getColumnIndex(COLUMN_COUNT));
+
+ otherAlbums.addRow(new String[]{
+ Long.toString(fileId),
+ Long.toString(bucketId), bucketDisplayName, mimeType, uri.toString(),
+ String.valueOf(count)});
+ totalCount += count;
+ }
+ if (albums.moveToFirst()) {
+ allAlbumCoverUri = getUri(albums);
+ }
+ }
+
+ allAlbum.addRow(new String[]{
+ Album.ALBUM_ID_ALL, Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, null,
+ allAlbumCoverUri == null ? null : allAlbumCoverUri.toString(),
+ String.valueOf(totalCount)});
+
+ return new MergeCursor(new Cursor[]{allAlbum, otherAlbums});
+ } else {
+ int totalCount = 0;
+ Uri allAlbumCoverUri = null;
+
+ // Pseudo GROUP BY
+ Map countMap = new HashMap<>();
+ if (albums != null) {
+ while (albums.moveToNext()) {
+ long bucketId = albums.getLong(albums.getColumnIndex(COLUMN_BUCKET_ID));
+
+ Long count = countMap.get(bucketId);
+ if (count == null) {
+ count = 1L;
+ } else {
+ count++;
+ }
+ countMap.put(bucketId, count);
+ }
+ }
+
+ MatrixCursor otherAlbums = new MatrixCursor(COLUMNS);
+ if (albums != null) {
+ if (albums.moveToFirst()) {
+ allAlbumCoverUri = getUri(albums);
+
+ Set done = new HashSet<>();
+
+ do {
+ long bucketId = albums.getLong(albums.getColumnIndex(COLUMN_BUCKET_ID));
+
+ if (done.contains(bucketId)) {
+ continue;
+ }
+
+ long fileId = albums.getLong(
+ albums.getColumnIndex(MediaStore.Files.FileColumns._ID));
+ String bucketDisplayName = albums.getString(
+ albums.getColumnIndex(COLUMN_BUCKET_DISPLAY_NAME));
+ String mimeType = albums.getString(
+ albums.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE));
+ Uri uri = getUri(albums);
+ long count = countMap.get(bucketId);
+
+ otherAlbums.addRow(new String[]{
+ Long.toString(fileId),
+ Long.toString(bucketId),
+ bucketDisplayName,
+ mimeType,
+ uri.toString(),
+ String.valueOf(count)});
+ done.add(bucketId);
+
+ totalCount += count;
+ } while (albums.moveToNext());
+ }
+ }
+
+ allAlbum.addRow(new String[]{
+ Album.ALBUM_ID_ALL,
+ Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, null,
+ allAlbumCoverUri == null ? null : allAlbumCoverUri.toString(),
+ String.valueOf(totalCount)});
+
+ return new MergeCursor(new Cursor[]{allAlbum, otherAlbums});
+ }
+ }
+
+ private static Uri getUri(Cursor cursor) {
+ long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns._ID));
+ String mimeType = cursor.getString(
+ cursor.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE));
+ Uri contentUri;
+
+ if (MimeType.isImage(mimeType)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if (MimeType.isVideo(mimeType)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else {
+ // ?
+ contentUri = MediaStore.Files.getContentUri("external");
+ }
+
+ Uri uri = ContentUris.withAppendedId(contentUri, id);
+ return uri;
+ }
+
+ @Override
+ public void onContentChanged() {
+ // FIXME a dirty way to fix loading multiple times
+ }
+
+ /**
+ * @return 是否是 Android 10 (Q) 之前的版本
+ */
+ private static boolean beforeAndroidTen() {
+ return android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q;
+ }
+}
\ No newline at end of file
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/loader/AlbumMediaLoader.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/loader/AlbumMediaLoader.java
new file mode 100644
index 0000000..7bba0d6
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/loader/AlbumMediaLoader.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.loader;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MergeCursor;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import androidx.loader.content.CursorLoader;
+
+import com.zhihu.matisse.internal.entity.Album;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.utils.MediaStoreCompat;
+
+/**
+ * Load images and videos into a single cursor.
+ */
+public class AlbumMediaLoader extends CursorLoader {
+ private static final Uri QUERY_URI = MediaStore.Files.getContentUri("external");
+ private static final String[] PROJECTION = {
+ MediaStore.Files.FileColumns._ID,
+ MediaStore.MediaColumns.DISPLAY_NAME,
+ MediaStore.MediaColumns.MIME_TYPE,
+ MediaStore.MediaColumns.SIZE,
+ "duration"};
+
+ // === params for album ALL && showSingleMediaType: false ===
+ private static final String SELECTION_ALL =
+ "(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " OR "
+ + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0";
+ private static final String[] SELECTION_ALL_ARGS = {
+ String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
+ String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
+ };
+ // ===========================================================
+
+ // === params for album ALL && showSingleMediaType: true ===
+ private static final String SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE =
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0";
+
+ private static String[] getSelectionArgsForSingleMediaType(int mediaType) {
+ return new String[]{String.valueOf(mediaType)};
+ }
+ // =========================================================
+
+ // === params for ordinary album && showSingleMediaType: false ===
+ private static final String SELECTION_ALBUM =
+ "(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " OR "
+ + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
+ + " AND "
+ + " bucket_id=?"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0";
+
+ private static String[] getSelectionAlbumArgs(String albumId) {
+ return new String[]{
+ String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
+ String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
+ albumId
+ };
+ }
+ // ===============================================================
+
+ // === params for ordinary album && showSingleMediaType: true ===
+ private static final String SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE =
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " AND "
+ + " bucket_id=?"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0";
+
+ private static String[] getSelectionAlbumArgsForSingleMediaType(int mediaType, String albumId) {
+ return new String[]{String.valueOf(mediaType), albumId};
+ }
+ // ===============================================================
+
+ // === params for album ALL && showSingleMediaType: true && MineType=="image/gif"
+ private static final String SELECTION_ALL_FOR_GIF =
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " AND "
+ + MediaStore.MediaColumns.MIME_TYPE + "=?"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0";
+
+ private static String[] getSelectionArgsForGifType(int mediaType) {
+ return new String[]{String.valueOf(mediaType), "image/gif"};
+ }
+ // ===============================================================
+
+ // === params for ordinary album && showSingleMediaType: true && MineType=="image/gif" ===
+ private static final String SELECTION_ALBUM_FOR_GIF =
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ + " AND "
+ + " bucket_id=?"
+ + " AND "
+ + MediaStore.MediaColumns.MIME_TYPE + "=?"
+ + " AND " + MediaStore.MediaColumns.SIZE + ">0";
+
+ private static String[] getSelectionAlbumArgsForGifType(int mediaType, String albumId) {
+ return new String[]{String.valueOf(mediaType), albumId, "image/gif"};
+ }
+ // ===============================================================
+
+ private static final String ORDER_BY = MediaStore.Images.Media.DATE_TAKEN + " DESC";
+ private final boolean mEnableCapture;
+
+ private AlbumMediaLoader(Context context, String selection, String[] selectionArgs, boolean capture) {
+ super(context, QUERY_URI, PROJECTION, selection, selectionArgs, ORDER_BY);
+ mEnableCapture = capture;
+ }
+
+ public static CursorLoader newInstance(Context context, Album album, boolean capture) {
+ String selection;
+ String[] selectionArgs;
+ boolean enableCapture;
+
+ if (album.isAll()) {
+ if (SelectionSpec.getInstance().onlyShowGif()) {
+ selection = SELECTION_ALL_FOR_GIF;
+ selectionArgs = getSelectionArgsForGifType(
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
+ } else if (SelectionSpec.getInstance().onlyShowImages()) {
+ selection = SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE;
+ selectionArgs =
+ getSelectionArgsForSingleMediaType(
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
+ } else if (SelectionSpec.getInstance().onlyShowVideos()) {
+ selection = SELECTION_ALL_FOR_SINGLE_MEDIA_TYPE;
+ selectionArgs =
+ getSelectionArgsForSingleMediaType(
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);
+ } else {
+ selection = SELECTION_ALL;
+ selectionArgs = SELECTION_ALL_ARGS;
+ }
+ enableCapture = capture;
+ } else {
+ if (SelectionSpec.getInstance().onlyShowGif()) {
+ selection = SELECTION_ALBUM_FOR_GIF;
+ selectionArgs =
+ getSelectionAlbumArgsForGifType(
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE, album.getId());
+ } else if (SelectionSpec.getInstance().onlyShowImages()) {
+ selection = SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE;
+ selectionArgs =
+ getSelectionAlbumArgsForSingleMediaType(
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE,
+ album.getId());
+ } else if (SelectionSpec.getInstance().onlyShowVideos()) {
+ selection = SELECTION_ALBUM_FOR_SINGLE_MEDIA_TYPE;
+ selectionArgs = getSelectionAlbumArgsForSingleMediaType(
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO,
+ album.getId());
+ } else {
+ selection = SELECTION_ALBUM;
+ selectionArgs = getSelectionAlbumArgs(album.getId());
+ }
+ enableCapture = false;
+ }
+ return new AlbumMediaLoader(context, selection, selectionArgs, enableCapture);
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+ Cursor result = super.loadInBackground();
+ if (!mEnableCapture || !MediaStoreCompat.hasCameraFeature(getContext())) {
+ return result;
+ }
+ MatrixCursor dummy = new MatrixCursor(PROJECTION);
+ dummy.addRow(new Object[]{Item.ITEM_ID_CAPTURE, Item.ITEM_DISPLAY_NAME_CAPTURE, "", 0, 0});
+ return new MergeCursor(new Cursor[]{dummy, result});
+ }
+
+ @Override
+ public void onContentChanged() {
+ // FIXME a dirty way to fix loading multiple times
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/model/AlbumCollection.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/model/AlbumCollection.java
new file mode 100644
index 0000000..ceabef6
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/model/AlbumCollection.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.model;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Bundle;
+import androidx.fragment.app.FragmentActivity;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+
+import com.zhihu.matisse.internal.loader.AlbumLoader;
+
+import java.lang.ref.WeakReference;
+
+public class AlbumCollection implements LoaderManager.LoaderCallbacks {
+ private static final int LOADER_ID = 1;
+ private static final String STATE_CURRENT_SELECTION = "state_current_selection";
+ private WeakReference mContext;
+ private LoaderManager mLoaderManager;
+ private AlbumCallbacks mCallbacks;
+ private int mCurrentSelection;
+ private boolean mLoadFinished;
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ Context context = mContext.get();
+ if (context == null) {
+ return null;
+ }
+ mLoadFinished = false;
+ return AlbumLoader.newInstance(context);
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Cursor data) {
+ Context context = mContext.get();
+ if (context == null) {
+ return;
+ }
+
+ if (!mLoadFinished) {
+ mLoadFinished = true;
+ mCallbacks.onAlbumLoad(data);
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ Context context = mContext.get();
+ if (context == null) {
+ return;
+ }
+
+ mCallbacks.onAlbumReset();
+ }
+
+ public void onCreate(FragmentActivity activity, AlbumCallbacks callbacks) {
+ mContext = new WeakReference(activity);
+ mLoaderManager = activity.getSupportLoaderManager();
+ mCallbacks = callbacks;
+ }
+
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ if (savedInstanceState == null) {
+ return;
+ }
+
+ mCurrentSelection = savedInstanceState.getInt(STATE_CURRENT_SELECTION);
+ }
+
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putInt(STATE_CURRENT_SELECTION, mCurrentSelection);
+ }
+
+ public void onDestroy() {
+ if (mLoaderManager != null) {
+ mLoaderManager.destroyLoader(LOADER_ID);
+ }
+ mCallbacks = null;
+ }
+
+ public void loadAlbums() {
+ mLoaderManager.initLoader(LOADER_ID, null, this);
+ }
+
+ public int getCurrentSelection() {
+ return mCurrentSelection;
+ }
+
+ public void setStateCurrentSelection(int currentSelection) {
+ mCurrentSelection = currentSelection;
+ }
+
+ public interface AlbumCallbacks {
+ void onAlbumLoad(Cursor cursor);
+
+ void onAlbumReset();
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/model/AlbumMediaCollection.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/model/AlbumMediaCollection.java
new file mode 100644
index 0000000..fa25939
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/model/AlbumMediaCollection.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.model;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+import androidx.loader.app.LoaderManager;
+import androidx.loader.content.Loader;
+
+import com.zhihu.matisse.internal.entity.Album;
+import com.zhihu.matisse.internal.loader.AlbumMediaLoader;
+
+import java.lang.ref.WeakReference;
+
+public class AlbumMediaCollection implements LoaderManager.LoaderCallbacks {
+ private static final int LOADER_ID = 2;
+ private static final String ARGS_ALBUM = "args_album";
+ private static final String ARGS_ENABLE_CAPTURE = "args_enable_capture";
+ private WeakReference mContext;
+ private LoaderManager mLoaderManager;
+ private AlbumMediaCallbacks mCallbacks;
+
+ @Override
+ public Loader onCreateLoader(int id, Bundle args) {
+ Context context = mContext.get();
+ if (context == null) {
+ return null;
+ }
+
+ Album album = args.getParcelable(ARGS_ALBUM);
+ if (album == null) {
+ return null;
+ }
+
+ return AlbumMediaLoader.newInstance(context, album,
+ album.isAll() && args.getBoolean(ARGS_ENABLE_CAPTURE, false));
+ }
+
+ @Override
+ public void onLoadFinished(Loader loader, Cursor data) {
+ Context context = mContext.get();
+ if (context == null) {
+ return;
+ }
+
+ mCallbacks.onAlbumMediaLoad(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader) {
+ Context context = mContext.get();
+ if (context == null) {
+ return;
+ }
+
+ mCallbacks.onAlbumMediaReset();
+ }
+
+ public void onCreate(@NonNull FragmentActivity context, @NonNull AlbumMediaCallbacks callbacks) {
+ mContext = new WeakReference(context);
+ mLoaderManager = context.getSupportLoaderManager();
+ mCallbacks = callbacks;
+ }
+
+ public void onDestroy() {
+ if (mLoaderManager != null) {
+ mLoaderManager.destroyLoader(LOADER_ID);
+ }
+ mCallbacks = null;
+ }
+
+ public void load(@Nullable Album target) {
+ load(target, false);
+ }
+
+ public void load(@Nullable Album target, boolean enableCapture) {
+ Bundle args = new Bundle();
+ args.putParcelable(ARGS_ALBUM, target);
+ args.putBoolean(ARGS_ENABLE_CAPTURE, enableCapture);
+ mLoaderManager.initLoader(LOADER_ID, args, this);
+ }
+
+ public interface AlbumMediaCallbacks {
+
+ void onAlbumMediaLoad(Cursor cursor);
+
+ void onAlbumMediaReset();
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/model/SelectedItemCollection.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/model/SelectedItemCollection.java
new file mode 100644
index 0000000..7fdf690
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/model/SelectedItemCollection.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.model;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Bundle;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.entity.IncapableCause;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.ui.widget.CheckView;
+import com.zhihu.matisse.internal.utils.PathUtils;
+import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressWarnings("unused")
+public class SelectedItemCollection {
+
+ public static final String STATE_SELECTION = "state_selection";
+ public static final String STATE_COLLECTION_TYPE = "state_collection_type";
+ /**
+ * Empty collection
+ */
+ public static final int COLLECTION_UNDEFINED = 0x00;
+ /**
+ * Collection only with images
+ */
+ public static final int COLLECTION_IMAGE = 0x01;
+ /**
+ * Collection only with videos
+ */
+ public static final int COLLECTION_VIDEO = 0x01 << 1;
+ /**
+ * Collection with images and videos.
+ */
+ public static final int COLLECTION_MIXED = COLLECTION_IMAGE | COLLECTION_VIDEO;
+ private final Context mContext;
+ private Set
- mItems;
+ private int mCollectionType = COLLECTION_UNDEFINED;
+
+ public SelectedItemCollection(Context context) {
+ mContext = context;
+ }
+
+ public void onCreate(Bundle bundle) {
+ if (bundle == null) {
+ mItems = new LinkedHashSet<>();
+ } else {
+ List
- saved = bundle.getParcelableArrayList(STATE_SELECTION);
+ mItems = new LinkedHashSet<>(saved);
+ mCollectionType = bundle.getInt(STATE_COLLECTION_TYPE, COLLECTION_UNDEFINED);
+ }
+ }
+
+ public void setDefaultSelection(List
- uris) {
+ mItems.addAll(uris);
+ }
+
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putParcelableArrayList(STATE_SELECTION, new ArrayList<>(mItems));
+ outState.putInt(STATE_COLLECTION_TYPE, mCollectionType);
+ }
+
+ public Bundle getDataWithBundle() {
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(STATE_SELECTION, new ArrayList<>(mItems));
+ bundle.putInt(STATE_COLLECTION_TYPE, mCollectionType);
+ return bundle;
+ }
+
+ public boolean add(Item item) {
+ if (typeConflict(item)) {
+ throw new IllegalArgumentException("Can't select images and videos at the same time.");
+ }
+ boolean added = mItems.add(item);
+ if (added) {
+ if (mCollectionType == COLLECTION_UNDEFINED) {
+ if (item.isImage()) {
+ mCollectionType = COLLECTION_IMAGE;
+ } else if (item.isVideo()) {
+ mCollectionType = COLLECTION_VIDEO;
+ }
+ } else if (mCollectionType == COLLECTION_IMAGE) {
+ if (item.isVideo()) {
+ mCollectionType = COLLECTION_MIXED;
+ }
+ } else if (mCollectionType == COLLECTION_VIDEO) {
+ if (item.isImage()) {
+ mCollectionType = COLLECTION_MIXED;
+ }
+ }
+ }
+ return added;
+ }
+
+ public boolean remove(Item item) {
+ boolean removed = mItems.remove(item);
+ if (removed) {
+ if (mItems.size() == 0) {
+ mCollectionType = COLLECTION_UNDEFINED;
+ } else {
+ if (mCollectionType == COLLECTION_MIXED) {
+ refineCollectionType();
+ }
+ }
+ }
+ return removed;
+ }
+
+ public void overwrite(ArrayList
- items, int collectionType) {
+ if (items.size() == 0) {
+ mCollectionType = COLLECTION_UNDEFINED;
+ } else {
+ mCollectionType = collectionType;
+ }
+ mItems.clear();
+ mItems.addAll(items);
+ }
+
+
+ public List
- asList() {
+ return new ArrayList<>(mItems);
+ }
+
+ public List asListOfUri() {
+ List uris = new ArrayList<>();
+ for (Item item : mItems) {
+ uris.add(item.getContentUri());
+ }
+ return uris;
+ }
+
+ public List asListOfString() {
+ List paths = new ArrayList<>();
+ for (Item item : mItems) {
+ paths.add(PathUtils.getPath(mContext, item.getContentUri()));
+ }
+ return paths;
+ }
+
+ public boolean isEmpty() {
+ return mItems == null || mItems.isEmpty();
+ }
+
+ public boolean isSelected(Item item) {
+ return mItems.contains(item);
+ }
+
+ public IncapableCause isAcceptable(Item item) {
+ if (maxSelectableReached()) {
+ int maxSelectable = currentMaxSelectable();
+ String cause;
+
+ try {
+ cause = mContext.getResources().getQuantityString(
+ R.plurals.error_over_count,
+ maxSelectable,
+ maxSelectable
+ );
+ } catch (Resources.NotFoundException e) {
+ cause = mContext.getString(
+ R.string.error_over_count,
+ maxSelectable
+ );
+ } catch (NoClassDefFoundError e) {
+ cause = mContext.getString(
+ R.string.error_over_count,
+ maxSelectable
+ );
+ }
+
+ return new IncapableCause(cause);
+ } else if (typeConflict(item)) {
+ return new IncapableCause(mContext.getString(R.string.error_type_conflict));
+ }
+
+ return PhotoMetadataUtils.isAcceptable(mContext, item);
+ }
+
+ public boolean maxSelectableReached() {
+ return mItems.size() == currentMaxSelectable();
+ }
+
+ // depends
+ private int currentMaxSelectable() {
+ SelectionSpec spec = SelectionSpec.getInstance();
+ if (spec.maxSelectable > 0) {
+ return spec.maxSelectable;
+ } else if (mCollectionType == COLLECTION_IMAGE) {
+ return spec.maxImageSelectable;
+ } else if (mCollectionType == COLLECTION_VIDEO) {
+ return spec.maxVideoSelectable;
+ } else {
+ return spec.maxSelectable;
+ }
+ }
+
+ public int getCollectionType() {
+ return mCollectionType;
+ }
+
+ private void refineCollectionType() {
+ boolean hasImage = false;
+ boolean hasVideo = false;
+ for (Item i : mItems) {
+ if (i.isImage() && !hasImage) hasImage = true;
+ if (i.isVideo() && !hasVideo) hasVideo = true;
+ }
+ if (hasImage && hasVideo) {
+ mCollectionType = COLLECTION_MIXED;
+ } else if (hasImage) {
+ mCollectionType = COLLECTION_IMAGE;
+ } else if (hasVideo) {
+ mCollectionType = COLLECTION_VIDEO;
+ }
+ }
+
+ /**
+ * Determine whether there will be conflict media types. A user can only select images and videos at the same time
+ * while {@link SelectionSpec#mediaTypeExclusive} is set to false.
+ */
+ public boolean typeConflict(Item item) {
+ return SelectionSpec.getInstance().mediaTypeExclusive
+ && ((item.isImage() && (mCollectionType == COLLECTION_VIDEO || mCollectionType == COLLECTION_MIXED))
+ || (item.isVideo() && (mCollectionType == COLLECTION_IMAGE || mCollectionType == COLLECTION_MIXED)));
+ }
+
+ public int count() {
+ return mItems.size();
+ }
+
+ public int checkedNumOf(Item item) {
+ int index = new ArrayList<>(mItems).indexOf(item);
+ return index == -1 ? CheckView.UNCHECKED : index + 1;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/AlbumPreviewActivity.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/AlbumPreviewActivity.java
new file mode 100644
index 0000000..01c82e3
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/AlbumPreviewActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui;
+
+import android.database.Cursor;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+
+import com.zhihu.matisse.internal.entity.Album;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.model.AlbumMediaCollection;
+import com.zhihu.matisse.internal.ui.adapter.PreviewPagerAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AlbumPreviewActivity extends BasePreviewActivity implements
+ AlbumMediaCollection.AlbumMediaCallbacks {
+
+ public static final String EXTRA_ALBUM = "extra_album";
+ public static final String EXTRA_ITEM = "extra_item";
+
+ private AlbumMediaCollection mCollection = new AlbumMediaCollection();
+
+ private boolean mIsAlreadySetPosition;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!SelectionSpec.getInstance().hasInited) {
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+ mCollection.onCreate(this, this);
+ Album album = getIntent().getParcelableExtra(EXTRA_ALBUM);
+ mCollection.load(album);
+
+ Item item = getIntent().getParcelableExtra(EXTRA_ITEM);
+ if (mSpec.countable) {
+ mCheckView.setCheckedNum(mSelectedCollection.checkedNumOf(item));
+ } else {
+ mCheckView.setChecked(mSelectedCollection.isSelected(item));
+ }
+ updateSize(item);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mCollection.onDestroy();
+ }
+
+ @Override
+ public void onAlbumMediaLoad(Cursor cursor) {
+ List
- items = new ArrayList<>();
+ while (cursor.moveToNext()) {
+ items.add(Item.valueOf(cursor));
+ }
+// cursor.close();
+
+ if (items.isEmpty()) {
+ return;
+ }
+
+ PreviewPagerAdapter adapter = (PreviewPagerAdapter) mPager.getAdapter();
+ adapter.addAll(items);
+ adapter.notifyDataSetChanged();
+ if (!mIsAlreadySetPosition) {
+ //onAlbumMediaLoad is called many times..
+ mIsAlreadySetPosition = true;
+ Item selected = getIntent().getParcelableExtra(EXTRA_ITEM);
+ int selectedIndex = items.indexOf(selected);
+ mPager.setCurrentItem(selectedIndex, false);
+ mPreviousPos = selectedIndex;
+ }
+ }
+
+ @Override
+ public void onAlbumMediaReset() {
+
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/BasePreviewActivity.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/BasePreviewActivity.java
new file mode 100644
index 0000000..770dea9
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/BasePreviewActivity.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Color;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.viewpager.widget.ViewPager;
+import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
+import androidx.appcompat.app.AppCompatActivity;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.entity.IncapableCause;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.model.SelectedItemCollection;
+import com.zhihu.matisse.internal.ui.adapter.PreviewPagerAdapter;
+import com.zhihu.matisse.internal.ui.widget.CheckRadioView;
+import com.zhihu.matisse.internal.ui.widget.CheckView;
+import com.zhihu.matisse.internal.ui.widget.IncapableDialog;
+import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
+import com.zhihu.matisse.internal.utils.Platform;
+import com.zhihu.matisse.listener.OnFragmentInteractionListener;
+
+public abstract class BasePreviewActivity extends AppCompatActivity implements View.OnClickListener,
+ ViewPager.OnPageChangeListener, OnFragmentInteractionListener {
+
+ public static final String EXTRA_DEFAULT_BUNDLE = "extra_default_bundle";
+ public static final String EXTRA_RESULT_BUNDLE = "extra_result_bundle";
+ public static final String EXTRA_RESULT_APPLY = "extra_result_apply";
+ public static final String EXTRA_RESULT_ORIGINAL_ENABLE = "extra_result_original_enable";
+ public static final String CHECK_STATE = "checkState";
+
+ protected final SelectedItemCollection mSelectedCollection = new SelectedItemCollection(this);
+ protected SelectionSpec mSpec;
+ protected ViewPager mPager;
+
+ protected PreviewPagerAdapter mAdapter;
+
+ protected CheckView mCheckView;
+ protected TextView mButtonBack;
+ protected TextView mButtonApply;
+ protected TextView mSize;
+
+ protected int mPreviousPos = -1;
+
+ private LinearLayout mOriginalLayout;
+ private CheckRadioView mOriginal;
+ protected boolean mOriginalEnable;
+
+ private FrameLayout mBottomToolbar;
+ private FrameLayout mTopToolbar;
+ private boolean mIsToolbarHide = false;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ setTheme(SelectionSpec.getInstance().themeId);
+ super.onCreate(savedInstanceState);
+ if (!SelectionSpec.getInstance().hasInited) {
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+ setContentView(R.layout.activity_media_preview);
+ if (Platform.hasKitKat()) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ }
+
+ mSpec = SelectionSpec.getInstance();
+ if (mSpec.needOrientationRestriction()) {
+ setRequestedOrientation(mSpec.orientation);
+ }
+
+ if (savedInstanceState == null) {
+ mSelectedCollection.onCreate(getIntent().getBundleExtra(EXTRA_DEFAULT_BUNDLE));
+ mOriginalEnable = getIntent().getBooleanExtra(EXTRA_RESULT_ORIGINAL_ENABLE, false);
+ } else {
+ mSelectedCollection.onCreate(savedInstanceState);
+ mOriginalEnable = savedInstanceState.getBoolean(CHECK_STATE);
+ }
+ mButtonBack = (TextView) findViewById(R.id.button_back);
+ mButtonApply = (TextView) findViewById(R.id.button_apply);
+ mSize = (TextView) findViewById(R.id.size);
+ mButtonBack.setOnClickListener(this);
+ mButtonApply.setOnClickListener(this);
+
+ mPager = (ViewPager) findViewById(R.id.pager);
+ mPager.addOnPageChangeListener(this);
+ mAdapter = new PreviewPagerAdapter(getSupportFragmentManager(), null);
+ mPager.setAdapter(mAdapter);
+ mCheckView = (CheckView) findViewById(R.id.check_view);
+ mCheckView.setCountable(mSpec.countable);
+ mBottomToolbar = findViewById(R.id.bottom_toolbar);
+ mTopToolbar = findViewById(R.id.top_toolbar);
+
+ mCheckView.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Item item = mAdapter.getMediaItem(mPager.getCurrentItem());
+ if (mSelectedCollection.isSelected(item)) {
+ mSelectedCollection.remove(item);
+ if (mSpec.countable) {
+ mCheckView.setCheckedNum(CheckView.UNCHECKED);
+ } else {
+ mCheckView.setChecked(false);
+ }
+ } else {
+ if (assertAddSelection(item)) {
+ mSelectedCollection.add(item);
+ if (mSpec.countable) {
+ mCheckView.setCheckedNum(mSelectedCollection.checkedNumOf(item));
+ } else {
+ mCheckView.setChecked(true);
+ }
+ }
+ }
+ updateApplyButton();
+
+ if (mSpec.onSelectedListener != null) {
+ mSpec.onSelectedListener.onSelected(
+ mSelectedCollection.asListOfUri(), mSelectedCollection.asListOfString());
+ }
+ }
+ });
+
+
+ mOriginalLayout = findViewById(R.id.originalLayout);
+ mOriginal = findViewById(R.id.original);
+ mOriginalLayout.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ int count = countOverMaxSize();
+ if (count > 0) {
+ IncapableDialog incapableDialog = IncapableDialog.newInstance("",
+ getString(R.string.error_over_original_count, count, mSpec.originalMaxSize));
+ incapableDialog.show(getSupportFragmentManager(),
+ IncapableDialog.class.getName());
+ return;
+ }
+
+ mOriginalEnable = !mOriginalEnable;
+ mOriginal.setChecked(mOriginalEnable);
+ if (!mOriginalEnable) {
+ mOriginal.setColor(Color.WHITE);
+ }
+
+
+ if (mSpec.onCheckedListener != null) {
+ mSpec.onCheckedListener.onCheck(mOriginalEnable);
+ }
+ }
+ });
+
+ updateApplyButton();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ mSelectedCollection.onSaveInstanceState(outState);
+ outState.putBoolean("checkState", mOriginalEnable);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onBackPressed() {
+ sendBackResult(false);
+ super.onBackPressed();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.button_back) {
+ onBackPressed();
+ } else if (v.getId() == R.id.button_apply) {
+ sendBackResult(true);
+ finish();
+ }
+ }
+
+ @Override
+ public void onClick() {
+ if (!mSpec.autoHideToobar) {
+ return;
+ }
+
+ if (mIsToolbarHide) {
+ mTopToolbar.animate()
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .translationYBy(mTopToolbar.getMeasuredHeight())
+ .start();
+ mBottomToolbar.animate()
+ .translationYBy(-mBottomToolbar.getMeasuredHeight())
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .start();
+ } else {
+ mTopToolbar.animate()
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .translationYBy(-mTopToolbar.getMeasuredHeight())
+ .start();
+ mBottomToolbar.animate()
+ .setInterpolator(new FastOutSlowInInterpolator())
+ .translationYBy(mBottomToolbar.getMeasuredHeight())
+ .start();
+ }
+
+ mIsToolbarHide = !mIsToolbarHide;
+
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ PreviewPagerAdapter adapter = (PreviewPagerAdapter) mPager.getAdapter();
+ if (mPreviousPos != -1 && mPreviousPos != position) {
+ ((PreviewItemFragment) adapter.instantiateItem(mPager, mPreviousPos)).resetView();
+
+ Item item = adapter.getMediaItem(position);
+ if (mSpec.countable) {
+ int checkedNum = mSelectedCollection.checkedNumOf(item);
+ mCheckView.setCheckedNum(checkedNum);
+ if (checkedNum > 0) {
+ mCheckView.setEnabled(true);
+ } else {
+ mCheckView.setEnabled(!mSelectedCollection.maxSelectableReached());
+ }
+ } else {
+ boolean checked = mSelectedCollection.isSelected(item);
+ mCheckView.setChecked(checked);
+ if (checked) {
+ mCheckView.setEnabled(true);
+ } else {
+ mCheckView.setEnabled(!mSelectedCollection.maxSelectableReached());
+ }
+ }
+ updateSize(item);
+ }
+ mPreviousPos = position;
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+
+ private void updateApplyButton() {
+ int selectedCount = mSelectedCollection.count();
+ if (selectedCount == 0) {
+ mButtonApply.setText(R.string.button_apply_default);
+ mButtonApply.setEnabled(false);
+ } else if (selectedCount == 1 && mSpec.singleSelectionModeEnabled()) {
+ mButtonApply.setText(R.string.button_apply_default);
+ mButtonApply.setEnabled(true);
+ } else {
+ mButtonApply.setEnabled(true);
+ mButtonApply.setText(getString(R.string.button_apply, selectedCount));
+ }
+
+ if (mSpec.originalable) {
+ mOriginalLayout.setVisibility(View.VISIBLE);
+ updateOriginalState();
+ } else {
+ mOriginalLayout.setVisibility(View.GONE);
+ }
+ }
+
+
+ private void updateOriginalState() {
+ mOriginal.setChecked(mOriginalEnable);
+ if (!mOriginalEnable) {
+ mOriginal.setColor(Color.WHITE);
+ }
+
+ if (countOverMaxSize() > 0) {
+
+ if (mOriginalEnable) {
+ IncapableDialog incapableDialog = IncapableDialog.newInstance("",
+ getString(R.string.error_over_original_size, mSpec.originalMaxSize));
+ incapableDialog.show(getSupportFragmentManager(),
+ IncapableDialog.class.getName());
+
+ mOriginal.setChecked(false);
+ mOriginal.setColor(Color.WHITE);
+ mOriginalEnable = false;
+ }
+ }
+ }
+
+
+ private int countOverMaxSize() {
+ int count = 0;
+ int selectedCount = mSelectedCollection.count();
+ for (int i = 0; i < selectedCount; i++) {
+ Item item = mSelectedCollection.asList().get(i);
+ if (item.isImage()) {
+ float size = PhotoMetadataUtils.getSizeInMB(item.size);
+ if (size > mSpec.originalMaxSize) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
+ protected void updateSize(Item item) {
+ if (item.isGif()) {
+ mSize.setVisibility(View.VISIBLE);
+ mSize.setText(PhotoMetadataUtils.getSizeInMB(item.size) + "M");
+ } else {
+ mSize.setVisibility(View.GONE);
+ }
+
+ if (item.isVideo()) {
+ mOriginalLayout.setVisibility(View.GONE);
+ } else if (mSpec.originalable) {
+ mOriginalLayout.setVisibility(View.VISIBLE);
+ }
+ }
+
+ protected void sendBackResult(boolean apply) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_RESULT_BUNDLE, mSelectedCollection.getDataWithBundle());
+ intent.putExtra(EXTRA_RESULT_APPLY, apply);
+ intent.putExtra(EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
+ setResult(Activity.RESULT_OK, intent);
+ }
+
+ private boolean assertAddSelection(Item item) {
+ IncapableCause cause = mSelectedCollection.isAcceptable(item);
+ IncapableCause.handleCause(this, cause);
+ return cause == null;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/MediaSelectionFragment.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/MediaSelectionFragment.java
new file mode 100644
index 0000000..86c46e2
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/MediaSelectionFragment.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.entity.Album;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.model.AlbumMediaCollection;
+import com.zhihu.matisse.internal.model.SelectedItemCollection;
+import com.zhihu.matisse.internal.ui.adapter.AlbumMediaAdapter;
+import com.zhihu.matisse.internal.ui.widget.MediaGridInset;
+import com.zhihu.matisse.internal.utils.UIUtils;
+
+public class MediaSelectionFragment extends Fragment implements
+ AlbumMediaCollection.AlbumMediaCallbacks, AlbumMediaAdapter.CheckStateListener,
+ AlbumMediaAdapter.OnMediaClickListener {
+
+ public static final String EXTRA_ALBUM = "extra_album";
+
+ private final AlbumMediaCollection mAlbumMediaCollection = new AlbumMediaCollection();
+ private RecyclerView mRecyclerView;
+ private AlbumMediaAdapter mAdapter;
+ private SelectionProvider mSelectionProvider;
+ private AlbumMediaAdapter.CheckStateListener mCheckStateListener;
+ private AlbumMediaAdapter.OnMediaClickListener mOnMediaClickListener;
+
+ public static MediaSelectionFragment newInstance(Album album) {
+ MediaSelectionFragment fragment = new MediaSelectionFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(EXTRA_ALBUM, album);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (context instanceof SelectionProvider) {
+ mSelectionProvider = (SelectionProvider) context;
+ } else {
+ throw new IllegalStateException("Context must implement SelectionProvider.");
+ }
+ if (context instanceof AlbumMediaAdapter.CheckStateListener) {
+ mCheckStateListener = (AlbumMediaAdapter.CheckStateListener) context;
+ }
+ if (context instanceof AlbumMediaAdapter.OnMediaClickListener) {
+ mOnMediaClickListener = (AlbumMediaAdapter.OnMediaClickListener) context;
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_media_selection, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
+ }
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ Album album = getArguments().getParcelable(EXTRA_ALBUM);
+
+ mAdapter = new AlbumMediaAdapter(getContext(),
+ mSelectionProvider.provideSelectedItemCollection(), mRecyclerView);
+ mAdapter.registerCheckStateListener(this);
+ mAdapter.registerOnMediaClickListener(this);
+ mRecyclerView.setHasFixedSize(true);
+
+ int spanCount;
+ SelectionSpec selectionSpec = SelectionSpec.getInstance();
+ if (selectionSpec.gridExpectedSize > 0) {
+ spanCount = UIUtils.spanCount(getContext(), selectionSpec.gridExpectedSize);
+ } else {
+ spanCount = selectionSpec.spanCount;
+ }
+ mRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), spanCount));
+
+ int spacing = getResources().getDimensionPixelSize(R.dimen.media_grid_spacing);
+ mRecyclerView.addItemDecoration(new MediaGridInset(spanCount, spacing, false));
+ mRecyclerView.setAdapter(mAdapter);
+ mAlbumMediaCollection.onCreate(getActivity(), this);
+ mAlbumMediaCollection.load(album, selectionSpec.capture);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mAlbumMediaCollection.onDestroy();
+ }
+
+ public void refreshMediaGrid() {
+ mAdapter.notifyDataSetChanged();
+ }
+
+ public void refreshSelection() {
+ mAdapter.refreshSelection();
+ }
+
+ @Override
+ public void onAlbumMediaLoad(Cursor cursor) {
+ mAdapter.swapCursor(cursor);
+ }
+
+ @Override
+ public void onAlbumMediaReset() {
+ mAdapter.swapCursor(null);
+ }
+
+ @Override
+ public void onUpdate() {
+ // notify outer Activity that check state changed
+ if (mCheckStateListener != null) {
+ mCheckStateListener.onUpdate();
+ }
+ }
+
+ @Override
+ public void onMediaClick(Album album, Item item, int adapterPosition) {
+ if (mOnMediaClickListener != null) {
+ mOnMediaClickListener.onMediaClick((Album) getArguments().getParcelable(EXTRA_ALBUM),
+ item, adapterPosition);
+ }
+ }
+
+ public interface SelectionProvider {
+ SelectedItemCollection provideSelectedItemCollection();
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/PreviewItemFragment.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/PreviewItemFragment.java
new file mode 100644
index 0000000..47240b6
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/PreviewItemFragment.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
+import com.zhihu.matisse.listener.OnFragmentInteractionListener;
+
+import it.sephiroth.android.library.imagezoom.ImageViewTouch;
+import it.sephiroth.android.library.imagezoom.ImageViewTouchBase;
+
+public class PreviewItemFragment extends Fragment {
+
+ private static final String ARGS_ITEM = "args_item";
+ private OnFragmentInteractionListener mListener;
+
+ public static PreviewItemFragment newInstance(Item item) {
+ PreviewItemFragment fragment = new PreviewItemFragment();
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(ARGS_ITEM, item);
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.fragment_preview_item, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ final Item item = getArguments().getParcelable(ARGS_ITEM);
+ if (item == null) {
+ return;
+ }
+
+ View videoPlayButton = view.findViewById(R.id.video_play_button);
+ if (item.isVideo()) {
+ videoPlayButton.setVisibility(View.VISIBLE);
+ videoPlayButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setDataAndType(item.uri, "video/*");
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(getContext(), R.string.error_no_video_activity, Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ } else {
+ videoPlayButton.setVisibility(View.GONE);
+ }
+
+ ImageViewTouch image = (ImageViewTouch) view.findViewById(R.id.image_view);
+ image.setDisplayType(ImageViewTouchBase.DisplayType.FIT_TO_SCREEN);
+
+ image.setSingleTapListener(new ImageViewTouch.OnImageViewTouchSingleTapListener() {
+ @Override
+ public void onSingleTapConfirmed() {
+ if (mListener != null) {
+ mListener.onClick();
+ }
+ }
+ });
+
+ Point size = PhotoMetadataUtils.getBitmapSize(item.getContentUri(), getActivity());
+ if (item.isGif()) {
+ SelectionSpec.getInstance().imageEngine.loadGifImage(getContext(), size.x, size.y, image,
+ item.getContentUri());
+ } else {
+ SelectionSpec.getInstance().imageEngine.loadImage(getContext(), size.x, size.y, image,
+ item.getContentUri());
+ }
+ }
+
+ public void resetView() {
+ if (getView() != null) {
+ ((ImageViewTouch) getView().findViewById(R.id.image_view)).resetMatrix();
+ }
+ }
+
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (context instanceof OnFragmentInteractionListener) {
+ mListener = (OnFragmentInteractionListener) context;
+ } else {
+ throw new RuntimeException(context.toString()
+ + " must implement OnFragmentInteractionListener");
+ }
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mListener = null;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/SelectedPreviewActivity.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/SelectedPreviewActivity.java
new file mode 100644
index 0000000..8b934c7
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/SelectedPreviewActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui;
+
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.model.SelectedItemCollection;
+
+import java.util.List;
+
+public class SelectedPreviewActivity extends BasePreviewActivity {
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!SelectionSpec.getInstance().hasInited) {
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ Bundle bundle = getIntent().getBundleExtra(EXTRA_DEFAULT_BUNDLE);
+ List
- selected = bundle.getParcelableArrayList(SelectedItemCollection.STATE_SELECTION);
+ mAdapter.addAll(selected);
+ mAdapter.notifyDataSetChanged();
+ if (mSpec.countable) {
+ mCheckView.setCheckedNum(1);
+ } else {
+ mCheckView.setChecked(true);
+ }
+ mPreviousPos = 0;
+ updateSize(selected.get(0));
+ }
+
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/AlbumMediaAdapter.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/AlbumMediaAdapter.java
new file mode 100644
index 0000000..f0eb971
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/AlbumMediaAdapter.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.adapter;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.entity.Album;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.entity.IncapableCause;
+import com.zhihu.matisse.internal.model.SelectedItemCollection;
+import com.zhihu.matisse.internal.ui.widget.CheckView;
+import com.zhihu.matisse.internal.ui.widget.MediaGrid;
+
+public class AlbumMediaAdapter extends
+ RecyclerViewCursorAdapter implements
+ MediaGrid.OnMediaGridClickListener {
+
+ private static final int VIEW_TYPE_CAPTURE = 0x01;
+ private static final int VIEW_TYPE_MEDIA = 0x02;
+ private final SelectedItemCollection mSelectedCollection;
+ private final Drawable mPlaceholder;
+ private SelectionSpec mSelectionSpec;
+ private CheckStateListener mCheckStateListener;
+ private OnMediaClickListener mOnMediaClickListener;
+ private RecyclerView mRecyclerView;
+ private int mImageResize;
+
+ public AlbumMediaAdapter(Context context, SelectedItemCollection selectedCollection, RecyclerView recyclerView) {
+ super(null);
+ mSelectionSpec = SelectionSpec.getInstance();
+ mSelectedCollection = selectedCollection;
+
+ TypedArray ta = context.getTheme().obtainStyledAttributes(new int[]{R.attr.item_placeholder});
+ mPlaceholder = ta.getDrawable(0);
+ ta.recycle();
+
+ mRecyclerView = recyclerView;
+ }
+
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if (viewType == VIEW_TYPE_CAPTURE) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.photo_capture_item, parent, false);
+ CaptureViewHolder holder = new CaptureViewHolder(v);
+ holder.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (v.getContext() instanceof OnPhotoCapture) {
+ ((OnPhotoCapture) v.getContext()).capture();
+ }
+ }
+ });
+ return holder;
+ } else if (viewType == VIEW_TYPE_MEDIA) {
+ View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.media_grid_item, parent, false);
+ return new MediaViewHolder(v);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onBindViewHolder(final RecyclerView.ViewHolder holder, Cursor cursor) {
+ if (holder instanceof CaptureViewHolder) {
+ CaptureViewHolder captureViewHolder = (CaptureViewHolder) holder;
+ Drawable[] drawables = captureViewHolder.mHint.getCompoundDrawables();
+ TypedArray ta = holder.itemView.getContext().getTheme().obtainStyledAttributes(
+ new int[]{R.attr.capture_textColor});
+ int color = ta.getColor(0, 0);
+ ta.recycle();
+
+ for (int i = 0; i < drawables.length; i++) {
+ Drawable drawable = drawables[i];
+ if (drawable != null) {
+ final Drawable.ConstantState state = drawable.getConstantState();
+ if (state == null) {
+ continue;
+ }
+
+ Drawable newDrawable = state.newDrawable().mutate();
+ newDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ newDrawable.setBounds(drawable.getBounds());
+ drawables[i] = newDrawable;
+ }
+ }
+ captureViewHolder.mHint.setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3]);
+ } else if (holder instanceof MediaViewHolder) {
+ MediaViewHolder mediaViewHolder = (MediaViewHolder) holder;
+
+ final Item item = Item.valueOf(cursor);
+ mediaViewHolder.mMediaGrid.preBindMedia(new MediaGrid.PreBindInfo(
+ getImageResize(mediaViewHolder.mMediaGrid.getContext()),
+ mPlaceholder,
+ mSelectionSpec.countable,
+ holder
+ ));
+ mediaViewHolder.mMediaGrid.bindMedia(item);
+ mediaViewHolder.mMediaGrid.setOnMediaGridClickListener(this);
+ setCheckStatus(item, mediaViewHolder.mMediaGrid);
+ }
+ }
+
+ private void setCheckStatus(Item item, MediaGrid mediaGrid) {
+ if (mSelectionSpec.countable) {
+ int checkedNum = mSelectedCollection.checkedNumOf(item);
+ if (checkedNum > 0) {
+ mediaGrid.setCheckEnabled(true);
+ mediaGrid.setCheckedNum(checkedNum);
+ } else {
+ if (mSelectedCollection.maxSelectableReached()) {
+ mediaGrid.setCheckEnabled(false);
+ mediaGrid.setCheckedNum(CheckView.UNCHECKED);
+ } else {
+ mediaGrid.setCheckEnabled(true);
+ mediaGrid.setCheckedNum(checkedNum);
+ }
+ }
+ } else {
+ boolean selected = mSelectedCollection.isSelected(item);
+ if (selected) {
+ mediaGrid.setCheckEnabled(true);
+ mediaGrid.setChecked(true);
+ } else {
+ if (mSelectedCollection.maxSelectableReached()) {
+ mediaGrid.setCheckEnabled(false);
+ mediaGrid.setChecked(false);
+ } else {
+ mediaGrid.setCheckEnabled(true);
+ mediaGrid.setChecked(false);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onThumbnailClicked(ImageView thumbnail, Item item, RecyclerView.ViewHolder holder) {
+ if (mSelectionSpec.showPreview) {
+ if (mOnMediaClickListener != null) {
+ mOnMediaClickListener.onMediaClick(null, item, holder.getAdapterPosition());
+ }
+ } else {
+ updateSelectedItem(item, holder);
+ }
+ }
+
+ @Override
+ public void onCheckViewClicked(CheckView checkView, Item item, RecyclerView.ViewHolder holder) {
+ updateSelectedItem(item, holder);
+ }
+
+ private void updateSelectedItem(Item item, RecyclerView.ViewHolder holder) {
+ if (mSelectionSpec.countable) {
+ int checkedNum = mSelectedCollection.checkedNumOf(item);
+ if (checkedNum == CheckView.UNCHECKED) {
+ if (assertAddSelection(holder.itemView.getContext(), item)) {
+ mSelectedCollection.add(item);
+ notifyCheckStateChanged();
+ }
+ } else {
+ mSelectedCollection.remove(item);
+ notifyCheckStateChanged();
+ }
+ } else {
+ if (mSelectedCollection.isSelected(item)) {
+ mSelectedCollection.remove(item);
+ notifyCheckStateChanged();
+ } else {
+ if (assertAddSelection(holder.itemView.getContext(), item)) {
+ mSelectedCollection.add(item);
+ notifyCheckStateChanged();
+ }
+ }
+ }
+ }
+
+ private void notifyCheckStateChanged() {
+ notifyDataSetChanged();
+ if (mCheckStateListener != null) {
+ mCheckStateListener.onUpdate();
+ }
+ }
+
+ @Override
+ public int getItemViewType(int position, Cursor cursor) {
+ return Item.valueOf(cursor).isCapture() ? VIEW_TYPE_CAPTURE : VIEW_TYPE_MEDIA;
+ }
+
+ private boolean assertAddSelection(Context context, Item item) {
+ IncapableCause cause = mSelectedCollection.isAcceptable(item);
+ IncapableCause.handleCause(context, cause);
+ return cause == null;
+ }
+
+
+ public void registerCheckStateListener(CheckStateListener listener) {
+ mCheckStateListener = listener;
+ }
+
+ public void unregisterCheckStateListener() {
+ mCheckStateListener = null;
+ }
+
+ public void registerOnMediaClickListener(OnMediaClickListener listener) {
+ mOnMediaClickListener = listener;
+ }
+
+ public void unregisterOnMediaClickListener() {
+ mOnMediaClickListener = null;
+ }
+
+ public void refreshSelection() {
+ GridLayoutManager layoutManager = (GridLayoutManager) mRecyclerView.getLayoutManager();
+ int first = layoutManager.findFirstVisibleItemPosition();
+ int last = layoutManager.findLastVisibleItemPosition();
+ if (first == -1 || last == -1) {
+ return;
+ }
+ Cursor cursor = getCursor();
+ for (int i = first; i <= last; i++) {
+ RecyclerView.ViewHolder holder = mRecyclerView.findViewHolderForAdapterPosition(first);
+ if (holder instanceof MediaViewHolder) {
+ if (cursor.moveToPosition(i)) {
+ setCheckStatus(Item.valueOf(cursor), ((MediaViewHolder) holder).mMediaGrid);
+ }
+ }
+ }
+ }
+
+ private int getImageResize(Context context) {
+ if (mImageResize == 0) {
+ RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
+ int spanCount = ((GridLayoutManager) lm).getSpanCount();
+ int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+ int availableWidth = screenWidth - context.getResources().getDimensionPixelSize(
+ R.dimen.media_grid_spacing) * (spanCount - 1);
+ mImageResize = availableWidth / spanCount;
+ mImageResize = (int) (mImageResize * mSelectionSpec.thumbnailScale);
+ }
+ return mImageResize;
+ }
+
+ public interface CheckStateListener {
+ void onUpdate();
+ }
+
+ public interface OnMediaClickListener {
+ void onMediaClick(Album album, Item item, int adapterPosition);
+ }
+
+ public interface OnPhotoCapture {
+ void capture();
+ }
+
+ private static class MediaViewHolder extends RecyclerView.ViewHolder {
+
+ private MediaGrid mMediaGrid;
+
+ MediaViewHolder(View itemView) {
+ super(itemView);
+ mMediaGrid = (MediaGrid) itemView;
+ }
+ }
+
+ private static class CaptureViewHolder extends RecyclerView.ViewHolder {
+
+ private TextView mHint;
+
+ CaptureViewHolder(View itemView) {
+ super(itemView);
+
+ mHint = (TextView) itemView.findViewById(R.id.hint);
+ }
+ }
+
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/AlbumsAdapter.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/AlbumsAdapter.java
new file mode 100644
index 0000000..8e809c9
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/AlbumsAdapter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.adapter;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.entity.Album;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+
+public class AlbumsAdapter extends CursorAdapter {
+
+ private final Drawable mPlaceholder;
+
+ public AlbumsAdapter(Context context, Cursor c, boolean autoRequery) {
+ super(context, c, autoRequery);
+
+ TypedArray ta = context.getTheme().obtainStyledAttributes(
+ new int[]{R.attr.album_thumbnail_placeholder});
+ mPlaceholder = ta.getDrawable(0);
+ ta.recycle();
+ }
+
+ public AlbumsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ TypedArray ta = context.getTheme().obtainStyledAttributes(
+ new int[]{R.attr.album_thumbnail_placeholder});
+ mPlaceholder = ta.getDrawable(0);
+ ta.recycle();
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return LayoutInflater.from(context).inflate(R.layout.album_list_item, parent, false);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ Album album = Album.valueOf(cursor);
+ ((TextView) view.findViewById(R.id.album_name)).setText(album.getDisplayName(context));
+ ((TextView) view.findViewById(R.id.album_media_count)).setText(String.valueOf(album.getCount()));
+
+ // do not need to load animated Gif
+ SelectionSpec.getInstance().imageEngine.loadThumbnail(context, context.getResources().getDimensionPixelSize(R
+ .dimen.media_grid_size), mPlaceholder,
+ (ImageView) view.findViewById(R.id.album_cover), album.getCoverUri());
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/PreviewPagerAdapter.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/PreviewPagerAdapter.java
new file mode 100644
index 0000000..b75abc1
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/PreviewPagerAdapter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.adapter;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import android.view.ViewGroup;
+
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.ui.PreviewItemFragment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PreviewPagerAdapter extends FragmentPagerAdapter {
+
+ private ArrayList
- mItems = new ArrayList<>();
+ private OnPrimaryItemSetListener mListener;
+
+ public PreviewPagerAdapter(FragmentManager manager, OnPrimaryItemSetListener listener) {
+ super(manager);
+ mListener = listener;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ return PreviewItemFragment.newInstance(mItems.get(position));
+ }
+
+ @Override
+ public int getCount() {
+ return mItems.size();
+ }
+
+ @Override
+ public void setPrimaryItem(ViewGroup container, int position, Object object) {
+ super.setPrimaryItem(container, position, object);
+ if (mListener != null) {
+ mListener.onPrimaryItemSet(position);
+ }
+ }
+
+ public Item getMediaItem(int position) {
+ return mItems.get(position);
+ }
+
+ public void addAll(List
- items) {
+ mItems.addAll(items);
+ }
+
+ interface OnPrimaryItemSetListener {
+
+ void onPrimaryItemSet(int position);
+ }
+
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/RecyclerViewCursorAdapter.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/RecyclerViewCursorAdapter.java
new file mode 100644
index 0000000..48203b0
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/RecyclerViewCursorAdapter.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.adapter;
+
+import android.database.Cursor;
+import android.provider.MediaStore;
+import androidx.recyclerview.widget.RecyclerView;
+
+public abstract class RecyclerViewCursorAdapter extends
+ RecyclerView.Adapter {
+
+ private Cursor mCursor;
+ private int mRowIDColumn;
+
+ RecyclerViewCursorAdapter(Cursor c) {
+ setHasStableIds(true);
+ swapCursor(c);
+ }
+
+ protected abstract void onBindViewHolder(VH holder, Cursor cursor);
+
+ @Override
+ public void onBindViewHolder(VH holder, int position) {
+ if (!isDataValid(mCursor)) {
+ throw new IllegalStateException("Cannot bind view holder when cursor is in invalid state.");
+ }
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("Could not move cursor to position " + position
+ + " when trying to bind view holder");
+ }
+
+ onBindViewHolder(holder, mCursor);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("Could not move cursor to position " + position
+ + " when trying to get item view type.");
+ }
+ return getItemViewType(position, mCursor);
+ }
+
+ protected abstract int getItemViewType(int position, Cursor cursor);
+
+ @Override
+ public int getItemCount() {
+ if (isDataValid(mCursor)) {
+ return mCursor.getCount();
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (!isDataValid(mCursor)) {
+ throw new IllegalStateException("Cannot lookup item id when cursor is in invalid state.");
+ }
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("Could not move cursor to position " + position
+ + " when trying to get an item id");
+ }
+
+ return mCursor.getLong(mRowIDColumn);
+ }
+
+ public void swapCursor(Cursor newCursor) {
+ if (newCursor == mCursor) {
+ return;
+ }
+
+ if (newCursor != null) {
+ mCursor = newCursor;
+ mRowIDColumn = mCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID);
+ // notify the observers about the new cursor
+ notifyDataSetChanged();
+ } else {
+ notifyItemRangeRemoved(0, getItemCount());
+ mCursor = null;
+ mRowIDColumn = -1;
+ }
+ }
+
+ public Cursor getCursor() {
+ return mCursor;
+ }
+
+ private boolean isDataValid(Cursor cursor) {
+ return cursor != null && !cursor.isClosed();
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/AlbumsSpinner.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/AlbumsSpinner.java
new file mode 100644
index 0000000..92dfa27
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/AlbumsSpinner.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.ListPopupWindow;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.CursorAdapter;
+import android.widget.TextView;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.entity.Album;
+import com.zhihu.matisse.internal.utils.Platform;
+
+public class AlbumsSpinner {
+
+ private static final int MAX_SHOWN_COUNT = 6;
+ private CursorAdapter mAdapter;
+ private TextView mSelected;
+ private ListPopupWindow mListPopupWindow;
+ private AdapterView.OnItemSelectedListener mOnItemSelectedListener;
+
+ public AlbumsSpinner(@NonNull Context context) {
+ mListPopupWindow = new ListPopupWindow(context, null, R.attr.listPopupWindowStyle);
+ mListPopupWindow.setModal(true);
+ float density = context.getResources().getDisplayMetrics().density;
+ mListPopupWindow.setContentWidth((int) (216 * density));
+ mListPopupWindow.setHorizontalOffset((int) (16 * density));
+ mListPopupWindow.setVerticalOffset((int) (-48 * density));
+
+ mListPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ AlbumsSpinner.this.onItemSelected(parent.getContext(), position);
+ if (mOnItemSelectedListener != null) {
+ mOnItemSelectedListener.onItemSelected(parent, view, position, id);
+ }
+ }
+ });
+ }
+
+ public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
+ mOnItemSelectedListener = listener;
+ }
+
+ public void setSelection(Context context, int position) {
+ mListPopupWindow.setSelection(position);
+ onItemSelected(context, position);
+ }
+
+ private void onItemSelected(Context context, int position) {
+ mListPopupWindow.dismiss();
+ Cursor cursor = mAdapter.getCursor();
+ cursor.moveToPosition(position);
+ Album album = Album.valueOf(cursor);
+ String displayName = album.getDisplayName(context);
+ if (mSelected.getVisibility() == View.VISIBLE) {
+ mSelected.setText(displayName);
+ } else {
+ if (Platform.hasICS()) {
+ mSelected.setAlpha(0.0f);
+ mSelected.setVisibility(View.VISIBLE);
+ mSelected.setText(displayName);
+ mSelected.animate().alpha(1.0f).setDuration(context.getResources().getInteger(
+ android.R.integer.config_longAnimTime)).start();
+ } else {
+ mSelected.setVisibility(View.VISIBLE);
+ mSelected.setText(displayName);
+ }
+
+ }
+ }
+
+ public void setAdapter(CursorAdapter adapter) {
+ mListPopupWindow.setAdapter(adapter);
+ mAdapter = adapter;
+ }
+
+ public void setSelectedTextView(TextView textView) {
+ mSelected = textView;
+ // tint dropdown arrow icon
+ Drawable[] drawables = mSelected.getCompoundDrawables();
+ Drawable right = drawables[2];
+ TypedArray ta = mSelected.getContext().getTheme().obtainStyledAttributes(
+ new int[]{R.attr.album_element_color});
+ int color = ta.getColor(0, 0);
+ ta.recycle();
+ right.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+
+ mSelected.setVisibility(View.GONE);
+ mSelected.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ int itemHeight = v.getResources().getDimensionPixelSize(R.dimen.album_item_height);
+ mListPopupWindow.setHeight(
+ mAdapter.getCount() > MAX_SHOWN_COUNT ? itemHeight * MAX_SHOWN_COUNT
+ : itemHeight * mAdapter.getCount());
+ mListPopupWindow.show();
+ }
+ });
+ mSelected.setOnTouchListener(mListPopupWindow.createDragToOpenListener(mSelected));
+ }
+
+ public void setPopupAnchorView(View view) {
+ mListPopupWindow.setAnchorView(view);
+ }
+
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckRadioView.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckRadioView.java
new file mode 100644
index 0000000..1d71b72
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckRadioView.java
@@ -0,0 +1,60 @@
+package com.zhihu.matisse.internal.ui.widget;
+
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import androidx.core.content.res.ResourcesCompat;
+import androidx.appcompat.widget.AppCompatImageView;
+import android.util.AttributeSet;
+
+import com.zhihu.matisse.R;
+
+public class CheckRadioView extends AppCompatImageView {
+
+ private Drawable mDrawable;
+
+ private int mSelectedColor;
+ private int mUnSelectUdColor;
+
+ public CheckRadioView(Context context) {
+ super(context);
+ init();
+ }
+
+
+
+ public CheckRadioView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ mSelectedColor = ResourcesCompat.getColor(
+ getResources(), R.color.zhihu_item_checkCircle_backgroundColor,
+ getContext().getTheme());
+ mUnSelectUdColor = ResourcesCompat.getColor(
+ getResources(), R.color.zhihu_check_original_radio_disable,
+ getContext().getTheme());
+ setChecked(false);
+ }
+
+ public void setChecked(boolean enable) {
+ if (enable) {
+ setImageResource(R.drawable.ic_preview_radio_on);
+ mDrawable = getDrawable();
+ mDrawable.setColorFilter(mSelectedColor, PorterDuff.Mode.SRC_IN);
+ } else {
+ setImageResource(R.drawable.ic_preview_radio_off);
+ mDrawable = getDrawable();
+ mDrawable.setColorFilter(mUnSelectUdColor, PorterDuff.Mode.SRC_IN);
+ }
+ }
+
+
+ public void setColor(int color) {
+ if (mDrawable == null) {
+ mDrawable = getDrawable();
+ }
+ mDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckView.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckView.java
new file mode 100644
index 0000000..7407325
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/CheckView.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import androidx.core.content.res.ResourcesCompat;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.zhihu.matisse.R;
+
+public class CheckView extends View {
+
+ public static final int UNCHECKED = Integer.MIN_VALUE;
+ private static final float STROKE_WIDTH = 3.0f; // dp
+ private static final float SHADOW_WIDTH = 6.0f; // dp
+ private static final int SIZE = 48; // dp
+ private static final float STROKE_RADIUS = 11.5f; // dp
+ private static final float BG_RADIUS = 11.0f; // dp
+ private static final int CONTENT_SIZE = 16; // dp
+ private boolean mCountable;
+ private boolean mChecked;
+ private int mCheckedNum;
+ private Paint mStrokePaint;
+ private Paint mBackgroundPaint;
+ private TextPaint mTextPaint;
+ private Paint mShadowPaint;
+ private Drawable mCheckDrawable;
+ private float mDensity;
+ private Rect mCheckRect;
+ private boolean mEnabled = true;
+
+ public CheckView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public CheckView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public CheckView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // fixed size 48dp x 48dp
+ int sizeSpec = MeasureSpec.makeMeasureSpec((int) (SIZE * mDensity), MeasureSpec.EXACTLY);
+ super.onMeasure(sizeSpec, sizeSpec);
+ }
+
+ private void init(Context context) {
+ mDensity = context.getResources().getDisplayMetrics().density;
+
+ mStrokePaint = new Paint();
+ mStrokePaint.setAntiAlias(true);
+ mStrokePaint.setStyle(Paint.Style.STROKE);
+ mStrokePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
+ mStrokePaint.setStrokeWidth(STROKE_WIDTH * mDensity);
+ TypedArray ta = getContext().getTheme().obtainStyledAttributes(new int[]{R.attr.item_checkCircle_borderColor});
+ int defaultColor = ResourcesCompat.getColor(
+ getResources(), R.color.zhihu_item_checkCircle_borderColor,
+ getContext().getTheme());
+ int color = ta.getColor(0, defaultColor);
+ ta.recycle();
+ mStrokePaint.setColor(color);
+
+ mCheckDrawable = ResourcesCompat.getDrawable(context.getResources(),
+ R.drawable.ic_check_white_18dp, context.getTheme());
+ }
+
+ public void setChecked(boolean checked) {
+ if (mCountable) {
+ throw new IllegalStateException("CheckView is countable, call setCheckedNum() instead.");
+ }
+ mChecked = checked;
+ invalidate();
+ }
+
+ public void setCountable(boolean countable) {
+ mCountable = countable;
+ }
+
+ public void setCheckedNum(int checkedNum) {
+ if (!mCountable) {
+ throw new IllegalStateException("CheckView is not countable, call setChecked() instead.");
+ }
+ if (checkedNum != UNCHECKED && checkedNum <= 0) {
+ throw new IllegalArgumentException("checked num can't be negative.");
+ }
+ mCheckedNum = checkedNum;
+ invalidate();
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (mEnabled != enabled) {
+ mEnabled = enabled;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // draw outer and inner shadow
+ initShadowPaint();
+ canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,
+ (STROKE_RADIUS + STROKE_WIDTH / 2 + SHADOW_WIDTH) * mDensity, mShadowPaint);
+
+ // draw white stroke
+ canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,
+ STROKE_RADIUS * mDensity, mStrokePaint);
+
+ // draw content
+ if (mCountable) {
+ if (mCheckedNum != UNCHECKED) {
+ initBackgroundPaint();
+ canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,
+ BG_RADIUS * mDensity, mBackgroundPaint);
+ initTextPaint();
+ String text = String.valueOf(mCheckedNum);
+ int baseX = (int) (canvas.getWidth() - mTextPaint.measureText(text)) / 2;
+ int baseY = (int) (canvas.getHeight() - mTextPaint.descent() - mTextPaint.ascent()) / 2;
+ canvas.drawText(text, baseX, baseY, mTextPaint);
+ }
+ } else {
+ if (mChecked) {
+ initBackgroundPaint();
+ canvas.drawCircle((float) SIZE * mDensity / 2, (float) SIZE * mDensity / 2,
+ BG_RADIUS * mDensity, mBackgroundPaint);
+
+ mCheckDrawable.setBounds(getCheckRect());
+ mCheckDrawable.draw(canvas);
+ }
+ }
+
+ // enable hint
+ setAlpha(mEnabled ? 1.0f : 0.5f);
+ }
+
+ private void initShadowPaint() {
+ if (mShadowPaint == null) {
+ mShadowPaint = new Paint();
+ mShadowPaint.setAntiAlias(true);
+ // all in dp
+ float outerRadius = STROKE_RADIUS + STROKE_WIDTH / 2;
+ float innerRadius = outerRadius - STROKE_WIDTH;
+ float gradientRadius = outerRadius + SHADOW_WIDTH;
+ float stop0 = (innerRadius - SHADOW_WIDTH) / gradientRadius;
+ float stop1 = innerRadius / gradientRadius;
+ float stop2 = outerRadius / gradientRadius;
+ float stop3 = 1.0f;
+ mShadowPaint.setShader(
+ new RadialGradient((float) SIZE * mDensity / 2,
+ (float) SIZE * mDensity / 2,
+ gradientRadius * mDensity,
+ new int[]{Color.parseColor("#00000000"), Color.parseColor("#0D000000"),
+ Color.parseColor("#0D000000"), Color.parseColor("#00000000")},
+ new float[]{stop0, stop1, stop2, stop3},
+ Shader.TileMode.CLAMP));
+ }
+ }
+
+ private void initBackgroundPaint() {
+ if (mBackgroundPaint == null) {
+ mBackgroundPaint = new Paint();
+ mBackgroundPaint.setAntiAlias(true);
+ mBackgroundPaint.setStyle(Paint.Style.FILL);
+ TypedArray ta = getContext().getTheme()
+ .obtainStyledAttributes(new int[]{R.attr.item_checkCircle_backgroundColor});
+ int defaultColor = ResourcesCompat.getColor(
+ getResources(), R.color.zhihu_item_checkCircle_backgroundColor,
+ getContext().getTheme());
+ int color = ta.getColor(0, defaultColor);
+ ta.recycle();
+ mBackgroundPaint.setColor(color);
+ }
+ }
+
+ private void initTextPaint() {
+ if (mTextPaint == null) {
+ mTextPaint = new TextPaint();
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setColor(Color.WHITE);
+ mTextPaint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
+ mTextPaint.setTextSize(12.0f * mDensity);
+ }
+ }
+
+ // rect for drawing checked number or mark
+ private Rect getCheckRect() {
+ if (mCheckRect == null) {
+ int rectPadding = (int) (SIZE * mDensity / 2 - CONTENT_SIZE * mDensity / 2);
+ mCheckRect = new Rect(rectPadding, rectPadding,
+ (int) (SIZE * mDensity - rectPadding), (int) (SIZE * mDensity - rectPadding));
+ }
+
+ return mCheckRect;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/IncapableDialog.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/IncapableDialog.java
new file mode 100644
index 0000000..a1b7201
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/IncapableDialog.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.widget;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import androidx.appcompat.app.AlertDialog;
+import android.text.TextUtils;
+
+import com.zhihu.matisse.R;
+
+public class IncapableDialog extends DialogFragment {
+
+ public static final String EXTRA_TITLE = "extra_title";
+ public static final String EXTRA_MESSAGE = "extra_message";
+
+ public static IncapableDialog newInstance(String title, String message) {
+ IncapableDialog dialog = new IncapableDialog();
+ Bundle args = new Bundle();
+ args.putString(EXTRA_TITLE, title);
+ args.putString(EXTRA_MESSAGE, message);
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ String title = getArguments().getString(EXTRA_TITLE);
+ String message = getArguments().getString(EXTRA_MESSAGE);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ if (!TextUtils.isEmpty(title)) {
+ builder.setTitle(title);
+ }
+ if (!TextUtils.isEmpty(message)) {
+ builder.setMessage(message);
+ }
+ builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+
+ return builder.create();
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/MediaGrid.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/MediaGrid.java
new file mode 100644
index 0000000..0abb367
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/MediaGrid.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import androidx.recyclerview.widget.RecyclerView;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+
+public class MediaGrid extends SquareFrameLayout implements View.OnClickListener {
+
+ private ImageView mThumbnail;
+ private CheckView mCheckView;
+ private ImageView mGifTag;
+ private TextView mVideoDuration;
+
+ private Item mMedia;
+ private PreBindInfo mPreBindInfo;
+ private OnMediaGridClickListener mListener;
+
+ public MediaGrid(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public MediaGrid(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ private void init(Context context) {
+ LayoutInflater.from(context).inflate(R.layout.media_grid_content, this, true);
+
+ mThumbnail = (ImageView) findViewById(R.id.media_thumbnail);
+ mCheckView = (CheckView) findViewById(R.id.check_view);
+ mGifTag = (ImageView) findViewById(R.id.gif);
+ mVideoDuration = (TextView) findViewById(R.id.video_duration);
+
+ mThumbnail.setOnClickListener(this);
+ mCheckView.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mListener != null) {
+ if (v == mThumbnail) {
+ mListener.onThumbnailClicked(mThumbnail, mMedia, mPreBindInfo.mViewHolder);
+ } else if (v == mCheckView) {
+ mListener.onCheckViewClicked(mCheckView, mMedia, mPreBindInfo.mViewHolder);
+ }
+ }
+ }
+
+ public void preBindMedia(PreBindInfo info) {
+ mPreBindInfo = info;
+ }
+
+ public void bindMedia(Item item) {
+ mMedia = item;
+ setGifTag();
+ initCheckView();
+ setImage();
+ setVideoDuration();
+ }
+
+ public Item getMedia() {
+ return mMedia;
+ }
+
+ private void setGifTag() {
+ mGifTag.setVisibility(mMedia.isGif() ? View.VISIBLE : View.GONE);
+ }
+
+ private void initCheckView() {
+ mCheckView.setCountable(mPreBindInfo.mCheckViewCountable);
+ }
+
+ public void setCheckEnabled(boolean enabled) {
+ mCheckView.setEnabled(enabled);
+ }
+
+ public void setCheckedNum(int checkedNum) {
+ mCheckView.setCheckedNum(checkedNum);
+ }
+
+ public void setChecked(boolean checked) {
+ mCheckView.setChecked(checked);
+ }
+
+ private void setImage() {
+ if (mMedia.isGif()) {
+ SelectionSpec.getInstance().imageEngine.loadGifThumbnail(getContext(), mPreBindInfo.mResize,
+ mPreBindInfo.mPlaceholder, mThumbnail, mMedia.getContentUri());
+ } else {
+ SelectionSpec.getInstance().imageEngine.loadThumbnail(getContext(), mPreBindInfo.mResize,
+ mPreBindInfo.mPlaceholder, mThumbnail, mMedia.getContentUri());
+ }
+ }
+
+ private void setVideoDuration() {
+ if (mMedia.isVideo()) {
+ mVideoDuration.setVisibility(VISIBLE);
+ mVideoDuration.setText(DateUtils.formatElapsedTime(mMedia.duration / 1000));
+ } else {
+ mVideoDuration.setVisibility(GONE);
+ }
+ }
+
+ public void setOnMediaGridClickListener(OnMediaGridClickListener listener) {
+ mListener = listener;
+ }
+
+ public void removeOnMediaGridClickListener() {
+ mListener = null;
+ }
+
+ public interface OnMediaGridClickListener {
+
+ void onThumbnailClicked(ImageView thumbnail, Item item, RecyclerView.ViewHolder holder);
+
+ void onCheckViewClicked(CheckView checkView, Item item, RecyclerView.ViewHolder holder);
+ }
+
+ public static class PreBindInfo {
+ int mResize;
+ Drawable mPlaceholder;
+ boolean mCheckViewCountable;
+ RecyclerView.ViewHolder mViewHolder;
+
+ public PreBindInfo(int resize, Drawable placeholder, boolean checkViewCountable,
+ RecyclerView.ViewHolder viewHolder) {
+ mResize = resize;
+ mPlaceholder = placeholder;
+ mCheckViewCountable = checkViewCountable;
+ mViewHolder = viewHolder;
+ }
+ }
+
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/MediaGridInset.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/MediaGridInset.java
new file mode 100644
index 0000000..dca609c
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/MediaGridInset.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.widget;
+
+import android.graphics.Rect;
+import androidx.recyclerview.widget.RecyclerView;
+import android.view.View;
+
+public class MediaGridInset extends RecyclerView.ItemDecoration {
+
+ private int mSpanCount;
+ private int mSpacing;
+ private boolean mIncludeEdge;
+
+ public MediaGridInset(int spanCount, int spacing, boolean includeEdge) {
+ this.mSpanCount = spanCount;
+ this.mSpacing = spacing;
+ this.mIncludeEdge = includeEdge;
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ int position = parent.getChildAdapterPosition(view); // item position
+ int column = position % mSpanCount; // item column
+
+ if (mIncludeEdge) {
+ // spacing - column * ((1f / spanCount) * spacing)
+ outRect.left = mSpacing - column * mSpacing / mSpanCount;
+ // (column + 1) * ((1f / spanCount) * spacing)
+ outRect.right = (column + 1) * mSpacing / mSpanCount;
+
+ if (position < mSpanCount) { // top edge
+ outRect.top = mSpacing;
+ }
+ outRect.bottom = mSpacing; // item bottom
+ } else {
+ // column * ((1f / spanCount) * spacing)
+ outRect.left = column * mSpacing / mSpanCount;
+ // spacing - (column + 1) * ((1f / spanCount) * spacing)
+ outRect.right = mSpacing - (column + 1) * mSpacing / mSpanCount;
+ if (position >= mSpanCount) {
+ outRect.top = mSpacing; // item top
+ }
+ }
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/PreviewViewPager.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/PreviewViewPager.java
new file mode 100644
index 0000000..b3e66c3
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/PreviewViewPager.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.widget;
+
+import android.content.Context;
+import androidx.viewpager.widget.ViewPager;
+import android.util.AttributeSet;
+import android.view.View;
+
+import it.sephiroth.android.library.imagezoom.ImageViewTouch;
+
+public class PreviewViewPager extends ViewPager {
+
+ public PreviewViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
+ if (v instanceof ImageViewTouch) {
+ return ((ImageViewTouch) v).canScroll(dx) || super.canScroll(v, checkV, dx, x, y);
+ }
+ return super.canScroll(v, checkV, dx, x, y);
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/RoundedRectangleImageView.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/RoundedRectangleImageView.java
new file mode 100644
index 0000000..b1a5220
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/RoundedRectangleImageView.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.RectF;
+import androidx.appcompat.widget.AppCompatImageView;
+import android.util.AttributeSet;
+
+public class RoundedRectangleImageView extends AppCompatImageView {
+
+ private float mRadius; // dp
+ private Path mRoundedRectPath;
+ private RectF mRectF;
+
+ public RoundedRectangleImageView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public RoundedRectangleImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public RoundedRectangleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context);
+ }
+
+ private void init(Context context) {
+ float density = context.getResources().getDisplayMetrics().density;
+ mRadius = 2.0f * density;
+ mRoundedRectPath = new Path();
+ mRectF = new RectF();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mRectF.set(0.0f, 0.0f, getMeasuredWidth(), getMeasuredHeight());
+ mRoundedRectPath.addRoundRect(mRectF, mRadius, mRadius, Path.Direction.CW);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.clipPath(mRoundedRectPath);
+ super.onDraw(canvas);
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/SquareFrameLayout.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/SquareFrameLayout.java
new file mode 100644
index 0000000..1f95518
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/ui/widget/SquareFrameLayout.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.ui.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+public class SquareFrameLayout extends FrameLayout {
+
+ public SquareFrameLayout(Context context) {
+ super(context);
+ }
+
+ public SquareFrameLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/ExifInterfaceCompat.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/ExifInterfaceCompat.java
new file mode 100644
index 0000000..b2be0e4
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/ExifInterfaceCompat.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.utils;
+
+import android.media.ExifInterface;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Bug fixture for ExifInterface constructor.
+ */
+final class ExifInterfaceCompat {
+ private static final String TAG = ExifInterfaceCompat.class.getSimpleName();
+ private static final int EXIF_DEGREE_FALLBACK_VALUE = -1;
+
+ /**
+ * Do not instantiate this class.
+ */
+ private ExifInterfaceCompat() {
+ }
+
+ /**
+ * Creates new instance of {@link ExifInterface}.
+ * Original constructor won't check filename value, so if null value has been passed,
+ * the process will be killed because of SIGSEGV.
+ * Google Play crash report system cannot perceive this crash, so this method will throw
+ * {@link NullPointerException} when the filename is null.
+ *
+ * @param filename a JPEG filename.
+ * @return {@link ExifInterface} instance.
+ * @throws IOException something wrong with I/O.
+ */
+ public static ExifInterface newInstance(String filename) throws IOException {
+ if (filename == null) throw new NullPointerException("filename should not be null");
+ return new ExifInterface(filename);
+ }
+
+ private static Date getExifDateTime(String filepath) {
+ ExifInterface exif;
+ try {
+ // ExifInterface does not check whether file path is null or not,
+ // so passing null file path argument to its constructor causing SIGSEGV.
+ // We should avoid such a situation by checking file path string.
+ exif = newInstance(filepath);
+ } catch (IOException ex) {
+ Log.e(TAG, "cannot read exif", ex);
+ return null;
+ }
+
+ String date = exif.getAttribute(ExifInterface.TAG_DATETIME);
+ if (TextUtils.isEmpty(date)) {
+ return null;
+ }
+ try {
+ SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+ formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return formatter.parse(date);
+ } catch (ParseException e) {
+ Log.d(TAG, "failed to parse date taken", e);
+ }
+ return null;
+ }
+
+ /**
+ * Read exif info and get datetime value of the photo.
+ *
+ * @param filepath to get datetime
+ * @return when a photo taken.
+ */
+ public static long getExifDateTimeInMillis(String filepath) {
+ Date datetime = getExifDateTime(filepath);
+ if (datetime == null) {
+ return -1;
+ }
+ return datetime.getTime();
+ }
+
+ /**
+ * Read exif info and get orientation value of the photo.
+ *
+ * @param filepath to get exif.
+ * @return exif orientation value
+ */
+ public static int getExifOrientation(String filepath) {
+ ExifInterface exif;
+ try {
+ // ExifInterface does not check whether file path is null or not,
+ // so passing null file path argument to its constructor causing SIGSEGV.
+ // We should avoid such a situation by checking file path string.
+ exif = newInstance(filepath);
+ } catch (IOException ex) {
+ Log.e(TAG, "cannot read exif", ex);
+ return EXIF_DEGREE_FALLBACK_VALUE;
+ }
+
+ int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, EXIF_DEGREE_FALLBACK_VALUE);
+ if (orientation == EXIF_DEGREE_FALLBACK_VALUE) {
+ return 0;
+ }
+ // We only recognize a subset of orientation tag values.
+ switch (orientation) {
+ case ExifInterface.ORIENTATION_ROTATE_90:
+ return 90;
+ case ExifInterface.ORIENTATION_ROTATE_180:
+ return 180;
+ case ExifInterface.ORIENTATION_ROTATE_270:
+ return 270;
+ default:
+ return 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/MediaStoreCompat.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/MediaStoreCompat.java
new file mode 100644
index 0000000..592c77e
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/MediaStoreCompat.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.utils;
+
+import android.app.Activity;
+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.Build;
+import android.os.Environment;
+import android.provider.MediaStore;
+import androidx.fragment.app.Fragment;
+import androidx.core.content.FileProvider;
+import androidx.core.os.EnvironmentCompat;
+
+import com.zhihu.matisse.internal.entity.CaptureStrategy;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+public class MediaStoreCompat {
+
+ private final WeakReference mContext;
+ private final WeakReference mFragment;
+ private CaptureStrategy mCaptureStrategy;
+ private Uri mCurrentPhotoUri;
+ private String mCurrentPhotoPath;
+
+ public MediaStoreCompat(Activity activity) {
+ mContext = new WeakReference<>(activity);
+ mFragment = null;
+ }
+
+ public MediaStoreCompat(Activity activity, Fragment fragment) {
+ mContext = new WeakReference<>(activity);
+ mFragment = new WeakReference<>(fragment);
+ }
+
+ /**
+ * Checks whether the device has a camera feature or not.
+ *
+ * @param context a context to check for camera feature.
+ * @return true if the device has a camera feature. false otherwise.
+ */
+ public static boolean hasCameraFeature(Context context) {
+ PackageManager pm = context.getApplicationContext().getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
+ }
+
+ public void setCaptureStrategy(CaptureStrategy strategy) {
+ mCaptureStrategy = strategy;
+ }
+
+ public void dispatchCaptureIntent(Context context, int requestCode) {
+ Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ if (captureIntent.resolveActivity(context.getPackageManager()) != null) {
+ File photoFile = null;
+ try {
+ photoFile = createImageFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ if (photoFile != null) {
+ mCurrentPhotoPath = photoFile.getAbsolutePath();
+ mCurrentPhotoUri = FileProvider.getUriForFile(mContext.get(),
+ mCaptureStrategy.authority, photoFile);
+ captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCurrentPhotoUri);
+ captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ List resInfoList = context.getPackageManager()
+ .queryIntentActivities(captureIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ context.grantUriPermission(packageName, mCurrentPhotoUri,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ if (mFragment != null) {
+ mFragment.get().startActivityForResult(captureIntent, requestCode);
+ } else {
+ mContext.get().startActivityForResult(captureIntent, requestCode);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private File createImageFile() throws IOException {
+ // Create an image file name
+ String timeStamp =
+ new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
+ String imageFileName = String.format("JPEG_%s.jpg", timeStamp);
+ File storageDir;
+ if (mCaptureStrategy.isPublic) {
+ storageDir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES);
+ if (!storageDir.exists()) storageDir.mkdirs();
+ } else {
+ storageDir = mContext.get().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+ }
+ if (mCaptureStrategy.directory != null) {
+ storageDir = new File(storageDir, mCaptureStrategy.directory);
+ if (!storageDir.exists()) storageDir.mkdirs();
+ }
+
+ // Avoid joining path components manually
+ File tempFile = new File(storageDir, imageFileName);
+
+ // Handle the situation that user's external storage is not ready
+ if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {
+ return null;
+ }
+
+ return tempFile;
+ }
+
+ public Uri getCurrentPhotoUri() {
+ return mCurrentPhotoUri;
+ }
+
+ public String getCurrentPhotoPath() {
+ return mCurrentPhotoPath;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/PathUtils.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/PathUtils.java
new file mode 100644
index 0000000..de64322
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/PathUtils.java
@@ -0,0 +1,135 @@
+package com.zhihu.matisse.internal.utils;
+
+import android.annotation.TargetApi;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+
+/**
+ * http://stackoverflow.com/a/27271131/4739220
+ */
+
+public class PathUtils {
+ /**
+ * Get a file path from a Uri. This will get the the path for Storage Access
+ * Framework Documents, as well as the _data field for the MediaStore and
+ * other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @author paulburke
+ */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ public static String getPath(final Context context, final Uri uri) {
+ // DocumentProvider
+ if (Platform.hasKitKat() && DocumentsContract.isDocumentUri(context, uri)) {
+ // ExternalStorageProvider
+ if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ if ("primary".equalsIgnoreCase(type)) {
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ }
+
+ // TODO handle non-primary volumes
+ } else if (isDownloadsDocument(uri)) { // DownloadsProvider
+
+ final String id = DocumentsContract.getDocumentId(uri);
+ final Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+ return getDataColumn(context, contentUri, null, null);
+ } else if (isMediaDocument(uri)) { // MediaProvider
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ Uri contentUri = null;
+ if ("image".equals(type)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ }
+
+ final String selection = "_id=?";
+ final String[] selectionArgs = new String[]{
+ split[1]
+ };
+
+ return getDataColumn(context, contentUri, selection, selectionArgs);
+ }
+ } else if ("content".equalsIgnoreCase(uri.getScheme())) { // MediaStore (and general)
+ return getDataColumn(context, uri, null, null);
+ } else if ("file".equalsIgnoreCase(uri.getScheme())) { // File
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @param selection (Optional) Filter used in the query.
+ * @param selectionArgs (Optional) Selection arguments used in the query.
+ * @return The value of the _data column, which is typically a file path.
+ */
+ public static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
+
+ Cursor cursor = null;
+ final String column = "_data";
+ final String[] projection = {
+ column
+ };
+
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ final int columnIndex = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(columnIndex);
+ }
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+ return null;
+ }
+
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ */
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ */
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ */
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/PhotoMetadataUtils.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/PhotoMetadataUtils.java
new file mode 100644
index 0000000..59c1c8c
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/PhotoMetadataUtils.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014 nohana, Inc.
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.utils;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.media.ExifInterface;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import com.zhihu.matisse.MimeType;
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.filter.Filter;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.entity.IncapableCause;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+public final class PhotoMetadataUtils {
+ private static final String TAG = PhotoMetadataUtils.class.getSimpleName();
+ private static final int MAX_WIDTH = 1600;
+ private static final String SCHEME_CONTENT = "content";
+
+ private PhotoMetadataUtils() {
+ throw new AssertionError("oops! the utility class is about to be instantiated...");
+ }
+
+ public static int getPixelsCount(ContentResolver resolver, Uri uri) {
+ Point size = getBitmapBound(resolver, uri);
+ return size.x * size.y;
+ }
+
+ public static Point getBitmapSize(Uri uri, Activity activity) {
+ ContentResolver resolver = activity.getContentResolver();
+ Point imageSize = getBitmapBound(resolver, uri);
+ int w = imageSize.x;
+ int h = imageSize.y;
+ if (PhotoMetadataUtils.shouldRotate(resolver, uri)) {
+ w = imageSize.y;
+ h = imageSize.x;
+ }
+ if (h == 0) return new Point(MAX_WIDTH, MAX_WIDTH);
+ DisplayMetrics metrics = new DisplayMetrics();
+ activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ float screenWidth = (float) metrics.widthPixels;
+ float screenHeight = (float) metrics.heightPixels;
+ float widthScale = screenWidth / w;
+ float heightScale = screenHeight / h;
+ if (widthScale > heightScale) {
+ return new Point((int) (w * widthScale), (int) (h * heightScale));
+ }
+ return new Point((int) (w * widthScale), (int) (h * heightScale));
+ }
+
+ public static Point getBitmapBound(ContentResolver resolver, Uri uri) {
+ InputStream is = null;
+ try {
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ is = resolver.openInputStream(uri);
+ BitmapFactory.decodeStream(is, null, options);
+ int width = options.outWidth;
+ int height = options.outHeight;
+ return new Point(width, height);
+ } catch (FileNotFoundException e) {
+ return new Point(0, 0);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public static String getPath(ContentResolver resolver, Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+
+ if (SCHEME_CONTENT.equals(uri.getScheme())) {
+ Cursor cursor = null;
+ try {
+ cursor = resolver.query(uri, new String[]{MediaStore.Images.ImageColumns.DATA},
+ null, null, null);
+ if (cursor == null || !cursor.moveToFirst()) {
+ return null;
+ }
+ return cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA));
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ return uri.getPath();
+ }
+
+ public static IncapableCause isAcceptable(Context context, Item item) {
+ if (!isSelectableType(context, item)) {
+ return new IncapableCause(context.getString(R.string.error_file_type));
+ }
+
+ if (SelectionSpec.getInstance().filters != null) {
+ for (Filter filter : SelectionSpec.getInstance().filters) {
+ IncapableCause incapableCause = filter.filter(context, item);
+ if (incapableCause != null) {
+ return incapableCause;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static boolean isSelectableType(Context context, Item item) {
+ if (context == null) {
+ return false;
+ }
+
+ ContentResolver resolver = context.getContentResolver();
+ for (MimeType type : SelectionSpec.getInstance().mimeTypeSet) {
+ if (type.checkType(resolver, item.getContentUri())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean shouldRotate(ContentResolver resolver, Uri uri) {
+ ExifInterface exif;
+ try {
+ exif = ExifInterfaceCompat.newInstance(getPath(resolver, uri));
+ } catch (IOException e) {
+ Log.e(TAG, "could not read exif info of the image: " + uri);
+ return false;
+ }
+ int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
+ return orientation == ExifInterface.ORIENTATION_ROTATE_90
+ || orientation == ExifInterface.ORIENTATION_ROTATE_270;
+ }
+
+ public static float getSizeInMB(long sizeInBytes) {
+ DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
+ df.applyPattern("0.0");
+ String result = df.format((float) sizeInBytes / 1024 / 1024);
+ Log.e(TAG, "getSizeInMB: " + result);
+ result = result.replaceAll(",", "."); // in some case , 0.0 will be 0,0
+ return Float.valueOf(result);
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/Platform.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/Platform.java
new file mode 100644
index 0000000..df41989
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/Platform.java
@@ -0,0 +1,16 @@
+package com.zhihu.matisse.internal.utils;
+
+import android.os.Build;
+
+/**
+ * @author JoongWon Baik
+ */
+public class Platform {
+ public static boolean hasICS() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
+ }
+
+ public static boolean hasKitKat() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/SingleMediaScanner.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/SingleMediaScanner.java
new file mode 100644
index 0000000..81370df
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/SingleMediaScanner.java
@@ -0,0 +1,44 @@
+package com.zhihu.matisse.internal.utils;
+
+import android.content.Context;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+
+/**
+ * @author 工藤
+ * @email gougou@16fan.com
+ * create at 2018年10月23日12:17:59
+ * description:媒体扫描
+ */
+public class SingleMediaScanner implements MediaScannerConnection.MediaScannerConnectionClient {
+
+ private MediaScannerConnection mMsc;
+ private String mPath;
+ private ScanListener mListener;
+
+ public interface ScanListener {
+
+ /**
+ * scan finish
+ */
+ void onScanFinish();
+ }
+
+ public SingleMediaScanner(Context context, String mPath, ScanListener mListener) {
+ this.mPath = mPath;
+ this.mListener = mListener;
+ this.mMsc = new MediaScannerConnection(context, this);
+ this.mMsc.connect();
+ }
+
+ @Override public void onMediaScannerConnected() {
+ mMsc.scanFile(mPath, null);
+ }
+
+ @Override public void onScanCompleted(String mPath, Uri mUri) {
+ mMsc.disconnect();
+ if (mListener != null) {
+ mListener.onScanFinish();
+ }
+ }
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/UIUtils.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/UIUtils.java
new file mode 100644
index 0000000..129b599
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/internal/utils/UIUtils.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.internal.utils;
+
+import android.content.Context;
+
+public class UIUtils {
+
+ public static int spanCount(Context context, int gridExpectedSize) {
+ int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
+ float expected = (float) screenWidth / (float) gridExpectedSize;
+ int spanCount = Math.round(expected);
+ if (spanCount == 0) {
+ spanCount = 1;
+ }
+ return spanCount;
+ }
+
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/listener/OnCheckedListener.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/listener/OnCheckedListener.java
new file mode 100644
index 0000000..4a0ebae
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/listener/OnCheckedListener.java
@@ -0,0 +1,9 @@
+package com.zhihu.matisse.listener;
+
+
+/**
+ * when original is enabled , callback immediately when user check or uncheck original.
+ */
+public interface OnCheckedListener {
+ void onCheck(boolean isChecked);
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/listener/OnFragmentInteractionListener.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/listener/OnFragmentInteractionListener.java
new file mode 100644
index 0000000..79f5536
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/listener/OnFragmentInteractionListener.java
@@ -0,0 +1,11 @@
+package com.zhihu.matisse.listener;
+
+/**
+ * PreViewItemFragment 和 BasePreViewActivity 通信的接口 ,为了方便拿到 ImageViewTouch 的点击事件
+ */
+public interface OnFragmentInteractionListener {
+ /**
+ * ImageViewTouch 被点击了
+ */
+ void onClick();
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/listener/OnSelectedListener.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/listener/OnSelectedListener.java
new file mode 100644
index 0000000..b7d6d22
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/listener/OnSelectedListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zhihu.matisse.listener;
+
+import android.net.Uri;
+import androidx.annotation.NonNull;
+
+import java.util.List;
+
+public interface OnSelectedListener {
+ /**
+ * @param uriList the selected item {@link Uri} list.
+ * @param pathList the selected item file path list.
+ */
+ void onSelected(@NonNull List uriList, @NonNull List pathList);
+}
diff --git a/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/ui/MatisseActivity.java b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/ui/MatisseActivity.java
new file mode 100644
index 0000000..662379a
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/java/com/zhihu/matisse/ui/MatisseActivity.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2017 Zhihu Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.zhihu.matisse.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.zhihu.matisse.R;
+import com.zhihu.matisse.internal.entity.Album;
+import com.zhihu.matisse.internal.entity.Item;
+import com.zhihu.matisse.internal.entity.SelectionSpec;
+import com.zhihu.matisse.internal.model.AlbumCollection;
+import com.zhihu.matisse.internal.model.SelectedItemCollection;
+import com.zhihu.matisse.internal.ui.AlbumPreviewActivity;
+import com.zhihu.matisse.internal.ui.BasePreviewActivity;
+import com.zhihu.matisse.internal.ui.MediaSelectionFragment;
+import com.zhihu.matisse.internal.ui.SelectedPreviewActivity;
+import com.zhihu.matisse.internal.ui.adapter.AlbumMediaAdapter;
+import com.zhihu.matisse.internal.ui.adapter.AlbumsAdapter;
+import com.zhihu.matisse.internal.ui.widget.AlbumsSpinner;
+import com.zhihu.matisse.internal.ui.widget.CheckRadioView;
+import com.zhihu.matisse.internal.ui.widget.IncapableDialog;
+import com.zhihu.matisse.internal.utils.MediaStoreCompat;
+import com.zhihu.matisse.internal.utils.PathUtils;
+import com.zhihu.matisse.internal.utils.PhotoMetadataUtils;
+
+import com.zhihu.matisse.internal.utils.SingleMediaScanner;
+import java.util.ArrayList;
+
+/**
+ * Main Activity to display albums and media content (images/videos) in each album
+ * and also support media selecting operations.
+ */
+public class MatisseActivity extends AppCompatActivity implements
+ AlbumCollection.AlbumCallbacks, AdapterView.OnItemSelectedListener,
+ MediaSelectionFragment.SelectionProvider, View.OnClickListener,
+ AlbumMediaAdapter.CheckStateListener, AlbumMediaAdapter.OnMediaClickListener,
+ AlbumMediaAdapter.OnPhotoCapture {
+
+ public static final String EXTRA_RESULT_SELECTION = "extra_result_selection";
+ public static final String EXTRA_RESULT_SELECTION_PATH = "extra_result_selection_path";
+ public static final String EXTRA_RESULT_ORIGINAL_ENABLE = "extra_result_original_enable";
+ private static final int REQUEST_CODE_PREVIEW = 23;
+ private static final int REQUEST_CODE_CAPTURE = 24;
+ public static final String CHECK_STATE = "checkState";
+ private final AlbumCollection mAlbumCollection = new AlbumCollection();
+ private MediaStoreCompat mMediaStoreCompat;
+ private SelectedItemCollection mSelectedCollection = new SelectedItemCollection(this);
+ private SelectionSpec mSpec;
+
+ private AlbumsSpinner mAlbumsSpinner;
+ private AlbumsAdapter mAlbumsAdapter;
+ private TextView mButtonPreview;
+ private TextView mButtonApply;
+ private View mContainer;
+ private View mEmptyView;
+
+ private LinearLayout mOriginalLayout;
+ private CheckRadioView mOriginal;
+ private boolean mOriginalEnable;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ // programmatically set theme before super.onCreate()
+ mSpec = SelectionSpec.getInstance();
+ setTheme(mSpec.themeId);
+ super.onCreate(savedInstanceState);
+ if (!mSpec.hasInited) {
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+ setContentView(R.layout.activity_matisse);
+
+ if (mSpec.needOrientationRestriction()) {
+ setRequestedOrientation(mSpec.orientation);
+ }
+
+ if (mSpec.capture) {
+ mMediaStoreCompat = new MediaStoreCompat(this);
+ if (mSpec.captureStrategy == null)
+ throw new RuntimeException("Don't forget to set CaptureStrategy.");
+ mMediaStoreCompat.setCaptureStrategy(mSpec.captureStrategy);
+ }
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayShowTitleEnabled(false);
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ Drawable navigationIcon = toolbar.getNavigationIcon();
+ TypedArray ta = getTheme().obtainStyledAttributes(new int[]{R.attr.album_element_color});
+ int color = ta.getColor(0, 0);
+ ta.recycle();
+ navigationIcon.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+
+ mButtonPreview = (TextView) findViewById(R.id.button_preview);
+ mButtonApply = (TextView) findViewById(R.id.button_apply);
+ mButtonPreview.setOnClickListener(this);
+ mButtonApply.setOnClickListener(this);
+ mContainer = findViewById(R.id.container);
+ mEmptyView = findViewById(R.id.empty_view);
+ mOriginalLayout = findViewById(R.id.originalLayout);
+ mOriginal = findViewById(R.id.original);
+ mOriginalLayout.setOnClickListener(this);
+
+ mSelectedCollection.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mOriginalEnable = savedInstanceState.getBoolean(CHECK_STATE);
+ }
+ updateBottomToolbar();
+
+ mAlbumsAdapter = new AlbumsAdapter(this, null, false);
+ mAlbumsSpinner = new AlbumsSpinner(this);
+ mAlbumsSpinner.setOnItemSelectedListener(this);
+ mAlbumsSpinner.setSelectedTextView((TextView) findViewById(R.id.selected_album));
+ mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.toolbar));
+ mAlbumsSpinner.setAdapter(mAlbumsAdapter);
+ mAlbumCollection.onCreate(this, this);
+ mAlbumCollection.onRestoreInstanceState(savedInstanceState);
+ mAlbumCollection.loadAlbums();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mSelectedCollection.onSaveInstanceState(outState);
+ mAlbumCollection.onSaveInstanceState(outState);
+ outState.putBoolean("checkState", mOriginalEnable);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mAlbumCollection.onDestroy();
+ mSpec.onCheckedListener = null;
+ mSpec.onSelectedListener = null;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ setResult(Activity.RESULT_CANCELED);
+ super.onBackPressed();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode != RESULT_OK)
+ return;
+
+ if (requestCode == REQUEST_CODE_PREVIEW) {
+ Bundle resultBundle = data.getBundleExtra(BasePreviewActivity.EXTRA_RESULT_BUNDLE);
+ ArrayList
- selected = resultBundle.getParcelableArrayList(SelectedItemCollection.STATE_SELECTION);
+ mOriginalEnable = data.getBooleanExtra(BasePreviewActivity.EXTRA_RESULT_ORIGINAL_ENABLE, false);
+ int collectionType = resultBundle.getInt(SelectedItemCollection.STATE_COLLECTION_TYPE,
+ SelectedItemCollection.COLLECTION_UNDEFINED);
+ if (data.getBooleanExtra(BasePreviewActivity.EXTRA_RESULT_APPLY, false)) {
+ Intent result = new Intent();
+ ArrayList selectedUris = new ArrayList<>();
+ ArrayList selectedPaths = new ArrayList<>();
+ if (selected != null) {
+ for (Item item : selected) {
+ selectedUris.add(item.getContentUri());
+ selectedPaths.add(PathUtils.getPath(this, item.getContentUri()));
+ }
+ }
+ result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selectedUris);
+ result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPaths);
+ result.putExtra(EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
+ setResult(RESULT_OK, result);
+ finish();
+ } else {
+ mSelectedCollection.overwrite(selected, collectionType);
+ Fragment mediaSelectionFragment = getSupportFragmentManager().findFragmentByTag(
+ MediaSelectionFragment.class.getSimpleName());
+ if (mediaSelectionFragment instanceof MediaSelectionFragment) {
+ ((MediaSelectionFragment) mediaSelectionFragment).refreshMediaGrid();
+ }
+ updateBottomToolbar();
+ }
+ } else if (requestCode == REQUEST_CODE_CAPTURE) {
+ // Just pass the data back to previous calling Activity.
+ Uri contentUri = mMediaStoreCompat.getCurrentPhotoUri();
+ String path = mMediaStoreCompat.getCurrentPhotoPath();
+ ArrayList selected = new ArrayList<>();
+ selected.add(contentUri);
+ ArrayList selectedPath = new ArrayList<>();
+ selectedPath.add(path);
+ Intent result = new Intent();
+ result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selected);
+ result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPath);
+ setResult(RESULT_OK, result);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+ MatisseActivity.this.revokeUriPermission(contentUri,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ new SingleMediaScanner(this.getApplicationContext(), path, new SingleMediaScanner.ScanListener() {
+ @Override public void onScanFinish() {
+ Log.i("SingleMediaScanner", "scan finish!");
+ }
+ });
+ finish();
+ }
+ }
+
+ private void updateBottomToolbar() {
+
+ int selectedCount = mSelectedCollection.count();
+ if (selectedCount == 0) {
+ mButtonPreview.setEnabled(false);
+ mButtonApply.setEnabled(false);
+ mButtonApply.setText(getString(R.string.button_apply_default));
+ } else if (selectedCount == 1 && mSpec.singleSelectionModeEnabled()) {
+ mButtonPreview.setEnabled(true);
+ mButtonApply.setText(R.string.button_apply_default);
+ mButtonApply.setEnabled(true);
+ } else {
+ mButtonPreview.setEnabled(true);
+ mButtonApply.setEnabled(true);
+ mButtonApply.setText(getString(R.string.button_apply, selectedCount));
+ }
+
+
+ if (mSpec.originalable) {
+ mOriginalLayout.setVisibility(View.VISIBLE);
+ updateOriginalState();
+ } else {
+ mOriginalLayout.setVisibility(View.INVISIBLE);
+ }
+
+
+ }
+
+
+ private void updateOriginalState() {
+ mOriginal.setChecked(mOriginalEnable);
+ if (countOverMaxSize() > 0) {
+
+ if (mOriginalEnable) {
+ IncapableDialog incapableDialog = IncapableDialog.newInstance("",
+ getString(R.string.error_over_original_size, mSpec.originalMaxSize));
+ incapableDialog.show(getSupportFragmentManager(),
+ IncapableDialog.class.getName());
+
+ mOriginal.setChecked(false);
+ mOriginalEnable = false;
+ }
+ }
+ }
+
+
+ private int countOverMaxSize() {
+ int count = 0;
+ int selectedCount = mSelectedCollection.count();
+ for (int i = 0; i < selectedCount; i++) {
+ Item item = mSelectedCollection.asList().get(i);
+
+ if (item.isImage()) {
+ float size = PhotoMetadataUtils.getSizeInMB(item.size);
+ if (size > mSpec.originalMaxSize) {
+ count++;
+ }
+ }
+ }
+ return count;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.button_preview) {
+ Intent intent = new Intent(this, SelectedPreviewActivity.class);
+ intent.putExtra(BasePreviewActivity.EXTRA_DEFAULT_BUNDLE, mSelectedCollection.getDataWithBundle());
+ intent.putExtra(BasePreviewActivity.EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
+ startActivityForResult(intent, REQUEST_CODE_PREVIEW);
+ } else if (v.getId() == R.id.button_apply) {
+ Intent result = new Intent();
+ ArrayList selectedUris = (ArrayList) mSelectedCollection.asListOfUri();
+ result.putParcelableArrayListExtra(EXTRA_RESULT_SELECTION, selectedUris);
+ ArrayList selectedPaths = (ArrayList) mSelectedCollection.asListOfString();
+ result.putStringArrayListExtra(EXTRA_RESULT_SELECTION_PATH, selectedPaths);
+ result.putExtra(EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
+ setResult(RESULT_OK, result);
+ finish();
+ } else if (v.getId() == R.id.originalLayout) {
+ int count = countOverMaxSize();
+ if (count > 0) {
+ IncapableDialog incapableDialog = IncapableDialog.newInstance("",
+ getString(R.string.error_over_original_count, count, mSpec.originalMaxSize));
+ incapableDialog.show(getSupportFragmentManager(),
+ IncapableDialog.class.getName());
+ return;
+ }
+
+ mOriginalEnable = !mOriginalEnable;
+ mOriginal.setChecked(mOriginalEnable);
+
+ if (mSpec.onCheckedListener != null) {
+ mSpec.onCheckedListener.onCheck(mOriginalEnable);
+ }
+ }
+ }
+
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ mAlbumCollection.setStateCurrentSelection(position);
+ mAlbumsAdapter.getCursor().moveToPosition(position);
+ Album album = Album.valueOf(mAlbumsAdapter.getCursor());
+ if (album.isAll() && SelectionSpec.getInstance().capture) {
+ album.addCaptureCount();
+ }
+ onAlbumSelected(album);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+
+ @Override
+ public void onAlbumLoad(final Cursor cursor) {
+ mAlbumsAdapter.swapCursor(cursor);
+ // select default album.
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ cursor.moveToPosition(mAlbumCollection.getCurrentSelection());
+ mAlbumsSpinner.setSelection(MatisseActivity.this,
+ mAlbumCollection.getCurrentSelection());
+ Album album = Album.valueOf(cursor);
+ if (album.isAll() && SelectionSpec.getInstance().capture) {
+ album.addCaptureCount();
+ }
+ onAlbumSelected(album);
+ }
+ });
+ }
+
+ @Override
+ public void onAlbumReset() {
+ mAlbumsAdapter.swapCursor(null);
+ }
+
+ private void onAlbumSelected(Album album) {
+ if (album.isAll() && album.isEmpty()) {
+ mContainer.setVisibility(View.GONE);
+ mEmptyView.setVisibility(View.VISIBLE);
+ } else {
+ mContainer.setVisibility(View.VISIBLE);
+ mEmptyView.setVisibility(View.GONE);
+ Fragment fragment = MediaSelectionFragment.newInstance(album);
+ getSupportFragmentManager()
+ .beginTransaction()
+ .replace(R.id.container, fragment, MediaSelectionFragment.class.getSimpleName())
+ .commitAllowingStateLoss();
+ }
+ }
+
+ @Override
+ public void onUpdate() {
+ // notify bottom toolbar that check state changed.
+ updateBottomToolbar();
+
+ if (mSpec.onSelectedListener != null) {
+ mSpec.onSelectedListener.onSelected(
+ mSelectedCollection.asListOfUri(), mSelectedCollection.asListOfString());
+ }
+ }
+
+ @Override
+ public void onMediaClick(Album album, Item item, int adapterPosition) {
+ Intent intent = new Intent(this, AlbumPreviewActivity.class);
+ intent.putExtra(AlbumPreviewActivity.EXTRA_ALBUM, album);
+ intent.putExtra(AlbumPreviewActivity.EXTRA_ITEM, item);
+ intent.putExtra(BasePreviewActivity.EXTRA_DEFAULT_BUNDLE, mSelectedCollection.getDataWithBundle());
+ intent.putExtra(BasePreviewActivity.EXTRA_RESULT_ORIGINAL_ENABLE, mOriginalEnable);
+ startActivityForResult(intent, REQUEST_CODE_PREVIEW);
+ }
+
+ @Override
+ public SelectedItemCollection provideSelectedItemCollection() {
+ return mSelectedCollection;
+ }
+
+ @Override
+ public void capture() {
+ if (mMediaStoreCompat != null) {
+ mMediaStoreCompat.dispatchCaptureIntent(this, REQUEST_CODE_CAPTURE);
+ }
+ }
+
+}
diff --git a/coalMiningApp/matisse/src/main/res/color/dracula_bottom_toolbar_apply.xml b/coalMiningApp/matisse/src/main/res/color/dracula_bottom_toolbar_apply.xml
new file mode 100644
index 0000000..84e13af
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/res/color/dracula_bottom_toolbar_apply.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/coalMiningApp/matisse/src/main/res/color/dracula_bottom_toolbar_preview.xml b/coalMiningApp/matisse/src/main/res/color/dracula_bottom_toolbar_preview.xml
new file mode 100644
index 0000000..56e04b7
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/res/color/dracula_bottom_toolbar_preview.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/coalMiningApp/matisse/src/main/res/color/dracula_preview_bottom_toolbar_apply.xml b/coalMiningApp/matisse/src/main/res/color/dracula_preview_bottom_toolbar_apply.xml
new file mode 100644
index 0000000..9920c27
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/res/color/dracula_preview_bottom_toolbar_apply.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/coalMiningApp/matisse/src/main/res/color/zhihu_bottom_toolbar_apply.xml b/coalMiningApp/matisse/src/main/res/color/zhihu_bottom_toolbar_apply.xml
new file mode 100644
index 0000000..d12c968
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/res/color/zhihu_bottom_toolbar_apply.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/coalMiningApp/matisse/src/main/res/color/zhihu_bottom_toolbar_preview.xml b/coalMiningApp/matisse/src/main/res/color/zhihu_bottom_toolbar_preview.xml
new file mode 100644
index 0000000..fef8d16
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/res/color/zhihu_bottom_toolbar_preview.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/coalMiningApp/matisse/src/main/res/color/zhihu_preview_bottom_toolbar_apply.xml b/coalMiningApp/matisse/src/main/res/color/zhihu_preview_bottom_toolbar_apply.xml
new file mode 100644
index 0000000..0bec454
--- /dev/null
+++ b/coalMiningApp/matisse/src/main/res/color/zhihu_preview_bottom_toolbar_apply.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_arrow_drop_down_white_24dp.png b/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_arrow_drop_down_white_24dp.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c6076df77463404d7163c9929bad5798576c49b
GIT binary patch
literal 123
zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K;lc$Sgh)3t!GrM^i6gXHN+!Ft<
zyz%6wgzzGf8m{hg<~baK7i~0n1e^UrcB-xUbawM9)9}#zn5)@-SM$qEFA7|Y7jyVu
Ve}1-8%O{{|44$rjF6*2UngF%-Cs62;P
z;rt^PnzR4-B~3VNI!Vkwpm#}DQ`QtoT{RCjhCut7rDpeTegIm<;OXk;vd$@?2>|#_
BI?Vt8
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_empty_dracula.png b/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_empty_dracula.png
new file mode 100644
index 0000000000000000000000000000000000000000..0920df470690bbbc7b20e2f8b3cc11d05d52c3fc
GIT binary patch
literal 5911
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
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_empty_zhihu.png b/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_empty_zhihu.png
new file mode 100644
index 0000000000000000000000000000000000000000..86ffe1cda6b9b98d715e1f670a2f99aeed337070
GIT binary patch
literal 5785
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
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_gif.png b/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_gif.png
new file mode 100644
index 0000000000000000000000000000000000000000..d4085f56fb084eba0112bb70017881ba618fa333
GIT binary patch
literal 929
zcmeAS@N?(olHy`uVBq!ia0vp^20$#!!3-pCzmtyzQY`6?zK#qG8~eHcB(gFvFa`wp
zgt!95NdiJbLNYQkj*gBwIXUI!H#avQA0Ln_A|fIt
zCMGE>n3z~uS=rdwxVpO9+1dH{`1t$#2L=WP2M4cQ
zxiTs$YSpS$DJd!G>FF668QIy{rKP1?wrtt9ZChDc+4k+*Crp?CH00#TlR%Sa&YU@W
z_U!rd=Pz8i@XD1d%a<=-x^(Hfb?Y{4*syi$R-l$0J9g~ey?Y-R96WgN>eZ{)uV24;
z^X9Evw?2RVeCN&`Ao>0K_lFN3K7Rc8>C>mr!Qdqryng)}DE0R3+jsBYefaR<%a<>I
z|Ni~C@dz
zliG_JVcVjnne^xXc80)>&F$Iu56{`z{!`)|gSaARbLW;<
z4zqYx^fo3%=q+PDZZu_)L}uK9rsch#{=ST6oLn9C%JCzUnT@JhLJMO8&xs>^N*mN%
zidKC+d(&~(4X^l9{z)}@+8V4foohDwRo02AD5+*{ffSutjI)4w)OD34HG75WN}0rT%0iZyWoyv
zH=TFgwcrdmBv|+CK=~(4j+F{)5_~;R{9&Qy;ZR-nEP2X!Y}OqFikkW%~6@vd$@?2>?<#oM-?5
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_play_circle_outline_white_48dp.png b/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_play_circle_outline_white_48dp.png
new file mode 100644
index 0000000000000000000000000000000000000000..6e1b578c54d13ceabffc2d6f49a383bc73605988
GIT binary patch
literal 1023
zcmV0zV6u|K(L}#j#NlM5x;x=T{UGWVyM6uO@Y=mavGW`M?Mv;Vk1VIByAc4}n(2A*U
z3=>39h#Cx`NLT5qBxa@2VKTTVm^*|2F3)|K+?O-=N$FyK=L`Jd+ugiT!=ubL>nt$F6^>#W8-sqjxydRXVfJ`Mfo~2z=pxJ1h^tDG{(}p;#A_O{
z$_4j9$N8Nk?sLp#&^11hRE0q|L1|{#-wqELCQlEYq)5?84|#@JdAN#x|i1x`yp%S}_Uc5VP2R)C3*p6LyYqFm_Uf
z?)X8A*vo2l64Xi&zhC19UBbSUvlvIy!6ttF@q%6>ywE%ATXoV&YKWI{f-d5B2NN>?
z@RK@e9KXKkpk>6nh{qtnyz=cZ%MM~GDyW+pqJW7Eg1?ljyN#%l3lF-9D3gg5gmQIl
z>>_T22dyF|F)@R*tD8o=2n)*KAx^1+G^;y{_{@>upgdv+qYF~3u7wifa&XWX;(w^yq^WtLsH<1qYQ8o%$ff>bel6
z;2;l?(g!J4mqzUUmqA0xg50ibBl*f)Uet5RFKU;!{ZNA2-uZ)ZL-}BwjrY^Bs1J%<
zmCPf=NN~^<#0OQ7tV;jm`a^KgQ9Q(HWzdHE*AK%h#FQ#1dQ~zrh!sp&Pyw;4c!eEO
zz2;XC*P~wYtB6}sL5fw$j37L^!h;MQX5?U5Zu7wDS@17!wuLkKbKP@F-R#GlAc^
z=pe%?qRI)Ue@dUBW?#^8f-c~N^RHaXc)&Kcffd&*&b
zkRi((Y*og|Dj$Or)Uf?Qrs1Q7x+K`)Hf=F~vqt#{bva#04qvi7r`|5poTVk?jylH-
z73w`Dlk|8h#U%SH@sI)o^wLF|G+p#EK!Hb;*xx-4yE(ucq(V|DvGR
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_preview_radio_off.webp b/coalMiningApp/matisse/src/main/res/drawable-hdpi/ic_preview_radio_off.webp
new file mode 100644
index 0000000000000000000000000000000000000000..80930b9a3851cd44287fc37cd9a93d090d5cde81
GIT binary patch
literal 290
zcmV+-0p0#mNk&E*0RRA3MM6+kP&iBt0RR9mAHW9?2QmNiKmQEdMp7KS>@hzx1k?or
z06c&P7@={);>Csy0DvD6z+eUNFaUxG0Vr?~Ns=PXF*DgNEx;IvhhPL~MI?WL$p}xGfHxQEfZJO2EZ#sT(E~K{
zi3WJ03OK`W3QwTwfk8ep0Q~~gUsVJ14isNi0Q(O}zA6EJYQ5^grr)AJk?Ol#K&Ah#
o>iU0Nk&FW0RRA3MM6+kP&iCJ0RR9mAHW9?FERh~KmUv)NopHq7SGQkuYkEA
z01yEpK!pXk;PGOi!2tm9BLM)11`8130E7$y6bKF?Nm8UfX2xgOFaIX@U|&H*{~5S#
zq-e6UOb4AuXosNemz(+Cmc9&hg0%av!>3D+YGL!`fNvA-dUD|emt@EDhjo+ZP-Rh+
zRm01=#v|X>Eq~X(Hu&}1N_I80MyFW2#FI5>EocBnPX~A-KqD#r0+Z!Mdo||T7FL>o
zZz^@bJC+poKP-NIC@Fpeoq`WgOA2Rpm_tc5@Ci!b=}YR_rI+MCfwBh%NyTs9eubm~
z=vSbU)N(*8sREWekV(28*;P^o+zk*(GDl`4MSyX(uXE$(KE-__r|mdKI;Vst078Ep6aWAK
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_check_white_18dp.png b/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_check_white_18dp.png
new file mode 100644
index 0000000000000000000000000000000000000000..ad1484767f791343581dfcdd35cfca4d0962c280
GIT binary patch
literal 146
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
P2`4eoG0`s9d>Z{9WSgOK
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_empty_zhihu.png b/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_empty_zhihu.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5628eee45059291eb92e29053378c59fb391fb0
GIT binary patch
literal 3843
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
E1Fvf!WdHyG
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_gif.png b/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_gif.png
new file mode 100644
index 0000000000000000000000000000000000000000..aeeb0d4353897d4728aa9489bbb8c53ad005c98f
GIT binary patch
literal 638
zcmeAS@N?(olHy`uVBq!ia0vp^3P3E(!3-pidHp{Mq*&4&eH|GXHuiJ>Nn{1`p9lDa
zxB_Wp;O6FLZEbC3Wd#&uW@ct%W8>!L78Dc|7Z>N?-~citB_(BKWK>jCw6wHzb#)aL
z6?JrUOifMA&CP+jY;0_toSfX<-90@${r&v|0|SGDgTunYVq;^Il9CDv3W|z~%F4ILxK7HoQnX_llUbt}K-Me>h-@d(i_3Cx&)@|6ZVfXIc
z=gytGaN)wun>T;|{{8ahOCb5~-MbGTKK%Xr7f62j^5xsNZ{NRv2a-R);3pUWl>q@z
z&Hw-Z`-90_a=2!;;4SwnA;uxZFzIM{fphE^Ct(TXz
zb}yLbD-a@(qRDluDP-fRM@)w*|36n$J#eA%+3$%bQ}&&lYx&*a*o%lMCjuW-NRB
zSY@B|^%enXmdKI;Vst0CDpou>b%7
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_play_circle_outline_white_48dp.png b/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_play_circle_outline_white_48dp.png
new file mode 100644
index 0000000000000000000000000000000000000000..615b80d08543f8285c7ac5ffd66435bf0da1b7ff
GIT binary patch
literal 699
zcmV;s0!00ZP)9ER~}Ocb>nNr>2151>$#LL?rDP#4l7hMYi)F$bWnTZ;s2>AO;FAR7@pi(*JO
z#wdzz)U=@XHOb~`;O1G)$1@Y!MZw>_4PNj!84~BgzknDs6sb_7NvFFeH7YEVC5B_0
z?FAF8ae<$6-ZS2NAk8KnY}%Aa^%9up2Vpgtixx=m8Ic>dSR_M|{_av_$g#~WA|>Ks
z14Ha%z#$6^;{Ccs>KL%cP?$i1eT@HkOB^R)36{7AlOS#ne8Q;n0>?fkX<(FWfq9H?
zq;W!Slmm=cL4hCF$h139M7D5(
z0+v(lAO&}zf-D9HET_sNWp|*4WP$^hQ)Q8=JJ3Xu!2!#u(#VxN&_Vix1C~<_AniXD
zcpW~_L{gyxwP+`b`cM86^`Bfpa`u2u6ZLJnjBHy2I*s?fB1`T-7P<9)6j)B9J?0L{
zcs=aSgFefy(*zyp)azmA@Byil@CpQ+M(gJoS;6rNjMD*gLDzy#qZN@BPuu~mgxoWU
z6LBwTA?rAPffP-o!6;6Ijd6lpGU6T3<}eN-B*v&>%-X(ql`tAig*?s)#z!1mAkH4f
zJp~e$cTSNO#x8x~zWgd7N8}jx`B>#S28>+>!+w2_Q$pmH9r9#JGtga{EP2Y@Vdyx0
z(SCr;aZOm4%=YpVD#-?IY+9@{()*9VXRLCDpHo)!skHK+2t{RV2URfJy|Ab$V=002ovPDHLkV1oZnH8lVL
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_preview_radio_off.webp b/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_preview_radio_off.webp
new file mode 100644
index 0000000000000000000000000000000000000000..1a8f6c181bd65a6bf17999e7e8310f9e4fcc1367
GIT binary patch
literal 410
zcmV;L0cHMDNk&GJ0RRA3MM6+kP&iD60RR9mFTe{BM=}5NKmQEdMpB%+fn#`vKsX@?
z5k$d`3}S%*gbW~~f&g9sKVB3h7z@UQj0S-K2*5A^z(FKQiqywU&&=dsW6$1hzk!JU
zGjQ9sQQUFU35u*YU>?b^Ih?QW_w{_(3<*M!O}ppMiRW(0Lavha?dO5p+WYfpdHH$b
zvW&cDe*6XCn0w61JoSE9&Cc=VI;ickEz-1Tms{5{zQ(!vwFd3G7!JhU-rDnZkIG)3
zYRsZO*-O{U-2Cxb!3(R8`60JjjiP%GPxN=A$0%KzS;8Bo8B6vie)j1Rfd0de5&&4U
z(JKUD#kQ>hWH(lXK(L-$225;n9V#_jHvv0#4+Mi9W`~61tP7Wp<1iWcK;m$T`FMv7
zF>N8hmiRUYVp@cX%bN~oVrmr965shsOoc)U;=8_yDOE^Hd~dzzZ}guq(NajH_Y=Jz
z>it|ENBTI`=K*=1(dRK)o_w%nOIaQ^^BBqVyu6Oc>y*3>%ImDWj?4Q5bszHIK1be1
E0d``_;{X5v
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_preview_radio_on.webp b/coalMiningApp/matisse/src/main/res/drawable-mdpi/ic_preview_radio_on.webp
new file mode 100644
index 0000000000000000000000000000000000000000..b336c9c6ea2b9d952c9516bd8c580ca0feed8c41
GIT binary patch
literal 486
zcmVMNk&HA0RRA3MM6+kP&iD|0RR9mFTe{Bk1_xAKmUr`wsC_e`+C3M1;Pcx
zNDwLvVW2?R5P(qtgoFkGOc({=W1*oUBVr@sK`7`j1ONbtFaQ7!+elKRxqD_lhG(YD
zKfUg~`S}DQ`maE3+h|!~cf;uQ25ClXk#CRZ*Xw-T=8Km6iq5)vI4>U6y7SAZm0g?y
zmrQ*;h)zz4QxvGAyK_Ha*Hy@fR@IB6Mp-?5A5})vLAyPeMy2l?pQ|SMGNF1FiU;AW
zWBc7&719q~=(1K)be)S5^DF5gr4h+c)EH8a(3)`V;~Dqk
z?cshuY~#LX_`xNxunbA_jb8C~gm=;73rHjrkpxuT|yf8yQY%TCZ(D(|cLV+fU-@C}+r4qHi
zb?uExOCXVN;iU~wiJcO$*P6CRcxeJuw%WD@Qd8h?Ji|)^pmNZ*1CSa3hw}?wY6F$K
zw%vhL8#uh|oBhQ82y%-IAF{U-dpoqZb9+DfgP+>R0rNOxAIFU1B>hcf7Kcf8fq9%a
c&m-n}$~+I6=UMwaZeAxC*CBtdbFAwq0h#XQv;Y7A
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-xhdpi/ic_arrow_drop_down_white_24dp.png b/coalMiningApp/matisse/src/main/res/drawable-xhdpi/ic_arrow_drop_down_white_24dp.png
new file mode 100644
index 0000000000000000000000000000000000000000..da239e4f4e0169d670679e8af8c86c5c4fffa7ca
GIT binary patch
literal 120
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
S42-GR13pM9NR*2i1^y4^lequ@
literal 0
HcmV?d00001
diff --git a/coalMiningApp/matisse/src/main/res/drawable-xhdpi/ic_empty_zhihu.png b/coalMiningApp/matisse/src/main/res/drawable-xhdpi/ic_empty_zhihu.png
new file mode 100644
index 0000000000000000000000000000000000000000..c223b5691fede910722565a39c0ad54f5837da53
GIT binary patch
literal 7977
zcmeHsRa9Hww{FNUl#~`L*5XjCK?}tx1S!(u?oyx>3GN!CMT)ya3l#SfG*Da{C=Nl2
zI}|7A$#0DRdAtwzj{9&P&K_fry&mTL*7wc5*IaY%6``ggPk>K_4*&oN6cuDO0010@
ze{b*u002N1QVGXCa9lOyr2&;Fnr#4pZdFlMO3MppKijvQM%Ta3`rMt~*?!YpAww9{
zFCaV}vHB;W_v=FvuB76mlu}3>LgPj7*thQ*EOVu!2A|pt$sxZfBINU0GZ26Ah{++z
zDa8mIM+-3&tlAL{3JV1&t~u>r&n$@C-k$sHA$_e;!w0B6)T(P`f%#($5
zS5%fW4G_ctzyU)6Kw>!He~16`;$X$_C*^B`e(~!3ywR;U&wNb=u3j?<3#h!BbuA)B%%4
zVF~kwY0!e#BCv$n9L+gE_;PUd>L}44``)PWA`1S0qtk3=+nJt|)gM;FQvU>Juyobx
zwa=>6WMkHhmEHu`Zq5n?=Pq5R1eh!h3um^eApo|_#=^N1nbU*tm9YA&T}!AywJHFd
z!RD}PA|Ng+Ho!AYS_D9SHaNg)L1qRo2uKQrhfJG(Oi|L0@W)74egUtKMVCB{
z1{4M-EBON#R#p#XLj9iDlQ5uyCcvPGSktFWYl-X^6gZHEd(ASWvfqs~31hCTcLP|>
z+dtRf*J^mIsqnuhn^=>gCCxH-$(2>=ATWk`u;C4SaPwrsRy_RjvX?_@VdRdf`VUS!i7IR45s`_WOENI5uK!_P1CUtvcGuo{jZm9F1S
zDIr99mH0mxo}nve|E
zUS0|o^>mKp!mhAO`}7%`j`SP>it?*?cr1|sj5R>Sew+~wDC`nAGLoMh)e2SN;Sy_x
z`A%`3kuza$LS_S4aihyYmikm7ih
z3lx*f`2c4^_oYKS`z>81fl12k!ZEvUi_95)=Pt&px4@kH=#L96qc<+660vUrejy
ztu&($Uj)(5ohRPrsc)VlfP+7!#FKE?K?kRW#E1X
z>5@)Q^ozP{{`#gQxwth}TH6uAWkc=Cw7L}M^WxDxR)hN5sh
zJr<6V+I|@9qrO~d47QQYs;ErRQm_mm%CHXl9?@+WSMS1fD^h+(!wLf_INt$_=vV#&
zhT#xs{KKSM{H^g?CyZ+|4b6{8OyQX7Y5wMFqra+_SZ<0cR=-e?#-q_nyULr|pQHDq
zPWH=dLxn_FMl|UU7d+XlK5nG^Q*SUiJDf2}A^khdR)G6&{)=cKxf0xuty}wryRK%F
z52@m^pU9u6!1=`w>AC~JYuK!tWU*;33OZIX>*7pp)n$Ki9h)tT5!EMIo#g9sk6ptg
zoM18z6^c$nHrxB|)~0sth&Xyxq`Kpud=xG@PcqGxapYOza}z4sTZ!7s6A`&fL+8+B1O`F8KJMuR2nGmrRAh^_GW+S
zf*&jV6LQG@b_xBE5Gw8?FGjjJ&jHXz1@k%cPw~0tjX#i?@v2E)ZOM7UYK0Viaa~jwF9vS
zPj_^gw)9kJSswXZ42BoeeEvy=vzm2ebO^ilXy3A6sKIWnoPLC~B6o(z$DNYvIzzw+
znPpHE=I3~9i|2xK-|$