FileRecoverViewModel.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. package com.datarecovery.master.module.filerecover;
  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.ActivityUtil;
  8. import com.atmob.common.runtime.ContextUtil;
  9. import com.datarecovery.master.R;
  10. import com.datarecovery.master.data.consts.EventId;
  11. import com.datarecovery.master.data.repositories.DeviceFuncRepository;
  12. import com.datarecovery.master.handler.EventHelper;
  13. import com.datarecovery.master.module.member.MemberActivity;
  14. import com.datarecovery.master.module.member.MemberType;
  15. import com.datarecovery.master.sdk.bugly.BuglyHelper;
  16. import com.datarecovery.master.utils.BoxingUtil;
  17. import com.datarecovery.master.utils.FileUtil;
  18. import com.datarecovery.master.utils.FilesSearch;
  19. import com.datarecovery.master.utils.ImageDeepDetector;
  20. import com.datarecovery.master.utils.Maps;
  21. import com.datarecovery.master.utils.MediaStoreHelper;
  22. import com.datarecovery.master.utils.ReportUtil;
  23. import com.datarecovery.master.utils.ReverseArrayList;
  24. import com.datarecovery.master.utils.ToastUtil;
  25. import org.reactivestreams.Subscriber;
  26. import org.reactivestreams.Subscription;
  27. import java.util.ArrayList;
  28. import java.util.Iterator;
  29. import java.util.List;
  30. import java.util.concurrent.TimeUnit;
  31. import javax.inject.Inject;
  32. import atmob.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
  33. import atmob.reactivex.rxjava3.annotations.NonNull;
  34. import atmob.reactivex.rxjava3.core.Single;
  35. import atmob.reactivex.rxjava3.core.SingleObserver;
  36. import atmob.reactivex.rxjava3.disposables.Disposable;
  37. import atmob.rxjava.utils.RxJavaUtil;
  38. import dagger.hilt.android.lifecycle.HiltViewModel;
  39. @HiltViewModel
  40. public class FileRecoverViewModel extends BaseViewModel {
  41. private final long SCANNING_COUNTDOWN = 1000 * 60 * 6;
  42. private final long SCANNING_IS_TRIAL_COUNTDOWN = 1000 * 5;
  43. private final int[] tabTitle = {R.string.word, R.string.excel, R.string.ppt, R.string.pdf};
  44. private final List<MutableLiveData<Boolean>> checkList = new ArrayList<>();
  45. private final List<FilesSearch.DocumentFile> wordList = new ReverseArrayList<>();
  46. private final List<FilesSearch.DocumentFile> excelList = new ReverseArrayList<>();
  47. private final List<FilesSearch.DocumentFile> pptList = new ReverseArrayList<>();
  48. private final List<FilesSearch.DocumentFile> pdfList = new ReverseArrayList<>();
  49. private final List<FilesSearch.DocumentFile> selectList = new ArrayList<>();
  50. private final MutableLiveData<List<FilesSearch.DocumentFile>> detectedWordList = new MutableLiveData<>();
  51. private final MutableLiveData<List<FilesSearch.DocumentFile>> detectedExcelList = new MutableLiveData<>();
  52. private final MutableLiveData<List<FilesSearch.DocumentFile>> detectedPPTList = new MutableLiveData<>();
  53. private final MutableLiveData<List<FilesSearch.DocumentFile>> detectedPDFList = new MutableLiveData<>();
  54. private final MutableLiveData<List<FilesSearch.DocumentFile>> selectedList = new MutableLiveData<>();
  55. private final SingleLiveEvent<?> scrollTop = new SingleLiveEvent<>();
  56. private final MutableLiveData<Boolean> showTrialView = new MutableLiveData<>();
  57. private final MutableLiveData<String> detectedLastFileName = new MutableLiveData<>();
  58. private final MutableLiveData<Integer> checkPosition = new MutableLiveData<>();
  59. private final SingleLiveEvent<?> showTrialFinishDialog = new SingleLiveEvent<>();
  60. private final SingleLiveEvent<?> showTrialExportFailDialog = new SingleLiveEvent<>();
  61. private final SingleLiveEvent<Boolean> showLoadingEvent = new SingleLiveEvent<>();
  62. private final SingleLiveEvent<Boolean> showScanDialogEvent = new SingleLiveEvent<>();
  63. private final DeviceFuncRepository deviceFuncRepository;
  64. private final LiveData<String> selectedCountTxt;
  65. private Disposable scanDisposable;
  66. private boolean isTrial;
  67. @Inject
  68. public FileRecoverViewModel(DeviceFuncRepository deviceFuncRepository) {
  69. this.deviceFuncRepository = deviceFuncRepository;
  70. selectedCountTxt = Transformations.map(selectedList, list -> {
  71. if (list == null || list.isEmpty()) {
  72. return ContextUtil.getContext().getString(R.string.export);
  73. }
  74. return ContextUtil.getContext().getString(R.string.export_count, list.size());
  75. });
  76. for (int i = 0; i < tabTitle.length; i++) checkList.add(new MutableLiveData<>(false));
  77. EventHelper.report(EventId.hf1000508);
  78. EventHelper.timeEvent(EventId.hf1000523);
  79. }
  80. public LiveData<?> getScrollTop() {
  81. return scrollTop;
  82. }
  83. public LiveData<?> getShowTrialFinishDialog() {
  84. return showTrialFinishDialog;
  85. }
  86. public LiveData<?> getShowTrialExportFailDialog() {
  87. return showTrialExportFailDialog;
  88. }
  89. public LiveData<Boolean> getShowTrialView() {
  90. return showTrialView;
  91. }
  92. public LiveData<String> getDetectedLastFileName() {
  93. return detectedLastFileName;
  94. }
  95. public boolean isTrial() {
  96. return isTrial;
  97. }
  98. public LiveData<Integer> getCheckPosition() {
  99. return checkPosition;
  100. }
  101. public int[] getTabTitle() {
  102. return tabTitle;
  103. }
  104. public LiveData<List<FilesSearch.DocumentFile>> getDetectedWordList() {
  105. return detectedWordList;
  106. }
  107. public LiveData<List<FilesSearch.DocumentFile>> getDetectedExcelList() {
  108. return detectedExcelList;
  109. }
  110. public LiveData<List<FilesSearch.DocumentFile>> getDetectedPPTList() {
  111. return detectedPPTList;
  112. }
  113. public LiveData<List<FilesSearch.DocumentFile>> getDetectedPDFList() {
  114. return detectedPDFList;
  115. }
  116. public LiveData<Boolean> getShowLoadingEvent() {
  117. return showLoadingEvent;
  118. }
  119. public LiveData<List<FilesSearch.DocumentFile>> getSelectedList() {
  120. return selectedList;
  121. }
  122. public LiveData<String> getSelectedCountTxt() {
  123. return selectedCountTxt;
  124. }
  125. public LiveData<Boolean> getShowScanDialogEvent() {
  126. return showScanDialogEvent;
  127. }
  128. public void setCheckPosition(int position) {
  129. this.checkPosition.setValue(position);
  130. }
  131. public void startFileScanning() {
  132. if (scanDisposable != null && !scanDisposable.isDisposed()) {
  133. return;
  134. }
  135. FilesSearch.search(ContextUtil.getContext(), FilesSearch.DocumentFile.WORD, FilesSearch.DocumentFile.EXCEL, FilesSearch.DocumentFile.PPT, FilesSearch.DocumentFile.PDF)
  136. .take(isTrial ? SCANNING_IS_TRIAL_COUNTDOWN : SCANNING_COUNTDOWN, TimeUnit.MILLISECONDS)
  137. .observeOn(AndroidSchedulers.mainThread())
  138. .subscribe(new Subscriber<List<FilesSearch.DocumentFile>>() {
  139. @Override
  140. public void onSubscribe(Subscription s) {
  141. s.request(Integer.MAX_VALUE);
  142. scanDisposable = Disposable.fromSubscription(s);
  143. addDisposable(scanDisposable);
  144. showScanDialogEvent.setValue(true);
  145. wordList.clear();
  146. excelList.clear();
  147. pptList.clear();
  148. pdfList.clear();
  149. selectList.clear();
  150. }
  151. @Override
  152. public void onNext(List<FilesSearch.DocumentFile> documentFiles) {
  153. if (documentFiles == null || documentFiles.isEmpty()) {
  154. return;
  155. }
  156. int wordCount = 0;
  157. int excelCount = 0;
  158. int pptCount = 0;
  159. int pdfCount = 0;
  160. for (FilesSearch.DocumentFile item : documentFiles) {
  161. detectedLastFileName.setValue(item.getName());
  162. if (item.getCategory() == FilesSearch.DocumentFile.WORD) {
  163. wordCount++;
  164. wordList.add(item);
  165. } else if (item.getCategory() == FilesSearch.DocumentFile.EXCEL) {
  166. excelCount++;
  167. excelList.add(item);
  168. } else if (item.getCategory() == FilesSearch.DocumentFile.PPT) {
  169. pptCount++;
  170. pptList.add(item);
  171. } else if (item.getCategory() == FilesSearch.DocumentFile.PDF) {
  172. pdfCount++;
  173. pdfList.add(item);
  174. }
  175. }
  176. if (wordCount > 0) {
  177. detectedWordList.setValue(wordList);
  178. }
  179. if (excelCount > 0) {
  180. detectedExcelList.setValue(excelList);
  181. }
  182. if (pptCount > 0) {
  183. detectedPPTList.setValue(pptList);
  184. }
  185. if (pdfCount > 0) {
  186. detectedPDFList.setValue(pdfList);
  187. }
  188. scrollTop.call();
  189. }
  190. @Override
  191. public void onError(Throwable t) {
  192. showScanDialogEvent.setValue(false);
  193. }
  194. @Override
  195. public void onComplete() {
  196. setFreeExport();
  197. if (isTrial) showTrialFinishDialog.call();
  198. showScanDialogEvent.setValue(false);
  199. }
  200. });
  201. }
  202. public void cancelScan() {
  203. if (scanDisposable != null) scanDisposable.dispose();
  204. showScanDialogEvent.setValue(false);
  205. setFreeExport();
  206. }
  207. private void setFreeExport() {
  208. if (isTrial) {
  209. showTrialView.setValue(true);
  210. if (!wordList.isEmpty()) {
  211. wordList.get(0).setTrial(true);
  212. }
  213. if (!excelList.isEmpty()) {
  214. excelList.get(0).setTrial(true);
  215. }
  216. if (!pptList.isEmpty()) {
  217. pptList.get(0).setTrial(true);
  218. }
  219. if (!pdfList.isEmpty()) {
  220. pdfList.get(0).setTrial(true);
  221. }
  222. }
  223. }
  224. public void onCheckAllClick(boolean isCheck) {
  225. int index = BoxingUtil.boxing(checkPosition.getValue());
  226. getCheckAll(index).setValue(isCheck);
  227. int id = tabTitle[index];
  228. List<FilesSearch.DocumentFile> list = null;
  229. if (id == R.string.word) {
  230. list = wordList;
  231. } else if (id == R.string.excel) {
  232. list = excelList;
  233. } else if (id == R.string.ppt) {
  234. list = pptList;
  235. } else if (id == R.string.pdf) {
  236. list = pdfList;
  237. }
  238. if (list == null || list.isEmpty()) {
  239. return;
  240. }
  241. for (FilesSearch.DocumentFile item : list) {
  242. if (item.isCheck() != isCheck) {
  243. if (isCheck) {
  244. selectList.add(item);
  245. } else {
  246. selectList.remove(item);
  247. }
  248. }
  249. item.setCheck(isCheck);
  250. }
  251. this.selectedList.setValue(selectList);
  252. }
  253. public MutableLiveData<Boolean> getCheckAll(int position) {
  254. return checkList.get(position);
  255. }
  256. public void onExportClick() {
  257. if (selectList == null || selectList.isEmpty()) {
  258. return;
  259. }
  260. if (isTrial) {
  261. for (FilesSearch.DocumentFile file : selectList) {
  262. if (!file.isTrial()) {
  263. showTrialExportFailDialog.call();
  264. return;
  265. }
  266. }
  267. } else {
  268. if (!deviceFuncRepository.isHaveAuth(MemberType.APP_FILE_RECOVER)) {
  269. return;
  270. }
  271. }
  272. Single.just(selectList.size())
  273. .map(oldSize -> {
  274. Iterator<FilesSearch.DocumentFile> iterator = selectList.iterator();
  275. int errorCount = 0;
  276. Exception exception = null;
  277. while (iterator.hasNext()) {
  278. FilesSearch.DocumentFile item = iterator.next();
  279. try {
  280. MediaStoreHelper.saveToSharedStorage(MediaStoreHelper.TYPE_FILES, item.newInputStream(), FileUtil.getCreateFileName(MediaStoreHelper.TYPE_FILES, item.getName()));
  281. item.setCheck(false);
  282. iterator.remove();
  283. } catch (Exception throwable) {
  284. errorCount++;
  285. exception = throwable;
  286. BuglyHelper.postCatchedException(new Exception("文件导出:", throwable));
  287. }
  288. }
  289. if (errorCount == oldSize) {
  290. throw new Exception("导出失败" + (exception == null ? "" : ":" + exception.getMessage()));
  291. } else if (errorCount > 0) {
  292. throw new Exception("部分导出失败" + ":" + exception.getMessage());
  293. }
  294. return errorCount;
  295. })
  296. .compose(RxJavaUtil.SingleSchedule.io2Main())
  297. .subscribe(new SingleObserver<Integer>() {
  298. @Override
  299. public void onSubscribe(@NonNull Disposable d) {
  300. showLoadingEvent.setValue(true);
  301. addDisposable(d);
  302. }
  303. @Override
  304. public void onSuccess(@NonNull Integer integer) {
  305. showLoadingEvent.setValue(false);
  306. selectedList.setValue(selectList);
  307. ToastUtil.show(R.string.export_success, ToastUtil.LENGTH_SHORT);
  308. getCheckAll(BoxingUtil.boxing(checkPosition.getValue())).setValue(false);
  309. }
  310. @Override
  311. public void onError(@NonNull Throwable throwable) {
  312. showLoadingEvent.setValue(false);
  313. selectedList.setValue(selectList);
  314. ToastUtil.show(throwable.getMessage(), ToastUtil.LENGTH_SHORT);
  315. }
  316. });
  317. }
  318. public void itemClick(FilesSearch.DocumentFile documentFile) {
  319. if (documentFile == null) {
  320. return;
  321. }
  322. documentFile.setCheck(!documentFile.isCheck());
  323. if (documentFile.isCheck()) {
  324. selectList.add(documentFile);
  325. } else {
  326. selectList.remove(documentFile);
  327. }
  328. this.selectedList.setValue(selectList);
  329. }
  330. @Override
  331. protected void onCleared() {
  332. super.onCleared();
  333. EventHelper.report(EventId.hf1000523);
  334. }
  335. public void setIsTrial(boolean isTrial) {
  336. this.isTrial = isTrial;
  337. }
  338. public void onTrialRecoverClick() {
  339. MemberActivity.start(ActivityUtil.getTopActivity(), MemberType.APP_FILE_RECOVER);
  340. }
  341. public void onViewTrialRecoverClick() {
  342. EventHelper.report(EventId.hf1001117, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(MemberType.APP_FILE_RECOVER)));
  343. MemberActivity.start(ActivityUtil.getTopActivity(), MemberType.APP_FILE_RECOVER);
  344. }
  345. public void scrollPosition(int lastCompletelyItemPosition, int itemCount) {
  346. if (scanDisposable == null || !scanDisposable.isDisposed()) {
  347. return;
  348. }
  349. if (lastCompletelyItemPosition == itemCount - 1 && !BoxingUtil.boxing(showScanDialogEvent.getValue())) {
  350. showTrialFinishDialog.call();
  351. EventHelper.report(EventId.hf1001119, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(MemberType.APP_FILE_RECOVER)));
  352. }
  353. }
  354. public void showTrialFinishDialog() {
  355. showTrialFinishDialog.call();
  356. }
  357. }