Browse Source

[New]新增预览相关

zhipeng 1 year ago
parent
commit
8914e14ca6

+ 0 - 8
app/src/main/java/com/datarecovery/master/module/main/MainActivity.java

@@ -1,17 +1,12 @@
 package com.datarecovery.master.module.main;
 package com.datarecovery.master.module.main;
 
 
-import static android.view.View.GONE;
-import static android.view.View.VISIBLE;
-
 import android.app.Activity;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Context;
 import android.content.Intent;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Bundle;
 import android.view.KeyEvent;
 import android.view.KeyEvent;
 
 
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
 
 
 import com.atmob.app.lib.base.BaseActivity;
 import com.atmob.app.lib.base.BaseActivity;
 import com.datarecovery.master.R;
 import com.datarecovery.master.R;
@@ -20,9 +15,6 @@ import com.datarecovery.master.databinding.ItemMainTabLayoutBinding;
 import com.datarecovery.master.utils.ToastUtil;
 import com.datarecovery.master.utils.ToastUtil;
 import com.google.android.material.tabs.TabLayout;
 import com.google.android.material.tabs.TabLayout;
 import com.google.android.material.tabs.TabLayoutMediator;
 import com.google.android.material.tabs.TabLayoutMediator;
-import com.gyf.immersionbar.ImmersionBar;
-
-import java.util.List;
 
 
 import dagger.hilt.android.AndroidEntryPoint;
 import dagger.hilt.android.AndroidEntryPoint;
 
 

+ 104 - 0
app/src/main/java/com/datarecovery/master/module/preview/PreviewActivity.java

@@ -0,0 +1,104 @@
+package com.datarecovery.master.module.preview;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.atmob.app.lib.base.BaseActivity;
+import com.datarecovery.master.databinding.ActivityPreviewBinding;
+import com.datarecovery.master.utils.SafeMediaPlayer;
+
+import dagger.hilt.android.AndroidEntryPoint;
+
+@AndroidEntryPoint
+public class PreviewActivity extends BaseActivity<ActivityPreviewBinding> {
+
+    public static final int TYPE_IMG = 1;
+    public static final int TYPE_VIDEO = 2;
+    public static final int TYPE_AUDIO = 3;
+    private PreviewViewModel previewViewModel;
+    private MediaPlayer mediaPlayer;
+    private boolean isSurfaceCreated;
+
+    @IntDef({TYPE_IMG, TYPE_VIDEO, TYPE_AUDIO})
+    @interface Type {
+    }
+
+    private static void start(Context context, @Type int type, Uri uri) {
+        Intent intent = new Intent(context, PreviewActivity.class);
+        intent.putExtra("type", type);
+        intent.putExtra("uri", uri);
+        if (!(context instanceof Activity)) {
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+        context.startActivity(intent);
+    }
+
+    @Override
+    protected boolean shouldImmersion() {
+        return true;
+    }
+
+    @Override
+    protected void initViewModel() {
+        previewViewModel = getViewModelProvider().get(PreviewViewModel.class);
+        binding.setPreviewViewModel(previewViewModel);
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        int type = getIntent().getIntExtra("type", 0);
+        Uri uri = getIntent().getParcelableExtra("uri");
+
+        previewViewModel.setPreviewData(type, uri);
+
+        if (type == TYPE_VIDEO || type == TYPE_AUDIO) {
+            initMediaPlayer(uri);
+        }
+        if (type == TYPE_VIDEO) {
+            initSurfaceView();
+        }
+    }
+
+    private void initSurfaceView() {
+        SurfaceHolder holder = binding.previewVideo.getHolder();
+        holder.addCallback(new SurfaceHolder.Callback() {
+            @Override
+            public void surfaceCreated(@NonNull SurfaceHolder holder) {
+                mediaPlayer.setDisplay(holder);
+                isSurfaceCreated = true;
+            }
+
+            @Override
+            public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
+            }
+
+            @Override
+            public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+                mediaPlayer.stop();
+                mediaPlayer.setDisplay(null);
+            }
+        });
+    }
+
+    private void initMediaPlayer(Uri uri) {
+        mediaPlayer = new SafeMediaPlayer();
+        try {
+            mediaPlayer.setDataSource(this, uri);
+            mediaPlayer.setOnPreparedListener(mp -> {
+
+            });
+            mediaPlayer.prepareAsync();
+        } catch (Exception ignored) {
+        }
+    }
+}

+ 46 - 0
app/src/main/java/com/datarecovery/master/module/preview/PreviewViewModel.java

@@ -0,0 +1,46 @@
+package com.datarecovery.master.module.preview;
+
+import android.net.Uri;
+
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+
+import com.atmob.app.lib.base.BaseViewModel;
+
+import javax.inject.Inject;
+
+import dagger.hilt.android.lifecycle.HiltViewModel;
+
+@HiltViewModel
+public class PreviewViewModel extends BaseViewModel {
+
+    private final MutableLiveData<String> title = new MutableLiveData<>();
+    private final MutableLiveData<Integer> type = new MutableLiveData<>();
+    private final MutableLiveData<Uri> previewUri = new MutableLiveData<>();
+    private final MutableLiveData<Boolean> isPlaying = new MutableLiveData<>();
+
+    @Inject
+    public PreviewViewModel() {
+    }
+
+    public LiveData<String> getTitle() {
+        return title;
+    }
+
+    public LiveData<Integer> getType() {
+        return type;
+    }
+
+    public LiveData<Uri> getPreviewUri() {
+        return previewUri;
+    }
+
+    public LiveData<Boolean> getIsPlaying() {
+        return isPlaying;
+    }
+
+    public void setPreviewData(int type, Uri uri) {
+        this.type.setValue(type);
+        this.previewUri.setValue(uri);
+    }
+}

+ 120 - 0
app/src/main/java/com/datarecovery/master/utils/SafeMediaPlayer.java

@@ -0,0 +1,120 @@
+package com.datarecovery.master.utils;
+
+import android.media.MediaPlayer;
+
+import com.atmob.common.logging.AtmobLog;
+
+import java.io.IOException;
+
+public class SafeMediaPlayer extends MediaPlayer {
+
+    private static final String TAG = "SafeMediaPlayer";
+
+    private OnErrorListener onErrorListener;
+
+    private OnPreparedListener onPreparedListener;
+
+    private volatile boolean isPrepared = false;
+
+    public SafeMediaPlayer() {
+        super.setOnErrorListener((mp, what, extra) -> {
+            AtmobLog.e(TAG, "onError() called with: mp = [" + mp + "], what = [" + what + "], extra = [" + extra + "]");
+            if (onErrorListener != null) {
+                onErrorListener.onError(mp, what, extra);
+            }
+            return true;
+        });
+        super.setOnPreparedListener(mp -> {
+            AtmobLog.d(TAG, "onPrepared() called with: mp = [" + mp + "]");
+            isPrepared = true;
+            if (onPreparedListener != null) {
+                onPreparedListener.onPrepared(mp);
+            }
+        });
+    }
+
+    @Override
+    public void setOnErrorListener(OnErrorListener listener) {
+        this.onErrorListener = listener;
+    }
+
+    @Override
+    public void setOnPreparedListener(OnPreparedListener listener) {
+        onPreparedListener = listener;
+    }
+
+    @Override
+    public void start() {
+        Thread.currentThread().getStackTrace();
+        AtmobLog.i(TAG, "%s start() called" + this);
+        try {
+            super.start();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void stop() {
+        AtmobLog.i(TAG, "%s stop() called" + this);
+        try {
+            super.stop();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void pause() {
+        AtmobLog.i(TAG, "%s pause() called" + this);
+        try {
+            super.pause();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public boolean isPlaying() {
+        try {
+            return super.isPlaying();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    @Override
+    public void setVolume(float leftVolume, float rightVolume) {
+        AtmobLog.i(TAG, "setVolume() called with: leftVolume = [" + leftVolume + "], rightVolume = [" + rightVolume + "]");
+        try {
+            super.setVolume(leftVolume, rightVolume);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void prepare() throws IOException, IllegalStateException {
+        AtmobLog.i(TAG, "prepare() called");
+        try {
+            super.prepare();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void prepareAsync() throws IllegalStateException {
+        AtmobLog.i(TAG, "prepareAsync() called");
+        try {
+            super.prepareAsync();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public boolean isPrepared() {
+        return isPrepared;
+    }
+}

BIN
app/src/main/res/drawable-xxhdpi/icon_preview_audio_pause.webp


BIN
app/src/main/res/drawable-xxhdpi/icon_preview_audio_play.webp


BIN
app/src/main/res/drawable-xxhdpi/icon_preview_video_play.webp


+ 6 - 0
app/src/main/res/drawable/bg_preview_audio.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">
+    <solid android:color="@color/white" />
+    <corners android:radius="8dp" />
+</shape>

+ 25 - 0
app/src/main/res/drawable/shape_preview_audio_seekbar.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Background -->
+    <item
+        android:id="@android:id/background"
+        android:bottom="2dp"
+        android:top="2dp">
+        <shape>
+            <solid android:color="#EEF4FE" />
+            <corners android:radius="100dp" />
+        </shape>
+    </item>
+
+    <!-- Progress -->
+    <item android:id="@android:id/progress" android:bottom="2dp"
+        android:top="2dp">
+        <clip>
+            <shape>
+                <solid android:color="#2B66FE" />
+                <corners android:radius="100dp" />
+            </shape>
+        </clip>
+    </item>
+</layer-list>

+ 8 - 0
app/src/main/res/drawable/shape_preview_audio_seekbar_thumb.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <solid android:color="#2B66FE" />
+    <size
+        android:width="20dp"
+        android:height="20dp" />
+</shape>

+ 124 - 0
app/src/main/res/layout/activity_preview.xml

@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <data>
+
+        <variable
+            name="previewViewModel"
+            type="com.datarecovery.master.module.preview.PreviewViewModel" />
+
+        <import type="com.datarecovery.master.module.preview.PreviewActivity" />
+    </data>
+
+    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:clipChildren="false"
+        tools:ignore="contentDescription">
+
+        <androidx.appcompat.widget.Toolbar
+            android:id="@+id/preview_header"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_constraintTop_toTopOf="parent"
+            app:navigationIcon="@drawable/icon_back">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:text="@{previewViewModel.title}"
+                android:textColor="#202020"
+                android:textSize="17sp"
+                android:textStyle="bold"
+                tools:text="Preview" />
+        </androidx.appcompat.widget.Toolbar>
+
+        <View
+            android:id="@+id/preview_background"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:background="#F8F8F8"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/preview_header" />
+
+        <ImageView
+            android:id="@+id/preview_image"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:scaleType="fitXY"
+            app:imageUri="@{previewViewModel.previewUri}"
+            app:isGone="@{previewViewModel.type != PreviewActivity.TYPE_IMG}"
+            app:layout_constraintBottom_toBottomOf="@id/preview_background"
+            app:layout_constraintTop_toTopOf="@id/preview_background" />
+
+        <SurfaceView
+            android:id="@+id/preview_video"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:isGone="@{previewViewModel.type != PreviewActivity.TYPE_VIDEO}"
+            app:layout_constraintBottom_toBottomOf="@id/preview_background"
+            app:layout_constraintTop_toTopOf="@id/preview_background" />
+
+        <ImageView
+            android:id="@+id/preview_play"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:src="@drawable/icon_preview_video_play"
+            app:isGone="@{previewViewModel.type != PreviewActivity.TYPE_VIDEO || previewViewModel.isPlaying}"
+            app:layout_constraintBottom_toBottomOf="@id/preview_video"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintLeft_toLeftOf="@id/preview_video"
+            app:layout_constraintRight_toRightOf="@id/preview_video"
+            app:layout_constraintTop_toTopOf="@id/preview_video"
+            app:layout_constraintWidth_percent="0.1333333333333333" />
+
+        <View
+            android:id="@+id/preview_audio_background"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_marginHorizontal="@dimen/app_common_page_horizontal_padding"
+            android:background="@drawable/bg_preview_audio"
+            app:isGone="@{previewViewModel.type != PreviewActivity.TYPE_AUDIO}"
+            app:layout_constraintBottom_toBottomOf="@id/preview_audio_play"
+            app:layout_constraintDimensionRatio="328:88"
+            app:layout_constraintTop_toTopOf="@id/preview_audio_play" />
+
+        <ImageView
+            android:id="@+id/preview_audio_play"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginHorizontal="@dimen/app_common_page_horizontal_padding"
+            android:src="@{previewViewModel.isPlaying ? @drawable/icon_preview_audio_pause : @drawable/icon_preview_audio_play}"
+            app:isGone="@{previewViewModel.type != PreviewActivity.TYPE_AUDIO}"
+            app:layout_constraintBottom_toTopOf="@id/preview_bottom"
+            app:layout_constraintDimensionRatio="1:1"
+            app:layout_constraintLeft_toLeftOf="@id/preview_audio_background"
+            app:layout_constraintTop_toTopOf="@id/preview_background"
+            app:layout_constraintWidth_percent="0.0888888888888889"
+            tools:src="@drawable/icon_preview_audio_pause" />
+
+        <SeekBar
+            android:id="@+id/preview_audio_seekbar"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            android:layout_marginHorizontal="@dimen/app_common_page_horizontal_padding"
+            android:progress="10"
+            android:progressDrawable="@drawable/shape_preview_audio_seekbar"
+            android:splitTrack="false"
+            android:thumb="@drawable/shape_preview_audio_seekbar_thumb"
+            app:isGone="@{previewViewModel.type != PreviewActivity.TYPE_AUDIO}"
+            app:layout_constraintBottom_toBottomOf="@id/preview_audio_play"
+            app:layout_constraintDimensionRatio="252:14"
+            app:layout_constraintLeft_toRightOf="@id/preview_audio_play"
+            app:layout_constraintRight_toRightOf="@id/preview_audio_background"
+            app:layout_constraintTop_toTopOf="@id/preview_audio_play" />
+
+        <androidx.coordinatorlayout.widget.CoordinatorLayout
+            android:id="@+id/preview_bottom"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:layout_constraintBottom_toBottomOf="parent" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>