Compare commits
4 Commits
fa07450606
...
b53e07195f
| Author | SHA1 | Date | |
|---|---|---|---|
| b53e07195f | |||
| 73892596b1 | |||
| 6f0f7c4c09 | |||
| f0e831b2fe |
@@ -18,8 +18,10 @@ 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 20
|
||||
versionName "1.1.9"
|
||||
// versionCode 21
|
||||
// versionName "1.2.0"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -188,6 +190,8 @@ dependencies {
|
||||
annotationProcessor 'com.arialyy.aria:compiler:3.8.15'
|
||||
//MMKV
|
||||
implementation 'com.tencent:mmkv-static:1.2.13'
|
||||
implementation 'com.tencent.bugly:crashreport:4.1.9.2'
|
||||
implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0'
|
||||
//工具类
|
||||
implementation 'com.blankj:utilcodex:1.31.0'
|
||||
//沉浸状态栏
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:name=".base.BaseApplication"
|
||||
|
||||
@@ -9,9 +9,13 @@ import com.hainaos.vc.R;
|
||||
import com.hainaos.vc.adapter.CategoryAdapter;
|
||||
import com.hainaos.vc.base.mvvm.BaseMvvmActivity;
|
||||
import com.hainaos.vc.bean.CategoryInfo;
|
||||
import com.hainaos.vc.bean.CategoryUpdateInfo;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
import com.hainaos.vc.databinding.ActivityCategoryListBinding;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CategoryListActivity extends BaseMvvmActivity<CategoryListViewModel, ActivityCategoryListBinding> {
|
||||
private static final String TAG = "CategoryListActivity";
|
||||
@@ -46,8 +50,26 @@ public class CategoryListActivity extends BaseMvvmActivity<CategoryListViewModel
|
||||
mCategoryAdapter.setCategoryInfos(categoryInfos);
|
||||
}
|
||||
});
|
||||
mViewModel.getCategoryList();
|
||||
|
||||
mViewModel.mVideoUpdateMutableLiveData.observe(this, new Observer<VideoUpdate>() {
|
||||
@Override
|
||||
public void onChanged(VideoUpdate videoUpdate) {
|
||||
if (videoUpdate.getHas_new() == 1) {
|
||||
mViewModel.getCategoryUpdate();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel.mCategoryUpdateInfoListData.observe(this, new Observer<List<CategoryUpdateInfo>>() {
|
||||
@Override
|
||||
public void onChanged(List<CategoryUpdateInfo> categoryUpdateInfos) {
|
||||
Map<String, Integer> map = categoryUpdateInfos.stream().collect(Collectors.toMap(CategoryUpdateInfo::getCategory_uuid, CategoryUpdateInfo::getCount));
|
||||
mCategoryAdapter.setCategoryUpdateInfoMap(map);
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel.getCategoryList();
|
||||
mViewModel.getVideoUpdate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,9 +7,14 @@ import androidx.lifecycle.MutableLiveData;
|
||||
import com.hainaos.vc.base.mvvm.BaseViewModel;
|
||||
import com.hainaos.vc.bean.BaseResponse;
|
||||
import com.hainaos.vc.bean.CategoryInfo;
|
||||
import com.hainaos.vc.bean.CategoryUpdateInfo;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
import com.hainaos.vc.config.CommonConfig;
|
||||
import com.hainaos.vc.databinding.ActivityCategoryListBinding;
|
||||
import com.hainaos.vc.network.NetInterfaceManager;
|
||||
import com.hainaos.vc.utils.TimeUtils;
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
|
||||
@@ -20,8 +25,9 @@ import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public class CategoryListViewModel extends BaseViewModel<ActivityCategoryListBinding, ActivityEvent> {
|
||||
private static final String TAG = "CategoryListViewModel";
|
||||
|
||||
private static final String TAG = "CategoryViewModel";
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@Override
|
||||
public ActivityCategoryListBinding getVDBinding() {
|
||||
@@ -66,4 +72,76 @@ public class CategoryListViewModel extends BaseViewModel<ActivityCategoryListBin
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public MutableLiveData<VideoUpdate> mVideoUpdateMutableLiveData = new MutableLiveData<>();
|
||||
|
||||
public void getVideoUpdate() {
|
||||
String currentTime = TimeUtils.transferMillisecondToDate(System.currentTimeMillis());
|
||||
String time = mMMKV.decodeString(CommonConfig.CATEGORY_VIDEOS_CHECK_UPDATE_TIME, currentTime);
|
||||
Log.e(TAG, "getVideoUpdate: " + time);
|
||||
NetInterfaceManager.getInstance().getVideoUpdateObservable(time)
|
||||
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
.subscribe(new Observer<BaseResponse<VideoUpdate>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("getVideoUpdate", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse<VideoUpdate> baseResponse) {
|
||||
Log.e("getVideoUpdate", "onNext: " + baseResponse);
|
||||
mMMKV.encode(CommonConfig.CATEGORY_VIDEOS_CHECK_UPDATE_TIME, currentTime);
|
||||
if (baseResponse.code == 200) {
|
||||
VideoUpdate videoUpdate = baseResponse.data;
|
||||
mVideoUpdateMutableLiveData.setValue(videoUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("getVideoUpdate", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getVideoUpdate", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public MutableLiveData<List<CategoryUpdateInfo>> mCategoryUpdateInfoListData = new MutableLiveData<>();
|
||||
|
||||
public void getCategoryUpdate() {
|
||||
String currentTime = TimeUtils.transferMillisecondToDate(System.currentTimeMillis());
|
||||
String time = mMMKV.decodeString(CommonConfig.CHECK_CATEGORY_UPDATE_NUMBER_TIME, currentTime);
|
||||
Log.e(TAG, "getCategoryUpdate: " + time);
|
||||
NetInterfaceManager.getInstance().getCategoryUpdateObservable(time)
|
||||
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
.subscribe(new Observer<BaseResponse<List<CategoryUpdateInfo>>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("getCategoryUpdate", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse<List<CategoryUpdateInfo>> listBaseResponse) {
|
||||
Log.e("getCategoryUpdate", "onNext: " + listBaseResponse);
|
||||
if (listBaseResponse.code == 200) {
|
||||
List<CategoryUpdateInfo> categoryUpdateInfos = listBaseResponse.data;
|
||||
mCategoryUpdateInfoListData.setValue(categoryUpdateInfos);
|
||||
}
|
||||
mMMKV.encode(CommonConfig.CHECK_CATEGORY_UPDATE_NUMBER_TIME, currentTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("getCategoryUpdate", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getCategoryUpdate", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
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.bean.VideoUpdate;
|
||||
import com.hainaos.vc.databinding.ActivityCategoryLocalBinding;
|
||||
import com.hainaos.vc.fragment.passwd.PasswdDialogFragment;
|
||||
import com.hainaos.vc.utils.FileUtils;
|
||||
@@ -27,6 +30,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 +78,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 +110,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("密码错误,请重新输入");
|
||||
@@ -126,6 +136,18 @@ public class LocalCategoryActivity extends BaseMvvmActivity<LocalCategoryViewMod
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel.mVideoUpdateMutableLiveData.observe(this, new Observer<VideoUpdate>() {
|
||||
@Override
|
||||
public void onChanged(VideoUpdate videoUpdate) {
|
||||
if (videoUpdate.getHas_new() == 1) {
|
||||
mViewDataBinding.ivUpdate.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mViewDataBinding.ivUpdate.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (TextUtils.isEmpty(mPasswd)) {
|
||||
mViewModel.getVideoList(mCategoryInfo.getFolder());
|
||||
} else {
|
||||
|
||||
@@ -8,10 +8,13 @@ import com.hainaos.vc.base.mvvm.BaseViewModel;
|
||||
import com.hainaos.vc.bean.BaseResponse;
|
||||
import com.hainaos.vc.bean.LocalVideoInfo;
|
||||
import com.hainaos.vc.bean.VideoListData;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
import com.hainaos.vc.config.CommonConfig;
|
||||
import com.hainaos.vc.databinding.ActivityCategoryLocalBinding;
|
||||
import com.hainaos.vc.network.NetInterfaceManager;
|
||||
import com.hainaos.vc.utils.FileUtils;
|
||||
import com.hainaos.vc.utils.VideoUtils;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
|
||||
@@ -33,8 +36,9 @@ import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import wseemann.media.FFmpegMediaMetadataRetriever;
|
||||
|
||||
public class LocalCategoryViewModel extends BaseViewModel<ActivityCategoryLocalBinding, ActivityEvent> {
|
||||
private static final String TAG = "LocalCategoryViewModel";
|
||||
|
||||
private static final String TAG = "CategoryViewModel";
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@Override
|
||||
public ActivityCategoryLocalBinding getVDBinding() {
|
||||
@@ -49,6 +53,7 @@ public class LocalCategoryViewModel extends BaseViewModel<ActivityCategoryLocalB
|
||||
public MutableLiveData<BaseResponse<VideoListData>> mCategoryVideoInfoListData = new MutableLiveData<>();
|
||||
|
||||
public void getVideoList(String uuid, String password) {
|
||||
// getVideoUpdate(uuid);
|
||||
NetInterfaceManager.getInstance().getVideoListObservable(uuid, password)
|
||||
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
.subscribe(new Observer<BaseResponse<VideoListData>>() {
|
||||
@@ -113,6 +118,9 @@ public class LocalCategoryViewModel extends BaseViewModel<ActivityCategoryLocalB
|
||||
localVideoInfo.setFile_name(FileUtils.getFileNameWithoutExtension(s));
|
||||
localVideoInfo.setLocalPath(videoFile.getAbsolutePath());
|
||||
|
||||
if (s.endsWith(".hnv")) {
|
||||
localVideoInfo.setDuration(0);
|
||||
} else {
|
||||
FFmpegMediaMetadataRetriever mmr = new FFmpegMediaMetadataRetriever();
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
@@ -126,6 +134,7 @@ public class LocalCategoryViewModel extends BaseViewModel<ActivityCategoryLocalB
|
||||
mmr.release();//释放资源
|
||||
}
|
||||
|
||||
}
|
||||
return localVideoInfo;
|
||||
}
|
||||
}).collect(Collectors.toCollection(ArrayList::new));
|
||||
@@ -134,6 +143,7 @@ public class LocalCategoryViewModel extends BaseViewModel<ActivityCategoryLocalB
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
.subscribe(new Observer<ArrayList<LocalVideoInfo>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
@@ -160,4 +170,40 @@ public class LocalCategoryViewModel extends BaseViewModel<ActivityCategoryLocalB
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public MutableLiveData<VideoUpdate> mVideoUpdateMutableLiveData = new MutableLiveData<>();
|
||||
|
||||
// public void getVideoUpdate(String uuid) {
|
||||
// String currentTime = TimeUtils.transferMillisecondToDate(System.currentTimeMillis());
|
||||
// String time = mMMKV.decodeString(uuid, currentTime);
|
||||
// Log.e(TAG, "getVideoUpdate: " + time);
|
||||
// NetInterfaceManager.getInstance().getVideoUpdateObservable(time)
|
||||
// .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
// .subscribe(new Observer<BaseResponse<VideoUpdate>>() {
|
||||
// @Override
|
||||
// public void onSubscribe(@NonNull Disposable d) {
|
||||
// Log.e("getVideoUpdate", "onSubscribe: ");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onNext(@NonNull BaseResponse<VideoUpdate> baseResponse) {
|
||||
// Log.e("getVideoUpdate", "onNext: " + baseResponse);
|
||||
// mMMKV.encode(uuid, currentTime);
|
||||
// if (baseResponse.code == 200) {
|
||||
// VideoUpdate videoUpdate = baseResponse.data;
|
||||
// mVideoUpdateMutableLiveData.setValue(videoUpdate);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onError(@NonNull Throwable e) {
|
||||
// Log.e("getVideoUpdate", "onError: " + e.getMessage());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onComplete() {
|
||||
// Log.e("getVideoUpdate", "onComplete: ");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -13,10 +13,11 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import com.hainaos.vc.R;
|
||||
import com.hainaos.vc.adapter.CategoryVideoAdapter;
|
||||
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.VideoListData;
|
||||
import com.hainaos.vc.bean.PasswdInfo;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
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;
|
||||
@@ -69,7 +74,7 @@ public class CategoryVideoActivity extends BaseMvvmActivity<CategoryVideoViewMod
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
mViewDataBinding.swipeRefreshLayout.setRefreshing(true);
|
||||
mViewModel.getVideoList(mCategoryInfo.getUuid(), mPasswd);
|
||||
mViewModel.getVideoList(mCategoryInfo.getFolder(), mCategoryInfo.getUuid(), mPasswd);
|
||||
}
|
||||
});
|
||||
mViewDataBinding.rvVideo.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@@ -106,14 +111,14 @@ public class CategoryVideoActivity extends BaseMvvmActivity<CategoryVideoViewMod
|
||||
// });
|
||||
// mViewModel.getViedoList(mCategoryInfo.getUuid());
|
||||
|
||||
mViewModel.mCategoryVideoInfoListData.observe(this, new Observer<BaseResponse<VideoListData>>() {
|
||||
mViewModel.mCategoryVideoInfoListData.observe(this, new Observer<List<CategoryVideoInfo>>() {
|
||||
@Override
|
||||
public void onChanged(BaseResponse<VideoListData> baseResponse) {
|
||||
if (baseResponse.code == 200) {
|
||||
VideoListData videoListData = baseResponse.data;
|
||||
List<CategoryVideoInfo> categoryVideoInfos = videoListData.getData();
|
||||
public void onChanged(List<CategoryVideoInfo> categoryVideoInfos) {
|
||||
|
||||
|
||||
mCategoryVideoAdapter.setData(categoryVideoInfos);
|
||||
|
||||
|
||||
if (categoryVideoInfos == null || categoryVideoInfos.isEmpty()) {
|
||||
mViewDataBinding.clNodata.setVisibility(View.VISIBLE);
|
||||
mViewDataBinding.rvVideo.setVisibility(View.GONE);
|
||||
@@ -122,25 +127,40 @@ public class CategoryVideoActivity extends BaseMvvmActivity<CategoryVideoViewMod
|
||||
mViewDataBinding.rvVideo.setVisibility(View.VISIBLE);
|
||||
mViewDataBinding.tvTotal.setText(String.format(getString(R.string.video_total), categoryVideoInfos.size()));
|
||||
}
|
||||
} else {
|
||||
Toaster.show(baseResponse.msg);
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel.mPasswdCorrect.observe(this, new Observer<PasswdInfo>() {
|
||||
@Override
|
||||
public void onChanged(PasswdInfo passwdInfo) {
|
||||
mViewDataBinding.swipeRefreshLayout.setRefreshing(false);
|
||||
if (!passwdInfo.isCorrect()) {
|
||||
Toaster.show(passwdInfo.getMsg());
|
||||
PasswdDialogFragment passwdDialogFragment = new PasswdDialogFragment(mCategoryInfo);
|
||||
passwdDialogFragment.setConfimCallback(new PasswdDialogFragment.ConfimCallback() {
|
||||
@Override
|
||||
public void onConfig(String passwd) {
|
||||
mPasswd = passwd;
|
||||
mViewModel.getVideoList(mCategoryInfo.getUuid(), mPasswd);
|
||||
mViewModel.getVideoList(mCategoryInfo.getFolder(), mCategoryInfo.getUuid(), mPasswd);
|
||||
}
|
||||
});
|
||||
passwdDialogFragment.show(getSupportFragmentManager(), "PasswdDialogFragment");
|
||||
|
||||
|
||||
}
|
||||
mViewDataBinding.swipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel.getVideoList(mCategoryInfo.getUuid(), mPasswd);
|
||||
mViewModel.mVideoUpdateMutableLiveData.observe(this, new Observer<VideoUpdate>() {
|
||||
@Override
|
||||
public void onChanged(VideoUpdate videoUpdate) {
|
||||
if (videoUpdate.getHas_new() == 1) {
|
||||
mViewDataBinding.ivUpdate.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mViewDataBinding.ivUpdate.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel.getVideoList(mCategoryInfo.getFolder(), mCategoryInfo.getUuid(), mPasswd);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -6,28 +6,42 @@ import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.hainaos.vc.base.mvvm.BaseViewModel;
|
||||
import com.hainaos.vc.bean.BaseResponse;
|
||||
import com.hainaos.vc.bean.LocalVideoInfo;
|
||||
import com.hainaos.vc.bean.CategoryVideoInfo;
|
||||
import com.hainaos.vc.bean.PasswdInfo;
|
||||
import com.hainaos.vc.bean.VideoListData;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
import com.hainaos.vc.config.CommonConfig;
|
||||
import com.hainaos.vc.databinding.ActivityCategoryVideoBinding;
|
||||
import com.hainaos.vc.network.NetInterfaceManager;
|
||||
import com.hainaos.vc.utils.FileUtils;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.functions.BiFunction;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
|
||||
public class CategoryVideoViewModel extends BaseViewModel<ActivityCategoryVideoBinding, ActivityEvent> {
|
||||
|
||||
private static final String TAG = "CategoryViewModel";
|
||||
private static final String TAG = "CategoryVideoViewModel";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@Override
|
||||
public ActivityCategoryVideoBinding getVDBinding() {
|
||||
@@ -40,7 +54,7 @@ public class CategoryVideoViewModel extends BaseViewModel<ActivityCategoryVideoB
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public MutableLiveData<ArrayList<LocalVideoInfo>> mLocalVideoInfosData = new MutableLiveData<>();
|
||||
public MutableLiveData<ArrayList<CategoryVideoInfo>> mLocalVideoInfosData = new MutableLiveData<>();
|
||||
|
||||
@Deprecated
|
||||
public void getViedoList(String dir) {
|
||||
@@ -50,12 +64,12 @@ public class CategoryVideoViewModel extends BaseViewModel<ActivityCategoryVideoB
|
||||
Log.e(TAG, "getViedoList: " + Arrays.toString(strings));
|
||||
if (strings != null) {
|
||||
List<String> paths = new ArrayList<>(Arrays.asList(strings));
|
||||
ArrayList<LocalVideoInfo> localVideoInfos = paths.stream().map(new Function<String, LocalVideoInfo>() {
|
||||
ArrayList<CategoryVideoInfo> localVideoInfos = paths.stream().map(new Function<String, CategoryVideoInfo>() {
|
||||
@Override
|
||||
public LocalVideoInfo apply(String s) {
|
||||
LocalVideoInfo localVideoInfo = new LocalVideoInfo();
|
||||
localVideoInfo.setFile_name(FileUtils.getFileNameWithoutExtension(s));
|
||||
localVideoInfo.setLocalPath(new File(file.getAbsolutePath() + File.separator + s).getAbsolutePath());
|
||||
public CategoryVideoInfo apply(String s) {
|
||||
CategoryVideoInfo localVideoInfo = new CategoryVideoInfo();
|
||||
localVideoInfo.setName(FileUtils.getFileNameWithoutExtension(s));
|
||||
localVideoInfo.setFile_url(new File(file.getAbsolutePath() + File.separator + s).getAbsolutePath());
|
||||
return localVideoInfo;
|
||||
}
|
||||
}).collect(Collectors.toCollection(ArrayList::new));
|
||||
@@ -68,22 +82,96 @@ public class CategoryVideoViewModel extends BaseViewModel<ActivityCategoryVideoB
|
||||
}
|
||||
}
|
||||
|
||||
public MutableLiveData<BaseResponse<VideoListData>> mCategoryVideoInfoListData = new MutableLiveData<>();
|
||||
private Observable<List<CategoryVideoInfo>> getLocalCategoryVideoInfo(String dirName) {
|
||||
return Observable.fromCallable(new Callable<List<CategoryVideoInfo>>() {
|
||||
@Override
|
||||
public List<CategoryVideoInfo> call() throws Exception {
|
||||
ArrayList<CategoryVideoInfo> localVideoInfos = new ArrayList<>();
|
||||
File file = new File(FileUtils.getHainaVideoPath(getCtx()) + dirName);
|
||||
if (file.exists()) {
|
||||
String[] strings = file.list();
|
||||
Log.e(TAG, "getViedoList: " + Arrays.toString(strings));
|
||||
if (strings != null) {
|
||||
List<String> paths = new ArrayList<>(Arrays.asList(strings));
|
||||
localVideoInfos = paths.stream()
|
||||
.filter(new Predicate<String>() {
|
||||
@Override
|
||||
public boolean test(String s) {
|
||||
return FileUtils.isVideoFile(s);
|
||||
}
|
||||
})
|
||||
.map(new Function<String, CategoryVideoInfo>() {
|
||||
@Override
|
||||
public CategoryVideoInfo apply(String s) {
|
||||
CategoryVideoInfo categoryVideoInfo = new CategoryVideoInfo();
|
||||
categoryVideoInfo.setName(FileUtils.getFileNameWithoutExtension(s));
|
||||
categoryVideoInfo.setFile_url(new File(file.getAbsolutePath() + File.separator + s).getAbsolutePath());
|
||||
return categoryVideoInfo;
|
||||
}
|
||||
}).collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
}
|
||||
return localVideoInfos;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void getVideoList(String uuid, String password) {
|
||||
NetInterfaceManager.getInstance().getVideoListObservable(uuid, password)
|
||||
public MutableLiveData<List<CategoryVideoInfo>> mCategoryVideoInfoListData = new MutableLiveData<>();
|
||||
public MutableLiveData<PasswdInfo> mPasswdCorrect = new MutableLiveData<>();
|
||||
|
||||
public void getVideoList(String dir, String uuid, String password) {
|
||||
Observable.zip(getLocalCategoryVideoInfo(dir), NetInterfaceManager.getInstance().getVideoListObservable(uuid, password),
|
||||
new BiFunction<List<CategoryVideoInfo>, BaseResponse<VideoListData>, List<CategoryVideoInfo>>() {
|
||||
@Override
|
||||
public List<CategoryVideoInfo> apply(List<CategoryVideoInfo> localCategoryVideoInfos, BaseResponse<VideoListData> baseResponse) throws Throwable {
|
||||
List<CategoryVideoInfo> allCategoryVideoInfos = new ArrayList<>();
|
||||
|
||||
PasswdInfo passwdInfo = new PasswdInfo();
|
||||
if (baseResponse.code == 200) {
|
||||
VideoListData videoListData = baseResponse.data;
|
||||
passwdInfo.setCorrect(true);
|
||||
List<CategoryVideoInfo> onlineCategoryVideoInfos = videoListData.getData();
|
||||
allCategoryVideoInfos.addAll(onlineCategoryVideoInfos);
|
||||
|
||||
// 1. 提取线上所有元素的name,存入Set(去重+O(1)查找,效率最高)
|
||||
Set<String> onlineNames = new HashSet<>();
|
||||
for (CategoryVideoInfo onlineInfo : onlineCategoryVideoInfos) {
|
||||
if (onlineInfo != null && onlineInfo.getName() != null) {
|
||||
onlineNames.add(onlineInfo.getName());
|
||||
}
|
||||
if (onlineInfo != null && onlineInfo.getFile_url() != null) {
|
||||
onlineNames.add(FileUtils.getFileNameWithoutExtension(onlineInfo.getFile_url()));
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 遍历本地列表,删除name存在于线上Set中的元素
|
||||
// 注意:使用迭代器删除,避免普通for循环删除报ConcurrentModificationException
|
||||
localCategoryVideoInfos.removeIf(info ->
|
||||
info != null && info.getName() != null && onlineNames.contains(info.getName())
|
||||
);
|
||||
allCategoryVideoInfos.addAll(localCategoryVideoInfos);
|
||||
|
||||
} else {
|
||||
passwdInfo.setCorrect(false);
|
||||
passwdInfo.setMsg(baseResponse.msg);
|
||||
}
|
||||
mPasswdCorrect.setValue(passwdInfo);
|
||||
return allCategoryVideoInfos;
|
||||
}
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
.subscribe(new Observer<BaseResponse<VideoListData>>() {
|
||||
.subscribe(new Observer<List<CategoryVideoInfo>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("getVideoList", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse<VideoListData> listBaseResponse) {
|
||||
Log.e("getVideoList", "onNext: ");
|
||||
mCategoryVideoInfoListData.setValue(listBaseResponse);
|
||||
|
||||
public void onNext(@NonNull List<CategoryVideoInfo> categoryVideoInfos) {
|
||||
Log.e("getVideoList", "onNext: " + categoryVideoInfos.size());
|
||||
mCategoryVideoInfoListData.setValue(categoryVideoInfos);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,5 +184,76 @@ public class CategoryVideoViewModel extends BaseViewModel<ActivityCategoryVideoB
|
||||
Log.e("getVideoList", "onComplete: ");
|
||||
}
|
||||
});
|
||||
|
||||
// NetInterfaceManager.getInstance().getVideoListObservable(uuid, password)
|
||||
// .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
// .subscribe(new Observer<BaseResponse<VideoListData>>() {
|
||||
// @Override
|
||||
// public void onSubscribe(@NonNull Disposable d) {
|
||||
// Log.e("getVideoList", "onSubscribe: ");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onNext(@NonNull BaseResponse<VideoListData> baseResponse) {
|
||||
// Log.e("getVideoList", "onNext: ");
|
||||
// PasswdInfo passwdInfo = new PasswdInfo();
|
||||
// if (baseResponse.code == 200) {
|
||||
// VideoListData videoListData = baseResponse.data;
|
||||
// List<CategoryVideoInfo> categoryVideoInfos = videoListData.getData();
|
||||
// mCategoryVideoInfoListData.setValue(categoryVideoInfos);
|
||||
// passwdInfo.setCorrect(true);
|
||||
// } else {
|
||||
// passwdInfo.setCorrect(false);
|
||||
// passwdInfo.setMsg(baseResponse.msg);
|
||||
// }
|
||||
// mPasswdCorrect.setValue(passwdInfo);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onError(@NonNull Throwable e) {
|
||||
// Log.e("getVideoList", "onError: " + e.getMessage());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onComplete() {
|
||||
// Log.e("getVideoList", "onComplete: ");
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
public MutableLiveData<VideoUpdate> mVideoUpdateMutableLiveData = new MutableLiveData<>();
|
||||
|
||||
// public void getVideoUpdate(String uuid) {
|
||||
// String currentTime = TimeUtils.transferMillisecondToDate(System.currentTimeMillis());
|
||||
// String time = mMMKV.decodeString(uuid, currentTime);
|
||||
// Log.e(TAG, "getVideoUpdate: " + time);
|
||||
// NetInterfaceManager.getInstance().getVideoUpdateObservable(time)
|
||||
// .compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
// .subscribe(new Observer<BaseResponse<VideoUpdate>>() {
|
||||
// @Override
|
||||
// public void onSubscribe(@NonNull Disposable d) {
|
||||
// Log.e("getVideoUpdate", "onSubscribe: ");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onNext(@NonNull BaseResponse<VideoUpdate> baseResponse) {
|
||||
// Log.e("getVideoUpdate", "onNext: " + baseResponse);
|
||||
// mMMKV.encode(uuid, currentTime);
|
||||
// if (baseResponse.code == 200) {
|
||||
// VideoUpdate videoUpdate = baseResponse.data;
|
||||
// mVideoUpdateMutableLiveData.setValue(videoUpdate);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onError(@NonNull Throwable e) {
|
||||
// Log.e("getVideoUpdate", "onError: " + e.getMessage());
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onComplete() {
|
||||
// Log.e("getVideoUpdate", "onComplete: ");
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -10,8 +10,11 @@ import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
@@ -21,15 +24,18 @@ import androidx.lifecycle.Observer;
|
||||
|
||||
import com.hainaos.vc.BuildConfig;
|
||||
import com.hainaos.vc.R;
|
||||
import com.hainaos.vc.activity.category.list.CategoryListActivity;
|
||||
import com.hainaos.vc.activity.login.LoginActivity;
|
||||
import com.hainaos.vc.base.BaseFragmentPagerAdapter;
|
||||
import com.hainaos.vc.base.mvvm.BaseMvvmActivity;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
import com.hainaos.vc.bean.uiuios.AppUpdateInfo;
|
||||
import com.hainaos.vc.config.CommonConfig;
|
||||
import com.hainaos.vc.config.Permissions;
|
||||
import com.hainaos.vc.databinding.ActivityMainBinding;
|
||||
import com.hainaos.vc.dialog.PermissionsDialog;
|
||||
import com.hainaos.vc.dialog.PrivacyPolicyDialog;
|
||||
import com.hainaos.vc.dialog.UpdateDialog;
|
||||
import com.hainaos.vc.fragment.app.AppFragment;
|
||||
import com.hainaos.vc.fragment.category.CategoryFragment;
|
||||
import com.hainaos.vc.utils.ApkUtils;
|
||||
@@ -40,6 +46,8 @@ import com.hainaos.vc.utils.VideoUtils;
|
||||
import com.hjq.permissions.OnPermissionCallback;
|
||||
import com.hjq.permissions.XXPermissions;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -47,8 +55,12 @@ import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.annotations.NonNull;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject;
|
||||
|
||||
public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBinding> {
|
||||
private static final String TAG = "MainActivity";
|
||||
@@ -61,6 +73,10 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
|
||||
private static final int REQUEST_PERMISSION_CODE = 200;
|
||||
|
||||
private PublishSubject<Object> clickSubject = PublishSubject.create();
|
||||
private CompositeDisposable disposables = new CompositeDisposable();
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setNightMode() {
|
||||
return true;
|
||||
@@ -96,6 +112,16 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
|
||||
@Override
|
||||
public void initData() {
|
||||
// 设置防抖,500毫秒内只响应最后一次点击
|
||||
disposables.add(clickSubject
|
||||
.throttleFirst(10, TimeUnit.MINUTES) // 防抖时间窗口
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.compose(RxLifecycle.bindUntilEvent(lifecycle(), ActivityEvent.DESTROY))
|
||||
.subscribe(o -> {
|
||||
// 这里执行需要防抖的操作,例如网络请求、计算等
|
||||
performAction();
|
||||
}));
|
||||
|
||||
mViewModel.mAppUpdateInfoUiUiOSData.observe(this, new Observer<AppUpdateInfo>() {
|
||||
@Override
|
||||
public void onChanged(AppUpdateInfo appUpdateInfo) {
|
||||
@@ -115,8 +141,26 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mViewModel.mVideoUpdateMutableLiveData.observe(this, new Observer<VideoUpdate>() {
|
||||
@Override
|
||||
public void onChanged(VideoUpdate videoUpdate) {
|
||||
if (videoUpdate.getHas_new() == 1) {
|
||||
showUpdateDialog();
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
clickSubject.onNext(new Object());
|
||||
}
|
||||
|
||||
private void performAction() {
|
||||
Log.e(TAG, "performAction: ");
|
||||
mViewModel.checkUpdateUiUiOS(BuildConfig.APPLICATION_ID);
|
||||
mViewModel.checkUpdate();
|
||||
mViewModel.getVideoUpdate();
|
||||
}
|
||||
|
||||
private void initDatas() {
|
||||
@@ -126,11 +170,51 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
|
||||
|
||||
}
|
||||
|
||||
private UpdateDialog mUpdateDialog;
|
||||
|
||||
private void showUpdateDialog() {
|
||||
mUpdateDialog = new UpdateDialog(this);
|
||||
mUpdateDialog.setMessage("更新了新的视频,请去下载")
|
||||
.setTitle("公告")
|
||||
.setPositive("点击查看")
|
||||
.setSingle(true)
|
||||
.setOnClickBottomListener(new UpdateDialog.OnClickBottomListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
startActivity(new Intent(MainActivity.this, CategoryListActivity.class));
|
||||
mUpdateDialog.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegtiveClick() {
|
||||
mUpdateDialog.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
Window window = mUpdateDialog.getWindow();
|
||||
if (window != null) {
|
||||
window.setGravity(Gravity.CENTER);
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
||||
}
|
||||
mUpdateDialog.setCancelable(false);
|
||||
if (!mUpdateDialog.isShowing()) {
|
||||
mUpdateDialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Log.e(TAG, "onResume: ");
|
||||
checkEULA();
|
||||
clickSubject.onNext(new Object());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
disposables.clear(); // 避免内存泄漏
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,10 +13,14 @@ import com.hainaos.vc.base.mvvm.BaseViewModel;
|
||||
import com.hainaos.vc.bean.AppInfo;
|
||||
import com.hainaos.vc.bean.BaseResponse;
|
||||
import com.hainaos.vc.bean.HomeAppInfo;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
import com.hainaos.vc.bean.uiuios.AppUpdateInfo;
|
||||
import com.hainaos.vc.config.CommonConfig;
|
||||
import com.hainaos.vc.databinding.ActivityMainBinding;
|
||||
import com.hainaos.vc.network.NetInterfaceManager;
|
||||
import com.hainaos.vc.utils.TimeUtils;
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
|
||||
@@ -40,6 +44,8 @@ import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEvent> {
|
||||
private static final String TAG = "MainViewModel";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@Override
|
||||
public ActivityMainBinding getVDBinding() {
|
||||
return binding;
|
||||
@@ -84,12 +90,12 @@ public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEv
|
||||
.subscribe(new Observer<BaseResponse<AppUpdateInfo>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("checkUpdate", "onSubscribe: ");
|
||||
Log.e("checkUpdateUiUiOS", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse<AppUpdateInfo> appUpdateInfoBaseResponse) {
|
||||
Log.e("checkUpdate", "onNext: " + appUpdateInfoBaseResponse);
|
||||
Log.e("checkUpdateUiUiOS", "onNext: " + appUpdateInfoBaseResponse);
|
||||
if (appUpdateInfoBaseResponse.code == 200) {
|
||||
AppUpdateInfo appUpdateInfo = appUpdateInfoBaseResponse.data;
|
||||
mAppUpdateInfoUiUiOSData.setValue(appUpdateInfo);
|
||||
@@ -100,13 +106,13 @@ public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEv
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("checkUpdate", "onError: ");
|
||||
Log.e("checkUpdateUiUiOS", "onError: ");
|
||||
Toaster.show("网络连接失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("checkUpdate", "onComplete: ");
|
||||
Log.e("checkUpdateUiUiOS", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -206,4 +212,40 @@ public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEv
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public MutableLiveData<VideoUpdate> mVideoUpdateMutableLiveData = new MutableLiveData<>();
|
||||
|
||||
public void getVideoUpdate() {
|
||||
String currentTime = TimeUtils.transferMillisecondToDate(System.currentTimeMillis());
|
||||
String time = mMMKV.decodeString(CommonConfig.MAIN_VIDEOS_CHECK_UPDATE_TIME, currentTime);
|
||||
Log.e(TAG, "getVideoUpdate: " + time);
|
||||
NetInterfaceManager.getInstance().getVideoUpdateObservable(time)
|
||||
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
|
||||
.subscribe(new Observer<BaseResponse<VideoUpdate>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Log.e("getVideoUpdate", "onSubscribe: ");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull BaseResponse<VideoUpdate> baseResponse) {
|
||||
Log.e("getVideoUpdate", "onNext: " + baseResponse);
|
||||
mMMKV.encode(CommonConfig.MAIN_VIDEOS_CHECK_UPDATE_TIME, currentTime);
|
||||
if (baseResponse.code == 200) {
|
||||
VideoUpdate videoUpdate = baseResponse.data;
|
||||
mVideoUpdateMutableLiveData.setValue(videoUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("getVideoUpdate", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getVideoUpdate", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ 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 android.view.WindowManager;
|
||||
|
||||
import com.google.android.exoplayer2.ExoPlayer;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
@@ -11,12 +13,13 @@ import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.FileDataSource;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.hainaos.vc.R;
|
||||
import com.hainaos.vc.base.mvvm.BaseMvvmActivity;
|
||||
import com.hainaos.vc.databinding.ActivityDecryptionPlayerBinding;
|
||||
import com.hainaos.vc.utils.JgyUtils;
|
||||
import com.hainaos.vc.video.AesDataSource;
|
||||
import com.hainaos.vc.video.AesDataSource2;
|
||||
import com.hjq.toast.Toaster;
|
||||
|
||||
public class DecryptionPlayerActivity extends BaseMvvmActivity<DecryptionPlayerViewModel, ActivityDecryptionPlayerBinding> {
|
||||
@@ -45,6 +48,8 @@ public class DecryptionPlayerActivity extends BaseMvvmActivity<DecryptionPlayerV
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
mExoPlayer = new SimpleExoPlayer.Builder(this).build();
|
||||
mViewDataBinding.playerView.setPlayer(mExoPlayer);
|
||||
|
||||
@@ -71,10 +76,11 @@ public class DecryptionPlayerActivity extends BaseMvvmActivity<DecryptionPlayerV
|
||||
// 创建自定义 Factory
|
||||
DataSource.Factory dataSourceFactory = () -> {
|
||||
try {
|
||||
return new AesDataSource(key, iv);
|
||||
// DataSource upstream = new FileDataSource();
|
||||
// return new AesDataSource2(upstream, key, iv);
|
||||
// return new AesDataSource(key, iv);
|
||||
DataSource upstream = new FileDataSource();
|
||||
return new AesDataSource2(upstream, key, iv);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initData: " + e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
};
|
||||
@@ -129,6 +135,13 @@ public class DecryptionPlayerActivity extends BaseMvvmActivity<DecryptionPlayerV
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
// 可选:清除标志以恢复正常息屏行为
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
private void initializePlayer() {
|
||||
// if (mExoPlayer == null) {
|
||||
// // 1. 创建 ExoPlayer 实例
|
||||
|
||||
@@ -64,6 +64,8 @@ public class TikTokActivity extends BaseMvvmActivity<TikTokViewModel, ActivityTi
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
Configuration config = getResources().getConfiguration();
|
||||
oldOrientation = config.orientation;
|
||||
Log.e(TAG, "orientation first:" + oldOrientation);
|
||||
@@ -232,5 +234,6 @@ public class TikTokActivity extends BaseMvvmActivity<TikTokViewModel, ActivityTi
|
||||
mTikTokRecyclerViewAdapter.unRegister();
|
||||
mTikTokRecyclerViewAdapter = null;
|
||||
}
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class UserActivity extends BaseMvvmActivity<UserViewModel, ActivityUserBi
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mViewDataBinding.tvSn.setText(LenovoCsdkUtil.getInstance().getSerial());
|
||||
mViewDataBinding.tvStorge.setText("已用" + Utils.getRemnantSize(this) + "/" + Utils.getDataTotalSize(this));
|
||||
mViewDataBinding.tvStorge.setText("可用" + Utils.getRemnantSize(this) + "/" + Utils.getDataTotalSize(this));
|
||||
}
|
||||
|
||||
private ActivityResultLauncher<Intent> mLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), new ActivityResultCallback<ActivityResult>() {
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.hainaos.vc.fragment.passwd.PasswdDialogFragment;
|
||||
import com.hainaos.vc.utils.GlideLoadUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.jessyan.autosize.AutoSizeCompat;
|
||||
|
||||
@@ -35,6 +36,13 @@ public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.Holder
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private Map<String, Integer> mCategoryUpdateInfoMap;
|
||||
|
||||
public void setCategoryUpdateInfoMap(Map<String, Integer> categoryUpdateInfoMap) {
|
||||
mCategoryUpdateInfoMap = categoryUpdateInfoMap;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
@@ -61,6 +69,11 @@ public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.Holder
|
||||
}
|
||||
|
||||
String uuid = categoryInfo.getUuid();
|
||||
if (mCategoryUpdateInfoMap != null && mCategoryUpdateInfoMap.get(uuid) != null && mCategoryUpdateInfoMap.get(uuid) != 0) {
|
||||
holder.iv_update.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.iv_update.setVisibility(View.GONE);
|
||||
}
|
||||
String icon = categoryInfo.getIcon();
|
||||
GlideLoadUtils.getInstance().glideLoad(mContext, icon, holder.iv_icon, R.drawable.icon_category);
|
||||
|
||||
@@ -91,7 +104,7 @@ public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.Holder
|
||||
|
||||
ConstraintLayout root;
|
||||
TextView tv_app_name;
|
||||
ImageView iv_icon, iv_lock_icon;
|
||||
ImageView iv_icon, iv_lock_icon, iv_update;
|
||||
|
||||
public Holder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
@@ -100,6 +113,7 @@ public class CategoryAdapter extends RecyclerView.Adapter<CategoryAdapter.Holder
|
||||
tv_app_name = itemView.findViewById(R.id.tv_app_name);
|
||||
iv_icon = itemView.findViewById(R.id.iv_icon);
|
||||
iv_lock_icon = itemView.findViewById(R.id.iv_lock_icon);
|
||||
iv_update = itemView.findViewById(R.id.iv_update);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,12 +24,14 @@ import com.arialyy.annotations.Download;
|
||||
import com.arialyy.aria.core.Aria;
|
||||
import com.arialyy.aria.core.download.DownloadEntity;
|
||||
import com.arialyy.aria.core.task.DownloadTask;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.hainaos.vc.R;
|
||||
import com.hainaos.vc.activity.player.DecryptionPlayerActivity;
|
||||
import com.hainaos.vc.activity.preview.VideoPreviewActivity;
|
||||
import com.hainaos.vc.bean.CategoryVideoInfo;
|
||||
import com.hainaos.vc.config.Permissions;
|
||||
import com.hainaos.vc.dialog.PermissionsDialog;
|
||||
import com.hainaos.vc.utils.FFmpegUtils;
|
||||
import com.hainaos.vc.utils.FileUtils;
|
||||
import com.hainaos.vc.utils.GlideLoadUtils;
|
||||
import com.hainaos.vc.utils.TimeUtils;
|
||||
@@ -43,6 +45,8 @@ import com.shehuan.niv.NiceImageView;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import me.jessyan.autosize.AutoSizeCompat;
|
||||
|
||||
public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdapter.VideoHolder> {
|
||||
@@ -82,30 +86,176 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
holder.tv_title.setText(name);
|
||||
}
|
||||
|
||||
String url = categoryVideoInfo.getFile_url();
|
||||
if (FileUtils.isLocalFileUriType(url)) {
|
||||
File file = new File(url);
|
||||
if (file.exists()) {
|
||||
String dirPaht = file.getParent();
|
||||
String videoFileNameWithoutEx = FileUtils.getFileNameWithoutExtension(url);
|
||||
File localCoverFile = new File(dirPaht + File.separator + videoFileNameWithoutEx + ".png");
|
||||
if (localCoverFile.exists()) {
|
||||
GlideLoadUtils.getInstance().glideLoad(mContext, localCoverFile, holder.video_image, R.drawable.picture_split);
|
||||
} else {
|
||||
Glide.with(mContext).load(localCoverFile).centerCrop().error(R.drawable.picture_split).into(holder.video_image);
|
||||
}
|
||||
String fileSize = Formatter.formatFileSize(mContext, file.length());
|
||||
if (url.endsWith(".hnv")) {
|
||||
holder.tv_duration.setText("视频时长: 00:00");
|
||||
} else {
|
||||
FFmpegUtils.getDurationInMilliseconds(url, new Observer<Integer>() {
|
||||
@Override
|
||||
public void onSubscribe(@io.reactivex.rxjava3.annotations.NonNull Disposable d) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@io.reactivex.rxjava3.annotations.NonNull Integer integer) {
|
||||
holder.tv_duration.setText("视频时长: " + TimeUtils.TimeFormat(integer * 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@io.reactivex.rxjava3.annotations.NonNull Throwable e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
holder.tv_size.setText("视频大小: " + fileSize);
|
||||
holder.tv_download.setVisibility(View.GONE);
|
||||
holder.tv_delete.setVisibility(View.VISIBLE);
|
||||
holder.tv_delete.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showDeleteLocalDialog(file.getAbsolutePath(), position);
|
||||
}
|
||||
});
|
||||
// holder.tv_download.setText("已下载");
|
||||
// holder.tv_download.setOnClickListener(new View.OnClickListener() {
|
||||
// @Override
|
||||
// public void onClick(View v) {
|
||||
//// if (file.exists()) {
|
||||
// showDeleteLocalDialog(file.getAbsolutePath(), position);
|
||||
//// }
|
||||
// }
|
||||
// });
|
||||
}
|
||||
} else {
|
||||
String cover = categoryVideoInfo.getCover();
|
||||
String md5 = categoryVideoInfo.getMd5();
|
||||
long sizeBytes = categoryVideoInfo.getFile_size();
|
||||
String fileSize = Formatter.formatFileSize(mContext, sizeBytes);
|
||||
holder.tv_size.setText("视频大小: " + fileSize);
|
||||
|
||||
String fileName = FileUtils.getFileNamefromURL(url);
|
||||
String fileExName = FileUtils.getFileType(url);
|
||||
|
||||
File file = new File(FileUtils.getHainaVideoPath(mContext) + mDirName + File.separator + fileName);
|
||||
File downloadFile = new File(FileUtils.getHainaVideoPath(mContext) + mDirName + File.separator + fileName);
|
||||
File localVideoFile = new File(FileUtils.getHainaVideoPath(mContext) + mDirName + File.separator + name + fileExName);
|
||||
|
||||
File file;
|
||||
if (localVideoFile.exists()) {
|
||||
file = localVideoFile;
|
||||
} else {
|
||||
file = downloadFile;
|
||||
}
|
||||
|
||||
DownloadEntity entity = Aria.download(mContext).getFirstDownloadEntity(url);
|
||||
if (entity == null) {
|
||||
if (file.exists()) {
|
||||
holder.tv_delete.setVisibility(View.VISIBLE);
|
||||
holder.tv_download.setText("已下载");
|
||||
} else {
|
||||
holder.tv_delete.setVisibility(View.GONE);
|
||||
holder.tv_download.setText("下载");
|
||||
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
|
||||
}
|
||||
} else {
|
||||
int state = entity.getState();
|
||||
switch (state) {
|
||||
case 1:
|
||||
if (file.exists()) {
|
||||
holder.tv_download.setText("删除");
|
||||
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_delete_button));
|
||||
holder.tv_delete.setVisibility(View.VISIBLE);
|
||||
holder.tv_download.setText("已下载");
|
||||
} else {
|
||||
holder.tv_delete.setVisibility(View.GONE);
|
||||
holder.tv_download.setText("下载");
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
holder.tv_delete.setVisibility(View.GONE);
|
||||
holder.tv_download.setText("停止");
|
||||
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
|
||||
break;
|
||||
case 3:
|
||||
holder.tv_delete.setVisibility(View.GONE);
|
||||
holder.tv_download.setText("等待");
|
||||
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
|
||||
break;
|
||||
case -1:
|
||||
case 0:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
holder.tv_delete.setVisibility(View.GONE);
|
||||
holder.tv_download.setText("下载");
|
||||
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
|
||||
break;
|
||||
case 4:
|
||||
holder.tv_delete.setVisibility(View.GONE);
|
||||
int percent = entity.getPercent();
|
||||
holder.tv_download.setText(percent + "%");
|
||||
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GlideLoadUtils.getInstance().glideLoad(mContext, cover, holder.video_image);
|
||||
|
||||
holder.tv_duration.setText("视频时长: " + TimeUtils.TimeFormat(categoryVideoInfo.getDuration() * 1000));
|
||||
holder.tv_delete.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (localVideoFile.exists()) {
|
||||
showDialog(localVideoFile.getAbsolutePath(), position);
|
||||
} else if (downloadFile.exists()) {
|
||||
showDialog(downloadFile.getAbsolutePath(), position);
|
||||
}
|
||||
}
|
||||
});
|
||||
holder.tv_download.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (entity == null) {
|
||||
if (file.exists()) {
|
||||
showDialog(file.getAbsolutePath(), position);
|
||||
} else {
|
||||
if (XXPermissions.isGranted(mContext, Permissions.STORAGE_PERMISSIONS)) {
|
||||
FileUtils.ariaDownload(mContext, mDirName, categoryVideoInfo);
|
||||
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, categoryVideoInfo);
|
||||
FileUtils.ariaDownload(mContext, mDirName, cover);
|
||||
// FileUtils.ariaDownloadCover(mContext, mDirName, cover, md5);
|
||||
} else {
|
||||
showPermissionsDialog(mContext);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case -1:
|
||||
@@ -115,39 +265,13 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
holder.tv_download.setText("下载");
|
||||
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
|
||||
break;
|
||||
case 4:
|
||||
int percent = entity.getPercent();
|
||||
holder.tv_download.setText(percent + "%");
|
||||
holder.tv_download.setBackground(mContext.getDrawable(R.drawable.bg_download_button));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
String cover = categoryVideoInfo.getCover();
|
||||
GlideLoadUtils.getInstance().glideLoad(mContext, cover, holder.video_image);
|
||||
|
||||
holder.tv_duration.setText("视频时长: " + TimeUtils.TimeFormat(categoryVideoInfo.getDuration() * 1000));
|
||||
|
||||
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);
|
||||
} else {
|
||||
showPermissionsDialog(mContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
holder.video_image.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
@@ -169,6 +293,8 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private PermissionsDialog mPermissionsDialog;
|
||||
|
||||
private void showPermissionsDialog(Context context) {
|
||||
@@ -244,6 +370,34 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void showDeleteLocalDialog(String path, int position) {
|
||||
CustomDialog dialog = new CustomDialog(mContext);
|
||||
dialog.setTitle("删除文件")
|
||||
.setMessage("确定要删除文件 " + FileUtils.getFileName(path) + "吗")
|
||||
.setPositive("确定")
|
||||
.setNegtive("取消")
|
||||
.setOnClickBottomListener(new CustomDialog.OnClickBottomListener() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
dialog.dismiss();
|
||||
File file = new File(path);
|
||||
if (file.delete()) {
|
||||
mLocalVideoInfos.remove(position);
|
||||
notifyDataSetChanged();
|
||||
ToastUtil.show("删除成功");
|
||||
} else {
|
||||
ToastUtil.show("删除失败,检查权限是否开启");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegtiveClick() {
|
||||
dialog.dismiss();
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void removeItem(int position) {
|
||||
if (null != mLocalVideoInfos.get(position)) {
|
||||
mLocalVideoInfos.remove(position);
|
||||
@@ -268,7 +422,7 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
|
||||
|
||||
static class VideoHolder extends RecyclerView.ViewHolder {
|
||||
NiceImageView video_image;
|
||||
TextView tv_title, tv_duration, tv_download, tv_size;
|
||||
TextView tv_title, tv_duration, tv_delete, tv_download, tv_size;
|
||||
ConstraintLayout root;
|
||||
|
||||
public VideoHolder(@NonNull View itemView) {
|
||||
@@ -277,6 +431,7 @@ public class CategoryVideoAdapter extends RecyclerView.Adapter<CategoryVideoAdap
|
||||
video_image = itemView.findViewById(R.id.video_image);
|
||||
tv_title = itemView.findViewById(R.id.tv_title);
|
||||
tv_duration = itemView.findViewById(R.id.tv_duration);
|
||||
tv_delete = itemView.findViewById(R.id.tv_delete);
|
||||
tv_download = itemView.findViewById(R.id.tv_download);
|
||||
tv_size = itemView.findViewById(R.id.tv_size);
|
||||
}
|
||||
|
||||
@@ -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,50 @@ 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);
|
||||
String videoFileNameWithoutEx = FileUtils.getFileNameWithoutExtension(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);
|
||||
if (file.getName().endsWith(".hnv")) {
|
||||
String dirPaht = file.getParent();
|
||||
Log.e(TAG, "onBindViewHolder: dirPaht = " + dirPaht);
|
||||
File localCoverFile = new File(dirPaht + File.separator + videoFileNameWithoutEx + ".png");
|
||||
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 {
|
||||
if (localCoverFile.exists()) {
|
||||
Glide.with(mContext).load(localCoverFile).centerCrop().error(R.drawable.picture_split).into(holder.video_image);
|
||||
} else {
|
||||
holder.video_image.setImageDrawable(mContext.getDrawable(R.drawable.picture_split));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (localCoverFile.exists()) {
|
||||
Glide.with(mContext).load(localCoverFile).centerCrop().error(R.drawable.picture_split).into(holder.video_image);
|
||||
} 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 +247,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.hainaos.vc.utils.LenovoCsdkUtil;
|
||||
import com.hainaos.vc.utils.LoginUtils;
|
||||
import com.hainaos.vc.utils.ToastUtil;
|
||||
import com.hjq.toast.Toaster;
|
||||
import com.tencent.bugly.crashreport.CrashReport;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
|
||||
public class BaseApplication extends Application {
|
||||
@@ -31,7 +32,13 @@ public class BaseApplication extends Application {
|
||||
|
||||
String rootDir = MMKV.initialize(this);
|
||||
Log.i(TAG, "mmkv root: " + rootDir);
|
||||
|
||||
LenovoCsdkUtil.init(this);
|
||||
|
||||
CrashReport.initCrashReport(getApplicationContext(), "3398d86ad9", false);
|
||||
CrashReport.setDeviceId(this, LenovoCsdkUtil.getInstance().getSerial());
|
||||
xcrash.XCrash.init(this);
|
||||
|
||||
JgyUtils.init(this);
|
||||
Aria.init(this);
|
||||
ConnectManager.init(this);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.hainaos.vc.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class CategoryUpdateInfo implements Serializable {
|
||||
private static final long serialVersionUID = 8109570355122467961L;
|
||||
|
||||
String category_uuid;
|
||||
int count;
|
||||
|
||||
public String getCategory_uuid() {
|
||||
return category_uuid;
|
||||
}
|
||||
|
||||
public void setCategory_uuid(String category_uuid) {
|
||||
this.category_uuid = category_uuid;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ public class CategoryVideoInfo implements Serializable {
|
||||
String name;
|
||||
String cover;
|
||||
long duration;
|
||||
long file_size;
|
||||
int file_size;
|
||||
String md5;
|
||||
int status;
|
||||
int download;
|
||||
@@ -75,11 +75,11 @@ public class CategoryVideoInfo implements Serializable {
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public long getFile_size() {
|
||||
public int getFile_size() {
|
||||
return file_size;
|
||||
}
|
||||
|
||||
public void setFile_size(long file_size) {
|
||||
public void setFile_size(int file_size) {
|
||||
this.file_size = file_size;
|
||||
}
|
||||
|
||||
|
||||
26
app/src/main/java/com/hainaos/vc/bean/PasswdInfo.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.hainaos.vc.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class PasswdInfo implements Serializable {
|
||||
private static final long serialVersionUID = -2630428449697230101L;
|
||||
|
||||
boolean correct;
|
||||
String msg;
|
||||
|
||||
public boolean isCorrect() {
|
||||
return correct;
|
||||
}
|
||||
|
||||
public void setCorrect(boolean correct) {
|
||||
this.correct = correct;
|
||||
}
|
||||
|
||||
public String getMsg() {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setMsg(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
26
app/src/main/java/com/hainaos/vc/bean/VideoUpdate.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.hainaos.vc.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class VideoUpdate implements Serializable {
|
||||
private static final long serialVersionUID = -1708177371148075587L;
|
||||
|
||||
int has_new;
|
||||
int video_num;
|
||||
|
||||
public int getHas_new() {
|
||||
return has_new;
|
||||
}
|
||||
|
||||
public void setHas_new(int has_new) {
|
||||
this.has_new = has_new;
|
||||
}
|
||||
|
||||
public int getVideo_num() {
|
||||
return video_num;
|
||||
}
|
||||
|
||||
public void setVideo_num(int video_num) {
|
||||
this.video_num = video_num;
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,13 @@ public class CommonConfig {
|
||||
|
||||
/*是否同意隐私协议*/
|
||||
public static final String AGREED_THE_PRIVACY_AGREEMENT = "agreed_the_privacy_agreement";
|
||||
|
||||
/*主页检测是否有视频更新*/
|
||||
public static final String MAIN_VIDEOS_CHECK_UPDATE_TIME = "MAIN_VIDEOS_CHECK_UPDATE_TIME";
|
||||
/*分类页面检测是否有视频更新*/
|
||||
public static final String CATEGORY_VIDEOS_CHECK_UPDATE_TIME = "CATEGORY_VIDEOS_CHECK_UPDATE_TIME";
|
||||
/*检查分类更新详情时间*/
|
||||
public static final String CHECK_CATEGORY_UPDATE_NUMBER_TIME = "CHECK_CATEGORY_UPDATE_NUMBER_TIME";
|
||||
|
||||
|
||||
}
|
||||
|
||||
194
app/src/main/java/com/hainaos/vc/dialog/UpdateDialog.java
Normal file
@@ -0,0 +1,194 @@
|
||||
package com.hainaos.vc.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hainaos.vc.R;
|
||||
|
||||
/**
|
||||
* description:自定义dialog
|
||||
*/
|
||||
|
||||
public class UpdateDialog extends AlertDialog {
|
||||
/**
|
||||
* 显示的标题
|
||||
*/
|
||||
private TextView titleTv;
|
||||
|
||||
/**
|
||||
* 显示的消息
|
||||
*/
|
||||
private TextView messageTv;
|
||||
|
||||
/**
|
||||
* 确认和取消按钮
|
||||
*/
|
||||
private TextView positiveBn;
|
||||
|
||||
/**
|
||||
* 按钮之间的分割线
|
||||
*/
|
||||
// private View columnLineView;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public UpdateDialog(Context context) {
|
||||
super(context, R.style.CustomDialog);
|
||||
this.mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* 都是内容数据
|
||||
*/
|
||||
private String message;
|
||||
private String title;
|
||||
private String positive;
|
||||
private int imageResId = -1;
|
||||
|
||||
/**
|
||||
* 底部是否只有一个按钮
|
||||
*/
|
||||
private boolean isSingle = false;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.dialog_update);
|
||||
//按空白处不能取消动画
|
||||
setCanceledOnTouchOutside(false);
|
||||
//初始化界面控件
|
||||
initView();
|
||||
//初始化界面数据
|
||||
refreshView();
|
||||
//初始化界面控件的事件
|
||||
initEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化界面的确定和取消监听器
|
||||
*/
|
||||
private void initEvent() {
|
||||
//设置确定按钮被点击后,向外界提供监听
|
||||
positiveBn.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (onClickBottomListener != null) {
|
||||
onClickBottomListener.onPositiveClick();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化界面控件的显示数据
|
||||
*/
|
||||
private void refreshView() {
|
||||
//如果用户自定了title和message
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
titleTv.setText(title);
|
||||
titleTv.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
titleTv.setVisibility(View.GONE);
|
||||
}
|
||||
if (!TextUtils.isEmpty(message)) {
|
||||
messageTv.setText(message);
|
||||
}
|
||||
//如果设置按钮的文字
|
||||
if (!TextUtils.isEmpty(positive)) {
|
||||
positiveBn.setText(positive);
|
||||
} else {
|
||||
positiveBn.setText("确定");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
super.show();
|
||||
refreshView();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化界面控件
|
||||
*/
|
||||
private void initView() {
|
||||
positiveBn = findViewById(R.id.positive);
|
||||
titleTv = findViewById(R.id.title);
|
||||
messageTv = findViewById(R.id.message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置确定取消按钮的回调
|
||||
*/
|
||||
private OnClickBottomListener onClickBottomListener;
|
||||
|
||||
public void setOnClickBottomListener(OnClickBottomListener onClickBottomListener) {
|
||||
this.onClickBottomListener = onClickBottomListener;
|
||||
}
|
||||
|
||||
public interface OnClickBottomListener {
|
||||
/**
|
||||
* 点击确定按钮事件
|
||||
*/
|
||||
void onPositiveClick();
|
||||
|
||||
/**
|
||||
* 点击取消按钮事件
|
||||
*/
|
||||
void onNegtiveClick();
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public UpdateDialog setMessage(String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public UpdateDialog setTitle(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPositive() {
|
||||
return positive;
|
||||
}
|
||||
|
||||
public UpdateDialog setPositive(String positive) {
|
||||
this.positive = positive;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getImageResId() {
|
||||
return imageResId;
|
||||
}
|
||||
|
||||
public boolean isSingle() {
|
||||
return isSingle;
|
||||
}
|
||||
|
||||
public UpdateDialog setSingle(boolean single) {
|
||||
isSingle = single;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UpdateDialog setImageResId(int imageResId) {
|
||||
this.imageResId = imageResId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
super.dismiss();
|
||||
}
|
||||
}
|
||||
@@ -43,16 +43,16 @@ public class CategoryViewModel extends BaseViewModel<FragmentCategoryBinding, Fr
|
||||
@Deprecated
|
||||
public void getDirList() {
|
||||
List<CategoryInfo> homeAppInfos = new ArrayList<>();
|
||||
homeAppInfos.add(new CategoryInfo("分类1", "", 1, "a1"));
|
||||
homeAppInfos.add(new CategoryInfo("分类2", "", 1, "a2"));
|
||||
homeAppInfos.add(new CategoryInfo("分类3", "", 1, "a3"));
|
||||
homeAppInfos.add(new CategoryInfo("分类4", "", 1, "a4"));
|
||||
homeAppInfos.add(new CategoryInfo("分类5", "", 1, "a5"));
|
||||
homeAppInfos.add(new CategoryInfo("分类6", "", 1, "a6"));
|
||||
homeAppInfos.add(new CategoryInfo("分类7", "", 1, "a7"));
|
||||
homeAppInfos.add(new CategoryInfo("分类8", "", 1, "a8"));
|
||||
homeAppInfos.add(new CategoryInfo("分类9", "", 1, "a9"));
|
||||
homeAppInfos.add(new CategoryInfo("分类10", "", 1, "a10"));
|
||||
homeAppInfos.add(new CategoryInfo("美容师招聘系统", "", 1, "a1"));
|
||||
homeAppInfos.add(new CategoryInfo("美容师培育系统", "", 1, "a2"));
|
||||
homeAppInfos.add(new CategoryInfo("薪酬设计系统", "", 1, "a3"));
|
||||
homeAppInfos.add(new CategoryInfo("拓客系统", "", 1, "a4"));
|
||||
homeAppInfos.add(new CategoryInfo("锁客系统", "", 1, "a5"));
|
||||
homeAppInfos.add(new CategoryInfo("培育系统", "", 1, "a6"));
|
||||
homeAppInfos.add(new CategoryInfo("晋级系统", "", 1, "a7"));
|
||||
homeAppInfos.add(new CategoryInfo("万元达标系统", "", 1, "a8"));
|
||||
homeAppInfos.add(new CategoryInfo("海纳五大指标", "", 1, "a9"));
|
||||
homeAppInfos.add(new CategoryInfo("设计课程", "", 1, "a10"));
|
||||
|
||||
// homeAppInfos.add(new CategoryInfo("下载视频", HomeAppAdapter.DOWNLOAD_CENTER, 1, ""));
|
||||
// homeAppInfos.add(new CategoryInfo("用户中心", HomeAppAdapter.USER_CENTER, 1, ""));
|
||||
|
||||
@@ -7,10 +7,12 @@ import com.hainaos.vc.BuildConfig;
|
||||
import com.hainaos.vc.bean.AppInfo;
|
||||
import com.hainaos.vc.bean.BaseResponse;
|
||||
import com.hainaos.vc.bean.CategoryInfo;
|
||||
import com.hainaos.vc.bean.CategoryUpdateInfo;
|
||||
import com.hainaos.vc.bean.CodeInfo;
|
||||
import com.hainaos.vc.bean.LoginInfo;
|
||||
import com.hainaos.vc.bean.UserInfo;
|
||||
import com.hainaos.vc.bean.VideoListData;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
import com.hainaos.vc.bean.uiuios.AppUpdateInfo;
|
||||
import com.hainaos.vc.config.CommonConfig;
|
||||
import com.hainaos.vc.network.api.AppApi;
|
||||
@@ -243,7 +245,32 @@ 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());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Observable<BaseResponse<VideoUpdate>> getVideoUpdateObservable(String category_uuid, String current_time) {
|
||||
String bearerToken = LoginUtils.getInstance().getBearerToken();
|
||||
return getVideoApi()
|
||||
.getVideoUpdate(bearerToken, category_uuid, current_time)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<VideoUpdate>> getVideoUpdateObservable(String current_time) {
|
||||
String bearerToken = LoginUtils.getInstance().getBearerToken();
|
||||
return getVideoApi()
|
||||
.getVideoUpdate(bearerToken, current_time)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<List<CategoryUpdateInfo>>> getCategoryUpdateObservable(String current_time) {
|
||||
String bearerToken = LoginUtils.getInstance().getBearerToken();
|
||||
return getVideoApi()
|
||||
.categoryUpdateNumber(bearerToken, current_time)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ public class UrlAddress {
|
||||
public static final String VIDEO_LIST = "videos/video-list";
|
||||
/*点击下载后统计上报*/
|
||||
public static final String VIDEOS_DOWNLOAD = "videos/download";
|
||||
/*检查视频分类更新*/
|
||||
public static final String VIDEOS_CHECK_UPDATE = "videos/check-update";
|
||||
/*检查分类列表是否有视频更新*/
|
||||
public static final String VIDEOS_CATEGORY_UPDATE = "videos/category-update-number";
|
||||
|
||||
|
||||
/*获取修改密码验证码*/
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.hainaos.vc.network.api;
|
||||
|
||||
import com.hainaos.vc.bean.BaseResponse;
|
||||
import com.hainaos.vc.bean.CategoryInfo;
|
||||
import com.hainaos.vc.bean.CategoryUpdateInfo;
|
||||
import com.hainaos.vc.bean.VideoListData;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
import com.hainaos.vc.network.UrlAddress;
|
||||
|
||||
import java.util.List;
|
||||
@@ -24,7 +26,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
|
||||
@@ -33,4 +36,23 @@ public interface VideoApi {
|
||||
@Header("Authorization") String token,
|
||||
@Query("uuid") String uuid
|
||||
);
|
||||
|
||||
@GET(UrlAddress.VIDEOS_CHECK_UPDATE)
|
||||
Observable<BaseResponse<VideoUpdate>> getVideoUpdate(
|
||||
@Header("Authorization") String token,
|
||||
@Query("category_uuid") String category_uuid,
|
||||
@Query("current_time") String current_time
|
||||
);
|
||||
|
||||
@GET(UrlAddress.VIDEOS_CHECK_UPDATE)
|
||||
Observable<BaseResponse<VideoUpdate>> getVideoUpdate(
|
||||
@Header("Authorization") String token,
|
||||
@Query("current_time") String current_time
|
||||
);
|
||||
|
||||
@GET(UrlAddress.VIDEOS_CATEGORY_UPDATE)
|
||||
Observable<BaseResponse<List<CategoryUpdateInfo>>> categoryUpdateNumber(
|
||||
@Header("Authorization") String token,
|
||||
@Query("current_time") String current_time
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,7 @@ import com.arialyy.aria.core.task.DownloadTask;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.hainaos.vc.R;
|
||||
import com.hainaos.vc.bean.uiuios.AriaDownloadInfo;
|
||||
import com.hainaos.vc.utils.LenovoCsdkUtil;
|
||||
import com.hainaos.vc.bean.CategoryVideoInfo;
|
||||
import com.hjq.toast.Toaster;
|
||||
|
||||
import java.io.File;
|
||||
@@ -46,9 +45,9 @@ public class DownloadService extends Service {
|
||||
Aria.download(this).register();
|
||||
|
||||
|
||||
// mNotificationManagerCompat = NotificationManagerCompat.from(this);
|
||||
mNotificationManagerCompat = NotificationManagerCompat.from(this);
|
||||
// createNotificationChannel();
|
||||
// createDownloadNotificationChannel();
|
||||
createDownloadNotificationChannel();
|
||||
// sendSimpleNotification();
|
||||
}
|
||||
|
||||
@@ -119,11 +118,11 @@ public class DownloadService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
private void sendDownloadRunning(AriaDownloadInfo ariaDownloadInfo, int progress) {
|
||||
private void sendDownloadRunning(CategoryVideoInfo ariaDownloadInfo, int progress) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_DOWNLOAD_ID)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
|
||||
.setContentTitle(ariaDownloadInfo.getAppName())
|
||||
.setContentTitle(ariaDownloadInfo.getName())
|
||||
.setContentText("下载中:" + progress + "%")
|
||||
.setAutoCancel(true)
|
||||
.setShowWhen(true)
|
||||
@@ -132,24 +131,26 @@ public class DownloadService extends Service {
|
||||
.setProgress(100, progress, false)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||
// notificationId is a unique int for each notification that you must define
|
||||
mNotificationManagerCompat.notify(ariaDownloadInfo.getAppId(), builder.build());
|
||||
// startForeground(ariaDownloadInfo.getAppId(), builder.build());
|
||||
// mNotificationManagerCompat.notify(ariaDownloadInfo.getAppId(), builder.build());
|
||||
startForeground(ariaDownloadInfo.getFile_size(), builder.build());
|
||||
}
|
||||
|
||||
private void sendDownloadComplete(AriaDownloadInfo ariaDownloadInfo, String path) {
|
||||
private void sendDownloadComplete(CategoryVideoInfo ariaDownloadInfo, String path) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_DOWNLOAD_ID)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
|
||||
.setContentTitle(ariaDownloadInfo.getAppName())
|
||||
.setContentTitle(ariaDownloadInfo.getName())
|
||||
.setContentText("下载完成")
|
||||
.setAutoCancel(true)
|
||||
.setShowWhen(true)
|
||||
.setOngoing(false)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setContentIntent(createIntent(path));
|
||||
// .setContentIntent(createIntent(path))
|
||||
;
|
||||
// notificationId is a unique int for each notification that you must define
|
||||
mNotificationManagerCompat.notify(ariaDownloadInfo.getAppId(), builder.build());
|
||||
// mNotificationManagerCompat.notify(ariaDownloadInfo.getAppId(), builder.build());
|
||||
startForeground(ariaDownloadInfo.getFile_size(), builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,11 +176,11 @@ public class DownloadService extends Service {
|
||||
return pendingIntent;
|
||||
}
|
||||
|
||||
private void sendDownloadFail(AriaDownloadInfo ariaDownloadInfo) {
|
||||
private void sendDownloadFail(CategoryVideoInfo ariaDownloadInfo) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_DOWNLOAD_ID)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
|
||||
.setContentTitle(ariaDownloadInfo.getAppName())
|
||||
.setContentTitle(ariaDownloadInfo.getName())
|
||||
.setContentText("下载失败")
|
||||
.setAutoCancel(true)
|
||||
.setShowWhen(true)
|
||||
@@ -187,17 +188,18 @@ public class DownloadService extends Service {
|
||||
.setOnlyAlertOnce(true)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||
// notificationId is a unique int for each notification that you must define
|
||||
mNotificationManagerCompat.notify(ariaDownloadInfo.getAppId(), builder.build());
|
||||
// mNotificationManagerCompat.notify(ariaDownloadInfo.getAppId(), builder.build());
|
||||
startForeground(ariaDownloadInfo.getFile_size(), builder.build());
|
||||
}
|
||||
|
||||
@Download.onTaskRunning
|
||||
void running(DownloadTask task) {
|
||||
String jsonString = task.getExtendField();
|
||||
Log.e(TAG, "running: " + "正在下载:" + task.getPercent() + "% " + jsonString);
|
||||
AriaDownloadInfo ariaDownloadInfo = getAriaDownloadInfo(jsonString);
|
||||
CategoryVideoInfo ariaDownloadInfo = getAriaDownloadInfo(jsonString);
|
||||
if (ariaDownloadInfo != null) {
|
||||
Toaster.show("正在下载: " + ariaDownloadInfo.getAppName() + "\t" + task.getPercent() + "%");
|
||||
// sendDownloadRunning(ariaDownloadInfo, task.getPercent());
|
||||
Toaster.show("正在下载: " + ariaDownloadInfo.getName() + "\t" + task.getPercent() + "%");
|
||||
sendDownloadRunning(ariaDownloadInfo, task.getPercent());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,14 +207,14 @@ public class DownloadService extends Service {
|
||||
void taskComplete(DownloadTask task) {
|
||||
String path = task.getFilePath();
|
||||
Log.e(TAG, "taskComplete: " + path);
|
||||
if (path.endsWith(".apk")) {
|
||||
LenovoCsdkUtil.getInstance().installPackage(task.getFilePath());
|
||||
if (path.endsWith(".hnv")) {
|
||||
// LenovoCsdkUtil.getInstance().installPackage(task.getFilePath());
|
||||
String jsonString = task.getExtendField();
|
||||
Log.e(TAG, "taskComplete: " + "下载完成:" + jsonString);
|
||||
AriaDownloadInfo ariaDownloadInfo = getAriaDownloadInfo(jsonString);
|
||||
CategoryVideoInfo ariaDownloadInfo = getAriaDownloadInfo(jsonString);
|
||||
if (ariaDownloadInfo != null) {
|
||||
Toaster.show("下载完成: " + "\t" + ariaDownloadInfo.getAppName());
|
||||
// sendDownloadComplete(ariaDownloadInfo, task.getFilePath());
|
||||
Toaster.show("下载完成: " + "\t" + ariaDownloadInfo.getName());
|
||||
sendDownloadComplete(ariaDownloadInfo, task.getFilePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,19 +224,19 @@ public class DownloadService extends Service {
|
||||
Log.e(TAG, "taskFail: ");
|
||||
String jsonString = task.getExtendField();
|
||||
Log.e(TAG, "taskFail: " + "下载失败:" + jsonString);
|
||||
AriaDownloadInfo ariaDownloadInfo = getAriaDownloadInfo(jsonString);
|
||||
CategoryVideoInfo ariaDownloadInfo = getAriaDownloadInfo(jsonString);
|
||||
if (ariaDownloadInfo != null) {
|
||||
Toaster.show("下载失败: " + "\t" + ariaDownloadInfo.getAppName());
|
||||
// sendDownloadFail(ariaDownloadInfo);
|
||||
Toaster.show("下载失败: " + "\t" + ariaDownloadInfo.getName());
|
||||
sendDownloadFail(ariaDownloadInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private AriaDownloadInfo getAriaDownloadInfo(String jsonString) {
|
||||
private CategoryVideoInfo getAriaDownloadInfo(String jsonString) {
|
||||
if (!TextUtils.isEmpty(jsonString)) {
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<AriaDownloadInfo>() {
|
||||
Type type = new TypeToken<CategoryVideoInfo>() {
|
||||
}.getType();
|
||||
AriaDownloadInfo ariaDownloadInfo = null;
|
||||
CategoryVideoInfo ariaDownloadInfo = null;
|
||||
try {
|
||||
ariaDownloadInfo = gson.fromJson(jsonString, type);
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.hainaos.vc.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
@@ -10,6 +12,7 @@ import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.arialyy.aria.core.Aria;
|
||||
import com.hainaos.vc.bean.AppInfo;
|
||||
import com.hainaos.vc.bean.CategoryVideoInfo;
|
||||
import com.hainaos.vc.bean.uiuios.AppUpdateInfo;
|
||||
import com.hainaos.vc.bean.uiuios.AriaDownloadInfo;
|
||||
import com.hainaos.vc.gson.GsonUtils;
|
||||
@@ -17,6 +20,8 @@ import com.hainaos.vc.service.DownloadService;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class FileUtils {
|
||||
private static final String TAG = "FileUtils";
|
||||
@@ -40,6 +45,77 @@ public class FileUtils {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static boolean isLocalFileUriType(String uriString) {
|
||||
if (TextUtils.isEmpty(uriString)) {
|
||||
return false;
|
||||
}
|
||||
if (uriString.startsWith("/")) {
|
||||
return true;
|
||||
}
|
||||
// 将字符串解析为 Uri 对象
|
||||
Uri uri = Uri.parse(uriString);
|
||||
// 获取 Scheme
|
||||
String scheme = uri.getScheme();
|
||||
if (scheme != null) {
|
||||
Log.e(TAG, "isLocalFileUriType: " + scheme);
|
||||
switch (scheme.toLowerCase()) {
|
||||
case "http":
|
||||
case "https":
|
||||
return false;
|
||||
case "content":
|
||||
case "file":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> videoFormat = new HashSet<String>() {{
|
||||
this.add(".hnv");
|
||||
this.add(".mp4");
|
||||
this.add(".avi");
|
||||
this.add(".mkv");
|
||||
this.add(".flv");
|
||||
}};
|
||||
private static Set<String> pictureFormat = new HashSet<String>() {{
|
||||
this.add(".png");
|
||||
this.add(".jpg");
|
||||
this.add(".jpeg");
|
||||
this.add(".bmp");
|
||||
}};
|
||||
|
||||
public static boolean isVideoFile(String fileName) {
|
||||
if (TextUtils.isEmpty(fileName)) {
|
||||
return false;
|
||||
} else {
|
||||
if (!fileName.startsWith(".")) {
|
||||
return videoFormat.contains(getFileType(fileName));
|
||||
} else {
|
||||
return videoFormat.contains(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFileType(String url) {
|
||||
if (TextUtils.isEmpty(url)) {
|
||||
return "";
|
||||
}
|
||||
Log.e(TAG, "getFileType: " + url);
|
||||
if (!url.contains("/")) {
|
||||
if (url.contains(".")) {
|
||||
return url.substring(url.indexOf("."));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
String fileName = url.substring(url.lastIndexOf("/"));
|
||||
return fileName.substring(fileName.indexOf("."));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换文件大小 MB
|
||||
*/
|
||||
@@ -124,6 +200,7 @@ public class FileUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static void ariaDownload(Context context, String dirName, String url, String md5) {
|
||||
String downLoadPath = getHainaVideoPath(context) + dirName + File.separator;
|
||||
File dirFile = new File(downLoadPath);
|
||||
@@ -144,6 +221,12 @@ public class FileUtils {
|
||||
// .ignoreFilePathOccupy()
|
||||
.setExtendField(url)
|
||||
.create(); //启动下载}
|
||||
Intent intent = new Intent(context, DownloadService.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent);
|
||||
} else {
|
||||
context.startService(intent);
|
||||
}
|
||||
} else {
|
||||
Log.e("ariaDownload", "fileName = " + fileName + " exists");
|
||||
}
|
||||
@@ -154,6 +237,104 @@ public class FileUtils {
|
||||
Aria.download(context)
|
||||
.load(url) //读取下载地址
|
||||
.setFilePath(file.getAbsolutePath())
|
||||
// .ignoreFilePathOccupy()
|
||||
.setExtendField(url)
|
||||
.create(); //启动下载}
|
||||
Intent intent = new Intent(context, DownloadService.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent);
|
||||
} else {
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void ariaDownload(Context context, String dirName, CategoryVideoInfo categoryVideoInfo) {
|
||||
String url = categoryVideoInfo.getFile_url();
|
||||
String md5 = categoryVideoInfo.getMd5();
|
||||
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()) {
|
||||
String fileMD5 = com.blankj.utilcode.util.FileUtils.getFileMD5ToString(file);
|
||||
Log.e("ariaDownload", "fileOnlineMD5=" + md5);
|
||||
Log.e("ariaDownload", "fileMD5=" + fileMD5);
|
||||
if (!TextUtils.isEmpty(md5)) {
|
||||
if (!md5.equals(fileMD5)) {
|
||||
Aria.download(context)
|
||||
.load(url) //读取下载地址
|
||||
.setFilePath(file.getAbsolutePath())
|
||||
// .ignoreFilePathOccupy()
|
||||
.setExtendField(GsonUtils.toJSONString(categoryVideoInfo))
|
||||
.create(); //启动下载}
|
||||
Intent intent = new Intent(context, DownloadService.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent);
|
||||
} else {
|
||||
context.startService(intent);
|
||||
}
|
||||
} else {
|
||||
Log.e("ariaDownload", "fileName = " + fileName + " exists");
|
||||
}
|
||||
} else {
|
||||
Log.e("ariaDownload", url + " no md5 params , skip");
|
||||
}
|
||||
} else {
|
||||
Aria.download(context)
|
||||
.load(url) //读取下载地址
|
||||
.setFilePath(file.getAbsolutePath())
|
||||
// .ignoreFilePathOccupy()
|
||||
.setExtendField(GsonUtils.toJSONString(categoryVideoInfo))
|
||||
.create(); //启动下载}
|
||||
Intent intent = new Intent(context, DownloadService.class);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(intent);
|
||||
} else {
|
||||
context.startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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(); //启动下载}
|
||||
@@ -204,12 +385,7 @@ public class FileUtils {
|
||||
.setExtendField(GsonUtils.toJSONString(ariaDownloadInfo))
|
||||
.create(); //启动下载}
|
||||
}
|
||||
Intent intent = new Intent(context, DownloadService.class);
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// context.startForegroundService(intent);
|
||||
// } else {
|
||||
context.startService(intent);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,4 +29,10 @@ public class TimeUtils {
|
||||
Date date = new Date(second * 1000);
|
||||
return sdf.format(date);
|
||||
}
|
||||
|
||||
public static String transferMillisecondToDate(long millisecond) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Date date = new Date(millisecond);
|
||||
return sdf.format(date);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,33 @@
|
||||
package com.hainaos.vc.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.StatFs;
|
||||
import android.text.format.Formatter;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class Utils {
|
||||
|
||||
@SuppressLint({"MissingPermission", "HardwareIds"})
|
||||
public static String getSerial() {
|
||||
// if (BuildConfig.DEBUG) {
|
||||
// return "T98005H1024GB32GB";
|
||||
// @SuppressLint({"MissingPermission", "HardwareIds"})
|
||||
// public static String getSerial() {
|
||||
//// if (BuildConfig.DEBUG) {
|
||||
//// return "T98005H1024GB32GB";
|
||||
//// }
|
||||
// String serial = "unknow";
|
||||
// try {
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//9.0+
|
||||
// serial = Build.getSerial();
|
||||
// } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {//8.0+
|
||||
// serial = Build.SERIAL;
|
||||
// } else {//8.0-
|
||||
// Class<?> c = Class.forName("android.os.SystemProperties");
|
||||
// Method get = c.getMethod("get", String.class);
|
||||
// serial = (String) get.invoke(c, "ro.serialno");
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// Log.e("getSerial", "读取设备序列号异常:" + e.toString());
|
||||
// }
|
||||
// return serial;
|
||||
// }
|
||||
String serial = "unknow";
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//9.0+
|
||||
serial = Build.getSerial();
|
||||
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {//8.0+
|
||||
serial = Build.SERIAL;
|
||||
} else {//8.0-
|
||||
Class<?> c = Class.forName("android.os.SystemProperties");
|
||||
Method get = c.getMethod("get", String.class);
|
||||
serial = (String) get.invoke(c, "ro.serialno");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e("getSerial", "读取设备序列号异常:" + e.toString());
|
||||
}
|
||||
return serial;
|
||||
}
|
||||
|
||||
public static String getDataTotalSize(Context context) {
|
||||
StatFs sf = new StatFs(context.getCacheDir().getAbsolutePath());
|
||||
|
||||
@@ -2,14 +2,15 @@ package com.hainaos.vc.video;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSourceInputStream;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -42,132 +43,80 @@ public class AesDataSource2 implements DataSource {
|
||||
|
||||
@Override
|
||||
public long open(DataSpec dataSpec) throws IOException {
|
||||
// 1. 获取请求的绝对位置
|
||||
// 1. 先关闭之前的流(如果存在)
|
||||
close();
|
||||
|
||||
final int AES_BLOCK_SIZE = 16;
|
||||
long position = dataSpec.position;
|
||||
|
||||
// AES 块大小通常为 16 字节
|
||||
final int AES_BLOCK_SIZE = 16;
|
||||
|
||||
// 2. 计算块索引 (Block Index) 和 块内偏移 (Offset inside the block)
|
||||
// 例如:position = 100,blockIndex = 6 (96字节处),offset = 4
|
||||
// 2. 计算 CTR 块索引和块内偏移
|
||||
long blockIndex = position / AES_BLOCK_SIZE;
|
||||
int offsetInBlock = (int) (position % AES_BLOCK_SIZE);
|
||||
|
||||
// 3. 计算对齐后的起始读取位置 (必须是 16 的倍数)
|
||||
long startPosition = blockIndex * AES_BLOCK_SIZE;
|
||||
|
||||
// 3. 构建新的 DataSpec,从 16 字节对齐的位置开始读取
|
||||
// 如果原始请求了长度,我们需要增加 offsetInBlock 以保证能读够对应的数据
|
||||
long requestLength = dataSpec.length != C.LENGTH_UNSET
|
||||
? dataSpec.length + offsetInBlock
|
||||
: C.LENGTH_UNSET;
|
||||
|
||||
DataSpec alignedSpec = dataSpec.buildUpon()
|
||||
.setPosition(startPosition)
|
||||
.setLength(requestLength)
|
||||
.build();
|
||||
|
||||
// 4. 打开上层数据源并获取实际可读长度
|
||||
long upstreamLength = upstream.open(alignedSpec);
|
||||
|
||||
try {
|
||||
// 4. 初始化 Cipher
|
||||
// 5. 初始化 Cipher
|
||||
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(secretKey, "AES");
|
||||
|
||||
// 【关键步骤】:根据 blockIndex 计算新的 IV
|
||||
// CTR 模式下:NewIV = OriginalIV + blockIndex
|
||||
byte[] newIv = getAdjustedIv(this.iv, blockIndex);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(newIv);
|
||||
|
||||
// 计算调整后的 IV (NewIV = OriginalIV + blockIndex)
|
||||
byte[] adjustedIv = getAdjustedIv(this.iv, blockIndex);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(adjustedIv);
|
||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
|
||||
|
||||
// 5. 让上层数据源 (FileDataSource) 从对齐的位置 (startPosition) 开始读
|
||||
// 注意:我们修改了 position,但保持 length 不变 (或者处理 open ended)
|
||||
long length = dataSpec.length != C.LENGTH_UNSET ? dataSpec.length + offsetInBlock : C.LENGTH_UNSET;
|
||||
// 6. 包装成解密流
|
||||
// 注意:我们直接包装 upstream 提供的 InputStream
|
||||
cipherInputStream = new CipherInputStream(new UpstreamInputStream(upstream), cipher);
|
||||
|
||||
DataSpec newSpec = dataSpec.buildUpon()
|
||||
.setPosition(startPosition)
|
||||
.setLength(length)
|
||||
.build();
|
||||
|
||||
// 打开上层流
|
||||
InputStream inputStream = new DataSourceInputStream(upstream, newSpec);
|
||||
|
||||
// 创建解密流
|
||||
cipherInputStream = new CipherInputStream(inputStream, cipher);
|
||||
|
||||
// 6. 【重要】跳过块内的偏移量
|
||||
// 因为我们要给 ExoPlayer 返回的是从 dataSpec.position 开始的数据,
|
||||
// 但我们是从 startPosition (前一个16倍数) 开始解密的,所以前面多读的 offsetInBlock 个字节是无用的。
|
||||
// 7. 丢弃块内偏移量多出的字节
|
||||
if (offsetInBlock > 0) {
|
||||
forceSkip(cipherInputStream, offsetInBlock);
|
||||
forceSkip(offsetInBlock);
|
||||
}
|
||||
|
||||
opened = true;
|
||||
|
||||
// 计算剩余长度
|
||||
// 8. 计算返回给 ExoPlayer 的剩余长度
|
||||
if (dataSpec.length != C.LENGTH_UNSET) {
|
||||
bytesRemaining = dataSpec.length;
|
||||
} else if (upstreamLength != C.LENGTH_UNSET) {
|
||||
bytesRemaining = upstreamLength - offsetInBlock;
|
||||
} else {
|
||||
long upstreamLength = upstream.open(newSpec); // 这一步其实已经在 DataSourceInputStream 里做过了,这里仅作逻辑参考
|
||||
// 通常 upstream.open 返回的是从 startPosition 开始的长度
|
||||
// 如果 upstream 支持长度解析:
|
||||
// bytesRemaining = upstreamLength != C.LENGTH_UNSET ? upstreamLength - offsetInBlock : C.LENGTH_UNSET;
|
||||
bytesRemaining = C.LENGTH_UNSET;
|
||||
}
|
||||
|
||||
opened = true;
|
||||
return bytesRemaining;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据块索引计算新的 IV。
|
||||
* CTR 模式将 IV 视为一个大整数 (BigEndian),每过一个块,计数器 +1。
|
||||
*/
|
||||
private byte[] getAdjustedIv(byte[] originalIv, long blockIndex) {
|
||||
// 使用 BigInteger 处理大数加法,防止溢出
|
||||
BigInteger ivVal = new BigInteger(1, originalIv);
|
||||
BigInteger offset = BigInteger.valueOf(blockIndex);
|
||||
BigInteger newIvVal = ivVal.add(offset);
|
||||
|
||||
byte[] newIv = newIvVal.toByteArray();
|
||||
|
||||
// BigInteger.toByteArray() 可能会因为符号位导致长度变为 17 (如果是正数且最高位是1)
|
||||
// 或者因为数值较小导致长度小于 16。必须确保返回 16 字节。
|
||||
byte[] result = new byte[16];
|
||||
int srcOffset = newIv.length > 16 ? newIv.length - 16 : 0;
|
||||
int dstOffset = newIv.length < 16 ? 16 - newIv.length : 0;
|
||||
int copyLength = newIv.length > 16 ? 16 : newIv.length;
|
||||
|
||||
System.arraycopy(newIv, srcOffset, result, dstOffset, copyLength);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制跳过指定字节数。CipherInputStream 的 skip 有时不可靠,建议循环 read。
|
||||
*/
|
||||
private void forceSkip(InputStream stream, int bytesToSkip) throws IOException {
|
||||
long skipped = 0;
|
||||
byte[] skipBuffer = new byte[1024];
|
||||
while (skipped < bytesToSkip) {
|
||||
int toRead = (int) Math.min(bytesToSkip - skipped, skipBuffer.length);
|
||||
int read = stream.read(skipBuffer, 0, toRead);
|
||||
if (read == -1) {
|
||||
break;
|
||||
}
|
||||
skipped += read;
|
||||
throw new IOException("Failed to initialize AES cipher", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer, int offset, int readLength) throws IOException {
|
||||
if (readLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
int bytesToRead = readLength;
|
||||
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||
bytesToRead = (int) Math.min(readLength, bytesRemaining);
|
||||
}
|
||||
if (bytesToRead == 0) {
|
||||
return C.RESULT_END_OF_INPUT;
|
||||
}
|
||||
if (readLength == 0) return 0;
|
||||
|
||||
// 限制读取长度,不要超过 bytesRemaining
|
||||
int bytesToRead = (bytesRemaining == C.LENGTH_UNSET)
|
||||
? readLength
|
||||
: (int) Math.min(readLength, bytesRemaining);
|
||||
|
||||
if (bytesToRead <= 0 && bytesRemaining != C.LENGTH_UNSET) return C.RESULT_END_OF_INPUT;
|
||||
|
||||
int bytesRead = cipherInputStream.read(buffer, offset, bytesToRead);
|
||||
if (bytesRead == -1) {
|
||||
if (bytesRemaining != C.LENGTH_UNSET) {
|
||||
// 预期还要读数据但读不到了,抛错
|
||||
throw new IOException("End of stream reached prematurely");
|
||||
}
|
||||
return C.RESULT_END_OF_INPUT;
|
||||
}
|
||||
|
||||
@@ -177,6 +126,41 @@ public class AesDataSource2 implements DataSource {
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
private void forceSkip(int bytesToSkip) throws IOException {
|
||||
int skipped = 0;
|
||||
byte[] skipBuffer = new byte[Math.min(bytesToSkip, 2048)];
|
||||
while (skipped < bytesToSkip) {
|
||||
int toRead = Math.min(bytesToSkip - skipped, skipBuffer.length);
|
||||
int read = cipherInputStream.read(skipBuffer, 0, toRead);
|
||||
if (read == -1) throw new EOFException("Check your offset and file size.");
|
||||
skipped += read;
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] getAdjustedIv(byte[] originalIv, long blockIndex) {
|
||||
if (blockIndex == 0) return originalIv;
|
||||
BigInteger ivVal = new BigInteger(1, originalIv);
|
||||
BigInteger newIvVal = ivVal.add(BigInteger.valueOf(blockIndex));
|
||||
byte[] raw = newIvVal.toByteArray();
|
||||
byte[] result = new byte[16];
|
||||
int length = Math.min(raw.length, 16);
|
||||
System.arraycopy(raw, Math.max(0, raw.length - 16), result, 16 - length, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (cipherInputStream != null) {
|
||||
cipherInputStream.close();
|
||||
cipherInputStream = null;
|
||||
}
|
||||
upstream.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Uri getUri() {
|
||||
return upstream.getUri();
|
||||
@@ -187,14 +171,25 @@ public class AesDataSource2 implements DataSource {
|
||||
return upstream.getResponseHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (opened) {
|
||||
opened = false;
|
||||
if (cipherInputStream != null) {
|
||||
cipherInputStream.close(); // 这也会关闭 upstream
|
||||
cipherInputStream = null;
|
||||
/**
|
||||
* 一个简单的内部类,将 DataSource 转为 InputStream
|
||||
*/
|
||||
private static final class UpstreamInputStream extends java.io.InputStream {
|
||||
private final DataSource dataSource;
|
||||
private final byte[] singleByteArray = new byte[1];
|
||||
|
||||
public UpstreamInputStream(DataSource dataSource) {
|
||||
this.dataSource = dataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return read(singleByteArray) == -1 ? -1 : (singleByteArray[0] & 0xFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return dataSource.read(b, off, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 51 KiB |
BIN
app/src/main/res/drawable-hdpi/picture_split.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
15
app/src/main/res/drawable/ic_video_update.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="100dp"
|
||||
android:height="41.4dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="41.4">
|
||||
<path
|
||||
android:pathData="M19.4,19.9h11.7v2.7h-11.7v-2.7Z"
|
||||
android:fillColor="#d81e06" />
|
||||
<path
|
||||
android:pathData="M79.9,0.8H20C9,0.8 0,9.8 0,20.9v16.7c0,2 1.4,3.4 3.4,3.4h76.6c11,0 20.1,-9 20.1,-20.1S91,0.8 79.9,0.8ZM61,13.7h5c-0.3,-0.7 -0.7,-1.3 -1.2,-2.1l1.5,-0.7c0.5,0.8 1,1.6 1.3,2.4l-0.8,0.4h4.9v1.5h-10.9s0,-1.5 0,-1.5ZM60.3,19.1h3.8c-0.7,-1.1 -1.3,-1.9 -1.9,-2.7l1.2,-0.9c0.5,0.6 1.2,1.5 2,2.6l-1.3,0.9h3c0.8,-1.2 1.6,-2.4 2.4,-3.6l1.6,0.9c-0.7,0.9 -1.5,1.9 -2.1,2.7h3.5v1.5h-12.2s0,-1.5 0,-1.4ZM35.7,15.6h-13.9c-0.5,0.9 -1,1.8 -1.6,2.6h12.7v12.4c0,1.9 -0.9,2.9 -2.8,2.9h-3.7c0,-0.6 -0.2,-1.2 -0.4,-1.9 1.4,0 2.5,0.2 3.5,0.2s1.6,-0.6 1.6,-1.6v-1.6h-11.6v5.3h-1.9v-12c-1.1,1.3 -2.3,2.5 -3.7,3.8 -0.3,-0.6 -0.7,-1.1 -1.1,-1.7 3,-2.5 5.3,-5.3 7,-8.3h-6.4v-1.7h7.2c0.5,-0.9 0.8,-1.9 1.2,-3l2,0.5c-0.3,0.9 -0.7,1.7 -1,2.5h13.1s0,1.7 0,1.7ZM58.7,33.4c-6.2,-0.2 -10.6,-0.9 -13.6,-2.2 -1.8,1.2 -4.3,2.1 -7.6,2.9 -0.3,-0.6 -0.7,-1.1 -1.1,-1.6 2.8,-0.4 5.1,-1.2 6.8,-2.2 -1.3,-0.9 -2.5,-2.1 -3.6,-3.6l1.2,-1h-1.8v-9.7h8.1v-2.1h-10v-1.6h21.3v1.6h-9.5v2.1h7.7v10.8h-1.7v-1.2h-6.5c-0.4,1.9 -1.1,3.2 -1.9,4.3 3.1,1.2 7.4,1.7 12.9,1.7 -0.3,0.7 -0.7,1.2 -1,1.8h0ZM60.3,30.6c0.7,-1.2 1.6,-2.7 2.5,-4.7l1.6,0.7c-0.8,1.7 -1.6,3.3 -2.5,4.8 -0.4,-0.3 -1,-0.6 -1.6,-0.8ZM67.4,31c0,1.6 -0.8,2.5 -2.4,2.5h-2.2c0,-0.5 -0.2,-1.1 -0.2,-1.7 0.9,0 1.6,0.2 2.1,0.2 0.7,0 1.1,-0.4 1.1,-1.2v-5.7h-4.8v-1.5h4.8v-2.2h1.6v2.2h4.5v1.5h-4.5v6ZM68.1,27l1.3,-0.9c0.8,1.2 1.6,2.2 2.2,3.3l-1.5,1c-0.6,-1.2 -1.3,-2.3 -2.1,-3.4ZM83.5,21.6h-3.3v12.2h-1.7v-12.2h-3.5v1.2c0,4.8 -1.2,8.5 -3.4,11.1 -0.3,-0.5 -0.8,-1 -1.2,-1.5 2,-2.2 3,-5.4 3,-9.6v-9.7c3.2,0 6.2,-0.4 8.9,-0.9l0.6,1.8c-3,0.4 -5.7,0.7 -7.8,0.8v5.4h8.5v1.6Z"
|
||||
android:fillColor="#d81e06" />
|
||||
<path
|
||||
android:pathData="M19.4,24.2h11.7v2.8h-11.7v-2.8ZM41.2,25.7c1.1,1.5 2.3,2.6 3.9,3.5 0.9,-0.7 1.4,-1.8 1.9,-3.5h-5.7ZM55.2,17.5h-5.9c0,0.9 0,1.8 0,2.5h6v-2.5ZM47.4,21.6h-6.4v2.5h6.1c0.2,-0.7 0.2,-1.6 0.3,-2.5ZM47.5,17.5h-6.4v2.5h6.3c0,-0.7 0,-1.6 0,-2.5ZM55.2,21.6h-6c0,0.9 0,1.7 -0.2,2.5h6.3v-2.5h0Z"
|
||||
android:fillColor="#d81e06" />
|
||||
</vector>
|
||||
@@ -63,6 +63,19 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="海纳美业学习机" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_update"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_video_update"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
@@ -71,10 +84,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"
|
||||
@@ -87,9 +100,9 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_nodata"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
@@ -109,7 +122,7 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
<!-- </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>-->
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
@@ -70,16 +70,30 @@
|
||||
android:id="@+id/tv_total"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="视频:0个"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/iv_update"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/video_total" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_update"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_video_update"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
||||
@@ -373,7 +373,7 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/iv_more6"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="已用28GB/128GB" />
|
||||
tools:text="可用28GB/128GB" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_more6"
|
||||
|
||||
90
app/src/main/res/layout/dialog_update.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.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:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:background="@drawable/bg_dialog"
|
||||
android:minWidth="240dp"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center_vertical"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="visible"
|
||||
tools:text="消息提示" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="3dp"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:minHeight="50dp"
|
||||
android:textColor="@color/gray"
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="提示消息提示消息提示消息提示消息提示消息" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/linearLayout3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/positive"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_login_button"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="8sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
tools:text="确定" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -28,6 +28,18 @@
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/icon_category" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_update"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_video_update"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -67,14 +79,15 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginStart="2dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:maxLines="2"
|
||||
android:minLines="2"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="8sp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -28,19 +28,6 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_download"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/bg_download_button"
|
||||
android:text="下载"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
@@ -51,7 +38,7 @@
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tv_download"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tv_delete"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/video_image"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
@@ -60,7 +47,9 @@
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="3"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="3"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="13sp"
|
||||
@@ -70,8 +59,9 @@
|
||||
<TextView
|
||||
android:id="@+id/tv_duration"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="2"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/text_content"
|
||||
@@ -81,8 +71,9 @@
|
||||
<TextView
|
||||
android:id="@+id/tv_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="2"
|
||||
android:gravity="center_vertical"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/text_content"
|
||||
@@ -91,6 +82,39 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_delete"
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="center"
|
||||
android:background="@drawable/bg_delete_button"
|
||||
android:text="删除"
|
||||
android:textColor="@color/white"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/tv_download"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_download"
|
||||
android:layout_width="56dp"
|
||||
android:gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:background="@drawable/bg_download_button"
|
||||
android:text="已下载"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="10sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -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
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
SET class_path=target/video-encryptor-1.0-SNAPSHOT.jar;
|
||||
native-image --no-fallback -H:ConfigurationFileDirectories=META-INF/native-image --allow-incomplete-classpath -classpath %class_path% com.penngo.gralvm.MainSwing
|
||||
SET class_path=target\video-encryptor-1.0.1.jar;
|
||||
|
||||
native-image --no-fallback -H:ConfigurationFileDirectories=src\main\resources\META-INF\native-image --allow-incomplete-classpath -classpath %class_path% com.hnos.video.VideoEncryptorGUI
|
||||
@@ -1,7 +1,9 @@
|
||||
@REM 使用idea 的mvn clean package
|
||||
|
||||
java -agentlib:native-image-agent=config-output-dir=./META-INF/native-image -jar video-encryptor-1.0.jar
|
||||
cd target
|
||||
|
||||
native-image -jar target\video-encryptor-1.0.jar --no-fallback -H:ConfigurationFileDirectories=.\src\main\resources\META-INF\native-image
|
||||
java -agentlib:native-image-agent=config-output-dir=src\main\resources\META-INF\native-image -jar video-encryptor-1.0.1.jar
|
||||
|
||||
native-image -jar target\video-encryptor-1.0.1.jar --no-fallback -H:ConfigurationFileDirectories=src\main\resources\META-INF\native-image
|
||||
|
||||
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\editbin.exe" /SUBSYSTEM:WINDOWS video-encryptor-1.0.exe
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.hainaos</groupId>
|
||||
<artifactId>video-encryptor</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.0.1</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
|
||||
@@ -21,8 +21,7 @@ public class VideoEncryptorGUI extends JFrame {
|
||||
// 加密算法常量(保持和VideoEncryptor一致)
|
||||
private static final String ALGORITHM = "AES/CTR/NoPadding";
|
||||
// 视频文件扩展名列表(可根据需要扩展)
|
||||
private static final List<String> VIDEO_EXTENSIONS = Arrays.asList("mp4", "avi", "mov", "mkv", "flv", "wmv",
|
||||
"rmvb");
|
||||
private static final List<String> VIDEO_EXTENSIONS = List.of("mp4");
|
||||
// 测试用AES密钥(16字节,AES-128),实际使用请替换为安全生成的密钥
|
||||
private static final byte[] TEST_KEY = "1234567890123456".getBytes();
|
||||
// 测试用IV向量(16字节,CTR模式要求IV长度等于块大小),实际使用请随机生成
|
||||
|
||||