package com.datarecovery.master.module.imgrecover; import android.app.PendingIntent; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; import android.content.IntentSender; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.provider.MediaStore; import android.text.TextUtils; import android.util.Log; import android.util.Pair; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.Observer; import androidx.lifecycle.Transformations; import com.atmob.app.lib.base.BaseViewModel; import com.atmob.app.lib.livedata.SingleLiveEvent; 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.MemberType; import com.datarecovery.master.utils.BoxingUtil; import com.datarecovery.master.utils.FileUtil; import com.datarecovery.master.utils.ImageDeepDetector; import com.datarecovery.master.utils.MediaStoreHelper; import com.datarecovery.master.utils.ToastUtil; import com.datarecovery.master.utils.xfile.XSAFFile; import org.reactivestreams.Subscription; import java.io.File; import java.io.InputStream; 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.reactivex.rxjava3.functions.Function; 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 MutableLiveData> detectedPhotoImg = new MutableLiveData<>(new ArrayList<>()); private final MutableLiveData> detectedWxImg = new MutableLiveData<>(new ArrayList<>()); private final MutableLiveData> detectedQQImg = new MutableLiveData<>(new ArrayList<>()); private final MutableLiveData> detectedOtherImg = new MutableLiveData<>(new ArrayList<>()); private final SingleLiveEvent> deleteUriListSdk11 = new SingleLiveEvent<>(); private final DeviceFuncRepository deviceFuncRepository; private LiveData detectedPhotoTitle; private LiveData detectedWxTitle; private LiveData detectedQQTitle; private LiveData detectedOtherTitle; private final SingleLiveEvent previewEvent = new SingleLiveEvent<>(); private final SingleLiveEvent detectedFinish = new SingleLiveEvent<>(); private final SingleLiveEvent showClearDialog = new SingleLiveEvent<>(); private final SingleLiveEvent showScanDialogEvent = new SingleLiveEvent<>(); private final SingleLiveEvent showLoadingEvent = 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> selectedList = new MutableLiveData<>(new ArrayList<>()); private LiveData selectedCountTxt; private Disposable scanDisposable; @MemberType private String type; private SingleObserver dealMediaObserver; @Inject public ImageRecoverViewModel(DeviceFuncRepository deviceFuncRepository) { this.deviceFuncRepository = deviceFuncRepository; barTitle.setValue(ContextUtil.getContext().getString(R.string.iamge_recover_all)); initLiveData(); } public LiveData> getDeleteUriListSdk11() { return deleteUriListSdk11; } public LiveData getShowClearDialog() { return showClearDialog; } public LiveData getNotifyList() { return notifyList; } public LiveData getShowLoadingEvent() { return showLoadingEvent; } public LiveData getPreviewEvent() { return previewEvent; } public LiveData getDetectedFinish() { return detectedFinish; } 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); List selectList = getList(selectedList); setListCheck(detectedPhotoImg.getValue(), isCheck); setListCheck(detectedWxImg.getValue(), isCheck); setListCheck(detectedQQImg.getValue(), isCheck); setListCheck(detectedOtherImg.getValue(), isCheck); if (isCheck) { selectList.clear(); selectList.addAll(getList(detectedPhotoImg)); selectList.addAll(getList(detectedWxImg)); selectList.addAll(getList(detectedQQImg)); selectList.addAll(getList(detectedOtherImg)); } 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); } } private List getList(LiveData> liveData) { List selectList = liveData.getValue(); if (selectList == null) { selectList = new ArrayList<>(); } return selectList; } public void startImageScanning() { if (scanDisposable != null && !scanDisposable.isDisposed()) { return; } ImageDeepDetector.detect(ContextUtil.getContext()) .take(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); } @Override public void onNext(List imageFiles) { if (imageFiles == null) { return; } totalCount += imageFiles.size(); totalDetectedCount.setValue(totalCount); for (ImageDeepDetector.ImageFile imageFile : imageFiles) { MutableLiveData> liveData; if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) { liveData = detectedPhotoImg; } else if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) { liveData = detectedWxImg; } else if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) { liveData = detectedQQImg; } else { liveData = detectedOtherImg; } List list = getList(liveData); list.add(0, imageFile); liveData.setValue(list); } } @Override public void onError(Throwable t) { showScanDialogEvent.setValue(false); } @Override public void onComplete() { detectedFinish.call(); } }); } public void cancelScan() { if (scanDisposable != null) scanDisposable.dispose(); } public void setItemCheck(@NonNull ImageDeepDetector.ImageFile imageFile) { imageFile.setCheck(!imageFile.isCheck()); List list = getList(this.selectedList); if (imageFile.isCheck()) { list.add(imageFile); } else { list.remove(imageFile); } selectedList.setValue(list); } public void checkPreview(@NonNull ImageDeepDetector.ImageFile imageFile) { 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; } previewEvent.setValue(imageFile); } public void onOperationClick() { if (Objects.equals(type, MemberType.APP_IMAGE_CLEAN)) { showClearDialog.call(); } else { onExportClick(); } } public void executeImageDelete() { List list = getList(selectedList); if (list.size() == 0) { 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 : list) { 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(list); } return new Pair<>(safList, uriList); }) .map(pair -> { if (pair.first != null && !pair.first.isEmpty()) { for (ImageDeepDetector.ImageFile item : pair.first) { item.delete(); MutableLiveData> liveData; if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) { liveData = detectedPhotoImg; } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) { liveData = detectedWxImg; } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) { liveData = detectedQQImg; } else { liveData = detectedOtherImg; } List itemList = getList(liveData); itemList.remove(item); } list.removeAll(pair.first); selectedList.postValue(list); 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 : list) { try { item.delete(); } catch (Exception e) { e.printStackTrace(); } MutableLiveData> liveData; if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) { liveData = detectedPhotoImg; } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) { liveData = detectedWxImg; } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) { liveData = detectedQQImg; } else { liveData = detectedOtherImg; } List itemList = getList(liveData); itemList.remove(item); } ToastUtil.show(R.string.delete_success, ToastUtil.LENGTH_SHORT); list.clear(); selectedList.setValue(list); notifyList.call(); } checkAll.setValue(false); } @Override public void onError(@NonNull Throwable e) { showLoadingEvent.setValue(false); ToastUtil.show(R.string.delete_fail, ToastUtil.LENGTH_SHORT); } }); } 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() { List list = getList(selectedList); if (list.size() == 0) { 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; } showLoadingEvent.setValue(true); RxJavaUtil.doInBackground(() -> { for (ImageDeepDetector.ImageFile item : list) { 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); list.clear(); selectedList.setValue(list); }, throwable -> { showLoadingEvent.setValue(false); ToastUtil.show(R.string.export_fail, ToastUtil.LENGTH_SHORT); }); } public List getAllDetectedList() { ArrayList allList = new ArrayList<>(); allList.addAll(getList(detectedPhotoImg)); allList.addAll(getList(detectedWxImg)); allList.addAll(getList(detectedQQImg)); allList.addAll(getList(detectedOtherImg)); 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); } } }