Bläddra i källkod

上传视频与发布发现功能

zengjiebin 7 år sedan
förälder
incheckning
72ad6e6311
100 ändrade filer med 9880 tillägg och 0 borttagningar
  1. 1 0
      .idea/gradle.xml
  2. 1 0
      RxGalleryFinal/.gitignore
  3. 41 0
      RxGalleryFinal/build.gradle
  4. 18 0
      RxGalleryFinal/proguard-rules.pro
  5. 3 0
      RxGalleryFinal/src/main/AndroidManifest.xml
  6. 358 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/Configuration.java
  7. 318 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/RxGalleryFinal.java
  8. 599 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/RxGalleryFinalApi.java
  9. 26 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/anim/Animation.java
  10. 17 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/anim/AnimationListener.java
  11. 180 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/anim/SlideInUnderneathAnimation.java
  12. 177 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/anim/SlideOutUnderneathAnimation.java
  13. 69 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/bean/BucketBean.java
  14. 81 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/bean/ImageCropBean.java
  15. 283 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/bean/MediaBean.java
  16. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/bean/package-info.java
  17. 25 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/imageloader/AbsImageLoader.java
  18. 27 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/imageloader/GlideImageLoader.java
  19. 10 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/imageloader/ImageLoaderType.java
  20. 36 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/imageloader/rotate/RotateTransformation.java
  21. 19 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/MediaBucketFactoryInteractor.java
  22. 23 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/MediaSrcFactoryInteractor.java
  23. 69 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/impl/MediaBucketFactoryInteractorImpl.java
  24. 67 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/impl/MediaSrcFactoryInteractorImpl.java
  25. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/impl/package-info.java
  26. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/package-info.java
  27. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/package-info.java
  28. 17 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/presenter/MediaGridPresenter.java
  29. 70 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/presenter/impl/MediaGridPresenterImpl.java
  30. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/presenter/impl/package-info.java
  31. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/presenter/package-info.java
  32. 167 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/RxBus.java
  33. 36 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/RxBusDisposable.java
  34. 11 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/RxBusResultDisposable.java
  35. 9 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/BaseResultEvent.java
  36. 9 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/CloseMediaViewPageFragmentEvent.java
  37. 9 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/CloseRxMediaGridPageEvent.java
  38. 22 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/ImageMultipleResultEvent.java
  39. 21 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/ImageRadioResultEvent.java
  40. 21 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/MediaCheckChangeEvent.java
  41. 31 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/MediaViewPagerChangedEvent.java
  42. 28 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/OpenMediaPageFragmentEvent.java
  43. 9 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/OpenMediaPreviewFragmentEvent.java
  44. 29 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/RequestStorageReadAccessPermissionEvent.java
  45. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/package-info.java
  46. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/package-info.java
  47. 48 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/Job.java
  48. 10 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/JobCreator.java
  49. 70 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/JobManager.java
  50. 31 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/RxJob.java
  51. 42 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/job/ImageThmbnailJob.java
  52. 31 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/job/ImageThmbnailJobCreate.java
  53. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/job/package-info.java
  54. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/package-info.java
  55. 36 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/RxGalleryListener.java
  56. 114 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/activity/BaseActivity.java
  57. 445 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/activity/MediaActivity.java
  58. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/activity/package-info.java
  59. 151 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/BucketAdapter.java
  60. 235 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/MediaGridAdapter.java
  61. 67 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/MediaPreviewAdapter.java
  62. 270 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/RecyclingPagerAdapter.java
  63. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/package-info.java
  64. 11 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/base/IMultiImageCheckedListener.java
  65. 18 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/base/IRadioImageCheckedListener.java
  66. 222 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/BaseFragment.java
  67. 901 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/MediaGridFragment.java
  68. 213 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/MediaPageFragment.java
  69. 180 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/MediaPreviewFragment.java
  70. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/package-info.java
  71. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/package-info.java
  72. 107 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/FixImageView.java
  73. 30 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/FixViewPager.java
  74. 434 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/FlexibleDividerDecoration.java
  75. 88 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/FooterAdapter.java
  76. 174 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/HorizontalDividerItemDecoration.java
  77. 22 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/MarginDecoration.java
  78. 280 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/RecyclerViewFinal.java
  79. 29 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/SquareImageView.java
  80. 27 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/SquareLinearLayout.java
  81. 31 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/SquareRelativeLayout.java
  82. 4 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/package-info.java
  83. 270 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/BitmapUtils.java
  84. 21 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/CameraUtils.java
  85. 21 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/DeviceUtils.java
  86. 35 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/EmptyViewUtils.java
  87. 15 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/FileUtils.java
  88. 1060 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/FilenameUtils.java
  89. 44 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/IOUtils.java
  90. 46 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/Logger.java
  91. 86 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/MediaScanner.java
  92. 16 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/MediaType.java
  93. 439 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/MediaUtils.java
  94. 32 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/ModelUtils.java
  95. 23 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/OsCompat.java
  96. 82 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/PermissionCheckUtils.java
  97. 17 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/SimpleDateUtils.java
  98. 170 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/StorageUtils.java
  99. 189 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/ThemeUtils.java
  100. 0 0
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/package-info.java

+ 1 - 0
.idea/gradle.xml

@@ -8,6 +8,7 @@
         <option name="modules">
           <set>
             <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/RxGalleryFinal" />
             <option value="$PROJECT_DIR$/WaterWaveProgress" />
             <option value="$PROJECT_DIR$/app" />
             <option value="$PROJECT_DIR$/ucrop" />

+ 1 - 0
RxGalleryFinal/.gitignore

@@ -0,0 +1 @@
+/build

+ 41 - 0
RxGalleryFinal/build.gradle

@@ -0,0 +1,41 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion ANDROID_COMPILE_SDK_VERSION as int
+    buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
+
+    defaultConfig {
+        minSdkVersion ANDROID_MIN_SDK_VERSION as int
+        targetSdkVersion ANDORID_TARGET_SDK_VERSION as int
+        versionCode VERSION_CODE as int
+        versionName VERSION_NAME
+    }
+
+    lintOptions {
+        abortOnError false
+    }
+
+    dexOptions {
+        preDexLibraries = false
+    }
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
+}
+
+
+
+dependencies {
+    compileOnly "com.android.support:appcompat-v7:$supportLibVersion"
+    compileOnly "com.android.support:recyclerview-v7:$supportLibVersion"
+    compileOnly ('com.github.bumptech.glide:glide:4.1.1')
+    api "com.android.support:exifinterface:$supportLibVersion"
+    api project(':ucrop')
+    compileOnly 'io.reactivex.rxjava2:rxandroid:2.1.0'
+    compileOnly 'io.reactivex.rxjava2:rxjava:2.2.3'
+}
+

+ 18 - 0
RxGalleryFinal/proguard-rules.pro

@@ -0,0 +1,18 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/pengjianbo  Dujinyang  Dujinyang/Documents/dev/android_dev/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+-dontwarn java.lang.invoke.*

+ 3 - 0
RxGalleryFinal/src/main/AndroidManifest.xml

@@ -0,0 +1,3 @@
+<manifest package="cn.finalteam.rxgalleryfinal">
+
+</manifest>

+ 358 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/Configuration.java

@@ -0,0 +1,358 @@
+package cn.finalteam.rxgalleryfinal;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.IntRange;
+
+import com.yalantis.ucrop.UCropActivity;
+import com.yalantis.ucrop.model.AspectRatio;
+import com.yalantis.ucrop.view.CropImageView;
+import com.yalantis.ucrop.view.OverlayView;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.imageloader.AbsImageLoader;
+import cn.finalteam.rxgalleryfinal.imageloader.GlideImageLoader;
+
+/**
+ * Desction:配置信息
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/7 下午3:58
+ */
+public class Configuration implements Parcelable {
+
+    public static final Creator<Configuration> CREATOR = new Creator<Configuration>() {
+        @Override
+        public Configuration createFromParcel(Parcel in) {
+            return new Configuration(in);
+        }
+
+        @Override
+        public Configuration[] newArray(int size) {
+            return new Configuration[size];
+        }
+    };
+    private boolean image = true;
+    private Context context;
+    private List<MediaBean> selectedList;
+    private boolean radio;
+    private boolean crop;
+    private int maxSize = 1;
+
+    private int imageLoaderType;
+    private int imageConfig;
+    private boolean hideCamera;
+    private boolean isPlayGif;
+    private boolean hidePreview;
+    private boolean isVideoPreview;
+
+    //==========UCrop START==========
+    //是否隐藏裁剪页面底部控制栏,默认显示
+    private boolean hideBottomControls;
+    //图片压缩质量,默认不压缩
+    private int compressionQuality = 90;
+    //手势方式,默认all
+    private int[] gestures;
+    //设置图片最大值,默认根据屏幕得出
+    private int maxBitmapSize = CropImageView.DEFAULT_MAX_BITMAP_SIZE;
+    //设置最大缩放值,默认10.f
+    private float maxScaleMultiplier = CropImageView.DEFAULT_MAX_SCALE_MULTIPLIER;
+    //宽高比
+    private float aspectRatioX;
+    private float aspectRatioY;
+    //等比缩放默认值索引,默认原图比例
+    private int selectedByDefault;
+    //等比缩放值表,默认1:1,3:4,原图比例,3:2,16:9
+    private AspectRatio[] aspectRatio;
+    //是否允许改变裁剪大小
+    private boolean freestyleCropEnabled = false;//OverlayView.DEFAULT_FREESTYLE_CROP_ENABLED;
+    //是否显示裁剪框半透明椭圆浮层
+    private boolean ovalDimmedLayer = OverlayView.DEFAULT_CIRCLE_DIMMED_LAYER;//DEFAULT_OVAL_DIMMED_LAYER
+    private int maxResultWidth;
+    private int maxResultHeight;
+
+    //==========UCrop END==========
+
+    protected Configuration() {
+    }
+
+    protected Configuration(Parcel in) {
+        image = in.readByte() != 0;
+        selectedList = in.createTypedArrayList(MediaBean.CREATOR);
+        radio = in.readByte() != 0;
+        crop = in.readByte() != 0;
+        maxSize = in.readInt();
+        hideBottomControls = in.readByte() != 0;
+        compressionQuality = in.readInt();
+        gestures = in.createIntArray();
+        maxBitmapSize = in.readInt();
+        maxScaleMultiplier = in.readFloat();
+        aspectRatioX = in.readFloat();
+        aspectRatioY = in.readFloat();
+        selectedByDefault = in.readInt();
+        aspectRatio = in.createTypedArray(AspectRatio.CREATOR);
+        freestyleCropEnabled = in.readByte() != 0;
+        ovalDimmedLayer = in.readByte() != 0;
+        maxResultWidth = in.readInt();
+        maxResultHeight = in.readInt();
+        imageLoaderType = in.readInt();
+        imageConfig = in.readInt();
+        hideCamera = in.readByte() != 0;
+        isPlayGif = in.readByte() != 0;
+        hidePreview = in.readByte() != 0;
+        isVideoPreview = in.readByte() != 0;
+    }
+
+    public boolean isHidePreview() {
+        return hidePreview;
+    }
+
+    public void setHidePreview(boolean hidePreview) {
+        this.hidePreview = hidePreview;
+    }
+
+    public boolean isPlayGif() {
+        return isPlayGif;
+    }
+
+    public void setPlayGif(boolean playGif) {
+        isPlayGif = playGif;
+    }
+
+    public boolean isImage() {
+        return image;
+    }
+
+    protected void setImage(boolean image) {
+        this.image = image;
+    }
+
+    public Context getContext() {
+        return context;
+    }
+
+    protected void setContext(Context context) {
+        this.context = context;
+    }
+
+    public List<MediaBean> getSelectedList() {
+        return selectedList;
+    }
+
+    protected void setSelectedList(List<MediaBean> selectedList) {
+        this.selectedList = selectedList;
+    }
+
+    public boolean isRadio() {
+        return radio;
+    }
+
+    protected void setRadio(boolean radio) {
+        this.radio = radio;
+    }
+
+    public int getMaxSize() {
+        return maxSize;
+    }
+
+    protected void setMaxSize(int maxSize) {
+        this.maxSize = maxSize;
+    }
+
+    public boolean isHideCamera() {
+        return hideCamera;
+    }
+
+    public void setHideCamera(boolean hideCamera) {
+        this.hideCamera = hideCamera;
+    }
+
+    //#ADD
+    public int getImageLoaderType() {
+        return imageLoaderType;
+    }
+
+    protected void setImageLoaderType(int imageLoaderType) {
+        this.imageLoaderType = imageLoaderType;
+    }
+
+    public AbsImageLoader getImageLoader() {
+        return new GlideImageLoader();
+    }
+
+    public Bitmap.Config getImageConfig() {
+        switch (imageConfig) {
+            case 1:
+                return Bitmap.Config.ALPHA_8;
+            case 2:
+                return Bitmap.Config.ARGB_4444;
+            case 3:
+                return Bitmap.Config.ARGB_8888;
+            case 4:
+                return Bitmap.Config.RGB_565;
+        }
+        return Bitmap.Config.ARGB_8888;
+    }
+
+    public void setImageConfig(int imageConfig) {
+        this.imageConfig = imageConfig;
+    }
+
+    public boolean isHideBottomControls() {
+        return hideBottomControls;
+    }
+
+    public void setHideBottomControls(boolean hideBottomControls) {
+        this.hideBottomControls = hideBottomControls;
+    }
+
+    public int getCompressionQuality() {
+        return compressionQuality;
+    }
+
+    public void setCompressionQuality(int compressionQuality) {
+        this.compressionQuality = compressionQuality;
+    }
+
+    public int[] getAllowedGestures() {
+        return gestures;
+    }
+
+    public void setAllowedGestures(@UCropActivity.GestureTypes int[] gestures) {
+        this.gestures = gestures;
+    }
+
+    public int getMaxBitmapSize() {
+        return maxBitmapSize;
+    }
+
+    public void setMaxBitmapSize(int maxBitmapSize) {
+        this.maxBitmapSize = maxBitmapSize;
+    }
+
+    public float getMaxScaleMultiplier() {
+        return maxScaleMultiplier;
+    }
+
+    public void setMaxScaleMultiplier(float maxScaleMultiplier) {
+        this.maxScaleMultiplier = maxScaleMultiplier;
+    }
+
+    public float getAspectRatioX() {
+        return aspectRatioX;
+    }
+
+    public void setAspectRatioX(float aspectRatioX) {
+        this.aspectRatioX = aspectRatioX;
+    }
+
+    public float getAspectRatioY() {
+        return aspectRatioY;
+    }
+
+    public void setAspectRatioY(float aspectRatioY) {
+        this.aspectRatioY = aspectRatioY;
+    }
+
+    public void setAspectRatioOptions(int selectedByDefault, AspectRatio... aspectRatio) {
+        this.selectedByDefault = selectedByDefault;
+        this.aspectRatio = aspectRatio;
+    }
+
+    public int getSelectedByDefault() {
+        return selectedByDefault;
+    }
+
+    public void setSelectedByDefault(int selectedByDefault) {
+        this.selectedByDefault = selectedByDefault;
+    }
+
+    public AspectRatio[] getAspectRatio() {
+        return aspectRatio;
+    }
+
+    public void setAspectRatio(AspectRatio[] aspectRatio) {
+        this.aspectRatio = aspectRatio;
+    }
+
+    public boolean isFreestyleCropEnabled() {
+        return freestyleCropEnabled;
+    }
+
+    public void setFreestyleCropEnabled(boolean freestyleCropEnabled) {
+        this.freestyleCropEnabled = freestyleCropEnabled;
+    }
+
+    public boolean isOvalDimmedLayer() {
+        return ovalDimmedLayer;
+    }
+
+    public void setOvalDimmedLayer(boolean ovalDimmedLayer) {
+        this.ovalDimmedLayer = ovalDimmedLayer;
+    }
+
+    public boolean isCrop() {
+        return crop;
+    }
+
+    public void setCrop(boolean crop) {
+        this.crop = crop;
+    }
+
+    public void setMaxResultSize(@IntRange(from = 100) int width, @IntRange(from = 100) int height) {
+        this.maxResultWidth = width;
+        this.maxResultHeight = height;
+    }
+
+    public int getMaxResultHeight() {
+        return maxResultHeight;
+    }
+
+    public int getMaxResultWidth() {
+        return maxResultWidth;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int i) {
+        parcel.writeByte((byte) (image ? 1 : 0));
+        parcel.writeTypedList(selectedList);
+        parcel.writeByte((byte) (radio ? 1 : 0));
+        parcel.writeByte((byte) (crop ? 1 : 0));
+        parcel.writeInt(maxSize);
+        parcel.writeByte((byte) (hideBottomControls ? 1 : 0));
+        parcel.writeInt(compressionQuality);
+        parcel.writeIntArray(gestures);
+        parcel.writeInt(maxBitmapSize);
+        parcel.writeFloat(maxScaleMultiplier);
+        parcel.writeFloat(aspectRatioX);
+        parcel.writeFloat(aspectRatioY);
+        parcel.writeInt(selectedByDefault);
+        parcel.writeTypedArray(aspectRatio, i);
+        parcel.writeByte((byte) (freestyleCropEnabled ? 1 : 0));
+        parcel.writeByte((byte) (ovalDimmedLayer ? 1 : 0));
+        parcel.writeInt(maxResultWidth);
+        parcel.writeInt(maxResultHeight);
+        parcel.writeInt(imageLoaderType);
+        parcel.writeInt(imageConfig);
+        parcel.writeByte((byte) (hideCamera ? 1 : 0));
+        parcel.writeByte((byte) (isPlayGif ? 1 : 0));
+        parcel.writeByte((byte) (hidePreview ? 1 : 0));
+        parcel.writeByte((byte) (isVideoPreview ? 1 : 0));
+    }
+
+    public boolean isVideoPreview() {
+        return isVideoPreview;
+    }
+
+    public void setVideoPreview(boolean videoPreview) {
+        isVideoPreview = videoPreview;
+    }
+}

+ 318 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/RxGalleryFinal.java

@@ -0,0 +1,318 @@
+package cn.finalteam.rxgalleryfinal;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.annotation.FloatRange;
+import android.support.annotation.IntRange;
+import android.support.annotation.NonNull;
+import android.widget.Toast;
+
+import com.yalantis.ucrop.UCropActivity;
+import com.yalantis.ucrop.model.AspectRatio;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.imageloader.ImageLoaderType;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBus;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBusResultDisposable;
+import cn.finalteam.rxgalleryfinal.rxbus.event.BaseResultEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.ImageMultipleResultEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.ImageRadioResultEvent;
+import cn.finalteam.rxgalleryfinal.ui.activity.MediaActivity;
+import cn.finalteam.rxgalleryfinal.utils.Logger;
+import cn.finalteam.rxgalleryfinal.utils.ModelUtils;
+import cn.finalteam.rxgalleryfinal.utils.StorageUtils;
+import io.reactivex.disposables.Disposable;
+
+/**
+ * Desction: RxGalleryFinal
+ * author: pengjianbo  Dujinyang
+ * author: karl-dujinyang
+ */
+public class RxGalleryFinal {
+
+    private Configuration configuration = new Configuration();
+    private RxBusResultDisposable<BaseResultEvent> isRadioDisposable;
+
+    private RxGalleryFinal() {
+    }
+
+    public static RxGalleryFinal with(@NonNull Context context) {
+        RxGalleryFinal instance = new RxGalleryFinal();
+        instance.configuration.setContext(context.getApplicationContext());
+        return instance;
+    }
+
+    public RxGalleryFinal image() {
+        configuration.setImage(true);
+        return this;
+    }
+
+    public RxGalleryFinal video() {
+        configuration.setImage(false);
+        return this;
+    }
+
+//    public RxGalleryFinal filterMime(MediaType ...mediaTypes) {
+//        configuration.setFilterMimes(mediaTypes);
+//        return this;
+//    }
+
+    public RxGalleryFinal gif() {
+        configuration.setPlayGif(true);
+        return this;
+    }
+
+    public RxGalleryFinal hidePreview() {
+        configuration.setHidePreview(true);
+        return this;
+    }
+
+    public RxGalleryFinal videoPreview() {
+        configuration.setVideoPreview(true);
+        return this;
+    }
+
+    public RxGalleryFinal gif(boolean flag) {
+        configuration.setPlayGif(flag);
+        return this;
+    }
+
+    public RxGalleryFinal radio() {
+        configuration.setRadio(true);
+        return this;
+    }
+
+    public RxGalleryFinal multiple() {
+        configuration.setRadio(false);
+        return this;
+    }
+
+    public RxGalleryFinal crop() {
+        configuration.setCrop(true);
+        return this;
+    }
+
+    public RxGalleryFinal crop(boolean flag) {
+        configuration.setCrop(flag);
+        return this;
+    }
+
+    public RxGalleryFinal maxSize(@IntRange(from = 1) int maxSize) {
+        configuration.setMaxSize(maxSize);
+        return this;
+    }
+
+    public RxGalleryFinal selected(@NonNull List<MediaBean> list) {
+        configuration.setSelectedList(list);
+        return this;
+    }
+
+    public RxGalleryFinal imageConfig(@NonNull Bitmap.Config config) {
+        int c = 3;
+        switch (config) {
+            case ALPHA_8:
+                c = 1;
+                break;
+            case ARGB_4444:
+                c = 2;
+                break;
+            case ARGB_8888:
+                c = 3;
+                break;
+            case RGB_565:
+                c = 4;
+                break;
+        }
+        configuration.setImageConfig(c);
+        return this;
+    }
+
+    public RxGalleryFinal imageLoader(@NonNull ImageLoaderType imageLoaderType) {
+        int type = 0;
+        if (imageLoaderType == ImageLoaderType.PICASSO) {
+            type = 1;
+        } else if (imageLoaderType == ImageLoaderType.GLIDE) {
+            type = 2;
+        } else if (imageLoaderType == ImageLoaderType.FRESCO) {
+            type = 3;
+        } else if (imageLoaderType == ImageLoaderType.UNIVERSAL) {
+            type = 4;
+        }
+        configuration.setImageLoaderType(type);
+        return this;
+    }
+
+    /**
+     * 隐藏相机
+     */
+    public RxGalleryFinal hideCamera() {
+        configuration.setHideCamera(true);
+        return this;
+    }
+
+    /**
+     * set to true to hide the bottom controls (shown by default)
+     */
+    public RxGalleryFinal cropHideBottomControls(boolean hide) {
+        configuration.setHideBottomControls(hide);
+        return this;
+    }
+
+    /**
+     * Set compression quality [0-100] that will be used to save resulting Bitmap.
+     */
+    public RxGalleryFinal cropropCompressionQuality(@IntRange(from = 0) int compressQuality) {
+        configuration.setCompressionQuality(compressQuality);
+        return this;
+    }
+
+    /**
+     * Choose what set of gestures will be enabled on each tab - if any.
+     */
+    public RxGalleryFinal cropAllowedGestures(@UCropActivity.GestureTypes int tabScale,
+                                              @UCropActivity.GestureTypes int tabRotate,
+                                              @UCropActivity.GestureTypes int tabAspectRatio) {
+        configuration.setAllowedGestures(new int[]{tabScale, tabRotate, tabAspectRatio});
+        return this;
+    }
+
+    /**
+     * Setter for max size for both width and height of bitmap that will be decoded from an input Uri and used in the view.
+     *
+     * @param maxBitmapSize - size in pixels
+     */
+    public RxGalleryFinal cropMaxBitmapSize(@IntRange(from = 100) int maxBitmapSize) {
+        configuration.setMaxBitmapSize(maxBitmapSize);
+        return this;
+    }
+
+    /**
+     * This method sets multiplier that is used to calculate max image scale from min image scale.
+     *
+     * @param maxScaleMultiplier - (minScale * maxScaleMultiplier) = maxScale
+     */
+    public RxGalleryFinal cropMaxScaleMultiplier(@FloatRange(from = 1.0, fromInclusive = false) float maxScaleMultiplier) {
+        configuration.setMaxScaleMultiplier(maxScaleMultiplier);
+        return this;
+    }
+
+    /**
+     * Set an aspect ratio for crop bounds.
+     * User won't see the menu with other ratios options.
+     *
+     * @param x aspect ratio X
+     * @param y aspect ratio Y
+     */
+    public RxGalleryFinal cropWithAspectRatio(float x, float y) {
+        configuration.setAspectRatioX(x);
+        configuration.setAspectRatioY(y);
+        return this;
+    }
+
+    /**
+     * Set an aspect ratio for crop bounds that is evaluated from source image width and height.
+     * User won't see the menu with other ratios options.
+     */
+    public RxGalleryFinal cropUseSourceImageAspectRatio() {
+        configuration.setAspectRatioX(0);
+        configuration.setAspectRatioY(0);
+        return this;
+    }
+
+    /**
+     * Pass an ordered list of desired aspect ratios that should be available for a user.
+     *
+     * @param selectedByDefault - index of aspect ratio option that is selected by default (starts with 0).
+     * @param aspectRatio       - list of aspect ratio options that are available to user
+     */
+    public RxGalleryFinal cropAspectRatioOptions(int selectedByDefault, AspectRatio... aspectRatio) {
+        configuration.setSelectedByDefault(selectedByDefault);
+        configuration.setAspectRatio(aspectRatio);
+        return this;
+    }
+
+    /**
+     * set to true to let user resize crop bounds (disabled by default)
+     */
+    public RxGalleryFinal cropFreeStyleCropEnabled(boolean enabled) {
+        configuration.setFreestyleCropEnabled(enabled);
+        return this;
+    }
+
+    /**
+     * set it to true if you want dimmed layer to have an oval inside
+     */
+    public RxGalleryFinal cropOvalDimmedLayer(boolean isOval) {
+        configuration.setOvalDimmedLayer(isOval);
+        return this;
+    }
+
+    /**
+     * 设置裁剪结果最大宽度和高度
+     */
+    public RxGalleryFinal cropMaxResultSize(@IntRange(from = 100) int width, @IntRange(from = 100) int height) {
+        configuration.setMaxResultSize(width, height);
+        return this;
+    }
+
+    /**
+     * 设置回调
+     */
+    public RxGalleryFinal subscribe(@NonNull RxBusResultDisposable<? extends BaseResultEvent> rxBusResultSubscriber) {
+        this.isRadioDisposable = (RxBusResultDisposable<BaseResultEvent>) rxBusResultSubscriber;
+        return this;
+    }
+
+
+    public void openGallery() {
+        //提示
+        ModelUtils.logDebug();
+        execute();
+    }
+
+    /**
+     * 执行
+     */
+    private void execute() {
+        Context context = configuration.getContext();
+        if (context == null) {
+            return;
+        }
+        if (!StorageUtils.existSDcard()) {
+            Logger.i("没有找到SD卡");
+            Toast.makeText(context, "没有找到SD卡", Toast.LENGTH_SHORT).show();
+            return;
+        }
+
+        if (configuration.getImageLoader() == null) {
+            throw new NullPointerException("imageLoader == null , please check imageLoader");
+        }
+        if (isRadioDisposable == null) {
+            return;
+        }
+        Disposable disposable;
+        if (configuration.isRadio()) {
+            disposable = RxBus.getDefault()
+                    .toObservable(ImageRadioResultEvent.class)
+                    .subscribeWith(isRadioDisposable);
+        } else {
+            disposable = RxBus.getDefault()
+                    .toObservable(ImageMultipleResultEvent.class)
+                    .subscribeWith(isRadioDisposable);
+        }
+        RxBus.getDefault().add(disposable);
+
+        Intent intent = new Intent(context, MediaActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(MediaActivity.EXTRA_CONFIGURATION, configuration);
+        intent.putExtras(bundle);
+        context.startActivity(intent);
+    }
+
+
+}

+ 599 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/RxGalleryFinalApi.java

@@ -0,0 +1,599 @@
+package cn.finalteam.rxgalleryfinal;
+
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.support.v4.app.Fragment;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import com.yalantis.ucrop.UCrop;
+import com.yalantis.ucrop.UCropActivity;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Random;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.imageloader.ImageLoaderType;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBusResultDisposable;
+import cn.finalteam.rxgalleryfinal.rxbus.event.ImageMultipleResultEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.ImageRadioResultEvent;
+import cn.finalteam.rxgalleryfinal.ui.RxGalleryListener;
+import cn.finalteam.rxgalleryfinal.ui.base.IMultiImageCheckedListener;
+import cn.finalteam.rxgalleryfinal.ui.base.IRadioImageCheckedListener;
+import cn.finalteam.rxgalleryfinal.ui.fragment.MediaGridFragment;
+import cn.finalteam.rxgalleryfinal.utils.Logger;
+import cn.finalteam.rxgalleryfinal.utils.MediaScanner;
+import cn.finalteam.rxgalleryfinal.utils.SimpleDateUtils;
+import io.reactivex.functions.Function;
+
+/**
+ * 设置回调
+ * Created by KARL-dujinyang on 2017-03-23 03-03-00.
+ */
+public class RxGalleryFinalApi {
+    public static final int TAKE_IMAGE_REQUEST_CODE = 19001;
+    private static String IMG_TYPE = "image/jpeg";
+    public static File fileImagePath;//拍照图片
+    public static File cropImagePath;//裁剪图片
+    private static RxGalleryFinalApi mRxApi = new RxGalleryFinalApi();
+    private static RxGalleryFinal rxGalleryFinal;
+
+    /**
+     * 默认使用 ImageLoaderType.GLIDE
+     */
+    public static RxGalleryFinalApi getInstance(Activity context) {
+        if (context == null) {
+            throw new NullPointerException("context == null");
+        }
+        rxGalleryFinal = RxGalleryFinal.with(context)
+                .imageLoader(ImageLoaderType.GLIDE)
+                .subscribe(null);
+        Logger.i("==========" + mRxApi + "====" + rxGalleryFinal);
+        return mRxApi;
+    }
+
+    /**
+     * 单选图片
+     *
+     * @param flag 标识是否开启裁剪
+     * @Override protected void onEvent(ImageRadioResultEvent imageRadioResultEvent) throws Exception {
+     * <p>
+     * }
+     * }
+     * @see new RxBusResultSubscriber<ImageRadioResultEvent>() {
+     */
+    public static RxGalleryFinalApi openRadioSelectImage(Activity context, RxBusResultDisposable<ImageRadioResultEvent> rxBusResultDisposable, boolean flag) {
+        getInstance(context);
+        if (flag) {
+            rxGalleryFinal
+                    .image()
+                    .radio()
+                    .imageLoader(ImageLoaderType.GLIDE)
+                    .subscribe(rxBusResultDisposable)
+                    .openGallery();
+        } else {
+            rxGalleryFinal
+                    .image()
+                    .radio()
+                    .crop()
+                    .imageLoader(ImageLoaderType.GLIDE)
+                    .subscribe(rxBusResultDisposable)
+                    .openGallery();
+        }
+        return mRxApi;
+    }
+
+    /**
+     * 得到裁剪之后的事件
+     *
+     * @return RxGalleryFinalApi
+     */
+    public RxGalleryFinalApi onCropImageResult(IRadioImageCheckedListener listener) {
+        RxGalleryListener.getInstance().setRadioImageCheckedListener(listener);
+        return mRxApi;
+    }
+
+    /**
+     * 得到裁剪之后的事件
+     *
+     * @return RxGalleryFinalApi
+     */
+    public static RxGalleryFinalApi onMultiImageResult(IMultiImageCheckedListener listener) {
+        RxGalleryListener.getInstance().setMultiImageCheckedListener(listener);
+        return mRxApi;
+    }
+
+    /**
+     * 得到多选限制事件
+     */
+    public static RxGalleryFinalApi onCrop(boolean flag) {
+        if (rxGalleryFinal == null)
+            return null;
+        rxGalleryFinal.crop(flag);
+        return mRxApi;
+    }
+
+    /**
+     * 单选默认设置
+     */
+    public RxGalleryFinalApi openGalleryRadioImgDefault(RxBusResultDisposable<ImageRadioResultEvent> rxBusResultDisposable) {
+        Logger.i("----rxGalleryFinal---" + rxGalleryFinal);
+        if (rxGalleryFinal == null)
+            return null;
+        rxGalleryFinal
+                .image()
+                .radio()
+                .crop()
+                .imageLoader(ImageLoaderType.GLIDE)
+                .subscribe(rxBusResultDisposable)
+                .openGallery();
+        return mRxApi;
+    }
+
+    /**
+     * 得到多选限制事件
+     */
+    public static RxGalleryFinalApi openGallery() {
+        if (rxGalleryFinal == null)
+            return null;
+        rxGalleryFinal.openGallery();
+        return mRxApi;
+    }
+
+    /**
+     * 单选图片:默认开启全部
+     *
+     * @Override protected void onEvent(ImageRadioResultEvent imageRadioResultEvent) throws Exception {
+     * <p>
+     * }
+     * }
+     * @see new RxBusResultSubscriber<ImageRadioResultEvent>() {
+     */
+    public static void openRadioSelectImage(Activity context, RxBusResultDisposable rxBusResultDisposable) {
+        RxGalleryFinal
+                .with(context)
+                .image()
+                .radio()
+                .crop()
+                .imageLoader(ImageLoaderType.GLIDE)
+                .subscribe(rxBusResultDisposable)
+                .openGallery();
+    }
+
+    /**
+     * 多选图片:默认开启全部
+     *
+     * @Override protected void onEvent(ImageRadioResultEvent imageRadioResultEvent) throws Exception {
+     * <p>
+     * }
+     * }
+     * @see new RxBusResultSubscriber<ImageRadioResultEvent>() {
+     */
+    public static void openMultiSelectImage(Activity context, RxBusResultDisposable<ImageMultipleResultEvent> rxBusResultDisposable) {
+        RxGalleryFinal
+                .with(context)
+                .image()
+                .multiple()
+                .crop()
+                .imageLoader(ImageLoaderType.GLIDE)
+                .subscribe(rxBusResultDisposable)
+                .openGallery();
+    }
+
+    /**
+     * 多选图片:
+     *
+     * @Override protected void onEvent(ImageRadioResultEvent imageRadioResultEvent) throws Exception {
+     * <p>
+     * }
+     * }
+     * @see new RxBusResultSubscriber<ImageRadioResultEvent>() {
+     */
+    public static void openMultiSelectImage(Activity context, int maxSize, RxBusResultDisposable<ImageMultipleResultEvent> rxBusResultDisposable) {
+        RxGalleryFinal
+                .with(context)
+                .image()
+                .maxSize(maxSize)
+                .multiple()
+                .crop()
+                .imageLoader(ImageLoaderType.GLIDE)
+                .subscribe(rxBusResultDisposable)
+                .openGallery();
+    }
+
+    /**
+     * 单选视频:默认开启全部
+     *
+     * @Override protected void onEvent(ImageRadioResultEvent imageRadioResultEvent) throws Exception {
+     * <p>
+     * }
+     * }
+     * @see new RxBusResultSubscriber<ImageRadioResultEvent>() {
+     */
+    public static void openRadioSelectVD(Activity context, RxBusResultDisposable<ImageRadioResultEvent> rxBusResultDisposable) {
+        RxGalleryFinal
+                .with(context)
+                .multiple()
+                .video()
+                .imageLoader(ImageLoaderType.GLIDE)
+                .subscribe(rxBusResultDisposable)
+                .openGallery();
+    }
+
+    /**
+     * 多选视频 :默认开启全部
+     * 默认选9个视频
+     *
+     * @Override protected void onEvent(ImageRadioResultEvent imageRadioResultEvent) throws Exception {
+     * <p>
+     * }
+     * }
+     * @see new RxBusResultSubscriber<ImageRadioResultEvent>() {
+     */
+    public static void openMultiSelectVD(Activity context, RxBusResultDisposable<ImageMultipleResultEvent> rxBusResultDisposable) {
+        RxGalleryFinal
+                .with(context)
+                .video()
+                .multiple()
+                .maxSize(9)
+                .imageLoader(ImageLoaderType.UNIVERSAL)
+                .subscribe(rxBusResultDisposable)
+                .openGallery();
+    }
+
+    /**
+     * 打开相机
+     * <p>
+     * fragment 或者 activity 直接传入  this,  内部处理
+     * <p>
+     * 这里的 Fragment 指的是 v4包下的Fragment
+     *
+     * @see Fragment
+     * <p>
+     * M 以上的权限处理,拍照以及读取相册需要自行申请,这里不做处理
+     * <p>
+     * 返回值: -1 : 设备没有相机
+     */
+    public static int openZKCamera(Object activity) {
+
+        if (activity == null) {
+            throw new NullPointerException("activity == null");
+        }
+        Activity cameraActivity = null;
+        if (activity instanceof Activity) {
+            cameraActivity = (Activity) activity;
+        }
+        if (activity instanceof Fragment) {
+            Fragment fragment = (Fragment) activity;
+            cameraActivity = fragment.getActivity();
+        }
+        assert cameraActivity != null;
+        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        if (captureIntent.resolveActivity(cameraActivity.getPackageManager()) != null) {
+            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
+            String imageName = "immqy_%s.jpg";
+            String filename = String.format(imageName, dateFormat.format(new Date()));
+            File mImageStoreDir = new File(Environment.getExternalStorageDirectory(), "/DCIM/IMMQY/");
+            if (!mImageStoreDir.exists()) {
+                mImageStoreDir.mkdirs();
+            }
+            fileImagePath = new File(mImageStoreDir, filename);
+            String mImagePath = fileImagePath.getAbsolutePath();
+            Logger.i("->mImagePath:" + mImagePath);
+            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(fileImagePath));
+            } else {
+                ContentValues contentValues = new ContentValues(1);
+                contentValues.put(MediaStore.Images.Media.DATA, mImagePath);
+                contentValues.put(MediaStore.Images.Media.MIME_TYPE, IMG_TYPE);
+                Uri uri = cameraActivity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
+                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
+            }
+            if (activity instanceof Activity) {
+                cameraActivity.startActivityForResult(captureIntent, TAKE_IMAGE_REQUEST_CODE);
+            }
+            if (activity instanceof Fragment) {
+                Fragment fragment = (Fragment) activity;
+                fragment.startActivityForResult(captureIntent, TAKE_IMAGE_REQUEST_CODE);
+            }
+            return 0;
+        } else {
+            Toast.makeText(cameraActivity, R.string.gallery_device_camera_unable, Toast.LENGTH_SHORT).show();
+            return -1;
+        }
+    }
+
+    /**
+     * 处理拍照返回
+     */
+    public static void openZKCameraForResult(Activity context, MediaScanner.ScanCallback mediaScanner) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            MediaScanner scanner = new MediaScanner(context);
+            scanner.scanFile(RxGalleryFinalApi.fileImagePath.getPath(), IMG_TYPE, mediaScanner);
+        } else {
+            ContentValues values = new ContentValues();
+            values.put(MediaStore.Images.ImageColumns.TITLE, "title");
+            values.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, "filename.jpg");
+            values.put(MediaStore.Images.ImageColumns.DATE_TAKEN, System.currentTimeMillis());
+            values.put(MediaStore.Images.ImageColumns.MIME_TYPE, IMG_TYPE);
+            values.put(MediaStore.Images.ImageColumns.ORIENTATION, 0);
+            values.put(MediaStore.Images.ImageColumns.DATA, RxGalleryFinalApi.fileImagePath.getPath());
+            try {
+                Uri uri = context.getContentResolver().insert(
+                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+                if (uri == null) {
+                    Logger.e("Failed to insert MediaStore");
+                } else {
+                    context.sendBroadcast(new Intent(
+                            "com.android.camera.NEW_PICTURE", uri));
+                }
+            } catch (Exception e) {
+                Logger.e("Failed to write MediaStore" + e);
+            }
+            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + RxGalleryFinalApi.fileImagePath.getPath())));
+        }
+    }
+
+    /**
+     * 快速生成图片的路径
+     *
+     * @return 虚拟路径
+     */
+    public static String getModelPath() {
+        File fileImagePath = null;
+        try {
+            String imageName = "immqy_%s_%s.jpg";
+            Random random = new Random();
+            String filename = String.format(imageName, SimpleDateUtils.getNowTime(), "" + random.nextInt(1024));
+            File mImageStoreDir = new File(Environment.getExternalStorageDirectory(), "/DCIM/IMMQY/");
+          /*  if(!mImageStoreDir.exists()){
+                mImageStoreDir.mkdirs();
+            }*/
+            fileImagePath = new File(mImageStoreDir, filename);
+            //mImagePath = fileImagePath.getPath();
+            Logger.i("Test Path:" + fileImagePath.getPath());
+        } catch (Exception e) {
+            e.printStackTrace();
+            Logger.e("e=>" + e.getMessage());
+        }
+        return fileImagePath.getPath();
+    }
+
+    /**
+     * 裁剪指定的路径-
+     * onActivityResult或者其它地方调用RxGalleryFinalApi.cropActivityForResult方法去刷新图库
+     * RxGalleryFinalApi.cropActivityForResult()
+     */
+    public static void cropScannerForResult(Activity context, String outPPath, String inputPath) {
+        if (TextUtils.isEmpty(inputPath)) {
+            Logger.e("-裁剪没有图片-");
+            return;
+        }
+        Uri outUri = Uri.fromFile(new File(outPPath));
+        Uri inputUri = Uri.fromFile(new File(inputPath));
+        Intent intent = new Intent(context, UCropActivity.class);
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(UCrop.EXTRA_OUTPUT_URI, outUri);
+        bundle.putParcelable(UCrop.EXTRA_INPUT_URI, inputUri);
+        cropImagePath = new File(outUri.getPath());
+        Logger.i("输出:" + outUri.getPath());
+        Logger.i("原图:" + inputUri.getPath());
+        intent.putExtras(bundle);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivityForResult(intent, -1);//无效
+    }
+
+    /**
+     * 扫描指定的图片路径--刷新图库
+     */
+    public static void cropActivityForResult(Activity context, MediaScanner.ScanCallback imgScanner) {
+        if (cropImagePath != null) {
+            MediaScanner scanner = new MediaScanner(context);
+            scanner.scanFile(RxGalleryFinalApi.cropImagePath.getPath(), IMG_TYPE, imgScanner);
+        }
+    }
+
+    /**
+     * 扫描指定的图片路径--刷新图库
+     *
+     * @param path 路径
+     */
+    public static void cropActivityForResult(Activity context, String path, MediaScanner.ScanCallback imgScanner) {
+        if (cropImagePath != null) {
+            MediaScanner scanner = new MediaScanner(context);
+            scanner.scanFile(path.trim(), IMG_TYPE, imgScanner);
+        }
+    }
+
+    /**
+     * 设置图片存储的路径
+     *
+     * @param file 返回原来的File
+     */
+    public static File setImgSaveRxDir(File file) {
+        MediaGridFragment.setImageStoreDir(file);
+        return file;
+    }
+    //***********************************************************************//
+
+    /**
+     * 设置图片存储到sd卡 -- 取文件夹名称
+     */
+    public static void setImgSaveRxSDCard(String name) {
+        MediaGridFragment.setImageStoreDir(name);
+    }
+
+    /**
+     * 设置裁剪存储的路径
+     *
+     * @param file 返回原来的File
+     */
+    public static File setImgSaveRxCropDir(File file) {
+        MediaGridFragment.setImageStoreCropDir(file);
+        return file;
+    }
+
+    /**
+     * 设置裁剪存储到sd卡 -- 取文件夹名称
+     */
+    public static void setImgSaveRxCropSDCard(String name) {
+        MediaGridFragment.setImageStoreCropDir(name);
+    }
+
+    /**
+     * 获取裁剪存储的路径
+     */
+    public static String getImgSaveRxCropDirByStr() {
+        return MediaGridFragment.getImageStoreCropDirByStr();
+    }
+
+    /**
+     * 获取裁剪存储的路径
+     *
+     * @return String
+     */
+    public static File getImgSaveRxCropDirByFile() {
+        return MediaGridFragment.getImageStoreCropDirByFile();
+    }
+
+    /**
+     * 获取图片存储的路径
+     *
+     * @return String 路径
+     */
+    public static String getImgSaveRxDirByStr() {
+        return MediaGridFragment.getImageStoreDirByStr();
+    }
+
+    /**
+     * 获取图片存储的路径
+     *
+     * @return File 路径
+     */
+    public static File getImgSaveRxDirByFile() {
+        return MediaGridFragment.getImageStoreDirByFile();
+    }
+
+    /**
+     * 设置打开的类型和方式--多选默认9张图
+     * setType(SelectRXType.TYPE_IMAGE,SelectRXType.TYPE_SELECT_RADIO);
+     * setType(SelectRXType.TYPE_VIDEO,SelectRXType.TYPE_SELECT_RADIO);
+     *
+     * @param type 图片或者视频
+     * @param mt   单选或者多选 .多选默认9张图
+     */
+    public RxGalleryFinalApi setType(int type, int mt) {
+        switch (type) {
+            case SelectRXType.TYPE_IMAGE:
+                rxGalleryFinal.image();
+                break;
+            case SelectRXType.TYPE_VIDEO:
+                rxGalleryFinal.video();
+                break;
+            default:
+                Logger.e("open type is error!!!");
+                break;
+        }
+        switch (mt) {
+            case SelectRXType.TYPE_SELECT_RADIO:
+                rxGalleryFinal.radio();
+                break;
+            case SelectRXType.TYPE_SELECT_MULTI:
+                rxGalleryFinal.multiple();
+                rxGalleryFinal.maxSize(9);
+                break;
+            default:
+                Logger.e("open mt is error!!!");
+                break;
+        }
+        return mRxApi;
+    }
+
+    /**
+     * 设置单选的按钮事件
+     */
+    public RxGalleryFinalApi setImageRadioResultEvent(RxBusResultDisposable<ImageRadioResultEvent> t) {
+        rxGalleryFinal.image();
+        rxGalleryFinal.subscribe(t);
+        return mRxApi;
+    }
+
+    /**
+     * 设置多选的按钮事件
+     */
+    public RxGalleryFinalApi setImageMultipleResultEvent(RxBusResultDisposable<ImageMultipleResultEvent> t) {
+        rxGalleryFinal.image();
+        rxGalleryFinal.subscribe(t);
+        return mRxApi;
+    }
+
+    /**
+     * 设置视频单选的按钮事件
+     */
+    public RxGalleryFinalApi setVDRadioResultEvent(RxBusResultDisposable<ImageRadioResultEvent> t) {
+        rxGalleryFinal.video();
+        rxGalleryFinal.subscribe(t);
+        return mRxApi;
+    }
+
+    /**
+     * 设置视频多选的按钮事件
+     */
+    public RxGalleryFinalApi setVDMultipleResultEvent(RxBusResultDisposable<ImageMultipleResultEvent> t) {
+        rxGalleryFinal.video();
+        rxGalleryFinal.subscribe(t);
+        return mRxApi;
+    }
+
+    public RxGalleryFinalApi setCrop() {
+        rxGalleryFinal.crop();
+        return mRxApi;
+    }
+
+    /**
+     * 直接打开默认设置好的参数
+     */
+    public RxGalleryFinalApi open() {
+        rxGalleryFinal.openGallery();
+        return mRxApi;
+    }
+
+    private Function<MediaBean, Boolean> filter;
+
+    public Function<MediaBean, Boolean> getFilter() {
+        return filter;
+    }
+
+    public RxGalleryFinalApi setFilter(Function<MediaBean, Boolean> filter) {
+        this.filter = filter;
+        return this;
+    }
+
+    public boolean checkMediaBean(MediaBean mediaBean) {
+        try {
+            return filter != null && filter.apply(mediaBean);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    /**
+     * 选择类型
+     */
+    public static class SelectRXType {
+        public static final int TYPE_IMAGE = 801;
+        public static final int TYPE_VIDEO = 702;
+        public static final int TYPE_SELECT_RADIO = 1;
+        public static final int TYPE_SELECT_MULTI = 2;
+    }
+}

+ 26 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/anim/Animation.java

@@ -0,0 +1,26 @@
+package cn.finalteam.rxgalleryfinal.anim;
+
+import android.view.View;
+
+/**
+ * The parent class of all animation classes.
+ */
+public abstract class Animation {
+
+    public static final int DIRECTION_DOWN = 4;
+    public static final int DURATION_DEFAULT = 300; // 300 ms
+    // constants
+    static final int DIRECTION_LEFT = 1;
+    static final int DIRECTION_RIGHT = 2;
+    static final int DIRECTION_UP = 3;
+    static final int DURATION_LONG = 500;    // 500 ms
+
+    View view;
+
+    /**
+     * This method animates the properties of the view specific to the Animation
+     * object.
+     */
+    public abstract void animate();
+
+}

+ 17 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/anim/AnimationListener.java

@@ -0,0 +1,17 @@
+package cn.finalteam.rxgalleryfinal.anim;
+
+/**
+ * This interface is a custom listener to determine the end of an animation.
+ *
+ * @author Phu
+ */
+public interface AnimationListener {
+
+    /**
+     * This method is called when the animation ends.
+     *
+     * @param animation The Animation object.
+     */
+    void onAnimationEnd(Animation animation);
+}
+

+ 180 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/anim/SlideInUnderneathAnimation.java

@@ -0,0 +1,180 @@
+package cn.finalteam.rxgalleryfinal.anim;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
+
+/**
+ * This animation causes the view to slide in underneath from its own borders.
+ *
+ * @author SiYao
+ */
+@TargetApi(14)
+public class SlideInUnderneathAnimation extends Animation {
+
+    private int direction;
+    private TimeInterpolator interpolator;
+    private long duration;
+    private AnimationListener listener;
+
+    /**
+     * This animation causes the view to slide in underneath from its own
+     * borders.
+     *
+     * @param view The view to be animated.
+     */
+    public SlideInUnderneathAnimation(View view) {
+        this.view = view;
+        direction = DIRECTION_LEFT;
+        interpolator = new AccelerateDecelerateInterpolator();
+        duration = DURATION_LONG;
+        listener = null;
+    }
+
+    @Override
+    public void animate() {
+        final ViewGroup parentView = (ViewGroup) view.getParent();
+        final FrameLayout slideInFrame = new FrameLayout(view.getContext());
+        final int positionView = parentView.indexOfChild(view);
+        slideInFrame.setLayoutParams(view.getLayoutParams());
+        slideInFrame.setClipChildren(true);
+        parentView.removeView(view);
+        slideInFrame.addView(view);
+        parentView.addView(slideInFrame, positionView);
+
+        ObjectAnimator slideInAnim = null;
+        float viewWidth = view.getWidth(), viewHeight = view.getHeight();
+        switch (direction) {
+            case DIRECTION_LEFT:
+                view.setTranslationX(-viewWidth);
+                slideInAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
+                        slideInFrame.getX());
+                break;
+            case DIRECTION_RIGHT:
+                view.setTranslationX(viewWidth);
+                slideInAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
+                        slideInFrame.getX());
+                break;
+            case DIRECTION_UP:
+                view.setTranslationY(-viewHeight);
+                slideInAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
+                        slideInFrame.getY());
+                break;
+            case DIRECTION_DOWN:
+                view.setTranslationY(viewHeight);
+                slideInAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
+                        slideInFrame.getY());
+                break;
+            default:
+                break;
+        }
+        if (slideInAnim == null) {
+            return;
+        }
+        slideInAnim.setInterpolator(interpolator);
+        slideInAnim.setDuration(duration);
+        slideInAnim.addListener(new AnimatorListenerAdapter() {
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                view.setVisibility(View.VISIBLE);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                slideInFrame.removeAllViews();
+                view.setLayoutParams(slideInFrame.getLayoutParams());
+                parentView.addView(view, positionView);
+                if (getListener() != null) {
+                    getListener().onAnimationEnd(
+                            SlideInUnderneathAnimation.this);
+                }
+            }
+        });
+        slideInAnim.start();
+    }
+
+    /**
+     * The available directions to slide in from are <code>DIRECTION_LEFT</code>
+     * , <code>DIRECTION_RIGHT</code>, <code>DIRECTION_TOP</code> and
+     * <code>DIRECTION_BOTTOM</code>.
+     *
+     * @return The direction to slide the view in from.
+     * @see Animation
+     */
+    public int getDirection() {
+        return direction;
+    }
+
+    /**
+     * The available directions to slide in from are <code>DIRECTION_LEFT</code>
+     * , <code>DIRECTION_RIGHT</code>, <code>DIRECTION_TOP</code> and
+     * <code>DIRECTION_BOTTOM</code>.
+     *
+     * @param direction The direction to set to slide the view in from.
+     * @return This object, allowing calls to methods in this class to be
+     * chained.
+     * @see Animation
+     */
+    public SlideInUnderneathAnimation setDirection(int direction) {
+        this.direction = direction;
+        return this;
+    }
+
+    /**
+     * @return The interpolator of the entire animation.
+     */
+    public TimeInterpolator getInterpolator() {
+        return interpolator;
+    }
+
+    /**
+     * @param interpolator The interpolator of the entire animation to set.
+     */
+    public SlideInUnderneathAnimation setInterpolator(
+            TimeInterpolator interpolator) {
+        this.interpolator = interpolator;
+        return this;
+    }
+
+    /**
+     * @return The duration of the entire animation.
+     */
+    public long getDuration() {
+        return duration;
+    }
+
+    /**
+     * @param duration The duration of the entire animation to set.
+     * @return This object, allowing calls to methods in this class to be
+     * chained.
+     */
+    public SlideInUnderneathAnimation setDuration(long duration) {
+        this.duration = duration;
+        return this;
+    }
+
+    /**
+     * @return The listener for the end of the animation.
+     */
+    private AnimationListener getListener() {
+        return listener;
+    }
+
+    /**
+     * @param listener The listener to set for the end of the animation.
+     * @return This object, allowing calls to methods in this class to be
+     * chained.
+     */
+    public SlideInUnderneathAnimation setListener(AnimationListener listener) {
+        this.listener = listener;
+        return this;
+    }
+
+}

+ 177 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/anim/SlideOutUnderneathAnimation.java

@@ -0,0 +1,177 @@
+package cn.finalteam.rxgalleryfinal.anim;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.FrameLayout;
+
+/**
+ * This animation causes the view to slide out underneath to its own borders. On
+ * animation end, the view is restored to its original state and is set to
+ * <code>View.INVISIBLE</code>.
+ *
+ * @author SiYao
+ */
+@TargetApi(14)
+public class SlideOutUnderneathAnimation extends Animation {
+
+    private int direction;
+    private TimeInterpolator interpolator;
+    private long duration;
+    private AnimationListener listener;
+    private ValueAnimator slideAnim;
+
+    /**
+     * This animation causes the view to slide out underneath to its own
+     * borders. On animation end, the view is restored to its original state and
+     * is set to <code>View.INVISIBLE</code>.
+     *
+     * @param view The view to be animated.
+     */
+    public SlideOutUnderneathAnimation(View view) {
+        this.view = view;
+        direction = DIRECTION_LEFT;
+        interpolator = new AccelerateDecelerateInterpolator();
+        duration = DURATION_LONG;
+        listener = null;
+    }
+
+    @Override
+    public void animate() {
+        final ViewGroup parentView = (ViewGroup) view.getParent();
+        final FrameLayout slideOutFrame = new FrameLayout(view.getContext());
+        final int positionView = parentView.indexOfChild(view);
+        slideOutFrame.setLayoutParams(view.getLayoutParams());
+        slideOutFrame.setClipChildren(true);
+        parentView.removeView(view);
+        slideOutFrame.addView(view);
+        parentView.addView(slideOutFrame, positionView);
+
+        switch (direction) {
+            case DIRECTION_LEFT:
+                slideAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
+                        view.getTranslationX() - view.getWidth());
+                break;
+            case DIRECTION_RIGHT:
+                slideAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
+                        view.getTranslationX() + view.getWidth());
+                break;
+            case DIRECTION_UP:
+                slideAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
+                        view.getTranslationY() - view.getHeight());
+                break;
+            case DIRECTION_DOWN:
+                slideAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
+                        view.getTranslationY() + view.getHeight());
+                break;
+            default:
+                break;
+        }
+
+        AnimatorSet slideSet = new AnimatorSet();
+        slideSet.play(slideAnim);
+        slideSet.setInterpolator(interpolator);
+        slideSet.setDuration(duration);
+        slideSet.addListener(new AnimatorListenerAdapter() {
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                view.setVisibility(View.INVISIBLE);
+                slideAnim.reverse();
+                slideOutFrame.removeAllViews();
+                parentView.removeView(slideOutFrame);
+                parentView.addView(view, positionView);
+                if (getListener() != null) {
+                    getListener().onAnimationEnd(
+                            SlideOutUnderneathAnimation.this);
+                }
+            }
+        });
+        slideSet.start();
+    }
+
+    /**
+     * The available directions to slide in from are <code>DIRECTION_LEFT</code>
+     * , <code>DIRECTION_RIGHT</code>, <code>DIRECTION_TOP</code> and
+     * <code>DIRECTION_BOTTOM</code>.
+     *
+     * @return The direction to slide the view out to.
+     * @see Animation
+     */
+    public int getDirection() {
+        return direction;
+    }
+
+    /**
+     * The available directions to slide in from are <code>DIRECTION_LEFT</code>
+     * , <code>DIRECTION_RIGHT</code>, <code>DIRECTION_TOP</code> and
+     * <code>DIRECTION_BOTTOM</code>.
+     *
+     * @param direction The direction to set to slide the view out to.
+     * @return This object, allowing calls to methods in this class to be
+     * chained.
+     * @see Animation
+     */
+    public SlideOutUnderneathAnimation setDirection(int direction) {
+        this.direction = direction;
+        return this;
+    }
+
+    /**
+     * @return The interpolator of the entire animation.
+     */
+    public TimeInterpolator getInterpolator() {
+        return interpolator;
+    }
+
+    /**
+     * @param interpolator The interpolator of the entire animation to set.
+     */
+    public SlideOutUnderneathAnimation setInterpolator(
+            TimeInterpolator interpolator) {
+        this.interpolator = interpolator;
+        return this;
+    }
+
+    /**
+     * @return The duration of the entire animation.
+     */
+    public long getDuration() {
+        return duration;
+    }
+
+    /**
+     * @param duration The duration of the entire animation to set.
+     * @return This object, allowing calls to methods in this class to be
+     * chained.
+     */
+    public SlideOutUnderneathAnimation setDuration(long duration) {
+        this.duration = duration;
+        return this;
+    }
+
+    /**
+     * @return The listener for the end of the animation.
+     */
+    private AnimationListener getListener() {
+        return listener;
+    }
+
+    /**
+     * @param listener The listener to set for the end of the animation.
+     * @return This object, allowing calls to methods in this class to be
+     * chained.
+     */
+    public SlideOutUnderneathAnimation setListener(AnimationListener listener) {
+        this.listener = listener;
+        return this;
+    }
+
+}

+ 69 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/bean/BucketBean.java

@@ -0,0 +1,69 @@
+package cn.finalteam.rxgalleryfinal.bean;
+
+import android.text.TextUtils;
+
+/**
+ * Desction:文件夹信息
+ * Author:pengjianbo  Dujinyang
+ * Date:16/6/9 下午2:47
+ */
+public class BucketBean {
+    private String bucketId;
+    private String bucketName;
+    private int imageCount;
+    private String cover;
+    //图片方向
+    private int orientation;
+
+    public String getBucketId() {
+        return bucketId;
+    }
+
+    public void setBucketId(String bucketId) {
+        this.bucketId = bucketId;
+    }
+
+    public String getBucketName() {
+        return bucketName;
+    }
+
+    public void setBucketName(String bucketName) {
+        this.bucketName = bucketName;
+    }
+
+    public int getImageCount() {
+        return imageCount;
+    }
+
+    public void setImageCount(int imageCount) {
+        this.imageCount = imageCount;
+    }
+
+    public String getCover() {
+        if (cover == null) {
+            return "";
+        }
+        return cover;
+    }
+
+    public void setCover(String cover) {
+        this.cover = cover;
+    }
+
+    public int getOrientation() {
+        return orientation;
+    }
+
+    public void setOrientation(int orientation) {
+        this.orientation = orientation;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof BucketBean)) {
+            return false;
+        }
+        BucketBean bucketBean = (BucketBean) o;
+        return TextUtils.equals(bucketBean.getBucketId(), getBucketId());
+    }
+}

+ 81 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/bean/ImageCropBean.java

@@ -0,0 +1,81 @@
+package cn.finalteam.rxgalleryfinal.bean;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/8/1 下午10:27
+ */
+public class ImageCropBean extends MediaBean implements Parcelable {
+    public static final Creator<ImageCropBean> CREATOR = new Creator<ImageCropBean>() {
+        @Override
+        public ImageCropBean createFromParcel(Parcel in) {
+            return new ImageCropBean(in);
+        }
+
+        @Override
+        public ImageCropBean[] newArray(int size) {
+            return new ImageCropBean[size];
+        }
+    };
+    private String cropPath;
+    private float aspectRatio;
+
+    public ImageCropBean() {
+    }
+
+    private ImageCropBean(Parcel in) {
+        super(in);
+        cropPath = in.readString();
+        aspectRatio = in.readFloat();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeString(cropPath);
+        dest.writeFloat(aspectRatio);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public String getCropPath() {
+        return cropPath;
+    }
+
+    public void setCropPath(String cropPath) {
+        this.cropPath = cropPath;
+    }
+
+    public float getAspectRatio() {
+        return aspectRatio;
+    }
+
+    public void setAspectRatio(float aspectRatio) {
+        this.aspectRatio = aspectRatio;
+    }
+
+    public void copyMediaBean(MediaBean mediaBean) {
+        if (mediaBean != null) {
+            setId(mediaBean.getId());
+            setTitle(mediaBean.getTitle());
+            setOriginalPath(mediaBean.getOriginalPath());
+            setCreateDate(mediaBean.getCreateDate());
+            setModifiedDate(mediaBean.getModifiedDate());
+            setMimeType(mediaBean.getMimeType());
+            setBucketId(mediaBean.getBucketId());
+            setBucketDisplayName(mediaBean.getBucketDisplayName());
+            setThumbnailSmallPath(mediaBean.getThumbnailSmallPath());
+            setThumbnailBigPath(mediaBean.getThumbnailBigPath());
+            setWidth(mediaBean.getWidth());
+            setHeight(mediaBean.getHeight());
+            setLength(mediaBean.getLength());
+            setDuration(mediaBean.getDuration());
+        }
+    }
+}

+ 283 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/bean/MediaBean.java

@@ -0,0 +1,283 @@
+package cn.finalteam.rxgalleryfinal.bean;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.File;
+
+/**
+ * Desction:Media Bean
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/4 下午4:14
+ */
+public class MediaBean implements Parcelable {
+
+    public static final Creator<MediaBean> CREATOR = new Creator<MediaBean>() {
+        @Override
+        public MediaBean createFromParcel(Parcel in) {
+            return new MediaBean(in);
+        }
+
+        @Override
+        public MediaBean[] newArray(int size) {
+            return new MediaBean[size];
+        }
+    };
+    //图片ID
+    private long id;
+    private String title;
+    //图片、视频源地址
+    private String originalPath;
+    //图片、视频创建时间
+    private long createDate;
+    //图片、视频最后修改时间
+    private long modifiedDate;
+    //媒体类型
+    private String mimeType;
+    //宽
+    private int width;
+    //高
+    private int height;
+    //纬度
+    private double latitude;
+    //经度
+    private double longitude;
+    //图片方向
+    private int orientation;
+    //文件大小
+    private long length;
+    //时长
+    private long duration;
+    //文件夹相关
+    private String bucketId;
+    private String bucketDisplayName;
+    //大缩略图
+    private String thumbnailBigPath;
+    //小缩略图
+    private String thumbnailSmallPath;
+
+    public MediaBean() {
+    }
+
+    MediaBean(Parcel in) {
+        id = in.readLong();
+        title = in.readString();
+        originalPath = in.readString();
+        createDate = in.readLong();
+        modifiedDate = in.readLong();
+        mimeType = in.readString();
+        bucketId = in.readString();
+        bucketDisplayName = in.readString();
+        thumbnailBigPath = in.readString();
+        thumbnailSmallPath = in.readString();
+        width = in.readInt();
+        height = in.readInt();
+        latitude = in.readDouble();
+        longitude = in.readDouble();
+        orientation = in.readInt();
+        length = in.readLong();
+        duration = in.readLong();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(id);
+        dest.writeString(title);
+        dest.writeString(originalPath);
+        dest.writeLong(createDate);
+        dest.writeLong(modifiedDate);
+        dest.writeString(mimeType);
+        dest.writeString(bucketId);
+        dest.writeString(bucketDisplayName);
+        dest.writeString(thumbnailBigPath);
+        dest.writeString(thumbnailSmallPath);
+        dest.writeInt(width);
+        dest.writeInt(height);
+        dest.writeDouble(latitude);
+        dest.writeDouble(longitude);
+        dest.writeInt(orientation);
+        dest.writeLong(length);
+        dest.writeLong(duration);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getOriginalPath() {
+        return originalPath;
+    }
+
+    public void setOriginalPath(String originalPath) {
+        this.originalPath = originalPath;
+    }
+
+    public long getCreateDate() {
+        return createDate;
+    }
+
+    public void setCreateDate(long createDate) {
+        this.createDate = createDate;
+    }
+
+    public long getModifiedDate() {
+        return modifiedDate;
+    }
+
+    public void setModifiedDate(long modifiedDate) {
+        this.modifiedDate = modifiedDate;
+    }
+
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    public void setMimeType(String mimeType) {
+        this.mimeType = mimeType;
+    }
+
+    public String getBucketId() {
+        return bucketId;
+    }
+
+    public void setBucketId(String bucketId) {
+        this.bucketId = bucketId;
+    }
+
+    public String getBucketDisplayName() {
+        return bucketDisplayName;
+    }
+
+    public void setBucketDisplayName(String bucketDisplayName) {
+        this.bucketDisplayName = bucketDisplayName;
+    }
+
+    public String getThumbnailBigPath() {
+        if (new File(thumbnailBigPath).exists()) {
+            return thumbnailBigPath;
+        }
+        return "";
+    }
+
+    public void setThumbnailBigPath(String thumbnailBigPath) {
+        this.thumbnailBigPath = thumbnailBigPath;
+    }
+
+    public String getThumbnailSmallPath() {
+        if (new File(thumbnailSmallPath).exists()) {
+            return thumbnailSmallPath;
+        }
+        return "";
+    }
+
+    public void setThumbnailSmallPath(String thumbnailSmallPath) {
+        this.thumbnailSmallPath = thumbnailSmallPath;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public double getLatitude() {
+        return latitude;
+    }
+
+    public void setLatitude(double latitude) {
+        this.latitude = latitude;
+    }
+
+    public double getLongitude() {
+        return longitude;
+    }
+
+    public void setLongitude(double longitude) {
+        this.longitude = longitude;
+    }
+
+    public int getOrientation() {
+        return orientation;
+    }
+
+    public void setOrientation(int orientation) {
+        this.orientation = orientation;
+    }
+
+    public long getLength() {
+        return length;
+    }
+
+    public void setLength(long length) {
+        this.length = length;
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    public void setDuration(long duration) {
+        this.duration = duration;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || !(obj instanceof MediaBean)) {
+            return false;
+        }
+
+        MediaBean bean = (MediaBean) obj;
+        return bean.getId() == getId();
+
+    }
+
+    @Override
+    public String toString() {
+        return "MediaBean{" +
+                "id=" + id +
+                ", title='" + title + '\'' +
+                ", originalPath='" + originalPath + '\'' +
+                ", createDate=" + createDate +
+                ", modifiedDate=" + modifiedDate +
+                ", mimeType='" + mimeType + '\'' +
+                ", width=" + width +
+                ", height=" + height +
+                ", latitude=" + latitude +
+                ", longitude=" + longitude +
+                ", orientation=" + orientation +
+                ", length=" + length +
+                ", duration=" + duration +
+                ", bucketId='" + bucketId + '\'' +
+                ", bucketDisplayName='" + bucketDisplayName + '\'' +
+                ", thumbnailBigPath='" + thumbnailBigPath + '\'' +
+                ", thumbnailSmallPath='" + thumbnailSmallPath + '\'' +
+                '}';
+    }
+}

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/bean/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 数据模型
+ */
+package cn.finalteam.rxgalleryfinal.bean;

+ 25 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/imageloader/AbsImageLoader.java

@@ -0,0 +1,25 @@
+package cn.finalteam.rxgalleryfinal.imageloader;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+
+import cn.finalteam.rxgalleryfinal.ui.widget.FixImageView;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/6/17 下午1:05
+ */
+public interface AbsImageLoader {
+    void displayImage(Context context,
+                      String path,
+                      FixImageView imageView,
+                      Drawable defaultDrawable,
+                      Bitmap.Config config,
+                      boolean resize,
+                      boolean isGif,
+                      int width,
+                      int height,
+                      int rotate);
+}

+ 27 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/imageloader/GlideImageLoader.java

@@ -0,0 +1,27 @@
+package cn.finalteam.rxgalleryfinal.imageloader;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.request.RequestOptions;
+
+import cn.finalteam.rxgalleryfinal.imageloader.rotate.RotateTransformation;
+import cn.finalteam.rxgalleryfinal.ui.widget.FixImageView;
+
+/**
+ * Created by pengjianbo  Dujinyang on 2016/8/13 0013.
+ */
+public class GlideImageLoader implements AbsImageLoader {
+
+    @Override
+    public void displayImage(Context context, String path, FixImageView imageView, Drawable defaultDrawable, Bitmap.Config config, boolean resize, boolean isGif, int width, int height, int rotate) {
+        Glide
+                .with(context)
+                .load(path)
+                .apply(new RequestOptions().placeholder(defaultDrawable).error(defaultDrawable).override(width, height).transform(new RotateTransformation(context, rotate)).diskCacheStrategy(DiskCacheStrategy.NONE))
+                .into(imageView);
+    }
+}

+ 10 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/imageloader/ImageLoaderType.java

@@ -0,0 +1,10 @@
+package cn.finalteam.rxgalleryfinal.imageloader;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/25 下午3:36
+ */
+public enum ImageLoaderType {
+    PICASSO, GLIDE, FRESCO, UNIVERSAL
+}

+ 36 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/imageloader/rotate/RotateTransformation.java

@@ -0,0 +1,36 @@
+package cn.finalteam.rxgalleryfinal.imageloader.rotate;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Matrix;
+
+import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
+import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
+
+import java.security.MessageDigest;
+
+/**
+ * Created by pengjianbo  Dujinyang on 2016/8/16 0016.
+ */
+public class RotateTransformation extends BitmapTransformation {
+
+    private float rotateRotationAngle = 0f;
+
+    public RotateTransformation(Context context, float rotateRotationAngle) {
+        super(context);
+
+        this.rotateRotationAngle = rotateRotationAngle;
+    }
+
+    @Override
+    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
+        Matrix matrix = new Matrix();
+        matrix.postRotate(rotateRotationAngle);
+        return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true);
+    }
+
+    @Override
+    public void updateDiskCacheKey(MessageDigest messageDigest) {
+        messageDigest.update(("rotate" + rotateRotationAngle).getBytes(CHARSET));
+    }
+}

+ 19 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/MediaBucketFactoryInteractor.java

@@ -0,0 +1,19 @@
+package cn.finalteam.rxgalleryfinal.interactor;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.BucketBean;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/4 下午8:24
+ */
+public interface MediaBucketFactoryInteractor {
+
+    void generateBuckets();
+
+    interface OnGenerateBucketListener {
+        void onFinished(List<BucketBean> list);
+    }
+}

+ 23 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/MediaSrcFactoryInteractor.java

@@ -0,0 +1,23 @@
+package cn.finalteam.rxgalleryfinal.interactor;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+
+/**
+ * Desction:媒体资源工厂
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/14 上午11:06
+ */
+public interface MediaSrcFactoryInteractor {
+
+    /**
+     * 生产资源
+     */
+    void generateMeidas(String bucketId, int page, int limit);
+
+    interface OnGenerateMediaListener {
+        void onFinished(String bucketId, int pageSize, int currentOffset, List<MediaBean> list);
+    }
+
+}

+ 69 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/impl/MediaBucketFactoryInteractorImpl.java

@@ -0,0 +1,69 @@
+package cn.finalteam.rxgalleryfinal.interactor.impl;
+
+
+import android.content.Context;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.BucketBean;
+import cn.finalteam.rxgalleryfinal.interactor.MediaBucketFactoryInteractor;
+import cn.finalteam.rxgalleryfinal.utils.MediaUtils;
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.observers.DisposableObserver;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/4 下午8:29
+ */
+public class MediaBucketFactoryInteractorImpl implements MediaBucketFactoryInteractor {
+
+    private final Context context;
+    private final boolean isImage;
+    private final OnGenerateBucketListener onGenerateBucketListener;
+
+    public MediaBucketFactoryInteractorImpl(Context context, boolean isImage, OnGenerateBucketListener onGenerateBucketListener) {
+        this.context = context;
+        this.isImage = isImage;
+        this.onGenerateBucketListener = onGenerateBucketListener;
+    }
+
+    @Override
+    public void generateBuckets() {
+        Observable.create(new ObservableOnSubscribe<List<BucketBean>>(){
+
+            @Override
+            public void subscribe(ObservableEmitter<List<BucketBean>> subscriber) throws Exception {
+                List<BucketBean> bucketBeanList;
+                if (isImage) {
+                    bucketBeanList = MediaUtils.getAllBucketByImage(context);
+                } else {
+                    bucketBeanList = MediaUtils.getAllBucketByVideo(context);
+                }
+                subscriber.onNext(bucketBeanList);
+                subscriber.onComplete();
+            }
+        })
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new DisposableObserver<List<BucketBean>>() {
+                    @Override
+                    public void onComplete() {
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+                        onGenerateBucketListener.onFinished(null);
+                    }
+
+                    @Override
+                    public void onNext(List<BucketBean> bucketBeanList) {
+                        onGenerateBucketListener.onFinished(bucketBeanList);
+                    }
+                });
+    }
+}

+ 67 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/impl/MediaSrcFactoryInteractorImpl.java

@@ -0,0 +1,67 @@
+package cn.finalteam.rxgalleryfinal.interactor.impl;
+
+import android.content.Context;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.interactor.MediaSrcFactoryInteractor;
+import cn.finalteam.rxgalleryfinal.utils.MediaUtils;
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.observers.DisposableObserver;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/14 上午11:08
+ */
+public class MediaSrcFactoryInteractorImpl implements MediaSrcFactoryInteractor {
+
+    private final Context context;
+    private final OnGenerateMediaListener onGenerateMediaListener;
+    private final boolean isImage;
+
+    public MediaSrcFactoryInteractorImpl(Context context, boolean isImage, OnGenerateMediaListener onGenerateMediaListener) {
+        this.context = context;
+        this.isImage = isImage;
+        this.onGenerateMediaListener = onGenerateMediaListener;
+    }
+
+    @Override
+    public void generateMeidas(final String bucketId, final int page, final int limit) {
+        Observable.create(new ObservableOnSubscribe<List<MediaBean>>(){
+            @Override
+            public void subscribe(ObservableEmitter<List<MediaBean>> subscriber) throws Exception {
+                List<MediaBean> mediaBeanList = null;
+                if (isImage) {
+                    mediaBeanList = MediaUtils.getMediaWithImageList(context, bucketId, page, limit);
+                } else {
+                    mediaBeanList = MediaUtils.getMediaWithVideoList(context, bucketId, page, limit);
+                }
+                subscriber.onNext(mediaBeanList);
+                subscriber.onComplete();
+            }
+        })
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new DisposableObserver<List<MediaBean>>() {
+                    @Override
+                    public void onComplete() {
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+                        onGenerateMediaListener.onFinished(bucketId, page, limit, null);
+                    }
+
+                    @Override
+                    public void onNext(List<MediaBean> mediaBeenList) {
+                        onGenerateMediaListener.onFinished(bucketId, page, limit, mediaBeenList);
+                    }
+                });
+    }
+}

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/impl/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * MVP M层包(业务生产)--实现
+ */
+package cn.finalteam.rxgalleryfinal.interactor.impl;

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/interactor/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * MVP M层包(业务生产)--抽象
+ */
+package cn.finalteam.rxgalleryfinal.interactor;

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 项目包名
+ */
+package cn.finalteam.rxgalleryfinal;

+ 17 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/presenter/MediaGridPresenter.java

@@ -0,0 +1,17 @@
+package cn.finalteam.rxgalleryfinal.presenter;
+
+import cn.finalteam.rxgalleryfinal.view.MediaGridView;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/14 上午10:53
+ */
+public interface MediaGridPresenter {
+
+    void setMediaGridView(MediaGridView mediaGridView);
+
+    void getMediaList(String bucketId, int pageSize, int currentOffset);
+
+    void getBucketList();
+}

+ 70 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/presenter/impl/MediaGridPresenterImpl.java

@@ -0,0 +1,70 @@
+package cn.finalteam.rxgalleryfinal.presenter.impl;
+
+import android.content.Context;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.BucketBean;
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.interactor.MediaBucketFactoryInteractor;
+import cn.finalteam.rxgalleryfinal.interactor.MediaSrcFactoryInteractor;
+import cn.finalteam.rxgalleryfinal.interactor.impl.MediaBucketFactoryInteractorImpl;
+import cn.finalteam.rxgalleryfinal.interactor.impl.MediaSrcFactoryInteractorImpl;
+import cn.finalteam.rxgalleryfinal.presenter.MediaGridPresenter;
+import cn.finalteam.rxgalleryfinal.view.MediaGridView;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/14 上午10:58
+ */
+public class MediaGridPresenterImpl implements MediaGridPresenter, MediaSrcFactoryInteractor.OnGenerateMediaListener,
+        MediaBucketFactoryInteractor.OnGenerateBucketListener {
+
+    private final MediaSrcFactoryInteractor mediaSrcFactoryInteractor;
+    private final MediaBucketFactoryInteractor mediaBucketFactoryInteractor;
+
+    private MediaGridView mediaGridView;
+
+    public MediaGridPresenterImpl(Context context, boolean isImage) {
+        this.mediaSrcFactoryInteractor = new MediaSrcFactoryInteractorImpl(context, isImage, this);
+        this.mediaBucketFactoryInteractor = new MediaBucketFactoryInteractorImpl(context, isImage, this);
+    }
+
+    /**
+     * 设置MVP view(操作UI接口)
+     */
+    @Override
+    public void setMediaGridView(MediaGridView mediaGridView) {
+        this.mediaGridView = mediaGridView;
+    }
+
+    /**
+     * 分页获取media
+     */
+    @Override
+    public void getMediaList(String bucketId, int pageSize, int currentOffset) {
+        mediaSrcFactoryInteractor.generateMeidas(bucketId, pageSize, currentOffset);
+    }
+
+    @Override
+    public void getBucketList() {
+        mediaBucketFactoryInteractor.generateBuckets();
+    }
+
+    /**
+     * Media获取事件回调
+     */
+    @Override
+    public void onFinished(String bucketId, int pageSize, int currentOffset, List<MediaBean> list) {
+        mediaGridView.onRequestMediaCallback(list);
+    }
+
+    /**
+     * BUCKET获取事件回调
+     */
+    @Override
+    public void onFinished(List<BucketBean> list) {
+        mediaGridView.onRequestBucketCallback(list);
+    }
+}

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/presenter/impl/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * MVP P层包(调度)--实现
+ */
+package cn.finalteam.rxgalleryfinal.presenter.impl;

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/presenter/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * MVP P层包(调度)--抽象
+ */
+package cn.finalteam.rxgalleryfinal.presenter;

+ 167 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/RxBus.java

@@ -0,0 +1,167 @@
+package cn.finalteam.rxgalleryfinal.rxbus;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.subjects.PublishSubject;
+import io.reactivex.subjects.Subject;
+
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/22 下午2:40
+ */
+public class RxBus {
+
+    private static volatile RxBus mInstance;
+    private final Subject<Object> mBus;
+    private final Map<Class<?>, Object> mStickyEventMap;
+
+    private final CompositeDisposable mDisposable;
+
+    private RxBus() {
+        mBus = PublishSubject.create().toSerialized();
+        mDisposable = new CompositeDisposable();
+        mStickyEventMap = new HashMap<>();
+    }
+
+    public static RxBus getDefault() {
+        if (mInstance == null) {
+            synchronized (RxBus.class) {
+                if (mInstance == null) {
+                    mInstance = new RxBus();
+                }
+            }
+        }
+        return mInstance;
+    }
+
+    /**
+     * 发送事件
+     */
+    public void post(Object event) {
+        mBus.onNext(event);
+    }
+
+    /**
+     * 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
+     */
+    public <T> Observable<T> toObservable(Class<T> eventType) {
+        return mBus.ofType(eventType);
+    }
+
+    /**
+     * 判断是否有订阅者
+     */
+    public boolean hasObservers() {
+        return mBus.hasObservers();
+    }
+
+    public void reset() {
+        mInstance = null;
+    }
+
+
+    /**
+     * 是否被取消订阅
+     */
+    public boolean isUnsubscribed() {
+        return mDisposable.isDisposed();
+    }
+
+    /**
+     * 添加订阅
+     */
+    public void add(Disposable s) {
+        if (s != null) {
+            mDisposable.add(s);
+        }
+    }
+
+    /**
+     * 移除订阅
+     */
+    public void remove(Disposable s) {
+        if (s != null) {
+            mDisposable.remove(s);
+        }
+    }
+
+    /**
+     * 清除所有订阅
+     */
+    public void clear() {
+        mDisposable.clear();
+    }
+
+    /**
+     * 取消订阅
+     */
+    public void unsubscribe() {
+        mDisposable.dispose();
+    }
+
+    /**
+     * 发送一个新Sticky事件
+     */
+    public void postSticky(Object event) {
+        synchronized (mStickyEventMap) {
+            mStickyEventMap.put(event.getClass(), event);
+        }
+        post(event);
+    }
+
+    /**
+     * 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
+     */
+    public <T> Observable<T> toObservableSticky(final Class<T> eventType) {
+        synchronized (mStickyEventMap) {
+            Observable<T> observable = mBus.ofType(eventType);
+            final Object event = mStickyEventMap.get(eventType);
+
+            if (event != null) {
+                return Observable.merge(observable, Observable.create(new ObservableOnSubscribe<T>(){
+                    @Override
+                    public void subscribe(ObservableEmitter<T> subscriber) throws Exception {
+                        subscriber.onNext(eventType.cast(event));
+                    }
+                }));
+            } else {
+                return observable;
+            }
+        }
+    }
+
+    /**
+     * 根据eventType获取Sticky事件
+     */
+    public <T> T getStickyEvent(Class<T> eventType) {
+        synchronized (mStickyEventMap) {
+            return eventType.cast(mStickyEventMap.get(eventType));
+        }
+    }
+
+    /**
+     * 移除指定eventType的Sticky事件
+     */
+    public <T> T removeStickyEvent(Class<T> eventType) {
+        synchronized (mStickyEventMap) {
+            return eventType.cast(mStickyEventMap.remove(eventType));
+        }
+    }
+
+    /**
+     * 移除所有的Sticky事件
+     */
+    public void removeAllStickyEvents() {
+        synchronized (mStickyEventMap) {
+            mStickyEventMap.clear();
+        }
+    }
+}

+ 36 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/RxBusDisposable.java

@@ -0,0 +1,36 @@
+package cn.finalteam.rxgalleryfinal.rxbus;
+
+import cn.finalteam.rxgalleryfinal.utils.Logger;
+import io.reactivex.observers.DisposableObserver;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/22 下午2:40
+ */
+public abstract class RxBusDisposable<T> extends DisposableObserver<T> {
+
+    @Override
+    public void onNext(T t) {
+        try {
+            onEvent(t);
+        } catch (Exception e) {
+            e.printStackTrace();
+            onError(e);
+        }
+    }
+
+
+    @Override
+    public void onComplete() {
+
+    }
+
+    @Override
+    public void onError(Throwable e) {
+        Logger.e(e.getMessage());
+    }
+
+    protected abstract void onEvent(T t) throws Exception;
+
+}

+ 11 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/RxBusResultDisposable.java

@@ -0,0 +1,11 @@
+package cn.finalteam.rxgalleryfinal.rxbus;
+
+import cn.finalteam.rxgalleryfinal.rxbus.event.BaseResultEvent;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/8/1 下午11:11
+ */
+public abstract class RxBusResultDisposable<T extends BaseResultEvent> extends RxBusDisposable<T> {
+}

+ 9 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/BaseResultEvent.java

@@ -0,0 +1,9 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/31 下午10:37
+ */
+public interface BaseResultEvent {
+}

+ 9 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/CloseMediaViewPageFragmentEvent.java

@@ -0,0 +1,9 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/28 上午12:19
+ */
+public class CloseMediaViewPageFragmentEvent {
+}

+ 9 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/CloseRxMediaGridPageEvent.java

@@ -0,0 +1,9 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/8/1 下午11:51
+ */
+public class CloseRxMediaGridPageEvent {
+}

+ 22 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/ImageMultipleResultEvent.java

@@ -0,0 +1,22 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/8/1 下午10:52
+ */
+public class ImageMultipleResultEvent implements BaseResultEvent {
+    private final List<MediaBean> mediaResultList;
+
+    public ImageMultipleResultEvent(List<MediaBean> list) {
+        this.mediaResultList = list;
+    }
+
+    public List<MediaBean> getResult() {
+        return mediaResultList;
+    }
+}

+ 21 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/ImageRadioResultEvent.java

@@ -0,0 +1,21 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+import cn.finalteam.rxgalleryfinal.bean.ImageCropBean;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/8/1 下午10:49
+ */
+public class ImageRadioResultEvent implements BaseResultEvent {
+    private final ImageCropBean resultBean;
+
+    public ImageRadioResultEvent(ImageCropBean bean) {
+        this.resultBean = bean;
+    }
+
+    public ImageCropBean getResult() {
+        return resultBean;
+    }
+
+}

+ 21 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/MediaCheckChangeEvent.java

@@ -0,0 +1,21 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/24 下午11:47
+ */
+public class MediaCheckChangeEvent {
+
+    private final MediaBean mediaBean;
+
+    public MediaCheckChangeEvent(MediaBean mediaBean) {
+        this.mediaBean = mediaBean;
+    }
+
+    public MediaBean getMediaBean() {
+        return this.mediaBean;
+    }
+}

+ 31 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/MediaViewPagerChangedEvent.java

@@ -0,0 +1,31 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/25 下午3:45
+ */
+public class MediaViewPagerChangedEvent {
+
+    private final int curIndex;
+    private final int totalSize;
+    private final boolean isPreview;
+
+    public MediaViewPagerChangedEvent(int curIndex, int totalSize, boolean isPreview) {
+        this.curIndex = curIndex;
+        this.totalSize = totalSize;
+        this.isPreview = isPreview;
+    }
+
+    public int getCurIndex() {
+        return curIndex;
+    }
+
+    public int getTotalSize() {
+        return totalSize;
+    }
+
+    public boolean isPreview() {
+        return isPreview;
+    }
+}

+ 28 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/OpenMediaPageFragmentEvent.java

@@ -0,0 +1,28 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+import java.util.ArrayList;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/27 下午11:14
+ */
+public class OpenMediaPageFragmentEvent {
+    private final ArrayList<MediaBean> mediaBeanList;
+    private final int position;
+
+    public OpenMediaPageFragmentEvent(ArrayList<MediaBean> mediaBeanList, int position) {
+        this.mediaBeanList = mediaBeanList;
+        this.position = position;
+    }
+
+    public ArrayList<MediaBean> getMediaBeanList() {
+        return mediaBeanList;
+    }
+
+    public int getPosition() {
+        return position;
+    }
+}

+ 9 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/OpenMediaPreviewFragmentEvent.java

@@ -0,0 +1,9 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/23 下午3:49
+ */
+public class OpenMediaPreviewFragmentEvent {
+}

+ 29 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/RequestStorageReadAccessPermissionEvent.java

@@ -0,0 +1,29 @@
+package cn.finalteam.rxgalleryfinal.rxbus.event;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/30 下午11:23
+ */
+public class RequestStorageReadAccessPermissionEvent {
+
+    public static final int TYPE_CAMERA = 0;
+    public static final int TYPE_WRITE = 1;
+
+    private final boolean success;
+    private final int type;
+
+    public RequestStorageReadAccessPermissionEvent(boolean success, int type) {
+        this.success = success;
+        this.type = type;
+    }
+
+    public boolean isSuccess() {
+        return success;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+}

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/event/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 事件包
+ */
+package cn.finalteam.rxgalleryfinal.rxbus.event;

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxbus/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * rxjava实现事件总线
+ */
+package cn.finalteam.rxgalleryfinal.rxbus;

+ 48 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/Job.java

@@ -0,0 +1,48 @@
+package cn.finalteam.rxgalleryfinal.rxjob;
+
+import android.support.annotation.NonNull;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/31 上午9:15
+ */
+public interface Job {
+    Result onRunJob();
+
+    enum Result {
+
+        SUCCESS(), FAILURE();
+
+        private Object data;
+
+        Result() {
+        }
+
+        public Object getResultData() {
+            return data;
+        }
+
+        public void setResultData(Object data) {
+            this.data = data;
+        }
+    }
+
+    class Params {
+        private final Object data;
+        private final String tag;
+
+        public Params(@NonNull String tag, Object requestData) {
+            this.tag = tag;
+            this.data = requestData;
+        }
+
+        public String getTag() {
+            return tag;
+        }
+
+        public Object getRequestData() {
+            return data;
+        }
+    }
+}

+ 10 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/JobCreator.java

@@ -0,0 +1,10 @@
+package cn.finalteam.rxgalleryfinal.rxjob;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/31 上午9:10
+ */
+public interface JobCreator {
+    Job create();
+}

+ 70 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/JobManager.java

@@ -0,0 +1,70 @@
+package cn.finalteam.rxgalleryfinal.rxjob;
+
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.observers.DisposableObserver;
+import io.reactivex.schedulers.Schedulers;
+
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/31 上午9:12
+ */
+class JobManager {
+
+    private final Queue<Job> jobQueue;
+    private boolean queueFree = true;
+
+    JobManager() {
+        jobQueue = new LinkedBlockingQueue<>();
+    }
+
+    void addJob(Job job) {
+        if (jobQueue.isEmpty() && queueFree) {
+            jobQueue.offer(job);
+            start();
+        } else {
+            jobQueue.offer(job);
+        }
+    }
+
+    private void start() {
+        Observable.create(new ObservableOnSubscribe<Job>(){
+            @Override
+            public void subscribe(ObservableEmitter<Job> subscriber) throws Exception {
+                queueFree = false;
+                Job job;
+                while ((job = jobQueue.poll()) != null) {
+                    job.onRunJob();
+                }
+                subscriber.onComplete();
+            }
+        })
+                .subscribeOn(Schedulers.newThread())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new DisposableObserver<Job>() {
+                    @Override
+                    public void onComplete() {
+                        queueFree = true;
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+                    }
+
+                    @Override
+                    public void onNext(Job job) {
+                    }
+                });
+    }
+
+    public void clear() {
+        jobQueue.clear();
+    }
+}

+ 31 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/RxJob.java

@@ -0,0 +1,31 @@
+package cn.finalteam.rxgalleryfinal.rxjob;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/31 上午9:09
+ */
+public class RxJob {
+
+    private static RxJob rxJob;
+    private final JobManager jobManager;
+
+    private RxJob() {
+        jobManager = new JobManager();
+    }
+
+    public static RxJob getDefault() {
+        if (rxJob == null) {
+            rxJob = new RxJob();
+        }
+        return rxJob;
+    }
+
+    public void addJob(Job job) {
+        jobManager.addJob(job);
+    }
+
+    public void clearJob() {
+        jobManager.clear();
+    }
+}

+ 42 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/job/ImageThmbnailJob.java

@@ -0,0 +1,42 @@
+package cn.finalteam.rxgalleryfinal.rxjob.job;
+
+import android.content.Context;
+
+import java.io.File;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.rxjob.Job;
+import cn.finalteam.rxgalleryfinal.utils.BitmapUtils;
+import cn.finalteam.rxgalleryfinal.utils.MediaUtils;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/31 上午11:46
+ */
+public class ImageThmbnailJob implements Job {
+
+    private final MediaBean mediaBean;
+    private final Context context;
+
+    public ImageThmbnailJob(Context context, Params params) {
+        this.context = context;
+        this.mediaBean = (MediaBean) params.getRequestData();
+    }
+
+    @Override
+    public Result onRunJob() {
+        String originalPath = mediaBean.getOriginalPath();
+        File bigThumFile = MediaUtils.createThumbnailBigFileName(context, originalPath);
+        File smallThumFile = MediaUtils.createThumbnailSmallFileName(context, originalPath);
+        if (!bigThumFile.exists()) {
+            BitmapUtils.createThumbnailBig(bigThumFile, originalPath);
+        }
+        if (!smallThumFile.exists()) {
+            BitmapUtils.createThumbnailSmall(smallThumFile, originalPath);
+        }
+        Result result = Result.SUCCESS;
+        result.setResultData(mediaBean);
+        return result;
+    }
+}

+ 31 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/job/ImageThmbnailJobCreate.java

@@ -0,0 +1,31 @@
+package cn.finalteam.rxgalleryfinal.rxjob.job;
+
+import android.content.Context;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.rxjob.Job;
+import cn.finalteam.rxgalleryfinal.rxjob.JobCreator;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/31 上午11:46
+ */
+public class ImageThmbnailJobCreate implements JobCreator {
+
+    private final MediaBean mediaBean;
+    private final Context context;
+
+    public ImageThmbnailJobCreate(Context context, MediaBean mediaBean) {
+        this.context = context;
+        this.mediaBean = mediaBean;
+    }
+
+    @Override
+    public Job create() {
+        Job.Params params = new Job.Params(mediaBean.getOriginalPath(), mediaBean);
+        return new ImageThmbnailJob(context, params);
+    }
+
+
+}

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/job/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 队列包
+ */
+package cn.finalteam.rxgalleryfinal.rxjob.job;

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/rxjob/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * rxjava实现queue
+ */
+package cn.finalteam.rxgalleryfinal.rxjob;

+ 36 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/RxGalleryListener.java

@@ -0,0 +1,36 @@
+package cn.finalteam.rxgalleryfinal.ui;
+
+import cn.finalteam.rxgalleryfinal.ui.adapter.MediaGridAdapter;
+import cn.finalteam.rxgalleryfinal.ui.base.IMultiImageCheckedListener;
+import cn.finalteam.rxgalleryfinal.ui.base.IRadioImageCheckedListener;
+import cn.finalteam.rxgalleryfinal.ui.fragment.MediaGridFragment;
+
+/**
+ * 处理回调监听
+ * Created by KARL on 2017-03-17 04-42-25.
+ */
+public class RxGalleryListener {
+
+    private static final class RxGalleryListenerHolder {
+        private static final RxGalleryListener RX_GALLERY_LISTENER = new RxGalleryListener();
+    }
+
+    public static RxGalleryListener getInstance() {
+        return RxGalleryListenerHolder.RX_GALLERY_LISTENER;
+    }
+
+    /**
+     * 图片多选的事件
+     */
+    public void setMultiImageCheckedListener(IMultiImageCheckedListener checkedImageListener) {
+        MediaGridAdapter.setCheckedListener(checkedImageListener);
+    }
+
+
+    /**
+     * 图片单选的事件
+     */
+    public void setRadioImageCheckedListener(IRadioImageCheckedListener checkedImageListener) {
+        MediaGridFragment.setRadioListener(checkedImageListener);
+    }
+}

+ 114 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/activity/BaseActivity.java

@@ -0,0 +1,114 @@
+package cn.finalteam.rxgalleryfinal.ui.activity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+
+import cn.finalteam.rxgalleryfinal.BuildConfig;
+import cn.finalteam.rxgalleryfinal.Configuration;
+import cn.finalteam.rxgalleryfinal.utils.Logger;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/16 下午7:36
+ */
+public abstract class BaseActivity extends AppCompatActivity {
+
+    public static final String EXTRA_PREFIX = BuildConfig.APPLICATION_ID;
+    public static final String EXTRA_CONFIGURATION = EXTRA_PREFIX + ".Configuration";
+
+    private final String CLASS_NAME = getClass().getSimpleName();
+
+    public Configuration mConfiguration;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        printActivityLife("onCreate");
+        Intent intent = getIntent();
+        Bundle bundle = null;
+        if (intent != null) {
+            bundle = intent.getExtras();
+        }
+
+
+        if (savedInstanceState != null) {
+            mConfiguration = savedInstanceState.getParcelable(EXTRA_CONFIGURATION);
+        }
+        if (mConfiguration == null && bundle != null) {
+            mConfiguration = bundle.getParcelable(EXTRA_CONFIGURATION);
+        }
+
+        if (mConfiguration == null) {
+            finish();
+        } else {
+            if (bundle == null) {
+                bundle = savedInstanceState;
+            }
+            setContentView(getContentView());
+            findViews();
+            setTheme();
+            onCreateOk(bundle);
+        }
+    }
+
+    @LayoutRes
+    public abstract int getContentView();
+
+    protected abstract void onCreateOk(@Nullable Bundle savedInstanceState);
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        printActivityLife("onStart");
+    }
+
+    @Override
+    protected void onRestart() {
+        super.onRestart();
+        printActivityLife("onRestart");
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        printActivityLife("onRestart");
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        printActivityLife("onPause");
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        printActivityLife("onDestroy");
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        printActivityLife("onSaveInstanceState");
+        outState.putParcelable(EXTRA_CONFIGURATION, mConfiguration);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        printActivityLife("onRestoreInstanceState");
+        mConfiguration = savedInstanceState.getParcelable(EXTRA_CONFIGURATION);
+    }
+
+    public abstract void findViews();
+
+    protected abstract void setTheme();
+
+    private void printActivityLife(String method) {
+        Logger.i(String.format("Activity:%s Method:%s", CLASS_NAME, method));
+    }
+}

+ 445 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/activity/MediaActivity.java

@@ -0,0 +1,445 @@
+package cn.finalteam.rxgalleryfinal.ui.activity;
+
+import android.content.pm.PackageManager;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.widget.Toolbar;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.R;
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBus;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBusDisposable;
+import cn.finalteam.rxgalleryfinal.rxbus.event.BaseResultEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.CloseRxMediaGridPageEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.ImageMultipleResultEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.MediaCheckChangeEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.MediaViewPagerChangedEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.OpenMediaPageFragmentEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.OpenMediaPreviewFragmentEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.RequestStorageReadAccessPermissionEvent;
+import cn.finalteam.rxgalleryfinal.rxjob.RxJob;
+import cn.finalteam.rxgalleryfinal.ui.fragment.MediaGridFragment;
+import cn.finalteam.rxgalleryfinal.ui.fragment.MediaPageFragment;
+import cn.finalteam.rxgalleryfinal.ui.fragment.MediaPreviewFragment;
+import cn.finalteam.rxgalleryfinal.utils.Logger;
+import cn.finalteam.rxgalleryfinal.utils.OsCompat;
+import cn.finalteam.rxgalleryfinal.utils.ThemeUtils;
+import cn.finalteam.rxgalleryfinal.view.ActivityFragmentView;
+import io.reactivex.disposables.Disposable;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/7 上午10:01
+ */
+public class MediaActivity extends BaseActivity implements ActivityFragmentView {
+
+    public static final int REQUEST_STORAGE_READ_ACCESS_PERMISSION = 101;
+    public static final int REQUEST_STORAGE_WRITE_ACCESS_PERMISSION = 102;
+    public static final int REQUEST_CAMERA_ACCESS_PERMISSION = 103;
+
+    private static final String EXTRA_CHECKED_LIST = EXTRA_PREFIX + ".CheckedList";
+    private static final String EXTRA_SELECTED_INDEX = EXTRA_PREFIX + ".SelectedIndex";
+    private static final String EXTRA_PAGE_MEDIA_LIST = EXTRA_PREFIX + ".PageMediaList";
+    private static final String EXTRA_PAGE_POSITION = EXTRA_PREFIX + ".PagePosition";
+    private static final String EXTRA_PREVIEW_POSITION = EXTRA_PREFIX + ".PreviewPosition";
+
+    private MediaGridFragment mMediaGridFragment;
+    private MediaPageFragment mMediaPageFragment;
+    private MediaPreviewFragment mMediaPreviewFragment;
+
+    private Toolbar mToolbar;
+    private TextView mTvToolbarTitle;
+    private TextView mTvOverAction;
+    private View mToolbarDivider;
+
+    private ArrayList<MediaBean> mCheckedList;
+    private int mSelectedIndex = 0;
+    private ArrayList<MediaBean> mPageMediaList;
+    private int mPagePosition;
+    private int mPreviewPosition;
+
+    @Override
+    public int getContentView() {
+        return R.layout.gallery_activity_media;
+    }
+
+    @Override
+    protected void onCreateOk(@Nullable Bundle savedInstanceState) {
+        mMediaGridFragment = MediaGridFragment.newInstance(mConfiguration);
+        if (!mConfiguration.isRadio()) {
+            mTvOverAction.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View view) {
+                    if (mMediaGridFragment != null && mMediaGridFragment.isShowRvBucketView()) {
+                        mMediaGridFragment.hideRvBucketView();
+                    } else {
+                        if (mCheckedList != null && mCheckedList.size() > 0) {
+                            BaseResultEvent event = new ImageMultipleResultEvent(mCheckedList);
+                            RxBus.getDefault().post(event);
+                            finish();
+                        }
+                    }
+                }
+            });
+            mTvOverAction.setVisibility(View.VISIBLE);
+        } else {
+            mTvOverAction.setVisibility(View.GONE);
+        }
+        mCheckedList = new ArrayList<>();
+        List<MediaBean> selectedList = mConfiguration.getSelectedList();
+        if (selectedList != null && selectedList.size() > 0) {
+            mCheckedList.addAll(selectedList);
+            if (mCheckedList.size() > 0) {
+                String text = getResources().getString(R.string.gallery_over_button_text_checked, mCheckedList.size(), mConfiguration.getMaxSize());
+                mTvOverAction.setText(text);
+                mTvOverAction.setEnabled(true);
+            } else {
+                mTvOverAction.setText(R.string.gallery_over_button_text);
+                mTvOverAction.setEnabled(false);
+            }
+        }
+
+        showMediaGridFragment();
+        subscribeEvent();
+    }
+
+    @Override
+    public void findViews() {
+        mToolbar = (Toolbar) findViewById(R.id.toolbar);
+        mToolbar.setTitle("");
+        mTvToolbarTitle = (TextView) findViewById(R.id.tv_toolbar_title);
+        mTvOverAction = (TextView) findViewById(R.id.tv_over_action);
+        mToolbarDivider = findViewById(R.id.toolbar_divider);
+    }
+
+    @Override
+    protected void setTheme() {
+        Drawable closeDrawable = ThemeUtils.resolveDrawable(this, R.attr.gallery_toolbar_close_image, R.drawable.gallery_default_toolbar_close_image);
+        int closeColor = ThemeUtils.resolveColor(this, R.attr.gallery_toolbar_close_color, R.color.gallery_default_toolbar_widget_color);
+        closeDrawable.setColorFilter(closeColor, PorterDuff.Mode.SRC_ATOP);
+        mToolbar.setNavigationIcon(closeDrawable);
+
+        int overButtonBg = ThemeUtils.resolveDrawableRes(this, R.attr.gallery_toolbar_over_button_bg);
+        if (overButtonBg != 0) {
+            mTvOverAction.setBackgroundResource(overButtonBg);
+        } else {
+            OsCompat.setBackgroundDrawableCompat(mTvOverAction, createDefaultOverButtonBgDrawable());
+        }
+
+        float overTextSize = ThemeUtils.resolveDimen(this, R.attr.gallery_toolbar_over_button_text_size, R.dimen.gallery_default_toolbar_over_button_text_size);
+        mTvOverAction.setTextSize(TypedValue.COMPLEX_UNIT_PX, overTextSize);
+
+        int overTextColor = ThemeUtils.resolveColor(this, R.attr.gallery_toolbar_over_button_text_color, R.color.gallery_default_toolbar_over_button_text_color);
+        mTvOverAction.setTextColor(overTextColor);
+
+        float titleTextSize = ThemeUtils.resolveDimen(this, R.attr.gallery_toolbar_text_size, R.dimen.gallery_default_toolbar_text_size);
+        mTvToolbarTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextSize);
+
+        int titleTextColor = ThemeUtils.resolveColor(this, R.attr.gallery_toolbar_text_color, R.color.gallery_default_toolbar_text_color);
+        mTvToolbarTitle.setTextColor(titleTextColor);
+
+        int gravity = ThemeUtils.resolveInteger(this, R.attr.gallery_toolbar_text_gravity, R.integer.gallery_default_toolbar_text_gravity);
+        mTvToolbarTitle.setLayoutParams(new Toolbar.LayoutParams(Toolbar.LayoutParams.WRAP_CONTENT, Toolbar.LayoutParams.WRAP_CONTENT, gravity));
+
+        int toolbarBg = ThemeUtils.resolveColor(this, R.attr.gallery_toolbar_bg, R.color.gallery_default_color_toolbar_bg);
+        mToolbar.setBackgroundColor(toolbarBg);
+
+        int toolbarHeight = (int) ThemeUtils.resolveDimen(this, R.attr.gallery_toolbar_height, R.dimen.gallery_default_toolbar_height);
+        mToolbar.setMinimumHeight(toolbarHeight);
+
+        int statusBarColor = ThemeUtils.resolveColor(this, R.attr.gallery_color_statusbar, R.color.gallery_default_color_statusbar);
+        ThemeUtils.setStatusBarColor(statusBarColor, getWindow());
+
+        int dividerHeight = (int) ThemeUtils.resolveDimen(this, R.attr.gallery_toolbar_divider_height, R.dimen.gallery_default_toolbar_divider_height);
+        int dividerBottomMargin = (int) ThemeUtils.resolveDimen(this, R.attr.gallery_toolbar_bottom_margin, R.dimen.gallery_default_toolbar_bottom_margin);
+        LayoutParams dividerLP = new LayoutParams(LayoutParams.MATCH_PARENT, dividerHeight);
+        dividerLP.bottomMargin = dividerBottomMargin;
+        mToolbarDivider.setLayoutParams(dividerLP);
+
+        Drawable dividerDrawable = ThemeUtils.resolveDrawable(this, R.attr.gallery_toolbar_divider_bg, R.color.gallery_default_toolbar_divider_bg);
+        OsCompat.setBackgroundDrawableCompat(mToolbarDivider, dividerDrawable);
+
+        setSupportActionBar(mToolbar);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (mCheckedList != null) {
+            outState.putParcelableArrayList(EXTRA_CHECKED_LIST, mCheckedList);
+        }
+        outState.putInt(EXTRA_SELECTED_INDEX, mSelectedIndex);
+        if (mPageMediaList != null) {
+            outState.putParcelableArrayList(EXTRA_PAGE_MEDIA_LIST, mPageMediaList);
+        }
+        outState.putInt(EXTRA_PAGE_POSITION, mPagePosition);
+        outState.putInt(EXTRA_PREVIEW_POSITION, mPreviewPosition);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        List<MediaBean> list = savedInstanceState.getParcelableArrayList(EXTRA_CHECKED_LIST);
+        if (list != null && list.size() > 0) {
+            mCheckedList.clear();
+            mCheckedList.addAll(list);
+        }
+        mPageMediaList = savedInstanceState.getParcelableArrayList(EXTRA_PAGE_MEDIA_LIST);
+        mPagePosition = savedInstanceState.getInt(EXTRA_PAGE_POSITION);
+        mPreviewPosition = savedInstanceState.getInt(EXTRA_PREVIEW_POSITION);
+        mSelectedIndex = savedInstanceState.getInt(EXTRA_SELECTED_INDEX);
+        if (!mConfiguration.isRadio()) {
+            switch (mSelectedIndex) {
+                case 1:
+                    showMediaPageFragment(mPageMediaList, mPagePosition);
+                    break;
+                case 2:
+                    showMediaPreviewFragment();
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == android.R.id.home) {
+            backAction();
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public void showMediaGridFragment() {
+        mMediaPreviewFragment = null;
+        mMediaPageFragment = null;
+        mSelectedIndex = 0;
+
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction()
+                .replace(R.id.fragment_container, mMediaGridFragment);
+        if (mMediaPreviewFragment != null) {
+            ft.hide(mMediaPreviewFragment);
+        }
+        if (mMediaPageFragment != null) {
+            ft.hide(mMediaPageFragment);
+        }
+        ft.show(mMediaGridFragment)
+                .commit();
+
+        if (mConfiguration.isImage()) {
+            mTvToolbarTitle.setText(R.string.gallery_media_grid_image_title);
+        } else {
+            mTvToolbarTitle.setText(R.string.gallery_media_grid_video_title);
+        }
+    }
+
+    @Override
+    public void showMediaPageFragment(ArrayList<MediaBean> list, int position) {
+        mSelectedIndex = 1;
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+        mMediaPageFragment = MediaPageFragment.newInstance(mConfiguration, list, position);
+        ft.add(R.id.fragment_container, mMediaPageFragment);
+        mMediaPreviewFragment = null;
+        ft.hide(mMediaGridFragment);
+        ft.show(mMediaPageFragment);
+        ft.commit();
+
+        String title = getString(R.string.gallery_page_title, position + 1, list.size());
+        mTvToolbarTitle.setText(title);
+    }
+
+    @Override
+    public void showMediaPreviewFragment() {
+        mSelectedIndex = 2;
+        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+        mMediaPreviewFragment = MediaPreviewFragment.newInstance(mConfiguration, mPreviewPosition);
+        ft.add(R.id.fragment_container, mMediaPreviewFragment);
+        mMediaPageFragment = null;
+        ft.hide(mMediaGridFragment);
+        ft.show(mMediaPreviewFragment);
+        ft.commit();
+
+        String title = getString(R.string.gallery_page_title, mPreviewPosition, mCheckedList.size());
+        mTvToolbarTitle.setText(title);
+    }
+
+    private void subscribeEvent() {
+        Disposable subscriptionOpenMediaPreviewEvent = RxBus.getDefault().toObservable(OpenMediaPreviewFragmentEvent.class)
+//                .map(mediaPreviewEvent -> mediaPreviewEvent)
+                .subscribeWith(new RxBusDisposable<OpenMediaPreviewFragmentEvent>() {
+                    @Override
+                    protected void onEvent(OpenMediaPreviewFragmentEvent openMediaPreviewFragmentEvent) {
+                        mPreviewPosition = 0;
+                        showMediaPreviewFragment();
+                    }
+                });
+
+        RxBus.getDefault().add(subscriptionOpenMediaPreviewEvent);
+
+        Disposable subscriptionMediaCheckChangeEvent = RxBus.getDefault().toObservable(MediaCheckChangeEvent.class)
+//                .map(mediaCheckChangeEvent -> mediaCheckChangeEvent)
+                .subscribeWith(new RxBusDisposable<MediaCheckChangeEvent>() {
+                    @Override
+                    protected void onEvent(MediaCheckChangeEvent mediaCheckChangeEvent) {
+                        MediaBean mediaBean = mediaCheckChangeEvent.getMediaBean();
+                        if (mCheckedList.contains(mediaBean)) {
+                            mCheckedList.remove(mediaBean);
+                        } else {
+                            mCheckedList.add(mediaBean);
+                        }
+
+                        if (mCheckedList.size() > 0) {
+                            String text = getResources().getString(R.string.gallery_over_button_text_checked, mCheckedList.size(), mConfiguration.getMaxSize());
+                            mTvOverAction.setText(text);
+                            mTvOverAction.setEnabled(true);
+                        } else {
+                            mTvOverAction.setText(R.string.gallery_over_button_text);
+                            mTvOverAction.setEnabled(false);
+                        }
+                    }
+                });
+        RxBus.getDefault().add(subscriptionMediaCheckChangeEvent);
+
+        Disposable subscriptionMediaViewPagerChangedEvent = RxBus.getDefault().toObservable(MediaViewPagerChangedEvent.class)
+//                .map(mediaViewPagerChangedEvent -> mediaViewPagerChangedEvent)
+                .subscribeWith(new RxBusDisposable<MediaViewPagerChangedEvent>() {
+                    @Override
+                    protected void onEvent(MediaViewPagerChangedEvent mediaPreviewViewPagerChangedEvent) {
+                        int curIndex = mediaPreviewViewPagerChangedEvent.getCurIndex();
+                        int totalSize = mediaPreviewViewPagerChangedEvent.getTotalSize();
+                        if (mediaPreviewViewPagerChangedEvent.isPreview()) {
+                            mPreviewPosition = curIndex;
+                        } else {
+                            mPagePosition = curIndex;
+                        }
+                        String title = getString(R.string.gallery_page_title, curIndex + 1, totalSize);
+                        mTvToolbarTitle.setText(title);
+                    }
+                });
+        RxBus.getDefault().add(subscriptionMediaViewPagerChangedEvent);
+
+        Disposable subscriptionCloseRxMediaGridPageEvent = RxBus.getDefault().toObservable(CloseRxMediaGridPageEvent.class)
+                .subscribeWith(new RxBusDisposable<CloseRxMediaGridPageEvent>() {
+                    @Override
+                    protected void onEvent(CloseRxMediaGridPageEvent closeRxMediaGridPageEvent) throws Exception {
+                        finish();
+                    }
+                });
+        RxBus.getDefault().add(subscriptionCloseRxMediaGridPageEvent);
+
+        Disposable subscriptionOpenMediaPageFragmentEvent = RxBus.getDefault().toObservable(OpenMediaPageFragmentEvent.class)
+                .subscribeWith(new RxBusDisposable<OpenMediaPageFragmentEvent>() {
+                    @Override
+                    protected void onEvent(OpenMediaPageFragmentEvent openMediaPageFragmentEvent) {
+                        mPageMediaList = openMediaPageFragmentEvent.getMediaBeanList();
+                        mPagePosition = openMediaPageFragmentEvent.getPosition();
+
+                        showMediaPageFragment(mPageMediaList, mPagePosition);
+                    }
+                });
+        RxBus.getDefault().add(subscriptionOpenMediaPageFragmentEvent);
+    }
+
+    public List<MediaBean> getCheckedList() {
+        return mCheckedList;
+    }
+
+    private void backAction() {
+        if (mMediaGridFragment != null && mMediaGridFragment.isShowRvBucketView()) {
+            mMediaGridFragment.hideRvBucketView();
+            return;
+        }
+        if ((mMediaPreviewFragment != null && mMediaPreviewFragment.isVisible())
+                || (mMediaPageFragment != null && mMediaPageFragment.isVisible())) {
+            showMediaGridFragment();
+            return;
+        }
+        onBackPressed();
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            backAction();
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        RxBus.getDefault().removeAllStickyEvents();
+        RxBus.getDefault().clear();
+        RxJob.getDefault().clearJob();
+    }
+
+    private StateListDrawable createDefaultOverButtonBgDrawable() {
+        int dp12 = (int) ThemeUtils.applyDimensionDp(this, 12.f);
+        int dp8 = (int) ThemeUtils.applyDimensionDp(this, 8.f);
+        float dp4 = ThemeUtils.applyDimensionDp(this, 4.f);
+        float[] round = new float[]{dp4, dp4, dp4, dp4, dp4, dp4, dp4, dp4};
+        ShapeDrawable pressedDrawable = new ShapeDrawable(new RoundRectShape(round, null, null));
+        pressedDrawable.setPadding(dp12, dp8, dp12, dp8);
+        int pressedColor = ThemeUtils.resolveColor(this, R.attr.gallery_toolbar_over_button_pressed_color, R.color.gallery_default_toolbar_over_button_pressed_color);
+        pressedDrawable.getPaint().setColor(pressedColor);
+
+        int normalColor = ThemeUtils.resolveColor(this, R.attr.gallery_toolbar_over_button_normal_color, R.color.gallery_default_toolbar_over_button_normal_color);
+        ShapeDrawable normalDrawable = new ShapeDrawable(new RoundRectShape(round, null, null));
+        normalDrawable.setPadding(dp12, dp8, dp12, dp8);
+        normalDrawable.getPaint().setColor(normalColor);
+
+        StateListDrawable stateListDrawable = new StateListDrawable();
+        stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressedDrawable);
+        stateListDrawable.addState(new int[]{}, normalDrawable);
+
+        return stateListDrawable;
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        Logger.i("onRequestPermissionsResult:requestCode=" + requestCode + " permissions=" + permissions[0]);
+        switch (requestCode) {
+            case REQUEST_STORAGE_READ_ACCESS_PERMISSION:
+                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    RxBus.getDefault().post(new RequestStorageReadAccessPermissionEvent(true, RequestStorageReadAccessPermissionEvent.TYPE_WRITE));
+                } else {
+                    finish();
+                }
+                break;
+            case REQUEST_STORAGE_WRITE_ACCESS_PERMISSION:
+                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    RxBus.getDefault().post(new RequestStorageReadAccessPermissionEvent(true, RequestStorageReadAccessPermissionEvent.TYPE_WRITE));
+                } else {
+                    finish();
+                }
+                break;
+            case REQUEST_CAMERA_ACCESS_PERMISSION:
+                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    RxBus.getDefault().post(new RequestStorageReadAccessPermissionEvent(true, RequestStorageReadAccessPermissionEvent.TYPE_CAMERA));
+                }
+                break;
+            default:
+                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+        }
+    }
+
+
+}

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/activity/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * activity包
+ */
+package cn.finalteam.rxgalleryfinal.ui.activity;

+ 151 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/BucketAdapter.java

@@ -0,0 +1,151 @@
+package cn.finalteam.rxgalleryfinal.ui.adapter;
+
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
+import android.support.v4.widget.CompoundButtonCompat;
+import android.support.v7.widget.AppCompatRadioButton;
+import android.support.v7.widget.RecyclerView;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RadioButton;
+import android.widget.TextView;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.Configuration;
+import cn.finalteam.rxgalleryfinal.R;
+import cn.finalteam.rxgalleryfinal.bean.BucketBean;
+import cn.finalteam.rxgalleryfinal.ui.widget.SquareImageView;
+import cn.finalteam.rxgalleryfinal.utils.ThemeUtils;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/4 下午5:40
+ */
+public class BucketAdapter extends RecyclerView.Adapter<BucketAdapter.BucketViewHolder> {
+
+    private final List<BucketBean> mBucketList;
+    private final Drawable mDefaultImage;
+    private final Configuration mConfiguration;
+    private OnRecyclerViewItemClickListener mOnRecyclerViewItemClickListener;
+    private BucketBean mSelectedBucket;
+
+    public BucketAdapter(
+            List<BucketBean> bucketList,
+            Configuration configuration,
+            @ColorInt int color) {
+        this.mBucketList = bucketList;
+        this.mConfiguration = configuration;
+        this.mDefaultImage = new ColorDrawable(color);
+    }
+
+    @Override
+    public BucketViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.gallery_adapter_bucket_item, parent, false);
+        return new BucketViewHolder(parent, view);
+    }
+
+    @Override
+    public void onBindViewHolder(BucketViewHolder holder, int position) {
+        BucketBean bucketBean = mBucketList.get(position);
+        String bucketName = bucketBean.getBucketName();
+        if (position != 0) {
+            SpannableString nameSpannable = new SpannableString(bucketName + "\n" + bucketBean.getImageCount() + "张");
+            nameSpannable.setSpan(new ForegroundColorSpan(Color.GRAY), bucketName.length(), nameSpannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            nameSpannable.setSpan(new RelativeSizeSpan(0.8f), bucketName.length(), nameSpannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            holder.mTvBucketName.setText(nameSpannable);
+        } else {
+            holder.mTvBucketName.setText(bucketName);
+        }
+        if (mSelectedBucket != null && TextUtils.equals(mSelectedBucket.getBucketId(), bucketBean.getBucketId())) {
+            holder.mRbSelected.setVisibility(View.VISIBLE);
+            holder.mRbSelected.setChecked(true);
+        } else {
+            holder.mRbSelected.setVisibility(View.GONE);
+        }
+
+        String path = bucketBean.getCover();
+        mConfiguration.getImageLoader()
+                .displayImage(holder.itemView.getContext(), path, holder.mIvBucketCover, mDefaultImage, mConfiguration.getImageConfig(),
+                        true, mConfiguration.isPlayGif(), 100, 100, bucketBean.getOrientation());
+    }
+
+    public void setSelectedBucket(BucketBean bucketBean) {
+        this.mSelectedBucket = bucketBean;
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public int getItemCount() {
+        return mBucketList.size();
+    }
+
+    public void setOnRecyclerViewItemClickListener(OnRecyclerViewItemClickListener listener) {
+        this.mOnRecyclerViewItemClickListener = listener;
+    }
+
+    public interface OnRecyclerViewItemClickListener {
+        void onItemClick(View view, int position);
+    }
+
+    class BucketViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+
+        final TextView mTvBucketName;
+        final SquareImageView mIvBucketCover;
+        final AppCompatRadioButton mRbSelected;
+
+        private final ViewGroup mParentView;
+
+        BucketViewHolder(ViewGroup parent, View itemView) {
+            super(itemView);
+            this.mParentView = parent;
+            mTvBucketName = (TextView) itemView.findViewById(R.id.tv_bucket_name);
+            mIvBucketCover = (SquareImageView) itemView.findViewById(R.id.iv_bucket_cover);
+            mRbSelected = (AppCompatRadioButton) itemView.findViewById(R.id.rb_selected);
+
+            itemView.setOnClickListener(this);
+
+            int checkTint = ThemeUtils.resolveColor(itemView.getContext(), R.attr.gallery_checkbox_button_tint_color, R.color.gallery_default_checkbox_button_tint_color);
+            CompoundButtonCompat.setButtonTintList(mRbSelected, ColorStateList.valueOf(checkTint));
+        }
+
+        @Override
+        public void onClick(View v) {
+            if (mOnRecyclerViewItemClickListener != null) {
+                mOnRecyclerViewItemClickListener.onItemClick(v, getLayoutPosition());
+            }
+
+            setRadioDisChecked(mParentView);
+            mRbSelected.setVisibility(View.VISIBLE);
+            mRbSelected.setChecked(true);
+        }
+
+        /**
+         * 设置未所有Item为未选中
+         */
+        private void setRadioDisChecked(ViewGroup parentView) {
+            if (parentView == null || parentView.getChildCount() < 1) {
+                return;
+            }
+
+            for (int i = 0; i < parentView.getChildCount(); i++) {
+                View itemView = parentView.getChildAt(i);
+                RadioButton rbSelect = (RadioButton) itemView.findViewById(R.id.rb_selected);
+                if (rbSelect != null) {
+                    rbSelect.setVisibility(View.GONE);
+                    rbSelect.setChecked(false);
+                }
+            }
+        }
+    }
+}

+ 235 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/MediaGridAdapter.java

@@ -0,0 +1,235 @@
+package cn.finalteam.rxgalleryfinal.ui.adapter;
+
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.widget.CompoundButtonCompat;
+import android.support.v7.widget.AppCompatCheckBox;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+
+import org.w3c.dom.Text;
+
+import java.io.File;
+import java.util.List;
+import java.util.Locale;
+
+import cn.finalteam.rxgalleryfinal.Configuration;
+import cn.finalteam.rxgalleryfinal.R;
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBus;
+import cn.finalteam.rxgalleryfinal.rxbus.event.MediaCheckChangeEvent;
+import cn.finalteam.rxgalleryfinal.rxjob.Job;
+import cn.finalteam.rxgalleryfinal.rxjob.RxJob;
+import cn.finalteam.rxgalleryfinal.rxjob.job.ImageThmbnailJobCreate;
+import cn.finalteam.rxgalleryfinal.ui.activity.MediaActivity;
+import cn.finalteam.rxgalleryfinal.ui.base.IMultiImageCheckedListener;
+import cn.finalteam.rxgalleryfinal.ui.widget.FixImageView;
+import cn.finalteam.rxgalleryfinal.ui.widget.SquareRelativeLayout;
+import cn.finalteam.rxgalleryfinal.utils.Logger;
+import cn.finalteam.rxgalleryfinal.utils.OsCompat;
+import cn.finalteam.rxgalleryfinal.utils.ThemeUtils;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/18 下午7:48
+ */
+public class MediaGridAdapter extends RecyclerView.Adapter<MediaGridAdapter.GridViewHolder> {
+
+    private static IMultiImageCheckedListener iMultiImageCheckedListener;
+    private final MediaActivity mMediaActivity;
+    private final List<MediaBean> mMediaBeanList;
+    private final int mImageSize;
+    private final Configuration mConfiguration;
+    private final Drawable mDefaultImage;
+    private final Drawable mImageViewBg;
+    private final Drawable mCameraImage;
+    private final int mCameraImageBgColor;
+    private final int mCameraTextColor;
+    private int imageLoaderType = 0;
+
+    public MediaGridAdapter(
+            MediaActivity mediaActivity,
+            List<MediaBean> list,
+            int screenWidth,
+            Configuration configuration) {
+        this.mMediaActivity = mediaActivity;
+        this.mMediaBeanList = list;
+        this.mImageSize = screenWidth / 3;
+        int defaultResId = ThemeUtils.resolveDrawableRes(mediaActivity, R.attr.gallery_default_image, R.drawable.gallery_default_image);
+        this.mDefaultImage = ContextCompat.getDrawable(mediaActivity, defaultResId);
+        this.mConfiguration = configuration;
+        this.imageLoaderType = configuration.getImageLoaderType();
+        this.mImageViewBg = ThemeUtils.resolveDrawable(mMediaActivity, R.attr.gallery_imageview_bg, R.drawable.gallery_default_image);
+        this.mCameraImage = ThemeUtils.resolveDrawable(mMediaActivity, R.attr.gallery_camera_image, R.drawable.gallery_ic_camera);
+        this.mCameraImageBgColor = ThemeUtils.resolveColor(mMediaActivity, R.attr.gallery_camera_bg, R.color.gallery_default_camera_bg_color);
+        this.mCameraTextColor = ThemeUtils.resolveColor(mMediaActivity, R.attr.gallery_take_image_text_color, R.color.gallery_default_take_image_text_color);
+    }
+
+    public static void setCheckedListener(IMultiImageCheckedListener checkedListener) {
+        iMultiImageCheckedListener = checkedListener;
+    }
+
+    @Override
+    public GridViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+//        View view;
+//        if (imageLoaderType != 3) {
+//            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_gallery_media_grid, parent, false);
+//        } else {
+//            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_gallery_media_grid_fresco, parent, false);
+//        }
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_gallery_media_grid, parent, false);
+        return new GridViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(GridViewHolder holder, int position) {
+        MediaBean mediaBean = mMediaBeanList.get(position);
+        if (mediaBean.getId() == Integer.MIN_VALUE) {
+            holder.mCbCheck.setVisibility(View.GONE);
+            holder.mIvMediaImage.setVisibility(View.GONE);
+            holder.mLlCamera.setVisibility(View.VISIBLE);
+            holder.mIvCameraImage.setImageDrawable(mCameraImage);
+            holder.mTvCameraTxt.setTextColor(mCameraTextColor);
+            holder.mTvCameraTxt.setText(mConfiguration.isImage() ? mMediaActivity.getString(R.string.gallery_take_image) : mMediaActivity.getString(R.string.gallery_video));
+            holder.mIvCameraImage.setBackgroundColor(mCameraImageBgColor);
+            holder.tv_media_duration.setVisibility(View.GONE);
+        } else {
+            if (mConfiguration.isRadio()) {
+                holder.mCbCheck.setVisibility(View.GONE);
+                holder.tv_media_duration.setVisibility(View.VISIBLE);
+                long second = mediaBean.getDuration() / 1000;
+                long m = second / 60;
+                holder.tv_media_duration.setText(String.format(Locale.CHINA, "%d:%d", m, second % 60));
+            } else {
+                holder.tv_media_duration.setVisibility(View.GONE);
+                holder.mCbCheck.setVisibility(View.VISIBLE);
+                holder.mCbCheck.setOnClickListener(new OnCheckBoxClickListener(mediaBean));
+                holder.mCbCheck.setOnCheckedChangeListener(new OnCheckBoxCheckListener(mediaBean));
+            }
+            holder.mIvMediaImage.setVisibility(View.VISIBLE);
+            holder.mLlCamera.setVisibility(View.GONE);
+            holder.mCbCheck.setChecked(mMediaActivity.getCheckedList() != null && mMediaActivity.getCheckedList().contains(mediaBean));
+            String bitPath = mediaBean.getThumbnailBigPath();
+            String smallPath = mediaBean.getThumbnailSmallPath();
+
+            if (!new File(bitPath).exists() || !new File(smallPath).exists()) {
+                Job job = new ImageThmbnailJobCreate(mMediaActivity, mediaBean).create();
+                RxJob.getDefault().addJob(job);
+            }
+            String path;
+            if (mConfiguration.isPlayGif() && (imageLoaderType == 3 || imageLoaderType == 2)) {
+                path = mediaBean.getOriginalPath();
+            } else {
+                path = mediaBean.getThumbnailSmallPath();
+                if (TextUtils.isEmpty(path)) {
+                    path = mediaBean.getThumbnailBigPath();
+                }
+                if (TextUtils.isEmpty(path)) {
+                    path = mediaBean.getOriginalPath();
+                }
+            }
+            Logger.w("提示path:" + path);
+//            if (imageLoaderType != 3) {
+                OsCompat.setBackgroundDrawableCompat(holder.mIvMediaImage, mImageViewBg);
+                mConfiguration.getImageLoader()
+                        .displayImage(mMediaActivity, path, (FixImageView) holder.mIvMediaImage, mDefaultImage, mConfiguration.getImageConfig(),
+                                true, mConfiguration.isPlayGif(), mImageSize, mImageSize, mediaBean.getOrientation());
+//            } else {
+//                OsCompat.setBackgroundDrawableCompat(holder.mIvMediaImage, mImageViewBg);
+//                FrescoImageLoader.setImageSmall("file://" + path, (SimpleDraweeView) holder.mIvMediaImage,
+//                        mImageSize, mImageSize, holder.relativeLayout, mConfiguration.isPlayGif());
+//            }
+        }
+    }
+
+    @Override
+    public int getItemCount() {
+        return mMediaBeanList.size();
+    }
+
+
+    static class GridViewHolder extends RecyclerView.ViewHolder {
+
+        final AppCompatCheckBox mCbCheck;
+        final TextView tv_media_duration;
+        final LinearLayout mLlCamera;
+        final TextView mTvCameraTxt;
+        final ImageView mIvCameraImage;
+        View mIvMediaImage;
+        SquareRelativeLayout relativeLayout;
+
+
+        GridViewHolder(View itemView) {
+            super(itemView);
+            mIvMediaImage = itemView.findViewById(R.id.iv_media_image);
+            mCbCheck = (AppCompatCheckBox) itemView.findViewById(R.id.cb_check);
+            relativeLayout = (SquareRelativeLayout) itemView.findViewById(R.id.rootView);
+            mLlCamera = (LinearLayout) itemView.findViewById(R.id.ll_camera);
+            mTvCameraTxt = (TextView) itemView.findViewById(R.id.tv_camera_txt);
+            mIvCameraImage = (ImageView) itemView.findViewById(R.id.iv_camera_image);
+            tv_media_duration = itemView.findViewById(R.id.tv_media_duration);
+
+            int checkTint = ThemeUtils.resolveColor(itemView.getContext(), R.attr.gallery_checkbox_button_tint_color, R.color.gallery_default_checkbox_button_tint_color);
+            CompoundButtonCompat.setButtonTintList(mCbCheck, ColorStateList.valueOf(checkTint));
+        }
+    }
+
+    private class OnCheckBoxClickListener implements View.OnClickListener {
+
+        private final MediaBean mediaBean;
+
+        OnCheckBoxClickListener(MediaBean bean) {
+            this.mediaBean = bean;
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (mConfiguration.getMaxSize() == mMediaActivity.getCheckedList().size() &&
+                    !mMediaActivity.getCheckedList().contains(mediaBean)) {
+                AppCompatCheckBox checkBox = (AppCompatCheckBox) view;
+                checkBox.setChecked(false);
+                Logger.i("=>" + mMediaActivity.getResources().getString(R.string.gallery_image_max_size_tip, mConfiguration.getMaxSize()));
+            } else {
+                RxBus.getDefault().post(new MediaCheckChangeEvent(mediaBean));
+            }
+        }
+    }
+
+    /**
+     * @author KARL-dujinyang
+     */
+    private class OnCheckBoxCheckListener implements CompoundButton.OnCheckedChangeListener {
+        private final MediaBean mediaBean;
+
+        OnCheckBoxCheckListener(MediaBean bean) {
+            this.mediaBean = bean;
+        }
+
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            if (mConfiguration.getMaxSize() == mMediaActivity.getCheckedList().size() &&
+                    !mMediaActivity.getCheckedList().contains(mediaBean)) {
+                AppCompatCheckBox checkBox = (AppCompatCheckBox) buttonView;
+                checkBox.setChecked(false);
+                Logger.i("选中:" + mMediaActivity.getResources().getString(R.string.gallery_image_max_size_tip, mConfiguration.getMaxSize()));
+                if (iMultiImageCheckedListener != null) {
+                    iMultiImageCheckedListener.selectedImgMax(buttonView, isChecked, mConfiguration.getMaxSize());
+                }
+            } else {
+                if (iMultiImageCheckedListener != null)
+                    iMultiImageCheckedListener.selectedImg(buttonView, isChecked);
+            }
+
+        }
+    }
+}

+ 67 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/MediaPreviewAdapter.java

@@ -0,0 +1,67 @@
+package cn.finalteam.rxgalleryfinal.ui.adapter;
+
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.Configuration;
+import cn.finalteam.rxgalleryfinal.R;
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import uk.co.senab.photoview.PhotoView;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/21 下午10:12
+ */
+public class MediaPreviewAdapter extends RecyclingPagerAdapter {
+
+    private final List<MediaBean> mMediaList;
+    private final Configuration mConfiguration;
+    private final Drawable mDefaultImage;
+    private final int mScreenWidth;
+    private final int mScreenHeight;
+    private final int mPageColor;
+
+    public MediaPreviewAdapter(List<MediaBean> list,
+                               int screenWidth,
+                               int screenHeight,
+                               Configuration configuration,
+                               int pageColor,
+                               Drawable drawable) {
+        this.mMediaList = list;
+        this.mScreenWidth = screenWidth;
+        this.mScreenHeight = screenHeight;
+        this.mConfiguration = configuration;
+        this.mPageColor = pageColor;
+        this.mDefaultImage = drawable;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup container) {
+        MediaBean mediaBean = mMediaList.get(position);
+        if (convertView == null) {
+            convertView = View.inflate(container.getContext(), R.layout.gallery_media_image_preview_item, null);
+        }
+        PhotoView ivImage = (PhotoView) convertView.findViewById(R.id.iv_media_image);
+        String path = null;
+        if (mediaBean.getWidth() > 1200 || mediaBean.getHeight() > 1200) {
+            path = mediaBean.getThumbnailBigPath();
+        }
+        if (TextUtils.isEmpty(path)) {
+            path = mediaBean.getOriginalPath();
+        }
+        ivImage.setBackgroundColor(mPageColor);
+        mConfiguration.getImageLoader().displayImage(container.getContext(), path, ivImage, mDefaultImage, mConfiguration.getImageConfig(),
+                false, mConfiguration.isPlayGif(), mScreenWidth, mScreenHeight, mediaBean.getOrientation());
+        return convertView;
+    }
+
+    @Override
+    public int getCount() {
+        return mMediaList.size();
+    }
+}

+ 270 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/RecyclingPagerAdapter.java

@@ -0,0 +1,270 @@
+package cn.finalteam.rxgalleryfinal.ui.adapter;
+
+import android.os.Build;
+import android.support.v4.view.PagerAdapter;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+
+/**
+ * Desction:
+ * A {@link PagerAdapter} which behaves like an {@link android.widget.Adapter} with view types and
+ * view recycling.
+ * Author:pengjianbo  Dujinyang
+ * Date:15/12/22 下午6:21
+ */
+abstract class RecyclingPagerAdapter extends PagerAdapter {
+    private static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
+
+    private final RecycleBin recycleBin;
+
+    RecyclingPagerAdapter() {
+        this(new RecycleBin());
+    }
+
+    private RecyclingPagerAdapter(RecycleBin recycleBin) {
+        this.recycleBin = recycleBin;
+        recycleBin.setViewTypeCount(getViewTypeCount());
+    }
+
+    @Override
+    public void notifyDataSetChanged() {
+        recycleBin.scrapActiveViews();
+        super.notifyDataSetChanged();
+    }
+
+    @Override
+    public final Object instantiateItem(ViewGroup container, int position) {
+        int viewType = getItemViewType(position);
+        View view = null;
+        if (viewType != IGNORE_ITEM_VIEW_TYPE) {
+            view = recycleBin.getScrapView(position, viewType);
+        }
+        view = getView(position, view, container);
+        container.addView(view);
+        return view;
+    }
+
+    @Override
+    public final void destroyItem(ViewGroup container, int position, Object object) {
+        View view = (View) object;
+        container.removeView(view);
+        int viewType = getItemViewType(position);
+        if (viewType != IGNORE_ITEM_VIEW_TYPE) {
+            recycleBin.addScrapView(view, position, viewType);
+        }
+    }
+
+    @Override
+    public final boolean isViewFromObject(View view, Object object) {
+        return view == object;
+    }
+
+    /**
+     * <p>
+     * Returns the number of types of Views that will be created by
+     * {@link #getView}. Each type represents a set of views that can be
+     * converted in {@link #getView}. If the adapter always returns the same
+     * type of View for all items, this method should return 1.
+     * </p>
+     * <p>
+     * This method will only be called when when the adapter is set on the
+     * the {@link AdapterView}.
+     * </p>
+     *
+     * @return The number of types of Views that will be created by this adapter
+     */
+    private int getViewTypeCount() {
+        return 1;
+    }
+
+    /**
+     * Get the type of View that will be created by {@link #getView} for the specified item.
+     *
+     * @param position The position of the item within the adapter's data set whose view type we
+     *                 want.
+     * @return An integer representing the type of View. Two views should share the same type if one
+     * can be converted to the other in {@link #getView}. Note: Integers must be in the
+     * range 0 to {@link #getViewTypeCount} - 1. {@link #IGNORE_ITEM_VIEW_TYPE} can
+     * also be returned.
+     * @see #IGNORE_ITEM_VIEW_TYPE
+     */
+    @SuppressWarnings("UnusedParameters") // Argument potentially used by subclasses.
+    private int getItemViewType(int position) {
+        return 0;
+    }
+
+    /**
+     * Get a View that displays the data at the specified position in the data set. You can either
+     * create a View manually or inflate it from an XML layout file. When the View is inflated, the
+     * parent View (GridView, ListView...) will apply default layout parameters unless you use
+     * {@link android.view.LayoutInflater#inflate(int, ViewGroup, boolean)}
+     * to specify a root view and to prevent attachment to the root.
+     *
+     * @param position    The position of the item within the adapter's data set of the item whose view
+     *                    we want.
+     * @param convertView The old view to reuse, if possible. Note: You should check that this view
+     *                    is non-null and of an appropriate type before using. If it is not possible to convert
+     *                    this view to display the correct data, this method can create a new view.
+     *                    Heterogeneous lists can specify their number of view types, so that this View is
+     *                    always of the right type (see {@link #getViewTypeCount()} and
+     *                    {@link #getItemViewType(int)}).
+     * @param container   The parent that this view will eventually be attached to
+     * @return A View corresponding to the data at the specified position.
+     */
+    protected abstract View getView(int position, View convertView, ViewGroup container);
+
+    /**
+     * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
+     * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
+     * start of a layout. By construction, they are displaying current information. At the end of
+     * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
+     * could potentially be used by the adapter to avoid allocating views unnecessarily.
+     * <p>
+     * This class was taken from Android's implementation of {@link android.widget.AbsListView} which
+     * is copyrighted 2006 The Android Open Source Project.
+     */
+    public static class RecycleBin {
+        /**
+         * Views that were on screen at the start of layout. This array is populated at the start of
+         * layout, and at the end of layout all view in activeViews are moved to scrapViews.
+         * Views in activeViews represent a contiguous range of Views, with position of the first
+         * view store in mFirstActivePosition.
+         */
+        private final View[] activeViews = new View[0];
+        private final int[] activeViewTypes = new int[0];
+
+        /**
+         * Unsorted views that can be used by the adapter as a convert view.
+         */
+        private SparseArray<View>[] scrapViews;
+
+        private int viewTypeCount;
+
+        private SparseArray<View> currentScrapViews;
+
+        static View retrieveFromScrap(SparseArray<View> scrapViews, int position) {
+            int size = scrapViews.size();
+            if (size > 0) {
+                // See if we still have a view for this position.
+                for (int i = 0; i < size; i++) {
+                    int fromPosition = scrapViews.keyAt(i);
+                    View view = scrapViews.get(fromPosition);
+                    if (fromPosition == position) {
+                        scrapViews.remove(fromPosition);
+                        return view;
+                    }
+                }
+                int index = size - 1;
+                View r = scrapViews.valueAt(index);
+                scrapViews.remove(scrapViews.keyAt(index));
+                return r;
+            } else {
+                return null;
+            }
+        }
+
+        public void setViewTypeCount(int viewTypeCount) {
+            if (viewTypeCount < 1) {
+                throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
+            }
+            //noinspection unchecked
+            SparseArray<View>[] scrapViews = new SparseArray[viewTypeCount];
+            for (int i = 0; i < viewTypeCount; i++) {
+                scrapViews[i] = new SparseArray<>();
+            }
+            this.viewTypeCount = viewTypeCount;
+            currentScrapViews = scrapViews[0];
+            this.scrapViews = scrapViews;
+        }
+
+        boolean shouldRecycleViewType(int viewType) {
+            return viewType >= 0;
+        }
+
+        /**
+         * @return A view from the ScrapViews collection. These are unordered.
+         */
+        View getScrapView(int position, int viewType) {
+            if (viewTypeCount == 1) {
+                return retrieveFromScrap(currentScrapViews, position);
+            } else if (viewType >= 0 && viewType < scrapViews.length) {
+                return retrieveFromScrap(scrapViews[viewType], position);
+            }
+            return null;
+        }
+
+        /**
+         * Put a view into the ScrapViews list. These views are unordered.
+         *
+         * @param scrap The view to add
+         */
+        void addScrapView(View scrap, int position, int viewType) {
+            if (viewTypeCount == 1) {
+                currentScrapViews.put(position, scrap);
+            } else {
+                scrapViews[viewType].put(position, scrap);
+            }
+
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+                scrap.setAccessibilityDelegate(null);
+            }
+        }
+
+        /**
+         * Move all views remaining in activeViews to scrapViews.
+         */
+        void scrapActiveViews() {
+            final View[] activeViews = this.activeViews;
+            final int[] activeViewTypes = this.activeViewTypes;
+            final boolean multipleScraps = viewTypeCount > 1;
+
+            SparseArray<View> scrapViews = currentScrapViews;
+            final int count = activeViews.length;
+            for (int i = count - 1; i >= 0; i--) {
+                final View victim = activeViews[i];
+                if (victim != null) {
+                    int whichScrap = activeViewTypes[i];
+
+                    activeViews[i] = null;
+                    activeViewTypes[i] = -1;
+
+                    if (!shouldRecycleViewType(whichScrap)) {
+                        continue;
+                    }
+
+                    if (multipleScraps) {
+                        scrapViews = this.scrapViews[whichScrap];
+                    }
+                    scrapViews.put(i, victim);
+
+                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+                        victim.setAccessibilityDelegate(null);
+                    }
+                }
+            }
+
+            pruneScrapViews();
+        }
+
+        /**
+         * Makes sure that the size of scrapViews does not exceed the size of activeViews.
+         * (This can happen if an adapter does not recycle its views).
+         */
+        private void pruneScrapViews() {
+            final int maxViews = activeViews.length;
+            final int viewTypeCount = this.viewTypeCount;
+            final SparseArray<View>[] scrapViews = this.scrapViews;
+            for (int i = 0; i < viewTypeCount; ++i) {
+                final SparseArray<View> scrapPile = scrapViews[i];
+                int size = scrapPile.size();
+                final int extras = size - maxViews;
+                size--;
+                for (int j = 0; j < extras; j++) {
+                    scrapPile.remove(scrapPile.keyAt(size--));
+                }
+            }
+        }
+    }
+}

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * adapter包
+ */
+package cn.finalteam.rxgalleryfinal.ui.adapter;

+ 11 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/base/IMultiImageCheckedListener.java

@@ -0,0 +1,11 @@
+package cn.finalteam.rxgalleryfinal.ui.base;
+
+/**
+ * 复选
+ * Created by KARL on 2017-03-17 04-22-30.
+ */
+public interface IMultiImageCheckedListener {
+    void selectedImg(Object t, boolean isChecked);
+
+    void selectedImgMax(Object t, boolean isChecked, int maxSize);
+}

+ 18 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/base/IRadioImageCheckedListener.java

@@ -0,0 +1,18 @@
+package cn.finalteam.rxgalleryfinal.ui.base;
+
+/**
+ * 单选裁剪
+ * Created by KARL on 2017-05-31.
+ */
+public interface IRadioImageCheckedListener {
+
+    /**
+     * 裁剪之后
+     */
+    void cropAfter(Object t);
+
+    /**
+     * 返回true则关闭,false默认不关闭
+     */
+    boolean isActivityFinish();
+}

+ 222 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/BaseFragment.java

@@ -0,0 +1,222 @@
+package cn.finalteam.rxgalleryfinal.ui.fragment;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Stack;
+
+import cn.finalteam.rxgalleryfinal.BuildConfig;
+import cn.finalteam.rxgalleryfinal.Configuration;
+import cn.finalteam.rxgalleryfinal.utils.Logger;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/14 上午10:46
+ */
+public abstract class BaseFragment extends Fragment {
+
+    public static final String EXTRA_PREFIX = BuildConfig.APPLICATION_ID;
+    public static final String EXTRA_CONFIGURATION = EXTRA_PREFIX + ".Configuration";
+    private static Stack<BaseFragment> fragmentStack = new Stack<>();
+    private final String CLASS_NAME = getClass().getSimpleName();
+    protected Bundle mSaveDataBundle;
+    protected String BUNDLE_KEY = "KEY_" + CLASS_NAME;
+    protected Configuration mConfiguration;
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        printFragmentLife("onAttach");
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        printFragmentLife("onCreate");
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        printFragmentLife("onViewCreated");
+
+        Bundle argsBundle = getArguments();
+
+        if (savedInstanceState != null) {
+            mConfiguration = savedInstanceState.getParcelable(EXTRA_CONFIGURATION);
+        }
+        if (mConfiguration == null && argsBundle != null) {
+            mConfiguration = argsBundle.getParcelable(EXTRA_CONFIGURATION);
+        }
+
+        if (mConfiguration != null) {
+            if (argsBundle == null) {
+                argsBundle = savedInstanceState;
+            }
+            onViewCreatedOk(view, argsBundle);
+            setTheme();
+        } else {
+            FragmentActivity activity = getActivity();
+            if (activity != null && !activity.isFinishing()) {
+                activity.finish();
+            }
+        }
+    }
+
+    public abstract void onViewCreatedOk(View view, @Nullable Bundle savedInstanceState);
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        printFragmentLife("onCreateView");
+        return inflater.inflate(getContentView(), container, false);
+    }
+
+    @Override
+    public void startActivityForResult(Intent intent, int requestCode) {
+        Logger.i("startActivityForResult");
+        Fragment parentFragment = getParentFragment();
+        if (null != parentFragment) {
+            fragmentStack.push(this);
+            parentFragment.startActivityForResult(intent, requestCode);
+        } else {
+            super.startActivityForResult(intent, requestCode);
+        }
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        Logger.i("onActivityResult");
+        BaseFragment fragment = fragmentStack.isEmpty() ? null : fragmentStack.pop();
+        if (null != fragment) {
+            fragment.onActivityResult(requestCode, resultCode, data);
+            return;
+        }
+        super.onActivityResult(requestCode, resultCode, data);
+    }
+
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        printFragmentLife("onStart");
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        printFragmentLife("onResume");
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        printFragmentLife("onPause");
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        printFragmentLife("onDestroyView");
+        saveStateToArguments();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        printFragmentLife("onDestroy");
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        printFragmentLife("onDetach");
+    }
+
+    public abstract int getContentView();
+
+    public void setTheme() {
+    }
+
+    private void printFragmentLife(String method) {
+        Logger.i(String.format("Fragment:%s Method:%s", CLASS_NAME, method));
+    }
+
+    @Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        printFragmentLife("onActivityCreated");
+        if (!restoreStateFromArguments()) {
+            onFirstTimeLaunched();
+        }
+    }
+
+    protected abstract void onFirstTimeLaunched();
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        printFragmentLife("onSaveInstanceState");
+        saveStateToArguments();
+    }
+
+    private void saveStateToArguments() {
+        if (getView() != null) {
+            mSaveDataBundle = saveState();
+        }
+
+        if (mSaveDataBundle != null) {
+            Bundle b = getArguments();
+            if (b != null) {
+                b.putBundle(BUNDLE_KEY, mSaveDataBundle);
+            }
+        }
+    }
+
+    private boolean restoreStateFromArguments() {
+        Bundle b = getArguments();
+        if (b != null) {
+            mSaveDataBundle = b.getBundle(BUNDLE_KEY);
+            if (mSaveDataBundle != null) {
+                restoreState();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Restore Instance State Here
+     */
+    private void restoreState() {
+        if (mSaveDataBundle != null) {
+            mConfiguration = mSaveDataBundle.getParcelable(EXTRA_CONFIGURATION);
+            onRestoreState(mSaveDataBundle);
+        }
+    }
+
+    /**
+     * 恢复数据
+     */
+    protected abstract void onRestoreState(Bundle savedInstanceState);
+
+    /**
+     * Save Instance State Here
+     */
+    private Bundle saveState() {
+        Bundle state = new Bundle();
+        state.putParcelable(EXTRA_CONFIGURATION, mConfiguration);
+        onSaveState(state);
+        return state;
+    }
+
+    protected abstract void onSaveState(Bundle outState);
+}

+ 901 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/MediaGridFragment.java

@@ -0,0 +1,901 @@
+package cn.finalteam.rxgalleryfinal.ui.fragment;
+
+import android.app.Activity;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.yalantis.ucrop.UCrop;
+import com.yalantis.ucrop.UCropActivity;
+import com.yalantis.ucrop.model.AspectRatio;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Random;
+
+import cn.finalteam.rxgalleryfinal.Configuration;
+import cn.finalteam.rxgalleryfinal.R;
+import cn.finalteam.rxgalleryfinal.RxGalleryFinalApi;
+import cn.finalteam.rxgalleryfinal.anim.Animation;
+import cn.finalteam.rxgalleryfinal.anim.AnimationListener;
+import cn.finalteam.rxgalleryfinal.anim.SlideInUnderneathAnimation;
+import cn.finalteam.rxgalleryfinal.anim.SlideOutUnderneathAnimation;
+import cn.finalteam.rxgalleryfinal.bean.BucketBean;
+import cn.finalteam.rxgalleryfinal.bean.ImageCropBean;
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.presenter.impl.MediaGridPresenterImpl;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBus;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBusDisposable;
+import cn.finalteam.rxgalleryfinal.rxbus.event.CloseMediaViewPageFragmentEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.ImageRadioResultEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.MediaCheckChangeEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.OpenMediaPageFragmentEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.OpenMediaPreviewFragmentEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.RequestStorageReadAccessPermissionEvent;
+import cn.finalteam.rxgalleryfinal.ui.activity.MediaActivity;
+import cn.finalteam.rxgalleryfinal.ui.adapter.BucketAdapter;
+import cn.finalteam.rxgalleryfinal.ui.adapter.MediaGridAdapter;
+import cn.finalteam.rxgalleryfinal.ui.base.IRadioImageCheckedListener;
+import cn.finalteam.rxgalleryfinal.ui.widget.FooterAdapter;
+import cn.finalteam.rxgalleryfinal.ui.widget.HorizontalDividerItemDecoration;
+import cn.finalteam.rxgalleryfinal.ui.widget.MarginDecoration;
+import cn.finalteam.rxgalleryfinal.ui.widget.RecyclerViewFinal;
+import cn.finalteam.rxgalleryfinal.utils.CameraUtils;
+import cn.finalteam.rxgalleryfinal.utils.DeviceUtils;
+import cn.finalteam.rxgalleryfinal.utils.EmptyViewUtils;
+import cn.finalteam.rxgalleryfinal.utils.FileUtils;
+import cn.finalteam.rxgalleryfinal.utils.Logger;
+import cn.finalteam.rxgalleryfinal.utils.MediaScanner;
+import cn.finalteam.rxgalleryfinal.utils.MediaUtils;
+import cn.finalteam.rxgalleryfinal.utils.PermissionCheckUtils;
+import cn.finalteam.rxgalleryfinal.utils.SimpleDateUtils;
+import cn.finalteam.rxgalleryfinal.utils.ThemeUtils;
+import cn.finalteam.rxgalleryfinal.view.MediaGridView;
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.observers.DisposableObserver;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/7 上午10:02
+ * <p>
+ * Desction: 直接暴漏
+ * Author:KARL-Dujinyang
+ * Date:2017.
+ */
+public class MediaGridFragment extends BaseFragment implements MediaGridView, RecyclerViewFinal.OnLoadMoreListener,
+        FooterAdapter.OnItemClickListener, View.OnClickListener, MediaScanner.ScanCallback, BucketAdapter.OnRecyclerViewItemClickListener {
+
+    private static final String IMAGE_TYPE = "image/jpeg";
+    //接口-单选-是否裁剪
+    public static IRadioImageCheckedListener iListenerRadio;
+    //预留公开命名接口
+    private static File mImageStoreDir;
+    private static File mImageStoreCropDir; //裁剪目录
+    //裁剪后+name
+    private static File mCropPath = null;
+    private final String IMAGE_STORE_FILE_NAME = "IMG_%s.jpg";
+    private final String VIDEO_STORE_FILE_NAME = "IMG_%s.mp4";
+    private final int TAKE_IMAGE_REQUEST_CODE = 1001;
+    private final int CROP_IMAGE_REQUEST_CODE = 1011;
+    private final String TAKE_URL_STORAGE_KEY = "take_url_storage_key";
+    private final String BUCKET_ID_KEY = "bucket_id_key";
+    private final int LIMIT = 23;
+    MediaGridPresenterImpl mMediaGridPresenter;
+    DisplayMetrics mScreenSize;
+    private List<MediaBean> mMediaBeanList;
+    private MediaGridAdapter mMediaGridAdapter;
+    private RecyclerViewFinal mRvMedia;
+    private LinearLayout mLlEmptyView;
+    private RecyclerView mRvBucket;
+    private BucketAdapter mBucketAdapter;
+    private RelativeLayout mRlBucektOverview;
+    private List<BucketBean> mBucketBeanList;
+    private TextView mTvFolderName;
+    private TextView mTvPreview;
+    private RelativeLayout mRlRootView;
+    //扫描
+    private MediaScanner mMediaScanner;
+    private int mPage = 1;
+    private String mImagePath;
+
+    private String mBucketId = String.valueOf(Integer.MIN_VALUE);
+
+    private MediaActivity mMediaActivity;
+    private Disposable mMediaCheckChangeDisposable;
+    private Disposable mCloseMediaViewPageFragmentDisposable;
+    private Disposable mRequestStorageReadAccessPermissionDisposable;
+
+    private SlideInUnderneathAnimation slideInUnderneathAnimation;
+    private SlideOutUnderneathAnimation slideOutUnderneathAnimation;
+
+    private int uCropStatusColor;
+    private int uCropToolbarColor;
+    private int uCropActivityWidgetColor;
+    private int uCropToolbarWidgetColor;
+    private String uCropTitle;
+    private String requestStorageAccessPermissionTips;
+
+    public static MediaGridFragment newInstance(Configuration configuration) {
+        MediaGridFragment fragment = new MediaGridFragment();
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(EXTRA_CONFIGURATION, configuration);
+        fragment.setArguments(bundle);
+        return fragment;
+    }
+
+    /**
+     * getImageStoreDir
+     *
+     * @return 存储路径
+     */
+    public static File getImageStoreDirByFile() {
+        return mImageStoreDir;
+    }
+
+    /**
+     * getImageStoreDir
+     *
+     * @return 存储路径
+     */
+    public static String getImageStoreDirByStr() {
+        if (mImageStoreDir != null)
+            return mImageStoreDir.getPath();
+        else
+            return null;
+    }
+
+    /**
+     * 设置路径
+     */
+    public static void setImageStoreDir(File imgFile) {
+        Logger.i("设置图片保存路径为:" + imgFile.getAbsolutePath());
+        mImageStoreDir = imgFile;
+    }
+
+    /**
+     * 设置路径
+     */
+    public static void setImageStoreDir(String imgFile) {
+        mImageStoreDir = new File(Environment.getExternalStorageDirectory(), "/DCIM" + File.separator + imgFile + File.separator);
+        Logger.i("设置图片保存路径为:" + mImageStoreDir.getAbsolutePath());
+    }
+
+    /**
+     * getImageStoreDir裁剪
+     *
+     * @return 裁剪存储路径
+     */
+    public static File getImageStoreCropDirByFile() {
+        return mImageStoreCropDir;
+    }
+
+    /**
+     * getImageStoreDir
+     *
+     * @return 存储路径
+     */
+    public static String getImageStoreCropDirByStr() {
+        if (mImageStoreCropDir != null)
+            return mImageStoreCropDir.getPath();
+        else
+            return null;
+    }
+
+    /**
+     * 设置裁剪路径
+     */
+    public static void setImageStoreCropDir(File imgFile) {
+        mImageStoreCropDir = imgFile;
+        Logger.i("设置图片裁剪保存路径为:" + mImageStoreCropDir.getAbsolutePath());
+    }
+
+    /**
+     * 设置裁剪路径
+     *
+     * @param imgFile 裁剪
+     */
+    public static void setImageStoreCropDir(String imgFile) {
+        mImageStoreCropDir = new File(Environment.getExternalStorageDirectory(), "/DCIM" + File.separator + imgFile + File.separator);
+        if (!mImageStoreCropDir.exists()) {
+            mImageStoreCropDir.mkdirs();
+        }
+        Logger.i("设置图片裁剪保存路径为:" + mImageStoreCropDir.getAbsolutePath());
+    }
+
+    public static void setRadioListener(IRadioImageCheckedListener radioListener) {
+        MediaGridFragment.iListenerRadio = radioListener;
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        if (context instanceof MediaActivity) {
+            mMediaActivity = (MediaActivity) context;
+        }
+        mMediaScanner = new MediaScanner(context);
+    }
+
+    @Override
+    public int getContentView() {
+        return R.layout.gallery_fragment_media_grid;
+    }
+
+    @Override
+    public void onViewCreatedOk(View view, @Nullable Bundle savedInstanceState) {
+
+        mRvMedia = (RecyclerViewFinal) view.findViewById(R.id.rv_media);
+        mLlEmptyView = (LinearLayout) view.findViewById(R.id.ll_empty_view);
+        mRvBucket = (RecyclerView) view.findViewById(R.id.rv_bucket);
+        mRlBucektOverview = (RelativeLayout) view.findViewById(R.id.rl_bucket_overview);
+        mRlRootView = (RelativeLayout) view.findViewById(R.id.rl_root_view);
+
+        mRvMedia.setEmptyView(mLlEmptyView);
+        GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), 3);
+        gridLayoutManager.setOrientation(GridLayoutManager.VERTICAL);
+        mRvMedia.addItemDecoration(new MarginDecoration(getContext()));
+        mRvMedia.setLayoutManager(gridLayoutManager);
+        mRvMedia.setOnLoadMoreListener(this);
+        mRvMedia.setFooterViewHide(true);
+
+        mTvFolderName = (TextView) view.findViewById(R.id.tv_folder_name);
+        mTvFolderName.setOnClickListener(this);
+        mTvPreview = (TextView) view.findViewById(R.id.tv_preview);
+        mTvPreview.setOnClickListener(this);
+        mTvPreview.setEnabled(false);
+        if (mConfiguration.isRadio()) {
+            view.findViewById(R.id.tv_preview_vr).setVisibility(View.GONE);
+            mTvPreview.setVisibility(View.GONE);
+
+        } else {
+            if (mConfiguration.isHidePreview()) {
+                view.findViewById(R.id.tv_preview_vr).setVisibility(View.GONE);
+                mTvPreview.setVisibility(View.GONE);
+            } else {
+                view.findViewById(R.id.tv_preview_vr).setVisibility(View.VISIBLE);
+                mTvPreview.setVisibility(View.VISIBLE);
+            }
+        }
+
+        mMediaBeanList = new ArrayList<>();
+        mScreenSize = DeviceUtils.getScreenSize(getContext());
+        mMediaGridAdapter = new MediaGridAdapter(mMediaActivity, mMediaBeanList, mScreenSize.widthPixels, mConfiguration);
+        mRvMedia.setAdapter(mMediaGridAdapter);
+        mMediaGridPresenter = new MediaGridPresenterImpl(getContext(), mConfiguration.isImage());
+        mMediaGridPresenter.setMediaGridView(this);
+
+        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
+        linearLayoutManager.setOrientation(GridLayoutManager.VERTICAL);
+        mRvBucket.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getContext())
+                .color(getResources().getColor(R.color.gallery_bucket_list_decoration_color))
+                .size(getResources().getDimensionPixelSize(R.dimen.gallery_divider_decoration_height))
+                .margin(getResources().getDimensionPixelSize(R.dimen.gallery_bucket_margin),
+                        getResources().getDimensionPixelSize(R.dimen.gallery_bucket_margin))
+                .build());
+        mRvBucket.setLayoutManager(linearLayoutManager);
+        mBucketBeanList = new ArrayList<>();
+        mBucketAdapter = new BucketAdapter(mBucketBeanList, mConfiguration, ContextCompat.getColor(getContext(), R.color.gallery_bucket_list_item_normal_color));
+        mRvBucket.setAdapter(mBucketAdapter);
+        mRvMedia.setOnItemClickListener(this);
+        mMediaGridPresenter.getBucketList();
+        mBucketAdapter.setOnRecyclerViewItemClickListener(this);
+
+        mRlBucektOverview.setVisibility(View.INVISIBLE);
+
+        if (slideInUnderneathAnimation == null) {
+            slideInUnderneathAnimation = new SlideInUnderneathAnimation(mRvBucket);
+        }
+
+        slideInUnderneathAnimation
+                .setDirection(Animation.DIRECTION_DOWN)
+                .animate();
+
+        subscribeEvent();
+
+        Activity activity = mMediaActivity;
+        if (activity == null) {
+            activity = getActivity();
+        }
+
+        if (mConfiguration.isImage()) {
+            mTvFolderName.setText(R.string.gallery_all_image);
+        } else {
+            mTvFolderName.setText(R.string.gallery_all_video);
+        }
+
+        String requestStorageAccessPermissionTips = ThemeUtils.resolveString(getContext(),
+                R.attr.gallery_request_storage_access_permission_tips,
+                R.string.gallery_default_request_storage_access_permission_tips);
+        boolean success = PermissionCheckUtils.checkReadExternalPermission(activity, requestStorageAccessPermissionTips,
+                MediaActivity.REQUEST_STORAGE_READ_ACCESS_PERMISSION);
+        if (success) {
+            mMediaGridPresenter.getMediaList(mBucketId, mPage, LIMIT);
+        }
+
+
+    }
+
+    /**
+     * RxBus
+     */
+    private void subscribeEvent() {
+        mMediaCheckChangeDisposable = RxBus.getDefault().toObservable(MediaCheckChangeEvent.class)
+                .subscribeWith(new RxBusDisposable<MediaCheckChangeEvent>() {
+                    @Override
+                    protected void onEvent(MediaCheckChangeEvent mediaCheckChangeEvent) {
+                        if (mMediaActivity.getCheckedList().size() == 0) {
+                            mTvPreview.setEnabled(false);
+                        } else {
+                            mTvPreview.setEnabled(true);
+                        }
+
+                    }
+                });
+        RxBus.getDefault().add(mMediaCheckChangeDisposable);
+
+        mCloseMediaViewPageFragmentDisposable = RxBus.getDefault().toObservable(CloseMediaViewPageFragmentEvent.class)
+                .subscribeWith(new RxBusDisposable<CloseMediaViewPageFragmentEvent>() {
+                    @Override
+                    protected void onEvent(CloseMediaViewPageFragmentEvent closeMediaViewPageFragmentEvent) throws Exception {
+                        mMediaGridAdapter.notifyDataSetChanged();
+                    }
+                });
+        RxBus.getDefault().add(mCloseMediaViewPageFragmentDisposable);
+
+        mRequestStorageReadAccessPermissionDisposable = RxBus.getDefault().toObservable(RequestStorageReadAccessPermissionEvent.class)
+                .subscribeWith(new RxBusDisposable<RequestStorageReadAccessPermissionEvent>() {
+                    @Override
+                    protected void onEvent(RequestStorageReadAccessPermissionEvent requestStorageReadAccessPermissionEvent) throws Exception {
+                        if (requestStorageReadAccessPermissionEvent.getType() == RequestStorageReadAccessPermissionEvent.TYPE_WRITE) {
+                            if (requestStorageReadAccessPermissionEvent.isSuccess()) {
+                                mMediaGridPresenter.getMediaList(mBucketId, mPage, LIMIT);
+                            } else {
+                                getActivity().finish();
+                            }
+                        } else {
+                            if (requestStorageReadAccessPermissionEvent.isSuccess()) {
+                                openCamera(mMediaActivity);
+                            }
+                        }
+                    }
+                });
+        RxBus.getDefault().add(mRequestStorageReadAccessPermissionDisposable);
+
+    }
+
+    /**
+     * 设置主题
+     */
+    @Override
+    public void setTheme() {
+        super.setTheme();
+        uCropStatusColor = ThemeUtils.resolveColor(getActivity(), R.attr.gallery_ucrop_status_bar_color, R.color.gallery_default_ucrop_color_widget_active);
+        uCropToolbarColor = ThemeUtils.resolveColor(getActivity(), R.attr.gallery_ucrop_toolbar_color, R.color.gallery_default_ucrop_color_widget_active);
+        uCropActivityWidgetColor = ThemeUtils.resolveColor(getActivity(), R.attr.gallery_ucrop_activity_widget_color, R.color.gallery_default_ucrop_color_widget);
+        uCropToolbarWidgetColor = ThemeUtils.resolveColor(getActivity(), R.attr.gallery_ucrop_toolbar_widget_color, R.color.gallery_default_toolbar_widget_color);
+        uCropTitle = ThemeUtils.resolveString(getActivity(), R.attr.gallery_ucrop_toolbar_title, R.string.gallery_edit_phote);
+        int pageColor = ThemeUtils.resolveColor(getContext(), R.attr.gallery_page_bg, R.color.gallery_default_page_bg);
+        mRlRootView.setBackgroundColor(pageColor);
+        requestStorageAccessPermissionTips = ThemeUtils.resolveString(getContext(), R.attr.gallery_request_camera_permission_tips, R.string.gallery_default_camera_access_permission_tips);
+    }
+
+    @Override
+    protected void onFirstTimeLaunched() {
+
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        onLoadFile();
+        //直接刷新一次
+        //   refreshUI();
+    }
+
+
+    @Override
+    public void loadMore() {
+        mMediaGridPresenter.getMediaList(mBucketId, mPage, LIMIT);
+    }
+
+    @Override
+    public void onRequestMediaCallback(List<MediaBean> list) {
+        if (!mConfiguration.isHideCamera()) {
+            if (mPage == 1 && TextUtils.equals(mBucketId, String.valueOf(Integer.MIN_VALUE))) {
+                MediaBean takePhotoBean = new MediaBean();
+                takePhotoBean.setId(Integer.MIN_VALUE);
+                takePhotoBean.setBucketId(String.valueOf(Integer.MIN_VALUE));
+                mMediaBeanList.add(takePhotoBean);
+            }
+        }
+        if (list != null && list.size() > 0) {
+            mMediaBeanList.addAll(list);
+            Logger.i(String.format("得到:%s张图片", list.size()));
+        } else {
+            Logger.i("没有更多图片");
+        }
+        mMediaGridAdapter.notifyDataSetChanged();
+
+        mPage++;
+
+        if (list == null || list.size() < LIMIT) {
+            mRvMedia.setFooterViewHide(true);
+            mRvMedia.setHasLoadMore(false);
+        } else {
+            mRvMedia.setFooterViewHide(false);
+            mRvMedia.setHasLoadMore(true);
+        }
+
+        if (mMediaBeanList.size() == 0) {
+            String mediaEmptyTils = ThemeUtils.resolveString(getContext(), R.attr.gallery_media_empty_tips, R.string.gallery_default_media_empty_tips);
+            EmptyViewUtils.showMessage(mLlEmptyView, mediaEmptyTils);
+        }
+
+        mRvMedia.onLoadMoreComplete();
+    }
+
+    @Override
+    public void onRequestBucketCallback(List<BucketBean> list) {
+        if (list == null || list.size() == 0) {
+            return;
+        }
+
+        mBucketBeanList.addAll(list);
+        mBucketAdapter.setSelectedBucket(list.get(0));
+    }
+
+    @Override
+    public void onItemClick(View view, int position) {
+        BucketBean bucketBean = mBucketBeanList.get(position);
+        String bucketId = bucketBean.getBucketId();
+        mRlBucektOverview.setVisibility(View.GONE);
+        if (TextUtils.equals(mBucketId, bucketId)) {
+            return;
+        }
+        mBucketId = bucketId;
+        EmptyViewUtils.showLoading(mLlEmptyView);
+        mRvMedia.setHasLoadMore(false);
+        mMediaBeanList.clear();
+        mMediaGridAdapter.notifyDataSetChanged();
+        mTvFolderName.setText(bucketBean.getBucketName());
+        mBucketAdapter.setSelectedBucket(bucketBean);
+        mRvMedia.setFooterViewHide(true);
+        mPage = 1;
+        mMediaGridPresenter.getMediaList(mBucketId, mPage, LIMIT);
+    }
+
+    @Override
+    public void onItemClick(RecyclerView.ViewHolder holder, int position) {
+        onObItemClick(position);
+    }
+
+    public void onObItemClick(int position) {
+        MediaBean mediaBean = mMediaBeanList.get(position);
+        if (mediaBean.getId() == Integer.MIN_VALUE) {
+
+            if (!CameraUtils.hasCamera(getContext())) {
+                Toast.makeText(getContext(), R.string.gallery_device_no_camera_tips, Toast.LENGTH_SHORT).show();
+                return;
+            }
+
+            boolean b = PermissionCheckUtils.checkCameraPermission(mMediaActivity, requestStorageAccessPermissionTips, MediaActivity.REQUEST_CAMERA_ACCESS_PERMISSION);
+            if (b) {
+                openCamera(mMediaActivity);
+            }
+        } else {
+            if (mConfiguration.isRadio()) {
+                if (mConfiguration.isImage()) {
+                    radioNext(mediaBean);
+                } else {
+                    if(RxGalleryFinalApi.getInstance(mMediaActivity).checkMediaBean(mediaBean))
+                        return;
+                    videoRadioNext(mediaBean);
+                }
+            } else {
+                MediaBean firstBean = mMediaBeanList.get(0);
+                ArrayList<MediaBean> gridMediaList = new ArrayList<>();
+                gridMediaList.addAll(mMediaBeanList);
+                int pos = position;
+                if (firstBean.getId() == Integer.MIN_VALUE) {
+                    pos = position - 1;
+                    gridMediaList.clear();
+                    List<MediaBean> list = mMediaBeanList.subList(1, mMediaBeanList.size());
+                    gridMediaList.addAll(list);
+                }
+                RxBus.getDefault().post(new OpenMediaPageFragmentEvent(gridMediaList, pos));
+            }
+        }
+    }
+
+    /**
+     * 处理 Video  选择  是否预览
+     *
+     * @param mediaBean
+     */
+    private void videoRadioNext(MediaBean mediaBean) {
+        if (!mConfiguration.isVideoPreview()) {
+            setPostMediaBean(mediaBean);
+            getActivity().finish();
+            return;
+        }
+        try {
+            Intent openVideo = new Intent(Intent.ACTION_VIEW);
+            openVideo.setDataAndType(Uri.parse(mediaBean.getOriginalPath()), "video/*");
+            startActivity(openVideo);
+        } catch (Exception e) {
+            Toast.makeText(getContext(), "启动播放器失败", Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /**
+     * 处理回调
+     */
+    private void setPostMediaBean(MediaBean mediaBean) {
+        ImageCropBean bean = new ImageCropBean();
+        bean.copyMediaBean(mediaBean);
+        RxBus.getDefault().post(new ImageRadioResultEvent(bean));
+    }
+
+    /**
+     * 区分功能
+     */
+    private void radioNext(MediaBean mediaBean) {
+        Logger.i("isCrop :" + mConfiguration.isCrop());
+        if (!mConfiguration.isCrop()) {
+            setPostMediaBean(mediaBean);
+            getActivity().finish();
+        } else {
+            //裁剪根据大家需求加上选择完图片后的回调
+            setPostMediaBean(mediaBean);
+            String originalPath = mediaBean.getOriginalPath();
+            File file = new File(originalPath);
+            Random random = new Random();
+            String outName = String.format(IMAGE_STORE_FILE_NAME, SimpleDateUtils.getNowTime() + "_" + random.nextInt(1024));
+            Logger.i("--->isCrop:" + mImageStoreCropDir);
+            Logger.i("--->mediaBean.getOriginalPath():" + mediaBean.getOriginalPath());
+            mCropPath = new File(mImageStoreCropDir, outName);
+            Uri outUri = Uri.fromFile(mCropPath);
+            if (!mImageStoreCropDir.exists()) {
+                mImageStoreCropDir.mkdirs();
+            }
+            if (!file.exists()) {
+                file.mkdirs();
+            }
+            Uri inputUri = Uri.fromFile(new File(mediaBean.getOriginalPath()));
+            Intent intent = new Intent(getContext(), UCropActivity.class);
+
+
+            // UCrop 参数 start
+            Bundle bundle = new Bundle();
+
+            bundle.putParcelable(UCrop.EXTRA_OUTPUT_URI, outUri);
+            bundle.putParcelable(UCrop.Options.EXTRA_ASPECT_RATIO_OPTIONS, mediaBean);
+            bundle.putInt(UCrop.Options.EXTRA_STATUS_BAR_COLOR, uCropStatusColor);
+            bundle.putInt(UCrop.Options.EXTRA_TOOL_BAR_COLOR, uCropToolbarColor);
+            bundle.putString(UCrop.Options.EXTRA_UCROP_TITLE_TEXT_TOOLBAR, uCropTitle);
+            bundle.putInt(UCrop.Options.EXTRA_UCROP_COLOR_WIDGET_ACTIVE, uCropActivityWidgetColor);
+            bundle.putInt(UCrop.Options.EXTRA_UCROP_WIDGET_COLOR_TOOLBAR, uCropToolbarWidgetColor);
+            bundle.putBoolean(UCrop.Options.EXTRA_HIDE_BOTTOM_CONTROLS, mConfiguration.isHideBottomControls());
+            bundle.putIntArray(UCrop.Options.EXTRA_ALLOWED_GESTURES, mConfiguration.getAllowedGestures());
+            bundle.putInt(UCrop.Options.EXTRA_COMPRESSION_QUALITY, mConfiguration.getCompressionQuality());
+            bundle.putInt(UCrop.Options.EXTRA_MAX_BITMAP_SIZE, mConfiguration.getMaxBitmapSize());
+            bundle.putFloat(UCrop.Options.EXTRA_MAX_SCALE_MULTIPLIER, mConfiguration.getMaxScaleMultiplier());
+            bundle.putFloat(UCrop.EXTRA_ASPECT_RATIO_X, mConfiguration.getAspectRatioX());
+            bundle.putFloat(UCrop.EXTRA_ASPECT_RATIO_Y, mConfiguration.getAspectRatioY());
+            bundle.putInt(UCrop.EXTRA_MAX_SIZE_X, mConfiguration.getMaxResultWidth());
+            bundle.putInt(UCrop.EXTRA_MAX_SIZE_Y, mConfiguration.getMaxResultHeight());
+            bundle.putInt(UCrop.Options.EXTRA_ASPECT_RATIO_SELECTED_BY_DEFAULT, mConfiguration.getSelectedByDefault());
+            bundle.putBoolean(UCrop.Options.EXTRA_FREE_STYLE_CROP, mConfiguration.isFreestyleCropEnabled());
+            bundle.putParcelable(UCrop.EXTRA_INPUT_URI, inputUri);
+            // UCrop 参数 end
+
+            int bk = FileUtils.existImageDir(inputUri.getPath());
+            Logger.i("--->" + inputUri.getPath());
+            Logger.i("--->" + outUri.getPath());
+            ArrayList<AspectRatio> aspectRatioList = new ArrayList<>();
+            AspectRatio[] aspectRatios = mConfiguration.getAspectRatio();
+            if (aspectRatios != null) {
+                for (int i = 0; i < aspectRatios.length; i++) {
+                    aspectRatioList.add(i, aspectRatios[i]);
+                    Logger.i("自定义比例=>" + aspectRatioList.get(i).getAspectRatioX() + " " + aspectRatioList.get(i).getAspectRatioY());
+                }
+            }
+            //  AspectRatio[]aspectRatios =  mConfiguration.getAspectRatio();
+            bundle.putParcelableArrayList(UCrop.Options.EXTRA_ASPECT_RATIO_OPTIONS, aspectRatioList);//EXTRA_CONFIGURATION
+            intent.putExtras(bundle);
+            if (bk != -1) {
+                //裁剪
+                startActivityForResult(intent, CROP_IMAGE_REQUEST_CODE);
+            } else {
+                Logger.w("点击图片无效");
+            }
+        }
+    }
+
+    public void openCamera(Context context) {
+
+
+        boolean image = mConfiguration.isImage();
+
+        Intent captureIntent = image ? new Intent(MediaStore.ACTION_IMAGE_CAPTURE) : new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
+        if (captureIntent.resolveActivity(context.getPackageManager()) == null) {
+            Toast.makeText(getContext(), R.string.gallery_device_camera_unable, Toast.LENGTH_SHORT).show();
+            return;
+        }
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
+        String filename = String.format(image ? IMAGE_STORE_FILE_NAME : VIDEO_STORE_FILE_NAME, dateFormat.format(new Date()));
+        Logger.i("openCamera:" + mImageStoreDir.getAbsolutePath());
+        File fileImagePath = new File(mImageStoreDir, filename);
+        mImagePath = fileImagePath.getAbsolutePath();
+
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(fileImagePath));
+        } else {
+            ContentValues contentValues = new ContentValues(1);
+            contentValues.put(MediaStore.Images.Media.DATA, mImagePath);
+            Uri uri = getContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
+            captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
+        }
+        // video : 1: 高质量  0 低质量
+//        captureIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
+        startActivityForResult(captureIntent, TAKE_IMAGE_REQUEST_CODE);
+    }
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        Logger.i("onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode);
+        if (requestCode == TAKE_IMAGE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
+            Logger.i(String.format("拍照成功,图片存储路径:%s", mImagePath));
+            mMediaScanner.scanFile(mImagePath, mConfiguration.isImage() ? IMAGE_TYPE : "", this);
+        } else if (requestCode == 222) {
+            Toast.makeText(getActivity(), "摄像成功", Toast.LENGTH_SHORT).show();
+        } else if (requestCode == CROP_IMAGE_REQUEST_CODE && data != null) {
+            Logger.i("裁剪成功");
+            refreshUI();
+            onCropFinished();
+        }
+    }
+
+    /**
+     * 裁剪之后
+     * setResult(RESULT_OK, new Intent()
+     * .putExtra(UCrop.EXTRA_OUTPUT_URI, uri)
+     * .putExtra(UCrop.EXTRA_OUTPUT_CROP_ASPECT_RATIO, resultAspectRatio)
+     * .putExtra(UCrop.EXTRA_OUTPUT_IMAGE_WIDTH, imageWidth)
+     * .putExtra(UCrop.EXTRA_OUTPUT_IMAGE_HEIGHT, imageHeight)
+     */
+    private void onCropFinished() {
+        if (iListenerRadio != null && mCropPath != null) {
+            if (mConfiguration.isCrop()) {
+                iListenerRadio.cropAfter(mCropPath);
+            }
+        } else {
+            Logger.i("# CropPath is null!# ");
+        }
+        //裁剪默认会关掉这个界面. 实现接口返回true则不关闭.
+        if (iListenerRadio == null) {
+            getActivity().finish();
+        } else {
+            boolean flag = iListenerRadio.isActivityFinish();
+            Logger.i("# crop image is flag # :" + flag);
+            if (flag)
+                getActivity().finish();
+        }
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        if (!TextUtils.isEmpty(mImagePath)) {
+            outState.putString(TAKE_URL_STORAGE_KEY, mImagePath);
+        }
+        if (!TextUtils.isEmpty(mBucketId)) {
+            outState.putString(BUCKET_ID_KEY, mBucketId);
+        }
+    }
+
+    @Override
+    protected void onRestoreState(Bundle savedInstanceState) {
+
+    }
+
+
+    //****************************************************************************
+
+    @Override
+    protected void onSaveState(Bundle outState) {
+
+    }
+
+    @Override
+    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
+        super.onViewStateRestored(savedInstanceState);
+        if (savedInstanceState == null) {
+            return;
+        }
+        mImagePath = savedInstanceState.getString(TAKE_URL_STORAGE_KEY);
+        mBucketId = savedInstanceState.getString(BUCKET_ID_KEY);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mMediaScanner.unScanFile();
+    }
+
+    @Override
+    public void onClick(View v) {
+        int id = v.getId();
+        if (id == R.id.tv_preview) {
+            RxBus.getDefault().post(new OpenMediaPreviewFragmentEvent());
+        } else if (id == R.id.tv_folder_name) {
+            v.setEnabled(false);
+            if (isShowRvBucketView()) {
+                hideRvBucketView();
+            } else {
+                showRvBucketView();
+            }
+        }
+    }
+
+    public boolean isShowRvBucketView() {
+        return mRlBucektOverview != null && mRlBucektOverview.getVisibility() == View.VISIBLE;
+    }
+
+
+    public void showRvBucketView() {
+        if (mRlBucektOverview == null) {
+            slideInUnderneathAnimation = new SlideInUnderneathAnimation(mRlBucektOverview);
+        }
+        mRlBucektOverview.setVisibility(View.VISIBLE);
+        slideInUnderneathAnimation
+                .setDirection(Animation.DIRECTION_DOWN)
+                .setDuration(Animation.DURATION_DEFAULT)
+                .setListener(new AnimationListener() {
+                    @Override
+                    public void onAnimationEnd(Animation animation) {
+                        mTvFolderName.setEnabled(true);
+                    }
+                })
+                .animate();
+    }
+
+    public void hideRvBucketView() {
+        if (slideOutUnderneathAnimation == null) {
+            slideOutUnderneathAnimation = new SlideOutUnderneathAnimation(mRvBucket);
+        }
+        slideOutUnderneathAnimation
+                .setDirection(Animation.DIRECTION_DOWN)
+                .setDuration(Animation.DURATION_DEFAULT)
+                .setListener(new AnimationListener() {
+                    @Override
+                    public void onAnimationEnd(Animation animation) {
+                        mTvFolderName.setEnabled(true);
+                        mRlBucektOverview.setVisibility(View.GONE);
+                    }
+                })
+                .animate();
+    }
+
+    /**
+     * Observable刷新图库
+     */
+    public void refreshUI() {
+        try {
+            Logger.i("->getImageStoreDirByFile().getPath().toString():" + getImageStoreDirByFile().getPath());
+            Logger.i("->getImageStoreCropDirByStr ().toString():" + getImageStoreCropDirByStr());
+            if (!TextUtils.isEmpty(mImagePath))
+                mMediaScanner.scanFile(mImagePath, IMAGE_TYPE, this);
+            if (mCropPath != null) {
+                Logger.i("->mCropPath:" + mCropPath.getPath() + " " + IMAGE_TYPE);
+                mMediaScanner.scanFile(mCropPath.getPath(), IMAGE_TYPE, this);
+            }
+        } catch (Exception e) {
+            Logger.e(e.getMessage());
+        }
+    }
+
+    @Override
+    public void onScanCompleted(final String[] images) {
+        if (images == null || images.length == 0) {
+            Logger.i("images empty");
+            return;
+        }
+
+        // mediaBean 有可能为Null,onNext 做了处理,在 getMediaBeanWithImage 时候就不处理Null了
+        Observable.create(new ObservableOnSubscribe<MediaBean>(){
+            @Override
+            public void subscribe(ObservableEmitter<MediaBean> subscriber) throws Exception {
+                MediaBean mediaBean =
+                        mConfiguration.isImage() ? MediaUtils.getMediaBeanWithImage(getContext(), images[0])
+                                :
+                                MediaUtils.getMediaBeanWithVideo(getContext(), images[0]);
+                subscriber.onNext(mediaBean);
+                subscriber.onComplete();
+            }
+        })
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new DisposableObserver<MediaBean>() {
+                    @Override
+                    public void onComplete() {
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+                        Logger.i("获取MediaBean异常" + e.toString());
+                    }
+
+                    @Override
+                    public void onNext(MediaBean mediaBean) {
+                        if (!isDetached() && mediaBean != null) {
+                            int bk = FileUtils.existImageDir(mediaBean.getOriginalPath());
+                            if (bk != -1) {
+                                mMediaBeanList.add(1, mediaBean);
+                                mMediaGridAdapter.notifyDataSetChanged();
+                            } else {
+                                Logger.i("获取:无");
+                            }
+                        }
+
+                    }
+                });
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        RxBus.getDefault().remove(mMediaCheckChangeDisposable);
+        RxBus.getDefault().remove(mCloseMediaViewPageFragmentDisposable);
+
+    }
+
+    /**
+     * onAttach 转 onStart
+     */
+    public void onLoadFile() {
+        //没有的话就默认路径
+        if (getImageStoreDirByFile() == null && getImageStoreDirByStr() == null) {
+            mImageStoreDir = new File(Environment.getExternalStorageDirectory(), "/DCIM/IMMQY/");
+            setImageStoreCropDir(mImageStoreDir);
+        }
+        if (!mImageStoreDir.exists()) {
+            mImageStoreDir.mkdirs();
+        }
+        if (getImageStoreCropDirByFile() == null && getImageStoreCropDirByStr() == null) {
+            mImageStoreCropDir = new File(mImageStoreDir, "crop");
+            if (!mImageStoreCropDir.exists()) {
+                mImageStoreCropDir.mkdirs();
+            }
+            setImageStoreCropDir(mImageStoreCropDir);
+        }
+    }
+}

+ 213 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/MediaPageFragment.java

@@ -0,0 +1,213 @@
+package cn.finalteam.rxgalleryfinal.ui.fragment;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewPager;
+import android.support.v4.widget.CompoundButtonCompat;
+import android.support.v7.widget.AppCompatCheckBox;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.Configuration;
+import cn.finalteam.rxgalleryfinal.R;
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBus;
+import cn.finalteam.rxgalleryfinal.rxbus.event.CloseMediaViewPageFragmentEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.MediaCheckChangeEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.MediaViewPagerChangedEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.OpenMediaPageFragmentEvent;
+import cn.finalteam.rxgalleryfinal.ui.activity.MediaActivity;
+import cn.finalteam.rxgalleryfinal.ui.adapter.MediaPreviewAdapter;
+import cn.finalteam.rxgalleryfinal.utils.DeviceUtils;
+import cn.finalteam.rxgalleryfinal.utils.Logger;
+import cn.finalteam.rxgalleryfinal.utils.ThemeUtils;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/14 下午10:02
+ */
+public class MediaPageFragment extends BaseFragment implements ViewPager.OnPageChangeListener,
+        View.OnClickListener {
+
+    private static final String EXTRA_MEDIA_LIST = EXTRA_PREFIX + ".MediaList";
+    private static final String EXTRA_ITEM_CLICK_POSITION = EXTRA_PREFIX + ".ItemClickPosition";
+
+    DisplayMetrics mScreenSize;
+
+    private AppCompatCheckBox mCbCheck;
+    private ViewPager mViewPager;
+    private MediaPreviewAdapter mMediaPreviewAdapter;
+    private ArrayList<MediaBean> mMediaBeanList;
+    private RelativeLayout mRlRootView;
+
+    private MediaActivity mMediaActivity;
+    private int mItemClickPosition;
+
+    public static MediaPageFragment newInstance(Configuration configuration, ArrayList<MediaBean> list, int position) {
+        MediaPageFragment fragment = new MediaPageFragment();
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(EXTRA_CONFIGURATION, configuration);
+        bundle.putParcelableArrayList(EXTRA_MEDIA_LIST, list);
+        bundle.putInt(EXTRA_ITEM_CLICK_POSITION, position);
+        fragment.setArguments(bundle);
+        return fragment;
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        if (context instanceof MediaActivity) {
+            mMediaActivity = (MediaActivity) context;
+        }
+    }
+
+    @Override
+    public int getContentView() {
+        return R.layout.gallery_fragment_media_page;
+    }
+
+    @Override
+    public void onViewCreatedOk(View view, @Nullable Bundle savedInstanceState) {
+        mCbCheck = (AppCompatCheckBox) view.findViewById(R.id.cb_page_check);
+        mViewPager = (ViewPager) view.findViewById(R.id.view_pager_page);
+        mRlRootView = (RelativeLayout) view.findViewById(R.id.rl_page_root_view);
+        mScreenSize = DeviceUtils.getScreenSize(getContext());
+
+        mMediaBeanList = new ArrayList<>();
+        if (savedInstanceState != null) {
+            List<MediaBean> mediaList = savedInstanceState.getParcelableArrayList(EXTRA_MEDIA_LIST);
+            mItemClickPosition = savedInstanceState.getInt(EXTRA_ITEM_CLICK_POSITION);
+
+            if (mediaList != null) {
+                mMediaBeanList.addAll(mediaList);
+            }
+        }
+        mMediaPreviewAdapter = new MediaPreviewAdapter(mMediaBeanList,
+                mScreenSize.widthPixels, mScreenSize.heightPixels, mConfiguration
+                , ThemeUtils.resolveColor(getActivity(), R.attr.gallery_page_bg, R.color.gallery_default_page_bg),
+                ContextCompat.getDrawable(getActivity(), ThemeUtils.resolveDrawableRes(getActivity(), R.attr.gallery_default_image, R.drawable.gallery_default_image)));
+        mViewPager.setAdapter(mMediaPreviewAdapter);
+        mCbCheck.setOnClickListener(this);
+        mViewPager.setCurrentItem(mItemClickPosition);
+        mViewPager.addOnPageChangeListener(this);
+    }
+
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mConfiguration == null || mMediaBeanList.size() == 0
+                || mCbCheck == null || mViewPager == null) {
+            return;
+        }
+        MediaBean mediaBean = mMediaBeanList.get(mItemClickPosition);
+        if (mMediaActivity != null && mMediaActivity.getCheckedList() != null) {
+            if (mMediaActivity.getCheckedList().contains(mediaBean)) {
+                mCbCheck.setChecked(true);
+            }
+        }
+    }
+
+    @Override
+    public void setTheme() {
+        super.setTheme();
+        int checkTint = ThemeUtils.resolveColor(getContext(), R.attr.gallery_checkbox_button_tint_color, R.color.gallery_default_checkbox_button_tint_color);
+        CompoundButtonCompat.setButtonTintList(mCbCheck, ColorStateList.valueOf(checkTint));
+        int cbTextColor = ThemeUtils.resolveColor(getContext(), R.attr.gallery_checkbox_text_color, R.color.gallery_default_checkbox_text_color);
+        mCbCheck.setTextColor(cbTextColor);
+
+        int pageColor = ThemeUtils.resolveColor(getContext(), R.attr.gallery_page_bg, R.color.gallery_default_page_bg);
+        mRlRootView.setBackgroundColor(pageColor);
+    }
+
+    @Override
+    protected void onFirstTimeLaunched() {
+
+    }
+
+    @Override
+    protected void onRestoreState(Bundle savedInstanceState) {
+        if (savedInstanceState == null) {
+            return;
+        }
+        List<MediaBean> mediaList = savedInstanceState.getParcelableArrayList(EXTRA_MEDIA_LIST);
+        mItemClickPosition = savedInstanceState.getInt(EXTRA_ITEM_CLICK_POSITION);
+        if (mediaList != null) {
+            mMediaBeanList.clear();
+            Logger.i("恢复数据:" + mediaList.size() + "  d=" + mediaList.get(0).getOriginalPath());
+            mMediaBeanList.addAll(mediaList);
+        }
+        mViewPager.setCurrentItem(mItemClickPosition);
+        mMediaPreviewAdapter.notifyDataSetChanged();
+    }
+
+    @Override
+    protected void onSaveState(Bundle outState) {
+        if (outState == null) {
+            return;
+        }
+        outState.putParcelableArrayList(EXTRA_MEDIA_LIST, mMediaBeanList);
+        outState.putInt(EXTRA_ITEM_CLICK_POSITION, mItemClickPosition);
+    }
+
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+    }
+
+    @Override
+    public void onPageSelected(int position) {
+        mItemClickPosition = position;
+
+        MediaBean mediaBean = mMediaBeanList.get(position);
+        //判断是否选择
+        if (mMediaActivity != null && mMediaActivity.getCheckedList() != null) {
+            mCbCheck.setChecked(mMediaActivity.getCheckedList().contains(mediaBean));
+        } else {
+            mCbCheck.setChecked(false);
+        }
+
+        RxBus.getDefault().post(new MediaViewPagerChangedEvent(position, mMediaBeanList.size(), false));
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int state) {
+    }
+
+    /**
+     * 改变选择
+     */
+    @Override
+    public void onClick(View view) {
+        if (mMediaBeanList.size() == 0) {
+            return;
+        }
+
+        int position = mViewPager.getCurrentItem();
+        MediaBean mediaBean = mMediaBeanList.get(position);
+        if (mConfiguration.getMaxSize() == mMediaActivity.getCheckedList().size()
+                && !mMediaActivity.getCheckedList().contains(mediaBean)) {
+            Toast.makeText(getContext(), getResources()
+                    .getString(R.string.gallery_image_max_size_tip, mConfiguration.getMaxSize()), Toast.LENGTH_SHORT).show();
+            mCbCheck.setChecked(false);
+        } else {
+            RxBus.getDefault().post(new MediaCheckChangeEvent(mediaBean));
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mItemClickPosition = 0;
+        RxBus.getDefault().removeStickyEvent(OpenMediaPageFragmentEvent.class);
+        RxBus.getDefault().post(new CloseMediaViewPageFragmentEvent());
+    }
+}

+ 180 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/MediaPreviewFragment.java

@@ -0,0 +1,180 @@
+package cn.finalteam.rxgalleryfinal.ui.fragment;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewPager;
+import android.support.v4.widget.CompoundButtonCompat;
+import android.support.v7.widget.AppCompatCheckBox;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.Configuration;
+import cn.finalteam.rxgalleryfinal.R;
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+import cn.finalteam.rxgalleryfinal.rxbus.RxBus;
+import cn.finalteam.rxgalleryfinal.rxbus.event.CloseMediaViewPageFragmentEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.MediaCheckChangeEvent;
+import cn.finalteam.rxgalleryfinal.rxbus.event.MediaViewPagerChangedEvent;
+import cn.finalteam.rxgalleryfinal.ui.activity.MediaActivity;
+import cn.finalteam.rxgalleryfinal.ui.adapter.MediaPreviewAdapter;
+import cn.finalteam.rxgalleryfinal.utils.DeviceUtils;
+import cn.finalteam.rxgalleryfinal.utils.ThemeUtils;
+
+/**
+ * Desction:图片预览
+ * Author:pengjianbo  Dujinyang
+ * Date:16/6/9 上午1:35
+ */
+public class MediaPreviewFragment extends BaseFragment implements ViewPager.OnPageChangeListener,
+        View.OnClickListener {
+
+    private static final String EXTRA_PAGE_INDEX = EXTRA_PREFIX + ".PageIndex";
+
+    DisplayMetrics mScreenSize;
+
+    private AppCompatCheckBox mCbCheck;
+    private ViewPager mViewPager;
+    private List<MediaBean> mMediaBeanList;
+    private RelativeLayout mRlRootView;
+
+    private MediaActivity mMediaActivity;
+    private int mPagerPosition;
+
+    public static MediaPreviewFragment newInstance(Configuration configuration, int position) {
+        MediaPreviewFragment fragment = new MediaPreviewFragment();
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(EXTRA_CONFIGURATION, configuration);
+        bundle.putInt(EXTRA_PAGE_INDEX, position);
+        fragment.setArguments(bundle);
+        return fragment;
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        if (context instanceof MediaActivity) {
+            mMediaActivity = (MediaActivity) context;
+        }
+    }
+
+    @Override
+    public int getContentView() {
+        return R.layout.gallery_fragment_media_preview;
+    }
+
+
+    @Override
+    public void onViewCreatedOk(View view, @Nullable Bundle savedInstanceState) {
+        mCbCheck = (AppCompatCheckBox) view.findViewById(R.id.cb_check);
+        mViewPager = (ViewPager) view.findViewById(R.id.view_pager);
+        mRlRootView = (RelativeLayout) view.findViewById(R.id.rl_root_view);
+        mScreenSize = DeviceUtils.getScreenSize(getContext());
+        mMediaBeanList = new ArrayList<>();
+        if (mMediaActivity.getCheckedList() != null) {
+            mMediaBeanList.addAll(mMediaActivity.getCheckedList());
+        }
+        MediaPreviewAdapter mMediaPreviewAdapter = new MediaPreviewAdapter(mMediaBeanList,
+                mScreenSize.widthPixels, mScreenSize.heightPixels, mConfiguration,
+                ThemeUtils.resolveColor(getActivity(), R.attr.gallery_page_bg, R.color.gallery_default_page_bg),
+                ContextCompat.getDrawable(getActivity(), ThemeUtils.resolveDrawableRes(getActivity(), R.attr.gallery_default_image, R.drawable.gallery_default_image)));
+        mViewPager.setAdapter(mMediaPreviewAdapter);
+        mCbCheck.setOnClickListener(this);
+
+        if (savedInstanceState != null) {
+            mPagerPosition = savedInstanceState.getInt(EXTRA_PAGE_INDEX);
+        }
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        mViewPager.setCurrentItem(mPagerPosition, false);
+        mViewPager.addOnPageChangeListener(this);
+        //#ADD UI预览数量的BUG
+        RxBus.getDefault().post(new MediaViewPagerChangedEvent(mPagerPosition, mMediaBeanList.size(), true));
+    }
+
+    @Override
+    public void setTheme() {
+        super.setTheme();
+        int checkTint = ThemeUtils.resolveColor(getContext(), R.attr.gallery_checkbox_button_tint_color, R.color.gallery_default_checkbox_button_tint_color);
+        CompoundButtonCompat.setButtonTintList(mCbCheck, ColorStateList.valueOf(checkTint));
+        int cbTextColor = ThemeUtils.resolveColor(getContext(), R.attr.gallery_checkbox_text_color, R.color.gallery_default_checkbox_text_color);
+        mCbCheck.setTextColor(cbTextColor);
+
+        int pageColor = ThemeUtils.resolveColor(getContext(), R.attr.gallery_page_bg, R.color.gallery_default_page_bg);
+        mRlRootView.setBackgroundColor(pageColor);
+    }
+
+    @Override
+    protected void onFirstTimeLaunched() {
+
+    }
+
+    @Override
+    protected void onRestoreState(Bundle savedInstanceState) {
+        if (savedInstanceState != null) {
+            mPagerPosition = savedInstanceState.getInt(EXTRA_PAGE_INDEX);
+        }
+    }
+
+    @Override
+    protected void onSaveState(Bundle outState) {
+        if (outState != null) {
+            outState.putInt(EXTRA_PAGE_INDEX, mPagerPosition);
+        }
+    }
+
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+    }
+
+    @Override
+    public void onPageSelected(int position) {
+        mPagerPosition = position;
+        MediaBean mediaBean = mMediaBeanList.get(position);
+        mCbCheck.setChecked(false);
+        //判断是否选择
+        if (mMediaActivity != null && mMediaActivity.getCheckedList() != null) {
+            mCbCheck.setChecked(mMediaActivity.getCheckedList().contains(mediaBean));
+        }
+
+        RxBus.getDefault().post(new MediaViewPagerChangedEvent(position, mMediaBeanList.size(), true));
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int state) {
+    }
+
+    /**
+     * 改变选择
+     */
+    @Override
+    public void onClick(View view) {
+        int position = mViewPager.getCurrentItem();
+        MediaBean mediaBean = mMediaBeanList.get(position);
+        if (mConfiguration.getMaxSize() == mMediaActivity.getCheckedList().size()
+                && !mMediaActivity.getCheckedList().contains(mediaBean)) {
+            Toast.makeText(getContext(), getResources()
+                    .getString(R.string.gallery_image_max_size_tip, mConfiguration.getMaxSize()), Toast.LENGTH_SHORT).show();
+            mCbCheck.setChecked(false);
+        } else {
+            RxBus.getDefault().post(new MediaCheckChangeEvent(mediaBean));
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        mPagerPosition = 0;
+        RxBus.getDefault().post(new CloseMediaViewPageFragmentEvent());
+    }
+}

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/fragment/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * fragment包
+ */
+package cn.finalteam.rxgalleryfinal.ui.fragment;

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * UI展示包
+ */
+package cn.finalteam.rxgalleryfinal.ui;

+ 107 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/FixImageView.java

@@ -0,0 +1,107 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.AppCompatImageView;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+/**
+ * Desction:为了兼容fresco框架而自定义的ImageView
+ * Author:pengjianbo  Dujinyang
+ * Date:2015/12/24 0024 20:14
+ */
+public class FixImageView extends AppCompatImageView {
+
+    private OnImageViewListener mOnImageViewListener;
+
+    public FixImageView(Context context) {
+        super(context);
+    }
+
+    public FixImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public FixImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void setOnImageViewListener(OnImageViewListener listener) {
+        mOnImageViewListener = listener;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mOnImageViewListener != null) {
+            mOnImageViewListener.onDetach();
+        }
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mOnImageViewListener != null) {
+            mOnImageViewListener.onAttach();
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(@NonNull Drawable dr) {
+        if (mOnImageViewListener != null) {
+            if (mOnImageViewListener.verifyDrawable(dr)) {
+                return true;
+            }
+        }
+        return super.verifyDrawable(dr);
+    }
+
+    @Override
+    public void onStartTemporaryDetach() {
+        super.onStartTemporaryDetach();
+        if (mOnImageViewListener != null) {
+            mOnImageViewListener.onDetach();
+        }
+    }
+
+    @Override
+    public void onFinishTemporaryDetach() {
+        super.onFinishTemporaryDetach();
+        if (mOnImageViewListener != null) {
+            mOnImageViewListener.onAttach();
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mOnImageViewListener != null) {
+
+            mOnImageViewListener.onDraw(canvas);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (mOnImageViewListener == null) {
+            return super.onTouchEvent(event);
+        }
+        return mOnImageViewListener.onTouchEvent(event) || super.onTouchEvent(event);
+    }
+
+    public interface OnImageViewListener {
+        void onDetach();
+
+        void onAttach();
+
+        boolean verifyDrawable(Drawable dr);
+
+        void onDraw(Canvas canvas);
+
+        boolean onTouchEvent(MotionEvent event);
+    }
+
+}

+ 30 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/FixViewPager.java

@@ -0,0 +1,30 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.content.Context;
+import android.support.v4.view.ViewPager;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:2015/12/29 0029 19:29
+ */
+public class FixViewPager extends ViewPager {
+    public FixViewPager(Context context) {
+        super(context);
+    }
+
+    public FixViewPager(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        try {
+            return super.dispatchTouchEvent(ev);
+        } catch (IllegalArgumentException ignored) {
+        }
+        return false;
+    }
+}

+ 434 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/FlexibleDividerDecoration.java

@@ -0,0 +1,434 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorRes;
+import android.support.annotation.DimenRes;
+import android.support.annotation.DrawableRes;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * by yqritc on 2015/01/08.
+ */
+public abstract class FlexibleDividerDecoration extends RecyclerView.ItemDecoration {
+
+    private static final int DEFAULT_SIZE = 2;
+    private static final int[] ATTRS = new int[]{
+            android.R.attr.listDivider
+    };
+    final boolean mPositionInsideItem;
+    private final VisibilityProvider mVisibilityProvider;
+    private final boolean mShowLastDivider;
+    DividerType mDividerType = DividerType.DRAWABLE;
+    PaintProvider mPaintProvider;
+    DrawableProvider mDrawableProvider;
+    SizeProvider mSizeProvider;
+    private ColorProvider mColorProvider;
+    private Paint mPaint;
+
+    FlexibleDividerDecoration(Builder builder) {
+        if (builder.mPaintProvider != null) {
+            mDividerType = DividerType.PAINT;
+            mPaintProvider = builder.mPaintProvider;
+        } else if (builder.mColorProvider != null) {
+            mDividerType = DividerType.COLOR;
+            mColorProvider = builder.mColorProvider;
+            mPaint = new Paint();
+            setSizeProvider(builder);
+        } else {
+            mDividerType = DividerType.DRAWABLE;
+            if (builder.mDrawableProvider == null) {
+                TypedArray a = builder.mContext.obtainStyledAttributes(ATTRS);
+                final Drawable divider = a.getDrawable(0);
+                a.recycle();
+                mDrawableProvider = new DrawableProvider() {
+                    @Override
+                    public Drawable drawableProvider(int position, RecyclerView parent) {
+                        return divider;
+                    }
+                };
+            } else {
+                mDrawableProvider = builder.mDrawableProvider;
+            }
+            mSizeProvider = builder.mSizeProvider;
+        }
+
+        mVisibilityProvider = builder.mVisibilityProvider;
+        mShowLastDivider = builder.mShowLastDivider;
+        mPositionInsideItem = builder.mPositionInsideItem;
+    }
+
+    private void setSizeProvider(Builder builder) {
+        mSizeProvider = builder.mSizeProvider;
+        if (mSizeProvider == null) {
+            mSizeProvider = new SizeProvider() {
+                @Override
+                public int dividerSize(int position, RecyclerView parent) {
+                    return DEFAULT_SIZE;
+                }
+            };
+        }
+    }
+
+    @Override
+    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+        RecyclerView.Adapter adapter = parent.getAdapter();
+        if (adapter == null) {
+            return;
+        }
+
+        int itemCount = adapter.getItemCount();
+        int lastDividerOffset = getLastDividerOffset(parent);
+        int validChildCount = parent.getChildCount();
+        int lastChildPosition = -1;
+        for (int i = 0; i < validChildCount; i++) {
+            View child = parent.getChildAt(i);
+            int childPosition = parent.getChildAdapterPosition(child);
+
+            if (childPosition < lastChildPosition) {
+                // Avoid remaining divider when animation starts
+                continue;
+            }
+            lastChildPosition = childPosition;
+
+            if (!mShowLastDivider && childPosition >= itemCount - lastDividerOffset) {
+                // Don't draw divider for last line if mShowLastDivider = false
+                continue;
+            }
+
+            if (wasDividerAlreadyDrawn(childPosition, parent)) {
+                // No need to draw divider again as it was drawn already by previous column
+                continue;
+            }
+
+            int groupIndex = getGroupIndex(childPosition, parent);
+            if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
+                continue;
+            }
+
+            Rect bounds = getDividerBound(groupIndex, parent, child);
+            switch (mDividerType) {
+                case DRAWABLE:
+                    Drawable drawable = mDrawableProvider.drawableProvider(groupIndex, parent);
+                    drawable.setBounds(bounds);
+                    drawable.draw(c);
+                    break;
+                case PAINT:
+                    mPaint = mPaintProvider.dividerPaint(groupIndex, parent);
+                    c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
+                    break;
+                case COLOR:
+                    mPaint.setColor(mColorProvider.dividerColor(groupIndex, parent));
+                    mPaint.setStrokeWidth(mSizeProvider.dividerSize(groupIndex, parent));
+                    c.drawLine(bounds.left, bounds.top, bounds.right, bounds.bottom, mPaint);
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public void getItemOffsets(Rect rect, View v, RecyclerView parent, RecyclerView.State state) {
+        int position = parent.getChildAdapterPosition(v);
+        int itemCount = parent.getAdapter().getItemCount();
+        int lastDividerOffset = getLastDividerOffset(parent);
+        if (!mShowLastDivider && position >= itemCount - lastDividerOffset) {
+            // Don't set item offset for last line if mShowLastDivider = false
+            return;
+        }
+
+        int groupIndex = getGroupIndex(position, parent);
+        if (mVisibilityProvider.shouldHideDivider(groupIndex, parent)) {
+            return;
+        }
+
+        setItemOffsets(rect, groupIndex, parent);
+    }
+
+    /**
+     * Check if recyclerview is reverse layout
+     *
+     * @param parent RecyclerView
+     * @return true if recyclerview is reverse layout
+     */
+    boolean isReverseLayout(RecyclerView parent) {
+        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
+        return layoutManager instanceof LinearLayoutManager && ((LinearLayoutManager) layoutManager).getReverseLayout();
+    }
+
+    /**
+     * In the case mShowLastDivider = false,
+     * Returns offset for how many views we don't have to draw a divider for,
+     * for LinearLayoutManager it is as simple as not drawing the last child divider,
+     * but for a GridLayoutManager it needs to take the span count for the last items into account
+     * until we use the span count configured for the grid.
+     *
+     * @param parent RecyclerView
+     * @return offset for how many views we don't have to draw a divider or 1 if its a
+     * LinearLayoutManager
+     */
+    private int getLastDividerOffset(RecyclerView parent) {
+        if (parent.getLayoutManager() instanceof GridLayoutManager) {
+            GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
+            GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
+            int spanCount = layoutManager.getSpanCount();
+            int itemCount = parent.getAdapter().getItemCount();
+            for (int i = itemCount - 1; i >= 0; i--) {
+                if (spanSizeLookup.getSpanIndex(i, spanCount) == 0) {
+                    return itemCount - i;
+                }
+            }
+        }
+
+        return 1;
+    }
+
+    /**
+     * Determines whether divider was already drawn for the row the item is in,
+     * effectively only makes sense for a grid
+     *
+     * @param position current view position to draw divider
+     * @param parent   RecyclerView
+     * @return true if the divider can be skipped as it is in the same row as the previous one.
+     */
+    private boolean wasDividerAlreadyDrawn(int position, RecyclerView parent) {
+        if (parent.getLayoutManager() instanceof GridLayoutManager) {
+            GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
+            GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
+            int spanCount = layoutManager.getSpanCount();
+            return spanSizeLookup.getSpanIndex(position, spanCount) > 0;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a group index for GridLayoutManager.
+     * for LinearLayoutManager, always returns position.
+     *
+     * @param position current view position to draw divider
+     * @param parent   RecyclerView
+     * @return group index of items
+     */
+    private int getGroupIndex(int position, RecyclerView parent) {
+        if (parent.getLayoutManager() instanceof GridLayoutManager) {
+            GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
+            GridLayoutManager.SpanSizeLookup spanSizeLookup = layoutManager.getSpanSizeLookup();
+            int spanCount = layoutManager.getSpanCount();
+            return spanSizeLookup.getSpanGroupIndex(position, spanCount);
+        }
+
+        return position;
+    }
+
+    protected abstract Rect getDividerBound(int position, RecyclerView parent, View child);
+
+    protected abstract void setItemOffsets(Rect outRect, int position, RecyclerView parent);
+
+    protected enum DividerType {
+        DRAWABLE, PAINT, COLOR
+    }
+
+    /**
+     * Interface for controlling divider visibility
+     */
+    public interface VisibilityProvider {
+
+        /**
+         * Returns true if divider should be hidden.
+         *
+         * @param position Divider position (or group index for GridLayoutManager)
+         * @param parent   RecyclerView
+         * @return True if the divider at position should be hidden
+         */
+        boolean shouldHideDivider(int position, RecyclerView parent);
+    }
+
+    /**
+     * Interface for controlling paint instance for divider drawing
+     */
+    public interface PaintProvider {
+
+        /**
+         * Returns {@link android.graphics.Paint} for divider
+         *
+         * @param position Divider position (or group index for GridLayoutManager)
+         * @param parent   RecyclerView
+         * @return Paint instance
+         */
+        Paint dividerPaint(int position, RecyclerView parent);
+    }
+
+    /**
+     * Interface for controlling divider color
+     */
+    public interface ColorProvider {
+
+        /**
+         * Returns {@link android.graphics.Color} value of divider
+         *
+         * @param position Divider position (or group index for GridLayoutManager)
+         * @param parent   RecyclerView
+         * @return Color value
+         */
+        int dividerColor(int position, RecyclerView parent);
+    }
+
+    /**
+     * Interface for controlling drawable object for divider drawing
+     */
+    public interface DrawableProvider {
+
+        /**
+         * Returns drawable instance for divider
+         *
+         * @param position Divider position (or group index for GridLayoutManager)
+         * @param parent   RecyclerView
+         * @return Drawable instance
+         */
+        Drawable drawableProvider(int position, RecyclerView parent);
+    }
+
+    /**
+     * Interface for controlling divider size
+     */
+    public interface SizeProvider {
+
+        /**
+         * Returns size value of divider.
+         * Height for horizontal divider, width for vertical divider
+         *
+         * @param position Divider position (or group index for GridLayoutManager)
+         * @param parent   RecyclerView
+         * @return Size of divider
+         */
+        int dividerSize(int position, RecyclerView parent);
+    }
+
+    public static class Builder<T extends Builder> {
+
+        final Resources mResources;
+        private final Context mContext;
+        private PaintProvider mPaintProvider;
+        private ColorProvider mColorProvider;
+        private DrawableProvider mDrawableProvider;
+        private SizeProvider mSizeProvider;
+        private VisibilityProvider mVisibilityProvider = new VisibilityProvider() {
+            @Override
+            public boolean shouldHideDivider(int position, RecyclerView parent) {
+                return false;
+            }
+        };
+        private boolean mShowLastDivider = false;
+        private boolean mPositionInsideItem = false;
+
+        public Builder(Context context) {
+            mContext = context;
+            mResources = context.getResources();
+        }
+
+        public T paint(final Paint paint) {
+            return paintProvider(new PaintProvider() {
+                @Override
+                public Paint dividerPaint(int position, RecyclerView parent) {
+                    return paint;
+                }
+            });
+        }
+
+        public T paintProvider(PaintProvider provider) {
+            mPaintProvider = provider;
+            return (T) this;
+        }
+
+        public T color(final int color) {
+            return colorProvider(new ColorProvider() {
+                @Override
+                public int dividerColor(int position, RecyclerView parent) {
+                    return color;
+                }
+            });
+        }
+
+        public T colorResId(@ColorRes int colorId) {
+            return color(ContextCompat.getColor(mContext, colorId));
+        }
+
+        public T colorProvider(ColorProvider provider) {
+            mColorProvider = provider;
+            return (T) this;
+        }
+
+        public T drawable(@DrawableRes int id) {
+            return drawable(ContextCompat.getDrawable(mContext, id));
+        }
+
+        public T drawable(final Drawable drawable) {
+            return drawableProvider(new DrawableProvider() {
+                @Override
+                public Drawable drawableProvider(int position, RecyclerView parent) {
+                    return drawable;
+                }
+            });
+        }
+
+        public T drawableProvider(DrawableProvider provider) {
+            mDrawableProvider = provider;
+            return (T) this;
+        }
+
+        public T size(final int size) {
+            return sizeProvider(new SizeProvider() {
+                @Override
+                public int dividerSize(int position, RecyclerView parent) {
+                    return size;
+                }
+            });
+        }
+
+        public T sizeResId(@DimenRes int sizeId) {
+            return size(mResources.getDimensionPixelSize(sizeId));
+        }
+
+        public T sizeProvider(SizeProvider provider) {
+            mSizeProvider = provider;
+            return (T) this;
+        }
+
+        public T visibilityProvider(VisibilityProvider provider) {
+            mVisibilityProvider = provider;
+            return (T) this;
+        }
+
+        public T showLastDivider() {
+            mShowLastDivider = true;
+            return (T) this;
+        }
+
+        public T positionInsideItem(boolean positionInsideItem) {
+            mPositionInsideItem = positionInsideItem;
+            return (T) this;
+        }
+
+        void checkBuilderParams() {
+            if (mPaintProvider != null) {
+                if (mColorProvider != null) {
+                    throw new IllegalArgumentException(
+                            "Use setColor method of Paint class to specify line color. Do not provider ColorProvider if you set PaintProvider.");
+                }
+                if (mSizeProvider != null) {
+                    throw new IllegalArgumentException(
+                            "Use setStrokeWidth method of Paint class to specify line size. Do not provider SizeProvider if you set PaintProvider.");
+                }
+            }
+        }
+    }
+}

+ 88 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/FooterAdapter.java

@@ -0,0 +1,88 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+/**
+ * Desction:footer适配器
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/21 下午3:59
+ */
+public class FooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+    private static final int ITEM_VIEW_TYPE_FOOTER = 0;
+    private static final int ITEM_VIEW_TYPE_ITEM = 1;
+
+    private final RecyclerView.Adapter mAdapter;
+    private final View mFooterView;
+    private OnItemClickListener mOnItemClickListener;
+
+    public FooterAdapter(RecyclerView.Adapter adapter, View footerView) {
+        this.mAdapter = adapter;
+        this.mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
+            @Override
+            public void onChanged() {
+                super.onChanged();
+                notifyDataSetChanged();
+            }
+        });
+        this.mFooterView = footerView;
+    }
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        if (viewType == ITEM_VIEW_TYPE_FOOTER) {
+            return new FooterViewHolder(mFooterView);
+        }
+        return mAdapter.onCreateViewHolder(parent, viewType);
+    }
+
+    @Override
+    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
+        if (!isFooter(position)) {
+            if (mOnItemClickListener != null) {
+                holder.itemView.setOnClickListener(new View.OnClickListener() {
+                    @Override
+                    public void onClick(View view) {
+                        mOnItemClickListener.onItemClick(holder, position);
+                    }
+                });
+            }
+            mAdapter.onBindViewHolder(holder, position);
+        }
+    }
+
+    /**
+     * 设置Item点击事件
+     */
+    public void setOnItemClickListener(OnItemClickListener listener) {
+        this.mOnItemClickListener = listener;
+    }
+
+    @Override
+    public int getItemCount() {
+        return mAdapter.getItemCount() + 1;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return isFooter(position) ? ITEM_VIEW_TYPE_FOOTER : ITEM_VIEW_TYPE_ITEM;
+    }
+
+    public boolean isFooter(int position) {
+        return position == getItemCount() - 1;
+    }
+
+    public interface OnItemClickListener {
+        void onItemClick(RecyclerView.ViewHolder holder, int position);
+    }
+
+    class FooterViewHolder extends RecyclerView.ViewHolder {
+
+        public FooterViewHolder(View itemView) {
+            super(itemView);
+        }
+    }
+
+}

+ 174 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/HorizontalDividerItemDecoration.java

@@ -0,0 +1,174 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.DimenRes;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * by yqritc on 2015/01/15.
+ */
+public class HorizontalDividerItemDecoration extends FlexibleDividerDecoration {
+
+    private final MarginProvider mMarginProvider;
+
+    private HorizontalDividerItemDecoration(Builder builder) {
+        super(builder);
+        mMarginProvider = builder.mMarginProvider;
+    }
+
+    @Override
+    protected Rect getDividerBound(int position, RecyclerView parent, View child) {
+        Rect bounds = new Rect(0, 0, 0, 0);
+        int transitionX = (int) ViewCompat.getTranslationX(child);
+        int transitionY = (int) ViewCompat.getTranslationY(child);
+        RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
+        bounds.left = parent.getPaddingLeft() +
+                mMarginProvider.dividerLeftMargin(position, parent) + transitionX;
+        bounds.right = parent.getWidth() - parent.getPaddingRight() -
+                mMarginProvider.dividerRightMargin(position, parent) + transitionX;
+
+        int dividerSize = getDividerSize(position, parent);
+        boolean isReverseLayout = isReverseLayout(parent);
+        if (mDividerType == DividerType.DRAWABLE) {
+            // set top and bottom position of divider
+            if (isReverseLayout) {
+                bounds.bottom = child.getTop() - params.topMargin + transitionY;
+                bounds.top = bounds.bottom - dividerSize;
+            } else {
+                bounds.top = child.getBottom() + params.bottomMargin + transitionY;
+                bounds.bottom = bounds.top + dividerSize;
+            }
+        } else {
+            // set center point of divider
+            int halfSize = dividerSize / 2;
+            if (isReverseLayout) {
+                bounds.top = child.getTop() - params.topMargin - halfSize + transitionY;
+            } else {
+                bounds.top = child.getBottom() + params.bottomMargin + halfSize + transitionY;
+            }
+            bounds.bottom = bounds.top;
+        }
+
+        if (mPositionInsideItem) {
+            if (isReverseLayout) {
+                bounds.top += dividerSize;
+                bounds.bottom += dividerSize;
+            } else {
+                bounds.top -= dividerSize;
+                bounds.bottom -= dividerSize;
+            }
+        }
+
+        return bounds;
+    }
+
+    @Override
+    protected void setItemOffsets(Rect outRect, int position, RecyclerView parent) {
+        if (mPositionInsideItem) {
+            outRect.set(0, 0, 0, 0);
+            return;
+        }
+
+        if (isReverseLayout(parent)) {
+            outRect.set(0, getDividerSize(position, parent), 0, 0);
+        } else {
+            outRect.set(0, 0, 0, getDividerSize(position, parent));
+        }
+    }
+
+    private int getDividerSize(int position, RecyclerView parent) {
+        if (mPaintProvider != null) {
+            return (int) mPaintProvider.dividerPaint(position, parent).getStrokeWidth();
+        } else if (mSizeProvider != null) {
+            return mSizeProvider.dividerSize(position, parent);
+        } else if (mDrawableProvider != null) {
+            Drawable drawable = mDrawableProvider.drawableProvider(position, parent);
+            return drawable.getIntrinsicHeight();
+        }
+        throw new RuntimeException("failed to get size");
+    }
+
+    /**
+     * Interface for controlling divider margin
+     */
+    public interface MarginProvider {
+
+        /**
+         * Returns left margin of divider.
+         *
+         * @param position Divider position (or group index for GridLayoutManager)
+         * @param parent   RecyclerView
+         * @return left margin
+         */
+        int dividerLeftMargin(int position, RecyclerView parent);
+
+        /**
+         * Returns right margin of divider.
+         *
+         * @param position Divider position (or group index for GridLayoutManager)
+         * @param parent   RecyclerView
+         * @return right margin
+         */
+        int dividerRightMargin(int position, RecyclerView parent);
+    }
+
+    public static class Builder extends FlexibleDividerDecoration.Builder<Builder> {
+
+        private MarginProvider mMarginProvider = new MarginProvider() {
+            @Override
+            public int dividerLeftMargin(int position, RecyclerView parent) {
+                return 0;
+            }
+
+            @Override
+            public int dividerRightMargin(int position, RecyclerView parent) {
+                return 0;
+            }
+        };
+
+        public Builder(Context context) {
+            super(context);
+        }
+
+        public Builder margin(final int leftMargin, final int rightMargin) {
+            return marginProvider(new MarginProvider() {
+                @Override
+                public int dividerLeftMargin(int position, RecyclerView parent) {
+                    return leftMargin;
+                }
+
+                @Override
+                public int dividerRightMargin(int position, RecyclerView parent) {
+                    return rightMargin;
+                }
+            });
+        }
+
+        public Builder margin(int horizontalMargin) {
+            return margin(horizontalMargin, horizontalMargin);
+        }
+
+        public Builder marginResId(@DimenRes int leftMarginId, @DimenRes int rightMarginId) {
+            return margin(mResources.getDimensionPixelSize(leftMarginId),
+                    mResources.getDimensionPixelSize(rightMarginId));
+        }
+
+        public Builder marginResId(@DimenRes int horizontalMarginId) {
+            return marginResId(horizontalMarginId, horizontalMarginId);
+        }
+
+        public Builder marginProvider(MarginProvider provider) {
+            mMarginProvider = provider;
+            return this;
+        }
+
+        public HorizontalDividerItemDecoration build() {
+            checkBuilderParams();
+            return new HorizontalDividerItemDecoration(this);
+        }
+    }
+}

+ 22 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/MarginDecoration.java

@@ -0,0 +1,22 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import cn.finalteam.rxgalleryfinal.R;
+
+public class MarginDecoration extends RecyclerView.ItemDecoration {
+    private final int margin;
+
+    public MarginDecoration(Context context) {
+        margin = context.getResources().getDimensionPixelSize(R.dimen.gallery_grid_item_margin);
+    }
+
+    @Override
+    public void getItemOffsets(
+            Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+        outRect.set(margin, margin, margin, margin);
+    }
+}

+ 280 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/RecyclerViewFinal.java

@@ -0,0 +1,280 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.os.SystemClock;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.StaggeredGridLayoutManager;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import cn.finalteam.rxgalleryfinal.R;
+
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/3/7 下午6:40
+ */
+public class RecyclerViewFinal extends RecyclerView {
+
+    /**
+     * 是否可以加载跟多
+     */
+    private boolean mHasLoadMore;
+    /**
+     * 加载更多lock
+     */
+    private boolean mLoadMoreLock;
+    /**
+     * 加载更多事件回调
+     */
+    private OnLoadMoreListener mOnLoadMoreListener;
+    /**
+     * emptyview
+     */
+    private View mEmptyView;
+    /**
+     * 刷新数据时停止滑动,避免出现数组下标越界问题
+     */
+    private final AdapterDataObserver mDataObserver = new AdapterDataObserver() {
+        @Override
+        public void onChanged() {
+            Adapter<?> adapter = getAdapter();
+            if (adapter != null && mEmptyView != null) {
+                if (adapter.getItemCount() == 0) {
+                    mEmptyView.setVisibility(View.VISIBLE);
+                    setVisibility(View.GONE);
+                } else {
+                    mEmptyView.setVisibility(View.GONE);
+                    setVisibility(View.VISIBLE);
+                }
+            }
+
+            dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_CANCEL, 0, 0, 0));
+        }
+    };
+    private FooterAdapter mFooterViewAdapter;
+    private TextView mTvMessage;
+    private ProgressBar mPbLoading;
+    private View mFooterView;
+
+    public RecyclerViewFinal(Context context) {
+        super(context);
+        init(context);
+    }
+
+    public RecyclerViewFinal(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init(context);
+    }
+
+    public RecyclerViewFinal(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init(context);
+    }
+
+    @SuppressLint("InflateParams")
+    private void init(Context context) {
+        mFooterView = LayoutInflater.from(context).inflate(R.layout.gallery_loading_view_final_footer_default, null);
+        mPbLoading = (ProgressBar) mFooterView.findViewById(R.id.pb_loading);
+        mTvMessage = (TextView) mFooterView.findViewById(R.id.tv_loading_msg);
+
+//        setHasLoadMore(false);
+        addOnScrollListener(new RecyclerViewOnScrollListener());
+    }
+
+    @Override
+    public void setAdapter(Adapter adapter) {
+        try {
+            adapter.unregisterAdapterDataObserver(mDataObserver);
+        } catch (Exception ignored) {
+        }
+
+        adapter.registerAdapterDataObserver(mDataObserver);
+        mFooterViewAdapter = new FooterAdapter(adapter, mFooterView);
+
+        if (getLayoutManager() != null) {
+            final GridLayoutManager manager = (GridLayoutManager) getLayoutManager();
+            manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+                @Override
+                public int getSpanSize(int position) {
+                    return mFooterViewAdapter.isFooter(position) ? manager.getSpanCount() : 1;
+                }
+            });
+        }
+
+        super.setAdapter(mFooterViewAdapter);
+    }
+
+    /**
+     * 设置recyclerview emptyview
+     */
+    public void setEmptyView(View emptyView) {
+        this.mEmptyView = emptyView;
+    }
+
+    /**
+     * 没有很多了
+     */
+    private void showNoMoreUI() {
+        mLoadMoreLock = false;
+        mPbLoading.setVisibility(View.GONE);
+        mTvMessage.setText(R.string.gallery_loading_view_no_more);
+    }
+
+    /**
+     * 显示默认UI
+     */
+    private void showNormalUI() {
+        mLoadMoreLock = false;
+        mPbLoading.setVisibility(View.GONE);
+        mTvMessage.setText(R.string.gallery_loading_view_click_loading_more);
+    }
+
+    /**
+     * 显示加载中UI
+     */
+    private void showLoadingUI() {
+        mPbLoading.setVisibility(View.VISIBLE);
+        mTvMessage.setText(R.string.gallery_loading_view_loading);
+    }
+
+    /**
+     * 是否有更多
+     */
+    public void setHasLoadMore(boolean hasLoadMore) {
+        mHasLoadMore = hasLoadMore;
+        if (!mHasLoadMore) {
+            showNoMoreUI();
+        } else {
+            showNormalUI();
+        }
+    }
+
+    /**
+     * 设置加载更多事件回调
+     */
+    public void setOnLoadMoreListener(OnLoadMoreListener loadMoreListener) {
+        this.mOnLoadMoreListener = loadMoreListener;
+    }
+
+    /**
+     * 完成加载更多
+     */
+    public void onLoadMoreComplete() {
+        if (mHasLoadMore) {
+            showNormalUI();
+        }
+    }
+
+    /**
+     * 加载更多
+     */
+    private void executeLoadMore() {
+        if (!mLoadMoreLock && mHasLoadMore) {
+            if (mOnLoadMoreListener != null) {
+                mOnLoadMoreListener.loadMore();
+            }
+            mLoadMoreLock = true;//上锁
+            showLoadingUI();
+        }
+    }
+
+    public void setFooterViewHide(boolean footerViewHide) {
+        if (footerViewHide) {
+            mFooterView.setVisibility(View.GONE);
+        } else {
+            mFooterView.setVisibility(View.VISIBLE);
+        }
+    }
+
+    /**
+     * 设置OnItemClickListener
+     */
+    public void setOnItemClickListener(FooterAdapter.OnItemClickListener listener) {
+        mFooterViewAdapter.setOnItemClickListener(listener);
+    }
+
+    public interface OnLoadMoreListener {
+        void loadMore();
+    }
+
+    /**
+     * 滚动到底部自动加载更多数据
+     */
+    private class RecyclerViewOnScrollListener extends OnScrollListener {
+
+        /**
+         * 最后一个的位置
+         */
+        private int[] lastPositions;
+
+        /**
+         * 最后一个可见的item的位置
+         */
+        private int lastVisibleItemPosition;
+
+        /**
+         * 当前滑动的状态
+         */
+        private int currentScrollState = 0;
+
+        @Override
+        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+            super.onScrolled(recyclerView, dx, dy);
+
+            LayoutManager layoutManager = recyclerView.getLayoutManager();
+
+            if (layoutManager instanceof GridLayoutManager) {
+                lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
+            } else if (layoutManager instanceof LinearLayoutManager) {
+                lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
+            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
+                StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
+                if (lastPositions == null) {
+                    lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
+                }
+                staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
+                lastVisibleItemPosition = findMax(lastPositions);
+            } else {
+                throw new RuntimeException(
+                        "Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager");
+            }
+        }
+
+        @Override
+        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+            super.onScrollStateChanged(recyclerView, newState);
+            currentScrollState = newState;
+            LayoutManager layoutManager = recyclerView.getLayoutManager();
+            int visibleItemCount = layoutManager.getChildCount();
+            int totalItemCount = layoutManager.getItemCount();
+            if ((visibleItemCount > 0 && currentScrollState == RecyclerView.SCROLL_STATE_IDLE && (lastVisibleItemPosition) >= totalItemCount - 1)) {
+                if (mHasLoadMore) {
+                    executeLoadMore();
+                }
+            }
+        }
+
+        /**
+         * 取数组中最大值
+         */
+        private int findMax(int[] lastPositions) {
+            int max = lastPositions[0];
+            for (int value : lastPositions) {
+                if (value > max) {
+                    max = value;
+                }
+            }
+
+            return max;
+        }
+    }
+}

+ 29 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/SquareImageView.java

@@ -0,0 +1,29 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/9 上午12:53
+ */
+public class SquareImageView extends FixImageView {
+    public SquareImageView(Context context) {
+        super(context);
+    }
+
+    public SquareImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()); //Snap to width
+    }
+}

+ 27 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/SquareLinearLayout.java

@@ -0,0 +1,27 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/6/17 下午1:48
+ */
+public class SquareLinearLayout extends LinearLayout {
+
+    public SquareLinearLayout(Context context) {
+        super(context);
+    }
+
+    public SquareLinearLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()); //Snap to width
+    }
+}

+ 31 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/SquareRelativeLayout.java

@@ -0,0 +1,31 @@
+package cn.finalteam.rxgalleryfinal.ui.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/6/8 下午4:07
+ */
+public class SquareRelativeLayout extends RelativeLayout {
+
+    public SquareRelativeLayout(Context context) {
+        super(context);
+    }
+
+    public SquareRelativeLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()); //Snap to width
+    }
+}

+ 4 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/widget/package-info.java

@@ -0,0 +1,4 @@
+/**
+ * 控件包
+ */
+package cn.finalteam.rxgalleryfinal.ui.widget;

+ 270 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/BitmapUtils.java

@@ -0,0 +1,270 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
+import android.graphics.drawable.Drawable;
+import android.media.ThumbnailUtils;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.MediaStore;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.media.ExifInterface;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.text.TextUtils;
+
+import com.yalantis.ucrop.callback.BitmapLoadCallback;
+import com.yalantis.ucrop.util.BitmapLoadUtils;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Desction:Bitmap处理工具类,图片压缩、裁剪、选择、存储
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/4 下午5:03
+ */
+public class BitmapUtils {
+
+    private final static int THUMBNAIL_BIG = 1;
+    private final static int THUMBNAIL_SMALL = 2;
+
+    public static void createVideoThumbnailBigPath(String thumbnailSaveDir, String originalPath) {
+        createVideoThumbnail(thumbnailSaveDir, originalPath, THUMBNAIL_BIG);
+    }
+
+    public static void createVideoThumbnailSmallPath(String thumbnailSaveDir, String originalPath) {
+        createVideoThumbnail(thumbnailSaveDir, originalPath, THUMBNAIL_SMALL);
+    }
+
+    /**
+     * 创建视频缩略图
+     */
+    public static void createVideoThumbnail(String thumbnailSaveDir, String originalPath, int scale) {
+        Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(originalPath, MediaStore.Video.Thumbnails.MINI_KIND);
+        if (bitmap == null) {
+            return;
+        }
+        int originalImageWidth = bitmap.getWidth();
+        int originalImageHeight = bitmap.getHeight();
+        int maxValue = Math.max(originalImageWidth, originalImageHeight);
+        BufferedInputStream bufferedInputStream = null;
+        FileOutputStream fileOutputStream = null;
+        File targetFile;
+        try {
+            BitmapFactory.Options options = new BitmapFactory.Options();
+            if (maxValue > 3000) {
+                options.inSampleSize = scale * 5;
+            } else if (maxValue > 2000 && maxValue <= 3000) {
+                options.inSampleSize = scale * 4;
+            } else if (maxValue > 1500 && maxValue <= 2000) {
+                options.inSampleSize = (int) (scale * 2.5);
+            } else if (maxValue > 1000 && maxValue <= 1500) {
+                options.inSampleSize = (int) (scale * 1.3);
+//            } else if (maxValue > 400 && maxValue <= 1000) {
+//                options.inSampleSize = scale * 2;
+            } else {
+                options.inSampleSize = scale;
+            }
+            options.inJustDecodeBounds = false;
+
+            //4、图片方向纠正和压缩(生成缩略图)
+            bufferedInputStream = new BufferedInputStream(new FileInputStream(originalPath));
+            Bitmap bm = BitmapFactory.decodeStream(bufferedInputStream, null, options);
+            bufferedInputStream.close();
+            bitmap.recycle();
+            if (bm == null) {
+                return;
+            }
+            bitmap = bm;
+
+            String scaleStr = (scale == THUMBNAIL_BIG ? "big" : "small");
+
+            String extension = FilenameUtils.getExtension(originalPath);
+            File original = new File(originalPath);
+            targetFile = new File(thumbnailSaveDir, scaleStr + "_" + original.getName().replace(extension, "jpg"));
+
+            fileOutputStream = new FileOutputStream(targetFile);
+            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
+        } catch (Exception e) {
+            Logger.e(e);
+        } finally {
+            if (!bitmap.isRecycled()) {
+                bitmap.recycle();
+            }
+
+            IOUtils.close(bufferedInputStream);
+            IOUtils.flush(fileOutputStream);
+            IOUtils.close(fileOutputStream);
+        }
+    }
+
+    /**
+     * 创建大缩略图
+     *
+     * @param targetFile   保存目标文件
+     * @param originalPath 图片地址
+     */
+    public static void createThumbnailBig(File targetFile, String originalPath) {
+        compressAndSaveImage(targetFile, originalPath, THUMBNAIL_BIG);
+    }
+
+    /**
+     * 创建小缩略图
+     *
+     * @param targetFile   保存目标文件
+     * @param originalPath 图片地址
+     */
+    public static void createThumbnailSmall(File targetFile, String originalPath) {
+        compressAndSaveImage(targetFile, originalPath, THUMBNAIL_SMALL);
+    }
+
+    /**
+     * 图片压缩并且存储
+     *
+     * @param targetFile   保存目标文件
+     * @param originalPath 图片地址
+     * @param scale        图片缩放值
+     */
+    public static void compressAndSaveImage(File targetFile, String originalPath, int scale) {
+
+        Bitmap bitmap = null;
+        BufferedInputStream bufferedInputStream = null;
+        FileOutputStream fileOutputStream = null;
+
+        try {
+            //1、得到图片的宽、高
+            BitmapFactory.Options options = new BitmapFactory.Options();
+            options.inJustDecodeBounds = true;
+            bufferedInputStream = new BufferedInputStream(new FileInputStream(originalPath));
+            bitmap = BitmapFactory.decodeStream(bufferedInputStream, null, options);
+            if (bitmap != null) {
+                bitmap.recycle();
+            }
+            bufferedInputStream.close();
+
+            int originalImageWidth = options.outWidth;
+            int originalImageHeight = options.outHeight;
+
+            //2、获取图片方向
+            int orientation = getImageOrientation(originalPath);
+            int rotate = 0;
+            switch (orientation) {//判断是否需要旋转
+                case ExifInterface.ORIENTATION_ROTATE_270:
+                    rotate = -90;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_180:
+                    rotate = 180;
+                    break;
+                case ExifInterface.ORIENTATION_ROTATE_90:
+                    rotate = 90;
+                    break;
+            }
+
+            //3、计算图片压缩inSampleSize
+            int maxValue = Math.max(originalImageWidth, originalImageHeight);
+            if (maxValue > 3000) {
+                options.inSampleSize = scale * 5;
+            } else if (maxValue > 2000 && maxValue <= 3000) {
+                options.inSampleSize = scale * 4;
+            } else if (maxValue > 1500 && maxValue <= 2000) {
+                options.inSampleSize = (int) (scale * 2.5);
+            } else if (maxValue > 1000 && maxValue <= 1500) {
+                options.inSampleSize = (int) (scale * 1.3);
+//            } else if (maxValue > 400 && maxValue <= 1000) {
+//                options.inSampleSize = scale * 2;
+            } else {
+                options.inSampleSize = scale;
+            }
+            options.inJustDecodeBounds = false;
+
+            //4、图片方向纠正和压缩(生成缩略图)
+            bufferedInputStream = new BufferedInputStream(new FileInputStream(originalPath));
+            bitmap = BitmapFactory.decodeStream(bufferedInputStream, null, options);
+            bufferedInputStream.close();
+
+            if (bitmap == null) {
+                return;
+            }
+            String extension = FilenameUtils.getExtension(originalPath);
+
+            targetFile.getParentFile().mkdirs();
+
+            fileOutputStream = new FileOutputStream(targetFile);
+            if (rotate != 0) {
+                Matrix matrix = new Matrix();
+                matrix.setRotate(rotate);
+                Bitmap bitmapOld = bitmap;
+                bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
+                        bitmap.getHeight(), matrix, false);
+                bitmapOld.recycle();
+            }
+
+            //5、保存图片
+            if (TextUtils.equals(extension.toLowerCase(), "jpg")
+                    || TextUtils.equals(extension.toLowerCase(), "jpeg")) {
+                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
+            } else if (TextUtils.equals(extension.toLowerCase(), "webp")
+                    && Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+                bitmap.compress(Bitmap.CompressFormat.WEBP, 100, fileOutputStream);
+            } else {
+                bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            IOUtils.close(bufferedInputStream);
+            IOUtils.flush(fileOutputStream);
+            IOUtils.close(fileOutputStream);
+            if (bitmap != null && bitmap.isRecycled()) {
+                bitmap.recycle();
+            }
+        }
+    }
+
+    /**
+     * 获取一张图片在手机上的方向值
+     */
+    public static int getImageOrientation(String uri) throws IOException {
+        if (!new File(uri).exists()) {
+            return 0;
+        }
+        return new ExifInterface(uri).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
+    }
+
+    /**
+     * Drawable着色工具
+     */
+    public static Drawable tintDrawable(Drawable drawable, ColorStateList colors) {
+        final Drawable wrappedDrawable = DrawableCompat.wrap(drawable);
+        DrawableCompat.setTintList(wrappedDrawable, colors);
+        return wrappedDrawable;
+    }
+
+    public void decodeBitmapInBackground(@NonNull Context context, @NonNull Uri uri, @Nullable Uri outputUri, BitmapLoadCallback loadCallback) {
+        int maxBitmapSize = BitmapLoadUtils.calculateMaxBitmapSize(context);
+        decodeBitmapInBackground(context, uri, outputUri, maxBitmapSize, maxBitmapSize, loadCallback);
+    }
+
+    /**
+     * 获取图片Bitmap
+     */
+    public void decodeBitmapInBackground(@NonNull Context context, @NonNull Uri uri, @Nullable Uri outputUri,
+                                         int requiredWidth, int requiredHeight, BitmapLoadCallback loadCallback) {
+        BitmapLoadUtils.decodeBitmapInBackground(context, uri, outputUri, requiredWidth, requiredHeight, loadCallback);
+    }
+
+    /**
+     * 图片压缩旋转
+     */
+    public void rotateImage(@NonNull Context context, @NonNull Uri uri, @Nullable Uri outputUri,
+                            int requiredWidth, int requiredHeight, BitmapLoadCallback loadCallback) {
+
+    }
+}

+ 21 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/CameraUtils.java

@@ -0,0 +1,21 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/6/3 下午8:28
+ */
+public class CameraUtils {
+
+    /**
+     * 判断设备是否有摄像头
+     */
+    public static boolean hasCamera(Context context) {
+        PackageManager packageManager = context.getPackageManager();
+        return packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
+
+    }
+}

+ 21 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/DeviceUtils.java

@@ -0,0 +1,21 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.view.WindowManager;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/8/8 下午11:05
+ */
+public class DeviceUtils {
+
+    public static DisplayMetrics getScreenSize(Context context) {
+        DisplayMetrics displaysMetrics = new DisplayMetrics();
+        context.getResources().getDisplayMetrics();
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        wm.getDefaultDisplay().getMetrics(displaysMetrics);
+        return displaysMetrics;
+    }
+}

+ 35 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/EmptyViewUtils.java

@@ -0,0 +1,35 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/14 下午7:26
+ */
+public class EmptyViewUtils {
+
+    public static void showLoading(ViewGroup emptyView) {
+        if (emptyView == null) {
+            return;
+        }
+        ProgressBar pbLoading = (ProgressBar) emptyView.getChildAt(0);
+        pbLoading.setVisibility(View.VISIBLE);
+        TextView tvEmptyMsg = (TextView) emptyView.getChildAt(1);
+        tvEmptyMsg.setVisibility(View.GONE);
+    }
+
+    public static void showMessage(ViewGroup emptyView, String msg) {
+        if (emptyView == null) {
+            return;
+        }
+        ProgressBar pbLoading = (ProgressBar) emptyView.getChildAt(0);
+        pbLoading.setVisibility(View.GONE);
+        TextView tvEmptyMsg = (TextView) emptyView.getChildAt(1);
+        tvEmptyMsg.setVisibility(View.VISIBLE);
+        tvEmptyMsg.setText(msg);
+    }
+}

+ 15 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/FileUtils.java

@@ -0,0 +1,15 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+/**
+ * Desction:文件工具类
+ * Author:dujinyang
+ */
+public class FileUtils {
+
+    /**
+     * 验证是否是图片路径
+     */
+    public static int existImageDir(String dir) {
+        return dir.trim().lastIndexOf(".");
+    }
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1060 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/FilenameUtils.java


+ 44 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/IOUtils.java

@@ -0,0 +1,44 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/7 上午9:45
+ */
+public class IOUtils {
+
+    public static void close(OutputStream stream) {
+        if (stream != null) {
+            try {
+                stream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public static void close(InputStream stream) {
+        if (stream != null) {
+            try {
+                stream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public static void flush(OutputStream stream) {
+        if (stream != null) {
+            try {
+                stream.flush();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+}

+ 46 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/Logger.java

@@ -0,0 +1,46 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.util.Log;
+
+import cn.finalteam.rxgalleryfinal.BuildConfig;
+
+/**
+ * Desction:日志工具
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/23 下午3:12
+ */
+public class Logger {
+
+    public static final String TAG = "RxGalleryFinal";
+    public static boolean DEBUG = BuildConfig.DEBUG;
+
+    public static void d(String value) {
+        if (DEBUG) {
+            Log.d(TAG, value);
+        }
+    }
+
+    public static void e(String value) {
+        if (DEBUG) {
+            Log.e(TAG, value);
+        }
+    }
+
+    public static void e(Exception value) {
+        if (DEBUG && value != null) {
+            Log.e(TAG, value.getMessage());
+        }
+    }
+
+    public static void i(String value) {
+        if (DEBUG) {
+            Log.i(TAG, value);
+        }
+    }
+
+    public static void w(String value) {
+        if (DEBUG) {
+            Log.w(TAG, value);
+        }
+    }
+}

+ 86 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/MediaScanner.java

@@ -0,0 +1,86 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.content.Context;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+
+/**
+ * Desction:媒体扫描工具类
+ * Author:pengjianbo  Dujinyang
+ * Date:16/6/8 上午11:36
+ */
+public class MediaScanner {
+    private MediaScannerConnection mediaScanConn = null;
+    private String fileType = null;
+    private String[] filePaths = null;
+    private ScanCallback scanCallback;
+
+    /**
+     * 然后调用MediaScanner.scanFile("/sdcard/2.mp3");
+     */
+    public MediaScanner(Context context) {
+        MusicSannerClient client;
+        client = new MusicSannerClient();
+
+        if (mediaScanConn == null) {
+            mediaScanConn = new MediaScannerConnection(context, client);
+        }
+    }
+
+    /**
+     * 扫描文件标签信息
+     *
+     * @param filePath 文件路径
+     * @param fileType 文件类型
+     */
+
+    public void scanFile(String filePath, String fileType, ScanCallback callback) {
+        this.filePaths = new String[]{filePath};
+        this.fileType = fileType;
+        this.scanCallback = callback;
+        //连接之后调用MusicSannerClient的onMediaScannerConnected()方法
+        mediaScanConn.connect();
+    }
+
+    /**
+     * @param filePaths 文件路径
+     * @param fileType  文件类型
+     */
+    public void scanFile(String[] filePaths, String fileType, ScanCallback callback) {
+        this.filePaths = filePaths;
+        this.fileType = fileType;
+        this.scanCallback = callback;
+        mediaScanConn.connect();
+    }
+
+    public void unScanFile() {
+        mediaScanConn.disconnect();
+    }
+
+    public interface ScanCallback {
+        void onScanCompleted(String[] images);
+    }
+
+    private class MusicSannerClient implements MediaScannerConnection.MediaScannerConnectionClient {
+        @Override
+        public void onMediaScannerConnected() {
+            Logger.i("onMediaScannerConnected");
+            if (filePaths != null) {
+                for (String file : filePaths) {
+                    mediaScanConn.scanFile(file, fileType);
+                }
+            }
+        }
+
+        @Override
+        public void onScanCompleted(String path, Uri uri) {
+            Logger.i("onScanCompleted");
+            mediaScanConn.disconnect();
+            if (scanCallback != null) {
+                scanCallback.onScanCompleted(filePaths);
+            }
+            fileType = null;
+            filePaths = null;
+        }
+    }
+}

+ 16 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/MediaType.java

@@ -0,0 +1,16 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import java.io.Serializable;
+
+/**
+ * Desction:支持的Media类型
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/5 下午5:03
+ */
+public enum MediaType implements Serializable {
+    JPG, PNG, WEBP, GIF, MP4;
+
+    public boolean hasVideo() {
+        return this == MP4;
+    }
+}

+ 439 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/MediaUtils.java

@@ -0,0 +1,439 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.MediaStore;
+import android.support.annotation.Nullable;
+import android.support.media.ExifInterface;
+import android.text.TextUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.R;
+import cn.finalteam.rxgalleryfinal.bean.BucketBean;
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+
+/**
+ * Desction:媒体获取工具
+ * Author:pengjianbo  Dujinyang
+ * Date:16/5/4 下午4:11
+ */
+public class MediaUtils {
+
+    public static List<MediaBean> getMediaWithImageList(Context context, int page, int limit) {
+        return getMediaWithImageList(context, String.valueOf(Integer.MIN_VALUE), page, limit);
+    }
+
+    /**
+     * 从数据库中读取图片
+     */
+    public static List<MediaBean> getMediaWithImageList(Context context, String bucketId, int page, int limit) {
+        int offset = (page - 1) * limit;
+        List<MediaBean> mediaBeanList = new ArrayList<>();
+        ContentResolver contentResolver = context.getContentResolver();
+        List<String> projection = new ArrayList<>();
+        projection.add(MediaStore.Images.Media._ID);
+        projection.add(MediaStore.Images.Media.TITLE);
+        projection.add(MediaStore.Images.Media.DATA);
+        projection.add(MediaStore.Images.Media.BUCKET_ID);
+        projection.add(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
+        projection.add(MediaStore.Images.Media.MIME_TYPE);
+        projection.add(MediaStore.Images.Media.DATE_ADDED);
+        projection.add(MediaStore.Images.Media.DATE_MODIFIED);
+        projection.add(MediaStore.Images.Media.LATITUDE);
+        projection.add(MediaStore.Images.Media.LONGITUDE);
+        projection.add(MediaStore.Images.Media.ORIENTATION);
+        projection.add(MediaStore.Images.Media.SIZE);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            projection.add(MediaStore.Images.Media.WIDTH);
+            projection.add(MediaStore.Images.Media.HEIGHT);
+        }
+        String selection = null;
+        String[] selectionArgs = null;
+        if (!TextUtils.equals(bucketId, String.valueOf(Integer.MIN_VALUE))) {
+            selection = MediaStore.Images.Media.BUCKET_ID + "=?";
+            selectionArgs = new String[]{bucketId};
+        }
+        Cursor cursor = contentResolver.query(
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection.toArray(new String[projection.size()]), selection,
+                selectionArgs, MediaStore.Images.Media.DATE_ADDED + " DESC LIMIT " + limit + " OFFSET " + offset);
+        if (cursor != null) {
+            int count = cursor.getCount();
+            if (count > 0) {
+                cursor.moveToFirst();
+                do {
+                    MediaBean mediaBean = parseImageCursorAndCreateThumImage(context, cursor);
+                    if (mediaBean != null) {
+                        mediaBeanList.add(mediaBean);
+                    }
+                } while (cursor.moveToNext());
+            }
+        }
+
+        if (cursor != null && !cursor.isClosed()) {
+            cursor.close();
+        }
+        return mediaBeanList;
+    }
+
+    public static List<MediaBean> getMediaWithVideoList(Context context, int page, int limit) {
+        return getMediaWithVideoList(context, String.valueOf(Integer.MIN_VALUE), page, limit);
+    }
+
+    /**
+     * 从数据库中读取视频
+     */
+    public static List<MediaBean> getMediaWithVideoList(Context context, String bucketId, int page, int limit) {
+        int offset = (page - 1) * limit;
+        List<MediaBean> mediaBeanList = new ArrayList<>();
+        ContentResolver contentResolver = context.getContentResolver();
+        List<String> projection = new ArrayList<>();
+        projection.add(MediaStore.Video.Media._ID);
+        projection.add(MediaStore.Video.Media.TITLE);
+        projection.add(MediaStore.Video.Media.DATA);
+        projection.add(MediaStore.Video.Media.BUCKET_ID);
+        projection.add(MediaStore.Video.Media.BUCKET_DISPLAY_NAME);
+        projection.add(MediaStore.Video.Media.MIME_TYPE);
+        projection.add(MediaStore.Video.Media.DATE_ADDED);
+        projection.add(MediaStore.Video.Media.DATE_MODIFIED);
+        projection.add(MediaStore.Video.Media.LATITUDE);
+        projection.add(MediaStore.Video.Media.LONGITUDE);
+        projection.add(MediaStore.Video.Media.SIZE);
+        projection.add(MediaStore.Video.Media.DURATION);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            projection.add(MediaStore.Video.Media.WIDTH);
+            projection.add(MediaStore.Video.Media.HEIGHT);
+        }
+        String selection = null;
+        String[] selectionArgs = null;
+        if (!TextUtils.equals(bucketId, String.valueOf(Integer.MIN_VALUE))) {
+            selection = MediaStore.Video.Media.BUCKET_ID + "=?";
+            selectionArgs = new String[]{bucketId};
+        }
+
+        Cursor cursor = contentResolver.query(
+                MediaStore.Video.Media.EXTERNAL_CONTENT_URI, projection.toArray(new String[projection.size()]), selection,
+                selectionArgs, MediaStore.Video.Media.DATE_ADDED + " DESC LIMIT " + limit + " OFFSET " + offset);
+        if (cursor != null) {
+            int count = cursor.getCount();
+            if (count > 0) {
+                cursor.moveToFirst();
+                do {
+                    MediaBean mediaBean = parseVideoCursorAndCreateThumImage(context, cursor);
+                    mediaBeanList.add(mediaBean);
+                } while (cursor.moveToNext());
+            }
+        }
+
+        if (cursor != null && !cursor.isClosed()) {
+            cursor.close();
+        }
+        return mediaBeanList;
+    }
+
+    /**
+     * 根据原图获取图片相关信息
+     */
+    public static MediaBean getMediaBeanWithImage(Context context, String originalPath) {
+        ContentResolver contentResolver = context.getContentResolver();
+        List<String> projection = new ArrayList<>();
+        projection.add(MediaStore.Images.Media._ID);
+        projection.add(MediaStore.Images.Media.TITLE);
+        projection.add(MediaStore.Images.Media.DATA);
+        projection.add(MediaStore.Images.Media.BUCKET_ID);
+        projection.add(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
+        projection.add(MediaStore.Images.Media.MIME_TYPE);
+        projection.add(MediaStore.Images.Media.DATE_ADDED);
+        projection.add(MediaStore.Images.Media.DATE_MODIFIED);
+        projection.add(MediaStore.Images.Media.LATITUDE);
+        projection.add(MediaStore.Images.Media.LONGITUDE);
+        projection.add(MediaStore.Images.Media.ORIENTATION);
+        projection.add(MediaStore.Images.Media.SIZE);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            projection.add(MediaStore.Images.Media.WIDTH);
+            projection.add(MediaStore.Images.Media.HEIGHT);
+        }
+        Cursor cursor = contentResolver.query(
+                MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection.toArray(new String[projection.size()]), MediaStore.Images.Media.DATA + "=?",
+                new String[]{originalPath}, null);
+        MediaBean mediaBean = null;
+        if (cursor != null && cursor.getCount() > 0) {
+            cursor.moveToFirst();
+            mediaBean = parseImageCursorAndCreateThumImage(context, cursor);
+        }
+        if (cursor != null && !cursor.isClosed()) {
+            cursor.close();
+        }
+        return mediaBean;
+    }
+
+    /**
+     * 根据地址获取视频相关信息
+     */
+    public static MediaBean getMediaBeanWithVideo(Context context, String originalPath) {
+        ContentResolver contentResolver = context.getContentResolver();
+        List<String> projection = new ArrayList<>();
+        projection.add(MediaStore.Video.Media._ID);
+        projection.add(MediaStore.Video.Media.TITLE);
+        projection.add(MediaStore.Video.Media.DATA);
+        projection.add(MediaStore.Video.Media.BUCKET_ID);
+        projection.add(MediaStore.Video.Media.BUCKET_DISPLAY_NAME);
+        projection.add(MediaStore.Video.Media.MIME_TYPE);
+        projection.add(MediaStore.Video.Media.DATE_ADDED);
+        projection.add(MediaStore.Video.Media.DATE_MODIFIED);
+        projection.add(MediaStore.Video.Media.LATITUDE);
+        projection.add(MediaStore.Video.Media.LONGITUDE);
+        projection.add(MediaStore.Video.Media.SIZE);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            projection.add(MediaStore.Video.Media.WIDTH);
+            projection.add(MediaStore.Video.Media.HEIGHT);
+        }
+        Cursor cursor = contentResolver.query(
+                MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
+                projection.toArray(new String[projection.size()]),
+                MediaStore.Images.Media.DATA + "=?",
+                new String[]{originalPath}, null);
+        MediaBean mediaBean = null;
+        if (cursor != null && cursor.getCount() > 0) {
+            cursor.moveToFirst();
+            mediaBean = parseVideoCursorAndCreateThumImage(context, cursor);
+        }
+        if (cursor != null && !cursor.isClosed()) {
+            cursor.close();
+        }
+        return mediaBean;
+    }
+
+
+    /**
+     * 解析图片cursor并且创建缩略图
+     * <p>
+     * update 17.07.23 log
+     * <p>
+     * 判断图片 Size ,如果小于等于0则返回 Null,避免出现 No such file or directory
+     */
+    @Nullable
+    private static MediaBean parseImageCursorAndCreateThumImage(Context context, Cursor cursor) {
+        long size = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.SIZE));
+        String originalPath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
+        if (TextUtils.isEmpty(originalPath) || size <= 0 || !new File(originalPath).exists()) {
+            return null;
+        }
+
+        long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID));
+        String title = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.TITLE));
+        String bucketId = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID));
+        String bucketDisplayName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME));
+        String mimeType = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE));
+        long createDate = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));
+        long modifiedDate = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_MODIFIED));
+
+        MediaBean mediaBean = new MediaBean();
+        mediaBean.setId(id);
+        mediaBean.setTitle(title);
+        mediaBean.setOriginalPath(originalPath);
+        mediaBean.setBucketId(bucketId);
+        mediaBean.setBucketDisplayName(bucketDisplayName);
+        mediaBean.setMimeType(mimeType);
+        mediaBean.setCreateDate(createDate);
+        mediaBean.setModifiedDate(modifiedDate);
+        mediaBean.setThumbnailBigPath(createThumbnailBigFileName(context, originalPath).getAbsolutePath());
+        mediaBean.setThumbnailSmallPath(createThumbnailSmallFileName(context, originalPath).getAbsolutePath());
+        int width = 0, height = 0;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            width = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media.WIDTH));
+            height = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media.HEIGHT));
+        } else {
+            try {
+                ExifInterface exifInterface = new ExifInterface(originalPath);
+                width = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
+                height = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
+            } catch (IOException e) {
+                Logger.e(e);
+            }
+        }
+        mediaBean.setWidth(width);
+        mediaBean.setHeight(height);
+        double latitude = cursor.getDouble(cursor.getColumnIndex(MediaStore.Images.Media.LATITUDE));
+        mediaBean.setLatitude(latitude);
+        double longitude = cursor.getDouble(cursor.getColumnIndex(MediaStore.Images.Media.LONGITUDE));
+        mediaBean.setLongitude(longitude);
+        int orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media.ORIENTATION));
+        mediaBean.setOrientation(orientation);
+        long length = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.SIZE));
+        mediaBean.setLength(length);
+
+        return mediaBean;
+    }
+
+    /**
+     * 解析视频cursor并且创建缩略图
+     */
+    private static MediaBean parseVideoCursorAndCreateThumImage(Context context, Cursor cursor) {
+        MediaBean mediaBean = new MediaBean();
+        long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media._ID));
+        mediaBean.setId(id);
+        String title = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.TITLE));
+        mediaBean.setTitle(title);
+        String originalPath = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA));
+        mediaBean.setOriginalPath(originalPath);
+        String bucketId = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_ID));
+        mediaBean.setBucketId(bucketId);
+        String bucketDisplayName = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_DISPLAY_NAME));
+        mediaBean.setBucketDisplayName(bucketDisplayName);
+        String mimeType = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.MIME_TYPE));
+        mediaBean.setMimeType(mimeType);
+        long createDate = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.DATE_ADDED));
+        mediaBean.setCreateDate(createDate);
+        long modifiedDate = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.DATE_MODIFIED));
+        mediaBean.setModifiedDate(modifiedDate);
+        long length = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.SIZE));
+        mediaBean.setLength(length);
+        long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.DURATION));
+        mediaBean.setDuration(duration);
+
+        //创建缩略图文件
+        mediaBean.setThumbnailBigPath(createThumbnailBigFileName(context, originalPath).getAbsolutePath());
+        mediaBean.setThumbnailSmallPath(createThumbnailSmallFileName(context, originalPath).getAbsolutePath());
+
+        int width = 0, height = 0;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            width = cursor.getInt(cursor.getColumnIndex(MediaStore.Video.Media.WIDTH));
+            height = cursor.getInt(cursor.getColumnIndex(MediaStore.Video.Media.HEIGHT));
+        } else {
+            try {
+                ExifInterface exifInterface = new ExifInterface(originalPath);
+                width = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
+                height = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
+            } catch (IOException e) {
+                Logger.e(e);
+            }
+        }
+        mediaBean.setWidth(width);
+        mediaBean.setHeight(height);
+
+        double latitude = cursor.getDouble(cursor.getColumnIndex(MediaStore.Video.Media.LATITUDE));
+        mediaBean.setLatitude(latitude);
+        double longitude = cursor.getDouble(cursor.getColumnIndex(MediaStore.Video.Media.LONGITUDE));
+        mediaBean.setLongitude(longitude);
+        return mediaBean;
+    }
+
+    public static File createThumbnailBigFileName(Context context, String originalPath) {
+        File storeFile = StorageUtils.getCacheDirectory(context);
+        return new File(storeFile, "big_" + FilenameUtils.getName(originalPath));
+    }
+
+    public static File createThumbnailSmallFileName(Context context, String originalPath) {
+        File storeFile = StorageUtils.getCacheDirectory(context);
+        return new File(storeFile, "small_" + FilenameUtils.getName(originalPath));
+    }
+
+    /**
+     * 获取所有的图片文件夹
+     */
+    public static List<BucketBean> getAllBucketByImage(Context context) {
+        return getAllBucket(context, true);
+    }
+
+    /**
+     * 获取所以视频文件夹
+     */
+    public static List<BucketBean> getAllBucketByVideo(Context context) {
+        return getAllBucket(context, false);
+    }
+
+    /**
+     * 获取所有的问media文件夹
+     */
+    public static List<BucketBean> getAllBucket(Context context, boolean isImage) {
+        List<BucketBean> bucketBeenList = new ArrayList<>();
+        ContentResolver contentResolver = context.getContentResolver();
+        String[] projection;
+        if (isImage) {
+            projection = new String[]{
+                    MediaStore.Images.Media.BUCKET_ID,
+                    MediaStore.Images.Media.DATA,
+                    MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
+                    MediaStore.Images.Media.ORIENTATION,
+            };
+        } else {
+            projection = new String[]{
+                    MediaStore.Video.Media.BUCKET_ID,
+                    MediaStore.Video.Media.DATA,
+                    MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
+            };
+        }
+        BucketBean allMediaBucket = new BucketBean();
+        allMediaBucket.setBucketId(String.valueOf(Integer.MIN_VALUE));
+        Uri uri;
+        if (isImage) {
+            allMediaBucket.setBucketName(context.getString(R.string.gallery_all_image));
+            uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+        } else {
+            allMediaBucket.setBucketName(context.getString(R.string.gallery_all_video));
+            uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+        }
+        bucketBeenList.add(allMediaBucket);
+        Cursor cursor = null;
+        try {
+            cursor = contentResolver.query(uri, projection, null, null, MediaStore.Video.Media.DATE_ADDED + " DESC");
+        } catch (Exception e) {
+            Logger.e(e);
+        }
+
+        if (cursor != null && cursor.getCount() > 0) {
+            cursor.moveToFirst();
+            do {
+                BucketBean bucketBean = new BucketBean();
+                String bucketId;
+                String bucketKey;
+                String cover;
+                if (isImage) {
+                    bucketId = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID));
+                    bucketBean.setBucketId(bucketId);
+                    String bucketDisplayName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME));
+                    bucketBean.setBucketName(bucketDisplayName);
+                    bucketKey = MediaStore.Images.Media.BUCKET_ID;
+                    cover = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
+                    int orientation = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media.ORIENTATION));
+                    bucketBean.setOrientation(orientation);
+                } else {
+                    bucketId = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_ID));
+                    bucketBean.setBucketId(bucketId);
+                    String bucketDisplayName = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.BUCKET_DISPLAY_NAME));
+                    bucketBean.setBucketName(bucketDisplayName);
+                    bucketKey = MediaStore.Video.Media.BUCKET_ID;
+                    cover = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA));
+                }
+                if (TextUtils.isEmpty(allMediaBucket.getCover())) {
+                    allMediaBucket.setCover(cover);
+                }
+                if (bucketBeenList.contains(bucketBean)) {
+                    continue;
+                }
+                //获取数量
+                Cursor c = contentResolver.query(uri, projection, bucketKey + "=?", new String[]{bucketId}, null);
+                if (c != null && c.getCount() > 0) {
+                    bucketBean.setImageCount(c.getCount());
+                }
+                bucketBean.setCover(cover);
+                if (c != null && !c.isClosed()) {
+                    c.close();
+                }
+                bucketBeenList.add(bucketBean);
+            } while (cursor.moveToNext());
+        }
+
+        if (cursor != null && !cursor.isClosed()) {
+            cursor.close();
+        }
+        return bucketBeenList;
+    }
+}

+ 32 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/ModelUtils.java

@@ -0,0 +1,32 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+
+import android.util.Log;
+
+import cn.finalteam.rxgalleryfinal.BuildConfig;
+
+/**
+ * 模式
+ * Created by KARL-dujinyang on 2017-03-17 02-24-08.
+ */
+public class ModelUtils {
+    private static final String TAG = "Test";
+
+    public static void logDebug() {
+        Log.w(TAG, "BuildConfig.DEBUG:--" + BuildConfig.DEBUG + "--");
+        if (BuildConfig.DEBUG)
+            Logger.w("is debug mode");
+        else
+            Logger.w("not debug mode");
+    }
+
+    /**
+     * 多层依赖时DEBUGCONFIG会出错,所以提供了内部接口更改
+     *
+     * @param f 是否打开
+     */
+    public static void setDebugModel(boolean f) {
+        Logger.DEBUG = f;
+    }
+
+}

+ 23 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/OsCompat.java

@@ -0,0 +1,23 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.View;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/20 下午4:23
+ */
+public class OsCompat {
+
+    public static void setBackgroundDrawableCompat(View view, Drawable drawable) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            view.setBackground(drawable);
+        } else {
+            //noinspection deprecation
+            view.setBackgroundDrawable(drawable);
+        }
+    }
+
+}

+ 82 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/PermissionCheckUtils.java

@@ -0,0 +1,82 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+
+/**
+ * Desction:权限检查工具
+ * Author:pengjianbo  Dujinyang
+ * Author:KARL-dujinyang
+ * Date:16/6/1 下午7:40
+ */
+public class PermissionCheckUtils {
+
+    /**
+     * 数组
+     */
+    public static boolean checkPermission(final Activity activity, final String permission, String permissionDesc, final int requestCode) {
+        int currentAPIVersion = Build.VERSION.SDK_INT;
+        if (currentAPIVersion >= android.os.Build.VERSION_CODES.M) {
+            if (ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
+                Logger.i("ContextCompat.checkSelfPermission(activity, permission):" + ContextCompat.checkSelfPermission(activity, permission));
+                Logger.i("PackageManager.PERMISSION_GRANTED:" + PackageManager.PERMISSION_GRANTED);
+                Logger.i("permission:" + permission);
+                if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
+                    AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity);
+                    alertBuilder.setCancelable(false);
+                    alertBuilder.setTitle("授权对话框");
+                    alertBuilder.setMessage(permissionDesc);
+                    alertBuilder.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            ActivityCompat.requestPermissions(activity, new String[]{permission}, requestCode);
+                        }
+                    });
+                    AlertDialog alert = alertBuilder.create();
+                    alert.show();
+                } else {
+                    ActivityCompat.requestPermissions(activity, new String[]{permission}, requestCode);
+                }
+                return false;
+            } else {
+                return true;
+            }
+        } else {
+            return true;
+        }
+    }
+
+
+    /**
+     * 检查是否对sd卡读取授权
+     */
+    @TargetApi(16)
+    public static boolean checkReadExternalPermission(Activity activity, String permissionDesc, int requestCode) {
+        return checkPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE, permissionDesc, requestCode);
+    }
+
+
+    /**
+     * 检查是否对sd卡读取授权
+     */
+    @TargetApi(16)
+    public static boolean checkWriteExternalPermission(Activity activity, String permissionDesc, int requestCode) {
+        return checkPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, permissionDesc, requestCode);
+    }
+
+    /**
+     * 检查是否对相机读取授权
+     */
+    @TargetApi(16)
+    public static boolean checkCameraPermission(Activity activity, String permissionDesc, int requestCode) {
+        return checkPermission(activity, Manifest.permission.CAMERA, permissionDesc, requestCode);
+    }
+
+}

+ 17 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/SimpleDateUtils.java

@@ -0,0 +1,17 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * 时间工具类
+ * Created by KARL-dujinyang on 2017-04-13.
+ */
+public class SimpleDateUtils {
+
+    public static String getNowTime() {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
+        return dateFormat.format(new Date());
+    }
+}

+ 170 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/StorageUtils.java

@@ -0,0 +1,170 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+
+import java.io.File;
+import java.io.IOException;
+
+import static android.os.Environment.MEDIA_MOUNTED;
+
+/**
+ * Provides application storage paths
+ *
+ * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
+ * @since 1.0.0
+ */
+public final class StorageUtils {
+
+    private static final String EXTERNAL_STORAGE_PERMISSION = "android.permission.WRITE_EXTERNAL_STORAGE";
+    private static final String INDIVIDUAL_DIR_NAME = "rxgalleryfinal";
+
+    private StorageUtils() {
+    }
+
+    /**
+     * Returns application cache directory. Cache directory will be created on SD card
+     * <i>("/Android/data/[app_package_name]/cache")</i> if card is mounted and app has appropriate permission. Else -
+     * Android defines cache directory on device's file system.
+     *
+     * @param context Application context
+     * @return Cache {@link File directory}.<br />
+     * <b>NOTE:</b> Can be null in some unpredictable cases (if SD card is unmounted and
+     * {@link android.content.Context#getCacheDir() Context.getCacheDir()} returns null).
+     */
+    public static File getCacheDirectory(Context context) {
+        return getCacheDirectory(context, true);
+    }
+
+    /**
+     * Returns application cache directory. Cache directory will be created on SD card
+     * <i>("/Android/data/[app_package_name]/cache")</i> (if card is mounted and app has appropriate permission) or
+     * on device's file system depending incoming parameters.
+     *
+     * @param context        Application context
+     * @param preferExternal Whether prefer external location for cache
+     * @return Cache {@link File directory}.<br />
+     * <b>NOTE:</b> Can be null in some unpredictable cases (if SD card is unmounted and
+     * {@link android.content.Context#getCacheDir() Context.getCacheDir()} returns null).
+     */
+    public static File getCacheDirectory(Context context, boolean preferExternal) {
+        File appCacheDir = null;
+
+        if (preferExternal && existSDcard() && hasExternalStoragePermission(context)) {
+            appCacheDir = getExternalCacheDir(context);
+        }
+        if (appCacheDir == null) {
+            appCacheDir = context.getCacheDir();
+        }
+        if (appCacheDir == null) {
+            @SuppressLint("SdCardPath") String cacheDirPath = "/data/data/" + context.getPackageName() + "/cache/";
+            Logger.w(String.format("Can't define system cache directory! '%s' will be used.", cacheDirPath));
+            appCacheDir = new File(cacheDirPath);
+        }
+        return appCacheDir;
+    }
+
+    public static boolean existSDcard() {
+        String externalStorageState;
+        try {
+            externalStorageState = Environment.getExternalStorageState();
+        } catch (NullPointerException e) { // (sh)it happens (Issue #660)
+            externalStorageState = "";
+        }
+        return MEDIA_MOUNTED.equals(externalStorageState);
+    }
+
+    /**
+     * Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be
+     * created on SD card <i>("/Android/data/[app_package_name]/cache/uil-images")</i> if card is mounted and app has
+     * appropriate permission. Else - Android defines cache directory on device's file system.
+     *
+     * @param context Application context
+     * @return Cache {@link File directory}
+     */
+    public static File getIndividualCacheDirectory(Context context) {
+        return getIndividualCacheDirectory(context, INDIVIDUAL_DIR_NAME);
+    }
+
+    /**
+     * Returns individual application cache directory (for only image caching from ImageLoader). Cache directory will be
+     * created on SD card <i>("/Android/data/[app_package_name]/cache/uil-images")</i> if card is mounted and app has
+     * appropriate permission. Else - Android defines cache directory on device's file system.
+     *
+     * @param context  Application context
+     * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
+     * @return Cache {@link File directory}
+     */
+    public static File getIndividualCacheDirectory(Context context, String cacheDir) {
+        File appCacheDir = getCacheDirectory(context);
+        File individualCacheDir = new File(appCacheDir, cacheDir);
+        if (!individualCacheDir.exists()) {
+            if (!individualCacheDir.mkdir()) {
+                individualCacheDir = appCacheDir;
+            }
+        }
+        return individualCacheDir;
+    }
+
+    /**
+     * Returns specified application cache directory. Cache directory will be created on SD card by defined path if card
+     * is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system.
+     *
+     * @param context  Application context
+     * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
+     * @return Cache {@link File directory}
+     */
+    public static File getOwnCacheDirectory(Context context, String cacheDir) {
+        File appCacheDir = null;
+        if (MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) {
+            appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
+        }
+        if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) {
+            appCacheDir = context.getCacheDir();
+        }
+        return appCacheDir;
+    }
+
+    /**
+     * Returns specified application cache directory. Cache directory will be created on SD card by defined path if card
+     * is mounted and app has appropriate permission. Else - Android defines cache directory on device's file system.
+     *
+     * @param context  Application context
+     * @param cacheDir Cache directory path (e.g.: "AppCacheDir", "AppDir/cache/images")
+     * @return Cache {@link File directory}
+     */
+    public static File getOwnCacheDirectory(Context context, String cacheDir, boolean preferExternal) {
+        File appCacheDir = null;
+        if (preferExternal && MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) && hasExternalStoragePermission(context)) {
+            appCacheDir = new File(Environment.getExternalStorageDirectory(), cacheDir);
+        }
+        if (appCacheDir == null || (!appCacheDir.exists() && !appCacheDir.mkdirs())) {
+            appCacheDir = context.getCacheDir();
+        }
+        return appCacheDir;
+    }
+
+    private static File getExternalCacheDir(Context context) {
+        File dataDir = new File(new File(Environment.getExternalStorageDirectory(), "Android"), "data");
+        File appCacheDir = new File(new File(dataDir, context.getPackageName()), "cache");
+        if (!appCacheDir.exists()) {
+            if (!appCacheDir.mkdirs()) {
+                Logger.w("Unable to create external cache directory");
+                return null;
+            }
+            try {
+                new File(appCacheDir, ".nomedia").createNewFile();
+            } catch (IOException e) {
+                Logger.i("Can't create \".nomedia\" file in application external cache directory");
+            }
+        }
+        return appCacheDir;
+    }
+
+    private static boolean hasExternalStoragePermission(Context context) {
+        int perm = context.checkCallingOrSelfPermission(EXTERNAL_STORAGE_PERMISSION);
+        return perm == PackageManager.PERMISSION_GRANTED;
+    }
+}

+ 189 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/ThemeUtils.java

@@ -0,0 +1,189 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.annotation.AttrRes;
+import android.support.annotation.ColorInt;
+import android.support.v4.content.ContextCompat;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+/**
+ * Desction:
+ * Author:pengjianbo  Dujinyang
+ * Date:16/7/14 下午7:10
+ */
+public class ThemeUtils {
+    public static int resolveColor(Context context, @AttrRes int attr) {
+        return resolveColor(context, attr, 0);
+    }
+
+    public static int resolveColor(Context context, @AttrRes int attr, int fallback) {
+        TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
+        int color = 0;
+        if (fallback != 0) {
+            color = ContextCompat.getColor(context, fallback);
+        }
+        try {
+            return a.getColor(0, color);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    public static float resolveDimen(Context context, @AttrRes int attr) {
+        return resolveDimen(context, attr, 0);
+    }
+
+    public static float resolveDimen(Context context, @AttrRes int attr, int fallback) {
+        TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
+        float size = 0;
+        if (fallback != 0) {
+            size = context.getResources().getDimension(fallback);
+        }
+        try {
+            return a.getDimension(0, size);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    public static String resolveString(Context context, @AttrRes int attr) {
+        return resolveString(context, attr, 0);
+    }
+
+    public static String resolveString(Context context, @AttrRes int attr, int fallback) {
+        TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
+        String value;
+        try {
+            String s = a.getString(0);
+            if (TextUtils.isEmpty(s)) {
+                s = context.getString(fallback);
+            }
+            value = s;
+        } finally {
+            a.recycle();
+        }
+
+        return value;
+    }
+
+    public static boolean resolveBoolean(Context context, @AttrRes int attr) {
+        return resolveBoolean(context, attr, 0);
+    }
+
+    public static boolean resolveBoolean(Context context, @AttrRes int attr, int fallback) {
+        TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
+        boolean defValue = false;
+        if (fallback != 0) {
+            defValue = context.getResources().getBoolean(fallback);
+        }
+        try {
+            return a.getBoolean(0, defValue);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    public static int resolveInteger(Context context, @AttrRes int attr) {
+        return resolveInteger(context, attr, 0);
+    }
+
+    public static int resolveInteger(Context context, @AttrRes int attr, int fallback) {
+        TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
+        int defValue = 0;
+        if (fallback != 0) {
+            defValue = context.getResources().getInteger(fallback);
+        }
+        try {
+            return a.getInteger(0, defValue);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    public static int resolveDrawableRes(Context context, @AttrRes int attr) {
+        return resolveDrawableRes(context, attr, 0);
+    }
+
+    public static int resolveDrawableRes(Context context, @AttrRes int attr, int fallback) {
+        TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
+        try {
+            return a.getResourceId(0, fallback);
+        } finally {
+            a.recycle();
+        }
+    }
+
+    public static Drawable resolveDrawable(Context context, @AttrRes int attr) {
+        return resolveDrawable(context, attr, 0);
+    }
+
+    public static Drawable resolveDrawable(Context context, @AttrRes int attr, int fallback) {
+        TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
+        Drawable drawable = null;
+        if (fallback != 0) {
+            drawable = ContextCompat.getDrawable(context, fallback).mutate();
+        }
+        try {
+            Drawable d = a.getDrawable(0);
+            if (d != null) {
+                drawable = d;
+            }
+        } finally {
+            a.recycle();
+        }
+
+        return drawable;
+    }
+
+    public static float applyDimensionDp(Context context, float value) {
+        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, context.getResources().getDisplayMetrics());
+    }
+
+    /**
+     * Sets status-bar color for L devices.
+     *
+     * @param color - status-bar color
+     */
+    public static void setStatusBarColor(@ColorInt int color, Window window) {
+        if (window != null) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                window.setStatusBarColor(color);
+            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+                window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+                ViewGroup mContentView = (ViewGroup) window.findViewById(Window.ID_ANDROID_CONTENT);
+                View mChildView = mContentView.getChildAt(0);
+                if (mChildView != null) {
+                    mChildView.setFitsSystemWindows(true);
+                }
+                View statusBarView = new View(window.getContext());
+                int statusBarHeight = getStatusBarHeight(window.getContext());
+                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, statusBarHeight);
+                params.gravity = Gravity.TOP;
+                statusBarView.setLayoutParams(params);
+                statusBarView.setBackgroundColor(color);
+                mContentView.addView(statusBarView);
+            }
+        }
+    }
+
+    private static int getStatusBarHeight(Context context) {
+        int statusBarHeight = 0;
+        Resources res = context.getResources();
+        int resourceId = res.getIdentifier("status_bar_height", "dimen", "android");
+        if (resourceId > 0) {
+            statusBarHeight = res.getDimensionPixelSize(resourceId);
+        }
+        return statusBarHeight;
+    }
+}

+ 0 - 0
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/utils/package-info.java


Vissa filer visades inte eftersom för många filer har ändrats