ImageRecoverActivity.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. package com.datarecovery.master.module.imgrecover;
  2. import android.annotation.SuppressLint;
  3. import android.app.Activity;
  4. import android.app.PendingIntent;
  5. import android.content.Context;
  6. import android.content.DialogInterface;
  7. import android.content.Intent;
  8. import android.content.IntentSender;
  9. import android.net.Uri;
  10. import android.os.Build;
  11. import android.os.Bundle;
  12. import android.provider.MediaStore;
  13. import android.view.KeyEvent;
  14. import android.view.MotionEvent;
  15. import android.view.View;
  16. import androidx.activity.result.ActivityResult;
  17. import androidx.activity.result.IntentSenderRequest;
  18. import androidx.activity.result.contract.ActivityResultContracts;
  19. import androidx.annotation.NonNull;
  20. import androidx.annotation.Nullable;
  21. import androidx.recyclerview.widget.ConcatAdapter;
  22. import androidx.recyclerview.widget.GridLayoutManager;
  23. import androidx.recyclerview.widget.RecyclerView;
  24. import com.atmob.app.lib.base.BaseActivity;
  25. import com.atmob.common.logging.AtmobLog;
  26. import com.atmob.common.runtime.ActivityUtil;
  27. import com.atmob.common.runtime.ContextUtil;
  28. import com.datarecovery.master.R;
  29. import com.datarecovery.master.data.consts.EventId;
  30. import com.datarecovery.master.databinding.ActivityImageRecoverBinding;
  31. import com.datarecovery.master.databinding.ItemTabImageRecoverBinding;
  32. import com.datarecovery.master.dialog.CommonLoadingDialog;
  33. import com.datarecovery.master.dialog.CommonSureDialog;
  34. import com.datarecovery.master.dialog.ScanFileDialog;
  35. import com.datarecovery.master.dialog.ScanProgressDialog;
  36. import com.datarecovery.master.handler.EventHelper;
  37. import com.datarecovery.master.module.member.MemberType;
  38. import com.datarecovery.master.module.preview.PreviewActivity;
  39. import com.datarecovery.master.utils.ActivityForResultUtil;
  40. import com.datarecovery.master.utils.BoxingUtil;
  41. import com.datarecovery.master.utils.FilePermissionHelper;
  42. import com.datarecovery.master.utils.GridRecoverItemDecoration;
  43. import com.datarecovery.master.utils.ImageDeepDetector;
  44. import com.datarecovery.master.utils.Maps;
  45. import com.datarecovery.master.utils.ReportUtil;
  46. import com.datarecovery.master.utils.ToastUtil;
  47. import com.google.android.material.tabs.TabLayout;
  48. import com.gyf.immersionbar.ImmersionBar;
  49. import java.util.List;
  50. import java.util.Objects;
  51. import dagger.hilt.android.AndroidEntryPoint;
  52. @AndroidEntryPoint
  53. public class ImageRecoverActivity extends BaseActivity<ActivityImageRecoverBinding> implements ImageItemAdapter.onItemClick {
  54. private ConcatAdapter concatAdapter;
  55. private static final String TYPE = "type";
  56. private static final String IS_TRIAL = "is_trial";
  57. ImageRecoverViewModel imageRecoverViewModel;
  58. private final int[] tabTitle = {R.string.photo, R.string.wx, R.string.qq, R.string.other};
  59. private CommonSureDialog backDialog;
  60. private CommonSureDialog clearDialog;
  61. private CommonSureDialog showTrialExportFailDialog;
  62. private CommonSureDialog showTrialFinishDialog;
  63. private CommonLoadingDialog loadingDialog;
  64. private ScanFileDialog scanFileDialog;
  65. private ImageItemAdapter photoAdapter;
  66. private ImageItemAdapter wxAdapter;
  67. private ImageItemAdapter qqAdapter;
  68. private ImageItemAdapter otherAdapter;
  69. private GridLayoutManager gridLayoutManager;
  70. public static void start(Context context, @MemberType String type) {
  71. start(context, type, false);
  72. }
  73. public static void start(Context context, @MemberType String type, boolean isTrial) {
  74. Intent intent = new Intent(context, ImageRecoverActivity.class);
  75. if (!(context instanceof Activity)) {
  76. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  77. }
  78. intent.putExtra(TYPE, type);
  79. intent.putExtra(IS_TRIAL, isTrial);
  80. context.startActivity(intent);
  81. }
  82. @Override
  83. protected void onCreate(@Nullable Bundle savedInstanceState) {
  84. super.onCreate(savedInstanceState);
  85. initData();
  86. initView();
  87. initObserver();
  88. initPermission();
  89. }
  90. private void initPermission() {
  91. new FilePermissionHelper().requestDataFilePermission(this, imageRecoverViewModel.getType(), imageRecoverViewModel.isTrial(), new FilePermissionHelper.NextStepCallback() {
  92. @Override
  93. public void onNextStep() {
  94. imageRecoverViewModel.startImageScanning();
  95. }
  96. @Override
  97. public void onCancel() {
  98. finish();
  99. }
  100. });
  101. }
  102. private void initData() {
  103. Intent intent = getIntent();
  104. if (intent != null) {
  105. imageRecoverViewModel.setType(intent.getStringExtra(TYPE));
  106. imageRecoverViewModel.setIsTrial(intent.getBooleanExtra(IS_TRIAL, false));
  107. }
  108. }
  109. private void initView() {
  110. binding.toolBar.setNavigationOnClickListener(v -> onBackPressed());
  111. addTopStatusBarHeight(binding.toolBar);
  112. initRecycleView();
  113. initTabLayout();
  114. }
  115. private void initRecycleView() {
  116. ImageTitleAdapter photoTitleAdapter = new ImageTitleAdapter(this, imageRecoverViewModel.getDetectedPhotoTitle());
  117. photoAdapter = new ImageItemAdapter(imageRecoverViewModel.getType(), this);
  118. photoAdapter.setOnItemClick(this);
  119. ImageTitleAdapter wxTitleAdapter = new ImageTitleAdapter(this, imageRecoverViewModel.getDetectedWxTitle());
  120. wxAdapter = new ImageItemAdapter(imageRecoverViewModel.getType(), this);
  121. wxAdapter.setOnItemClick(this);
  122. ImageTitleAdapter qqTitleAdapter = new ImageTitleAdapter(this, imageRecoverViewModel.getDetectedQQTitle());
  123. qqAdapter = new ImageItemAdapter(imageRecoverViewModel.getType(), this);
  124. qqAdapter.setOnItemClick(this);
  125. ImageTitleAdapter otherTitleAdapter = new ImageTitleAdapter(this, imageRecoverViewModel.getDetectedOtherTitle());
  126. otherAdapter = new ImageItemAdapter(imageRecoverViewModel.getType(), this);
  127. otherAdapter.setOnItemClick(this);
  128. concatAdapter = new ConcatAdapter(
  129. photoTitleAdapter,
  130. photoAdapter,
  131. wxTitleAdapter,
  132. wxAdapter,
  133. qqTitleAdapter,
  134. qqAdapter,
  135. otherTitleAdapter,
  136. otherAdapter
  137. );
  138. binding.ryImageRecover.setAdapter(concatAdapter);
  139. gridLayoutManager = new GridLayoutManager(this, 3);
  140. gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
  141. @Override
  142. public int getSpanSize(int position) {
  143. if (position == getPhotoPosition() || position == getWxPosition() || position == getQQPosition() || position == getOtherPosition()) {
  144. return 3;
  145. }
  146. return 1;
  147. }
  148. });
  149. binding.ryImageRecover.addItemDecoration(new GridRecoverItemDecoration(3, 0.0282222222222222f, 0.0202222222222222f));
  150. binding.ryImageRecover.setLayoutManager(gridLayoutManager);
  151. RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
  152. @Override
  153. public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
  154. int firstVisibleItem = gridLayoutManager.findFirstVisibleItemPosition();
  155. int lastVisibleItem = gridLayoutManager.findLastVisibleItemPosition();
  156. if (imageRecoverViewModel.isTrial()) {
  157. imageRecoverViewModel.scrollPosition(gridLayoutManager.findLastCompletelyVisibleItemPosition(), gridLayoutManager.getItemCount());
  158. }
  159. int photoPosition = getPhotoPosition();
  160. int wxPosition = getWxPosition();
  161. int qqPosition = getQQPosition();
  162. int otherPosition = getOtherPosition();
  163. if (firstVisibleItem == photoPosition || (firstVisibleItem >= photoPosition && lastVisibleItem < wxPosition)) {
  164. binding.tabLayout.selectTab(binding.tabLayout.getTabAt(0));
  165. } else if (firstVisibleItem == wxPosition || (firstVisibleItem >= wxPosition && lastVisibleItem < qqPosition)) {
  166. binding.tabLayout.selectTab(binding.tabLayout.getTabAt(1));
  167. } else if (firstVisibleItem == qqPosition || (firstVisibleItem >= qqPosition && lastVisibleItem < otherPosition)) {
  168. binding.tabLayout.selectTab(binding.tabLayout.getTabAt(2));
  169. } else if (firstVisibleItem >= otherPosition) {
  170. binding.tabLayout.selectTab(binding.tabLayout.getTabAt(3));
  171. }
  172. }
  173. };
  174. if (imageRecoverViewModel.isTrial()) {
  175. binding.ryImageRecover.setOnFlingListener(new RecyclerView.OnFlingListener() {
  176. @Override
  177. public boolean onFling(int velocityX, int velocityY) {
  178. if (gridLayoutManager.findFirstCompletelyVisibleItemPosition() == 0 && gridLayoutManager.findLastCompletelyVisibleItemPosition() == gridLayoutManager.getItemCount() - 1 && velocityY > 0) {
  179. showTrialFinishDialog();
  180. return true;
  181. }
  182. return false;
  183. }
  184. });
  185. }
  186. binding.ryImageRecover.addOnScrollListener(onScrollListener);
  187. }
  188. public int getPhotoPosition() {
  189. return 0;
  190. }
  191. public int getWxPosition() {
  192. return getPhotoPosition() + photoAdapter.getItemCount() + 1;
  193. }
  194. public int getQQPosition() {
  195. return getWxPosition() + wxAdapter.getItemCount() + 1;
  196. }
  197. public int getOtherPosition() {
  198. return getQQPosition() + qqAdapter.getItemCount() + 1;
  199. }
  200. @Override
  201. public void onCheck(ImageDeepDetector.ImageFile imageFile) {
  202. if (imageFile == null) {
  203. return;
  204. }
  205. imageRecoverViewModel.setItemCheck(imageFile);
  206. }
  207. @Override
  208. public void onImgItemClick(ImageDeepDetector.ImageFile imageFile) {
  209. if (imageFile == null) {
  210. return;
  211. }
  212. // ToastUtil.show(imageFile.getPath(), ToastUtil.LENGTH_SHORT);
  213. if (Objects.equals(imageRecoverViewModel.getType(), MemberType.APP_IMAGE_CLEAN)) {
  214. imageRecoverViewModel.setItemCheck(imageFile);
  215. } else {
  216. imageRecoverViewModel.checkPreview(imageFile);
  217. }
  218. }
  219. private void initTabLayout() {
  220. binding.tabLayout.removeAllTabs();
  221. binding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
  222. @SuppressLint("UseCompatLoadingForDrawables")
  223. @Override
  224. public void onTabSelected(TabLayout.Tab tab) {
  225. if (tab.getCustomView() == null) {
  226. return;
  227. }
  228. ItemTabImageRecoverBinding itemBinding = ItemTabImageRecoverBinding.bind(tab.getCustomView());
  229. itemBinding.tabTitle.setTextColor(getResources().getColor(com.atmob.app.base.R.color.white));
  230. itemBinding.tabTitle.setBackground(getResources().getDrawable(R.drawable.bg_tab_selected));
  231. }
  232. @Override
  233. public void onTabUnselected(TabLayout.Tab tab) {
  234. if (tab.getCustomView() == null) {
  235. return;
  236. }
  237. ItemTabImageRecoverBinding itemBinding = ItemTabImageRecoverBinding.bind(tab.getCustomView());
  238. itemBinding.tabTitle.setTextColor(getResources().getColor(R.color.tab_un_select_text_color));
  239. itemBinding.tabTitle.setBackground(null);
  240. }
  241. @Override
  242. public void onTabReselected(TabLayout.Tab tab) {
  243. }
  244. });
  245. for (int i = 0; i < tabTitle.length; i++) {
  246. TabLayout.Tab tab = binding.tabLayout.newTab();
  247. ItemTabImageRecoverBinding tabLayoutBinding = ItemTabImageRecoverBinding.inflate(getLayoutInflater());
  248. int finalI = i;
  249. tabLayoutBinding.getRoot().setOnClickListener(v -> {
  250. switch (finalI) {
  251. case 0:
  252. gridLayoutManager.scrollToPositionWithOffset(getPhotoPosition(), 0);
  253. break;
  254. case 1:
  255. gridLayoutManager.scrollToPositionWithOffset(getWxPosition(), 0);
  256. break;
  257. case 2:
  258. gridLayoutManager.scrollToPositionWithOffset(getQQPosition(), 0);
  259. break;
  260. case 3:
  261. gridLayoutManager.scrollToPositionWithOffset(getOtherPosition(), 0);
  262. break;
  263. }
  264. binding.tabLayout.selectTab(binding.tabLayout.getTabAt(finalI));
  265. });
  266. tabLayoutBinding.tabTitle.setText(tabTitle[i]);
  267. tab.setCustomView(tabLayoutBinding.getRoot());
  268. binding.tabLayout.addTab(tab);
  269. }
  270. }
  271. @SuppressLint("NotifyDataSetChanged")
  272. private void initObserver() {
  273. imageRecoverViewModel.getScrollTop().observe(this, o -> binding.ryImageRecover.scrollToPosition(0));
  274. imageRecoverViewModel.getShowTrialFinishDialog().observe(this, o -> showTrialFinishDialog());
  275. imageRecoverViewModel.getShowTrialExportFailDialog().observe(this, o -> showTrialExportFailDialog());
  276. imageRecoverViewModel.getDeleteUriListSdk11().observe(this, list -> {
  277. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
  278. PendingIntent pi = MediaStore.createDeleteRequest(getBaseContext().getContentResolver(), list);
  279. IntentSenderRequest request = new IntentSenderRequest.Builder(pi.getIntentSender()).build();
  280. new ActivityForResultUtil<IntentSenderRequest, ActivityResult>().startActivityForResult(this, new ActivityResultContracts.StartIntentSenderForResult(), request, result -> {
  281. imageRecoverViewModel.callDealComplete(result.getResultCode() == Activity.RESULT_OK);
  282. });
  283. }
  284. });
  285. imageRecoverViewModel.getNotifyList().observe(this, o -> {
  286. photoAdapter.notifyDataSetChanged();
  287. wxAdapter.notifyDataSetChanged();
  288. qqAdapter.notifyDataSetChanged();
  289. otherAdapter.notifyDataSetChanged();
  290. });
  291. imageRecoverViewModel.getShowScanDialogEvent().observe(this, this::showScanProgressDialog);
  292. imageRecoverViewModel.getDetectedPhotoImg().observe(this, list -> photoAdapter.submit(list));
  293. imageRecoverViewModel.getDetectedWxImg().observe(this, list -> wxAdapter.submit(list));
  294. imageRecoverViewModel.getDetectedQQImg().observe(this, list -> qqAdapter.submit(list));
  295. imageRecoverViewModel.getDetectedOtherImg().observe(this, list -> otherAdapter.submit(list));
  296. imageRecoverViewModel.getPreviewEvent().observe(this, pair -> PreviewActivity.startImagePreView(this, pair.first, pair.second, imageRecoverViewModel.isTrial()));
  297. imageRecoverViewModel.getShowLoadingEvent().observe(this, this::showLoadingDialog);
  298. imageRecoverViewModel.getShowClearDialog().observe(this, o -> showClearDialog());
  299. }
  300. private void showTrialExportFailDialog() {
  301. if (showTrialExportFailDialog == null) {
  302. showTrialExportFailDialog = new CommonSureDialog(this);
  303. showTrialExportFailDialog.setDialogTitle(R.string.trial_export_fail_title)
  304. .setDialogContent(R.string.trial_export_fail_content).setSureText(R.string.dialog_trial_recover);
  305. showTrialExportFailDialog.setOnDialogClickListener(() -> {
  306. imageRecoverViewModel.onTrialRecoverClick();
  307. EventHelper.report(EventId.hf1001123, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(MemberType.APP_IMAGE_RECOVER)));
  308. });
  309. showTrialExportFailDialog.setOnDismissListener(dialog -> EventHelper.report(EventId.hf1001124, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(MemberType.APP_IMAGE_RECOVER))));
  310. }
  311. if (!showTrialExportFailDialog.isShowing()) {
  312. showTrialExportFailDialog.show();
  313. EventHelper.report(EventId.hf1001120, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(MemberType.APP_IMAGE_RECOVER)));
  314. }
  315. }
  316. private void showTrialFinishDialog() {
  317. if (showTrialFinishDialog == null) {
  318. showTrialFinishDialog = new CommonSureDialog(this);
  319. showTrialFinishDialog.setDialogTitle(getString(R.string.scanning_progress, 13))
  320. .setDialogContent(getString(R.string.trial_scanning_finish_content, 13)).setSureText(R.string.dialog_trial_recover);
  321. showTrialFinishDialog.setOnDialogClickListener(() -> {
  322. imageRecoverViewModel.onTrialRecoverClick();
  323. EventHelper.report(EventId.hf1001121, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(MemberType.APP_IMAGE_RECOVER)));
  324. });
  325. showTrialFinishDialog.setOnDismissListener(dialog -> EventHelper.report(EventId.hf1001122, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(MemberType.APP_IMAGE_RECOVER))));
  326. }
  327. if (!showTrialFinishDialog.isShowing()) {
  328. showTrialFinishDialog.show();
  329. EventHelper.report(EventId.hf1001118, Maps.asMap(EventId.EVENT_ID, ReportUtil.getReportId(MemberType.APP_IMAGE_RECOVER)));
  330. }
  331. }
  332. private void showClearDialog() {
  333. if (clearDialog == null) {
  334. clearDialog = new CommonSureDialog(this);
  335. clearDialog.setDialogTitle(R.string.img_clear_sure_title).setDialogContent(R.string.img_clear_sure_content);
  336. clearDialog.setOnDialogClickListener(() -> imageRecoverViewModel.executeImageDelete());
  337. }
  338. clearDialog.show();
  339. }
  340. private void showBackDialog() {
  341. if (backDialog == null) {
  342. backDialog = new CommonSureDialog(this);
  343. backDialog.setDialogTitle(R.string.img_recovery_back_title)
  344. .setDialogContent(R.string.img_recovery_back_content)
  345. .setOnDialogClickListener(this::finish);
  346. }
  347. backDialog.show();
  348. }
  349. @Override
  350. protected void configImmersion(@NonNull ImmersionBar immersionBar) {
  351. immersionBar.statusBarDarkFont(true);
  352. }
  353. public void showScanProgressDialog(Boolean show) {
  354. if (BoxingUtil.boxing(show)) {
  355. if (scanFileDialog == null) {
  356. scanFileDialog = new ScanFileDialog(this, imageRecoverViewModel.getDetectedLastFileName());
  357. scanFileDialog.setOnCancelListener(() -> imageRecoverViewModel.cancelScan());
  358. }
  359. scanFileDialog.show();
  360. } else {
  361. if (scanFileDialog != null) {
  362. scanFileDialog.dismiss();
  363. }
  364. }
  365. }
  366. public void showLoadingDialog(Boolean show) {
  367. if (BoxingUtil.boxing(show)) {
  368. if (loadingDialog == null) {
  369. loadingDialog = new CommonLoadingDialog(this);
  370. loadingDialog.setMessage(Objects.equals(imageRecoverViewModel.getType(), MemberType.APP_IMAGE_CLEAN) ? R.string.deleting : R.string.exporting);
  371. }
  372. loadingDialog.show();
  373. } else {
  374. if (loadingDialog != null) {
  375. loadingDialog.dismiss();
  376. }
  377. }
  378. }
  379. @Override
  380. public void onBackPressed() {
  381. showBackDialog();
  382. }
  383. @Override
  384. public boolean onKeyDown(int keyCode, KeyEvent event) {
  385. if (keyCode == KeyEvent.KEYCODE_BACK) {
  386. onBackPressed();
  387. return true;
  388. }
  389. return super.onKeyDown(keyCode, event);
  390. }
  391. @Override
  392. protected void onDestroy() {
  393. super.onDestroy();
  394. binding.tabLayout.clearOnTabSelectedListeners();
  395. }
  396. @Override
  397. protected boolean shouldImmersion() {
  398. return true;
  399. }
  400. @Override
  401. protected void initViewModel() {
  402. super.initViewModel();
  403. imageRecoverViewModel = getViewModelProvider().get(ImageRecoverViewModel.class);
  404. binding.setImageRecoverViewModel(imageRecoverViewModel);
  405. }
  406. }