Prechádzať zdrojové kódy

Merge branch 'sheep_develop' of 10.8.230.114:xmy_android/small_sheep_android into sheep_develop

hanjing 7 rokov pred
rodič
commit
9451b29568
73 zmenil súbory, kde vykonal 4969 pridanie a 163 odobranie
  1. 0 1
      .idea/gradle.xml
  2. 1 1
      RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/MediaGridAdapter.java
  3. 4 3
      app/build.gradle
  4. 12 2
      app/src/main/AndroidManifest.xml
  5. 14 0
      app/src/main/java/cn/finalteam/rxgalleryfinal/api/CameraCallBack.java
  6. 69 0
      app/src/main/java/cn/finalteam/rxgalleryfinal/bean/BucketBean.java
  7. 283 0
      app/src/main/java/cn/finalteam/rxgalleryfinal/bean/MediaBean.java
  8. 1060 0
      app/src/main/java/cn/finalteam/rxgalleryfinal/utils/FilenameUtils.java
  9. 506 0
      app/src/main/java/cn/finalteam/rxgalleryfinal/utils/MediaUtils.java
  10. 170 0
      app/src/main/java/cn/finalteam/rxgalleryfinal/utils/StorageUtils.java
  11. 9 0
      app/src/main/java/com/kfzs/duanduan/utils/NumberFormatUtils.java
  12. 7 4
      app/src/main/java/com/kfzs/duanduan/view/DialogStorageLow.java
  13. 75 0
      app/src/main/java/com/sheep/gamegroup/absBase/AbsApiRefresh.java
  14. 4 1
      app/src/main/java/com/sheep/gamegroup/absBase/AbsObserver.java
  15. 9 0
      app/src/main/java/com/sheep/gamegroup/absBase/BaseActivity.java
  16. 48 0
      app/src/main/java/com/sheep/gamegroup/absBase/CheckErrorApiRefresh.java
  17. 19 0
      app/src/main/java/com/sheep/gamegroup/absBase/IApiRefresh.java
  18. 22 9
      app/src/main/java/com/sheep/gamegroup/absBase/ApiRefresh.java
  19. 119 0
      app/src/main/java/com/sheep/gamegroup/find/activity/ActMediaChoose.java
  20. 109 0
      app/src/main/java/com/sheep/gamegroup/find/fragment/FgtMediaPickerImg.java
  21. 126 0
      app/src/main/java/com/sheep/gamegroup/find/fragment/FgtMediaPickerVideo.java
  22. 15 0
      app/src/main/java/com/sheep/gamegroup/greendao/DDProviderHelper.java
  23. 2 2
      app/src/main/java/com/sheep/gamegroup/greendao/download/DaoMaster.java
  24. 16 2
      app/src/main/java/com/sheep/gamegroup/greendao/download/DownLoadInfo.java
  25. 16 2
      app/src/main/java/com/sheep/gamegroup/greendao/download/DownLoadInfoDao.java
  26. 1 1
      app/src/main/java/com/sheep/gamegroup/model/api/ApiService.java
  27. 3 3
      app/src/main/java/com/sheep/gamegroup/model/entity/DiscoveryVideo.java
  28. 36 0
      app/src/main/java/com/sheep/gamegroup/model/entity/Lp.java
  29. 70 0
      app/src/main/java/com/sheep/gamegroup/model/entity/Video.java
  30. 25 4
      app/src/main/java/com/sheep/gamegroup/util/FileUtil.java
  31. 35 1
      app/src/main/java/com/sheep/gamegroup/util/Jump2View.java
  32. 359 0
      app/src/main/java/com/sheep/gamegroup/util/MediaHandleUtil.java
  33. 226 0
      app/src/main/java/com/sheep/gamegroup/util/MediaMetadataRetrieverUtil.java
  34. 1 1
      app/src/main/java/com/sheep/gamegroup/util/TestUtil.java
  35. 1 1
      app/src/main/java/com/sheep/gamegroup/util/TimeUtil.java
  36. 60 12
      app/src/main/java/com/sheep/gamegroup/util/ViewUtil.java
  37. 510 0
      app/src/main/java/com/sheep/gamegroup/view/activity/ActCutVideo.java
  38. 38 0
      app/src/main/java/com/sheep/gamegroup/view/activity/ActEditVideo.java
  39. 1 1
      app/src/main/java/com/sheep/gamegroup/view/activity/ActMain.java
  40. 2 1
      app/src/main/java/com/sheep/gamegroup/view/activity/ActPlayVideo.java
  41. 7 1
      app/src/main/java/com/sheep/gamegroup/view/activity/ActPlayVideoArticle.java
  42. 45 44
      app/src/main/java/com/sheep/gamegroup/view/activity/ActPublishArticle.java
  43. 7 1
      app/src/main/java/com/sheep/gamegroup/view/adapter/AdpVideo.java
  44. 268 0
      app/src/main/java/com/sheep/gamegroup/view/customview/VideoFramesView.java
  45. 8 1
      app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment.java
  46. 18 1
      app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment2.java
  47. 8 1
      app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment3.java
  48. 8 1
      app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment4.java
  49. 8 1
      app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment5.java
  50. 41 39
      app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment6.java
  51. 0 1
      app/src/main/java/com/sheep/gamegroup/view/fragment/FgtArticleVideo.java
  52. 83 0
      app/src/main/java/com/sheep/gamegroup/view/fragment/FgtDiscoveryTopic.java
  53. 6 6
      app/src/main/java/com/sheep/gamegroup/view/fragment/FgtUserFocusLogList.java
  54. 34 0
      app/src/main/java/com/sheep/jiuyan/samllsheep/base/BaseFragment.java
  55. 23 4
      app/src/main/java/com/sheep/jiuyan/samllsheep/service/DownloadService.java
  56. 1 1
      app/src/main/java/com/sheep/jiuyan/samllsheep/utils/G.java
  57. 6 0
      app/src/main/res/drawable/shape_oval_solid_10_f5.xml
  58. 6 0
      app/src/main/res/drawable/shape_oval_solid_10_main.xml
  59. 6 0
      app/src/main/res/drawable/shape_white_20_solid_rectangle_15.xml
  60. 10 0
      app/src/main/res/layout/act_cut_video.xml
  61. 109 0
      app/src/main/res/layout/act_edit_video.xml
  62. 22 0
      app/src/main/res/layout/act_media_choose.xml
  63. 59 0
      app/src/main/res/layout/custom_video_frames_view.xml
  64. 34 0
      app/src/main/res/layout/include_lll.xml
  65. 20 0
      app/src/main/res/layout/item_discovery_topic.xml
  66. 6 0
      app/src/main/res/layout/item_iv_mh.xml
  67. 29 0
      app/src/main/res/layout/item_media_picker.xml
  68. 4 4
      app/src/main/res/layout/line_1px_hor.xml
  69. BIN
      app/src/main/res/mipmap-xxhdpi/ic_open_camera.webp
  70. 1 0
      app/src/main/res/values/colors.xml
  71. 2 0
      app/src/main/res/values/strings.xml
  72. 1 1
      settings.gradle
  73. 6 4
      view/src/main/java/com/kfzs/android/view/widget/WrapContentHeightViewPager.java

+ 0 - 1
.idea/gradle.xml

@@ -8,7 +8,6 @@
         <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 - 1
RxGalleryFinal/src/main/java/cn/finalteam/rxgalleryfinal/ui/adapter/MediaGridAdapter.java

@@ -109,7 +109,7 @@ public class MediaGridAdapter extends RecyclerView.Adapter<MediaGridAdapter.Grid
                 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));
+                holder.tv_media_duration.setText(String.format(Locale.CHINA, "%d:%02d", m, second % 60));
             } else {
                 holder.tv_media_duration.setVisibility(View.GONE);
                 holder.mCbCheck.setVisibility(View.VISIBLE);

+ 4 - 3
app/build.gradle

@@ -23,7 +23,7 @@ android {
         flavorDimensions "versionCode"
     }
     greendao {
-        schemaVersion 14
+        schemaVersion 15
         targetGenDir 'src/main/java'
     }
 //    compileOptions{
@@ -325,7 +325,7 @@ dependencies {
     //    implementation 'com.github.yalantis:ucrop:2.2.1-native'
 
     //    implementation 'com.google.zxing:core:3.3.0'
-//    implementation project(':ucrop')
+    implementation project(':ucrop')
     implementation ('com.sunfusheng:marqueeview:1.3.3') {
         exclude group: 'com.android.support', module: 'appcompat-v7'
     }
@@ -385,7 +385,8 @@ dependencies {
 //        exclude group: 'com.android.support', module: 'recyclerview-v7'
 //    }
 //    implementation "com.android.support:exifinterface:$supportLibVersion"
-    implementation project(':RxGalleryFinal')
+//    implementation project(':RxGalleryFinal')
+    implementation "com.android.support:exifinterface:$supportLibVersion"
     implementation ('com.qiniu:qiniu-android-sdk:7.3.13') {
         exclude group: 'com.squareup.okhttp3', module: 'okhttp'
     }

+ 12 - 2
app/src/main/AndroidManifest.xml

@@ -448,6 +448,16 @@
             android:screenOrientation="user"
             android:theme="@style/AppTheme.noTitleBar" />
         <activity
+            android:name="com.sheep.gamegroup.view.activity.ActCutVideo"
+            android:configChanges="orientation|keyboardHidden|navigation|screenSize"
+            android:screenOrientation="portrait"
+            android:theme="@style/AppTheme.noTitleBar" />
+        <activity
+            android:name="com.sheep.gamegroup.view.activity.ActEditVideo"
+            android:configChanges="orientation|keyboardHidden|navigation|screenSize"
+            android:screenOrientation="user"
+            android:theme="@style/AppTheme.noTitleBar" />
+        <activity
             android:name="com.sheep.gamegroup.view.activity.ActInputAndPickerImg"
             android:theme="@style/AppTheme.translucent" />
         <activity
@@ -882,8 +892,8 @@
 
         <!-- 小绵羊3.4.5 选择图片与视频  -->
         <activity
-            android:name="cn.finalteam.rxgalleryfinal.ui.activity.MediaActivity"
-            android:exported="true" />
+            android:name="com.sheep.gamegroup.find.activity.ActMediaChoose"
+            android:screenOrientation="portrait" />
     </application>
 
 </manifest>

+ 14 - 0
app/src/main/java/cn/finalteam/rxgalleryfinal/api/CameraCallBack.java

@@ -0,0 +1,14 @@
+package cn.finalteam.rxgalleryfinal.api;
+
+/**
+ * Created by realicing on 2018/11/30.
+ * realicing@sina.com
+ */
+public interface CameraCallBack {
+
+    void onGetImageSuccess(String cropFilePath);
+
+    void onGetVideoSuccess();
+
+    void onCropImageSuccess();
+}

+ 69 - 0
app/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());
+    }
+}

+ 283 - 0
app/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 + '\'' +
+                '}';
+    }
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1060 - 0
app/src/main/java/cn/finalteam/rxgalleryfinal/utils/FilenameUtils.java


+ 506 - 0
app/src/main/java/cn/finalteam/rxgalleryfinal/utils/MediaUtils.java

@@ -0,0 +1,506 @@
+package cn.finalteam.rxgalleryfinal.utils;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+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 android.util.Log;
+import android.widget.Toast;
+
+import com.sheep.jiuyan.samllsheep.SheepApp;
+import com.sheep.jiuyan.samllsheep.utils.ClassFileHelper;
+import com.yalantis.ucrop.UCrop;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import cn.finalteam.rxgalleryfinal.api.CameraCallBack;
+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);
+                    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);
+        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);
+        }
+        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("所有图片");
+            uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+        } else {
+            allMediaBucket.setBucketName("所有视频");
+            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;
+    }
+
+    public static String openCamera(Activity activity, boolean isImage) {
+        Intent captureIntent = isImage ? new Intent(MediaStore.ACTION_IMAGE_CAPTURE) : new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
+        if (captureIntent.resolveActivity(activity.getPackageManager()) == null) {
+            Toast.makeText(SheepApp.getInstance(), "相机不可用", Toast.LENGTH_SHORT).show();
+            return null;
+        }
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
+        String filename = String.format(isImage ? IMAGE_STORE_FILE_NAME : VIDEO_STORE_FILE_NAME, dateFormat.format(new Date()));
+        File fileImagePath = new File(ClassFileHelper.DIR, filename);
+        String 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 = SheepApp.getInstance().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);
+        activity.startActivityForResult(captureIntent, isImage ? TAKE_IMAGE_REQUEST_CODE : TAKE_VIDEO_REQUEST_CODE);
+        return mImagePath;
+    }
+
+    public static void onActivityResult(Activity activity, int requestCode, String path, CameraCallBack cameraCallBack) {
+        Log.i(MediaUtils.class.getSimpleName(), "onActivityResult: requestCode=" + requestCode);
+        switch (requestCode) {
+            case TAKE_IMAGE_REQUEST_CODE:
+                if (path == null) {
+                    Log.i(MediaUtils.class.getSimpleName(), "拍照成功");
+                    cameraCallBack.onGetImageSuccess(null);
+                    break;
+                }
+                String cropFilePath = path.substring(0, path.lastIndexOf(".")) + "temp.png";
+                Log.i(MediaUtils.class.getSimpleName(), "拍照成功: " + cropFilePath);
+                cameraCallBack.onGetImageSuccess(cropFilePath);
+                UCrop.of(Uri.parse(path), Uri.parse(cropFilePath))
+                        .start(activity);
+                break;
+            case TAKE_VIDEO_REQUEST_CODE:
+                Log.i(MediaUtils.class.getSimpleName(), "摄像成功");
+                cameraCallBack.onGetVideoSuccess();
+                break;
+            case UCrop.REQUEST_CROP:
+                Log.i(MediaUtils.class.getSimpleName(), "裁剪成功");
+                cameraCallBack.onCropImageSuccess();
+                break;
+        }
+    }
+
+    public static final String IMAGE_STORE_FILE_NAME = "IMG_%s.jpg";
+    public static final String VIDEO_STORE_FILE_NAME = "IMG_%s.mp4";
+    public static final int TAKE_IMAGE_REQUEST_CODE = 1001;
+    public static final int TAKE_VIDEO_REQUEST_CODE = 1002;
+}

+ 170 - 0
app/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;
+    }
+}

+ 9 - 0
app/src/main/java/com/kfzs/duanduan/utils/NumberFormatUtils.java

@@ -92,6 +92,15 @@ public class NumberFormatUtils {
     }
 
     /**
+     * 最多保留一位小数
+     * @param bonus
+     * @return
+     */
+    public static String retainMost1(double bonus) {
+        DecimalFormat df = new DecimalFormat("#.#");
+        return df.format(bonus);
+    }
+    /**
      * 最多保留两位小数
      * @param bonus
      * @return

+ 7 - 4
app/src/main/java/com/kfzs/duanduan/view/DialogStorageLow.java

@@ -1,12 +1,15 @@
 package com.kfzs.duanduan.view;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.provider.Settings;
 import android.support.v7.app.AlertDialog;
+import android.text.TextUtils;
 import android.view.View;
 
 import com.sheep.gamegroup.model.entity.DialogConfig;
+import com.sheep.gamegroup.util.ActivityManager;
 import com.sheep.gamegroup.util.ViewUtil;
 import com.sheep.jiuyan.samllsheep.SheepApp;
 
@@ -17,15 +20,15 @@ import com.sheep.jiuyan.samllsheep.SheepApp;
 
 public class DialogStorageLow {
 
-    public static AlertDialog showDialog(Context context) {
-        final Context mContext = context == null ? SheepApp.getInstance() : context;
-        return ViewUtil.showMsgDialog(mContext, new DialogConfig().setTitle("温馨提示").setMsgMore("存储空间不足\n请释放一些储存空间,并重新尝试安装!")
+    public static AlertDialog showDialog(String msgMore) {
+        final Activity activity = ActivityManager.getInstance().currentActivity();
+        return ViewUtil.showMsgDialog(activity, new DialogConfig().setTitle("温馨提示").setMsgMore("存储空间不足\n请释放一些储存空间,并重新尝试安装!" + (TextUtils.isEmpty(msgMore) ? "" : ("\n"+msgMore)))
             .setBtnRightText("取消").setBtnLeftText("管理应用").setBtnLeftOnClickListener(new View.OnClickListener() {
                     @Override
                     public void onClick(View view) {
                         Intent intent = new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
                         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                        mContext.startActivity(intent);
+                        activity.startActivity(intent);
                     }
                 }));
     }

+ 75 - 0
app/src/main/java/com/sheep/gamegroup/absBase/AbsApiRefresh.java

@@ -0,0 +1,75 @@
+package com.sheep.gamegroup.absBase;
+
+import com.alibaba.fastjson.JSONObject;
+import com.sheep.gamegroup.util.ListUtil;
+import com.sheep.gamegroup.util.LogUtil;
+import com.sheep.gamegroup.view.fragment.BaseListFragment6;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.sheep.gamegroup.util.DataUtil.PER_PAGE;
+
+/**
+ * Created by realicing on 2018/11/30.
+ * realicing@sina.com
+ */
+public abstract class AbsApiRefresh<T> implements IApiRefresh {
+
+    public AbsApiRefresh(BaseListFragment6 baseListFragment6) {
+        this.baseListFragment6 = baseListFragment6;
+    }
+
+    protected List<T> list = new ArrayList<>();
+    private BaseListFragment6 baseListFragment6;
+    protected int page = 1;
+    protected int per_page = PER_PAGE;
+    private boolean loadMore = false;
+
+    @Override
+    public void setLoadMore(boolean loadMore) {
+        this.loadMore = loadMore;
+    }
+
+    //还有数据没有获取
+    protected boolean hasMore() {
+        return ListUtil.size(list) >= per_page * page;
+    }
+
+    protected void loadList(List<T> newList) {
+        LogUtil.println(AbsApiRefresh.class.getSimpleName(), "loadList", JSONObject.toJSONString(newList));
+        list.addAll(newList);
+        notifyDataSetChanged();
+    }
+
+    private void notifyDataSetChanged() {
+        if (baseListFragment6 != null)
+            baseListFragment6.notifyDataSetChanged(page, hasMore());
+    }
+
+    @Override
+    public void clear() {
+        list.clear();
+        page = 1;
+    }
+
+    @Override
+    public void loadMoreData() {
+        if (!loadMore) {
+            loadMore = true;
+            if (hasMore()) {
+                page += 1;
+                LogUtil.println(AbsApiRefresh.class.getSimpleName(), "loadMoreData", "hasMore", page);
+                initData();
+            } else {
+                LogUtil.println(AbsApiRefresh.class.getSimpleName(), "loadMoreData", "noMore", page);
+                notifyDataSetChanged();
+            }
+        }
+    }
+
+    @Override
+    public List<T> getList() {
+        return list;
+    }
+}

+ 4 - 1
app/src/main/java/com/sheep/gamegroup/absBase/AbsObserver.java

@@ -1,5 +1,8 @@
 package com.sheep.gamegroup.absBase;
 
+import com.sheep.gamegroup.util.TestUtil;
+import com.sheep.jiuyan.samllsheep.utils.G;
+
 import io.reactivex.Observer;
 import io.reactivex.disposables.Disposable;
 
@@ -20,7 +23,7 @@ public abstract class AbsObserver<T> implements Observer<T> {
 
     @Override
     public void onError(Throwable e) {
-
+        if(TestUtil.isDev()) G.showToast(e.getMessage());
     }
 
     @Override

+ 9 - 0
app/src/main/java/com/sheep/gamegroup/absBase/BaseActivity.java

@@ -7,6 +7,8 @@ import com.sheep.gamegroup.util.ActionUtil;
 import com.sheep.gamegroup.view.dialog.DialogLoading;
 import com.trello.rxlifecycle2.components.support.RxAppCompatActivity;
 
+import org.greenrobot.eventbus.EventBus;
+
 import java.util.concurrent.TimeUnit;
 
 import butterknife.ButterKnife;
@@ -23,6 +25,9 @@ public abstract class BaseActivity extends RxAppCompatActivity {
 
     protected DialogLoading dialogLoading;
     public Unbinder unbinder;
+    protected boolean needRegisterEventBus(){
+        return false;
+    }
     protected boolean needButterKnife(){
         return true;
     }
@@ -32,6 +37,8 @@ public abstract class BaseActivity extends RxAppCompatActivity {
         setContentView(getLayoutId());
         if(needButterKnife())
             unbinder = ButterKnife.bind(this);
+        if(needRegisterEventBus())
+            EventBus.getDefault().register(this);
         initView();
         initListener();
         initData();
@@ -111,5 +118,7 @@ public abstract class BaseActivity extends RxAppCompatActivity {
         } catch (Exception e) {
             e.printStackTrace();
         }
+        if(needRegisterEventBus())
+            EventBus.getDefault().unregister(this);
     }
 }

+ 48 - 0
app/src/main/java/com/sheep/gamegroup/absBase/CheckErrorApiRefresh.java

@@ -0,0 +1,48 @@
+package com.sheep.gamegroup.absBase;
+
+import com.sheep.gamegroup.util.ListUtil;
+import com.sheep.gamegroup.view.fragment.BaseListFragment6;
+
+import java.util.List;
+
+/**
+ * Created by realicing on 2018/11/30.
+ * realicing@sina.com
+ * 可以排除无效数据
+ */
+public abstract class CheckErrorApiRefresh<T> extends AbsApiRefresh<T> {
+
+    public CheckErrorApiRefresh(BaseListFragment6 baseListFragment6) {
+        super(baseListFragment6);
+    }
+
+    private int errorCount = 0;//错误数据个数
+
+    @Override
+    public void clear() {
+        errorCount = 0;
+        super.clear();
+    }
+
+    @Override
+    protected void loadList(List<T> newList) {
+        ListUtil.removeItem(newList, new ListUtil.CallBack<T, Boolean>() {
+            @Override
+            public Boolean call(T mediaBean) {
+                boolean result = isRightData(mediaBean);
+                if (!result) {//错误数据,删除,并记录个数
+                    errorCount++;
+                }
+                return !result;
+            }
+        });
+        super.loadList(newList);
+    }
+
+    protected abstract boolean isRightData(T item);
+
+    @Override
+    protected boolean hasMore() {//有错误数据,删除后翻页就不正常了,要加上
+        return ListUtil.size(list) + errorCount >= per_page * page;
+    }
+}

+ 19 - 0
app/src/main/java/com/sheep/gamegroup/absBase/IApiRefresh.java

@@ -0,0 +1,19 @@
+package com.sheep.gamegroup.absBase;
+
+import java.util.List;
+
+/**
+ * Created by realicing on 2018/11/30.
+ * realicing@sina.com
+ */
+public interface IApiRefresh<T> {
+    void setLoadMore(boolean loadMore);
+
+    void initData();
+
+    void clear();
+
+    void loadMoreData();
+
+    List<T> getList();
+}

+ 22 - 9
app/src/main/java/com/sheep/gamegroup/absBase/ApiRefresh.java

@@ -18,8 +18,8 @@ import io.reactivex.schedulers.Schedulers;
  * Created by realicing on 2018/11/28.
  * realicing@sina.com
  */
-public abstract class ApiRefresh<T> {
-    public ApiRefresh(BaseListFragment6<?> baseListFragment6) {
+public abstract class NetApiRefresh<T> implements IApiRefresh {
+    public NetApiRefresh(BaseListFragment6<?> baseListFragment6) {
         this.baseListFragment6 = baseListFragment6;
     }
 
@@ -49,22 +49,27 @@ public abstract class ApiRefresh<T> {
         return lastMessage;
     }
 
-    public boolean loadMoreData() {
+    public void loadMoreData() {
         if (!loadMore) {
             loadMore = true;
-            if (ListUtil.size(list) >= per_page * page) {
+            if (hasMore()) {
                 page += 1;
                 initData();
             } else {
                 if (baseListFragment6 != null)
-                    baseListFragment6.setNoMore(true);
+                    baseListFragment6.notifyDataSetChanged(page, hasMore());
             }
-            return false;
-        } else {
-            return true;
         }
     }
 
+    //还有数据没有获取
+    protected boolean hasMore() {
+        if(lastMessage != null && lastMessage.getTotal() > 0){
+            return lastMessage.getTotal() > ListUtil.size(list);
+        }
+        return ListUtil.size(list) >= per_page * page;
+    }
+
     public void clear() {
         lastMessage = null;
         list.clear();
@@ -108,7 +113,7 @@ public abstract class ApiRefresh<T> {
 
     private void notifyDataSetChanged() {
         if (baseListFragment6 != null)
-            baseListFragment6.notifyDataSetChanged();
+            baseListFragment6.notifyDataSetChanged(page, hasMore());
     }
 
     protected List<T> newList;
@@ -122,4 +127,12 @@ public abstract class ApiRefresh<T> {
     public void setLoadMore(boolean loadMore) {
         this.loadMore = loadMore;
     }
+
+    public boolean hasPosition(int position){
+        return ListUtil.hasIndex(list, position);
+    }
+
+    public T getItem(int position){
+        return ListUtil.getItem(list, position);
+    }
 }

+ 119 - 0
app/src/main/java/com/sheep/gamegroup/find/activity/ActMediaChoose.java

@@ -0,0 +1,119 @@
+package com.sheep.gamegroup.find.activity;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.support.annotation.Nullable;
+import android.support.design.widget.TabLayout;
+import android.support.v4.view.ViewPager;
+import android.view.View;
+
+import com.sheep.gamegroup.absBase.BaseActivity;
+import com.sheep.gamegroup.absBase.IRefresh;
+import com.sheep.gamegroup.find.fragment.FgtMediaPickerImg;
+import com.sheep.gamegroup.find.fragment.FgtMediaPickerVideo;
+import com.sheep.gamegroup.util.CommonUtil;
+import com.sheep.gamegroup.util.DataUtil;
+import com.sheep.gamegroup.util.TestUtil;
+import com.sheep.gamegroup.util.ViewUtil;
+import com.sheep.gamegroup.view.adapter.TitleFragmentListAdapter;
+import com.sheep.jiuyan.samllsheep.R;
+import com.sheep.jiuyan.samllsheep.utils.TitleBarUtils;
+
+import butterknife.BindView;
+import cn.finalteam.rxgalleryfinal.api.CameraCallBack;
+import cn.finalteam.rxgalleryfinal.utils.MediaUtils;
+
+import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;
+import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_NONE;
+import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO;
+
+/**
+ * Created by realicing on 2018/11/26.
+ * realicing@sina.com
+ * 小绵羊3.4.5新增--发布发现内容--选择视频和图片
+ */
+public class ActMediaChoose extends BaseActivity {
+
+    @Override
+    protected int getLayoutId() {
+        return R.layout.act_media_choose;
+    }
+
+    @BindView(R.id.indicator)
+    TabLayout indicator;
+    @BindView(R.id.below_tab_line)
+    View below_tab_line;
+    @BindView(R.id.viewPager)
+    ViewPager viewPager;
+
+    private TitleFragmentListAdapter mAdapter;
+    @Override
+    public void initView() {
+        TitleBarUtils.getInstance().setTitle(this, "选择媒体")
+                .setTitleFinish(this).setRightImgBotton(this, R.mipmap.ic_open_camera, new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+//                Jump2View.getInstance().goActEditVideo(video);
+                boolean isVideo = mAdapter.getItem(viewPager.getCurrentItem()) instanceof FgtMediaPickerVideo;
+                MediaUtils.openCamera(ActMediaChoose.this, !isVideo);
+            }
+        });
+
+        mAdapter = new TitleFragmentListAdapter(getSupportFragmentManager());
+        mAdapter.add(new FgtMediaPickerVideo(), "视频");
+        if(TestUtil.isDev()) {
+            mAdapter.add(new FgtMediaPickerImg(), "图片");
+        }
+        viewPager.setAdapter(mAdapter);
+        indicator.setupWithViewPager(viewPager);
+        viewPager.setCurrentItem(0);
+        viewPager.setOffscreenPageLimit(mAdapter.getCount());
+        CommonUtil.getInstance().reflex(indicator, this);
+        if(mAdapter.getCount() < 2){
+            ViewUtil.setVisibility(indicator, false);
+            ViewUtil.setVisibility(below_tab_line, false);
+        }
+//        Integer type = DataUtil.getObject(getIntent(), Integer.class);
+//        if(type != null){
+//            switch (type){
+//                case MEDIA_TYPE_IMAGE:
+//                    break;
+//                case MEDIA_TYPE_VIDEO:
+//                    break;
+//                case MEDIA_TYPE_NONE:
+//                default:
+//                    break;
+//            }
+//        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if(resultCode == Activity.RESULT_OK){
+            MediaUtils.onActivityResult(this, requestCode, null, new CameraCallBack() {
+                @Override
+                public void onGetImageSuccess(String cropFilePath) {
+                    if(mAdapter.getItem(1) instanceof IRefresh){
+                        ((IRefresh) mAdapter.getItem(1)).refreshData();
+                    }
+                }
+
+                @Override
+                public void onGetVideoSuccess() {
+                    if(mAdapter.getItem(0) instanceof IRefresh){
+                        ((IRefresh) mAdapter.getItem(0)).refreshData();
+                    }
+                }
+
+                @Override
+                public void onCropImageSuccess() {
+                    if(mAdapter.getItem(1) instanceof IRefresh){
+                        ((IRefresh) mAdapter.getItem(1)).refreshData();
+                    }
+                }
+            });
+        }
+    }
+}

+ 109 - 0
app/src/main/java/com/sheep/gamegroup/find/fragment/FgtMediaPickerImg.java

@@ -0,0 +1,109 @@
+package com.sheep.gamegroup.find.fragment;
+
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.StaggeredGridLayoutManager;
+import android.util.LongSparseArray;
+import android.widget.ImageView;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.sheep.gamegroup.absBase.AbsApiRefresh;
+import com.sheep.gamegroup.absBase.AbsObserver;
+import com.sheep.gamegroup.absBase.CheckErrorApiRefresh;
+import com.sheep.gamegroup.absBase.IApiRefresh;
+import com.sheep.gamegroup.model.entity.Lp;
+import com.sheep.gamegroup.util.ViewUtil;
+import com.sheep.gamegroup.util.viewHelper.LayoutParamsUtil;
+import com.sheep.gamegroup.view.fragment.BaseListFragment6;
+import com.sheep.jiuyan.samllsheep.R;
+import com.sheep.jiuyan.samllsheep.SheepApp;
+import com.sheep.jiuyan.samllsheep.utils.G;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+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.schedulers.Schedulers;
+
+/**
+ * Created by realicing on 2018/11/27.
+ * realicing@sina.com
+ * 小绵羊3.4.5新增--发布发现内容--选择图片
+ */
+public class FgtMediaPickerImg extends BaseListFragment6<MediaBean> {
+
+    @Override
+    public int getLayoutId() {
+        return R.layout.common_srl_rv;
+    }
+
+    @Override
+    public void initView() {
+        LayoutParamsUtil.resetLayoutParams(recyclerView, new Lp(G.DENSITY).setTbMargin(14).setLrMargin(6));
+    }
+
+    protected LongSparseArray<MediaBean> sparseArray = new LongSparseArray<>();
+    protected LongSparseArray<String> textSparseArray = new LongSparseArray<>();
+
+    private int width = (G.WIDTH - G.getRealPix(48)) / 2;
+
+    @Override
+    protected RecyclerView.LayoutManager getLayoutManager() {
+        return new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
+    }
+
+    @Override
+    protected BaseQuickAdapter<MediaBean, BaseViewHolder> getAdapter() {
+        return new BaseQuickAdapter<MediaBean, BaseViewHolder>(R.layout.item_media_picker, apiRefresh.getList()) {
+
+            @Override
+            protected void convert(BaseViewHolder helper, MediaBean item) {
+                boolean isChoose = sparseArray.get(item.getId()) != null;
+                helper.setText(R.id.item_media_picker_tv, isChoose ? textSparseArray.get(item.getId()) : "√");
+                helper.setBackgroundRes(R.id.item_media_picker_tv, isChoose ? R.drawable.shape_oval_solid_10_main : R.drawable.shape_oval_solid_10_f5);
+                helper.setTextColor(R.id.item_media_picker_tv, SheepApp.getInstance().getResources().getColor(isChoose ? R.color.white : R.color.gray_CBCBCB));
+                ImageView item_media_picker_iv = helper.getView(R.id.item_media_picker_iv);
+                ViewUtil.setViewWH(item_media_picker_iv, width, item.getHeight() * 1.0f / item.getWidth());
+                ViewUtil.setImagePath(item_media_picker_iv, item.getOriginalPath(), G.getRealPix(5));
+            }
+        };
+    }
+
+    private AbsApiRefresh<MediaBean> apiRefresh;
+
+    @Override
+    protected void addApiRefresh(List<IApiRefresh> apiRefreshList) {
+        apiRefresh = new CheckErrorApiRefresh<MediaBean>(this) {
+            @Override
+            protected boolean isRightData(MediaBean item) {
+                return item != null && item.getWidth() > 0 && item.getHeight() > 0;
+            }
+            @Override
+            public void initData() {
+                Observable.create(new ObservableOnSubscribe<List<MediaBean>>() {
+                    @Override
+                    public void subscribe(ObservableEmitter<List<MediaBean>> emitter) {
+                        emitter.onNext(initNewList(page, per_page));
+                    }
+                })
+                        .subscribeOn(Schedulers.io())
+                        .observeOn(AndroidSchedulers.mainThread()).
+                        subscribe(new AbsObserver<List<MediaBean>>() {
+                            @Override
+                            public void onNext(List<MediaBean> newList) {
+                                loadList(newList);
+                            }
+                        });
+            }
+        };
+        apiRefreshList.add(apiRefresh);
+    }
+
+    protected List<MediaBean> initNewList(int page, int per_page) {
+        return MediaUtils.getMediaWithImageList(SheepApp.getInstance(), page, per_page);
+    }
+}

+ 126 - 0
app/src/main/java/com/sheep/gamegroup/find/fragment/FgtMediaPickerVideo.java

@@ -0,0 +1,126 @@
+package com.sheep.gamegroup.find.fragment;
+
+
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.StaggeredGridLayoutManager;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.sheep.gamegroup.absBase.AbsApiRefresh;
+import com.sheep.gamegroup.absBase.AbsObserver;
+import com.sheep.gamegroup.absBase.CheckErrorApiRefresh;
+import com.sheep.gamegroup.absBase.IApiRefresh;
+import com.sheep.gamegroup.model.entity.Lp;
+import com.sheep.gamegroup.model.entity.Video;
+import com.sheep.gamegroup.util.Jump2View;
+import com.sheep.gamegroup.util.ListUtil;
+import com.sheep.gamegroup.util.TimeUtil;
+import com.sheep.gamegroup.util.ViewUtil;
+import com.sheep.gamegroup.util.viewHelper.LayoutParamsUtil;
+import com.sheep.gamegroup.view.fragment.BaseListFragment6;
+import com.sheep.jiuyan.samllsheep.R;
+import com.sheep.jiuyan.samllsheep.SheepApp;
+import com.sheep.jiuyan.samllsheep.utils.G;
+
+import java.util.List;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+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.schedulers.Schedulers;
+
+/**
+ * Created by realicing on 2018/11/27.
+ * realicing@sina.com
+ * 小绵羊3.4.5新增--发布发现内容--选择视频
+ */
+public class FgtMediaPickerVideo extends BaseListFragment6<MediaBean> {
+
+    @Override
+    public int getLayoutId() {
+        return R.layout.common_srl_rv;
+    }
+
+    @Override
+    public void initView() {
+        LayoutParamsUtil.resetLayoutParams(recyclerView, new Lp(G.DENSITY).setTbMargin(14).setLrMargin(6));
+    }
+
+    private int width = (G.WIDTH - G.getRealPix(48)) / 2;
+
+    @Override
+    protected RecyclerView.LayoutManager getLayoutManager() {
+        return new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
+    }
+
+    @Override
+    protected BaseQuickAdapter<MediaBean, BaseViewHolder> getAdapter() {
+        return new BaseQuickAdapter<MediaBean, BaseViewHolder>(R.layout.item_video, apiRefresh.getList()) {
+
+            @Override
+            protected void convert(BaseViewHolder helper, MediaBean item) {
+                ImageView item_video_cover = helper.getView(R.id.item_video_cover);
+                TextView item_video_time_tv = helper.itemView.findViewById(R.id.item_video_time_tv);
+                ViewUtil.setViewWH(item_video_cover, width, item.getHeight() * 1.0f / item.getWidth());
+                ViewUtil.setImagePath(item_video_cover, item.getOriginalPath(), G.getRealPix(5));
+                ViewUtil.setText(item_video_time_tv, TimeUtil.getDurationText(item.getDuration() / 1000));
+            }
+        };
+    }
+
+    @Override
+    public void initListener() {
+        super.initListener();
+        baseQuickAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
+            @Override
+            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
+                MediaBean item = ListUtil.getItem(apiRefresh.getList(), position);
+                if (item != null)
+                    Jump2View.getInstance().goActCutVideo(Video.from(item));
+            }
+        });
+
+    }
+
+    private AbsApiRefresh<MediaBean> apiRefresh;
+
+    @Override
+    protected void addApiRefresh(List<IApiRefresh> apiRefreshList) {
+        apiRefresh = new CheckErrorApiRefresh<MediaBean>(this) {
+            @Override
+            protected boolean isRightData(MediaBean item) {
+                return item != null && item.getWidth() > 0 && item.getHeight() > 0;
+            }
+
+            @Override
+            public void initData() {
+                Observable.create(new ObservableOnSubscribe<List<MediaBean>>() {
+                    @Override
+                    public void subscribe(ObservableEmitter<List<MediaBean>> emitter) {
+                        emitter.onNext(initNewList(page, per_page));
+                    }
+                })
+                        .subscribeOn(Schedulers.io())
+                        .observeOn(AndroidSchedulers.mainThread()).
+                        subscribe(new AbsObserver<List<MediaBean>>() {
+                            @Override
+                            public void onNext(List<MediaBean> newList) {
+                                loadList(newList);
+                            }
+                        });
+            }
+
+        };
+        apiRefreshList.add(apiRefresh);
+    }
+
+    protected List<MediaBean> initNewList(int page, int per_page) {
+        return MediaUtils.getMediaWithVideoList(SheepApp.getInstance(), page, per_page);
+    }
+}

+ 15 - 0
app/src/main/java/com/sheep/gamegroup/greendao/DDProviderHelper.java

@@ -197,6 +197,21 @@ public class DDProviderHelper {
     /**
      * 更新任务状态
      *
+     * @param downloadTaskId
+     * @param downloadUrl
+     */
+    public DownLoadInfo updateDownload(int downloadTaskId, String downloadUrl, int state) {
+        DownLoadInfo downloadTask = getDownloadTask(downloadUrl);
+        if (downloadTask != null) {
+            downloadTask.setMDownloadTaskId(downloadTaskId);
+            downloadTask.setMStatus(state);
+            updateDownload(downloadTask);
+        }
+        return downloadTask;
+    }
+    /**
+     * 更新任务状态
+     *
      * @param downloadedSize
      * @param downloadTaskId
      * @param downloadUrl

+ 2 - 2
app/src/main/java/com/sheep/gamegroup/greendao/download/DaoMaster.java

@@ -14,10 +14,10 @@ import org.greenrobot.greendao.identityscope.IdentityScopeType;
 
 // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
 /**
- * Master of DAO (schema version 14): knows all DAOs.
+ * Master of DAO (schema version 15): knows all DAOs.
  */
 public class DaoMaster extends AbstractDaoMaster {
-    public static final int SCHEMA_VERSION = 14;
+    public static final int SCHEMA_VERSION = 15;
 
     /** Creates underlying database table using DAOs. */
     public static void createAllTables(Database db, boolean ifNotExists) {

+ 16 - 2
app/src/main/java/com/sheep/gamegroup/greendao/download/DownLoadInfo.java

@@ -125,6 +125,11 @@ public class DownLoadInfo implements Serializable{
      */
     @Property(nameInDb = "average_speed")
     private String averageSpeed;
+    /**
+     * 下载速度
+     */
+    @Property(nameInDb = "real_cause_msg")
+    private String realCauseMsg;
 
     public int getWhereFrom() {
         return whereFrom;
@@ -262,12 +267,20 @@ public class DownLoadInfo implements Serializable{
         this.averageSpeed = averageSpeed;
     }
 
-    @Generated(hash = 601502121)
+    public String getRealCauseMsg() {
+        return this.realCauseMsg;
+    }
+
+    public void setRealCauseMsg(String realCauseMsg) {
+        this.realCauseMsg = realCauseMsg;
+    }
+
+    @Generated(hash = 74805486)
     public DownLoadInfo(Long mId, Long mCreateTime, Integer mDownloadTaskId,
             String mGameName, String mIconUrl, Integer mPercent, @NotNull Integer mGameID,
             Double mDownloadedSize, Double mTotalSize, Integer mStatus, String mDownloadUrl,
             String mChannelDownloadUrl, String mPackageName, Integer mVersionCode,
-            String mApkPath, int whereFrom, String averageSpeed) {
+            String mApkPath, int whereFrom, String averageSpeed, String realCauseMsg) {
         this.mId = mId;
         this.mCreateTime = mCreateTime;
         this.mDownloadTaskId = mDownloadTaskId;
@@ -285,6 +298,7 @@ public class DownLoadInfo implements Serializable{
         this.mApkPath = mApkPath;
         this.whereFrom = whereFrom;
         this.averageSpeed = averageSpeed;
+        this.realCauseMsg = realCauseMsg;
     }
 
     @Generated(hash = 1743687477)

+ 16 - 2
app/src/main/java/com/sheep/gamegroup/greendao/download/DownLoadInfoDao.java

@@ -39,6 +39,7 @@ public class DownLoadInfoDao extends AbstractDao<DownLoadInfo, Long> {
         public final static Property MApkPath = new Property(14, String.class, "mApkPath", false, "apk_path");
         public final static Property WhereFrom = new Property(15, int.class, "whereFrom", false, "where_from");
         public final static Property AverageSpeed = new Property(16, String.class, "averageSpeed", false, "average_speed");
+        public final static Property RealCauseMsg = new Property(17, String.class, "realCauseMsg", false, "real_cause_msg");
     }
 
 
@@ -70,7 +71,8 @@ public class DownLoadInfoDao extends AbstractDao<DownLoadInfo, Long> {
                 "\"version_code\" INTEGER," + // 13: mVersionCode
                 "\"apk_path\" TEXT," + // 14: mApkPath
                 "\"where_from\" INTEGER NOT NULL ," + // 15: whereFrom
-                "\"average_speed\" TEXT);"); // 16: averageSpeed
+                "\"average_speed\" TEXT," + // 16: averageSpeed
+                "\"real_cause_msg\" TEXT);"); // 17: realCauseMsg
     }
 
     /** Drops the underlying database table. */
@@ -159,6 +161,11 @@ public class DownLoadInfoDao extends AbstractDao<DownLoadInfo, Long> {
         if (averageSpeed != null) {
             stmt.bindString(17, averageSpeed);
         }
+ 
+        String realCauseMsg = entity.getRealCauseMsg();
+        if (realCauseMsg != null) {
+            stmt.bindString(18, realCauseMsg);
+        }
     }
 
     @Override
@@ -241,6 +248,11 @@ public class DownLoadInfoDao extends AbstractDao<DownLoadInfo, Long> {
         if (averageSpeed != null) {
             stmt.bindString(17, averageSpeed);
         }
+ 
+        String realCauseMsg = entity.getRealCauseMsg();
+        if (realCauseMsg != null) {
+            stmt.bindString(18, realCauseMsg);
+        }
     }
 
     @Override
@@ -267,7 +279,8 @@ public class DownLoadInfoDao extends AbstractDao<DownLoadInfo, Long> {
             cursor.isNull(offset + 13) ? null : cursor.getInt(offset + 13), // mVersionCode
             cursor.isNull(offset + 14) ? null : cursor.getString(offset + 14), // mApkPath
             cursor.getInt(offset + 15), // whereFrom
-            cursor.isNull(offset + 16) ? null : cursor.getString(offset + 16) // averageSpeed
+            cursor.isNull(offset + 16) ? null : cursor.getString(offset + 16), // averageSpeed
+            cursor.isNull(offset + 17) ? null : cursor.getString(offset + 17) // realCauseMsg
         );
         return entity;
     }
@@ -291,6 +304,7 @@ public class DownLoadInfoDao extends AbstractDao<DownLoadInfo, Long> {
         entity.setMApkPath(cursor.isNull(offset + 14) ? null : cursor.getString(offset + 14));
         entity.setWhereFrom(cursor.getInt(offset + 15));
         entity.setAverageSpeed(cursor.isNull(offset + 16) ? null : cursor.getString(offset + 16));
+        entity.setRealCauseMsg(cursor.isNull(offset + 17) ? null : cursor.getString(offset + 17));
      }
     
     @Override

+ 1 - 1
app/src/main/java/com/sheep/gamegroup/model/api/ApiService.java

@@ -1482,7 +1482,7 @@ public interface ApiService {
 
     /**
      * 小绵羊3.4.5新增 -- 用户视频发布
-     * UserAddressReqUserAddressReq
+     * CreateUserVideoReq
      */
     @POST("app/video")
     Observable<BaseMessage> postVideo(@Body JSONObject jsonObject);

+ 3 - 3
app/src/main/java/com/sheep/gamegroup/model/entity/DiscoveryVideo.java

@@ -51,7 +51,7 @@ public class DiscoveryVideo implements Serializable {
 
     private int create_time;
 
-    private int duration;
+    private long duration;
 
     private int id;
 
@@ -105,10 +105,10 @@ public class DiscoveryVideo implements Serializable {
     public int getCreate_time(){
         return this.create_time;
     }
-    public void setDuration(int duration){
+    public void setDuration(long duration){
         this.duration = duration;
     }
-    public int getDuration(){
+    public long getDuration(){
         return this.duration;
     }
     public void setId(int id){

+ 36 - 0
app/src/main/java/com/sheep/gamegroup/model/entity/Lp.java

@@ -80,4 +80,40 @@ public class Lp {
         this.height = (int) (height * per);
         return this;
     }
+
+    public Lp addLeftMargin(float mx) {
+        int addMargin = (int) (per * mx);
+        switch (leftMargin) {
+            case NONE:
+                leftMargin = addMargin;
+                break;
+            default:
+                leftMargin += addMargin;
+                break;
+        }
+        return this;
+    }
+
+    public Lp setLrMargin(int margin) {
+        margin = (int) (margin * per);
+        this.leftMargin = margin;
+        this.rightMargin = margin;
+        return this;
+    }
+
+    public Lp setTbMargin(int margin) {
+        margin = (int) (margin * per);
+        this.topMargin = margin;
+        this.bottomMargin = margin;
+        return this;
+    }
+
+    public Lp setMargin(int margin) {
+        margin = (int) (margin * per);
+        this.topMargin = margin;
+        this.bottomMargin = margin;
+        this.leftMargin = margin;
+        this.rightMargin = margin;
+        return this;
+    }
 }

+ 70 - 0
app/src/main/java/com/sheep/gamegroup/model/entity/Video.java

@@ -0,0 +1,70 @@
+package com.sheep.gamegroup.model.entity;
+
+import java.io.Serializable;
+
+import cn.finalteam.rxgalleryfinal.bean.MediaBean;
+
+/**
+ * Created by realicing on 2018/11/30.
+ * realicing@sina.com
+ * 视频数据
+ */
+public class Video implements Serializable {
+
+    private int width;//视频宽
+    private int height;//视频高
+    private long duration;//视频长度,单位毫秒
+    private String filePath;//文件对应地址
+    private int topicId;//选择的话题的id
+
+    public String getFilePath() {
+        return filePath;
+    }
+
+    public void setFilePath(String filePath) {
+        this.filePath = filePath;
+    }
+
+    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 long getDuration() {
+        return duration;
+    }
+
+    public void setDuration(long duration) {
+        this.duration = duration;
+    }
+
+    public int getTopicId() {
+        return topicId;
+    }
+
+    public void setTopicId(int topicId) {
+        this.topicId = topicId;
+    }
+
+
+    //通过MediaBean获取Video
+    public static Video from(MediaBean item) {
+        Video video = new Video();
+        video.setWidth(item.getWidth());
+        video.setHeight(item.getHeight());
+        video.setDuration(item.getDuration());
+        video.setFilePath(item.getOriginalPath());
+        return video;
+    }
+}

+ 25 - 4
app/src/main/java/com/sheep/gamegroup/util/FileUtil.java

@@ -1,5 +1,6 @@
 package com.sheep.gamegroup.util;
 
+import android.net.Uri;
 import android.text.TextUtils;
 
 import com.sheep.jiuyan.samllsheep.BuildConfig;
@@ -178,6 +179,7 @@ public class FileUtil {
         }
         return filename;
     }
+
     /*
      * Java文件操作 获取带扩展名的文件名
      * */
@@ -195,7 +197,8 @@ public class FileUtil {
 
     /**
      * 获取文件名,带扩展名
-     * @param filename 前提是fileName前面一定没有/
+     *
+     * @param filename  前提是fileName前面一定没有/
      * @param defaultEx 默认的扩展
      * @return
      */
@@ -212,9 +215,26 @@ public class FileUtil {
     }
 
 
-
-
-
+    /**
+     * 获取文件名称且不带扩展名
+     *
+     * @param filePath
+     * @return
+     */
+    public static String getFileNameNoExFromPath(String filePath) {
+        if ((filePath != null) && (filePath.length() > 0)) {
+            Uri uri = Uri.parse(filePath);
+            String lastPathSegment = uri.getLastPathSegment();
+            if (lastPathSegment != null) {
+                int dot = lastPathSegment.lastIndexOf('.');
+                if (dot > 0) {
+                    return lastPathSegment.substring(0, dot);
+                }
+                return lastPathSegment;
+            }
+        }
+        return filePath;
+    }
 
 
     /**
@@ -225,6 +245,7 @@ public class FileUtil {
     public static void deleteFiles(File pDir) {
         deleteFiles(pDir, true);
     }
+
     /**
      * 递归删除文件或文件夹。
      *

+ 35 - 1
app/src/main/java/com/sheep/gamegroup/util/Jump2View.java

@@ -16,6 +16,7 @@ import com.danikula.videocache.HttpProxyCacheServer;
 import com.sheep.gamegroup.absBase.BaseActivity;
 import com.sheep.gamegroup.absBase.IHomePageSearch;
 import com.sheep.gamegroup.absBase.IJumpWeb;
+import com.sheep.gamegroup.find.activity.ActMediaChoose;
 import com.sheep.gamegroup.greendao.download.DownLoadInfo;
 import com.sheep.gamegroup.model.entity.Advertising;
 import com.sheep.gamegroup.model.entity.Agreement;
@@ -28,7 +29,6 @@ import com.sheep.gamegroup.model.entity.Container;
 import com.sheep.gamegroup.model.entity.CreditCard;
 import com.sheep.gamegroup.model.entity.DialogConfig;
 import com.sheep.gamegroup.model.entity.DialogEntity;
-import com.sheep.gamegroup.model.entity.DiscoveryVideo;
 import com.sheep.gamegroup.model.entity.GameListTag;
 import com.sheep.gamegroup.model.entity.GameListType;
 import com.sheep.gamegroup.model.entity.HomeListEntity;
@@ -41,6 +41,7 @@ import com.sheep.gamegroup.model.entity.TaskAcceptedEty;
 import com.sheep.gamegroup.model.entity.TaskEty;
 import com.sheep.gamegroup.model.entity.UserEntity;
 import com.sheep.gamegroup.model.entity.VersionInfo;
+import com.sheep.gamegroup.model.entity.Video;
 import com.sheep.gamegroup.model.entity.WebviewEntity;
 import com.sheep.gamegroup.model.entity.XianWanEntity;
 import com.sheep.gamegroup.model.util.SheepSubscriber;
@@ -53,7 +54,9 @@ import com.sheep.gamegroup.view.activity.ActCoinCenter;
 import com.sheep.gamegroup.view.activity.ActCommentGameApp;
 import com.sheep.gamegroup.view.activity.ActCreditCardTaskList;
 import com.sheep.gamegroup.view.activity.ActCreditCardWeb;
+import com.sheep.gamegroup.view.activity.ActCutVideo;
 import com.sheep.gamegroup.view.activity.ActDownloadWelfareList;
+import com.sheep.gamegroup.view.activity.ActEditVideo;
 import com.sheep.gamegroup.view.activity.ActEntertainmentLuckDraw;
 import com.sheep.gamegroup.view.activity.ActEveryDayShare;
 import com.sheep.gamegroup.view.activity.ActExchangeCMCC;
@@ -2257,6 +2260,29 @@ public class Jump2View {
     }
 
     /**
+     * 剪切视频
+     *
+     * @param data 视频数据,包括地址与时长,宽高等
+     */
+    public void goActCutVideo(Video data) {
+        Activity activity = ActivityManager.getInstance().currentActivity();
+        Intent intent = new Intent(activity, ActCutVideo.class);
+        DataUtil.putObject(intent, data);
+        activity.startActivity(intent);
+    }
+    /**
+     * 编辑视频
+     *
+     * @param data 视频数据,包括地址与时长,宽高等
+     */
+    public void goActEditVideo(Video data) {
+        Activity activity = ActivityManager.getInstance().currentActivity();
+        Intent intent = new Intent(activity, ActEditVideo.class);
+        DataUtil.putObject(intent, data);
+        activity.startActivity(intent);
+    }
+
+    /**
      * 提交自定义字段
      *
      * @param activity
@@ -2354,4 +2380,12 @@ public class Jump2View {
         Intent intent = new Intent(activity, ActVideoComment.class);
         activity.startActivity(DataUtil.putObject(intent, video_id));
     }
+    /**
+     * 小绵羊3.4.5新增--跳转到选择视频与图片界面
+     */
+    public void gotoActMediaChoose(@ActPublishArticle.Type int type) {
+        Activity activity = ActivityManager.getInstance().currentActivity();
+        Intent intent = new Intent(activity, ActMediaChoose.class);
+        activity.startActivity(DataUtil.putObject(intent, type));
+    }
 }

+ 359 - 0
app/src/main/java/com/sheep/gamegroup/util/MediaHandleUtil.java

@@ -0,0 +1,359 @@
+package com.sheep.gamegroup.util;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.util.Log;
+
+import com.sheep.gamegroup.absBase.AbsObserver;
+import com.sheep.gamegroup.model.entity.Video;
+
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Created by realicing on 2018/11/30.
+ * realicing@sina.com
+ */
+public class MediaHandleUtil {
+    public static final String TAG = "MediaHandleUtil";
+    private String url;
+    private long clipPoint;
+    private long clipDuration;
+
+    public MediaHandleUtil(String url, long clipPoint, long clipDuration) {
+        this.url = url;
+        this.clipPoint = clipPoint;
+        this.clipDuration = clipDuration;
+    }
+
+    public void tryCutVideo(AbsObserver<Video> absObserver) {
+        Observable.create(new ObservableOnSubscribe<Video>() {
+            @Override
+            public void subscribe(ObservableEmitter<Video> emitter) throws Exception {
+                long time = System.currentTimeMillis();
+                Video video = cutVideo(url, clipPoint, clipDuration);
+                if (video == null) {
+                    emitter.onError(new Throwable("剪切失败"));
+                } else {
+                    emitter.onNext(video);
+                }
+                long curTime = System.currentTimeMillis();
+                LogUtil.println(TAG, time, curTime, curTime - time, (curTime - time) / 1000);
+            }
+        })
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(absObserver);
+    }
+
+    /**
+     * 获取可以剪切出的视频的地址
+     *
+     * @param url          源文本地址
+     * @param clipPoint    开始位置
+     * @param clipDuration 要剪切的长度
+     * @return 返回可以剪切出来的文件的地址
+     */
+    public static String getCutVideoFilePath(String url, long clipPoint, long clipDuration) {
+        if (clipPoint < 0) {
+            Log.e(TAG, "clipPoint is error! but reset clipPoint " + clipPoint + "0 ");
+            clipPoint = 0;
+        }
+        return String.format(Locale.CHINA, "%s_%d_%d.%s", url.substring(0, url.lastIndexOf(".")), clipPoint, clipDuration, FileUtil.getExtensionName(url));
+    }
+
+    /**
+     * 剪切视频
+     *
+     * @param url          源文本地址
+     * @param clipPoint    开始位置
+     * @param clipDuration 要剪切的长度
+     * @return 返回剪切出来的文件的地址
+     */
+    public static Video cutVideo(String url, long clipPoint, long clipDuration) {
+        Video video = new Video();
+        if (clipPoint < 0) {
+            Log.e(TAG, "clipPoint is error! but reset clipPoint " + clipPoint + "0 ");
+            clipPoint = 0;
+        }
+        LogUtil.println(TAG, url, clipPoint, clipDuration);
+        String outFilePath = getCutVideoFilePath(url, clipPoint, clipDuration);
+        video.setFilePath(outFilePath);
+        int videoTrackIndex = -1;
+        int audioTrackIndex = -1;
+        int videoMaxInputSize = 0;
+        int audioMaxInputSize = 0;
+        int sourceVTrack = 0;
+        int sourceATrack = 0;
+        long videoDuration, audioDuration;
+        //创建分离器
+        MediaExtractor mediaExtractor = new MediaExtractor();
+        MediaMuxer mediaMuxer = null;
+        MediaFormat mediaFormat;
+
+        try {
+            //设置文件路径
+            mediaExtractor.setDataSource(url);
+            //创建合成器
+            mediaMuxer = new MediaMuxer(outFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+            String mime;
+            //获取每个轨道的信息
+            for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
+                mediaFormat = mediaExtractor.getTrackFormat(i);
+                mime = mediaFormat.getString(MediaFormat.KEY_MIME);
+                if (mime.startsWith("video/")) {
+                    sourceVTrack = i;
+                    int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
+                    int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
+                    videoMaxInputSize = mediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
+                    videoDuration = mediaFormat.getLong(MediaFormat.KEY_DURATION);
+                    //检测剪辑点和剪辑时长是否正确
+                    if (clipPoint >= videoDuration) {
+                        Log.e(TAG, "clip point is error! but reset clipPoint " + clipPoint + " 0 ");
+                        clipPoint = 0;
+                    }
+                    if ((clipDuration + clipPoint) > videoDuration) {
+                        if (clipPoint == 0) {
+                            Log.e(TAG, "clip duration is error!"
+                                    + ";duration is " + videoDuration
+                                    + ";clipPoint is " + clipPoint
+                                    + ";clipDuration is " + clipDuration
+                                    + ";totalDuration is " + (clipDuration + clipPoint)
+                            );
+                            return null;
+                        }
+                        Log.e(TAG, "clip duration is error! but reset clipDuration " + clipDuration + "  " + (videoDuration - clipPoint));
+                        clipDuration = videoDuration - clipPoint;
+                    }
+                    Log.d(TAG, "width and height is " + width + " " + height
+                            + ";maxInputSize is " + videoMaxInputSize
+                            + ";duration is " + videoDuration
+                            + ";clipPoint is " + clipPoint
+                            + ";clipDuration is " + clipDuration
+                            + ";totalDuration is " + (clipDuration + clipPoint)
+                    );
+                    video.setWidth(width);
+                    video.setHeight(height);
+                    video.setDuration(clipDuration / 1000);//微秒转换为毫秒
+                    //向合成器添加视频轨
+                    videoTrackIndex = mediaMuxer.addTrack(mediaFormat);
+                } else if (mime.startsWith("audio/")) {
+                    sourceATrack = i;
+                    int sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+                    int channelCount = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+                    audioMaxInputSize = mediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
+                    audioDuration = mediaFormat.getLong(MediaFormat.KEY_DURATION);
+                    Log.d(TAG, "sampleRate is " + sampleRate
+                            + ";channelCount is " + channelCount
+                            + ";audioMaxInputSize is " + audioMaxInputSize
+                            + ";audioDuration is " + audioDuration
+                    );
+                    //添加音轨
+                    audioTrackIndex = mediaMuxer.addTrack(mediaFormat);
+                }
+                Log.d(TAG, "file mime is " + mime);
+            }
+            //分配缓冲
+            ByteBuffer inputBuffer = ByteBuffer.allocate(videoMaxInputSize);
+            //根据官方文档的解释MediaMuxer的start一定要在addTrack之后
+            mediaMuxer.start();
+            //视频处理部分
+            mediaExtractor.selectTrack(sourceVTrack);
+            MediaCodec.BufferInfo videoInfo = new MediaCodec.BufferInfo();
+            videoInfo.presentationTimeUs = 0;
+            long videoSampleTime;
+            //获取源视频相邻帧之间的时间间隔。(1)
+            {
+                mediaExtractor.readSampleData(inputBuffer, 0);
+                //skip first I frame
+                if (mediaExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC)
+                    mediaExtractor.advance();
+                mediaExtractor.readSampleData(inputBuffer, 0);
+                long firstVideoPTS = mediaExtractor.getSampleTime();
+                mediaExtractor.advance();
+                mediaExtractor.readSampleData(inputBuffer, 0);
+                long SecondVideoPTS = mediaExtractor.getSampleTime();
+                videoSampleTime = Math.abs(SecondVideoPTS - firstVideoPTS);
+                Log.d(TAG, "videoSampleTime is " + videoSampleTime);
+            }
+            //选择起点
+            mediaExtractor.seekTo(clipPoint, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
+            while (true) {
+                int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
+                if (sampleSize < 0) {
+                    //这里一定要释放选择的轨道,不然另一个轨道就无法选中了
+                    mediaExtractor.unselectTrack(sourceVTrack);
+                    break;
+                }
+                int trackIndex = mediaExtractor.getSampleTrackIndex();
+                //获取时间戳
+                long presentationTimeUs = mediaExtractor.getSampleTime();
+                //获取帧类型,只能识别是否为I帧
+                int sampleFlag = mediaExtractor.getSampleFlags();
+                Log.d(TAG, "trackIndex is " + trackIndex
+                        + ";presentationTimeUs is " + presentationTimeUs
+                        + ";sampleFlag is " + sampleFlag
+                        + ";sampleSize is " + sampleSize);
+                //剪辑时间到了就跳出
+                if ((clipDuration != 0) && (presentationTimeUs > (clipPoint + clipDuration))) {
+                    mediaExtractor.unselectTrack(sourceVTrack);
+                    break;
+                }
+                mediaExtractor.advance();
+                videoInfo.offset = 0;
+                videoInfo.size = sampleSize;
+                videoInfo.flags = sampleFlag;
+                mediaMuxer.writeSampleData(videoTrackIndex, inputBuffer, videoInfo);
+                videoInfo.presentationTimeUs += videoSampleTime;//presentationTimeUs;
+            }
+            //音频部分
+            mediaExtractor.selectTrack(sourceATrack);
+            MediaCodec.BufferInfo audioInfo = new MediaCodec.BufferInfo();
+            audioInfo.presentationTimeUs = 0;
+            long audioSampleTime;
+            //获取音频帧时长
+            {
+                mediaExtractor.readSampleData(inputBuffer, 0);
+                //skip first sample
+                if (mediaExtractor.getSampleTime() == 0)
+                    mediaExtractor.advance();
+                mediaExtractor.readSampleData(inputBuffer, 0);
+                long firstAudioPTS = mediaExtractor.getSampleTime();
+                mediaExtractor.advance();
+                mediaExtractor.readSampleData(inputBuffer, 0);
+                long SecondAudioPTS = mediaExtractor.getSampleTime();
+                audioSampleTime = Math.abs(SecondAudioPTS - firstAudioPTS);
+                Log.d(TAG, "AudioSampleTime is " + audioSampleTime);
+            }
+            mediaExtractor.seekTo(clipPoint, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+            while (true) {
+                int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
+                if (sampleSize < 0) {
+                    mediaExtractor.unselectTrack(sourceATrack);
+                    break;
+                }
+                int trackIndex = mediaExtractor.getSampleTrackIndex();
+                long presentationTimeUs = mediaExtractor.getSampleTime();
+                Log.d(TAG, "trackIndex is " + trackIndex
+                        + ";presentationTimeUs is " + presentationTimeUs);
+                if ((clipDuration != 0) && (presentationTimeUs > (clipPoint + clipDuration))) {
+                    mediaExtractor.unselectTrack(sourceATrack);
+                    break;
+                }
+                mediaExtractor.advance();
+                audioInfo.offset = 0;
+                audioInfo.size = sampleSize;
+                mediaMuxer.writeSampleData(audioTrackIndex, inputBuffer, audioInfo);
+                audioInfo.presentationTimeUs += audioSampleTime;//presentationTimeUs;
+            }
+        } catch (Exception e) {
+            Log.e(TAG, " read error " + e.getMessage());
+        } finally {
+            //全部写完后释放MediaMuxer和MediaExtractor
+            if (mediaMuxer != null) {
+                mediaMuxer.stop();
+                mediaMuxer.release();
+            }
+            mediaExtractor.release();
+        }
+        return video;
+    }
+
+    public static void mixVideAndAudio(File outVideoPath, File outAudioPath, File outMergeVideoPath) throws Exception {
+        //分别初始化视频、音频的Extractor
+        MediaExtractor videoExtractor = new MediaExtractor();
+        videoExtractor.setDataSource(outVideoPath.getAbsolutePath());
+        MediaExtractor audioExtractor = new MediaExtractor();
+        audioExtractor.setDataSource(outAudioPath.getAbsolutePath());
+        int videoTrack = getTrack(videoExtractor, "video/");
+        int audioTrack = getTrack(audioExtractor, "audio/");
+        videoExtractor.selectTrack(videoTrack);
+        audioExtractor.selectTrack(audioTrack);
+        MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
+        MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();
+        //写入新的视频
+        if (outMergeVideoPath.exists() && outMergeVideoPath.createNewFile())
+            ;
+        ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
+
+        MediaMuxer mediaMuxer = new MediaMuxer(outMergeVideoPath.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+        int writeVideoTrackIndex = mediaMuxer.addTrack(videoExtractor.getTrackFormat(videoTrack));
+        int writeAudioTrackIndex = mediaMuxer.addTrack(audioExtractor.getTrackFormat(audioTrack));
+        mediaMuxer.start();
+        long videoSampleTime = getSampleTime(videoExtractor, byteBuffer, videoTrack);
+        while (true) {
+            int data = videoExtractor.readSampleData(byteBuffer, 0);
+            if (data < 0) {
+                break;
+            }
+            videoBufferInfo.size = data;
+            videoBufferInfo.offset = 0;
+            videoBufferInfo.flags = videoExtractor.getSampleFlags();
+            videoBufferInfo.presentationTimeUs += videoSampleTime;
+            mediaMuxer.writeSampleData(writeVideoTrackIndex, byteBuffer, videoBufferInfo);
+            videoExtractor.advance();
+        }
+        long audioSampleTime = getSampleTime(audioExtractor, byteBuffer, audioTrack);
+        while (true) {
+            int data = audioExtractor.readSampleData(byteBuffer, 0);
+            if (data < 0) {
+                break;
+            }
+            audioBufferInfo.size = data;
+            audioBufferInfo.offset = 0;
+            audioBufferInfo.flags = audioExtractor.getSampleFlags();
+            audioBufferInfo.presentationTimeUs += audioSampleTime;
+            mediaMuxer.writeSampleData(writeAudioTrackIndex, byteBuffer, audioBufferInfo);
+            audioExtractor.advance();
+        }
+        Log.i("video", "合并完成");
+        mediaMuxer.stop();
+        mediaMuxer.release();
+        videoExtractor.release();
+        audioExtractor.release();
+    }
+
+    public static long getSampleTime(MediaExtractor mediaExtractor, ByteBuffer byteBuffer, int videoTrack) {
+        mediaExtractor.readSampleData(byteBuffer, 0);
+        //跳过I帧,要P帧(视频是由个别I帧和很多P帧组成)h264编码中有IBP帧 I为关键帧。
+        if (mediaExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
+            mediaExtractor.advance();
+        }
+        mediaExtractor.readSampleData(byteBuffer, 0);
+
+        // 得到第一帧的PTS
+        long firstVideoPTS = mediaExtractor.getSampleTime();
+        //下一帧
+        mediaExtractor.advance();
+        mediaExtractor.readSampleData(byteBuffer, 0);
+        long secondVideoPTS = mediaExtractor.getSampleTime();
+        long sampleTime = Math.abs(secondVideoPTS - firstVideoPTS);
+
+        // 重新切换此信道,不然上面跳过了3帧,造成前面的帧数模糊
+        mediaExtractor.unselectTrack(videoTrack);
+        mediaExtractor.selectTrack(videoTrack);
+        return sampleTime;
+    }
+
+    public static int getTrack(MediaExtractor extractor, String willFormat) {
+        //获得信道数
+        int trackCount = extractor.getTrackCount();
+        for (int i = 0; i < trackCount; i++) {
+            MediaFormat trackFormat = extractor.getTrackFormat(i);
+            String format = trackFormat.getString(MediaFormat.KEY_MIME);
+            if (format.startsWith(willFormat)) {
+                return i;
+            }
+        }
+        return 0;
+    }
+}

+ 226 - 0
app/src/main/java/com/sheep/gamegroup/util/MediaMetadataRetrieverUtil.java

@@ -0,0 +1,226 @@
+package com.sheep.gamegroup.util;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.MediaMetadataRetriever;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.widget.ImageView;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.sheep.gamegroup.absBase.AbsObserver;
+import com.sheep.jiuyan.samllsheep.R;
+import com.sheep.jiuyan.samllsheep.SheepApp;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Created by realicing on 2018/11/29.
+ * realicing@sina.com
+ * 可以获取视频帧
+ */
+public class MediaMetadataRetrieverUtil {
+    private static final String TAG = "MediaMetadataRetrieverUtil";
+    //要操作的本地视频文件
+    private String fromFile;
+    //输出路径
+    private String outDir = SheepApp.getInstance().getDir("frame", Context.MODE_PRIVATE).getAbsolutePath();
+    //视频文件的总时长,单位毫秒
+    private long duration;
+    //要截取的图片个数
+    private int size = 10;
+
+    public MediaMetadataRetrieverUtil(String fromFile, long duration) {
+        this.fromFile = fromFile;
+        this.duration = duration;
+    }
+
+    public String getFromFile() {
+        return fromFile;
+    }
+
+    public void setFromFile(String fromFile) {
+        this.fromFile = fromFile;
+    }
+
+    public String getOutDir() {
+        return outDir;
+    }
+
+    public void setOutDir(String outDir) {
+        this.outDir = outDir;
+    }
+
+    public long getDuration() {
+        return duration;
+    }
+
+    public void setDuration(long duration) {
+        this.duration = duration;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public void setSize(int size) {
+        this.size = size;
+    }
+
+    /**
+     * 取出视频文件中的图片帧列表
+     */
+    public void initRv(final RecyclerView recyclerView) {
+        Observable.create(new ObservableOnSubscribe<List<Bitmap>>() {
+            @Override
+            public void subscribe(ObservableEmitter<List<Bitmap>> emitter) throws Exception {
+                emitter.onNext(getFrameBitmapList(fromFile, duration, size));
+            }
+        })
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new AbsObserver<List<Bitmap>>() {
+                    @Override
+                    public void onNext(final List<Bitmap> strings) {
+                        recyclerView.setLayoutManager(new GridLayoutManager(SheepApp.getInstance(), size));
+                        BaseQuickAdapter<Bitmap, BaseViewHolder> baseQuickAdapter = new BaseQuickAdapter<Bitmap, BaseViewHolder>(R.layout.item_iv_mh, strings) {
+                            @Override
+                            protected void convert(BaseViewHolder helper, Bitmap item) {
+                                ImageView imageView = helper.getView(R.id.item_iv);
+                                imageView.setImageBitmap(item);
+                            }
+                        };
+                        recyclerView.setAdapter(baseQuickAdapter);
+                    }
+                });
+
+    }
+    /**
+     * 取出视频文件中的图片帧列表
+     */
+    public void initRv2(final RecyclerView recyclerView) {
+        Observable.create(new ObservableOnSubscribe<List<String>>() {
+            @Override
+            public void subscribe(ObservableEmitter<List<String>> emitter) throws Exception {
+                emitter.onNext(getFrameFileList(fromFile, outDir, duration, size));
+            }
+        })
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new AbsObserver<List<String>>() {
+                    @Override
+                    public void onNext(final List<String> strings) {
+                        recyclerView.setLayoutManager(new GridLayoutManager(SheepApp.getInstance(), size));
+                        BaseQuickAdapter<String, BaseViewHolder> baseQuickAdapter = new BaseQuickAdapter<String, BaseViewHolder>(R.layout.item_iv_mh, strings) {
+                            @Override
+                            protected void convert(BaseViewHolder helper, String item) {
+                                ImageView imageView = helper.getView(R.id.item_iv);
+                                ViewUtil.setImagePath(imageView, item);
+                                Jump2View.getInstance().showImgList(ActivityManager.getInstance().currentActivity(), helper.getAdapterPosition(), (ArrayList<String>) strings);
+                            }
+                        };
+                        recyclerView.setAdapter(baseQuickAdapter);
+                    }
+                });
+
+    }
+
+
+    public static String getFilePath(String fromFile, String outDir, int index) {
+        return String.format(Locale.CHINA, "%s%s%s_frame_%d.png", outDir, File.separator, FileUtil.getFileNameNoExFromPath(fromFile), index);
+    }
+
+    /**
+     * 取出视频文件中的图片帧列表
+     *
+     * @param fromFile 要操作的本地视频文件
+     * @param duration 视频文件的总时长,单位毫秒
+     * @param size     需要的图片个数
+     * @return
+     */
+    public static List<Bitmap> getFrameBitmapList(String fromFile, long duration, int size) {
+        if (duration < size || size < 1) {
+            return null;
+        }
+        long per = duration * 1000L / (size - 1);
+        List<Bitmap> resultList = new ArrayList<>();
+        long time = System.currentTimeMillis();
+        MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
+        metadataRetriever.setDataSource(fromFile);
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
+            MediaMetadataRetriever.BitmapParams bitmapParams = new MediaMetadataRetriever.BitmapParams();
+            Bitmap.Config config = Bitmap.Config.ARGB_8888;
+            bitmapParams.setPreferredConfig(config);
+            return metadataRetriever.getFramesAtIndex(0, size, bitmapParams);
+        }
+        for (int i = 0; i < size; i++) {
+            //这里单位为微秒
+            long atTime = i + 1 == size ? (duration * 1000L) : i * per;
+            Bitmap bitmap = metadataRetriever.getFrameAtTime(atTime, MediaMetadataRetriever.OPTION_CLOSEST);
+            resultList.add(bitmap);
+        }
+        long curTime = System.currentTimeMillis();
+        LogUtil.println(TAG, time, curTime, curTime - time, (curTime - time) / 1000, (curTime - time) / 10000);
+        return resultList;
+    }
+
+    /**
+     * 取出视频文件中的图片帧列表
+     *
+     * @param fromFile 要操作的本地视频文件
+     * @param outDir   输出路径
+     * @param duration 视频文件的总时长,单位毫秒
+     * @param size     需要的图片个数
+     * @return
+     */
+    public static List<String> getFrameFileList(String fromFile, String outDir, long duration, int size) {
+        if (duration < size || size < 1) {
+            return null;
+        }
+        long per = duration * 1000L / (size - 1);
+        List<String> resultList = new ArrayList<>();
+        long time = System.currentTimeMillis();
+        MediaMetadataRetriever metadataRetriever = new MediaMetadataRetriever();
+        metadataRetriever.setDataSource(fromFile);
+
+        for (int i = 0; i < size; i++) {
+            //这里单位为微秒
+            long atTime = i + 1 == size ? (duration * 1000L) : i * per;
+            Bitmap bitmap = metadataRetriever.getFrameAtTime(atTime, MediaMetadataRetriever.OPTION_CLOSEST);
+
+            String path = getFilePath(fromFile, outDir, i);
+            FileOutputStream fileOutputStream = null;
+            try {
+                fileOutputStream = new FileOutputStream(path);
+                bitmap.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream);
+                resultList.add(path);
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                if (fileOutputStream != null) {
+                    try {
+                        fileOutputStream.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+            bitmap.recycle();
+        }
+        long curTime = System.currentTimeMillis();
+        LogUtil.println(TAG, time, curTime, curTime - time, (curTime - time) / 1000, (curTime - time) / 10000);
+        return resultList;
+    }
+}

+ 1 - 1
app/src/main/java/com/sheep/gamegroup/util/TestUtil.java

@@ -476,7 +476,7 @@ public class TestUtil {
                                 G.showToast(md5);
                                 break;
                             case "空间不足提示框":
-                                DialogStorageLow.showDialog(activity);
+                                DialogStorageLow.showDialog(null);
                                 break;
                             case "显示已经安装应用列表":
                                 showHasInstallApkList(activity);

+ 1 - 1
app/src/main/java/com/sheep/gamegroup/util/TimeUtil.java

@@ -239,7 +239,7 @@ public class TimeUtil {
         }
     }
 
-    public static String getDurationText(int duration) {
+    public static String getDurationText(long duration) {
         if(duration > 3600)
             return String.format(Locale.CHINA, "%d:%d:%d", duration / 3600, duration / 60 % 60, duration % 60);
         else if(duration > 60)

+ 60 - 12
app/src/main/java/com/sheep/gamegroup/util/ViewUtil.java

@@ -18,12 +18,10 @@ import android.media.MediaPlayer;
 import android.net.Uri;
 import android.os.Build;
 import android.provider.MediaStore;
-import android.support.annotation.IntegerRes;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.StringRes;
 import android.support.v4.app.FragmentActivity;
 import android.support.v7.app.AlertDialog;
-import android.support.v7.widget.AppCompatCheckedTextView;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -70,11 +68,13 @@ import com.sheep.gamegroup.model.entity.Container;
 import com.sheep.gamegroup.model.entity.DialogConfig;
 import com.sheep.gamegroup.model.entity.GameAccountEntity;
 import com.sheep.gamegroup.model.entity.GiftBagApp;
+import com.sheep.gamegroup.model.entity.Lp;
 import com.sheep.gamegroup.model.entity.PayEntity;
 import com.sheep.gamegroup.model.entity.RobTask;
 import com.sheep.gamegroup.model.entity.TaskEty;
 import com.sheep.gamegroup.util.glide.RoundedCornersTransformation;
 import com.sheep.gamegroup.util.share.ShareLinkConfig;
+import com.sheep.gamegroup.util.viewHelper.LayoutParamsUtil;
 import com.sheep.gamegroup.view.activity.ActMain;
 import com.sheep.gamegroup.view.activity.PersonalInfoAct;
 import com.sheep.gamegroup.view.activity.SplashAct;
@@ -111,7 +111,6 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Observable;
 import java.util.Random;
 import java.util.concurrent.TimeUnit;
 
@@ -280,24 +279,26 @@ public class ViewUtil {
     }
 
     public static void dismiss(DialogProgress dialogProgress, int delay) {
-        if(dialogProgress != null ){
+        if (dialogProgress != null) {
             dismiss(dialogProgress.getAlertDialog(), delay);
         }
     }
+
     public static void dismiss(DialogLoading dialogLoading, int delay) {
-        if(dialogLoading != null ){
+        if (dialogLoading != null) {
             dismiss(dialogLoading.getAlertDialog(), delay);
         }
     }
+
     public static void dismiss(final AlertDialog dialog, int delay) {
-        if(dialog != null ){
-            if(delay > 0){
+        if (dialog != null) {
+            if (delay > 0) {
                 delay(new AbsObserver<Integer>() {
                     @Override
                     public void onNext(Integer integer) {
                         try {
                             dialog.dismiss();
-                        } catch (Exception ignore){
+                        } catch (Exception ignore) {
 
                         }
                     }
@@ -307,6 +308,7 @@ public class ViewUtil {
             }
         }
     }
+
     public static void delay(Observer<Integer> observer, int delay) {
         io.reactivex.Observable.just(1)
                 .delay(delay, TimeUnit.SECONDS)
@@ -316,7 +318,7 @@ public class ViewUtil {
     }
 
     public static void setChecked(Checkable view, boolean checked) {
-        if(view != null)
+        if (view != null)
             view.setChecked(checked);
     }
 
@@ -1589,9 +1591,32 @@ public class ViewUtil {
             }
         }
     }
+
+    public static void setViewWH(ImageView imageView, String pictures, int width) {
+        if (imageView != null) {
+            if (TextUtils.isEmpty(pictures)) {
+                imageView.setImageResource(R.mipmap.icon);
+            } else {
+                if (pictures.contains(";")) {
+                    pictures = pictures.split(";")[0];
+                }
+                Uri uri = Uri.parse(pictures);
+                int w = NumberFormatUtils.parseInteger(uri.getQueryParameter("w"));
+                int h = NumberFormatUtils.parseInteger(uri.getQueryParameter("h"));
+                if (w > 0 && h > 0) {
+                    LayoutParamsUtil.resetLayoutParams(imageView, new Lp().setWidth(width).setHeight((int) (width * h * 1.0f / w)));
+                }
+            }
+        }
+    }
+
+    public static void setViewWH(View view, int width, float radio) {
+        LayoutParamsUtil.resetLayoutParams(view, new Lp().setWidth(width).setHeight((int) (width * radio)));
+    }
+
     public static void setImagePath(ImageView imageView, String paths) {
-        if (imageView != null){
-            if(TextUtils.isEmpty(paths)) {
+        if (imageView != null) {
+            if (TextUtils.isEmpty(paths)) {
                 imageView.setImageResource(R.mipmap.icon);
             } else {
                 if (paths.contains(";")) {
@@ -1602,6 +1627,19 @@ public class ViewUtil {
         }
     }
 
+    public static void setImagePath(ImageView imageView, String paths, int radio) {
+        if (imageView != null) {
+            if (TextUtils.isEmpty(paths)) {
+                imageView.setImageResource(R.mipmap.icon);
+            } else {
+                if (paths.contains(";")) {
+                    paths = paths.split(";")[0];
+                }
+                Glide.with(SheepApp.getInstance()).load(new File(paths)).apply(new RequestOptions().transform(new RoundedCornersTransformation(radio, 0))).into(imageView);
+            }
+        }
+    }
+
     public static void setImageLoading(ImageView imageView, String pictures) {
         if (imageView != null) {
             if (TextUtils.isEmpty(pictures)) {
@@ -1671,12 +1709,19 @@ public class ViewUtil {
             textView.setText(TextUtils.isEmpty(msg) ? "" : msg);
         }
     }
-    public static void setText(TextView textView, @StringRes int stringId, Object...objects) {
+
+    public static void setText(TextView textView, @StringRes int stringId, Object... objects) {
         if (textView != null) {
             textView.setText(SheepApp.getInstance().getString(stringId, objects));
         }
     }
 
+    public static void setText(TextView textView, String format, Object... objects) {
+        if (textView != null) {
+            textView.setText(String.format(Locale.CHINA, format, objects));
+        }
+    }
+
     public static void setText(TextView textView, IContentTypeContainer<Integer, String> contentTypeContainer, int maxWidth) {
         switch (contentTypeContainer.getContentType()) {
             case StringUtils.CONTENT_TYPE_FONT:
@@ -1798,11 +1843,13 @@ public class ViewUtil {
             view.setVisibility(isVisible ? View.VISIBLE : View.GONE);
         }
     }
+
     public static void toggleVisibility(View view) {
         if (view != null) {
             view.setVisibility(isVisible(view) ? View.GONE : View.VISIBLE);
         }
     }
+
     public static boolean isVisible(View view) {
         return view != null && view.getVisibility() == View.VISIBLE;
     }
@@ -1812,6 +1859,7 @@ public class ViewUtil {
             view.setVisibility(isVisible ? View.VISIBLE : View.INVISIBLE);
         }
     }
+
     public static void toggleVisibility2(View view) {
         if (view != null) {
             view.setVisibility(isVisible(view) ? View.INVISIBLE : View.VISIBLE);

+ 510 - 0
app/src/main/java/com/sheep/gamegroup/view/activity/ActCutVideo.java

@@ -0,0 +1,510 @@
+package com.sheep.gamegroup.view.activity;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.text.TextUtils;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+import android.widget.VideoView;
+
+import com.kfzs.duanduan.utils.NumberFormatUtils;
+import com.kfzs.duanduan.utils.StatusBarUtils;
+import com.sheep.gamegroup.absBase.AbsObserver;
+import com.sheep.gamegroup.absBase.BaseContainerActivity;
+import com.sheep.gamegroup.model.entity.DiscoveryTopic;
+import com.sheep.gamegroup.model.entity.Video;
+import com.sheep.gamegroup.util.DataUtil;
+import com.sheep.gamegroup.util.Jump2View;
+import com.sheep.gamegroup.util.KeyEventUtil;
+import com.sheep.gamegroup.util.LogUtil;
+import com.sheep.gamegroup.util.MediaHandleUtil;
+import com.sheep.gamegroup.util.TestUtil;
+import com.sheep.gamegroup.util.ViewUtil;
+import com.sheep.gamegroup.view.customview.VideoFramesView;
+import com.sheep.gamegroup.view.fragment.FgtDiscoveryTopic;
+import com.sheep.jiuyan.samllsheep.R;
+import com.sheep.jiuyan.samllsheep.utils.G;
+
+import org.greenrobot.eventbus.Subscribe;
+
+import java.util.Locale;
+
+import butterknife.BindView;
+import rx.functions.Action1;
+
+/**
+ * Created by realicing on 2018/11/9.
+ * realicing@sina.com
+ * 添加话题并剪切视频
+ */
+public class ActCutVideo extends BaseContainerActivity implements MediaPlayer.OnInfoListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, View.OnLayoutChangeListener {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        StatusBarUtils.setTranslucent(this);
+        super.onCreate(savedInstanceState);
+    }
+
+    @BindView(R.id.videoView)
+    VideoView videoView;
+    @BindView(R.id.video_loading)
+    View video_loading;
+    @BindView(R.id.videoFramesView)
+    VideoFramesView videoFramesView;
+    @BindView(R.id.edit_video_time_tv)
+    TextView edit_video_time_tv;
+
+    @Override
+    protected int getLayoutId() {
+        return R.layout.act_edit_video;
+    }
+
+    public static final long MAX_VIDEO_DURATION = 15_000L;//视频最大时长,超过需要剪切
+    private Video data;
+
+    @Override
+    public void initView() {
+        super.initView();
+        initVolume();
+        data = DataUtil.getObject(getIntent(), Video.class);
+        Uri uri = Uri.parse(data.getFilePath());
+        videoView.setOnInfoListener(this);
+        videoView.setOnPreparedListener(this);
+        videoView.setOnErrorListener(this);
+        videoView.setOnCompletionListener(this);
+        videoView.addOnLayoutChangeListener(this);
+//        videoView.setMediaController(new MediaController(this));
+        videoView.setVideoURI(uri);
+        videoFramesView.initVideo(data).setMaxDuration(MAX_VIDEO_DURATION).showVideoList().setOnTimeChangeListener(new Action1<Float>() {
+            @Override
+            public void call(Float duration) {
+                if(duration == null)
+                    return;
+                if(ViewUtil.isVisible(videoFramesView))
+                    checkDuration(duration.longValue());
+                ViewUtil.setText(edit_video_time_tv, R.string.has_choose_x_second, NumberFormatUtils.retainMost1(duration / 1000.0f));
+                if (TestUtil.isDev()) {
+                    edit_video_time_tv.append("\t");
+                    edit_video_time_tv.append(String.valueOf(duration));
+                    edit_video_time_tv.append("\t");
+                    edit_video_time_tv.append(String.valueOf(data.getDuration()));
+                    edit_video_time_tv.append("\t");
+                    edit_video_time_tv.append(String.format(Locale.CHINA, "%.1f秒-%.1f秒", videoFramesView.getStartPoint() / 1000.0f, videoFramesView.getEndPoint() / 1000.0f));
+                    edit_video_time_tv.append("\t");
+                    edit_video_time_tv.append(videoFramesView.getLineInfo());
+                }
+            }
+        });
+        //隐藏视频剪切相关view
+        ViewUtil.setVisibility2(edit_video_time_tv, false);
+        ViewUtil.setVisibility2(videoFramesView, false);
+    }
+
+    //检查有没有设置剪切视频的时长
+    private void checkDuration(long duration) {
+        if ((data.getDuration() - duration) >= 1000) {
+            ViewUtil.setText(edit_video_sure_tv, BTN_TEXT_FINISH);
+        } else {
+            ViewUtil.setText(edit_video_sure_tv, BTN_TEXT_CUT);
+        }
+    }
+
+    @Override
+    protected Fragment initFragment() {
+        return new FgtDiscoveryTopic();
+    }
+
+    @SuppressLint("ClickableViewAccessibility")
+    @Override
+    public void initListener() {
+        mGestureDetector = new GestureDetector(getApplicationContext(), mGestureListener);
+        View.OnTouchListener listener = new View.OnTouchListener() {
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                return onTouchEvent(event);
+            }
+        };
+        videoView.setOnTouchListener(listener);
+    }
+
+    private GestureDetector mGestureDetector;
+
+    //播放或者暂停
+    private void playOrPause() {
+        if (videoView.isPlaying()) {
+            pause();
+        } else {
+            play();
+        }
+    }
+
+    //播放
+    private void play() {
+        if (videoView == null) {
+            return;
+        }
+        videoView.start();
+    }
+
+    //暂停
+    private void pause() {
+        if (videoView == null) {
+            return;
+        }
+        videoView.pause();
+    }
+
+    //销毁
+    private void destroy() {
+        if (videoView == null) {
+            return;
+        }
+        videoView.stopPlayback();
+    }
+
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mGestureDetector.onTouchEvent(event);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (!isPauseByUser)
+            play();
+    }
+
+    //是否是用户手机暂停
+    private boolean isPauseByUser;
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (videoView != null && videoView.isPlaying())
+            isPauseByUser = false;
+        if (video_loading != null)
+            video_loading.setVisibility(View.VISIBLE);
+        pause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        destroy();
+        if (videoFramesView != null)
+            videoFramesView.onDestroy();
+        super.onDestroy();
+        mAudioManager = null;
+    }
+
+    //    //滑动方向
+//    private int direction = 0;
+//    //左右滑动
+//    public static final int DIRECTION_LEFT_RIGHT = 1;
+//    //上下滑动
+//    public static final int DIRECTION_TOP_BOTTOM = 2;
+    private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {
+        @Override
+        public boolean onDown(MotionEvent e) {
+            LogUtil.println("mGestureListener", "onDown");
+//            direction = 0;
+            return super.onDown(e);
+        }
+
+        @Override
+        public void onLongPress(MotionEvent e) {
+            LogUtil.println("mGestureListener", "onLongPress");
+            super.onLongPress(e);
+        }
+
+        @Override
+        public boolean onSingleTapConfirmed(MotionEvent e) {
+            LogUtil.println("mGestureListener", "onSingleTapConfirmed");
+//            if(isPlaying)
+//                isPauseByUser = true;
+//            playOrPause();
+            return super.onSingleTapConfirmed(e);
+        }
+
+        @Override
+        public boolean onScroll(MotionEvent e1, MotionEvent e2,
+                                float distanceX, float distanceY) {
+            float mOldX = e1.getX(), mOldY = e1.getY();
+            int y = (int) e2.getRawY();
+            int windowWidth = ActCutVideo.this.windowWidth;
+            int windowHeight = ActCutVideo.this.windowHeight;
+            int lineMiddle = windowWidth / 2;
+            if (mOldX > lineMiddle)// 左边滑动
+                onBrightnessSlide((mOldY - y) / windowHeight);
+            else if (mOldX < lineMiddle)// 右边滑动
+                onVolumeSlide((mOldY - y) / windowHeight);
+            return super.onScroll(e1, e2, distanceX, distanceY);
+        }
+
+
+//        @Override
+//        public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) {
+//            LogUtil.println("mGestureListener", distanceX, distanceY);
+//            if (event1 == null || event2 == null) {
+//                return super.onScroll(event1, event2, distanceX, distanceY);
+//            }
+//            if (direction == 0) {
+//                float a = Math.abs(distanceY);
+//                float b = Math.abs(distanceX);
+//                if (a > b) {
+//                    LogUtil.println("mGestureListener", "上下滑动");
+//                    direction = DIRECTION_TOP_BOTTOM;
+//                } else if (b > a) {
+//                    LogUtil.println("mGestureListener", "左右滑动");
+//                    direction = DIRECTION_LEFT_RIGHT;
+//                }
+//            }
+//            return super.onScroll(event1, event2, distanceX, distanceY);
+//        }
+//
+//        @Override
+//        public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) {
+//            LogUtil.println("mGestureListener", velocityX, velocityY);
+//            if (event1 == null || event2 == null) {
+//                return super.onFling(event1, event2, velocityX, velocityY);
+//            }
+//            final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200;
+//            if (event1.getX() - event2.getX() > FLING_MIN_DISTANCE
+//                    && Math.abs(velocityX) > FLING_MIN_VELOCITY) {
+//                // Fling left
+//                LogUtil.println("mGestureListener", "Fling left");
+//                //音量增益减
+//            } else if (event2.getX() - event1.getX() > FLING_MIN_DISTANCE
+//                    && Math.abs(velocityX) > FLING_MIN_VELOCITY) {
+//                // Fling right
+//                LogUtil.println("mGestureListener", "Fling rigth");
+//                //音量增益加
+//            } else if (event2.getY() - event1.getY() > FLING_MIN_DISTANCE
+//                    && Math.abs(velocityY) > FLING_MIN_VELOCITY) {
+//                if (direction == DIRECTION_LEFT_RIGHT) {
+//                    // Fling down
+//                    LogUtil.println("mGestureListener", "Fling down");
+//                    //频道减
+//                }
+//            } else if (event1.getY() - event2.getY() > FLING_MIN_DISTANCE
+//                    && Math.abs(velocityY) > FLING_MIN_VELOCITY) {
+//                if (direction == DIRECTION_LEFT_RIGHT) {
+//                    // Fling up
+//                    LogUtil.println("mGestureListener", "Fling up");
+//                    //频道加
+//                }
+//            }
+////            return super.onFling(event1, event2, velocityX, velocityY);
+//            return false;
+//        }
+    };
+    private AudioManager mAudioManager;
+    private int mMaxVolume;//最大音量
+    private int mVolume;//当前音量
+
+    private void initVolume() {
+        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        if (mAudioManager != null)
+            mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+    }
+
+    //滑动改变音量
+    private void onVolumeSlide(float percent) {
+        if (mVolume == -1) {
+            mVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+            if (mVolume < 0)
+                mVolume = 0;
+
+            // 显示
+//                mOperationBg.setImageResource(R.drawable.video_volumn_bg);
+//                mVolumeBrightnessLayout.setVisibility(View.VISIBLE);
+        }
+
+        int index = (int) (percent * mMaxVolume) + mVolume;
+        if (index > mMaxVolume)
+            index = mMaxVolume;
+        else if (index < 0)
+            index = 0;
+
+        // 变更声音
+        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0);
+        // 变更进度条
+//            ViewGroup.LayoutParams lp = mOperationPercent.getLayoutParams();
+//            lp.width = findViewById(R.id.operation_full).getLayoutParams().width * index / mMaxVolume;
+//            mOperationPercent.setLayoutParams(lp);
+    }
+
+    private float mBrightness = -1;
+
+    //滑动改变亮度
+    private void onBrightnessSlide(float percent) {
+        if (mBrightness < 0) {
+            mBrightness = getWindow().getAttributes().screenBrightness;
+            if (mBrightness <= 0.00f)
+                mBrightness = 0.50f;
+            if (mBrightness < 0.01f)
+                mBrightness = 0.01f;
+
+            // 显示
+//                mOperationBg.setImageResource(R.drawable.video_brightness_bg);
+//                mVolumeBrightnessLayout.setVisibility(View.VISIBLE);
+        }
+        WindowManager.LayoutParams lpa = getWindow().getAttributes();
+        lpa.screenBrightness = mBrightness + percent;
+        if (lpa.screenBrightness > 1.0f)
+            lpa.screenBrightness = 1.0f;
+        else if (lpa.screenBrightness < 0.01f)
+            lpa.screenBrightness = 0.01f;
+        getWindow().setAttributes(lpa);
+
+//            ViewGroup.LayoutParams lp = mOperationPercent.getLayoutParams();
+//            lp.width = (int) (findViewById(R.id.operation_full).getLayoutParams().width * lpa.screenBrightness);
+//            mOperationPercent.setLayoutParams(lp);
+    }
+
+    @Override
+    public boolean onInfo(MediaPlayer mediaPlayer, int what, int extra) {
+        LogUtil.println("ActPlayVideo", "onInfo what = " + what + " extra = " + extra);
+        if (!mediaPlayer.isLooping())
+            mediaPlayer.setLooping(true);
+        switch (what) {
+            case MediaPlayer.MEDIA_INFO_BUFFERING_START:
+                LogUtil.println("ActPlayVideo", "onInfo", "正在缓冲");
+                if (video_loading != null)
+                    video_loading.setVisibility(View.VISIBLE);
+                break;
+            case MediaPlayer.MEDIA_INFO_BUFFERING_END:
+            case MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
+                LogUtil.println("ActPlayVideo", "onInfo", "缓冲完成");
+                //缓存完成,继续播放
+                if (video_loading != null)
+                    video_loading.setVisibility(View.GONE);
+                break;
+        }
+        return false;
+    }
+
+    @Override
+    public void onPrepared(MediaPlayer mediaPlayer) {
+        LogUtil.println("ActPlayVideo", "onPrepared");
+        if (video_loading != null)
+            video_loading.setVisibility(View.GONE);
+    }
+
+    @Override
+    public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
+        LogUtil.println("ActPlayVideo", "onInfo what = " + what + " extra = " + extra);
+        return false;
+    }
+
+    @Override
+    public void onCompletion(MediaPlayer mediaPlayer) {
+        LogUtil.println("ActPlayVideo", "onCompletion");
+    }
+
+    private int windowWidth = G.WIDTH;
+    private int windowHeight = G.HEIGHT;
+
+    @Override
+    public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
+        LogUtil.println("ActPlayVideo", "onLayoutChange", left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom);
+        this.windowWidth = left + right;
+        this.windowHeight = top + bottom;
+    }
+
+
+    @BindView(R.id.edit_video_topic_tv)
+    TextView edit_video_topic_tv;
+    @BindView(R.id.edit_video_sure_tv)
+    TextView edit_video_sure_tv;
+    @BindView(R.id.frame_container)
+    View frame_container;
+
+    @Override
+    protected boolean needRegisterEventBus() {
+        return true;
+    }
+
+    @Subscribe
+    public void onEventMainThread(DiscoveryTopic result) {
+        ViewUtil.setText(edit_video_topic_tv, "#%s#", result.getTopic());
+        data.setTopicId(result.getId());
+    }
+
+    //点击话题进行切换
+    public void onClickTopic(View view) {
+        ViewUtil.toggleVisibility(frame_container);
+        ViewUtil.setText(edit_video_sure_tv, BTN_TEXT_NEXT);
+        if(ViewUtil.isVisible(frame_container)){
+            ViewUtil.setVisibility2(edit_video_time_tv, false);
+            ViewUtil.setVisibility2(videoFramesView, false);
+        }
+    }
+
+    //点击返回
+    public void onClickBackImg(View view) {
+        KeyEventUtil.sendKeyDownUp(KeyEvent.KEYCODE_BACK);
+    }
+
+    private static final String BTN_TEXT_NEXT = "下一步";
+    private static final String BTN_TEXT_CUT = "剪切";
+    private static final String BTN_TEXT_FINISH = "完成";
+    //点击确定
+    public void onClickSure(View view) {
+        String btnText = ((TextView) view).getText().toString();
+        switch (btnText){
+            case BTN_TEXT_NEXT:
+                showCut();
+                break;
+            case BTN_TEXT_CUT:
+                finishCut();
+                break;
+            case BTN_TEXT_FINISH:
+                goEditVideo();
+                break;
+        }
+    }
+
+    private void goEditVideo() {//完成并跳转编辑视频界面
+        Jump2View.getInstance().goActEditVideo(data);
+        finish();
+    }
+
+    private void finishCut() {//其次完成编辑
+        G.showToast("开始剪切");
+        new MediaHandleUtil(data.getFilePath(), videoFramesView.getStartPoint() * 1000, videoFramesView.getLineDuration() * 1000)
+                .tryCutVideo(new AbsObserver<Video>() {
+                    @Override
+                    public void onNext(Video video) {
+                        data.setFilePath(video.getFilePath());
+                        data.setWidth(video.getWidth());
+                        data.setHeight(video.getHeight());
+                        data.setDuration(video.getDuration());
+                        G.showToast("完成剪切");
+                        goEditVideo();
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+                        G.showToast(e.getMessage());
+                    }
+                });
+    }
+
+    private void showCut() {//首先隐藏话题列表
+        ViewUtil.setVisibility(frame_container, false);
+        ViewUtil.setVisibility2(edit_video_time_tv, true);
+        ViewUtil.setVisibility2(videoFramesView, true);
+        checkDuration(videoFramesView.getLineDuration());
+    }
+}

+ 38 - 0
app/src/main/java/com/sheep/gamegroup/view/activity/ActEditVideo.java

@@ -0,0 +1,38 @@
+package com.sheep.gamegroup.view.activity;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import com.kfzs.duanduan.utils.StatusBarUtils;
+import com.sheep.gamegroup.absBase.BaseActivity;
+import com.sheep.gamegroup.model.entity.Video;
+import com.sheep.gamegroup.util.DataUtil;
+import com.sheep.jiuyan.samllsheep.R;
+
+/**
+ * Created by realicing on 2018/11/9.
+ * realicing@sina.com
+ * 编辑视频
+ */
+public class ActEditVideo extends BaseActivity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        StatusBarUtils.setTranslucent(this);
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected int getLayoutId() {
+        return R.layout.act_cut_video;
+    }
+
+    private Video data;
+
+    @Override
+    public void initView() {
+        data = DataUtil.getObject(getIntent(), Video.class);
+        Uri uri = Uri.parse(data.getFilePath());
+    }
+}

+ 1 - 1
app/src/main/java/com/sheep/gamegroup/view/activity/ActMain.java

@@ -399,7 +399,7 @@ public class ActMain extends BaseActYmPermissionCheck  {
     public void onEventMainThread(BigEvent event) {
         switch (event.getEventTypes()) {
             case STORAGE_LOW:
-                DialogStorageLow.showDialog(this);
+                DialogStorageLow.showDialog(null);
                 break;
         }
         if (progressView != null && event.getData() instanceof DownLoadInfo) {

+ 2 - 1
app/src/main/java/com/sheep/gamegroup/view/activity/ActPlayVideo.java

@@ -130,8 +130,9 @@ public class ActPlayVideo extends BaseActivity implements MediaPlayer.OnInfoList
 
     @Override
     protected void onDestroy() {
-        super.onDestroy();
         destroy();
+        super.onDestroy();
+        mAudioManager = null;
     }
 
 //    //滑动方向

+ 7 - 1
app/src/main/java/com/sheep/gamegroup/view/activity/ActPlayVideoArticle.java

@@ -21,9 +21,11 @@ import android.widget.VideoView;
 
 import com.kfzs.duanduan.utils.StatusBarUtils;
 import com.sheep.gamegroup.absBase.BaseActivity;
+import com.sheep.gamegroup.absBase.IRefresh;
 import com.sheep.gamegroup.model.entity.BaseMessage;
 import com.sheep.gamegroup.model.entity.DiscoveryVideo;
 import com.sheep.gamegroup.model.util.SheepSubscriber;
+import com.sheep.gamegroup.util.ActionUtil;
 import com.sheep.gamegroup.util.ApiUtil;
 import com.sheep.gamegroup.util.DataUtil;
 import com.sheep.gamegroup.util.Jump2View;
@@ -32,6 +34,7 @@ import com.sheep.gamegroup.util.TestUtil;
 import com.sheep.gamegroup.util.ViewUtil;
 import com.sheep.gamegroup.util.share.CommonUMShareListener;
 import com.sheep.gamegroup.util.share.ShareLinkConfig;
+import com.sheep.gamegroup.view.fragment.FgtArticleVideo;
 import com.sheep.jiuyan.samllsheep.R;
 import com.sheep.jiuyan.samllsheep.SheepApp;
 import com.sheep.jiuyan.samllsheep.utils.G;
@@ -262,6 +265,8 @@ public class ActPlayVideoArticle extends BaseActivity implements MediaPlayer.OnI
                             }
                             loadVideoData(newVideo);
                         }
+                        //回去视频列表后需要刷新界面
+                        ActionUtil.getInstance().addNextAction(FgtArticleVideo.class.getSimpleName(), IRefresh.class.getSimpleName());
                     }
 
                     @Override
@@ -334,8 +339,9 @@ public class ActPlayVideoArticle extends BaseActivity implements MediaPlayer.OnI
 
     @Override
     protected void onDestroy() {
-        super.onDestroy();
         destroy();
+        super.onDestroy();
+        mAudioManager = null;
     }
 
     //    //滑动方向

+ 45 - 44
app/src/main/java/com/sheep/gamegroup/view/activity/ActPublishArticle.java

@@ -14,23 +14,28 @@ import com.chad.library.adapter.base.BaseViewHolder;
 import com.qiniu.android.http.ResponseInfo;
 import com.qiniu.android.storage.UpCompletionHandler;
 import com.qiniu.android.storage.UpProgressHandler;
+import com.sheep.gamegroup.absBase.AbsObserver;
 import com.sheep.gamegroup.absBase.BaseActivity;
+import com.sheep.gamegroup.absBase.IRefresh;
 import com.sheep.gamegroup.model.entity.BaseMessage;
 import com.sheep.gamegroup.model.entity.CreateUserVideoReq;
 import com.sheep.gamegroup.model.entity.InputAndUrlList;
+import com.sheep.gamegroup.model.entity.Video;
 import com.sheep.gamegroup.model.util.SheepSubscriber;
+import com.sheep.gamegroup.util.ActionUtil;
 import com.sheep.gamegroup.util.Jump2View;
 import com.sheep.gamegroup.util.ListUtil;
 import com.sheep.gamegroup.util.LogUtil;
 import com.sheep.gamegroup.util.QiNiuUploadUtil;
 import com.sheep.gamegroup.util.ViewUtil;
-import com.sheep.gamegroup.view.dialog.DialogLoading;
 import com.sheep.gamegroup.view.dialog.DialogProgress;
+import com.sheep.gamegroup.view.fragment.FgtArticleVideo;
 import com.sheep.jiuyan.samllsheep.R;
 import com.sheep.jiuyan.samllsheep.SheepApp;
 import com.sheep.jiuyan.samllsheep.utils.G;
 import com.sheep.jiuyan.samllsheep.utils.TitleBarUtils;
 
+import org.greenrobot.eventbus.Subscribe;
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -38,13 +43,7 @@ import java.io.File;
 import java.util.Locale;
 
 import butterknife.BindView;
-import cn.finalteam.rxgalleryfinal.RxGalleryFinalApi;
-import cn.finalteam.rxgalleryfinal.bean.ImageCropBean;
-import cn.finalteam.rxgalleryfinal.bean.MediaBean;
-import cn.finalteam.rxgalleryfinal.rxbus.RxBusResultDisposable;
-import cn.finalteam.rxgalleryfinal.rxbus.event.ImageRadioResultEvent;
 import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.functions.Function;
 import io.reactivex.schedulers.Schedulers;
 
 import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;
@@ -103,7 +102,7 @@ public class ActPublishArticle extends BaseActivity {
                     helper.itemView.setOnClickListener(new View.OnClickListener() {
                         @Override
                         public void onClick(View view) {
-                            Jump2View.getInstance().goActPlayVideo(item, false);
+                            addVideo();
                         }
                     });
                 }
@@ -123,33 +122,9 @@ public class ActPublishArticle extends BaseActivity {
     }
 
     private void addVideo() {
-        RxGalleryFinalApi
-                .getInstance(ActPublishArticle.this)
-                .setFilter(new Function<MediaBean, Boolean>() {
-                    @Override
-                    public Boolean apply(MediaBean mediaBean) throws Exception {
-                        boolean result = mediaBean.getDuration() > 15_000L;
-                        if (result) {
-                            G.showToast(R.string.need_picker_video_dur_15);
-                        }
-                        return result;
-                    }
-                })
-                .setType(RxGalleryFinalApi.SelectRXType.TYPE_VIDEO, RxGalleryFinalApi.SelectRXType.TYPE_SELECT_RADIO)
-                .setVDRadioResultEvent(new RxBusResultDisposable<ImageRadioResultEvent>() {
-                    @Override
-                    protected void onEvent(ImageRadioResultEvent imageRadioResultEvent) throws Exception {
-                        ListUtil.removeNull(inputAndUrlList.getList());
-                        inputAndUrlList.getList().add(imageRadioResultEvent.getResult().getOriginalPath());
-                        ViewUtil.notifyDataSetChanged(recyclerView);
-                        imageCropBean = imageRadioResultEvent.getResult();
-
-                    }
-                })
-                .open();
+        Jump2View.getInstance().gotoActMediaChoose(MEDIA_TYPE_NONE);
     }
 
-    private ImageCropBean imageCropBean;
     private int uploadType = UPLOAD_TYPE_NONE;
 
     //点击确定按钮
@@ -163,13 +138,14 @@ public class ActPublishArticle extends BaseActivity {
 //            G.showToast(R.string.nedd_picker_img);
 //            return;
 //        }
-        if (imageCropBean == null) {
+        if (isErrorVideo()) {
             G.showToast(R.string.need_picker_video);
             return;
         }
         jsonObject.put("resource_type", CreateUserVideoReq.TYPE_VIDEO);
         jsonObject.put("title", input);
-        jsonObject.put("duration", imageCropBean.getDuration() / 1000);
+        jsonObject.put("duration", video.getDuration() / 1000);
+        jsonObject.put("topic_id", video.getTopicId());
         inputAndUrlList.setInput(input);
         switch (uploadType) {
             case UPLOAD_TYPE_NONE:
@@ -188,14 +164,19 @@ public class ActPublishArticle extends BaseActivity {
         }
     }
 
+    //
+    private boolean isErrorVideo() {
+        return video == null || video.getFilePath() == null || !new File(video.getFilePath()).exists();
+    }
+
     private void uploadFile() {
         final DialogProgress dialogProgress = DialogProgress.showDialog(this);
         uploadType = UPLOAD_TYPE_START;
-        QiNiuUploadUtil.getInstance().uploadFile(imageCropBean.getOriginalPath()/*, QiNiuUploadUtil.TAG_VIDEO*/, new UpCompletionHandler() {
+        QiNiuUploadUtil.getInstance().uploadFile(video.getFilePath()/*, QiNiuUploadUtil.TAG_VIDEO*/, new UpCompletionHandler() {
             @Override
             public void complete(String key, ResponseInfo info, JSONObject response) {
                 LogUtil.println("qiniu", key, info, response);
-                if(info == null){
+                if (info == null) {
                     ViewUtil.setText(dialogProgress.getTextView(), "上传失败");
                     ViewUtil.dismiss(dialogProgress, 1);
                     uploadType = UPLOAD_TYPE_FAIL;
@@ -212,7 +193,7 @@ public class ActPublishArticle extends BaseActivity {
                         e.printStackTrace();
                     }
                     jsonObject.put("resource", url);
-                    jsonObject.put("cover", url + "?vframe/jpg/offset/0");
+                    jsonObject.put("cover", String.format(Locale.CHINA, "%s?vframe/jpg/offset/0&w=%d&h=%d", url, video.getWidth(), video.getHeight()));
                     publishArticle(dialogProgress);
                 } else {
                     ViewUtil.setText(dialogProgress.getTextView(), "上传失败");
@@ -222,12 +203,12 @@ public class ActPublishArticle extends BaseActivity {
                 }
                 LogUtil.println("qiniu", key + ",\r\n " + info + ",\r\n " + response);
             }
-        },new UpProgressHandler(){
-                    public void progress(String key, double percent){
-                        LogUtil.println("qiniu", key + ": " + percent);
-                        ViewUtil.setText(dialogProgress.getTextView(), String.format(Locale.CHINA, "%d%%", (int)(percent * 100)));
-                    }
-                });
+        }, new UpProgressHandler() {
+            public void progress(String key, double percent) {
+                LogUtil.println("qiniu", key + ": " + percent);
+                ViewUtil.setText(dialogProgress.getTextView(), String.format(Locale.CHINA, "%d%%", (int) (percent * 100)));
+            }
+        });
     }
 
     private com.alibaba.fastjson.JSONObject jsonObject = new com.alibaba.fastjson.JSONObject();
@@ -241,6 +222,13 @@ public class ActPublishArticle extends BaseActivity {
                     public void onNext(BaseMessage baseMessage) {
                         ViewUtil.setText(dialogProgress.getTextView(), "发布成功");
                         ViewUtil.dismiss(dialogProgress, 1);
+                        ViewUtil.delay(new AbsObserver<Integer>() {
+                            @Override
+                            public void onNext(Integer integer) {
+                                ActionUtil.getInstance().addNextAction(FgtArticleVideo.class.getSimpleName(), IRefresh.class.getSimpleName());
+                                ActPublishArticle.this.finish();
+                            }
+                        },1);
                     }
 
                     @Override
@@ -254,4 +242,17 @@ public class ActPublishArticle extends BaseActivity {
     @IntDef({MEDIA_TYPE_IMAGE, MEDIA_TYPE_VIDEO, MEDIA_TYPE_NONE})
     public @interface Type {
     }
+
+    @Override
+    protected boolean needRegisterEventBus() {
+        return true;
+    }
+    private Video video;
+    @Subscribe
+    public void onEventMainThread(Video data) {
+        this.video = data;
+        ListUtil.removeNull(inputAndUrlList.getList());
+        inputAndUrlList.getList().add(video.getFilePath());
+        ViewUtil.notifyDataSetChanged(recyclerView);
+    }
 }

+ 7 - 1
app/src/main/java/com/sheep/gamegroup/view/adapter/AdpVideo.java

@@ -4,6 +4,8 @@ import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
 import com.kfzs.appstore.utils.adapter.recyclerview.RecyclerViewAdapter;
 import com.kfzs.appstore.utils.adapter.recyclerview.ViewHolder;
 import com.kfzs.duanduan.utils.NumberFormatUtils;
@@ -13,6 +15,7 @@ import com.sheep.gamegroup.util.TimeUtil;
 import com.sheep.gamegroup.util.ViewUtil;
 import com.sheep.jiuyan.samllsheep.R;
 import com.sheep.jiuyan.samllsheep.SheepApp;
+import com.sheep.jiuyan.samllsheep.utils.G;
 
 import java.util.List;
 import java.util.Locale;
@@ -27,12 +30,15 @@ public class AdpVideo extends RecyclerViewAdapter<DiscoveryVideo> {
         super(SheepApp.getInstance(), layoutId, datas);
     }
 
+    private int width = (G.WIDTH - G.getRealPix(48)) / 2;
+
     @Override
     public void convert(ViewHolder viewHolder, final DiscoveryVideo item, int position) {
         ImageView item_video_cover = viewHolder.itemView.findViewById(R.id.item_video_cover);
         TextView item_video_play_no_tv = viewHolder.itemView.findViewById(R.id.item_video_play_no_tv);
         TextView item_video_time_tv = viewHolder.itemView.findViewById(R.id.item_video_time_tv);
-        ViewUtil.setImage(item_video_cover, item.getCover());
+        ViewUtil.setViewWH(item_video_cover, item.getCover(), width);
+        ViewUtil.setImage(item_video_cover, item.getCover(), G.getRealPix(5));
         ViewUtil.setVisibility(item_video_play_no_tv, item.getPlay() > 0);
         ViewUtil.setText(item_video_play_no_tv, String.format(Locale.CHINA, "%s次播放", NumberFormatUtils.getText(item.getPlay())));
         ViewUtil.setText(item_video_time_tv, TimeUtil.getDurationText(item.getDuration()));

+ 268 - 0
app/src/main/java/com/sheep/gamegroup/view/customview/VideoFramesView.java

@@ -0,0 +1,268 @@
+package com.sheep.gamegroup.view.customview;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.sheep.gamegroup.model.entity.Video;
+import com.sheep.gamegroup.util.LogUtil;
+import com.sheep.gamegroup.util.MediaMetadataRetrieverUtil;
+import com.sheep.jiuyan.samllsheep.R;
+
+import java.io.File;
+import java.util.Locale;
+
+import rx.functions.Action1;
+
+
+/**
+ * Created by realicing on 2018/11/29.
+ * realicing@sina.com
+ * 显示本地视频文件对应的视频帧
+ */
+public class VideoFramesView extends RelativeLayout {
+    public VideoFramesView(Context context) {
+        super(context);
+        initView();
+    }
+
+    public VideoFramesView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initView();
+    }
+
+    public VideoFramesView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        initView();
+    }
+
+    private RecyclerView recyclerView;
+    private View line_start;
+    private View line_end;
+
+    private void initView() {
+        LayoutInflater.from(getContext()).inflate(R.layout.custom_video_frames_view, this, true);
+        recyclerView = findViewById(R.id.recyclerView);
+        line_start = findViewById(R.id.line_start);
+        line_end = findViewById(R.id.line_end);
+        line_start.setOnTouchListener(new OnTouchListener() {
+            private float lastEventX;
+
+            @SuppressLint("ClickableViewAccessibility")
+            @Override
+            public boolean onTouch(View view, MotionEvent event) {
+                float maxX = line_end.getX();
+                float minX = maxX - (firstEndX - firstStartX);//结束位置 - 可以设置的长度
+                switch (event.getAction()) {
+                    case MotionEvent.ACTION_DOWN:
+                        lastEventX = event.getX();
+                        break;
+                    case MotionEvent.ACTION_MOVE:
+                        float mx = Math.max(firstStartX, view.getX() + event.getX() - lastEventX);
+                        if (mx > maxX)
+                            mx = maxX;
+                        if (mx < minX) {
+                            maxX += mx - minX;
+                            line_end.setX(maxX);
+                            view.setX(mx);
+                        } else {
+                            view.setX(mx);
+                        }
+                        if (onTimeChangeListener != null) {
+                            onTimeChangeListener.call((maxX - mx) * video.getDuration() / (rvW - lineW));
+                        }
+                        break;
+                    case MotionEvent.ACTION_UP:
+                        break;
+                    case MotionEvent.ACTION_CANCEL:
+                        break;
+                }
+                view.invalidate();
+                return true;
+            }
+        });
+        line_end.setOnTouchListener(new OnTouchListener() {
+            private float lastEventX;
+
+            @SuppressLint("ClickableViewAccessibility")
+            @Override
+            public boolean onTouch(View view, MotionEvent event) {
+                float minX = line_start.getX();
+                float maxX = firstEndX - firstStartX + minX;
+                float endX = firstStartX + rvW - lineW;
+                switch (event.getAction()) {
+                    case MotionEvent.ACTION_DOWN:
+                        lastEventX = event.getX();
+                        break;
+                    case MotionEvent.ACTION_MOVE:
+                        float mx = Math.min(view.getX() + event.getX() - lastEventX, endX);
+                        if (mx < minX)
+                            mx = minX;
+                        if (mx > maxX) {
+                            minX += mx - maxX;
+                            line_start.setX(minX);
+                            view.setX(mx);
+                        } else {
+                            view.setX(mx);
+                        }
+                        if (onTimeChangeListener != null) {
+                            onTimeChangeListener.call((mx - minX) * video.getDuration() / (rvW - lineW));
+                        }
+                        break;
+                    case MotionEvent.ACTION_UP:
+                        break;
+                    case MotionEvent.ACTION_CANCEL:
+                        break;
+                }
+                view.invalidate();
+                return true;
+            }
+        });
+    }
+
+    //开头线的进度
+    public float getStartPercent() {
+        if (line_start != null && recyclerView != null) {
+            return (line_start.getX() - firstStartX) / (rvW - lineW);
+        }
+        return 0;
+    }
+
+    private float firstStartX;//初始时开头的x位置
+    private float firstEndX;//初始时结尾的x位置
+    private int lineW;//开头与结尾线的宽度
+    private int rvW;//图片列表的长度,减去lineW后==总进度的长度
+
+    //结尾线的进度
+    public float getEndPercent() {
+        if (line_end != null && recyclerView != null) {
+            return 1.0f - ((firstStartX + rvW - lineW - line_end.getX()) / (rvW - lineW));
+        }
+        return 0;
+    }
+
+    //总的进度
+    public float getPercent() {
+        if (line_end != null && recyclerView != null) {
+            return (line_end.getX() - line_start.getX()) / (rvW - lineW);
+        }
+        return 0;
+    }
+
+    public String getLineInfo() {
+        return String.format(Locale.CHINA, "firstStartX = %f, firstEndX = %f, startX = %f, endX = %f, lineW = %d, rvW = %d, startPercent = %f, endPercent = %f, percent = %f", firstStartX, firstEndX, line_start.getX(), line_end.getX(), line_end.getWidth(), recyclerView.getWidth(), getStartPercent(), getEndPercent(), getPercent());
+    }
+
+    private MediaMetadataRetrieverUtil mediaMetadataRetrieverUtil;
+
+    public MediaMetadataRetrieverUtil getMediaMetadataRetrieverUtil() {
+        return mediaMetadataRetrieverUtil;
+    }
+
+    private Video video;
+    /**
+     * 显示视频帧图片列表
+     *
+     * @param data 视频数据,包括地址与时长,宽高等
+     * @return
+     */
+    public VideoFramesView initVideo(Video data) {
+        if (data == null || data.getFilePath() == null) {
+            LogUtil.println(VideoFramesView.class.getSimpleName(), "showList", "error", "videoPath is null");
+            return this;
+        }
+        File videoFile = new File(data.getFilePath());
+        if (!videoFile.exists()) {
+            LogUtil.println(VideoFramesView.class.getSimpleName(), "showList", "error", "videoFile not exists");
+            return this;
+        }
+        if (!videoFile.canRead()) {
+            LogUtil.println(VideoFramesView.class.getSimpleName(), "showList", "error", "videoFile cant read");
+            return this;
+        }
+        if (data.getDuration() < 1) {
+            LogUtil.println(VideoFramesView.class.getSimpleName(), "showList", "error", "duration < 1");
+            return this;
+        }
+        if (mediaMetadataRetrieverUtil == null) {
+            mediaMetadataRetrieverUtil = new MediaMetadataRetrieverUtil(data.getFilePath(), data.getDuration());
+        }
+        this.video = data;
+        return this;
+    }
+
+    /**
+     * 显示视频帧图片列表
+     */
+    public VideoFramesView showVideoList() {
+        if (mediaMetadataRetrieverUtil != null)
+            mediaMetadataRetrieverUtil.initRv(recyclerView);
+        return this;
+    }
+
+    private Action1<Float> onTimeChangeListener;
+
+    public void setOnTimeChangeListener(Action1<Float> action1) {
+        onTimeChangeListener = action1;
+        postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                firstStartX = line_start.getX();
+                firstEndX = line_end.getX();
+                lineW = line_start.getWidth();
+                rvW = recyclerView.getWidth();
+                long duration = mediaMetadataRetrieverUtil.getDuration();
+                if(maxVideoDuration > 0) {
+                    if(duration > maxVideoDuration) {
+                        firstEndX = firstStartX + ((maxVideoDuration * 1.0f / duration) * (rvW - lineW));
+                        line_end.setX(firstEndX);
+                    }
+                }
+                onTimeChangeListener.call((float) duration);
+            }
+        }, 100);
+    }
+
+    //开始的时间,单位秒
+    public long getStartPoint() {
+        return (long) (getStartPercent() * mediaMetadataRetrieverUtil.getDuration());
+    }
+
+    //总共的时长,单位秒
+    public long getLineDuration() {
+        return (long) (getPercent() * mediaMetadataRetrieverUtil.getDuration());
+    }
+    //结束的时间,单位秒
+    public long getEndPoint() {
+        return (long) (getEndPercent() * mediaMetadataRetrieverUtil.getDuration());
+    }
+
+    //最大剪切视频时长
+    private long maxVideoDuration;
+    public VideoFramesView setMaxDuration(long maxVideoDuration) {
+        this.maxVideoDuration = maxVideoDuration;
+        return this;
+    }
+    public void onDestroy(){
+        if(recyclerView != null && recyclerView.getAdapter() instanceof BaseQuickAdapter){
+            for (int i = 0; i < recyclerView.getAdapter().getItemCount(); i++) {
+                Object object = ((BaseQuickAdapter) recyclerView.getAdapter()).getItem(i);
+                if(object instanceof Bitmap){
+                    try {
+                        ((Bitmap) object).recycle();
+                    } catch (Exception e){
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+}

+ 8 - 1
app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment.java

@@ -98,7 +98,7 @@ public abstract class BaseListFragment<T> extends BaseFragment implements IRefre
     public void loadMoreData() {
         if (!loadMore) {
             loadMore = true;
-            if (ListUtil.size(list) >= per_page * page) {
+            if (hasMore()) {
                 page += 1;
                 initNetAndData();
             } else {
@@ -109,6 +109,13 @@ public abstract class BaseListFragment<T> extends BaseFragment implements IRefre
             view_list.loadMoreComplete();
         }
     }
+    //还有数据没有获取
+    protected boolean hasMore() {
+        if(lastMessage != null && lastMessage.getTotal() > 0){
+            return lastMessage.getTotal() > ListUtil.size(list);
+        }
+        return ListUtil.size(list) >= per_page * page;
+    }
 
     public void setNoMore(boolean noMore) {
         this.noMore = noMore;

+ 18 - 1
app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment2.java

@@ -3,6 +3,7 @@ package com.sheep.gamegroup.view.fragment;
 import android.app.Activity;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
 import android.view.View;
 
 import com.jcodecraeer.xrecyclerview.XRecyclerView;
@@ -108,7 +109,7 @@ public abstract class BaseListFragment2<T> extends BaseFragment implements IRefr
     public void loadMoreData() {
         if (!loadMore) {
             loadMore = true;
-            if (ListUtil.size(list) >= per_page * page) {
+            if (hasMore()) {
                 page += 1;
                 initData();
             } else {
@@ -119,6 +120,14 @@ public abstract class BaseListFragment2<T> extends BaseFragment implements IRefr
             view_list.loadMoreComplete();
         }
     }
+    //还有数据没有获取
+    protected boolean hasMore() {
+        if(lastMessage != null && lastMessage.getTotal() > 0){
+            return lastMessage.getTotal() > ListUtil.size(list);
+        }
+        return ListUtil.size(list) >= per_page * page;
+    }
+
 
     public void setNoMore(boolean noMore) {
         this.noMore = noMore;
@@ -270,4 +279,12 @@ public abstract class BaseListFragment2<T> extends BaseFragment implements IRefr
     public void notifyData(){
 
     }
+    @Override
+    public void doNextAction(Object action) {
+        if(action instanceof String) {
+            if(TextUtils.equals((String) action, IRefresh.class.getSimpleName())){
+                refreshData();
+            }
+        }
+    }
 }

+ 8 - 1
app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment3.java

@@ -88,7 +88,7 @@ public abstract class BaseListFragment3<T> extends BaseRefreshLoadMoreFragment{
 
     //加载更多数据
     public void loadMoreData() {
-        if (ListUtil.size(list) >= per_page * page) {
+        if (hasMore()) {
             page += 1;
             initData();
         } else {
@@ -96,6 +96,13 @@ public abstract class BaseListFragment3<T> extends BaseRefreshLoadMoreFragment{
             refreshOrLoadMoreComplete();
         }
     }
+    //还有数据没有获取
+    protected boolean hasMore() {
+        if(lastMessage != null && lastMessage.getTotal() > 0){
+            return lastMessage.getTotal() > ListUtil.size(list);
+        }
+        return ListUtil.size(list) >= per_page * page;
+    }
 
     protected List<T> list = ListUtil.emptyList();
     protected List<T> lastCacheList = ListUtil.emptyList();

+ 8 - 1
app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment4.java

@@ -55,13 +55,20 @@ public abstract class BaseListFragment4<T> extends BaseFragment {
 
     //加载更多数据
     public void loadMoreData() {
-        if (ListUtil.size(list) >= per_page * page) {
+        if (hasMore()) {
             page += 1;
             initData();
         } else {
             setNoMore(true);
         }
     }
+    //还有数据没有获取
+    protected boolean hasMore() {
+        if(lastMessage != null && lastMessage.getTotal() > 0){
+            return lastMessage.getTotal() > ListUtil.size(list);
+        }
+        return ListUtil.size(list) >= per_page * page;
+    }
 
     protected void setNoMore(boolean noMore) {
         if (noMore)

+ 8 - 1
app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment5.java

@@ -115,7 +115,7 @@ public abstract class BaseListFragment5<T> extends BaseFragment implements IRefr
     public void loadMoreData() {
         if (!loadMore) {
             loadMore = true;
-            if (ListUtil.size(list) >= per_page * page) {
+            if (hasMore()) {
                 page += 1;
                 initData();
             } else {
@@ -125,6 +125,13 @@ public abstract class BaseListFragment5<T> extends BaseFragment implements IRefr
             finishRefresh();
         }
     }
+    //还有数据没有获取
+    protected boolean hasMore() {
+        if(lastMessage != null && lastMessage.getTotal() > 0){
+            return lastMessage.getTotal() > ListUtil.size(list);
+        }
+        return ListUtil.size(list) >= per_page * page;
+    }
 
     protected void setNoMore(boolean noMore) {
         if (noMore) {

+ 41 - 39
app/src/main/java/com/sheep/gamegroup/view/fragment/BaseListFragment6.java

@@ -10,7 +10,7 @@ import com.chad.library.adapter.base.BaseQuickAdapter;
 import com.chad.library.adapter.base.BaseViewHolder;
 import com.chad.library.adapter.base.loadmore.LoadMoreView;
 import com.sheep.gamegroup.absBase.AbsObserver;
-import com.sheep.gamegroup.absBase.ApiRefresh;
+import com.sheep.gamegroup.absBase.IApiRefresh;
 import com.sheep.gamegroup.absBase.ILoadMore;
 import com.sheep.gamegroup.absBase.IRefresh;
 import com.sheep.gamegroup.util.ListUtil;
@@ -23,7 +23,6 @@ import com.sheep.jiuyan.samllsheep.base.BaseFragment;
 import java.util.ArrayList;
 import java.util.List;
 
-import butterknife.BindView;
 import rx.functions.Action1;
 
 /**
@@ -43,6 +42,8 @@ public abstract class BaseListFragment6<T> extends BaseFragment implements IRefr
     @Override
     public void onViewCreated() {
         activity = getActivity();
+        findView();
+        addApiRefresh(apiRefreshList);
         initView();
         initListener();
         switch (refreshDataType()) {
@@ -53,28 +54,33 @@ public abstract class BaseListFragment6<T> extends BaseFragment implements IRefr
                 break;
             case REFRESH_ON_YOURSELF:
             default:
-                notifyDataSetChanged();
+                notifyDataSetChanged(1, true);
                 break;
         }
     }
 
-    protected List<ApiRefresh<? extends Object>> apiRefreshList = new ArrayList<>();
+    protected abstract void addApiRefresh(List<IApiRefresh> apiRefreshList);
+
+    protected List<IApiRefresh> apiRefreshList = new ArrayList<>();
 
     public static final int REFRESH_ON_CREATE = 0;
     public static final int REFRESH_ON_RESUME = 1;
     public static final int REFRESH_ON_YOURSELF = -1;
-    @BindView(R.id.title)
-    View title;
-    @BindView(R.id.check_net_ll)
-    View check_net_ll;
-    @BindView(R.id.swipeRefreshLayout)
-    SwipeRefreshLayout swipeRefreshLayout;
-    @BindView(R.id.recyclerView)
-    RecyclerView recyclerView;
+    protected View title;
+    protected View check_net_ll;
+    protected SwipeRefreshLayout swipeRefreshLayout;
+    protected RecyclerView recyclerView;
 
     protected BaseQuickAdapter<T, BaseViewHolder> baseQuickAdapter;
 
 
+    public void findView() {
+        title = findViewById(R.id.title);
+        check_net_ll = findViewById(R.id.check_net_ll);
+        swipeRefreshLayout = findViewById(R.id.swipeRefreshLayout);
+        recyclerView = findViewById(R.id.recyclerView);
+    }
+
     public void initView() {
         ViewUtil.setVisibility(title, false);
     }
@@ -129,25 +135,14 @@ public abstract class BaseListFragment6<T> extends BaseFragment implements IRefr
     }
 
     public void loadMoreData() {
-        ListUtil.forEach(apiRefreshList, new Action1<ApiRefresh<? extends Object>>() {
+        ListUtil.forEach(apiRefreshList, new Action1<IApiRefresh>() {
             @Override
-            public void call(ApiRefresh<?> apiRefresh) {
-                if (apiRefresh.loadMoreData()) {
-                    finishRefresh();
-                }
+            public void call(IApiRefresh apiRefresh) {
+                apiRefresh.loadMoreData();
             }
         });
     }
 
-    public void setNoMore(boolean noMore) {
-        if (noMore) {
-            baseQuickAdapter.loadMoreEnd();
-        } else {
-            baseQuickAdapter.loadMoreComplete();
-        }
-        finishRefresh();
-    }
-
     @Override
     public void onResume() {
         super.onResume();
@@ -168,23 +163,23 @@ public abstract class BaseListFragment6<T> extends BaseFragment implements IRefr
     public void refreshData() {
         checkMetLl();
         clear();
-        ListUtil.forEach(apiRefreshList, new Action1<ApiRefresh<? extends Object>>() {
+        ListUtil.forEach(apiRefreshList, new Action1<IApiRefresh>() {
             @Override
-            public void call(ApiRefresh<?> apiRefresh) {
+            public void call(IApiRefresh apiRefresh) {
                 apiRefresh.clear();
             }
         });
         ViewUtil.notifyDataSetChanged(recyclerView);
-        ListUtil.forEach(apiRefreshList, new Action1<ApiRefresh<? extends Object>>() {
+        ListUtil.forEach(apiRefreshList, new Action1<IApiRefresh>() {
             @Override
-            public void call(ApiRefresh<?> apiRefresh) {
+            public void call(IApiRefresh apiRefresh) {
                 apiRefresh.initData();
             }
         });
     }
 
     protected void clear() {
-        setNoMore(false);
+        notifyDataSetChanged(1, true);
         initLoadMoreListener();
     }
 
@@ -199,23 +194,30 @@ public abstract class BaseListFragment6<T> extends BaseFragment implements IRefr
         }
     }
 
-    public void notifyDataSetChanged() {
+    public void notifyDataSetChanged(int page, boolean hasMore) {
         if (recyclerView == null)
             recyclerView = findViewById(R.id.recyclerView);
-        ListUtil.forEach(apiRefreshList, new Action1<ApiRefresh<? extends Object>>() {
+        ListUtil.forEach(apiRefreshList, new Action1<IApiRefresh>() {
             @Override
-            public void call(ApiRefresh<?> apiRefresh) {
+            public void call(IApiRefresh apiRefresh) {
                 apiRefresh.setLoadMore(false);
             }
         });
         ViewUtil.notifyDataSetChanged(recyclerView);
-        finishRefresh();
+        if (hasMore) {
+            switch (page) {
+                case 1:
+                    if (swipeRefreshLayout != null)
+                        swipeRefreshLayout.setRefreshing(false);
+                default:
+                    baseQuickAdapter.loadMoreComplete();
+                    break;
+            }
+        } else {
+            baseQuickAdapter.loadMoreEnd(false);
+        }
     }
 
-    protected void finishRefresh() {
-        if (swipeRefreshLayout != null)
-            swipeRefreshLayout.setRefreshing(false);
-    }
 
     @Override
     public void onDestroyView() {

+ 0 - 1
app/src/main/java/com/sheep/gamegroup/view/fragment/FgtArticleVideo.java

@@ -76,5 +76,4 @@ public class FgtArticleVideo extends BaseListFragment2<DiscoveryVideo> {
         }
     }
 
-
 }

+ 83 - 0
app/src/main/java/com/sheep/gamegroup/view/fragment/FgtDiscoveryTopic.java

@@ -0,0 +1,83 @@
+package com.sheep.gamegroup.view.fragment;
+
+import android.view.View;
+
+import com.chad.library.adapter.base.BaseQuickAdapter;
+import com.chad.library.adapter.base.BaseViewHolder;
+import com.sheep.gamegroup.absBase.IApiRefresh;
+import com.sheep.gamegroup.absBase.NetApiRefresh;
+import com.sheep.gamegroup.model.api.ApiService;
+import com.sheep.gamegroup.model.entity.BaseMessage;
+import com.sheep.gamegroup.model.entity.DiscoveryTopic;
+import com.sheep.jiuyan.samllsheep.R;
+
+import org.afinal.simplecache.ApiKey;
+import org.greenrobot.eventbus.EventBus;
+
+import java.util.List;
+
+import io.reactivex.Observable;
+
+/**
+ * Created by realicing on 2018/11/29.
+ * realicing@sina.com
+ * 小绵羊3.4.5新增 -- 为视频添加话题,话题列表
+ */
+public class FgtDiscoveryTopic extends BaseListFragment6<DiscoveryTopic> {
+
+    @Override
+    public int getLayoutId() {
+        return R.layout.common_srl_rv;
+    }
+
+    private NetApiRefresh<DiscoveryTopic> mApiRefresh;
+    @Override
+    protected void addApiRefresh(List<IApiRefresh> apiRefreshList) {
+        mApiRefresh = new NetApiRefresh<DiscoveryTopic>(this) {
+            @Override
+            protected boolean hasMore() {
+                return false;
+            }
+
+            @Override
+            public String getKey(int page, int per_page) {
+                return ApiKey.getVideoTopic;
+            }
+
+            @Override
+            public Observable<BaseMessage> getApi(ApiService apiService) {
+                return apiService.getVideoTopic();
+            }
+
+            @Override
+            public Class<DiscoveryTopic> getTClass() {
+                return DiscoveryTopic.class;
+            }
+        };
+        apiRefreshList.add(mApiRefresh);
+    }
+
+    @Override
+    protected BaseQuickAdapter<DiscoveryTopic, BaseViewHolder> getAdapter() {
+        return new BaseQuickAdapter<DiscoveryTopic, BaseViewHolder>(R.layout.item_discovery_topic, mApiRefresh.getList()) {
+            @Override
+            protected void convert(BaseViewHolder helper, DiscoveryTopic item) {
+                helper.setText(R.id.item_discovery_topic_content, item.getTopic());
+            }
+        };
+    }
+
+    @Override
+    public void initListener() {
+        super.initListener();
+        baseQuickAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
+            @Override
+            public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
+                DiscoveryTopic item = mApiRefresh.getItem(position);
+                if(item != null) {
+                    EventBus.getDefault().post(item);
+                }
+            }
+        });
+    }
+}

+ 6 - 6
app/src/main/java/com/sheep/gamegroup/view/fragment/FgtUserFocusLogList.java

@@ -9,7 +9,8 @@ import android.widget.TextView;
 
 import com.chad.library.adapter.base.BaseQuickAdapter;
 import com.chad.library.adapter.base.BaseViewHolder;
-import com.sheep.gamegroup.absBase.ApiRefresh;
+import com.sheep.gamegroup.absBase.IApiRefresh;
+import com.sheep.gamegroup.absBase.NetApiRefresh;
 import com.sheep.gamegroup.model.api.ApiService;
 import com.sheep.gamegroup.model.entity.BaseMessage;
 import com.sheep.gamegroup.model.entity.UserFocusLog;
@@ -51,10 +52,10 @@ public class FgtUserFocusLogList extends BaseListFragment6<String> {
     };
 
     @Override
-    public void initView() {
+    protected void addApiRefresh(List<IApiRefresh> apiRefreshList) {
         tagList.add("1");
         tagList.add("2");
-        ApiRefresh<UserFocusLog> apiRefresh1 = new ApiRefresh<UserFocusLog>(this) {
+        NetApiRefresh<UserFocusLog> apiRefresh1 = new NetApiRefresh<UserFocusLog>(this) {
             @Override
             public String getKey(int page, int per_page) {
                 return ApiKey.pageKeyUrl(ApiKey.getGameUserUserFocusList, page, per_page);
@@ -67,10 +68,10 @@ public class FgtUserFocusLogList extends BaseListFragment6<String> {
 
             @Override
             public Class<UserFocusLog> getTClass() {
-                return null;
+                return UserFocusLog.class;
             }
         };
-        ApiRefresh<UserFocusLog> apiRefresh2 = new ApiRefresh<UserFocusLog>(this) {
+        NetApiRefresh<UserFocusLog> apiRefresh2 = new NetApiRefresh<UserFocusLog>(this) {
             @Override
             public String getKey(int page, int per_page) {
                 return ApiKey.pageKeyUrl(ApiKey.getGameUserUserFocusHotUser, page, per_page);
@@ -108,7 +109,6 @@ public class FgtUserFocusLogList extends BaseListFragment6<String> {
         adpUserFocusLogList2 = new AdpUserFocusLogList(apiRefresh2.getList());
         adpUserFocusLogList.setAction1(action1);
         adpUserFocusLogList2.setAction1(action1);
-        super.initView();
     }
 
     private View headerView;

+ 34 - 0
app/src/main/java/com/sheep/jiuyan/samllsheep/base/BaseFragment.java

@@ -7,11 +7,18 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.sheep.gamegroup.absBase.AbsObserver;
+import com.sheep.gamegroup.util.ActionUtil;
 import com.sheep.gamegroup.view.dialog.DialogLoading;
 import com.trello.rxlifecycle2.components.support.RxFragment;
 
+import java.util.concurrent.TimeUnit;
+
 import butterknife.ButterKnife;
 import butterknife.Unbinder;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
 
 /**
  * @ Created by Dlg
@@ -163,4 +170,31 @@ public abstract class BaseFragment extends RxFragment {
     public boolean userButterKnife() {
         return true;
     }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        checkNextAction(this.getClass().getSimpleName());
+    }
+
+    protected boolean checkNextAction(String key) {
+        final Object action = ActionUtil.getInstance().getAction(key);
+        if (action != null) {
+            //延时一会儿,不然有可能操作会无效
+            Observable.just(1).delay(50L, TimeUnit.MILLISECONDS)
+                    .subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(new AbsObserver<Integer>() {
+                        @Override
+                        public void onNext(Integer integer) {
+                            doNextAction(action);
+                        }
+                    });
+            return true;
+        }
+        return false;
+    }
+    public void doNextAction(Object action) {
+
+    }
 }

+ 23 - 4
app/src/main/java/com/sheep/jiuyan/samllsheep/service/DownloadService.java

@@ -26,6 +26,7 @@ import com.liulishuo.okdownload.core.listener.DownloadListener4WithSpeed;
 import com.liulishuo.okdownload.core.listener.assist.Listener4SpeedAssistExtend;
 import com.sheep.gamegroup.event.BigEvent;
 import com.sheep.gamegroup.event.EventTypes;
+import com.sheep.gamegroup.greendao.DDProviderHelper;
 import com.sheep.gamegroup.greendao.download.DownLoadInfo;
 import com.sheep.gamegroup.model.entity.Applications;
 import com.sheep.gamegroup.model.entity.TaskEty;
@@ -76,6 +77,7 @@ public class DownloadService extends Service {
         @Override
         public void taskStart(@NonNull DownloadTask task) {
             LogUtil.println("DownloadListener", "taskStart");
+            DDProviderHelper.getInstance().updateDownload(task.getId(), task.getUrl(), DownloadUtil.STATUS_INIT);
         }
 
         @Override
@@ -125,19 +127,36 @@ public class DownloadService extends Service {
                         EventBus.getDefault().post(new BigEvent().setEventTypes(EventTypes.DOWNLOAD_COMPLETE).setData(downLoadInfo));
                     }
                     break;
-                case FILE_BUSY:
                 case PRE_ALLOCATE_FAILED://预先分配失败
-                case ERROR://下载错误
-                    downLoadInfo = downloadUtil.setDownloadTaskStatus(task.getUrl(), DownloadUtil.STATUS_FAIL);
+                    //空间不足,弹框提示
+                    try {
+                        DialogStorageLow.showDialog(realCause != null ? realCause.getMessage() : null);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                    downLoadInfo = downloadUtil.getDownloadTaskByUrl(task.getUrl());
+                    downLoadInfo.setMStatus(DownloadUtil.STATUS_FAIL);
+                    if(realCause != null)
+                        downLoadInfo.setRealCauseMsg(realCause.getMessage());
+                    DDProviderHelper.getInstance().updateDownload(downLoadInfo);
                     EventBus.getDefault().post(new BigEvent().setEventTypes(EventTypes.DOWNLOAD_FAIL).setData(downLoadInfo));
+                    break;
+                case FILE_BUSY:
+                case ERROR://下载错误
                     //空间不足,弹框提示
                     try {
                         if (0 <= task.getReadBufferSize() && task.getReadBufferSize() > G.getFreeSpaceB()) {
-                            DialogStorageLow.showDialog(DownloadService.this);
+                            DialogStorageLow.showDialog(realCause != null ? realCause.getMessage() : null);
                         }
                     } catch (Exception e) {
                         e.printStackTrace();
                     }
+                    downLoadInfo = downloadUtil.getDownloadTaskByUrl(task.getUrl());
+                    downLoadInfo.setMStatus(DownloadUtil.STATUS_FAIL);
+                    if(realCause != null)
+                        downLoadInfo.setRealCauseMsg(realCause.getMessage());
+                    DDProviderHelper.getInstance().updateDownload(downLoadInfo);
+                    EventBus.getDefault().post(new BigEvent().setEventTypes(EventTypes.DOWNLOAD_FAIL).setData(downLoadInfo));
                     break;
                 case CANCELED://取消下载
                     downLoadInfo = downloadUtil.setDownloadTaskStatus(task.getUrl(), DownloadUtil.STATUS_PAUSE);

+ 1 - 1
app/src/main/java/com/sheep/jiuyan/samllsheep/utils/G.java

@@ -239,7 +239,7 @@ public class G {
                     file.length() > getFreeSpaceB() / 2
                             && file.length() > getSdkFreeSpace() / 2
             )) {
-                DialogStorageLow.showDialog(context);
+                DialogStorageLow.showDialog(null);
             }
         }catch (Exception e){
             e.printStackTrace();

+ 6 - 0
app/src/main/res/drawable/shape_oval_solid_10_f5.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <corners android:radius="10dp" />
+    <solid android:color="#fff5f5f5" />
+</shape>

+ 6 - 0
app/src/main/res/drawable/shape_oval_solid_10_main.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <corners android:radius="10dp" />
+    <solid android:color="@color/btn_color_main_stroke" />
+</shape>

+ 6 - 0
app/src/main/res/drawable/shape_white_20_solid_rectangle_15.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="15dp" />
+    <solid android:color="#33ffffff" />
+</shape>

+ 10 - 0
app/src/main/res/layout/act_cut_video.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#000000"
+    tools:context="com.sheep.gamegroup.view.activity.ActEditVideo">
+
+</android.support.constraint.ConstraintLayout>

+ 109 - 0
app/src/main/res/layout/act_edit_video.xml

@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#000000"
+    tools:context="com.sheep.gamegroup.view.activity.ActEditVideo">
+
+    <VideoView
+        android:id="@+id/videoView"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#99000000" />
+
+    <com.github.ybq.android.spinkit.SpinKitView
+        android:id="@+id/video_loading"
+        style="@style/SpinKitView.Large.ThreeBounce"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:SpinKit_Color="@color/colorAccent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <ImageView
+        android:id="@+id/video_back_iv"
+        android:layout_width="?attr/actionBarSize"
+        android:layout_height="?attr/actionBarSize"
+        android:layout_marginTop="20dp"
+        android:onClick="onClickBackImg"
+        android:scaleType="centerInside"
+        android:src="@drawable/narrow_back_white"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <TextView
+        android:id="@+id/edit_video_topic_tv"
+        android:layout_width="0dp"
+        android:layout_height="30dp"
+        android:background="@drawable/shape_white_20_solid_rectangle_15"
+        android:gravity="center|start"
+        android:maxLength="100"
+        android:onClick="onClickTopic"
+        android:paddingStart="18dp"
+        android:paddingEnd="18dp"
+        android:text="#添加话题#"
+        android:textColor="#FEFFFF"
+        android:textSize="13sp"
+        app:layout_constraintBottom_toBottomOf="@id/video_back_iv"
+        app:layout_constraintEnd_toStartOf="@id/edit_video_sure_tv"
+        app:layout_constraintStart_toEndOf="@id/video_back_iv"
+        app:layout_constraintTop_toTopOf="@id/video_back_iv" />
+
+    <TextView
+        android:id="@+id/edit_video_sure_tv"
+        android:layout_width="75dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="31dp"
+        android:layout_marginEnd="16dp"
+        android:background="@drawable/selector_button_full_main"
+        android:gravity="center"
+        android:onClick="onClickSure"
+        android:paddingTop="6dp"
+        android:paddingBottom="6dp"
+        android:text="下一步"
+        android:textColor="#FEFFFF"
+        android:textSize="15sp"
+        app:layout_constraintBottom_toBottomOf="@id/video_back_iv"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@id/edit_video_topic_tv"
+        app:layout_constraintTop_toTopOf="@id/video_back_iv" />
+
+    <FrameLayout
+        android:id="@+id/frame_container"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginTop="27dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/edit_video_topic_tv" />
+
+    <com.sheep.gamegroup.view.customview.VideoFramesView
+        android:id="@+id/videoFramesView"
+        android:layout_width="match_parent"
+        android:layout_height="69dp"
+        android:layout_marginStart="36dp"
+        android:layout_marginEnd="36dp"
+        android:layout_marginBottom="17dp"
+        app:layout_constraintBottom_toBottomOf="parent" />
+
+    <TextView
+        android:id="@+id/edit_video_time_tv"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="15dp"
+        android:layout_marginBottom="100dp"
+        android:text="@string/has_choose_x_second"
+        android:textColor="#ffffffff"
+        android:textSize="12sp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+</android.support.constraint.ConstraintLayout>

+ 22 - 0
app/src/main/res/layout/act_media_choose.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/white"
+    android:orientation="vertical">
+
+    <include layout="@layout/title" />
+
+    <android.support.design.widget.TabLayout
+        android:id="@+id/indicator"
+        style="@style/style_tab"
+        android:layout_marginTop="7dp" />
+
+    <include layout="@layout/line_1px_hor" />
+
+    <android.support.v4.view.ViewPager
+        android:id="@+id/viewPager"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1" />
+</LinearLayout>

+ 59 - 0
app/src/main/res/layout/custom_video_frames_view.xml

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="69dp"
+    android:layout_marginStart="36dp"
+    android:layout_marginEnd="36dp"
+    android:layout_marginBottom="17dp"
+    app:layout_constraintBottom_toBottomOf="parent">
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="61dp"
+        android:layout_marginTop="4dp"
+        android:layout_marginBottom="4dp"
+        android:background="#000" />
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/recyclerView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginStart="12dp"
+        android:layout_marginTop="8dp"
+        android:layout_marginEnd="12dp"
+        android:layout_marginBottom="8dp"
+        android:orientation="horizontal"
+        app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
+
+    <View
+        android:id="@+id/line_start"
+        android:layout_width="6dp"
+        android:layout_height="match_parent"
+        android:layout_alignStart="@id/recyclerView"
+        android:background="@drawable/shape_white_solid_rectangle_5" />
+
+    <View
+        android:id="@+id/line_end"
+        android:layout_width="6dp"
+        android:layout_height="match_parent"
+        android:layout_alignEnd="@id/recyclerView"
+        android:background="@drawable/shape_white_solid_rectangle_5" />
+
+    <include
+        layout="@layout/include_lll"
+        android:layout_width="5dp"
+        android:layout_height="20dp"
+        android:layout_alignParentEnd="true"
+        android:layout_centerVertical="true"
+        android:layout_marginEnd="3dp" />
+
+    <include
+        layout="@layout/include_lll"
+        android:layout_width="5dp"
+        android:layout_height="20dp"
+        android:layout_alignParentStart="true"
+        android:layout_centerVertical="true"
+        android:layout_marginStart="3dp" />
+
+</RelativeLayout>

+ 34 - 0
app/src/main/res/layout/include_lll.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal">
+
+    <View
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:background="#B0AFAF" />
+
+    <View
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1" />
+
+    <View
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:background="#B0AFAF" />
+
+    <View
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1" />
+
+    <View
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:background="#B0AFAF" />
+</LinearLayout>

+ 20 - 0
app/src/main/res/layout/item_discovery_topic.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <TextView
+        android:id="@+id/item_discovery_topic_content"
+        android:layout_width="wrap_content"
+        android:layout_height="30dp"
+        android:layout_centerHorizontal="true"
+        android:layout_margin="10dp"
+        android:background="@drawable/shape_white_20_solid_rectangle_15"
+        android:gravity="center"
+        android:maxLength="100"
+        android:paddingStart="18dp"
+        android:paddingEnd="18dp"
+        android:text="#添加话题#"
+        android:textColor="#FEFFFF"
+        android:textSize="13sp" />
+</RelativeLayout>

+ 6 - 0
app/src/main/res/layout/item_iv_mh.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/item_iv"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:scaleType="fitXY" />

+ 29 - 0
app/src/main/res/layout/item_media_picker.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <ImageView
+        android:id="@+id/item_media_picker_iv"
+        android:layout_width="match_parent"
+        android:layout_marginStart="8dp"
+        android:layout_marginEnd="8dp"
+        android:layout_marginTop="6dp"
+        android:layout_marginBottom="6dp"
+        android:layout_height="wrap_content"
+        android:adjustViewBounds="true"
+        android:scaleType="fitXY" />
+
+    <TextView
+        android:id="@+id/item_media_picker_tv"
+        android:layout_width="20dp"
+        android:layout_height="20dp"
+        android:layout_alignTop="@id/item_media_picker_iv"
+        android:layout_alignEnd="@id/item_media_picker_iv"
+        android:layout_margin="10dp"
+        android:background="@drawable/shape_oval_solid_10_f5"
+        android:gravity="center"
+        android:text="√"
+        android:textColor="@color/yellow_D3AF57"
+        android:textSize="12sp" />
+</RelativeLayout>

+ 4 - 4
app/src/main/res/layout/line_1px_hor.xml

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <View xmlns:android="http://schemas.android.com/apk/res/android"
-      android:layout_width="match_parent"
-      android:layout_height="1px"
-      android:background="@color/gray_5">
-</View>
+    android:id="@+id/below_tab_line"
+    android:layout_width="match_parent"
+    android:layout_height="1px"
+    android:background="@color/gray_5"/>

BIN
app/src/main/res/mipmap-xxhdpi/ic_open_camera.webp


+ 1 - 0
app/src/main/res/values/colors.xml

@@ -9,5 +9,6 @@
     <color name="green">#4cd117</color>
 
     <color name="bg_class_grey">#f9f9f9</color>
+    <color name="gray_CBCBCB">#cbcbcb</color>
 
 </resources>

+ 2 - 0
app/src/main/res/values/strings.xml

@@ -141,4 +141,6 @@
     <string name="hot_user">热门玩家</string>
     <!--小绵羊3.4.5新增功能 发布发现-->
     <string name="please_say_something">说点什么~</string>
+    <string name="jin_x_jin">#%s#</string>
+    <string name="has_choose_x_second">已选取 %s秒</string>
 </resources>

+ 1 - 1
settings.gradle

@@ -1 +1 @@
-include ':app', ':view', ':ucrop', ':WaterWaveProgress', ':RxGalleryFinal'//, ':Aria', ':datashare', ':AriaAnnotations'
+include ':app', ':view', ':ucrop', ':WaterWaveProgress'//, ':RxGalleryFinal', ':Aria', ':datashare', ':AriaAnnotations'

+ 6 - 4
view/src/main/java/com/kfzs/android/view/widget/WrapContentHeightViewPager.java

@@ -45,10 +45,12 @@ public class WrapContentHeightViewPager extends ViewPager {
             @Override
             public void onPageSelected(int position) {
                 View view = getChildAt(position);
-                int height = view.getMeasuredHeight();
-                ViewGroup.LayoutParams layoutParams = getLayoutParams();
-                layoutParams.height = height;
-                setLayoutParams(layoutParams);
+                if(view != null) {
+                    int height = view.getMeasuredHeight();
+                    ViewGroup.LayoutParams layoutParams = getLayoutParams();
+                    layoutParams.height = height;
+                    setLayoutParams(layoutParams);
+                }
             }
 
             @Override