ImageRecoverViewModel.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. package com.datarecovery.master.module.imgrecover;
  2. import androidx.lifecycle.LiveData;
  3. import androidx.lifecycle.MutableLiveData;
  4. import androidx.lifecycle.Transformations;
  5. import com.atmob.app.lib.base.BaseViewModel;
  6. import com.atmob.app.lib.livedata.SingleLiveEvent;
  7. import com.atmob.common.runtime.ContextUtil;
  8. import com.datarecovery.master.R;
  9. import com.datarecovery.master.utils.FileUtil;
  10. import com.datarecovery.master.utils.ImageDeepDetector;
  11. import com.datarecovery.master.utils.MediaStoreHelper;
  12. import com.datarecovery.master.utils.ToastUtil;
  13. import org.reactivestreams.Subscription;
  14. import java.util.ArrayList;
  15. import java.util.List;
  16. import java.util.concurrent.TimeUnit;
  17. import javax.inject.Inject;
  18. import atmob.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
  19. import atmob.reactivex.rxjava3.annotations.NonNull;
  20. import atmob.reactivex.rxjava3.core.FlowableSubscriber;
  21. import atmob.reactivex.rxjava3.disposables.Disposable;
  22. import atmob.rxjava.utils.RxJavaUtil;
  23. import dagger.hilt.android.lifecycle.HiltViewModel;
  24. @HiltViewModel
  25. public class ImageRecoverViewModel extends BaseViewModel {
  26. private final long SCANNING_COUNTDOWN = 1000 * 60 * 6;
  27. private final MutableLiveData<List<ImageDeepDetector.ImageFile>> detectedPhotoImg = new MutableLiveData<>(new ArrayList<>());
  28. private final MutableLiveData<List<ImageDeepDetector.ImageFile>> detectedWxImg = new MutableLiveData<>(new ArrayList<>());
  29. private final MutableLiveData<List<ImageDeepDetector.ImageFile>> detectedQQImg = new MutableLiveData<>(new ArrayList<>());
  30. private final MutableLiveData<List<ImageDeepDetector.ImageFile>> detectedOtherImg = new MutableLiveData<>(new ArrayList<>());
  31. private LiveData<String> detectedPhotoTitle;
  32. private LiveData<String> detectedWxTitle;
  33. private LiveData<String> detectedQQTitle;
  34. private LiveData<String> detectedOtherTitle;
  35. private final SingleLiveEvent<ImageDeepDetector.ImageFile> previewEvent = new SingleLiveEvent<>();
  36. private final SingleLiveEvent<?> detectedFinish = new SingleLiveEvent<>();
  37. private final SingleLiveEvent<?> showClearDialog = new SingleLiveEvent<>();
  38. private final SingleLiveEvent<Boolean> showScanDialogEvent = new SingleLiveEvent<>();
  39. private final SingleLiveEvent<Boolean> showLoadingEvent = new SingleLiveEvent<>();
  40. private final SingleLiveEvent<?> notifyList = new SingleLiveEvent<>();
  41. private final MutableLiveData<String> barTitle = new MutableLiveData<>();
  42. private final MutableLiveData<Boolean> checkAll = new MutableLiveData<>(false);
  43. //总探测到的图片数量
  44. private int totalCount = 0;
  45. private final MutableLiveData<Integer> totalDetectedCount = new MutableLiveData<>();
  46. private final MutableLiveData<List<ImageDeepDetector.ImageFile>> selectedList = new MutableLiveData<>(new ArrayList<>());
  47. private LiveData<String> selectedCountTxt;
  48. private Disposable scanDisposable;
  49. private int type;
  50. @Inject
  51. public ImageRecoverViewModel() {
  52. barTitle.setValue(ContextUtil.getContext().getString(R.string.iamge_recover_all));
  53. startImageScanning();
  54. initLiveData();
  55. }
  56. public LiveData<?> getShowClearDialog() {
  57. return showClearDialog;
  58. }
  59. public LiveData<?> getNotifyList() {
  60. return notifyList;
  61. }
  62. public LiveData<Boolean> getShowLoadingEvent() {
  63. return showLoadingEvent;
  64. }
  65. public LiveData<ImageDeepDetector.ImageFile> getPreviewEvent() {
  66. return previewEvent;
  67. }
  68. public LiveData<?> getDetectedFinish() {
  69. return detectedFinish;
  70. }
  71. public LiveData<Integer> getTotalDetectedCount() {
  72. return totalDetectedCount;
  73. }
  74. public LiveData<String> getSelectedCountTxt() {
  75. return selectedCountTxt;
  76. }
  77. public LiveData<List<ImageDeepDetector.ImageFile>> getSelectedList() {
  78. return selectedList;
  79. }
  80. public LiveData<List<ImageDeepDetector.ImageFile>> getDetectedPhotoImg() {
  81. return detectedPhotoImg;
  82. }
  83. public LiveData<List<ImageDeepDetector.ImageFile>> getDetectedWxImg() {
  84. return detectedWxImg;
  85. }
  86. public LiveData<List<ImageDeepDetector.ImageFile>> getDetectedQQImg() {
  87. return detectedQQImg;
  88. }
  89. public LiveData<List<ImageDeepDetector.ImageFile>> getDetectedOtherImg() {
  90. return detectedOtherImg;
  91. }
  92. public LiveData<String> getDetectedPhotoTitle() {
  93. return detectedPhotoTitle;
  94. }
  95. public LiveData<String> getDetectedWxTitle() {
  96. return detectedWxTitle;
  97. }
  98. public LiveData<String> getDetectedQQTitle() {
  99. return detectedQQTitle;
  100. }
  101. public LiveData<String> getDetectedOtherTitle() {
  102. return detectedOtherTitle;
  103. }
  104. public LiveData<Boolean> getShowScanDialogEvent() {
  105. return showScanDialogEvent;
  106. }
  107. public LiveData<Boolean> getCheckAll() {
  108. return checkAll;
  109. }
  110. public LiveData<String> getBarTitle() {
  111. return barTitle;
  112. }
  113. public int getType() {
  114. return type;
  115. }
  116. private void initLiveData() {
  117. detectedPhotoTitle = Transformations.map(detectedPhotoImg, list ->
  118. ContextUtil.getContext().getString(R.string.photo_count, (list == null || list.isEmpty()) ? 0 : list.size()));
  119. detectedWxTitle = Transformations.map(detectedWxImg, list ->
  120. ContextUtil.getContext().getString(R.string.wx_count, (list == null || list.isEmpty()) ? 0 : list.size()));
  121. detectedQQTitle = Transformations.map(detectedQQImg, list ->
  122. ContextUtil.getContext().getString(R.string.qq_count, (list == null || list.isEmpty()) ? 0 : list.size()));
  123. detectedOtherTitle = Transformations.map(detectedOtherImg, list ->
  124. ContextUtil.getContext().getString(R.string.other_count, (list == null || list.isEmpty()) ? 0 : list.size()));
  125. }
  126. public void onCheckAllClick(boolean isCheck) {
  127. checkAll.setValue(isCheck);
  128. List<ImageDeepDetector.ImageFile> selectList = getList(selectedList);
  129. setListCheck(detectedPhotoImg.getValue(), isCheck);
  130. setListCheck(detectedWxImg.getValue(), isCheck);
  131. setListCheck(detectedQQImg.getValue(), isCheck);
  132. setListCheck(detectedOtherImg.getValue(), isCheck);
  133. if (isCheck) {
  134. selectList.clear();
  135. selectList.addAll(getList(detectedPhotoImg));
  136. selectList.addAll(getList(detectedWxImg));
  137. selectList.addAll(getList(detectedQQImg));
  138. selectList.addAll(getList(detectedOtherImg));
  139. } else {
  140. selectList.clear();
  141. }
  142. selectedList.setValue(selectList);
  143. }
  144. private void setListCheck(List<ImageDeepDetector.ImageFile> list, boolean isCheck) {
  145. if (list == null || list.isEmpty()) {
  146. return;
  147. }
  148. for (ImageDeepDetector.ImageFile imageFile : list) {
  149. imageFile.setCheck(isCheck);
  150. }
  151. }
  152. private List<ImageDeepDetector.ImageFile> getList(LiveData<List<ImageDeepDetector.ImageFile>> liveData) {
  153. List<ImageDeepDetector.ImageFile> selectList = liveData.getValue();
  154. if (selectList == null) {
  155. selectList = new ArrayList<>();
  156. }
  157. return selectList;
  158. }
  159. private void startImageScanning() {
  160. ImageDeepDetector.detect(ContextUtil.getContext())
  161. .take(SCANNING_COUNTDOWN, TimeUnit.MILLISECONDS)
  162. .observeOn(AndroidSchedulers.mainThread())
  163. .subscribe(new FlowableSubscriber<List<ImageDeepDetector.ImageFile>>() {
  164. @Override
  165. public void onSubscribe(@NonNull Subscription s) {
  166. s.request(Integer.MAX_VALUE);
  167. scanDisposable = Disposable.fromSubscription(s);
  168. addDisposable(scanDisposable);
  169. showScanDialogEvent.setValue(true);
  170. checkAll.setValue(false);
  171. totalCount = 0;
  172. totalDetectedCount.setValue(0);
  173. }
  174. @Override
  175. public void onNext(List<ImageDeepDetector.ImageFile> imageFiles) {
  176. if (imageFiles == null) {
  177. return;
  178. }
  179. totalCount += imageFiles.size();
  180. totalDetectedCount.setValue(totalCount);
  181. for (ImageDeepDetector.ImageFile imageFile : imageFiles) {
  182. MutableLiveData<List<ImageDeepDetector.ImageFile>> liveData;
  183. if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) {
  184. liveData = detectedPhotoImg;
  185. } else if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) {
  186. liveData = detectedWxImg;
  187. } else if (imageFile.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) {
  188. liveData = detectedQQImg;
  189. } else {
  190. liveData = detectedOtherImg;
  191. }
  192. List<ImageDeepDetector.ImageFile> list = getList(liveData);
  193. list.add(0, imageFile);
  194. liveData.setValue(list);
  195. }
  196. }
  197. @Override
  198. public void onError(Throwable t) {
  199. showScanDialogEvent.setValue(false);
  200. }
  201. @Override
  202. public void onComplete() {
  203. detectedFinish.call();
  204. }
  205. });
  206. }
  207. public void cancelScan() {
  208. if (scanDisposable != null) scanDisposable.dispose();
  209. }
  210. public void setItemCheck(@NonNull ImageDeepDetector.ImageFile imageFile) {
  211. imageFile.setCheck(!imageFile.isCheck());
  212. List<ImageDeepDetector.ImageFile> list = getList(this.selectedList);
  213. if (imageFile.isCheck()) {
  214. list.add(imageFile);
  215. } else {
  216. list.remove(imageFile);
  217. }
  218. selectedList.setValue(list);
  219. }
  220. public void checkPreview(@NonNull ImageDeepDetector.ImageFile imageFile) {
  221. if (type == ImageRecoverActivity.Type.DELETE) {
  222. return;
  223. }
  224. //TODO 判断是否有会员
  225. previewEvent.setValue(imageFile);
  226. }
  227. public void onOperationClick() {
  228. if (type == ImageRecoverActivity.Type.DELETE) {
  229. showClearDialog.call();
  230. } else {
  231. onExportClick();
  232. }
  233. }
  234. public void executeImageDelete() {
  235. List<ImageDeepDetector.ImageFile> list = getList(selectedList);
  236. if (list.size() == 0) {
  237. return;
  238. }
  239. showLoadingEvent.setValue(true);
  240. RxJavaUtil.doInBackground(() -> {
  241. for (ImageDeepDetector.ImageFile item : list) {
  242. item.delete();
  243. MutableLiveData<List<ImageDeepDetector.ImageFile>> liveData;
  244. if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_GALLERY) {
  245. liveData = detectedPhotoImg;
  246. } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_WECHAT) {
  247. liveData = detectedWxImg;
  248. } else if (item.getCategory() == ImageDeepDetector.ImageFile.CATEGORY_QQ) {
  249. liveData = detectedQQImg;
  250. } else {
  251. liveData = detectedOtherImg;
  252. }
  253. List<ImageDeepDetector.ImageFile> itemList = getList(liveData);
  254. itemList.remove(item);
  255. }
  256. return true;
  257. }, o -> {
  258. checkAll.setValue(false);
  259. showLoadingEvent.setValue(false);
  260. ToastUtil.show(R.string.delete_success, ToastUtil.LENGTH_SHORT);
  261. list.clear();
  262. selectedList.setValue(list);
  263. notifyList.call();
  264. }, throwable -> {
  265. showLoadingEvent.setValue(false);
  266. ToastUtil.show(R.string.delete_fail, ToastUtil.LENGTH_SHORT);
  267. });
  268. }
  269. public void onExportClick() {
  270. List<ImageDeepDetector.ImageFile> list = getList(selectedList);
  271. if (list.size() == 0) {
  272. return;
  273. }
  274. //TODO 判断是否有会员
  275. showLoadingEvent.setValue(true);
  276. RxJavaUtil.doInBackground(() -> {
  277. for (ImageDeepDetector.ImageFile item : list) {
  278. MediaStoreHelper.saveToSharedStorage(MediaStoreHelper.TYPE_IMAGE, item.newInputStream(), FileUtil.getExportImageFileName(item.getName()));
  279. item.setCheck(false);
  280. }
  281. return true;
  282. }, o -> {
  283. checkAll.setValue(false);
  284. showLoadingEvent.setValue(false);
  285. ToastUtil.show(R.string.export_success, ToastUtil.LENGTH_SHORT);
  286. list.clear();
  287. selectedList.setValue(list);
  288. }, throwable -> {
  289. showLoadingEvent.setValue(false);
  290. ToastUtil.show(R.string.export_fail, ToastUtil.LENGTH_SHORT);
  291. });
  292. }
  293. public List<ImageDeepDetector.ImageFile> getAllDetectedList() {
  294. ArrayList<ImageDeepDetector.ImageFile> allList = new ArrayList<>();
  295. allList.addAll(getList(detectedPhotoImg));
  296. allList.addAll(getList(detectedWxImg));
  297. allList.addAll(getList(detectedQQImg));
  298. allList.addAll(getList(detectedOtherImg));
  299. return allList;
  300. }
  301. public int getPosition(@NonNull List<ImageDeepDetector.ImageFile> list, ImageDeepDetector.ImageFile target) {
  302. return list.indexOf(target);
  303. }
  304. public void setOperationType(@ImageRecoverActivity.Type int type) {
  305. this.type = type;
  306. switch (type) {
  307. case ImageRecoverActivity.Type.DELETE:
  308. selectedCountTxt = Transformations.map(selectedList, list -> {
  309. if (list == null || list.isEmpty()) {
  310. return ContextUtil.getContext().getString(R.string.delete);
  311. }
  312. return ContextUtil.getContext().getString(R.string.delete_count, list.size());
  313. });
  314. break;
  315. case ImageRecoverActivity.Type.RECOVER:
  316. selectedCountTxt = Transformations.map(selectedList, list -> {
  317. if (list == null || list.isEmpty()) {
  318. return ContextUtil.getContext().getString(R.string.export);
  319. }
  320. return ContextUtil.getContext().getString(R.string.export_count, list.size());
  321. });
  322. break;
  323. }
  324. }
  325. }