mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-31 06:43:56 +01:00 
			
		
		
		
	added javadoc/kdoc and fixed minor bug
This commit is contained in:
		
							parent
							
								
									9a3acdc3a0
								
							
						
					
					
						commit
						71c3e81fa3
					
				
					 21 changed files with 610 additions and 245 deletions
				
			
		|  | @ -127,14 +127,14 @@ public class ContributionController { | |||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Shows a dialog alerting the user about location services being off | ||||
|      * and asking them to turn it on | ||||
|      * Shows a dialog alerting the user about location services being off and asking them to turn it | ||||
|      * on | ||||
|      * TODO: Add a seperate callback in LocationPermissionsHelper for this. | ||||
|      *      Ref: https://github.com/commons-app/apps-android-commons/pull/5494/files#r1510553114 | ||||
|      * | ||||
|      * @param activity Activity reference | ||||
|      * @param activity           Activity reference | ||||
|      * @param dialogTextResource Resource id of text to be shown in dialog | ||||
|      * @param toastTextResource Resource id of text to be shown in toast | ||||
|      * @param toastTextResource  Resource id of text to be shown in toast | ||||
|      */ | ||||
|     private void showLocationOffDialog(Activity activity, int dialogTextResource, | ||||
|         int toastTextResource) { | ||||
|  | @ -320,6 +320,10 @@ public class ContributionController { | |||
|         return shareIntent; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetches the contributions with the state "IN_PROGRESS", "QUEUED" and "PAUSED" and then it | ||||
|      * populates the `pendingContributionList`. | ||||
|      **/ | ||||
|     void getPendingContributions() { | ||||
|         final PagedList.Config pagedListConfig = | ||||
|             (new PagedList.Config.Builder()) | ||||
|  | @ -335,6 +339,10 @@ public class ContributionController { | |||
|         pendingContributionList = livePagedListBuilder.build(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetches the contributions with the state "FAILED" and populates the | ||||
|      * `failedContributionList`. | ||||
|      **/ | ||||
|     void getFailedContributions() { | ||||
|         final PagedList.Config pagedListConfig = | ||||
|             (new PagedList.Config.Builder()) | ||||
|  | @ -349,6 +357,10 @@ public class ContributionController { | |||
|         failedContributionList = livePagedListBuilder.build(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetches the contributions with the state "IN_PROGRESS", "QUEUED", "PAUSED" and "FAILED" and | ||||
|      * then it populates the `failedAndPendingContributionList`. | ||||
|      **/ | ||||
|     void getFailedAndPendingContributions() { | ||||
|         final PagedList.Config pagedListConfig = | ||||
|             (new PagedList.Config.Builder()) | ||||
|  |  | |||
|  | @ -48,14 +48,27 @@ public abstract class ContributionDao { | |||
|     @Delete | ||||
|     public abstract void deleteSynchronous(Contribution contribution); | ||||
| 
 | ||||
|     /** | ||||
|      * Deletes contributions with specific states from the database. | ||||
|      * | ||||
|      * @param states The states of the contributions to delete. | ||||
|      * @throws SQLiteException If an SQLite error occurs. | ||||
|      */ | ||||
|     @Query("DELETE FROM contribution WHERE state IN (:states)") | ||||
|     public abstract void deleteContributionsWithStatesSynchronous(List<Integer> states) throws SQLiteException; | ||||
|     public abstract void deleteContributionsWithStatesSynchronous(List<Integer> states) | ||||
|         throws SQLiteException; | ||||
| 
 | ||||
|     public Completable delete(final Contribution contribution) { | ||||
|         return Completable | ||||
|             .fromAction(() -> deleteSynchronous(contribution)); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Deletes contributions with specific states from the database. | ||||
|      * | ||||
|      * @param states The states of the contributions to delete. | ||||
|      * @return A Completable indicating the result of the operation. | ||||
|      */ | ||||
|     public Completable deleteContributionsWithStates(List<Integer> states) { | ||||
|         return Completable | ||||
|             .fromAction(() -> deleteContributionsWithStatesSynchronous(states)); | ||||
|  | @ -70,10 +83,22 @@ public abstract class ContributionDao { | |||
|     @Query("SELECT * from contribution WHERE state IN (:states) order by media_dateUploaded DESC") | ||||
|     public abstract Single<List<Contribution>> getContribution(List<Integer> states); | ||||
| 
 | ||||
|     /** | ||||
|      * Gets contributions with specific states in descending order by the date they were uploaded. | ||||
|      * | ||||
|      * @param states The states of the contributions to fetch. | ||||
|      * @return A DataSource factory for paginated contributions with the specified states. | ||||
|      */ | ||||
|     @Query("SELECT * from contribution WHERE state IN (:states) order by media_dateUploaded DESC") | ||||
|     public abstract DataSource.Factory<Integer, Contribution> getContributions( | ||||
|         List<Integer> states); | ||||
| 
 | ||||
|     /** | ||||
|      * Gets contributions with specific states in ascending order by the date the upload started. | ||||
|      * | ||||
|      * @param states The states of the contributions to fetch. | ||||
|      * @return A DataSource factory for paginated contributions with the specified states. | ||||
|      */ | ||||
|     @Query("SELECT * from contribution WHERE state IN (:states) order by dateUploadStarted ASC") | ||||
|     public abstract DataSource.Factory<Integer, Contribution> getContributionsSortedByDateUploadStarted( | ||||
|         List<Integer> states); | ||||
|  | @ -87,6 +112,12 @@ public abstract class ContributionDao { | |||
|     @Update | ||||
|     public abstract void updateSynchronous(Contribution contribution); | ||||
| 
 | ||||
|     /** | ||||
|      * Updates the state of contributions with specific states. | ||||
|      * | ||||
|      * @param states   The current states of the contributions to update. | ||||
|      * @param newState The new state to set. | ||||
|      */ | ||||
|     @Query("UPDATE contribution SET state = :newState WHERE state IN (:states)") | ||||
|     public abstract void updateContributionsState(List<Integer> states, int newState); | ||||
| 
 | ||||
|  | @ -98,6 +129,13 @@ public abstract class ContributionDao { | |||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Updates the state of contributions with specific states asynchronously. | ||||
|      * | ||||
|      * @param states   The current states of the contributions to update. | ||||
|      * @param newState The new state to set. | ||||
|      * @return A Completable indicating the result of the operation. | ||||
|      */ | ||||
|     public Completable updateContributionsWithStates(List<Integer> states, int newState) { | ||||
|         return Completable | ||||
|             .fromAction(() -> { | ||||
|  |  | |||
|  | @ -122,7 +122,8 @@ public class ContributionsFragment | |||
| 
 | ||||
|     public FragmentContributionsBinding binding; | ||||
| 
 | ||||
|     @Inject ContributionsPresenter contributionsPresenter; | ||||
|     @Inject | ||||
|     ContributionsPresenter contributionsPresenter; | ||||
| 
 | ||||
|     @Inject | ||||
|     SessionManager sessionManager; | ||||
|  | @ -159,20 +160,22 @@ public class ContributionsFragment | |||
|                     areAllGranted = areAllGranted && b; | ||||
|                 } | ||||
| 
 | ||||
|             if (areAllGranted) { | ||||
|                 onLocationPermissionGranted(); | ||||
|             } else { | ||||
|                 if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) | ||||
|                     && store.getBoolean("displayLocationPermissionForCardView", true) | ||||
|                     && !store.getBoolean("doNotAskForLocationPermission", false) | ||||
|                     && (((MainActivity) getActivity()).activeFragment == ActiveFragment.CONTRIBUTIONS)) { | ||||
|                     binding.cardViewNearby.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; | ||||
|                 if (areAllGranted) { | ||||
|                     onLocationPermissionGranted(); | ||||
|                 } else { | ||||
|                     displayYouWontSeeNearbyMessage(); | ||||
|                     if (shouldShowRequestPermissionRationale( | ||||
|                         Manifest.permission.ACCESS_FINE_LOCATION) | ||||
|                         && store.getBoolean("displayLocationPermissionForCardView", true) | ||||
|                         && !store.getBoolean("doNotAskForLocationPermission", false) | ||||
|                         && (((MainActivity) getActivity()).activeFragment | ||||
|                         == ActiveFragment.CONTRIBUTIONS)) { | ||||
|                         binding.cardViewNearby.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; | ||||
|                     } else { | ||||
|                         displayYouWontSeeNearbyMessage(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|         }); | ||||
| 
 | ||||
|     @NonNull | ||||
|     public static ContributionsFragment newInstance() { | ||||
|  | @ -210,11 +213,10 @@ public class ContributionsFragment | |||
|         checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> { | ||||
|             if (isChecked) { | ||||
|                 // Do not ask for permission on activity start again | ||||
|                 store.putBoolean("displayLocationPermissionForCardView",false); | ||||
|                 store.putBoolean("displayLocationPermissionForCardView", false); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
| 
 | ||||
|         if (savedInstanceState != null) { | ||||
|             mediaDetailPagerFragment = (MediaDetailPagerFragment) getChildFragmentManager() | ||||
|                 .findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG); | ||||
|  | @ -224,7 +226,7 @@ public class ContributionsFragment | |||
|         } | ||||
| 
 | ||||
|         initFragments(); | ||||
|         if(!isUserProfile) { | ||||
|         if (!isUserProfile) { | ||||
|             upDateUploadCount(); | ||||
|         } | ||||
|         if (shouldShowMediaDetailsFragment) { | ||||
|  | @ -269,10 +271,13 @@ public class ContributionsFragment | |||
|         notificationCount = notification.findViewById(R.id.notification_count_badge); | ||||
|         MenuItem uploadMenuItem = menu.findItem(R.id.upload_tab); | ||||
|         final View uploadMenuItemActionView = uploadMenuItem.getActionView(); | ||||
|         pendingUploadsCountTextView = uploadMenuItemActionView.findViewById(R.id.pending_uploads_count_badge); | ||||
|         uploadsErrorTextView = uploadMenuItemActionView.findViewById(R.id.uploads_error_count_badge); | ||||
|         pendingUploadsImageView = uploadMenuItemActionView.findViewById(R.id.pending_uploads_image_view); | ||||
|         if (pendingUploadsImageView != null){ | ||||
|         pendingUploadsCountTextView = uploadMenuItemActionView.findViewById( | ||||
|             R.id.pending_uploads_count_badge); | ||||
|         uploadsErrorTextView = uploadMenuItemActionView.findViewById( | ||||
|             R.id.uploads_error_count_badge); | ||||
|         pendingUploadsImageView = uploadMenuItemActionView.findViewById( | ||||
|             R.id.pending_uploads_image_view); | ||||
|         if (pendingUploadsImageView != null) { | ||||
|             pendingUploadsImageView.setOnClickListener(view -> { | ||||
|                 startActivity(new Intent(getContext(), UploadProgressActivity.class)); | ||||
|             }); | ||||
|  | @ -301,13 +306,21 @@ public class ContributionsFragment | |||
|                 throwable -> Timber.e(throwable, "Error occurred while loading notifications"))); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the visibility of the upload icon based on the number of failed and pending | ||||
|      * contributions. | ||||
|      */ | ||||
|     public void setUploadIconVisibility() { | ||||
|         contributionController.getFailedAndPendingContributions(); | ||||
|         contributionController.failedAndPendingContributionList.observe(getViewLifecycleOwner(), list -> { | ||||
|             updateUploadIcon(list.size()); | ||||
|         }); | ||||
|         contributionController.failedAndPendingContributionList.observe(getViewLifecycleOwner(), | ||||
|             list -> { | ||||
|                 updateUploadIcon(list.size()); | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the count for the upload icon based on the number of pending and failed contributions. | ||||
|      */ | ||||
|     public void setUploadIconCount() { | ||||
|         contributionController.getPendingContributions(); | ||||
|         contributionController.pendingContributionList.observe(getViewLifecycleOwner(), | ||||
|  | @ -379,7 +392,7 @@ public class ContributionsFragment | |||
|     } | ||||
| 
 | ||||
|     private void setupViewForMediaDetails() { | ||||
|         if (binding!=null) { | ||||
|         if (binding != null) { | ||||
|             binding.campaignsView.setVisibility(View.GONE); | ||||
|         } | ||||
|     } | ||||
|  | @ -489,7 +502,7 @@ public class ContributionsFragment | |||
|         contributionsPresenter.onAttachView(this); | ||||
|         locationManager.addLocationListener(this); | ||||
| 
 | ||||
|         if (binding==null) { | ||||
|         if (binding == null) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|  | @ -508,7 +521,8 @@ public class ContributionsFragment | |||
|                 } catch (Exception e) { | ||||
|                     Timber.e(e); | ||||
|                 } | ||||
|                 if (binding.cardViewNearby.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) { | ||||
|                 if (binding.cardViewNearby.cardViewVisibilityState | ||||
|                     == NearbyNotificationCardView.CardViewVisibilityState.READY) { | ||||
|                     binding.cardViewNearby.setVisibility(View.VISIBLE); | ||||
|                 } | ||||
| 
 | ||||
|  | @ -518,7 +532,7 @@ public class ContributionsFragment | |||
|             } | ||||
| 
 | ||||
|             // Notification Count and Campaigns should not be set, if it is used in User Profile | ||||
|             if(!isUserProfile) { | ||||
|             if (!isUserProfile) { | ||||
|                 setNotificationCount(); | ||||
|                 fetchCampaigns(); | ||||
|                 setUploadIconVisibility(); | ||||
|  | @ -529,7 +543,8 @@ public class ContributionsFragment | |||
|     } | ||||
| 
 | ||||
|     private void checkPermissionsAndShowNearbyCardView() { | ||||
|         if (PermissionUtils.hasPermission(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) { | ||||
|         if (PermissionUtils.hasPermission(getActivity(), | ||||
|             new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) { | ||||
|             onLocationPermissionGranted(); | ||||
|         } else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) | ||||
|             && store.getBoolean("displayLocationPermissionForCardView", true) | ||||
|  | @ -662,14 +677,14 @@ public class ContributionsFragment | |||
|      */ | ||||
|     private void fetchCampaigns() { | ||||
|         if (Utils.isMonumentsEnabled(new Date())) { | ||||
|             if (binding!=null) { | ||||
|             if (binding != null) { | ||||
|                 binding.campaignsView.setCampaign(wlmCampaign); | ||||
|                 binding.campaignsView.setVisibility(View.VISIBLE); | ||||
|             } | ||||
|         } else if (store.getBoolean(CampaignView.CAMPAIGNS_DEFAULT_PREFERENCE, true)) { | ||||
|             presenter.getCampaigns(); | ||||
|         } else { | ||||
|             if (binding!=null) { | ||||
|             if (binding != null) { | ||||
|                 binding.campaignsView.setVisibility(View.GONE); | ||||
|             } | ||||
|         } | ||||
|  | @ -683,7 +698,7 @@ public class ContributionsFragment | |||
|     @Override | ||||
|     public void showCampaigns(Campaign campaign) { | ||||
|         if (campaign != null && !isUserProfile) { | ||||
|             if (binding!=null) { | ||||
|             if (binding != null) { | ||||
|                 binding.campaignsView.setCampaign(campaign); | ||||
|             } | ||||
|         } | ||||
|  | @ -712,33 +727,49 @@ public class ContributionsFragment | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Updates the visibility and text of the pending uploads count TextView based on the given | ||||
|      * count. | ||||
|      * | ||||
|      * @param pendingCount The number of pending uploads. | ||||
|      */ | ||||
|     public void updatePendingIcon(int pendingCount) { | ||||
|         if (pendingUploadsCountTextView != null){ | ||||
|             if (pendingCount != 0){ | ||||
|         if (pendingUploadsCountTextView != null) { | ||||
|             if (pendingCount != 0) { | ||||
|                 pendingUploadsCountTextView.setVisibility(View.VISIBLE); | ||||
|                 pendingUploadsCountTextView.setText(String.valueOf(pendingCount)); | ||||
|             }else { | ||||
|             } else { | ||||
|                 pendingUploadsCountTextView.setVisibility(View.INVISIBLE); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Updates the visibility and text of the error uploads TextView based on the given count. | ||||
|      * | ||||
|      * @param errorCount The number of error uploads. | ||||
|      */ | ||||
|     public void updateErrorIcon(int errorCount) { | ||||
|         if (uploadsErrorTextView != null){ | ||||
|             if (errorCount != 0){ | ||||
|         if (uploadsErrorTextView != null) { | ||||
|             if (errorCount != 0) { | ||||
|                 uploadsErrorTextView.setVisibility(View.VISIBLE); | ||||
|                 uploadsErrorTextView.setText(String.valueOf(errorCount)); | ||||
|             }else { | ||||
|             } else { | ||||
|                 uploadsErrorTextView.setVisibility(View.GONE); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Updates the visibility of the pending uploads ImageView based on the given count. | ||||
|      * | ||||
|      * @param count The number of pending uploads. | ||||
|      */ | ||||
|     public void updateUploadIcon(int count) { | ||||
|         if (pendingUploadsImageView != null){ | ||||
|             if (count != 0){ | ||||
|         if (pendingUploadsImageView != null) { | ||||
|             if (count != 0) { | ||||
|                 pendingUploadsImageView.setVisibility(View.VISIBLE); | ||||
|             }else { | ||||
|             } else { | ||||
|                 pendingUploadsImageView.setVisibility(View.GONE); | ||||
|             } | ||||
|         } | ||||
|  | @ -779,7 +810,8 @@ public class ContributionsFragment | |||
|     public boolean backButtonClicked() { | ||||
|         if (mediaDetailPagerFragment != null && mediaDetailPagerFragment.isVisible()) { | ||||
|             if (store.getBoolean("displayNearbyCardView", true) && !isUserProfile) { | ||||
|                 if (binding.cardViewNearby.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) { | ||||
|                 if (binding.cardViewNearby.cardViewVisibilityState | ||||
|                     == NearbyNotificationCardView.CardViewVisibilityState.READY) { | ||||
|                     binding.cardViewNearby.setVisibility(View.VISIBLE); | ||||
|                 } | ||||
|             } else { | ||||
|  | @ -834,7 +866,7 @@ public class ContributionsFragment | |||
|     public void restartUpload(Contribution contribution) { | ||||
|         contribution.setDateUploadStarted(Calendar.getInstance().getTime()); | ||||
|         if (contribution.getState() == Contribution.STATE_FAILED) { | ||||
|             if (contribution.getErrorInfo() == null){ | ||||
|             if (contribution.getErrorInfo() == null) { | ||||
|                 contribution.setChunkInfo(null); | ||||
|                 contribution.setTransferred(0); | ||||
|             } | ||||
|  |  | |||
|  | @ -80,8 +80,6 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl | |||
|     private Animation rotate_forward; | ||||
|     private Animation rotate_backward; | ||||
|     private boolean isFabOpen; | ||||
|     public int pendingUploadsCount = 0; | ||||
|     public int uploadErrorCount = 0; | ||||
|     @VisibleForTesting | ||||
|     protected RecyclerView rvContributionsList; | ||||
| 
 | ||||
|  | @ -151,7 +149,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl | |||
|         contributionsListPresenter.onAttachView(this); | ||||
|         binding.fabCustomGallery.setOnClickListener(v -> launchCustomSelector()); | ||||
|         binding.fabCustomGallery.setOnLongClickListener(view -> { | ||||
|             ViewUtil.showShortToast(getContext(),R.string.custom_selector_title); | ||||
|             ViewUtil.showShortToast(getContext(), R.string.custom_selector_title); | ||||
|             return true; | ||||
|         }); | ||||
| 
 | ||||
|  | @ -160,7 +158,8 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl | |||
|             binding.fabLayout.setVisibility(VISIBLE); | ||||
|         } else { | ||||
|             binding.tvContributionsOfUser.setVisibility(VISIBLE); | ||||
|             binding.tvContributionsOfUser.setText(getString(R.string.contributions_of_user, userName)); | ||||
|             binding.tvContributionsOfUser.setText( | ||||
|                 getString(R.string.contributions_of_user, userName)); | ||||
|             binding.fabLayout.setVisibility(GONE); | ||||
|         } | ||||
| 
 | ||||
|  | @ -305,8 +304,9 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl | |||
|     public void onConfigurationChanged(final Configuration newConfig) { | ||||
|         super.onConfigurationChanged(newConfig); | ||||
|         // check orientation | ||||
|         binding.fabLayout.setOrientation(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? | ||||
|             LinearLayout.HORIZONTAL : LinearLayout.VERTICAL); | ||||
|         binding.fabLayout.setOrientation( | ||||
|             newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? | ||||
|                 LinearLayout.HORIZONTAL : LinearLayout.VERTICAL); | ||||
|         rvContributionsList | ||||
|             .setLayoutManager( | ||||
|                 new GridLayoutManager(getContext(), getSpanCount(newConfig.orientation))); | ||||
|  | @ -326,7 +326,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl | |||
|             animateFAB(isFabOpen); | ||||
|         }); | ||||
|         binding.fabCamera.setOnLongClickListener(view -> { | ||||
|             ViewUtil.showShortToast(getContext(),R.string.add_contribution_from_camera); | ||||
|             ViewUtil.showShortToast(getContext(), R.string.add_contribution_from_camera); | ||||
|             return true; | ||||
|         }); | ||||
|         binding.fabGallery.setOnClickListener(view -> { | ||||
|  | @ -334,7 +334,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl | |||
|             animateFAB(isFabOpen); | ||||
|         }); | ||||
|         binding.fabGallery.setOnLongClickListener(view -> { | ||||
|             ViewUtil.showShortToast(getContext(),R.string.menu_from_gallery); | ||||
|             ViewUtil.showShortToast(getContext(), R.string.menu_from_gallery); | ||||
|             return true; | ||||
|         }); | ||||
|     } | ||||
|  |  | |||
|  | @ -64,6 +64,12 @@ class ContributionsLocalDataSource { | |||
|         return contributionDao.delete(contribution); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Deletes contributions with specific states. | ||||
|      * | ||||
|      * @param states The states of the contributions to delete. | ||||
|      * @return A Completable indicating the result of the operation. | ||||
|      */ | ||||
|     public Completable deleteContributionsWithStates(List<Integer> states) { | ||||
|         return contributionDao.deleteContributionsWithStates(states); | ||||
|     } | ||||
|  | @ -72,10 +78,23 @@ class ContributionsLocalDataSource { | |||
|         return contributionDao.fetchContributions(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetches contributions with specific states. | ||||
|      * | ||||
|      * @param states The states of the contributions to fetch. | ||||
|      * @return A DataSource factory for paginated contributions with the specified states. | ||||
|      */ | ||||
|     public Factory<Integer, Contribution> getContributionsWithStates(List<Integer> states) { | ||||
|         return contributionDao.getContributions(states); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetches contributions with specific states sorted by the date the upload started. | ||||
|      * | ||||
|      * @param states The states of the contributions to fetch. | ||||
|      * @return A DataSource factory for paginated contributions with the specified states sorted by | ||||
|      * date upload started. | ||||
|      */ | ||||
|     public Factory<Integer, Contribution> getContributionsWithStatesSortedByDateUploadStarted( | ||||
|         List<Integer> states) { | ||||
|         return contributionDao.getContributionsSortedByDateUploadStarted(states); | ||||
|  |  | |||
|  | @ -34,13 +34,13 @@ public class ContributionsPresenter implements UserActionListener { | |||
|         @Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler) { | ||||
|         this.contributionsRepository = repository; | ||||
|         this.uploadRepository = uploadRepository; | ||||
|         this.ioThreadScheduler=ioThreadScheduler; | ||||
|         this.ioThreadScheduler = ioThreadScheduler; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onAttachView(ContributionsContract.View view) { | ||||
|         this.view = view; | ||||
|         compositeDisposable=new CompositeDisposable(); | ||||
|         compositeDisposable = new CompositeDisposable(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | @ -54,7 +54,12 @@ public class ContributionsPresenter implements UserActionListener { | |||
|         return contributionsRepository.getContributionWithFileName(title); | ||||
|     } | ||||
| 
 | ||||
|     public void checkDuplicateImageAndRestartContribution(Contribution contribution){ | ||||
|     /** | ||||
|      * Checks if a contribution is a duplicate and restarts the contribution process if it is not. | ||||
|      * | ||||
|      * @param contribution The contribution to check and potentially restart. | ||||
|      */ | ||||
|     public void checkDuplicateImageAndRestartContribution(Contribution contribution) { | ||||
|         compositeDisposable.add(uploadRepository | ||||
|             .checkDuplicateImage(contribution.getLocalUriPath().getPath()) | ||||
|             .subscribeOn(ioThreadScheduler) | ||||
|  | @ -62,7 +67,7 @@ public class ContributionsPresenter implements UserActionListener { | |||
|                 if (imageCheckResult == IMAGE_OK) { | ||||
|                     contribution.setState(Contribution.STATE_QUEUED); | ||||
|                     saveContribution(contribution); | ||||
|                 }else { | ||||
|                 } else { | ||||
|                     Timber.e("Contribution already exists"); | ||||
|                     compositeDisposable.add(contributionsRepository | ||||
|                         .deleteContributionFromDB(contribution) | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ public class ContributionsRepository { | |||
| 
 | ||||
|     /** | ||||
|      * Deletes a failed upload from DB | ||||
|      * | ||||
|      * @param contribution | ||||
|      * @return | ||||
|      */ | ||||
|  | @ -36,12 +37,19 @@ public class ContributionsRepository { | |||
|         return localDataSource.deleteContribution(contribution); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Deletes contributions from the database with specific states. | ||||
|      * | ||||
|      * @param states The states of the contributions to delete. | ||||
|      * @return A Completable indicating the result of the operation. | ||||
|      */ | ||||
|     public Completable deleteContributionsFromDBWithStates(List<Integer> states) { | ||||
|         return localDataSource.deleteContributionsWithStates(states); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get contribution object with title | ||||
|      * | ||||
|      * @param fileName | ||||
|      * @return | ||||
|      */ | ||||
|  | @ -53,11 +61,25 @@ public class ContributionsRepository { | |||
|         return localDataSource.getContributions(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetches contributions with specific states. | ||||
|      * | ||||
|      * @param states The states of the contributions to fetch. | ||||
|      * @return A DataSource factory for paginated contributions with the specified states. | ||||
|      */ | ||||
|     public Factory<Integer, Contribution> fetchContributionsWithStates(List<Integer> states) { | ||||
|         return localDataSource.getContributionsWithStates(states); | ||||
|     } | ||||
| 
 | ||||
|     public Factory<Integer, Contribution> fetchContributionsWithStatesSortedByDateUploadStarted(List<Integer> states) { | ||||
|     /** | ||||
|      * Fetches contributions with specific states sorted by the date the upload started. | ||||
|      * | ||||
|      * @param states The states of the contributions to fetch. | ||||
|      * @return A DataSource factory for paginated contributions with the specified states sorted by | ||||
|      * date upload started. | ||||
|      */ | ||||
|     public Factory<Integer, Contribution> fetchContributionsWithStatesSortedByDateUploadStarted( | ||||
|         List<Integer> states) { | ||||
|         return localDataSource.getContributionsWithStatesSortedByDateUploadStarted(states); | ||||
|     } | ||||
| 
 | ||||
|  | @ -65,19 +87,26 @@ public class ContributionsRepository { | |||
|         return localDataSource.saveContributions(contributions); | ||||
|     } | ||||
| 
 | ||||
|     public Completable save(Contribution contributions){ | ||||
|     public Completable save(Contribution contributions) { | ||||
|         return localDataSource.saveContributions(contributions); | ||||
|     } | ||||
| 
 | ||||
|     public void set(String key, long value) { | ||||
|         localDataSource.set(key,value); | ||||
|         localDataSource.set(key, value); | ||||
|     } | ||||
| 
 | ||||
|     public Completable updateContribution(Contribution contribution) { | ||||
|         return localDataSource.updateContribution(contribution); | ||||
|     } | ||||
| 
 | ||||
|     public Completable updateContributionWithStates(List<Integer> states, int newState) { | ||||
|     /** | ||||
|      * Updates the state of contributions with specific states. | ||||
|      * | ||||
|      * @param states   The current states of the contributions to update. | ||||
|      * @param newState The new state to set. | ||||
|      * @return A Completable indicating the result of the operation. | ||||
|      */ | ||||
|     public Completable updateContributionsWithStates(List<Integer> states, int newState) { | ||||
|         return localDataSource.updateContributionsWithStates(states, newState); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -55,7 +55,7 @@ import javax.inject.Inject; | |||
| import javax.inject.Named; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| public class MainActivity  extends BaseActivity | ||||
| public class MainActivity extends BaseActivity | ||||
|     implements FragmentManager.OnBackStackChangedListener { | ||||
| 
 | ||||
|     @Inject | ||||
|  | @ -147,16 +147,16 @@ public class MainActivity  extends BaseActivity | |||
|                 applicationKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", false); | ||||
|                 applicationKvStore.putBoolean("hasAlreadyLaunchedCategoriesDialog", false); | ||||
|             } | ||||
|             if(savedInstanceState == null){ | ||||
|             if (savedInstanceState == null) { | ||||
|                 //starting a fresh fragment. | ||||
|                 // Open Last opened screen if it is Contributions or Nearby, otherwise Contributions | ||||
|                 if(applicationKvStore.getBoolean("last_opened_nearby")){ | ||||
|                 if (applicationKvStore.getBoolean("last_opened_nearby")) { | ||||
|                     setTitle(getString(R.string.nearby_fragment)); | ||||
|                     showNearby(); | ||||
|                     loadFragment(NearbyParentFragment.newInstance(),false); | ||||
|                 }else{ | ||||
|                     loadFragment(NearbyParentFragment.newInstance(), false); | ||||
|                 } else { | ||||
|                     setTitle(getString(R.string.contributions_fragment)); | ||||
|                     loadFragment(ContributionsFragment.newInstance(),false); | ||||
|                     loadFragment(ContributionsFragment.newInstance(), false); | ||||
|                 } | ||||
|             } | ||||
|             setUpPager(); | ||||
|  | @ -168,7 +168,8 @@ public class MainActivity  extends BaseActivity | |||
|             if (VERSION.SDK_INT >= VERSION_CODES.Q) { | ||||
|                 PermissionUtils.checkPermissionsAndPerformAction( | ||||
|                     this, | ||||
|                     () -> {}, | ||||
|                     () -> { | ||||
|                     }, | ||||
|                     R.string.media_location_permission_denied, | ||||
|                     R.string.add_location_manually, | ||||
|                     permission.ACCESS_MEDIA_LOCATION); | ||||
|  | @ -182,32 +183,33 @@ public class MainActivity  extends BaseActivity | |||
|     } | ||||
| 
 | ||||
|     private void setUpPager() { | ||||
|         binding.fragmentMainNavTabLayout.setOnNavigationItemSelectedListener(navListener = (item) -> { | ||||
|             if (!item.getTitle().equals(getString(R.string.more))) { | ||||
|                 // do not change title for more fragment | ||||
|                 setTitle(item.getTitle()); | ||||
|             } | ||||
|             // set last_opened_nearby true if item is nearby screen else set false | ||||
|             applicationKvStore.putBoolean("last_opened_nearby", | ||||
|                 item.getTitle().equals(getString(R.string.nearby_fragment))); | ||||
|             final Fragment fragment = NavTab.of(item.getOrder()).newInstance(); | ||||
|             return loadFragment(fragment, true); | ||||
|         }); | ||||
|         binding.fragmentMainNavTabLayout.setOnNavigationItemSelectedListener( | ||||
|             navListener = (item) -> { | ||||
|                 if (!item.getTitle().equals(getString(R.string.more))) { | ||||
|                     // do not change title for more fragment | ||||
|                     setTitle(item.getTitle()); | ||||
|                 } | ||||
|                 // set last_opened_nearby true if item is nearby screen else set false | ||||
|                 applicationKvStore.putBoolean("last_opened_nearby", | ||||
|                     item.getTitle().equals(getString(R.string.nearby_fragment))); | ||||
|                 final Fragment fragment = NavTab.of(item.getOrder()).newInstance(); | ||||
|                 return loadFragment(fragment, true); | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     private void setUpLoggedOutPager() { | ||||
|         loadFragment(ExploreFragment.newInstance(),false); | ||||
|         loadFragment(ExploreFragment.newInstance(), false); | ||||
|         binding.fragmentMainNavTabLayout.setOnNavigationItemSelectedListener(item -> { | ||||
|             if (!item.getTitle().equals(getString(R.string.more))) { | ||||
|                 // do not change title for more fragment | ||||
|                 setTitle(item.getTitle()); | ||||
|             } | ||||
|             Fragment fragment = NavTabLoggedOut.of(item.getOrder()).newInstance(); | ||||
|             return loadFragment(fragment,true); | ||||
|             return loadFragment(fragment, true); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     private boolean loadFragment(Fragment fragment,boolean showBottom ) { | ||||
|     private boolean loadFragment(Fragment fragment, boolean showBottom) { | ||||
|         //showBottom so that we do not show the bottom tray again when constructing | ||||
|         //from the saved instance state. | ||||
|         if (fragment instanceof ContributionsFragment) { | ||||
|  | @ -237,7 +239,8 @@ public class MainActivity  extends BaseActivity | |||
|             bookmarkFragment = (BookmarkFragment) fragment; | ||||
|             activeFragment = ActiveFragment.BOOKMARK; | ||||
|         } else if (fragment == null && showBottom) { | ||||
|             if (applicationKvStore.getBoolean("login_skipped") == true) { // If logged out, more sheet is different | ||||
|             if (applicationKvStore.getBoolean("login_skipped") | ||||
|                 == true) { // If logged out, more sheet is different | ||||
|                 MoreBottomSheetLoggedOutFragment bottomSheet = new MoreBottomSheetLoggedOutFragment(); | ||||
|                 bottomSheet.show(getSupportFragmentManager(), | ||||
|                     "MoreBottomSheetLoggedOut"); | ||||
|  | @ -267,28 +270,30 @@ public class MainActivity  extends BaseActivity | |||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Adds number of uploads next to tab text "Contributions" then it will look like | ||||
|      * "Contributions (NUMBER)" | ||||
|      * Adds number of uploads next to tab text "Contributions" then it will look like "Contributions | ||||
|      * (NUMBER)" | ||||
|      * | ||||
|      * @param uploadCount | ||||
|      */ | ||||
|     public void setNumOfUploads(int uploadCount) { | ||||
|         if (activeFragment == ActiveFragment.CONTRIBUTIONS) { | ||||
|             setTitle(getResources().getString(R.string.contributions_fragment) +" "+ ( | ||||
|             setTitle(getResources().getString(R.string.contributions_fragment) + " " + ( | ||||
|                 !(uploadCount == 0) ? | ||||
|                 getResources() | ||||
|                 .getQuantityString(R.plurals.contributions_subtitle, | ||||
|                     uploadCount, uploadCount):getString(R.string.contributions_subtitle_zero))); | ||||
|                     getResources() | ||||
|                         .getQuantityString(R.plurals.contributions_subtitle, | ||||
|                             uploadCount, uploadCount) | ||||
|                     : getString(R.string.contributions_subtitle_zero))); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Resume the uploads that got stuck because of the app being killed | ||||
|      * or the device being rebooted. | ||||
|      * | ||||
|      * Resume the uploads that got stuck because of the app being killed or the device being | ||||
|      * rebooted. | ||||
|      * <p> | ||||
|      * When the app is terminated or the device is restarted, contributions remain in the | ||||
|      * 'STATE_IN_PROGRESS' state. This status persists and doesn't change during these events. | ||||
|      * So, retrieving contributions labeled as 'STATE_IN_PROGRESS' | ||||
|      * from the database will provide the list of uploads that appear as stuck on opening the app again | ||||
|      * 'STATE_IN_PROGRESS' state. This status persists and doesn't change during these events. So, | ||||
|      * retrieving contributions labeled as 'STATE_IN_PROGRESS' from the database will provide the | ||||
|      * list of uploads that appear as stuck on opening the app again | ||||
|      */ | ||||
|     @SuppressLint("CheckResult") | ||||
|     private void checkAndResumeStuckUploads() { | ||||
|  | @ -297,8 +302,8 @@ public class MainActivity  extends BaseActivity | |||
|             .subscribeOn(Schedulers.io()) | ||||
|             .blockingGet(); | ||||
|         Timber.d("Resuming " + stuckUploads.size() + " uploads..."); | ||||
|         if(!stuckUploads.isEmpty()) { | ||||
|             for(Contribution contribution: stuckUploads) { | ||||
|         if (!stuckUploads.isEmpty()) { | ||||
|             for (Contribution contribution : stuckUploads) { | ||||
|                 contribution.setState(Contribution.STATE_QUEUED); | ||||
|                 contribution.setDateUploadStarted(Calendar.getInstance().getTime()); | ||||
|                 Completable.fromAction(() -> contributionDao.saveSynchronous(contribution)) | ||||
|  | @ -327,24 +332,24 @@ public class MainActivity  extends BaseActivity | |||
|     protected void onRestoreInstanceState(Bundle savedInstanceState) { | ||||
|         super.onRestoreInstanceState(savedInstanceState); | ||||
|         String activeFragmentName = savedInstanceState.getString("activeFragment"); | ||||
|         if(activeFragmentName != null) { | ||||
|         if (activeFragmentName != null) { | ||||
|             restoreActiveFragment(activeFragmentName); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private void restoreActiveFragment(@NonNull String fragmentName) { | ||||
|         if(fragmentName.equals(ActiveFragment.CONTRIBUTIONS.name())) { | ||||
|         if (fragmentName.equals(ActiveFragment.CONTRIBUTIONS.name())) { | ||||
|             setTitle(getString(R.string.contributions_fragment)); | ||||
|             loadFragment(ContributionsFragment.newInstance(),false); | ||||
|         }else if(fragmentName.equals(ActiveFragment.NEARBY.name())) { | ||||
|             loadFragment(ContributionsFragment.newInstance(), false); | ||||
|         } else if (fragmentName.equals(ActiveFragment.NEARBY.name())) { | ||||
|             setTitle(getString(R.string.nearby_fragment)); | ||||
|             loadFragment(NearbyParentFragment.newInstance(),false); | ||||
|         }else if(fragmentName.equals(ActiveFragment.EXPLORE.name())) { | ||||
|             loadFragment(NearbyParentFragment.newInstance(), false); | ||||
|         } else if (fragmentName.equals(ActiveFragment.EXPLORE.name())) { | ||||
|             setTitle(getString(R.string.navigation_item_explore)); | ||||
|             loadFragment(ExploreFragment.newInstance(),false); | ||||
|         }else if(fragmentName.equals(ActiveFragment.BOOKMARK.name())) { | ||||
|             loadFragment(ExploreFragment.newInstance(), false); | ||||
|         } else if (fragmentName.equals(ActiveFragment.BOOKMARK.name())) { | ||||
|             setTitle(getString(R.string.bookmarks)); | ||||
|             loadFragment(BookmarkFragment.newInstance(),false); | ||||
|             loadFragment(BookmarkFragment.newInstance(), false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -360,8 +365,9 @@ public class MainActivity  extends BaseActivity | |||
|             // Means that nearby fragment is visible | ||||
|             /* If function nearbyParentFragment.backButtonClick() returns false, it means that the bottomsheet is | ||||
|               not expanded. So if the back button is pressed, then go back to the Contributions tab */ | ||||
|             if(!nearbyParentFragment.backButtonClicked()){ | ||||
|                 getSupportFragmentManager().beginTransaction().remove(nearbyParentFragment).commit(); | ||||
|             if (!nearbyParentFragment.backButtonClicked()) { | ||||
|                 getSupportFragmentManager().beginTransaction().remove(nearbyParentFragment) | ||||
|                     .commit(); | ||||
|                 setSelectedItemId(NavTab.CONTRIBUTIONS.code()); | ||||
|             } | ||||
|         } else if (exploreFragment != null && activeFragment == ActiveFragment.EXPLORE) { | ||||
|  | @ -395,12 +401,16 @@ public class MainActivity  extends BaseActivity | |||
|             getContribution(Collections.singletonList(Contribution.STATE_FAILED)) | ||||
|             .subscribeOn(Schedulers.io()) | ||||
|             .subscribe(failedUploads -> { | ||||
|                 for (Contribution contribution: failedUploads) { | ||||
|                 for (Contribution contribution : failedUploads) { | ||||
|                     contributionsFragment.retryUpload(contribution); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Handles item selection in the options menu. This method is called when a user interacts with | ||||
|      * the options menu in the Top Bar. | ||||
|      */ | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         switch (item.getItemId()) { | ||||
|  | @ -418,17 +428,18 @@ public class MainActivity  extends BaseActivity | |||
| 
 | ||||
|     public void centerMapToPlace(Place place) { | ||||
|         setSelectedItemId(NavTab.NEARBY.code()); | ||||
|         nearbyParentFragment.setNearbyParentFragmentInstanceReadyCallback(new NearbyParentFragmentInstanceReadyCallback() { | ||||
|             @Override | ||||
|             public void onReady() { | ||||
|                 nearbyParentFragment.centerMapToPlace(place); | ||||
|             } | ||||
|         }); | ||||
|         nearbyParentFragment.setNearbyParentFragmentInstanceReadyCallback( | ||||
|             new NearbyParentFragmentInstanceReadyCallback() { | ||||
|                 @Override | ||||
|                 public void onReady() { | ||||
|                     nearbyParentFragment.centerMapToPlace(place); | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onActivityResult(int requestCode, int resultCode, Intent data) { | ||||
|         Timber.d(data!=null?data.toString():"onActivityResult data is null"); | ||||
|         Timber.d(data != null ? data.toString() : "onActivityResult data is null"); | ||||
|         super.onActivityResult(requestCode, resultCode, data); | ||||
|         controller.handleActivityResult(this, requestCode, resultCode, data); | ||||
|     } | ||||
|  | @ -473,14 +484,15 @@ public class MainActivity  extends BaseActivity | |||
|     /** | ||||
|      * Load default language in onCreate from SharedPreferences | ||||
|      */ | ||||
|     private void loadLocale(){ | ||||
|         final SharedPreferences preferences = getSharedPreferences("Settings", Activity.MODE_PRIVATE); | ||||
|     private void loadLocale() { | ||||
|         final SharedPreferences preferences = getSharedPreferences("Settings", | ||||
|             Activity.MODE_PRIVATE); | ||||
|         final String language = preferences.getString("language", ""); | ||||
|         final SettingsFragment settingsFragment = new SettingsFragment(); | ||||
|         settingsFragment.setLocale(this, language); | ||||
|     } | ||||
| 
 | ||||
|     public NavTabLayout.OnNavigationItemSelectedListener getNavListener(){ | ||||
|     public NavTabLayout.OnNavigationItemSelectedListener getNavListener() { | ||||
|         return navListener; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -22,12 +22,19 @@ class FailedUploadsAdapter(callback: Callback) : | |||
|     PagedListAdapter<Contribution, FailedUploadsAdapter.ViewHolder>(ContributionDiffCallback()) { | ||||
|     private var callback: Callback = callback | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a new ViewHolder instance. Inflates the layout for each item in the list. | ||||
|      */ | ||||
|     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { | ||||
|         val view: View = | ||||
|             LayoutInflater.from(parent.context).inflate(R.layout.item_failed_upload, parent, false) | ||||
|         return ViewHolder(view) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Binds data to the provided ViewHolder. Sets up the item view with data from the | ||||
|      * contribution at the specified position. | ||||
|      */ | ||||
|     override fun onBindViewHolder(holder: ViewHolder, position: Int) { | ||||
|         val item: Contribution? = getItem(position) | ||||
|         if (item != null) { | ||||
|  | @ -69,6 +76,9 @@ class FailedUploadsAdapter(callback: Callback) : | |||
|         holder.itemImage.setImageRequest(imageRequest) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * ViewHolder for the failed upload item. Holds references to the views for each item. | ||||
|      */ | ||||
|     class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { | ||||
|         var itemImage: com.facebook.drawee.view.SimpleDraweeView = | ||||
|             itemView.findViewById(R.id.itemImage) | ||||
|  | @ -79,10 +89,18 @@ class FailedUploadsAdapter(callback: Callback) : | |||
|         var retryButton: ImageView = itemView.findViewById(R.id.retryButton) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the ID of the item at the specified position. Uses the pageId of the contribution | ||||
|      * for unique identification. | ||||
|      */ | ||||
|     override fun getItemId(position: Int): Long { | ||||
|         return getItem(position)?.pageId?.hashCode()?.toLong() ?: position.toLong() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Uses DiffUtil to calculate the changes in the list | ||||
|      * It has methods that check pageId and the content of the items to determine if its a new item | ||||
|      */ | ||||
|     class ContributionDiffCallback : DiffUtil.ItemCallback<Contribution>() { | ||||
|         override fun areItemsTheSame(oldItem: Contribution, newItem: Contribution): Boolean { | ||||
|             return oldItem.pageId.hashCode() == newItem.pageId.hashCode() | ||||
|  | @ -93,8 +111,22 @@ class FailedUploadsAdapter(callback: Callback) : | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Callback interface for handling actions related to failed uploads. | ||||
|      */ | ||||
|     interface Callback { | ||||
|         /** | ||||
|          * Deletes the failed upload item. | ||||
|          * | ||||
|          * @param contribution to be deleted. | ||||
|          */ | ||||
|         fun deleteUpload(contribution: Contribution?) | ||||
| 
 | ||||
|         /** | ||||
|          * Restarts the upload for the item at the specified index. | ||||
|          * | ||||
|          * @param index The position of the item in the list. | ||||
|          */ | ||||
|         fun restartUpload(index: Int) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -23,16 +23,11 @@ import java.util.Locale | |||
| import javax.inject.Inject | ||||
| 
 | ||||
| /** | ||||
|  * A simple [Fragment] subclass. | ||||
|  * Use the [FailedUploadsFragment.newInstance] factory method to | ||||
|  * create an instance of this fragment. | ||||
|  * Fragment for displaying a list of failed uploads in Upload Progress Activity. This fragment provides | ||||
|  * functionality for the user to retry or cancel failed uploads. | ||||
|  */ | ||||
| class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsContract.View, | ||||
|     FailedUploadsAdapter.Callback { | ||||
|     private var param1: String? = null | ||||
|     private var param2: String? = null | ||||
|     private val ARG_PARAM1 = "param1" | ||||
|     private val ARG_PARAM2 = "param2" | ||||
| 
 | ||||
|     @Inject | ||||
|     lateinit var pendingUploadsPresenter: PendingUploadsPresenter | ||||
|  | @ -62,11 +57,6 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont | |||
| 
 | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         arguments?.let { | ||||
|             param1 = it.getString(ARG_PARAM1) | ||||
|             param2 = it.getString(ARG_PARAM2) | ||||
|         } | ||||
| 
 | ||||
|         //Now that we are allowing this fragment to be started for | ||||
|         // any userName- we expect it to be passed as an argument | ||||
|         if (arguments != null) { | ||||
|  | @ -97,6 +87,9 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont | |||
|         initRecyclerView() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initializes the recycler view. | ||||
|      */ | ||||
|     fun initRecyclerView() { | ||||
|         binding.failedUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context)) | ||||
|         binding.failedUploadsRecyclerView.adapter = adapter | ||||
|  | @ -124,17 +117,9 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         @JvmStatic | ||||
|         fun newInstance(param1: String, param2: String) = | ||||
|             FailedUploadsFragment().apply { | ||||
|                 arguments = Bundle().apply { | ||||
|                     putString(ARG_PARAM1, param1) | ||||
|                     putString(ARG_PARAM2, param2) | ||||
|                 } | ||||
|             } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Restarts all the failed uploads. | ||||
|      */ | ||||
|     fun restartUploads() { | ||||
|         if (contributionsList != null) { | ||||
|             pendingUploadsPresenter.restartUploads( | ||||
|  | @ -145,6 +130,9 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Restarts a specific upload. | ||||
|      */ | ||||
|     override fun restartUpload(index: Int) { | ||||
|         if (contributionsList != null) { | ||||
|             pendingUploadsPresenter.restartUpload( | ||||
|  | @ -155,19 +143,22 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Deletes a specific upload after getting a confirmation from the user using Dialog. | ||||
|      */ | ||||
|     override fun deleteUpload(contribution: Contribution?) { | ||||
|         DialogUtil.showAlertDialog( | ||||
|             requireActivity(), | ||||
|             String.format( | ||||
|                 Locale.getDefault(), | ||||
|                 getString(R.string.cancelling_upload) | ||||
|                 requireActivity().getString(R.string.cancelling_upload) | ||||
|             ), | ||||
|             String.format( | ||||
|                 Locale.getDefault(), | ||||
|                 getString(R.string.cancel_upload_dialog) | ||||
|                 requireActivity().getString(R.string.cancel_upload_dialog) | ||||
|             ), | ||||
|             String.format(Locale.getDefault(), getString(R.string.yes)), | ||||
|             String.format(Locale.getDefault(), getString(R.string.no)), | ||||
|             String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), | ||||
|             String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), | ||||
|             { | ||||
|                 ViewUtil.showShortToast(context, R.string.cancelling_upload) | ||||
|                 pendingUploadsPresenter.deleteUpload( | ||||
|  | @ -179,20 +170,23 @@ class FailedUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCont | |||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Deletes all the uploads after getting a confirmation from the user using Dialog. | ||||
|      */ | ||||
|     fun deleteUploads() { | ||||
|         if (contributionsList != null) { | ||||
|             DialogUtil.showAlertDialog( | ||||
|                 requireActivity(), | ||||
|                 String.format( | ||||
|                     Locale.getDefault(), | ||||
|                     getString(R.string.cancelling_all_the_uploads) | ||||
|                     requireActivity().getString(R.string.cancelling_all_the_uploads) | ||||
|                 ), | ||||
|                 String.format( | ||||
|                     Locale.getDefault(), | ||||
|                     getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads) | ||||
|                     requireActivity().getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads) | ||||
|                 ), | ||||
|                 String.format(Locale.getDefault(), getString(R.string.yes)), | ||||
|                 String.format(Locale.getDefault(), getString(R.string.no)), | ||||
|                 String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), | ||||
|                 String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), | ||||
|                 { | ||||
|                     ViewUtil.showShortToast(context, R.string.cancelling_upload) | ||||
|                     uploadProgressActivity.hidePendingIcons() | ||||
|  |  | |||
|  | @ -1,4 +0,0 @@ | |||
| package fr.free.nrw.commons.upload | ||||
| 
 | ||||
| 
 | ||||
| data class PendingUploadItem(var title: String, var image: String, var queued : Boolean ,var error:String) | ||||
|  | @ -22,12 +22,19 @@ import java.io.File | |||
| class PendingUploadsAdapter(private val callback: Callback) : | ||||
|     PagedListAdapter<Contribution, PendingUploadsAdapter.ViewHolder>(ContributionDiffCallback()) { | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a new ViewHolder instance. Inflates the layout for each item in the list. | ||||
|      */ | ||||
|     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { | ||||
|         val view: View = LayoutInflater.from(parent.context) | ||||
|             .inflate(R.layout.item_pending_upload, parent, false) | ||||
|         return ViewHolder(view) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Binds data to the provided ViewHolder. Sets up the item view with data from the | ||||
|      * contribution at the specified position utilizing payloads. | ||||
|      */ | ||||
|     override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) { | ||||
|         if (payloads.isNotEmpty()) { | ||||
|             when (val latestPayload = payloads.lastOrNull()) { | ||||
|  | @ -36,6 +43,7 @@ class PendingUploadsAdapter(private val callback: Callback) : | |||
|                     latestPayload.total, | ||||
|                     getItem(position)!!.state | ||||
|                 ) | ||||
| 
 | ||||
|                 is ContributionChangePayload.State -> holder.bindState(latestPayload.state) | ||||
|                 else -> onBindViewHolder(holder, position) | ||||
|             } | ||||
|  | @ -44,6 +52,10 @@ class PendingUploadsAdapter(private val callback: Callback) : | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Binds data to the provided ViewHolder. Sets up the item view with data from the | ||||
|      * contribution at the specified position. | ||||
|      */ | ||||
|     override fun onBindViewHolder(holder: ViewHolder, position: Int) { | ||||
|         val contribution = getItem(position) | ||||
|         contribution?.let { | ||||
|  | @ -54,6 +66,9 @@ class PendingUploadsAdapter(private val callback: Callback) : | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * ViewHolder class for holding and binding item views. | ||||
|      */ | ||||
|     class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { | ||||
|         var itemImage: com.facebook.drawee.view.SimpleDraweeView = | ||||
|             itemView.findViewById(R.id.itemImage) | ||||
|  | @ -102,11 +117,11 @@ class PendingUploadsAdapter(private val callback: Callback) : | |||
|                 errorTextView.visibility = View.VISIBLE | ||||
|                 itemProgress.visibility = View.GONE | ||||
|             } else { | ||||
|                 if (state == Contribution.STATE_QUEUED || state == Contribution.STATE_PAUSED){ | ||||
|                 if (state == Contribution.STATE_QUEUED || state == Contribution.STATE_PAUSED) { | ||||
|                     errorTextView.text = "Queued" | ||||
|                     errorTextView.visibility = View.VISIBLE | ||||
|                     itemProgress.visibility = View.GONE | ||||
|                 } else{ | ||||
|                 } else { | ||||
|                     errorTextView.visibility = View.GONE | ||||
|                     itemProgress.visibility = View.VISIBLE | ||||
|                     if (transferred >= total) { | ||||
|  | @ -121,19 +136,49 @@ class PendingUploadsAdapter(private val callback: Callback) : | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Callback interface for handling actions related to failed uploads. | ||||
|      */ | ||||
|     interface Callback { | ||||
|         /** | ||||
|          * Deletes the failed upload item. | ||||
|          * | ||||
|          * @param contribution to be deleted. | ||||
|          */ | ||||
|         fun deleteUpload(contribution: Contribution?) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Uses DiffUtil and payloads to calculate the changes in the list | ||||
|      * It has methods that check pageId and the content of the items to determine if its a new item | ||||
|      */ | ||||
|     class ContributionDiffCallback : DiffUtil.ItemCallback<Contribution>() { | ||||
|         /** | ||||
|          * Checks if two items represent the same contribution. | ||||
|          * @param oldItem The old contribution item. | ||||
|          * @param newItem The new contribution item. | ||||
|          * @return True if the items are the same, false otherwise. | ||||
|          */ | ||||
|         override fun areItemsTheSame(oldItem: Contribution, newItem: Contribution): Boolean { | ||||
|             return oldItem.pageId.hashCode() == newItem.pageId.hashCode() | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Checks if the content of two items is the same. | ||||
|          * @param oldItem The old contribution item. | ||||
|          * @param newItem The new contribution item. | ||||
|          * @return True if the contents are the same, false otherwise. | ||||
|          */ | ||||
|         override fun areContentsTheSame(oldItem: Contribution, newItem: Contribution): Boolean { | ||||
|             return oldItem.transferred == newItem.transferred | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Returns a payload representing the change between the old and new items. | ||||
|          * @param oldItem The old contribution item. | ||||
|          * @param newItem The new contribution item. | ||||
|          * @return An object representing the change, or null if there are no changes. | ||||
|          */ | ||||
|         override fun getChangePayload(oldItem: Contribution, newItem: Contribution): Any? { | ||||
|             return when { | ||||
|                 oldItem.transferred != newItem.transferred -> { | ||||
|  | @ -149,12 +194,30 @@ class PendingUploadsAdapter(private val callback: Callback) : | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the unique item ID for the contribution at the specified position. | ||||
|      * @param position The position of the item. | ||||
|      * @return The unique item ID. | ||||
|      */ | ||||
|     override fun getItemId(position: Int): Long { | ||||
|         return getItem(position)?.pageId?.hashCode()?.toLong() ?: position.toLong() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sealed interface representing different types of changes to a contribution. | ||||
|      */ | ||||
|     private sealed interface ContributionChangePayload { | ||||
|         /** | ||||
|          * Represents a change in the progress of a contribution. | ||||
|          * @param transferred The amount of data transferred. | ||||
|          * @param total The total amount of data. | ||||
|          */ | ||||
|         data class Progress(val transferred: Long, val total: Long) : ContributionChangePayload | ||||
| 
 | ||||
|         /** | ||||
|          * Represents a change in the state of a contribution. | ||||
|          * @param state The state of the contribution. | ||||
|          */ | ||||
|         data class State(val state: Int) : ContributionChangePayload | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -6,12 +6,26 @@ import fr.free.nrw.commons.contributions.Contribution; | |||
| import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract; | ||||
| import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract.View; | ||||
| 
 | ||||
| /** | ||||
|  * The contract using which the PendingUploadsFragment or FailedUploadsFragment would communicate | ||||
|  * with its PendingUploadsPresenter | ||||
|  */ | ||||
| public class PendingUploadsContract { | ||||
| 
 | ||||
|     /** | ||||
|      * Interface representing the view for uploads. | ||||
|      */ | ||||
|     public interface View { } | ||||
| 
 | ||||
|     /** | ||||
|      * Interface representing the user actions related to uploads. | ||||
|      */ | ||||
|     public interface UserActionListener extends | ||||
|         BasePresenter<fr.free.nrw.commons.upload.PendingUploadsContract.View> { | ||||
| 
 | ||||
|         /** | ||||
|          * Deletes a upload. | ||||
|          */ | ||||
|         void deleteUpload(Contribution contribution, Context context); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -27,18 +27,12 @@ import timber.log.Timber | |||
| import java.util.Locale | ||||
| import javax.inject.Inject | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * A simple [Fragment] subclass. | ||||
|  * Use the [PendingUploadsFragment.newInstance] factory method to | ||||
|  * create an instance of this fragment. | ||||
|  * Fragment for showing pending uploads in Upload Progress Activity. This fragment provides | ||||
|  * functionality for the user to pause uploads. | ||||
|  */ | ||||
| class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsContract.View, | ||||
|     PendingUploadsAdapter.Callback { | ||||
|     private var param1: String? = null | ||||
|     private var param2: String? = null | ||||
|     private val ARG_PARAM1 = "param1" | ||||
|     private val ARG_PARAM2 = "param2" | ||||
| 
 | ||||
|     @Inject | ||||
|     lateinit var pendingUploadsPresenter: PendingUploadsPresenter | ||||
|  | @ -52,14 +46,6 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon | |||
|     private var contributionsSize = 0 | ||||
|     var contributionsList = ArrayList<Contribution>() | ||||
| 
 | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         arguments?.let { | ||||
|             param1 = it.getString(ARG_PARAM1) | ||||
|             param2 = it.getString(ARG_PARAM2) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override fun onAttach(context: Context) { | ||||
|         super.onAttach(context) | ||||
|         if (context is UploadProgressActivity) { | ||||
|  | @ -87,6 +73,9 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon | |||
|         initRecyclerView() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initializes the recycler view. | ||||
|      */ | ||||
|     fun initRecyclerView() { | ||||
|         binding.pendingUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context)) | ||||
|         binding.pendingUploadsRecyclerView.adapter = adapter | ||||
|  | @ -130,19 +119,22 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Cancels a specific upload after getting a confirmation from the user using Dialog. | ||||
|      */ | ||||
|     override fun deleteUpload(contribution: Contribution?) { | ||||
|         showAlertDialog( | ||||
|             requireActivity(), | ||||
|             String.format( | ||||
|                 Locale.getDefault(), | ||||
|                 getString(R.string.cancelling_upload) | ||||
|                 requireActivity().getString(R.string.cancelling_upload) | ||||
|             ), | ||||
|             String.format( | ||||
|                 Locale.getDefault(), | ||||
|                 getString(R.string.cancel_upload_dialog) | ||||
|                 requireActivity().getString(R.string.cancel_upload_dialog) | ||||
|             ), | ||||
|             String.format(Locale.getDefault(), getString(R.string.yes)), | ||||
|             String.format(Locale.getDefault(), getString(R.string.no)), | ||||
|             String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), | ||||
|             String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), | ||||
|             { | ||||
|                 ViewUtil.showShortToast(context, R.string.cancelling_upload) | ||||
|                 pendingUploadsPresenter.deleteUpload( | ||||
|  | @ -154,17 +146,9 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon | |||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         @JvmStatic | ||||
|         fun newInstance(param1: String, param2: String) = | ||||
|             PendingUploadsFragment().apply { | ||||
|                 arguments = Bundle().apply { | ||||
|                     putString(ARG_PARAM1, param1) | ||||
|                     putString(ARG_PARAM2, param2) | ||||
|                 } | ||||
|             } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Restarts all the paused uploads. | ||||
|      */ | ||||
|     fun restartUploads() { | ||||
|         if (contributionsList != null) { | ||||
|             pendingUploadsPresenter.restartUploads( | ||||
|  | @ -175,27 +159,29 @@ class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsCon | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Pauses all the ongoing uploads. | ||||
|      */ | ||||
|     fun pauseUploads() { | ||||
|         pendingUploadsPresenter.pauseUploads( | ||||
|             listOf(Contribution.STATE_QUEUED, Contribution.STATE_IN_PROGRESS), | ||||
|             Contribution.STATE_PAUSED | ||||
|         ) | ||||
| 
 | ||||
|         pendingUploadsPresenter.pauseUploads() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Cancels all the uploads after getting a confirmation from the user using Dialog. | ||||
|      */ | ||||
|     fun deleteUploads() { | ||||
|         showAlertDialog( | ||||
|             requireActivity(), | ||||
|             String.format( | ||||
|                 Locale.getDefault(), | ||||
|                 getString(R.string.cancelling_all_the_uploads) | ||||
|                 requireActivity().getString(R.string.cancelling_all_the_uploads) | ||||
|             ), | ||||
|             String.format( | ||||
|                 Locale.getDefault(), | ||||
|                 getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads) | ||||
|                 requireActivity().getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads) | ||||
|             ), | ||||
|             String.format(Locale.getDefault(), getString(R.string.yes)), | ||||
|             String.format(Locale.getDefault(), getString(R.string.no)), | ||||
|             String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), | ||||
|             String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), | ||||
|             { | ||||
|                 ViewUtil.showShortToast(context, R.string.cancelling_upload) | ||||
|                 uploadProgressActivity.hidePendingIcons() | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ import fr.free.nrw.commons.upload.PendingUploadsContract.View; | |||
| import fr.free.nrw.commons.upload.worker.WorkRequestHelper; | ||||
| import io.reactivex.Scheduler; | ||||
| import io.reactivex.disposables.CompositeDisposable; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Calendar; | ||||
| import java.util.Collections; | ||||
|  | @ -31,7 +32,7 @@ import javax.inject.Named; | |||
| import timber.log.Timber; | ||||
| 
 | ||||
| /** | ||||
|  * The presenter class for Contributions | ||||
|  * The presenter class for PendingUploadsFragment and FailedUploadsFragment | ||||
|  */ | ||||
| public class PendingUploadsPresenter implements UserActionListener { | ||||
| 
 | ||||
|  | @ -61,11 +62,10 @@ public class PendingUploadsPresenter implements UserActionListener { | |||
|         compositeDisposable = new CompositeDisposable(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Setup the paged list. This method sets the configuration for paged list and ties it up with | ||||
|      * the live data object. This method can be tweaked to update the lazy loading behavior of the | ||||
|      * contributions list | ||||
|      * Setups the paged list of Pending Uploads. This method sets the configuration for paged list | ||||
|      * and ties it up with the live data object. This method can be tweaked to update the lazy | ||||
|      * loading behavior of the contributions list | ||||
|      */ | ||||
|     void setup() { | ||||
|         final PagedList.Config pagedListConfig = | ||||
|  | @ -82,6 +82,11 @@ public class PendingUploadsPresenter implements UserActionListener { | |||
|         totalContributionList = livePagedListBuilder.build(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Setups the paged list of Failed Uploads. This method sets the configuration for paged list | ||||
|      * and ties it up with the live data object. This method can be tweaked to update the lazy | ||||
|      * loading behavior of the contributions list | ||||
|      */ | ||||
|     void getFailedContributions() { | ||||
|         final PagedList.Config pagedListConfig = | ||||
|             (new PagedList.Config.Builder()) | ||||
|  | @ -107,6 +112,12 @@ public class PendingUploadsPresenter implements UserActionListener { | |||
|         contributionBoundaryCallback.dispose(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Deletes the specified upload (contribution) from the database. | ||||
|      * | ||||
|      * @param contribution The contribution object representing the upload to be deleted. | ||||
|      * @param context      The context in which the operation is being performed. | ||||
|      */ | ||||
|     @Override | ||||
|     public void deleteUpload(final Contribution contribution, Context context) { | ||||
|         compositeDisposable.add(contributionsRepository | ||||
|  | @ -115,14 +126,25 @@ public class PendingUploadsPresenter implements UserActionListener { | |||
|             .subscribe()); | ||||
|     } | ||||
| 
 | ||||
|     public void pauseUploads(List<Integer> states, int newState) { | ||||
|         CommonsApplication.isPaused = true ; | ||||
|     /** | ||||
|      * Pauses all the uploads by changing the state of contributions from STATE_QUEUED and | ||||
|      * STATE_IN_PROGRESS to STATE_PAUSED in the database. | ||||
|      */ | ||||
|     public void pauseUploads() { | ||||
|         CommonsApplication.isPaused = true; | ||||
|         compositeDisposable.add(contributionsRepository | ||||
|             .updateContributionWithStates(states, newState) | ||||
|             .updateContributionsWithStates( | ||||
|                 List.of(Contribution.STATE_QUEUED, Contribution.STATE_IN_PROGRESS), | ||||
|                 Contribution.STATE_PAUSED) | ||||
|             .subscribeOn(ioThreadScheduler) | ||||
|             .subscribe()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Deletes contributions from the database that match the specified states. | ||||
|      * | ||||
|      * @param states A list of integers representing the states of the contributions to be deleted. | ||||
|      */ | ||||
|     public void deleteUploads(List<Integer> states) { | ||||
|         compositeDisposable.add(contributionsRepository | ||||
|             .deleteContributionsFromDBWithStates(states) | ||||
|  | @ -130,6 +152,13 @@ public class PendingUploadsPresenter implements UserActionListener { | |||
|             .subscribe()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Restarts the uploads for the specified list of contributions starting from the given index. | ||||
|      * | ||||
|      * @param contributionList The list of contributions to be restarted. | ||||
|      * @param index            The starting index in the list from which to restart uploads. | ||||
|      * @param context          The context in which the operation is being performed. | ||||
|      */ | ||||
|     public void restartUploads(List<Contribution> contributionList, int index, Context context) { | ||||
|         CommonsApplication.isPaused = false; | ||||
|         if (index >= contributionList.size()) { | ||||
|  | @ -182,6 +211,13 @@ public class PendingUploadsPresenter implements UserActionListener { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Restarts the upload for the specified list of contributions for the given index. | ||||
|      * | ||||
|      * @param contributionList The list of contributions. | ||||
|      * @param index            The index in the list which to be restarted. | ||||
|      * @param context          The context in which the operation is being performed. | ||||
|      */ | ||||
|     public void restartUpload(List<Contribution> contributionList, int index, Context context) { | ||||
|         CommonsApplication.isPaused = false; | ||||
|         if (index >= contributionList.size()) { | ||||
|  | @ -190,7 +226,7 @@ public class PendingUploadsPresenter implements UserActionListener { | |||
|         Contribution it = contributionList.get(index); | ||||
|         if (it.getState() == Contribution.STATE_FAILED) { | ||||
|             it.setDateUploadStarted(Calendar.getInstance().getTime()); | ||||
|             if (it.getErrorInfo() == null){ | ||||
|             if (it.getErrorInfo() == null) { | ||||
|                 it.setChunkInfo(null); | ||||
|                 it.setTransferred(0); | ||||
|             } | ||||
|  | @ -205,7 +241,7 @@ public class PendingUploadsPresenter implements UserActionListener { | |||
|                             .subscribeOn(ioThreadScheduler) | ||||
|                             .subscribe(() -> WorkRequestHelper.Companion.makeOneTimeWorkRequest( | ||||
|                                 context, ExistingWorkPolicy.KEEP))); | ||||
|                     }else { | ||||
|                     } else { | ||||
|                         Timber.e("Contribution already exists"); | ||||
|                         compositeDisposable.add(contributionsRepository | ||||
|                             .deleteContributionFromDB(it) | ||||
|  |  | |||
|  | @ -17,7 +17,12 @@ import io.reactivex.schedulers.Schedulers | |||
| import timber.log.Timber | ||||
| import javax.inject.Inject | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Activity to manage the progress of uploads. It includes tabs to show pending and failed uploads, | ||||
|  * and provides menu options to pause, resume, cancel, and retry uploads. Also, it contains ViewPager | ||||
|  * which holds Pending Uploads Fragment and Failed Uploads Fragment to show list of pending and | ||||
|  * failed uploads respectively. | ||||
|  */ | ||||
| class UploadProgressActivity : BaseActivity() { | ||||
| 
 | ||||
|     private lateinit var binding: ActivityUploadProgressBinding | ||||
|  | @ -70,6 +75,11 @@ class UploadProgressActivity : BaseActivity() { | |||
|         setTabs() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initializes and sets up the tabs data by creating instances of `PendingUploadsFragment` | ||||
|      * and `FailedUploadsFragment`, adds them to the `fragmentList`, and assigns corresponding | ||||
|      * titles from resources to the `titleList`. | ||||
|      */ | ||||
|     fun setTabs() { | ||||
|         pendingUploadsFragment = PendingUploadsFragment() | ||||
|         failedUploadsFragment = FailedUploadsFragment() | ||||
|  | @ -83,7 +93,7 @@ class UploadProgressActivity : BaseActivity() { | |||
|     } | ||||
| 
 | ||||
|     override fun onCreateOptionsMenu(menu: Menu?): Boolean { | ||||
|         menuInflater.inflate(R.menu.menu_uploads,menu) | ||||
|         menuInflater.inflate(R.menu.menu_uploads, menu) | ||||
|         this.menu = menu | ||||
|         updateMenuItems(0) | ||||
|         return super.onCreateOptionsMenu(menu) | ||||
|  | @ -176,17 +186,28 @@ class UploadProgressActivity : BaseActivity() { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Hides the menu icons related to pending uploads. | ||||
|      */ | ||||
|     fun hidePendingIcons() { | ||||
|         isPendingIconsVisible = false | ||||
|         updateMenuItems(binding.uploadProgressViewPager.currentItem) | ||||
|     } | ||||
| 
 | ||||
|     fun setPausedIcon(paused : Boolean){ | ||||
|     /** | ||||
|      * Sets the paused state and updates the menu items accordingly. | ||||
|      * @param paused A boolean indicating whether all the uploads are paused. | ||||
|      */ | ||||
|     fun setPausedIcon(paused: Boolean) { | ||||
|         isPaused = paused | ||||
|         updateMenuItems(binding.uploadProgressViewPager.currentItem) | ||||
|     } | ||||
| 
 | ||||
|     fun setErrorIconsVisibility(visible : Boolean){ | ||||
|     /** | ||||
|      * Sets the visibility of the menu icons related to failed uploads. | ||||
|      * @param visible A boolean indicating whether the error icons should be visible. | ||||
|      */ | ||||
|     fun setErrorIconsVisibility(visible: Boolean) { | ||||
|         isErrorIconsVisisble = visible | ||||
|         updateMenuItems(binding.uploadProgressViewPager.currentItem) | ||||
|     } | ||||
|  |  | |||
|  | @ -8,8 +8,6 @@ import android.content.Context | |||
| import android.content.Intent | ||||
| import android.graphics.BitmapFactory | ||||
| import android.os.Build | ||||
| import android.os.Build.VERSION | ||||
| import android.os.Build.VERSION_CODES | ||||
| import androidx.core.app.NotificationCompat | ||||
| import androidx.core.app.NotificationManagerCompat | ||||
| import androidx.multidex.BuildConfig | ||||
|  | @ -41,9 +39,6 @@ import fr.free.nrw.commons.upload.UploadResult | |||
| import fr.free.nrw.commons.wikidata.WikidataEditService | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.MainScope | ||||
| import kotlinx.coroutines.flow.asFlow | ||||
| import kotlinx.coroutines.flow.collect | ||||
| import kotlinx.coroutines.flow.map | ||||
| import kotlinx.coroutines.launch | ||||
| import kotlinx.coroutines.withContext | ||||
| import timber.log.Timber | ||||
|  | @ -169,7 +164,7 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : | |||
| 
 | ||||
|     override suspend fun doWork(): Result { | ||||
|         try { | ||||
|             var countUpload = 0 | ||||
|             var totalUploadsStarted = 0 | ||||
|             // Start a foreground service | ||||
|             setForeground(createForegroundInfo()) | ||||
|             notificationManager = NotificationManagerCompat.from(appContext) | ||||
|  | @ -217,8 +212,8 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : | |||
|                         contribution.transferred = 0 | ||||
|                         contribution.state = Contribution.STATE_IN_PROGRESS | ||||
|                         contributionDao.saveSynchronous(contribution) | ||||
|                         setProgressAsync(Data.Builder().putInt("progress", countUpload).build()) | ||||
|                         countUpload++ | ||||
|                         setProgressAsync(Data.Builder().putInt("progress", totalUploadsStarted).build()) | ||||
|                         totalUploadsStarted++ | ||||
|                         uploadContribution(contribution = contribution) | ||||
|                     } | ||||
|                 } | ||||
|  | @ -576,6 +571,9 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) : | |||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Shows a notification for a failed contribution upload. | ||||
|      */ | ||||
|     @SuppressLint("StringFormatInvalid") | ||||
|     private fun showErrorNotification(contribution: Contribution) { | ||||
|         val displayTitle = contribution.media.displayTitle | ||||
|  |  | |||
|  | @ -49,6 +49,9 @@ class WorkRequestHelper { | |||
|             isUploadWorkerRunning = true | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Sets the flag isUploadWorkerRunning to`false` allowing new worker to be started. | ||||
|          */ | ||||
|         fun markUploadWorkerAsStopped() { | ||||
|             isUploadWorkerRunning = false | ||||
|         } | ||||
|  |  | |||
|  | @ -818,8 +818,6 @@ Upload your first media by tapping on the add button.</string> | |||
|   </plurals> | ||||
|   <string name="multiple_files_depiction">Please remember that all images in a multi-upload get the same categories and depictions. If the images do not share depictions and categories, please perform several separate uploads.</string> | ||||
|   <string name="multiple_files_depiction_header">Note about multi-uploads</string> | ||||
|   <!-- TODO: Remove or change this placeholder text --> | ||||
|   <string name="hello_blank_fragment">Hello blank fragment</string> | ||||
|   <string name="nearby_wikitalk">Report a problem about this item to Wikidata</string> | ||||
|   <string name="please_enter_some_comments">Please enter some comments</string> | ||||
|   <string name="talk">Talk</string> | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import androidx.loader.content.CursorLoader | |||
| import androidx.loader.content.Loader | ||||
| import com.nhaarman.mockitokotlin2.verify | ||||
| import com.nhaarman.mockitokotlin2.whenever | ||||
| import fr.free.nrw.commons.repository.UploadRepository | ||||
| import io.reactivex.Completable | ||||
| import io.reactivex.schedulers.TestScheduler | ||||
| import org.junit.Before | ||||
|  | @ -24,6 +25,10 @@ import org.mockito.MockitoAnnotations | |||
| class ContributionsPresenterTest { | ||||
|     @Mock | ||||
|     internal lateinit var repository: ContributionsRepository | ||||
| 
 | ||||
|     @Mock | ||||
|     internal lateinit var uploadRepository: UploadRepository | ||||
| 
 | ||||
|     @Mock | ||||
|     internal lateinit var view: ContributionsContract.View | ||||
| 
 | ||||
|  | @ -37,9 +42,11 @@ class ContributionsPresenterTest { | |||
| 
 | ||||
|     lateinit var liveData: LiveData<List<Contribution>> | ||||
| 
 | ||||
|     @Rule @JvmField var instantTaskExecutorRule = InstantTaskExecutorRule() | ||||
|     @Rule | ||||
|     @JvmField | ||||
|     var instantTaskExecutorRule = InstantTaskExecutorRule() | ||||
| 
 | ||||
|     lateinit var scheduler : TestScheduler | ||||
|     lateinit var scheduler: TestScheduler | ||||
| 
 | ||||
|     /** | ||||
|      * initial setup | ||||
|  | @ -48,24 +55,23 @@ class ContributionsPresenterTest { | |||
|     @Throws(Exception::class) | ||||
|     fun setUp() { | ||||
|         MockitoAnnotations.initMocks(this) | ||||
|         scheduler=TestScheduler() | ||||
|         scheduler = TestScheduler() | ||||
|         cursor = Mockito.mock(Cursor::class.java) | ||||
|         contribution = Mockito.mock(Contribution::class.java) | ||||
|         contributionsPresenter = ContributionsPresenter(repository, scheduler) | ||||
|         contributionsPresenter = ContributionsPresenter(repository, uploadRepository, scheduler) | ||||
|         loader = Mockito.mock(CursorLoader::class.java) | ||||
|         contributionsPresenter.onAttachView(view) | ||||
|         liveData=MutableLiveData() | ||||
|         liveData = MutableLiveData() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Test fetch contribution with filename | ||||
|      */ | ||||
|     @Test | ||||
|     fun testGetContributionWithFileName(){ | ||||
|     fun testGetContributionWithFileName() { | ||||
|         contributionsPresenter.getContributionsWithTitle("ashish") | ||||
|         verify(repository).getContributionWithFileName("ashish") | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | @ -17,6 +17,7 @@ import fr.free.nrw.commons.TestCommonsApplication | |||
| import fr.free.nrw.commons.auth.csrf.CsrfTokenClient | ||||
| import fr.free.nrw.commons.contributions.ChunkInfo | ||||
| import fr.free.nrw.commons.contributions.Contribution | ||||
| import fr.free.nrw.commons.contributions.ContributionDao | ||||
| import fr.free.nrw.commons.upload.UploadClient.TimeProvider | ||||
| import fr.free.nrw.commons.upload.worker.UploadWorker | ||||
| import fr.free.nrw.commons.wikidata.mwapi.MwException | ||||
|  | @ -44,8 +45,17 @@ class UploadClientTest { | |||
|     private val pageContentsCreator = mock<PageContentsCreator>() | ||||
|     private val fileUtilsWrapper = mock<FileUtilsWrapper>() | ||||
|     private val gson = mock<Gson>() | ||||
|     private val contributionDao = mock<ContributionDao> { } | ||||
|     private val timeProvider = mock<TimeProvider>() | ||||
|     private val uploadClient = UploadClient(uploadInterface, csrfTokenClient, pageContentsCreator, fileUtilsWrapper, gson, timeProvider) | ||||
|     private val uploadClient = UploadClient( | ||||
|         uploadInterface, | ||||
|         csrfTokenClient, | ||||
|         pageContentsCreator, | ||||
|         fileUtilsWrapper, | ||||
|         gson, | ||||
|         timeProvider, | ||||
|         contributionDao | ||||
|     ) | ||||
| 
 | ||||
|     private val expectedChunkSize = 512 * 1024 | ||||
|     private val testToken = "test-token" | ||||
|  | @ -67,7 +77,15 @@ class UploadClientTest { | |||
|     @Test | ||||
|     fun testUploadFileFromStash_NoErrors() { | ||||
|         whenever(gson.fromJson(uploadJson, UploadResponse::class.java)).thenReturn(uploadResponse) | ||||
|         whenever(uploadInterface.uploadFileFromStash(testToken, createdContent, DEFAULT_EDIT_SUMMARY, filename, filekey)).thenReturn(Observable.just(uploadJson)) | ||||
|         whenever( | ||||
|             uploadInterface.uploadFileFromStash( | ||||
|                 testToken, | ||||
|                 createdContent, | ||||
|                 DEFAULT_EDIT_SUMMARY, | ||||
|                 filename, | ||||
|                 filekey | ||||
|             ) | ||||
|         ).thenReturn(Observable.just(uploadJson)) | ||||
| 
 | ||||
|         val result = uploadClient.uploadFileFromStash(contribution, filename, filekey).test() | ||||
| 
 | ||||
|  | @ -83,7 +101,15 @@ class UploadClientTest { | |||
| 
 | ||||
|         whenever(gson.fromJson(uploadJson, UploadResponse::class.java)).thenReturn(errorResponse) | ||||
|         whenever(gson.fromJson(uploadJson, MwException::class.java)).thenReturn(uploadException) | ||||
|         whenever(uploadInterface.uploadFileFromStash(testToken, createdContent, DEFAULT_EDIT_SUMMARY, filename, filekey)).thenReturn(Observable.just(uploadJson)) | ||||
|         whenever( | ||||
|             uploadInterface.uploadFileFromStash( | ||||
|                 testToken, | ||||
|                 createdContent, | ||||
|                 DEFAULT_EDIT_SUMMARY, | ||||
|                 filename, | ||||
|                 filekey | ||||
|             ) | ||||
|         ).thenReturn(Observable.just(uploadJson)) | ||||
| 
 | ||||
|         val result = uploadClient.uploadFileFromStash(contribution, filename, filekey).test() | ||||
| 
 | ||||
|  | @ -94,7 +120,15 @@ class UploadClientTest { | |||
|     @Test | ||||
|     fun testUploadFileFromStash_Failure() { | ||||
|         val exception = Exception("test") | ||||
|         whenever(uploadInterface.uploadFileFromStash(testToken, createdContent, DEFAULT_EDIT_SUMMARY, filename, filekey)) | ||||
|         whenever( | ||||
|             uploadInterface.uploadFileFromStash( | ||||
|                 testToken, | ||||
|                 createdContent, | ||||
|                 DEFAULT_EDIT_SUMMARY, | ||||
|                 filename, | ||||
|                 filekey | ||||
|             ) | ||||
|         ) | ||||
|             .thenReturn(Observable.error(exception)) | ||||
| 
 | ||||
|         val result = uploadClient.uploadFileFromStash(contribution, filename, filekey).test() | ||||
|  | @ -107,7 +141,8 @@ class UploadClientTest { | |||
|     fun testUploadChunkToStash_Success() { | ||||
|         val fileContent = "content" | ||||
|         val requestBody: RequestBody = fileContent.toRequestBody("text/plain".toMediaType()) | ||||
|         val countingRequestBody = CountingRequestBody(requestBody, mock(), 0, fileContent.length.toLong()) | ||||
|         val countingRequestBody = | ||||
|             CountingRequestBody(requestBody, mock(), 0, fileContent.length.toLong()) | ||||
| 
 | ||||
|         val filenameCaptor: KArgumentCaptor<RequestBody> = argumentCaptor<RequestBody>() | ||||
|         val totalFileSizeCaptor = argumentCaptor<RequestBody>() | ||||
|  | @ -116,12 +151,15 @@ class UploadClientTest { | |||
|         val tokenCaptor = argumentCaptor<RequestBody>() | ||||
|         val fileCaptor = argumentCaptor<MultipartBody.Part>() | ||||
| 
 | ||||
|         whenever(uploadInterface.uploadFileToStash( | ||||
|             filenameCaptor.capture(), totalFileSizeCaptor.capture(), offsetCaptor.capture(), | ||||
|             fileKeyCaptor.capture(), tokenCaptor.capture(), fileCaptor.capture() | ||||
|         )).thenReturn(Observable.just(uploadResponse)) | ||||
|         whenever( | ||||
|             uploadInterface.uploadFileToStash( | ||||
|                 filenameCaptor.capture(), totalFileSizeCaptor.capture(), offsetCaptor.capture(), | ||||
|                 fileKeyCaptor.capture(), tokenCaptor.capture(), fileCaptor.capture() | ||||
|             ) | ||||
|         ).thenReturn(Observable.just(uploadResponse)) | ||||
| 
 | ||||
|         val result = uploadClient.uploadChunkToStash(filename, 100, 10, filekey, countingRequestBody).test() | ||||
|         val result = | ||||
|             uploadClient.uploadChunkToStash(filename, 100, 10, filekey, countingRequestBody).test() | ||||
| 
 | ||||
|         result.assertNoErrors() | ||||
|         assertSame(uploadResult, result.values()[0]) | ||||
|  | @ -164,7 +202,12 @@ class UploadClientTest { | |||
|         whenever(contribution.isCompleted()).thenReturn(false) | ||||
|         whenever(contribution.fileKey).thenReturn(filekey) | ||||
|         whenever(fileUtilsWrapper.getMimeType(anyOrNull<File>())).thenReturn("image/png") | ||||
|         whenever(fileUtilsWrapper.getFileChunks(anyOrNull<File>(), eq(expectedChunkSize))).thenReturn(emptyList()) | ||||
|         whenever( | ||||
|             fileUtilsWrapper.getFileChunks( | ||||
|                 anyOrNull<File>(), | ||||
|                 eq(expectedChunkSize) | ||||
|             ) | ||||
|         ).thenReturn(emptyList()) | ||||
|         val result = uploadClient.uploadFileToStash(filename, contribution, mock()).test() | ||||
|         result.assertNoErrors() | ||||
|         verify(contribution, times(1)) | ||||
|  | @ -185,7 +228,12 @@ class UploadClientTest { | |||
|         whenever(contribution.isCompleted()).thenReturn(false) | ||||
|         whenever(contribution.fileKey).thenReturn(filekey) | ||||
|         whenever(fileUtilsWrapper.getMimeType(anyOrNull<File>())).thenReturn("image/png") | ||||
|         whenever(fileUtilsWrapper.getFileChunks(anyOrNull<File>(), eq(expectedChunkSize))).thenReturn(emptyList()) | ||||
|         whenever( | ||||
|             fileUtilsWrapper.getFileChunks( | ||||
|                 anyOrNull<File>(), | ||||
|                 eq(expectedChunkSize) | ||||
|             ) | ||||
|         ).thenReturn(emptyList()) | ||||
| 
 | ||||
|         val result = uploadClient.uploadFileToStash(filename, contribution, mock()).test() | ||||
| 
 | ||||
|  | @ -201,8 +249,22 @@ class UploadClientTest { | |||
|         whenever(contribution.isCompleted()).thenReturn(false) | ||||
|         whenever(contribution.fileKey).thenReturn(filekey) | ||||
|         whenever(fileUtilsWrapper.getMimeType(anyOrNull<File>())).thenReturn("image/png") | ||||
|         whenever(fileUtilsWrapper.getFileChunks(anyOrNull<File>(), eq(expectedChunkSize))).thenReturn(listOf(mockFile)) | ||||
|         whenever(uploadInterface.uploadFileToStash(any(), any(), any(), any(), any(), any())).thenReturn(Observable.just(uploadResponse)) | ||||
|         whenever( | ||||
|             fileUtilsWrapper.getFileChunks( | ||||
|                 anyOrNull<File>(), | ||||
|                 eq(expectedChunkSize) | ||||
|             ) | ||||
|         ).thenReturn(listOf(mockFile)) | ||||
|         whenever( | ||||
|             uploadInterface.uploadFileToStash( | ||||
|                 any(), | ||||
|                 any(), | ||||
|                 any(), | ||||
|                 any(), | ||||
|                 any(), | ||||
|                 any() | ||||
|             ) | ||||
|         ).thenReturn(Observable.just(uploadResponse)) | ||||
| 
 | ||||
|         val result = uploadClient.uploadFileToStash(filename, contribution, mock()).test() | ||||
| 
 | ||||
|  | @ -228,10 +290,19 @@ class UploadClientTest { | |||
|         whenever(contribution.fileKey).thenReturn(filekey) | ||||
| 
 | ||||
|         whenever(fileUtilsWrapper.getMimeType(anyOrNull<File>())).thenReturn("image/png") | ||||
|         whenever(fileUtilsWrapper.getFileChunks(anyOrNull<File>(), eq(expectedChunkSize))).thenReturn(listOf(mockFile)) | ||||
|         whenever( | ||||
|             fileUtilsWrapper.getFileChunks( | ||||
|                 anyOrNull<File>(), | ||||
|                 eq(expectedChunkSize) | ||||
|             ) | ||||
|         ).thenReturn(listOf(mockFile)) | ||||
| 
 | ||||
|         whenever(uploadInterface.uploadFileToStash(anyOrNull(), anyOrNull(), anyOrNull(), | ||||
|             anyOrNull(), anyOrNull(), anyOrNull())).thenReturn(Observable.just(uploadResponse)) | ||||
|         whenever( | ||||
|             uploadInterface.uploadFileToStash( | ||||
|                 anyOrNull(), anyOrNull(), anyOrNull(), | ||||
|                 anyOrNull(), anyOrNull(), anyOrNull() | ||||
|             ) | ||||
|         ).thenReturn(Observable.just(uploadResponse)) | ||||
| 
 | ||||
|         val result = uploadClient.uploadFileToStash(filename, contribution, mock()).test() | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Kanahia
						Kanahia