1.1.8 增加更新弹窗和提示
This commit is contained in:
@@ -18,8 +18,8 @@ android {
|
||||
//There are no CERT files because If the mini sdk version is 23+, the AGP will ignore the V1 scheme signature.
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode 16
|
||||
versionName "1.1.5"
|
||||
versionCode 19
|
||||
versionName "1.1.8"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
|
||||
@@ -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,9 +25,10 @@ import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
|
||||
public class CategoryListViewModel extends BaseViewModel<ActivityCategoryListBinding, ActivityEvent> {
|
||||
|
||||
private static final String TAG = "CategoryViewModel";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@Override
|
||||
public ActivityCategoryListBinding getVDBinding() {
|
||||
return binding;
|
||||
@@ -66,4 +72,75 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Log.e("getCategoryUpdate", "onError: " + e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("getCategoryUpdate", "onComplete: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ 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;
|
||||
@@ -135,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,14 @@ 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.TimeUtils;
|
||||
import com.hainaos.vc.utils.VideoUtils;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
|
||||
@@ -33,9 +37,10 @@ import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import wseemann.media.FFmpegMediaMetadataRetriever;
|
||||
|
||||
public class LocalCategoryViewModel extends BaseViewModel<ActivityCategoryLocalBinding, ActivityEvent> {
|
||||
|
||||
private static final String TAG = "LocalCategoryViewModel";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@Override
|
||||
public ActivityCategoryLocalBinding getVDBinding() {
|
||||
return binding;
|
||||
@@ -49,6 +54,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>>() {
|
||||
@@ -138,6 +144,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) {
|
||||
@@ -164,4 +171,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: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.hainaos.vc.bean.BaseResponse;
|
||||
import com.hainaos.vc.bean.CategoryInfo;
|
||||
import com.hainaos.vc.bean.CategoryVideoInfo;
|
||||
import com.hainaos.vc.bean.VideoListData;
|
||||
import com.hainaos.vc.bean.VideoUpdate;
|
||||
import com.hainaos.vc.config.CommonConfig;
|
||||
import com.hainaos.vc.databinding.ActivityCategoryVideoBinding;
|
||||
import com.hainaos.vc.fragment.passwd.PasswdDialogFragment;
|
||||
@@ -146,6 +147,17 @@ public class CategoryVideoActivity extends BaseMvvmActivity<CategoryVideoViewMod
|
||||
}
|
||||
});
|
||||
|
||||
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.getUuid(), mPasswd);
|
||||
|
||||
}
|
||||
|
||||
@@ -8,9 +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.ActivityCategoryVideoBinding;
|
||||
import com.hainaos.vc.network.NetInterfaceManager;
|
||||
import com.hainaos.vc.utils.FileUtils;
|
||||
import com.hainaos.vc.utils.TimeUtils;
|
||||
import com.tencent.mmkv.MMKV;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
|
||||
@@ -29,6 +33,8 @@ public class CategoryVideoViewModel extends BaseViewModel<ActivityCategoryVideoB
|
||||
|
||||
private static final String TAG = "CategoryVideoViewModel";
|
||||
|
||||
private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE);
|
||||
|
||||
@Override
|
||||
public ActivityCategoryVideoBinding getVDBinding() {
|
||||
return binding;
|
||||
@@ -71,6 +77,7 @@ public class CategoryVideoViewModel extends BaseViewModel<ActivityCategoryVideoB
|
||||
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>>() {
|
||||
@@ -97,4 +104,40 @@ public class CategoryVideoViewModel extends BaseViewModel<ActivityCategoryVideoB
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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: ");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -12,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> {
|
||||
@@ -46,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);
|
||||
|
||||
@@ -72,9 +76,9 @@ 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);
|
||||
@@ -131,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
26
app/src/main/java/com/hainaos/vc/bean/VideoUpdate.java
Normal file
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
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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -248,6 +250,31 @@ public class NetInterfaceManager {
|
||||
.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());
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<AppInfo>> getCheckUpdateObservable() {
|
||||
return mRetrofit.create(AppApi.class)
|
||||
.checkUpdate(BuildConfig.APPLICATION_ID)
|
||||
|
||||
@@ -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;
|
||||
@@ -34,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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 27 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 51 KiB |
15
app/src/main/res/drawable/ic_video_update.xml
Normal file
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
|
||||
@@ -76,39 +89,39 @@
|
||||
<!-- android:layout_width="match_parent"-->
|
||||
<!-- android:layout_height="match_parent">-->
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_video"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_video"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_nodata"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_nodata"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="没有数据"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:text="没有数据"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<!-- </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
|
||||
|
||||
90
app/src/main/res/layout/dialog_update.xml
Normal file
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"
|
||||
@@ -72,9 +84,9 @@
|
||||
android:maxLines="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>
|
||||
|
||||
Reference in New Issue
Block a user