package com.datarecovery.master.module.imgrecover; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.provider.MediaStore; import android.text.TextUtils; import android.util.Pair; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Transformations; import com.atmob.app.lib.base.BaseViewModel; import com.atmob.app.lib.livedata.SingleLiveEvent; import com.atmob.common.logging.AtmobLog; import com.atmob.common.runtime.ActivityUtil; import com.atmob.common.runtime.ContextUtil; import com.datarecovery.master.R; import com.datarecovery.master.data.consts.EventId; import com.datarecovery.master.data.repositories.DeviceFuncRepository; import com.datarecovery.master.handler.EventHelper; import com.datarecovery.master.module.member.MemberActivity; import com.datarecovery.master.module.member.MemberType; import com.datarecovery.master.sdk.bugly.BuglyHelper; import com.datarecovery.master.utils.BoxingUtil; import com.datarecovery.master.utils.FileUtil; import com.datarecovery.master.utils.ImageDeepDetector; import com.datarecovery.master.utils.Maps; import com.datarecovery.master.utils.MediaStoreHelper; import com.datarecovery.master.utils.ReportUtil; import com.datarecovery.master.utils.ToastUtil; import org.reactivestreams.Subscription; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import atmob.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import atmob.reactivex.rxjava3.annotations.NonNull; import atmob.reactivex.rxjava3.core.FlowableSubscriber; import atmob.reactivex.rxjava3.core.Single; import atmob.reactivex.rxjava3.core.SingleObserver; import atmob.reactivex.rxjava3.core.SingleSource; import atmob.reactivex.rxjava3.disposables.Disposable; import atmob.rxjava.utils.RxJavaUtil; import dagger.hilt.android.lifecycle.HiltViewModel; @HiltViewModel public class ImageRecoverViewModel extends BaseViewModel { private final long SCANNING_COUNTDOWN = 1000 * 60 * 6; private final long SCANNING_IS_TRIAL_COUNTDOWN = 1000 * 5; private final List detectedPhotoList = new ArrayList<>(); private final List detectedWxList = new ArrayList<>(); private final List detectedQQList = new ArrayList<>(); private final List detectedOtherList = new ArrayList<>(); private final List selectList = new ArrayList<>(); private final MutableLiveData> detectedPhotoImg = new MutableLiveData<>(); private final MutableLiveData> detectedWxImg = new MutableLiveData<>(); private final MutableLiveData> detectedQQImg = new MutableLiveData<>(); private final MutableLiveData> detectedOtherImg = new MutableLiveData<>(); private final MutableLiveData> selectedList = new MutableLiveData<>(); private final SingleLiveEvent> deleteUriListSdk11 = new SingleLiveEvent<>(); private final SingleLiveEvent showTrialFinishDialog = new SingleLiveEvent<>(); private final SingleLiveEvent showTrialExportFailDialog = new SingleLiveEvent<>(); private final MutableLiveData showTrialView = new MutableLiveData<>(); private final DeviceFuncRepository deviceFuncRepository; private LiveData detectedPhotoTitle; private LiveData detectedWxTitle; private LiveData detectedQQTitle; private LiveData detectedOtherTitle; private final SingleLiveEvent, Integer>> previewEvent = new SingleLiveEvent<>(); private final SingleLiveEvent showClearDialog = new SingleLiveEvent<>(); private final SingleLiveEvent showScanDialogEvent = new SingleLiveEvent<>(); private final SingleLiveEvent showLoadingEvent = new SingleLiveEvent<>(); private final SingleLiveEvent scrollTop = new SingleLiveEvent<>(); private final SingleLiveEvent notifyList = new SingleLiveEvent<>(); private final MutableLiveData barTitle = new MutableLiveData<>(); private final MutableLiveData checkAll = new MutableLiveData<>(false); //总探测到的图片数量 private int totalCount = 0; private final MutableLiveData totalDetectedCount = new MutableLiveData<>(); private final MutableLiveData detectedLastFileName = new MutableLiveData<>(); private LiveData selectedCountTxt; private Disposable scanDisposable; @MemberType private String type; private SingleObserver dealMediaObserver; //是否会员试用 private boolean isTrial; private long detectTime; @Inject public ImageRecoverViewModel(DeviceFuncRepository deviceFuncRepository) { this.deviceFuncRepository = deviceFuncRepository; barTitle.setValue(ContextUtil.getContext().getString(R.string.iamge_recover_all)); initLiveData(); } public LiveData getScrollTop() { return scrollTop; } public LiveData getShowTrialFinishDialog() { return showTrialFinishDialog; } public LiveData getShowTrialExportFailDialog() { return showTrialExportFailDialog; } public LiveData getDetectedLastFileName() { return detectedLastFileName; } public LiveData getShowTrialView() { return showTrialView; } public boolean isTrial() { if (Objects.equals(type, MemberType.APP_IMAGE_CLEAN)) { return !deviceFuncRepository.isHaveAuth(type); } else { return isTrial; } } public LiveData> getDeleteUriListSdk11() { return deleteUriListSdk11; } public LiveData getShowClearDialog() { return showClearDialog; } public LiveData getNotifyList() { return notifyList; } public LiveData getShowLoadingEvent() { return showLoadingEvent; } public LiveData, Integer>> getPreviewEvent() { return previewEvent; } public LiveData getTotalDetectedCount() { return totalDetectedCount; } public LiveData getSelectedCountTxt() { return selectedCountTxt; } public LiveData> getSelectedList() { return selectedList; } public LiveData> getDetectedPhotoImg() { return detectedPhotoImg; } public LiveData> getDetectedWxImg() { return detectedWxImg; } public LiveData> getDetectedQQImg() { return detectedQQImg; } public LiveData> getDetectedOtherImg() { return detectedOtherImg; } public LiveData getDetectedPhotoTitle() { return detectedPhotoTitle; } public LiveData getDetectedWxTitle() { return detectedWxTitle; } public LiveData getDetectedQQTitle() { return detectedQQTitle; } public LiveData getDetectedOtherTitle() { return detectedOtherTitle; } public LiveData getShowScanDialogEvent() { return showScanDialogEvent; } public LiveData getCheckAll() { return checkAll; } public LiveData getBarTitle() { return barTitle; } public String getType() { return type; } private void initLiveData() { detectedPhotoTitle = Transformations.map(detectedPhotoImg, list -> ContextUtil.getContext().getString(R.string.photo_count, (list == null || list.isEmpty()) ? 0 : list.size())); detectedWxTitle = Transformations.map(detectedWxImg, list -> ContextUtil.getContext().getString(R.string.wx_count, (list == null || list.isEmpty()) ? 0 : list.size())); detectedQQTitle = Transformations.map(detectedQQImg, list -> ContextUtil.getContext().getString(R.string.qq_count, (list == null || list.isEmpty()) ? 0 : list.size())); detectedOtherTitle = Transformations.map(detectedOtherImg, list -> ContextUtil.getContext().getString(R.string.other_count, (list == null || list.isEmpty()) ? 0 : list.size())); } public void onCheckAllClick(boolean isCheck) { checkAll.setValue(isCheck); setListCheck(detectedPhotoList, isCheck); setListCheck(detectedWxList, isCheck); setListCheck(detectedQQList, isCheck); setListCheck(detectedOtherList, isCheck); if (isCheck) { selectList.clear(); selectList.addAll(detectedPhotoList); selectList.addAll(detectedWxList); selectList.addAll(detectedQQList); selectList.addAll(detectedOtherList); } else { selectList.clear(); } selectedList.setValue(selectList); } private void setListCheck(List list, boolean isCheck) { if (list == null || list.isEmpty()) { return; } for (ImageDeepDetector.ImageFile imageFile : list) { imageFile.setCheck(isCheck); } } public void startImageScanning() { if (scanDisposable != null && !scanDisposable.isDisposed()) { return; } ImageDeepDetector.detect(ContextUtil.getContext()) .take(isTrial ? SCANNING_IS_TRIAL_COUNTDOWN : SCANNING_COUNTDOWN, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new FlowableSubscriber>() { @Override public void onSubscribe(@NonNull Subscription s) { s.request(Integer.MAX_VALUE); scanDisposable = Disposable.fromSubscription(s); addDisposable(scanDisposable); showScanDialogEvent.setValue(true); checkAll.setValue(false); totalCount = 0; totalDetectedCount.setValue(0); detectTime = System.currentTimeMillis(); detectedPhotoList.clear(); detectedWxList.clear(); detectedQQList.clear(); detectedOtherList.clear(); selectList.clear(); } @Override public void onNext(List imageFiles) { if (imageFiles == null) { return; } totalCount += imageFiles.size(); totalDetectedCount.setValue(totalCount); int photoCount = 0; int wxCount = 0; int qqCount = 0; int otherCount = 0; for (ImageDeepDetector.ImageFile imageFile : imageFiles) { if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) { detectedPhotoList.add(imageFile); photoCount++; } else if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) { detectedWxList.add(imageFile); wxCount++; } else if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) { detectedQQList.add(imageFile); qqCount++; } else { detectedOtherList.add(imageFile); otherCount++; } detectedLastFileName.setValue(imageFile.getName()); } if (photoCount > 0) { detectedPhotoImg.setValue(detectedPhotoList); } if (wxCount > 0) { detectedWxImg.setValue(detectedWxList); } if (qqCount > 0) { detectedQQImg.setValue(detectedQQList); } if (otherCount > 0) { detectedOtherImg.setValue(detectedOtherList); } scrollTop.call(); } @Override public void onError(Throwable t) { showScanDialogEvent.setValue(false); } @Override public void onComplete() { setFreeExport(); AtmobLog.d("ImageRecoverViewModel", System.currentTimeMillis() - detectTime + "ms - " + totalCount); if (isTrial) { showTrialFinishDialog.call(); } showScanDialogEvent.setValue(false); } }); } private void setFreeExport() { if (isTrial) { showTrialView.setValue(true); if (!detectedPhotoList.isEmpty()) { detectedPhotoList.get(0).setTrial(true); return; } if (!detectedWxList.isEmpty()) { detectedWxList.get(0).setTrial(true); return; } if (!detectedQQList.isEmpty()) { detectedQQList.get(0).setTrial(true); return; } if (!detectedOtherList.isEmpty()) { detectedOtherList.get(0).setTrial(true); return; } } } public void cancelScan() { if (scanDisposable != null) scanDisposable.dispose(); showScanDialogEvent.setValue(false); setFreeExport(); } public void setItemCheck(@NonNull ImageDeepDetector.ImageFile imageFile) { imageFile.setCheck(!imageFile.isCheck()); if (imageFile.isCheck()) { selectList.add(imageFile); } else { selectList.remove(imageFile); } selectedList.setValue(selectList); } public void checkPreview(@NonNull ImageDeepDetector.ImageFile imageFile) { if (isTrial) { sendPreviewEvent(imageFile); return; } if (Objects.equals(type, MemberType.APP_IMAGE_CLEAN)) { return; } if (Objects.equals(type, MemberType.APP_IMAGE_RECOVER) && !deviceFuncRepository.isHaveAuth(type)) { return; } else if (Objects.equals(type, MemberType.APP_IMAGE_CLEAN) && !deviceFuncRepository.isHaveAuth(type)) { return; } sendPreviewEvent(imageFile); } public void sendPreviewEvent(@NonNull ImageDeepDetector.ImageFile imageFile) { List allDetectedList = getAllDetectedList(); int position = getPosition(allDetectedList, imageFile); previewEvent.setValue(new Pair<>(allDetectedList, position)); } public void onOperationClick() { if (Objects.equals(type, MemberType.APP_IMAGE_CLEAN)) { if (deviceFuncRepository.isHaveAuth(type)) { showClearDialog.call(); } else { MemberActivity.start(ActivityUtil.getTopActivity(), type); } } else { onExportClick(); } } public void executeImageDelete() { if (selectList.isEmpty()) { return; } Single.just(Build.VERSION.SDK_INT) .map(sdkInt -> sdkInt >= Build.VERSION_CODES.R) .map(isSdk11 -> { List safList = new ArrayList<>(); List uriList = new ArrayList<>(); if (isSdk11) { for (ImageDeepDetector.ImageFile item : selectList) { File tempFile = new File(item.getPath()); long mediaID = getFilePathToMediaID(tempFile.getAbsolutePath(), ContextUtil.getContext()); if (mediaID > 0) { Uri Uri_one = ContentUris.withAppendedId(MediaStore.Images.Media.getContentUri("external"), mediaID); uriList.add(Uri_one); } else { safList.add(item); } } } else { safList.addAll(selectList); } return new Pair<>(safList, uriList); }) .map(pair -> { if (pair.first != null && !pair.first.isEmpty()) { for (ImageDeepDetector.ImageFile item : pair.first) { try { item.delete(); if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) { detectedPhotoList.remove(item); } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) { detectedWxList.remove(item); } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) { detectedQQList.remove(item); } else { detectedOtherList.remove(item); } } catch (Exception ignore) { } } selectList.removeAll(pair.first); selectedList.postValue(selectList); notifyList.postValue(null); } return pair.second; }) .map(uriList -> { if (uriList != null && !uriList.isEmpty()) { deleteUriListSdk11.postValue(uriList); return false; } else { return true; } }) .flatMap(aBoolean -> { if (BoxingUtil.boxing(aBoolean)) { return Single.just(true); } else { return (SingleSource) observer -> this.dealMediaObserver = observer; } }) .compose(RxJavaUtil.SingleSchedule.io2Main()) .subscribe(new SingleObserver() { @Override public void onSubscribe(@NonNull Disposable d) { showLoadingEvent.setValue(true); } @Override public void onSuccess(@NonNull Boolean result) { showLoadingEvent.setValue(false); if (result) { for (ImageDeepDetector.ImageFile item : selectList) { try { item.delete(); if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) { detectedPhotoList.remove(item); } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) { detectedWxList.remove(item); } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) { detectedQQList.remove(item); } else { detectedOtherList.remove(item); } } catch (Exception ignore) { } } ToastUtil.show(R.string.delete_success, ToastUtil.LENGTH_SHORT); selectList.clear(); selectedList.setValue(selectList); notifyList.call(); } checkAll.setValue(false); } @Override public void onError(@NonNull Throwable throwable) { showLoadingEvent.setValue(false); ToastUtil.show(throwable.getMessage(), ToastUtil.LENGTH_SHORT); BuglyHelper.postCatchedException(new Exception("图片删除:", throwable)); } }); } public long getFilePathToMediaID(String songPath, Context context) { long id = 0; ContentResolver cr = context.getContentResolver(); Uri uri = MediaStore.Files.getContentUri("external"); String selection = MediaStore.Audio.Media.DATA; String[] selectionArgs = {songPath}; String[] projection = {MediaStore.Audio.Media._ID}; String sortOrder = MediaStore.Audio.Media.TITLE + " ASC"; Cursor cursor = cr.query(uri, projection, selection + "=?", selectionArgs, null); if (cursor != null) { while (cursor.moveToNext()) { int idIndex = cursor.getColumnIndex(MediaStore.Audio.Media._ID); id = Long.parseLong(cursor.getString(idIndex)); } } return id; } public void onExportClick() { if (selectList.isEmpty()) { return; } if (isTrial) { for (ImageDeepDetector.ImageFile imageFile : selectList) { if (!imageFile.isTrial()) { showTrialExportFailDialog.call(); return; } } } else { if (Objects.equals(type, MemberType.APP_IMAGE_RECOVER) && !deviceFuncRepository.isHaveAuth(type)) { return; } else if (Objects.equals(type, MemberType.APP_IMAGE_CLEAN) && !deviceFuncRepository.isHaveAuth(type)) { return; } } showLoadingEvent.setValue(true); RxJavaUtil.doInBackground(() -> { for (ImageDeepDetector.ImageFile item : selectList) { MediaStoreHelper.saveToSharedStorage(MediaStoreHelper.TYPE_IMAGE, item.newInputStream(), FileUtil.getExportImageFileName(item.getName())); item.setCheck(false); } return true; }, o -> { checkAll.setValue(false); showLoadingEvent.setValue(false); ToastUtil.show(R.string.export_success, ToastUtil.LENGTH_SHORT); selectList.clear(); selectedList.setValue(selectList); }, throwable -> { showLoadingEvent.setValue(false); ToastUtil.show(throwable.getMessage(), ToastUtil.LENGTH_SHORT); BuglyHelper.postCatchedException(new Exception("图片导出:", throwable)); }); } public List getAllDetectedList() { ArrayList allList = new ArrayList<>(); allList.addAll(detectedPhotoList); allList.addAll(detectedWxList); allList.addAll(detectedQQList); allList.addAll(detectedOtherList); return allList; } public int getPosition(@NonNull List list, ImageDeepDetector.ImageFile target) { return list.indexOf(target); } public void setType(@MemberType String type) { if (!TextUtils.isEmpty(this.type)) { return; } this.type = type; switch (type) { case MemberType.APP_IMAGE_CLEAN: EventHelper.report(EventId.hf1000514); EventHelper.timeEvent(EventId.hf1000529); selectedCountTxt = Transformations.map(selectedList, list -> { if (list == null || list.isEmpty()) { return ContextUtil.getContext().getString(R.string.delete); } return ContextUtil.getContext().getString(R.string.delete_count, list.size()); }); break; case MemberType.APP_IMAGE_RECOVER: EventHelper.report(EventId.hf1000506); EventHelper.timeEvent(EventId.hf1000521); selectedCountTxt = Transformations.map(selectedList, list -> { if (list == null || list.isEmpty()) { return ContextUtil.getContext().getString(R.string.export); } return ContextUtil.getContext().getString(R.string.export_count, list.size()); }); break; } } @Override protected void onCleared() { super.onCleared(); switch (type) { case MemberType.APP_IMAGE_CLEAN: EventHelper.report(EventId.hf1000529); break; case MemberType.APP_IMAGE_RECOVER: EventHelper.report(EventId.hf1000521); break; } } public void callDealComplete(boolean isDelete) { if (dealMediaObserver != null) { dealMediaObserver.onSuccess(isDelete); } } public void setIsTrial(boolean isTrial) { this.isTrial = isTrial; } public void onTrialRecoverClick() { MemberActivity.start(ActivityUtil.getTopActivity(), type); } public void onViewTrialRecoverClick() { EventHelper.report(EventId.hf1001117, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(type))); MemberActivity.start(ActivityUtil.getTopActivity(), type); } public void scrollPosition(int lastCompletelyItemPosition, int itemCount) { if (scanDisposable == null || !scanDisposable.isDisposed()) { return; } if (lastCompletelyItemPosition == itemCount - 1 && !BoxingUtil.boxing(showScanDialogEvent.getValue())) { showTrialFinishDialog.call(); EventHelper.report(EventId.hf1001119, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(type))); } } }