Bläddra i källkod

修改部分方法名称,添加云教练录制视频保存到相册

Pq 2 år sedan
förälder
incheckning
f778834731

+ 107 - 10
BaseLibrary/src/main/java/com/cooleshow/base/utils/FileUtils.java

@@ -2,12 +2,14 @@ package com.cooleshow.base.utils;
 
 import android.app.Application;
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.media.MediaScannerConnection;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Environment;
@@ -18,11 +20,8 @@ import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Log;
 
-import com.luck.picture.lib.tools.BitmapUtils;
-
-import org.w3c.dom.Text;
-
 import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -34,6 +33,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URL;
+import java.nio.file.Files;
 import java.security.DigestInputStream;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -1656,7 +1656,7 @@ public final class FileUtils {
         }
         String targetFileName = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()) + ".png";
         File file = new File(parentFile, targetFileName);
-        FileUtils.saveImagToGallery(bitmap, file.getAbsolutePath());
+        FileUtils.saveImag(bitmap, file.getAbsolutePath());
         if (file != null && file.exists()) {
             try {
                 MediaStore.Images.Media.insertImage(Utils.getApp().getContentResolver(),
@@ -1672,11 +1672,11 @@ public final class FileUtils {
     }
 
     /**
-     * 保存图片到图库
+     * 保存图片
      *
      * @param bmp
      */
-    public static void saveImagToGallery(Bitmap bmp, String path) throws Exception {
+    public static void saveImag(Bitmap bmp, String path) throws Exception {
         // 首先保存图片
         File file = new File(path);
         FileOutputStream fos = new FileOutputStream(file);
@@ -1727,7 +1727,7 @@ public final class FileUtils {
      *
      * @param bmp
      */
-    public static void saveImageToGallery(Bitmap bmp, String path) {
+    public static void saveImageToLocal(Bitmap bmp, String path) {
         // 首先保存图片
         File file = new File(path);
         try {
@@ -1753,12 +1753,12 @@ public final class FileUtils {
 
     public static boolean isMatchTargetFileType(String targetType, String filePath) {
         try {
-            Log.i("FileUtils","filePath:"+filePath);
+            Log.i("FileUtils", "filePath:" + filePath);
             if (TextUtils.isEmpty(targetType)) {
                 Log.i("FileUtils", "targetType is null");
                 return true;
             }
-            if (TextUtils.equals(targetType,"video") || TextUtils.equals(targetType,"img")) {
+            if (TextUtils.equals(targetType, "video") || TextUtils.equals(targetType, "img")) {
                 //忽略video或者img格式
                 Log.i("FileUtils", "video or img type is ignore");
                 return true;
@@ -1789,4 +1789,101 @@ public final class FileUtils {
         }
         return true;
     }
+
+    public static boolean saveVideoToGallery(Context context, String filePath) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+            return saveVideoToAlbumBeforeQ(context, filePath);
+        } else {
+            return saveVideoToAlbumAfterQ(context, filePath);
+        }
+    }
+
+    private static boolean saveVideoToAlbumAfterQ(Context context, String videoFile) {
+        try {
+            ContentResolver contentResolver = context.getContentResolver();
+            File tempFile = new File(videoFile);
+            ContentValues contentValues = getVideoContentValues(context, tempFile, System.currentTimeMillis());
+            Uri uri = contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues);
+            copyFileAfterQ(context, contentResolver, tempFile, uri);
+            contentValues.clear();
+            contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
+            context.getContentResolver().update(uri, contentValues, null, null);
+            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 获取视频的contentValue
+     */
+    public static ContentValues getVideoContentValues(Context context, File paramFile, long timestamp) {
+        ContentValues localContentValues = new ContentValues();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            localContentValues.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM
+                    + File.separator + context.getPackageName());
+        }
+        localContentValues.put(MediaStore.Video.Media.TITLE, paramFile.getName());
+        localContentValues.put(MediaStore.Video.Media.DISPLAY_NAME, paramFile.getName());
+        localContentValues.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
+        localContentValues.put(MediaStore.Video.Media.DATE_TAKEN, timestamp);
+        localContentValues.put(MediaStore.Video.Media.DATE_MODIFIED, timestamp);
+        localContentValues.put(MediaStore.Video.Media.DATE_ADDED, timestamp);
+        localContentValues.put(MediaStore.Video.Media.SIZE, paramFile.length());
+        return localContentValues;
+    }
+
+    private static void copyFileAfterQ(Context context, ContentResolver localContentResolver, File tempFile, Uri localUri) throws IOException {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&
+                context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q) {
+            //拷贝文件到相册的uri,android10及以上得这么干,否则不会显示。可以参考ScreenMediaRecorder的save方法
+            OutputStream os = localContentResolver.openOutputStream(localUri);
+            Files.copy(tempFile.toPath(), os);
+            os.close();
+            tempFile.delete();
+        }
+    }
+
+    private static boolean saveVideoToAlbumBeforeQ(Context context, String videoFile) {
+        File picDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
+        File tempFile = new File(videoFile);
+        File destFile = new File(picDir, context.getPackageName() + File.separator + tempFile.getName());
+        FileInputStream ins = null;
+        BufferedOutputStream ous = null;
+        try {
+            ins = new FileInputStream(tempFile);
+            ous = new BufferedOutputStream(new FileOutputStream(destFile));
+            long nread = 0L;
+            byte[] buf = new byte[1024];
+            int n;
+            while ((n = ins.read(buf)) > 0) {
+                ous.write(buf, 0, n);
+                nread += n;
+            }
+            MediaScannerConnection.scanFile(
+                    context,
+                    new String[]{destFile.getAbsolutePath()},
+                    new String[]{"video/*"},
+                    (path, uri) -> {
+                        // Scan Completed
+                    });
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        } finally {
+            try {
+                if (ins != null) {
+                    ins.close();
+                }
+                if (ous != null) {
+                    ous.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
 }

+ 91 - 0
BaseLibrary/src/main/java/com/cooleshow/base/utils/MediaMuxerUtils.java

@@ -0,0 +1,91 @@
+package com.cooleshow.base.utils;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Author by pq, Date on 2022/12/6.
+ */
+public class MediaMuxerUtils {
+    public static final String TAG = "MediaMuxerUtils";
+
+
+    public void start(String inputFilePath) {
+        MediaExtractor mediaExtractor = new MediaExtractor();
+        try {
+            //2.设置数据源
+            mediaExtractor.setDataSource(inputFilePath);
+            //3. 获取轨道数
+            int trackCount = mediaExtractor.getTrackCount();
+            Log.i(TAG, "demuxerMP4: trackCount=" + trackCount);
+            //遍历轨道,查看音频轨或者视频轨道信息
+            for (int i = 0; i < trackCount; i++) {
+                //4. 获取某一轨道的媒体格式
+                MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
+                String keyMime = trackFormat.getString(MediaFormat.KEY_MIME);
+                Log.i(TAG, "demuxerMp4: keyMime=" + keyMime);
+                if (TextUtils.isEmpty(keyMime)) {
+                    continue;
+                }
+                //5.通过mime信息识别音轨或视频轨道,打印相关信息
+                if (keyMime.startsWith("video/")) {
+                    //打印视频的宽高
+                    Log.i(TAG, "extractorAndMuxerMP4:                     videoWidth=" + trackFormat.getInteger(MediaFormat.KEY_WIDTH) + " videoHeight=" + trackFormat.getInteger(MediaFormat.KEY_HEIGHT));
+
+                } else if (keyMime.startsWith("audio/")) {
+                    //打印音轨的通道数以及比特率
+                    Log.i(TAG, "extractorAndMuxerMP4: channelCount=" + trackFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) + " bitRate=" + trackFormat.getInteger(MediaFormat.KEY_BIT_RATE));
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            mediaExtractor.release();
+        }
+    }
+
+    public void mix(String outputFile){
+
+//        MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
+//        MediaMuxer mediaMuxer;
+//        mediaExtractor.selectTrack(i);
+//
+//        //1. 构造MediaMuxer
+//        mediaMuxer = new MediaMuxer(outputFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+//        //2. 添加轨道信息 参数为MediaFormat
+//        mediaMuxer.addTrack(trackFormat);
+//        //3. 开始合成
+//        mediaMuxer.start();
+//
+//        //4. 设置buffer
+//        ByteBuffer buffer = ByteBuffer.allocate(500 * 1024);
+//        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+//
+//        //5.通过mediaExtractor.readSampleData读取数据流
+//        int sampleSize = 0;
+//        while ((sampleSize = mediaExtractor.readSampleData(buffer, 0)) > 0) {
+//            bufferInfo.flags = mediaExtractor.getSampleFlags();
+//            bufferInfo.offset = 0;
+//            bufferInfo.size = sampleSize;
+//            bufferInfo.presentationTimeUs = mediaExtractor.getSampleTime();
+//            int isEOS = bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+//            Log.i(TAG, "demuxerMp4:  flags=" + bufferInfo.flags + " size=" + sampleSize + " time=" + bufferInfo.presentationTimeUs + " outputName" + outputName+" isEOS="+isEOS);
+//            //6. 把通过mediaExtractor解封装的数据通过writeSampleData写入到对应的轨道
+//            mediaMuxer.writeSampleData(0, buffer, bufferInfo);
+//            mediaExtractor.advance();
+//        }
+//        Log.i(TAG, "extractorAndMuxer: " + outputName + "提取封装完成");
+//
+//        mediaExtractor.unselectTrack(i);
+//        //6.关闭
+//        mediaMuxer.stop();
+//        mediaMuxer.release();
+    }
+}

+ 1 - 3
student/src/main/java/com/cooleshow/student/helper/ShareHelper.java

@@ -14,7 +14,6 @@ import android.view.View;
 import com.cooleshow.base.utils.FileUtils;
 import com.cooleshow.base.utils.ToastUtil;
 import com.cooleshow.base.utils.Utils;
-import com.luck.picture.lib.tools.BitmapUtils;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -30,7 +29,6 @@ import io.reactivex.rxjava3.core.Observer;
 import io.reactivex.rxjava3.disposables.Disposable;
 import io.reactivex.rxjava3.schedulers.Schedulers;
 import io.rong.imkit.IMCenter;
-import io.rong.imkit.picture.tools.PictureFileUtils;
 import io.rong.imkit.utils.helper.OpenChatHelper;
 import io.rong.imlib.IRongCallback;
 import io.rong.imlib.RongIMClient;
@@ -375,7 +373,7 @@ public class ShareHelper {
         }
         String targetFileName = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()) + ".png";
         File file = new File(parentFile, targetFileName);
-        FileUtils.saveImageToGallery(bitmap, file.getAbsolutePath());
+        FileUtils.saveImageToLocal(bitmap, file.getAbsolutePath());
         return file;
     }
 }

+ 0 - 1
student/src/main/java/com/cooleshow/student/presenter/main/HomePresenter.java

@@ -238,7 +238,6 @@ public class HomePresenter extends BasePresenter<HomeContract.HomeView> implemen
                 return observable.map(new Function<BaseResponse<HomeStyleBean>, BaseResponse<HomeStyleBean>>() {
                     @Override
                     public BaseResponse<HomeStyleBean> apply(BaseResponse<HomeStyleBean> homeStyleBeanBaseResponse) throws Throwable {
-                        Log.i("pq", "匹配直播线程:" + Thread.currentThread().getName());
                         if (arrayListBaseResponse != null && page == 1) {
                             //只有第一页合并添加数据
                             ArrayList<TempLiveTeacherListBean> data = arrayListBaseResponse.getData();

+ 28 - 1
student/src/main/java/com/cooleshow/student/ui/web/AccompanyActivity.java

@@ -6,6 +6,7 @@ import android.app.Activity;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Handler;
+import android.provider.MediaStore;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -14,11 +15,20 @@ import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import androidx.annotation.Nullable;
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.functions.Consumer;
+import io.reactivex.rxjava3.schedulers.Schedulers;
 
 import com.alibaba.android.arouter.facade.annotation.Route;
 import com.cooleshow.base.router.RouterPath;
 import com.cooleshow.base.service.PlayMusicService;
 import com.cooleshow.base.ui.activity.BaseMVPActivity;
+import com.cooleshow.base.utils.FileUtils;
+import com.cooleshow.base.utils.LogUtils;
 import com.cooleshow.base.utils.MyFileUtils;
 import com.cooleshow.base.utils.ToastUtil;
 import com.cooleshow.base.utils.helper.WebParamsHelper;
@@ -154,7 +164,7 @@ public class AccompanyActivity extends BaseMVPActivity<ActivityAccompanyBinding,
                             try {
                                 File file = cameraKitVideo.getVideoFile();
                                 filePath = file.getPath();
-                                sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(filePath))));
+                                saveVideoToGallery(filePath);
                                 hideLoading();
                                 ToastUtil.getInstance().showShort("保存成功");
                             } catch (Exception e) {
@@ -255,6 +265,23 @@ public class AccompanyActivity extends BaseMVPActivity<ActivityAccompanyBinding,
         startService(intentOne);
     }
 
+    private void saveVideoToGallery(String filePath){
+        Observable.create(new ObservableOnSubscribe<Object>() {
+            @Override
+            public void subscribe(@NonNull ObservableEmitter<Object> emitter) throws Throwable {
+                Log.i("pq","saveVideoToGallery:"+Thread.currentThread().getName());
+                FileUtils.saveVideoToGallery(AccompanyActivity.this.getApplicationContext(),filePath);
+            }
+        }).subscribeOn(Schedulers.newThread())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Consumer<Object>() {
+                    @Override
+                    public void accept(Object o) throws Throwable {
+
+                    }
+                });
+    }
+
     public void setStatusBarTextColor(boolean statusBarTextColor) {
         if (statusBarTextColor) {
             setStatusBarColor();

+ 8 - 0
student/src/main/java/com/cooleshow/student/widgets/helper/JsInterfaceAccomPanyUtils.java

@@ -4,6 +4,7 @@ import android.app.Activity;
 import android.content.Intent;
 import android.net.Uri;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.Window;
 import android.view.WindowManager;
 import android.webkit.JavascriptInterface;
@@ -12,6 +13,7 @@ import com.alibaba.android.arouter.launcher.ARouter;
 import com.cooleshow.base.common.WebApi;
 import com.cooleshow.base.common.WebConstants;
 import com.cooleshow.base.router.RouterPath;
+import com.cooleshow.base.utils.LogUtils;
 import com.cooleshow.student.helper.EventHelper;
 import com.cooleshow.usercenter.constants.UserConstants;
 import com.cooleshow.usercenter.helper.UserHelper;
@@ -316,6 +318,12 @@ public class JsInterfaceAccomPanyUtils extends Object {
                     }
                 }
 
+                if (TextUtils.equals("cloudAccompanyMessage", api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    LogUtils.i("pq", "cloudAccompanyMessage:" + content.toString());
+                    return;
+                }
+
                 if (TextUtils.equals(WebApi.SET_EVENT_TRACKING, api)) {
                     //事件埋点
                     JSONObject content = jsonObject.getJSONObject("content");

+ 2 - 2
teacher/src/main/java/com/cooleshow/teacher/helper/ShareHelper.java

@@ -253,7 +253,7 @@ public class ShareHelper {
         }
         String targetFileName = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()) + ".png";
         File file = new File(parentFile, targetFileName);
-        FileUtils.saveImageToGallery(bitmap, file.getAbsolutePath());
+        FileUtils.saveImageToLocal(bitmap, file.getAbsolutePath());
         return new File(file.getAbsolutePath());
     }
 
@@ -264,7 +264,7 @@ public class ShareHelper {
         }
         String targetFileName = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()) + ".png";
         File file = new File(parentFile, targetFileName);
-        FileUtils.saveImageToGallery(bitmap, file.getAbsolutePath());
+        FileUtils.saveImageToLocal(bitmap, file.getAbsolutePath());
         return new File(file.getAbsolutePath());
     }
 

+ 27 - 1
teacher/src/main/java/com/cooleshow/teacher/ui/web/AccompanyActivity.java

@@ -12,12 +12,20 @@ import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import androidx.annotation.Nullable;
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.functions.Consumer;
+import io.reactivex.rxjava3.schedulers.Schedulers;
 
 import com.alibaba.android.arouter.facade.annotation.Route;
 import com.cooleshow.base.common.WebConstants;
 import com.cooleshow.base.router.RouterPath;
 import com.cooleshow.base.service.PlayMusicService;
 import com.cooleshow.base.ui.activity.BaseMVPActivity;
+import com.cooleshow.base.utils.FileUtils;
 import com.cooleshow.base.utils.ToastUtil;
 import com.cooleshow.base.utils.helper.WebParamsHelper;
 import com.cooleshow.base.utils.helper.upload.UploadHelper;
@@ -154,10 +162,11 @@ public class AccompanyActivity extends BaseMVPActivity<ActivityAccompanyBinding,
                             try {
                                 File file = cameraKitVideo.getVideoFile();
                                 filePath = file.getPath();
-                                sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(new File(filePath))));
+                                saveVideoToGallery(filePath);
                                 hideLoading();
                                 ToastUtil.getInstance().showShort("保存成功");
                             } catch (Exception e) {
+                                e.printStackTrace();
                             }
 
                         }
@@ -255,6 +264,23 @@ public class AccompanyActivity extends BaseMVPActivity<ActivityAccompanyBinding,
         startService(intentOne);
     }
 
+    private void saveVideoToGallery(String filePath) {
+        Observable.create(new ObservableOnSubscribe<Object>() {
+            @Override
+            public void subscribe(@NonNull ObservableEmitter<Object> emitter) throws Throwable {
+                Log.i("pq", "saveVideoToGallery:" + Thread.currentThread().getName());
+                FileUtils.saveVideoToGallery(AccompanyActivity.this.getApplicationContext(), filePath);
+            }
+        }).subscribeOn(Schedulers.newThread())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Consumer<Object>() {
+                    @Override
+                    public void accept(Object o) throws Throwable {
+
+                    }
+                });
+    }
+
     public void setStatusBarTextColor(boolean statusBarTextColor) {
         if (statusBarTextColor) {
             setStatusBarColor();