1.1.5增加视频封面显示

This commit is contained in:
2026-03-19 16:59:48 +08:00
parent fa07450606
commit f0e831b2fe
14 changed files with 219 additions and 50 deletions

View File

@@ -18,8 +18,8 @@ android {
//There are no CERT files because If the mini sdk version is 23+, the AGP will ignore the V1 scheme signature.
minSdkVersion 23
targetSdkVersion 29
versionCode 14
versionName "1.1.3"
versionCode 16
versionName "1.1.5"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -1,18 +1,20 @@
package com.hainaos.vc.activity.category.local;
import android.content.Intent;
import android.os.Build;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.RequiresApi;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.hainaos.vc.R;
import com.hainaos.vc.adapter.VideoAdapter;
import com.hainaos.vc.base.mvvm.BaseMvvmActivity;
import com.hainaos.vc.bean.BaseResponse;
import com.hainaos.vc.bean.CategoryInfo;
import com.hainaos.vc.bean.CategoryVideoInfo;
import com.hainaos.vc.bean.LocalVideoInfo;
import com.hainaos.vc.bean.VideoListData;
import com.hainaos.vc.databinding.ActivityCategoryLocalBinding;
@@ -27,6 +29,9 @@ import com.hjq.toast.Toaster;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class LocalCategoryActivity extends BaseMvvmActivity<LocalCategoryViewModel, ActivityCategoryLocalBinding> {
private static final String TAG = "LocalCategoryActivity";
@@ -72,13 +77,13 @@ public class LocalCategoryActivity extends BaseMvvmActivity<LocalCategoryViewMod
});
mViewDataBinding.rvVideo.setAdapter(mVideoAdapter);
mViewDataBinding.swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mViewDataBinding.swipeRefreshLayout.setRefreshing(true);
mViewModel.getVideoList(mCategoryInfo.getFolder());
}
});
// mViewDataBinding.swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
// @Override
// public void onRefresh() {
// mViewDataBinding.swipeRefreshLayout.setRefreshing(true);
// mViewModel.getVideoList(mCategoryInfo.getFolder());
// }
// });
}
@Override
@@ -104,13 +109,17 @@ public class LocalCategoryActivity extends BaseMvvmActivity<LocalCategoryViewMod
mViewDataBinding.rvVideo.setVisibility(View.VISIBLE);
}
mVideoAdapter.setData(localVideoInfos);
mViewDataBinding.swipeRefreshLayout.setRefreshing(false);
// mViewDataBinding.swipeRefreshLayout.setRefreshing(false);
}
});
mViewModel.mCategoryVideoInfoListData.observe(this, new Observer<BaseResponse<VideoListData>>() {
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void onChanged(BaseResponse<VideoListData> baseResponse) {
if (baseResponse.code == 200) {
List<CategoryVideoInfo> categoryVideoInfos = baseResponse.data.getData();
Map<String, String> coverMap = categoryVideoInfos.stream().collect(Collectors.toMap(categoryVideoInfo -> FileUtils.getFileNamefromURL(categoryVideoInfo.getFile_url()), CategoryVideoInfo::getCover));
mVideoAdapter.setCoverMap(coverMap);
mViewModel.getVideoList(mCategoryInfo.getFolder());
} else {
Toaster.show("密码错误,请重新输入");

View File

@@ -34,7 +34,7 @@ import wseemann.media.FFmpegMediaMetadataRetriever;
public class LocalCategoryViewModel extends BaseViewModel<ActivityCategoryLocalBinding, ActivityEvent> {
private static final String TAG = "CategoryViewModel";
private static final String TAG = "LocalCategoryViewModel";
@Override
public ActivityCategoryLocalBinding getVDBinding() {
@@ -113,19 +113,23 @@ public class LocalCategoryViewModel extends BaseViewModel<ActivityCategoryLocalB
localVideoInfo.setFile_name(FileUtils.getFileNameWithoutExtension(s));
localVideoInfo.setLocalPath(videoFile.getAbsolutePath());
FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
try {
long time = System.currentTimeMillis();
mmr.setDataSource(videoFile.getAbsolutePath());
int duration = Integer.parseInt(mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION));
Log.e("AudioUtils", "getDurationInMilliseconds: " + (System.currentTimeMillis() - time));
localVideoInfo.setDuration(duration / 1000);
} catch (Exception e) {
Log.e(TAG, "apply: " + e.getMessage());
} finally {
mmr.release();//释放资源
}
if (s.endsWith(".hnv")) {
localVideoInfo.setDuration(0);
} else {
FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
try {
long time = System.currentTimeMillis();
mmr.setDataSource(videoFile.getAbsolutePath());
int duration = Integer.parseInt(mmr.extractMetadata(FFmpegMediaMetadataRetriever.METADATA_KEY_DURATION));
Log.e("AudioUtils", "getDurationInMilliseconds: " + (System.currentTimeMillis() - time));
localVideoInfo.setDuration(duration / 1000);
} catch (Exception e) {
Log.e(TAG, "apply: " + e.getMessage());
} finally {
mmr.release();//释放资源
}
}
return localVideoInfo;
}
}).collect(Collectors.toCollection(ArrayList::new));

View File

@@ -17,6 +17,7 @@ import com.hainaos.vc.bean.BaseResponse;
import com.hainaos.vc.bean.CategoryInfo;
import com.hainaos.vc.bean.CategoryVideoInfo;
import com.hainaos.vc.bean.VideoListData;
import com.hainaos.vc.config.CommonConfig;
import com.hainaos.vc.databinding.ActivityCategoryVideoBinding;
import com.hainaos.vc.fragment.passwd.PasswdDialogFragment;
import com.hainaos.vc.utils.FileUtils;
@@ -24,6 +25,7 @@ import com.hainaos.vc.utils.ScreenUtils;
import com.hainaos.vc.view.CustomDialog;
import com.hainaos.vc.view.EquallyDividedItemDecoration;
import com.hjq.toast.Toaster;
import com.tencent.mmkv.MMKV;
import java.io.File;
import java.util.List;
@@ -31,6 +33,9 @@ import java.util.List;
public class CategoryVideoActivity extends BaseMvvmActivity<CategoryVideoViewModel, ActivityCategoryVideoBinding> {
private static final String TAG = "CategoryActivity";
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
private CategoryInfo mCategoryInfo;
private String mPasswd = "";
private CategoryVideoAdapter mCategoryVideoAdapter;
@@ -114,6 +119,7 @@ public class CategoryVideoActivity extends BaseMvvmActivity<CategoryVideoViewMod
List<CategoryVideoInfo> categoryVideoInfos = videoListData.getData();
mCategoryVideoAdapter.setData(categoryVideoInfos);
if (categoryVideoInfos == null || categoryVideoInfos.isEmpty()) {
mViewDataBinding.clNodata.setVisibility(View.VISIBLE);
mViewDataBinding.rvVideo.setVisibility(View.GONE);

View File

@@ -27,7 +27,7 @@ import io.reactivex.rxjava3.disposables.Disposable;
public class CategoryVideoViewModel extends BaseViewModel<ActivityCategoryVideoBinding, ActivityEvent> {
private static final String TAG = "CategoryViewModel";
private static final String TAG = "CategoryVideoViewModel";
@Override
public ActivityCategoryVideoBinding getVDBinding() {

View File

@@ -3,6 +3,7 @@ package com.hainaos.vc.activity.player;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import com.google.android.exoplayer2.ExoPlayer;
@@ -75,6 +76,7 @@ public class DecryptionPlayerActivity extends BaseMvvmActivity<DecryptionPlayerV
// DataSource upstream = new FileDataSource();
// return new AesDataSource2(upstream, key, iv);
} catch (Exception e) {
Log.e(TAG, "initData: " + e.getMessage());
throw new RuntimeException(e);
}
};

View File

@@ -83,6 +83,7 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
holder.tv_title.setText(name);
}
String url = categoryVideoInfo.getFile_url();
String cover = categoryVideoInfo.getCover();
String md5 = categoryVideoInfo.getMd5();
long sizeBytes = categoryVideoInfo.getFile_size();
String fileSize = Formatter.formatFileSize(mContext, sizeBytes);
@@ -94,8 +95,14 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
DownloadEntity entity = Aria.download(mContext).getFirstDownloadEntity(url);
if (entity == null) {
holder.tv_download.setText("下载");
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
if (file.exists()) {
holder.tv_download.setText("删除");
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_delete_button));
} else {
holder.tv_download.setText("下载");
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
}
} else {
int state = entity.getState();
switch (state) {
@@ -108,10 +115,16 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
}
break;
case 2:
holder.tv_download.setText("停止");
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
break;
case 3:
holder.tv_download.setText("等待");
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
break;
case -1:
case 0:
case 2:
case 3:
case 5:
case 6:
case 7:
@@ -127,8 +140,6 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
}
String cover = categoryVideoInfo.getCover();
GlideLoadUtils.getInstance().glideLoad(mContext, cover, holder.video_image);
holder.tv_duration.setText("视频时长: " + TimeUtils.TimeFormat(categoryVideoInfo.getDuration() * 1000));
@@ -136,14 +147,43 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
holder.tv_download.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (file.exists()) {
showDialog(file.getAbsolutePath(), position);
} else {
if (XXPermissions.isGranted(mContext, Permissions.STORAGE_PERMISSIONS)) {
FileUtils.ariaDownload(mContext, mDirName, url, md5);
// FileUtils.ariaDownloadCover(mContext, mDirName, cover, md5);
if (entity == null) {
if (file.exists()) {
showDialog(file.getAbsolutePath(), position);
} else {
showPermissionsDialog(mContext);
if (XXPermissions.isGranted(mContext, Permissions.STORAGE_PERMISSIONS)) {
FileUtils.ariaDownload(mContext, mDirName, url, md5);
FileUtils.ariaDownload(mContext, mDirName, cover);
// FileUtils.ariaDownloadCover(mContext, mDirName, cover, md5);
} else {
showPermissionsDialog(mContext);
}
}
} else {
int state = entity.getState();
switch (state) {
case 1:
if (file.exists()) {
showDialog(file.getAbsolutePath(), position);
} else {
if (XXPermissions.isGranted(mContext, Permissions.STORAGE_PERMISSIONS)) {
FileUtils.ariaDownload(mContext, mDirName, url, md5);
FileUtils.ariaDownload(mContext, mDirName, cover);
// FileUtils.ariaDownloadCover(mContext, mDirName, cover, md5);
} else {
showPermissionsDialog(mContext);
}
}
break;
case -1:
case 0:
case 2:
case 3:
case 5:
case 6:
case 7:
case 4:
}
}
}

View File

@@ -6,6 +6,7 @@ import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.os.AsyncTask;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -16,6 +17,9 @@ import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.arialyy.annotations.Download;
import com.arialyy.aria.core.Aria;
import com.arialyy.aria.core.task.DownloadTask;
import com.bumptech.glide.Glide;
import com.google.gson.Gson;
import com.google.gson.JsonParser;
@@ -23,19 +27,28 @@ import com.hainaos.vc.R;
import com.hainaos.vc.activity.player.DecryptionPlayerActivity;
import com.hainaos.vc.activity.tiktok.TikTokActivity;
import com.hainaos.vc.bean.LocalVideoInfo;
import com.hainaos.vc.config.CommonConfig;
import com.hainaos.vc.utils.FileUtils;
import com.hainaos.vc.utils.TimeUtils;
import com.shehuan.niv.NiceImageView;
import com.tencent.mmkv.MMKV;
import java.io.File;
import java.util.ArrayList;
import java.util.Map;
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoHolder> {
private static final String TAG = "VideoAdapter";
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
private Activity mContext;
private ArrayList<LocalVideoInfo> mLocalVideoInfos;
private onItemLongClickListener onItemLongClickListener;
private Map<String, String> mCoverMap;
public VideoAdapter() {
}
@@ -49,6 +62,11 @@ public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoHolder>
this.mLocalVideoInfos = path;
}
public void setCoverMap(Map<String, String> coverMap) {
mCoverMap = coverMap;
notifyDataSetChanged();
}
private static class VideoResult {
long time;
Bitmap frame;
@@ -72,6 +90,7 @@ public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoHolder>
@NonNull
@Override
public VideoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Aria.download(this).register();
VideoHolder holder = new VideoHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_video_file, parent, false));
return holder;
}
@@ -81,16 +100,40 @@ public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoHolder>
LocalVideoInfo localVideoInfo = mLocalVideoInfos.get(position);
final String localPath = localVideoInfo.getLocalPath();
Log.e(TAG, "onBindViewHolder: " + localPath);
holder.title.setText(FileUtils.getFileName(localPath));
String videoFileName = FileUtils.getFileName(localPath);
holder.title.setText(videoFileName);
File file = new File(localPath);
if (file.exists()) {
holder.iv_status.setVisibility(View.GONE);
Glide.with(mContext).load(file).error(R.mipmap.ic_launcher).into(holder.video_image);
holder.duration.setText(TimeUtils.TimeFormat(localVideoInfo.getDuration() * 1000));
if (file.getName().endsWith(".hnv")) {
String dirPaht = file.getParent();
Log.e(TAG, "onBindViewHolder: dirPaht = " + dirPaht);
if (mCoverMap != null) {
String coverUrl = mCoverMap.get(videoFileName);
Log.e(TAG, "onBindViewHolder: coverUrl = " + coverUrl);
if (!TextUtils.isEmpty(coverUrl)) {
String coverName = FileUtils.getFileNamefromURL(coverUrl);
Log.e(TAG, "onBindViewHolder: coverName = " + coverName);
File coverFile = new File(dirPaht + File.separator + coverName);
Log.e(TAG, "onBindViewHolder: coverFile = " + coverFile.getAbsolutePath());
if (coverFile.exists()) {
Glide.with(mContext).load(coverFile).centerCrop().error(R.drawable.picture_split).into(holder.video_image);
} else {
FileUtils.ariaDownloadCover(mContext, coverFile.getParent(), coverUrl);
}
} else {
holder.video_image.setImageDrawable(mContext.getDrawable(R.drawable.picture_split));
}
} else {
holder.video_image.setImageDrawable(mContext.getDrawable(R.drawable.picture_split));
}
} else {
Glide.with(mContext).load(file).centerCrop().error(R.drawable.picture_split).into(holder.video_image);
holder.duration.setText(TimeUtils.TimeFormat(localVideoInfo.getDuration() * 1000));
}
} else {
holder.iv_status.setVisibility(View.VISIBLE);
}
holder.iv_status.setOnClickListener(new View.OnClickListener() {
@@ -194,4 +237,29 @@ public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoHolder>
void onScanCompleted(Bitmap bitmap);
}
//在这里处理任务执行中的状态,如进度进度条的刷新
@Download.onTaskRunning
void running(DownloadTask task) {
Log.e(TAG, "running: " + "正在下载:" + task.getState() + "-" + task.getPercent() + "--" + task.getExtendField());
notifyDataSetChanged();
}
@Download.onTaskComplete
void taskComplete(DownloadTask task) {
//在这里处理任务完成的状态
Log.e(TAG, "taskComplete: " + task.getFilePath());
notifyDataSetChanged();
}
@Download.onTaskFail
void taskFail(DownloadTask task, Exception e) {
try {
Log.e(TAG, "taskFail: e " + e.getMessage());
} catch (Exception ex) {
Log.e(TAG, "taskFail: ex " + ex.getMessage());
}
notifyDataSetChanged();
}
}

View File

@@ -243,7 +243,7 @@ public class NetInterfaceManager {
public Observable<BaseResponse<VideoListData>> getVideoListObservable(String category_uuid, String password) {
String bearerToken = LoginUtils.getInstance().getBearerToken();
return getVideoApi()
.getVideoList(bearerToken, category_uuid, password)
.getVideoList(bearerToken, category_uuid, password, 20)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}

View File

@@ -24,7 +24,8 @@ public interface VideoApi {
Observable<BaseResponse<VideoListData>> getVideoList(
@Header("Authorization") String token,
@Query("category_uuid") String category_uuid,
@Query("password") String password
@Query("password") String password,
@Query("page_size") int page_size
);
@FormUrlEncoded

View File

@@ -160,6 +160,45 @@ public class FileUtils {
}
}
public static void ariaDownload(Context context, String dirName, String url) {
String downLoadPath = getHainaVideoPath(context) + dirName + File.separator;
File dirFile = new File(downLoadPath);
if (!dirFile.exists()) {
Log.e(TAG, "ariaDownload: mkdirs = " + dirFile.mkdirs());
}
String fileName = getFileNamefromURL(url);
File file = new File(downLoadPath + fileName);
if (file.exists() && !file.isDirectory()) {
Log.e("ariaDownload", url + " pic exists, skip");
} else {
Aria.download(context)
.load(url) //读取下载地址
.setFilePath(file.getAbsolutePath())
// .ignoreFilePathOccupy()
.setExtendField(url)
.create(); //启动下载}
}
}
public static void ariaDownloadCover(Context context, String coverPath, String url) {
File dirFile = new File(coverPath);
if (!dirFile.exists()) {
Log.e(TAG, "ariaDownload: mkdirs = " + dirFile.mkdirs());
}
String fileName = getFileNamefromURL(url);
File file = new File(coverPath + File.separator + fileName);
if (file.exists() && !file.isDirectory()) {
Log.e("ariaDownload", url + " pic exists, skip");
} else {
Aria.download(context)
.load(url) //读取下载地址
.setFilePath(file.getAbsolutePath())
// .ignoreFilePathOccupy()
.setExtendField(url)
.create(); //启动下载}
}
}
public static void ariaDownload(Context context, String url, AppInfo appInfo) {
Log.e(TAG, "ariaDownload: " + appInfo);
AriaDownloadInfo ariaDownloadInfo = AriaDownloadInfo.toAriaDownloadInfo(appInfo);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -71,10 +71,10 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cl_title">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- <androidx.swiperefreshlayout.widget.SwipeRefreshLayout-->
<!-- android:id="@+id/swipeRefreshLayout"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent">-->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
@@ -109,7 +109,7 @@
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<!-- </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>-->
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:scaleType="centerInside"
app:is_cover_src="true" />
<com.shehuan.niv.NiceImageView