diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5ba49201a..79640049b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,262 +1,247 @@ - - - - - - - - - - - - - - - - - - + xmlns:tools="http://schemas.android.com/tools"> - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - + + + - + - + + + + + + - + + + + + + + + + - + + - + + + + + + - - - + - - + + + + + - + - - + + + + + + + + + + + + + + + + + + - - - + + + + - + + + - - - - - + + + + + + + + + - + + + + - - - - - - - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java index b4889b6a2..5fb002319 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java @@ -20,6 +20,10 @@ public abstract class ContributionDao { @Query("SELECT * FROM contribution order by media_dateUploaded DESC") abstract DataSource.Factory fetchContributions(); + @Query("SELECT * FROM contribution WHERE state = -1 ORDER BY media_dateUploaded DESC") + abstract DataSource.Factory fetchContributionsWithStateCompleted(); + + @Insert(onConflict = OnConflictStrategy.REPLACE) public abstract void saveSynchronous(Contribution contribution); diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java index 7ea5163bb..568ac9a37 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java @@ -48,11 +48,8 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder { binding = LayoutContributionBinding.bind(parent); - binding.retryButton.setOnClickListener(v -> retryUpload()); - binding.cancelButton.setOnClickListener(v -> deleteUpload()); binding.contributionImage.setOnClickListener(v -> imageClicked()); binding.wikipediaButton.setOnClickListener(v -> wikipediaButtonClicked()); - binding.pauseResumeButton.setOnClickListener(v -> onPauseResumeButtonClicked()); /* Set a dialog indicating that the upload is being paused. This is needed because pausing an upload might take a dozen seconds. */ @@ -79,9 +76,6 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder { binding.contributionImage.getHierarchy().setPlaceholderImage(R.drawable.image_placeholder); binding.contributionImage.getHierarchy().setFailureImage(R.drawable.image_placeholder); - - - final String imageSource = chooseImageSource(contribution.getMedia().getThumbUrl(), contribution.getLocalUri()); @@ -90,79 +84,27 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder { imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageSource)) .setProgressiveRenderingEnabled(true) .build(); - } - else if (URLUtil.isFileUrl(imageSource)){ - imageRequest=ImageRequest.fromUri(Uri.parse(imageSource)); - } - else if(imageSource != null) { + } else if (URLUtil.isFileUrl(imageSource)) { + imageRequest = ImageRequest.fromUri(Uri.parse(imageSource)); + } else if (imageSource != null) { final File file = new File(imageSource); imageRequest = ImageRequest.fromFile(file); } - if(imageRequest != null){ + if (imageRequest != null) { binding.contributionImage.setImageRequest(imageRequest); } } binding.contributionSequenceNumber.setText(String.valueOf(position + 1)); binding.contributionSequenceNumber.setVisibility(View.VISIBLE); - binding.wikipediaButton.setVisibility(View.GONE); - switch (contribution.getState()) { - case Contribution.STATE_COMPLETED: - binding.contributionState.setVisibility(View.GONE); - binding.contributionProgress.setVisibility(View.GONE); - binding.imageOptions.setVisibility(View.GONE); - binding.contributionState.setText(""); - checkIfMediaExistsOnWikipediaPage(contribution); - break; - case Contribution.STATE_QUEUED: - case Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE: - binding.contributionProgress.setVisibility(View.GONE); - binding.contributionState.setVisibility(View.VISIBLE); - binding.contributionState.setText(R.string.contribution_state_queued); - binding.imageOptions.setVisibility(View.GONE); - break; - case Contribution.STATE_IN_PROGRESS: - binding.contributionState.setVisibility(View.GONE); - binding.contributionProgress.setVisibility(View.VISIBLE); - binding.wikipediaButton.setVisibility(View.GONE); - binding.pauseResumeButton.setVisibility(View.VISIBLE); - binding.cancelButton.setVisibility(View.GONE); - binding.retryButton.setVisibility(View.GONE); - binding.imageOptions.setVisibility(View.VISIBLE); - final long total = contribution.getDataLength(); - final long transferred = contribution.getTransferred(); - if (transferred == 0 || transferred >= total) { - binding.contributionProgress.setIndeterminate(true); - } else { - binding.contributionProgress.setIndeterminate(false); - binding.contributionProgress.setProgress((int) (((double) transferred / (double) total) * 100)); - } - break; - case Contribution.STATE_PAUSED: - binding.contributionProgress.setVisibility(View.GONE); - binding.contributionState.setVisibility(View.VISIBLE); - binding.contributionState.setText(R.string.paused); - binding.cancelButton.setVisibility(View.VISIBLE); - binding.retryButton.setVisibility(View.GONE); - binding.pauseResumeButton.setVisibility(View.VISIBLE); - binding.imageOptions.setVisibility(View.VISIBLE); - setResume(); - if(pausingPopUp.isShowing()){ - pausingPopUp.hide(); - } - break; - case Contribution.STATE_FAILED: - binding.contributionState.setVisibility(View.VISIBLE); - binding.contributionState.setText(R.string.contribution_state_failed); - binding.contributionProgress.setVisibility(View.GONE); - binding.cancelButton.setVisibility(View.VISIBLE); - binding.retryButton.setVisibility(View.VISIBLE); - binding.pauseResumeButton.setVisibility(View.GONE); - binding.imageOptions.setVisibility(View.VISIBLE); - break; - } + binding.contributionState.setVisibility(View.GONE); + binding.contributionProgress.setVisibility(View.GONE); + binding.imageOptions.setVisibility(View.GONE); + binding.contributionState.setText(""); + checkIfMediaExistsOnWikipediaPage(contribution); + } /** @@ -196,8 +138,6 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder { if (!mediaExists) { binding.wikipediaButton.setVisibility(View.VISIBLE); isWikipediaButtonDisplayed = true; - binding.cancelButton.setVisibility(View.GONE); - binding.retryButton.setVisibility(View.GONE); binding.imageOptions.setVisibility(View.VISIBLE); } } @@ -217,20 +157,6 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder { null; } - /** - * Retry upload when it is failed - */ - public void retryUpload() { - callback.retryUpload(contribution); - } - - /** - * Delete a failed upload attempt - */ - public void deleteUpload() { - callback.deleteUpload(contribution); - } - public void imageClicked() { callback.openMediaDetail(position, isWikipediaButtonDisplayed); } @@ -239,44 +165,6 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder { callback.addImageToWikipedia(contribution); } - /** - * Triggers a callback for pause/resume - */ - public void onPauseResumeButtonClicked() { - if (binding.pauseResumeButton.getTag().toString().equals("pause")) { - pause(); - } else { - resume(); - } - } - - private void resume() { - callback.resumeUpload(contribution); - setPaused(); - } - - private void pause() { - pausingPopUp.show(); - callback.pauseUpload(contribution); - setResume(); - } - - /** - * Update pause/resume button to show pause state - */ - private void setPaused() { - binding.pauseResumeButton.setImageResource(R.drawable.pause_icon); - binding.pauseResumeButton.setTag(parent.getContext().getString(R.string.pause)); - } - - /** - * Update pause/resume button to show resume state - */ - private void setResume() { - binding.pauseResumeButton.setImageResource(R.drawable.play_icon); - binding.pauseResumeButton.setTag(parent.getContext().getString(R.string.resume)); - } - public ImageRequest getImageRequest() { return imageRequest; } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java index 189b2665f..c995cb582 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java @@ -12,6 +12,7 @@ import android.Manifest; import android.Manifest.permission; import android.annotation.SuppressLint; import android.content.Context; +import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -25,6 +26,7 @@ import android.view.MenuItem.OnMenuItemClickListener; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -44,6 +46,7 @@ import fr.free.nrw.commons.notification.models.Notification; import fr.free.nrw.commons.notification.NotificationController; import fr.free.nrw.commons.profile.ProfileActivity; import fr.free.nrw.commons.theme.BaseActivity; +import fr.free.nrw.commons.upload.UploadProgressActivity; import java.util.Date; import java.util.List; import java.util.Map; @@ -129,6 +132,10 @@ public class ContributionsFragment public TextView notificationCount; + public TextView pendingUploadsCountTextView; + + public TextView uploadsErrorTextView; + private Campaign wlmCampaign; String userName; @@ -212,9 +219,7 @@ public class ContributionsFragment } initFragments(); - if(isUserProfile) { - binding.limitedConnectionEnabledLayout.setVisibility(View.GONE); - }else { + if(!isUserProfile) { upDateUploadCount(); } if (shouldShowMediaDetailsFragment) { @@ -230,7 +235,6 @@ public class ContributionsFragment && sessionManager.getCurrentAccount() != null && !isUserProfile) { setUploadCount(); } - binding.limitedConnectionEnabledLayout.setOnClickListener(toggleDescriptionListener); setHasOptionsMenu(true); return binding.getRoot(); } @@ -258,10 +262,27 @@ public class ContributionsFragment MenuItem notificationsMenuItem = menu.findItem(R.id.notifications); final View notification = notificationsMenuItem.getActionView(); 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); + final ImageView pendingUploadsImageView = uploadMenuItemActionView.findViewById(R.id.pending_uploads_image_view); + + pendingUploadsImageView.setOnClickListener(view -> { + startActivity(new Intent(getContext(), UploadProgressActivity.class)); + }); + + pendingUploadsCountTextView.setOnClickListener(view -> { + startActivity(new Intent(getContext(), UploadProgressActivity.class)); + }); + + uploadsErrorTextView.setOnClickListener(view -> { + startActivity(new Intent(getContext(), UploadProgressActivity.class)); + }); + notification.setOnClickListener(view -> { NotificationActivity.startYourself(getContext(), "unread"); }); - updateLimitedConnectionToggle(menu); } @SuppressLint("CheckResult") @@ -289,29 +310,6 @@ public class ContributionsFragment } } - public void updateLimitedConnectionToggle(Menu menu) { - MenuItem checkable = menu.findItem(R.id.toggle_limited_connection_mode); - boolean isEnabled = store - .getBoolean(CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, false); - - checkable.setChecked(isEnabled); - if (binding!=null) { - binding.limitedConnectionEnabledLayout.setVisibility(isEnabled ? View.VISIBLE : View.GONE); - } - - checkable.setIcon((isEnabled) ? R.drawable.ic_baseline_cloud_off_24:R.drawable.ic_baseline_cloud_queue_24); - checkable.setOnMenuItemClickListener(new OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - ((MainActivity) getActivity()).toggleLimitedConnectionMode(); - boolean isEnabled = store.getBoolean(CommonsApplication.IS_LIMITED_CONNECTION_MODE_ENABLED, false); - binding.limitedConnectionEnabledLayout.setVisibility(isEnabled ? View.VISIBLE : View.GONE); - checkable.setIcon((isEnabled) ? R.drawable.ic_baseline_cloud_off_24:R.drawable.ic_baseline_cloud_queue_24); - return false; - } - }); - } - @Override public void onAttach(Context context) { super.onAttach(context); @@ -747,6 +745,26 @@ public class ContributionsFragment } } + @Override + public void updateUploadsIcon(int pendingCount, int errorCount) { + if (pendingUploadsCountTextView != null){ + if (pendingCount != 0){ + pendingUploadsCountTextView.setVisibility(View.VISIBLE); + pendingUploadsCountTextView.setText(String.valueOf(pendingCount)); + }else { + pendingUploadsCountTextView.setVisibility(View.INVISIBLE); + } + + if (errorCount != 0){ + uploadsErrorTextView.setVisibility(View.VISIBLE); + uploadsErrorTextView.setText(String.valueOf(errorCount)); + }else { + uploadsErrorTextView.setVisibility(View.GONE); + } + + } + } + /** * Replace whatever is in the current contributionsFragmentContainer view with * mediaDetailPagerFragment, and preserve previous state in back stack. Called when user selects @@ -844,21 +862,6 @@ public class ContributionsFragment } } - // click listener to toggle description that means uses can press the limited connection - // banner and description will hide. Tap again to show description. - private View.OnClickListener toggleDescriptionListener = new View.OnClickListener() { - - @Override - public void onClick(View view) { - View view2 = binding.limitedConnectionDescriptionTextView; - if (view2.getVisibility() == View.GONE) { - view2.setVisibility(View.VISIBLE); - } else { - view2.setVisibility(View.GONE); - } - } - }; - /** * When the device rotates, rotate the Nearby banner's compass arrow in tandem. */ diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java index 2dbe8aff0..330d35ff4 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java @@ -19,7 +19,7 @@ import android.view.animation.AnimationUtils; import android.widget.LinearLayout; import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; +import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -35,6 +35,7 @@ import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.contributions.ContributionsListAdapter.Callback; import fr.free.nrw.commons.databinding.FragmentContributionsListBinding; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.media.MediaClient; @@ -49,6 +50,7 @@ import javax.inject.Inject; import javax.inject.Named; import org.apache.commons.lang3.StringUtils; import fr.free.nrw.commons.wikidata.model.WikiSite; +import timber.log.Timber; /** @@ -56,7 +58,7 @@ import fr.free.nrw.commons.wikidata.model.WikiSite; */ public class ContributionsListFragment extends CommonsDaggerSupportFragment implements - ContributionsListContract.View, ContributionsListAdapter.Callback, + ContributionsListContract.View, Callback, WikipediaInstructionsDialogFragment.Callback { private static final String RV_STATE = "rv_scroll_state"; @@ -81,7 +83,8 @@ 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; @@ -99,7 +102,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl private String userName; private ActivityResultLauncher inAppCameraLocationPermissionLauncher = registerForActivityResult( - new ActivityResultContracts.RequestMultiplePermissions(), + new RequestMultiplePermissions(), new ActivityResultCallback>() { @Override public void onActivityResult(Map result) { @@ -214,6 +217,21 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl contributionsListPresenter.setup(userName, Objects.equals(sessionManager.getUserName(), userName)); + contributionsListPresenter.getTotalContribution(userName); + contributionsListPresenter.totalContributionList.observe(getViewLifecycleOwner(), list -> { + uploadErrorCount = 0; + pendingUploadsCount = 0; + for (int i = 0; i< list.size(); i++){ + if (list.get(i).getState() != Contribution.STATE_COMPLETED){ + if (list.get(i).getState() == Contribution.STATE_FAILED){ + uploadErrorCount++; + }else { + pendingUploadsCount++; + } + } + } + callback.updateUploadsIcon(pendingUploadsCount, uploadErrorCount); + }); contributionsListPresenter.contributionList.observe(getViewLifecycleOwner(), list -> { contributionsSize = list.size(); adapter.submitList(list); @@ -544,5 +562,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl // Notify the viewpager that number of items have changed. void viewPagerNotifyDataSetChanged(); + + void updateUploadsIcon(int pendingCount, int errorCount); } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java index 320ba88a2..50b114768 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java @@ -26,6 +26,7 @@ public class ContributionsListPresenter implements UserActionListener { private final ContributionsRemoteDataSource contributionsRemoteDataSource; LiveData> contributionList; + LiveData> totalContributionList; @Inject ContributionsListPresenter( @@ -71,7 +72,7 @@ public class ContributionsListPresenter implements UserActionListener { } else { contributionBoundaryCallback.setUserName(userName); shouldSetBoundaryCallback = true; - factory = repository.fetchContributions(); + factory = repository.fetchCompletedContributions(); } LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, pagedListConfig); @@ -100,4 +101,22 @@ public class ContributionsListPresenter implements UserActionListener { .subscribe()); } + void getTotalContribution(String userName) { + final PagedList.Config pagedListConfig = + (new PagedList.Config.Builder()) + .setPrefetchDistance(50) + .setPageSize(10).build(); + Factory factory; + boolean shouldSetBoundaryCallback; + contributionBoundaryCallback.setUserName(userName); + shouldSetBoundaryCallback = true; + factory = repository.fetchContributions(); + LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, + pagedListConfig); + if (shouldSetBoundaryCallback) { + livePagedListBuilder.setBoundaryCallback(contributionBoundaryCallback); + } + totalContributionList = livePagedListBuilder.build(); + } + } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java index dcfca2519..56a525c50 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java @@ -67,6 +67,10 @@ class ContributionsLocalDataSource { return contributionDao.fetchContributions(); } + public Factory getCompletedContributions() { + return contributionDao.fetchContributionsWithStateCompleted(); + } + public Single> saveContributions(final List contributions) { final List contributionList = new ArrayList<>(); for(final Contribution contribution: contributions) { diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java index 8054cfb4a..63c65084e 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java @@ -49,6 +49,10 @@ public class ContributionsRepository { return localDataSource.getContributions(); } + public Factory fetchCompletedContributions() { + return localDataSource.getCompletedContributions(); + } + public Single> save(List contributions) { return localDataSource.saveContributions(contributions); } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index 63bde1be9..96b97971c 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -41,6 +41,8 @@ import fr.free.nrw.commons.notification.NotificationController; import fr.free.nrw.commons.quiz.QuizChecker; import fr.free.nrw.commons.settings.SettingsFragment; import fr.free.nrw.commons.theme.BaseActivity; +import fr.free.nrw.commons.upload.UploadActivity; +import fr.free.nrw.commons.upload.UploadProgressActivity; import fr.free.nrw.commons.upload.worker.WorkRequestHelper; import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.ViewUtilWrapper; @@ -385,6 +387,9 @@ public class MainActivity extends BaseActivity @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + case R.id.upload_tab: + startActivity(new Intent(this, UploadProgressActivity.class)); + return true; case R.id.notifications: // Starts notification activity on click to notification icon NotificationActivity.startYourself(this, "unread"); diff --git a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java index 6a9277906..bc0f3003e 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java @@ -19,6 +19,7 @@ import fr.free.nrw.commons.profile.ProfileActivity; import fr.free.nrw.commons.review.ReviewActivity; import fr.free.nrw.commons.settings.SettingsActivity; import fr.free.nrw.commons.upload.UploadActivity; +import fr.free.nrw.commons.upload.UploadProgressActivity; /** * This Class handles the dependency injection (using dagger) @@ -79,4 +80,7 @@ public abstract class ActivityBuilderModule { @ContributesAndroidInjector abstract ZoomableActivity bindZoomableActivity(); + + @ContributesAndroidInjector + abstract UploadProgressActivity bindUploadProgressActivity(); } diff --git a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java index 5c2b1af4d..671ee3078 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java @@ -34,6 +34,7 @@ import fr.free.nrw.commons.profile.achievements.AchievementsFragment; import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment; import fr.free.nrw.commons.review.ReviewImageFragment; import fr.free.nrw.commons.settings.SettingsFragment; +import fr.free.nrw.commons.upload.PendingUploadsFragment; import fr.free.nrw.commons.upload.categories.UploadCategoriesFragment; import fr.free.nrw.commons.upload.depicts.DepictsFragment; import fr.free.nrw.commons.upload.license.MediaLicenseFragment; @@ -155,4 +156,7 @@ public abstract class FragmentBuilderModule { @ContributesAndroidInjector abstract LeaderboardFragment bindLeaderboardFragment(); + + @ContributesAndroidInjector + abstract PendingUploadsFragment bindPendingUploadsFragment(); } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadItem.kt b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadItem.kt new file mode 100644 index 000000000..01d73e90f --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadItem.kt @@ -0,0 +1,4 @@ +package fr.free.nrw.commons.upload + + +data class PendingUploadItem(var title: String, var image: String, var queued : Boolean ,var error:String) \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsAdapter.kt new file mode 100644 index 000000000..a63dceaff --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsAdapter.kt @@ -0,0 +1,94 @@ +package fr.free.nrw.commons.upload + +import android.net.Uri +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.webkit.URLUtil +import android.widget.ImageView +import android.widget.ProgressBar +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.facebook.imagepipeline.request.ImageRequest +import fr.free.nrw.commons.R +import fr.free.nrw.commons.contributions.Contribution +import timber.log.Timber +import java.io.File + + +class PendingUploadsAdapter(items: List, callback: Callback) : + RecyclerView.Adapter() { + private val items: List = items + private var callback:Callback = callback + + 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) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item: Contribution = items[position] + holder.titleTextView.setText(item.media.displayTitle) + var imageRequest: ImageRequest? = null + + val imageSource: String = item.localUri.toString() + Timber.tag("PRINT").e("--"+imageSource) + + if (!TextUtils.isEmpty(imageSource)) { + if (URLUtil.isFileUrl(imageSource)) { + imageRequest = ImageRequest.fromUri(Uri.parse(imageSource))!! + } else if (imageSource != null) { + val file = File(imageSource) + imageRequest = ImageRequest.fromFile(file)!! + } + + if (imageRequest != null) { + holder.itemImage.setImageRequest(imageRequest) + } + } + + if (item.state == Contribution.STATE_QUEUED || item.state == Contribution.STATE_PAUSED) { + holder.errorTextView.setText("Queued") + holder.errorTextView.visibility = View.VISIBLE + holder.itemProgress.visibility = View.GONE + } else { + holder.errorTextView.visibility = View.GONE + holder.itemProgress.visibility = View.VISIBLE + val total: Long = item.dataLength + val transferred: Long = item.transferred + if (transferred == 0L || transferred >= total) { + holder.itemProgress.setIndeterminate(true) + } else { + holder.itemProgress.setIndeterminate(false) + holder.itemProgress.setProgress(((transferred.toDouble() / total.toDouble()) * 100).toInt()) + } + } + + holder.itemImage.setImageRequest(imageRequest) + + holder.deleteButton.setOnClickListener{ + callback!!.deleteUpload(item) + } + } + + + + override fun getItemCount(): Int { + return items.size + } + + + class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + var itemImage: com.facebook.drawee.view.SimpleDraweeView = itemView.findViewById(R.id.itemImage) + var titleTextView: TextView = itemView.findViewById(R.id.titleTextView) + var itemProgress: ProgressBar = itemView.findViewById(R.id.itemProgress) + var errorTextView: TextView = itemView.findViewById(R.id.errorTextView) + var deleteButton: ImageView = itemView.findViewById(R.id.deleteButton) + } + + interface Callback { + fun deleteUpload(contribution: Contribution?) + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsContract.java b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsContract.java new file mode 100644 index 000000000..2efd48a76 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsContract.java @@ -0,0 +1,22 @@ +package fr.free.nrw.commons.upload; + +import fr.free.nrw.commons.BasePresenter; +import fr.free.nrw.commons.contributions.Contribution; + +public class PendingUploadsContract { + + public interface View { + + void showWelcomeTip(boolean numberOfUploads); + + void showProgress(boolean shouldShow); + + void showNoContributionsUI(boolean shouldShow); + } + + public interface UserActionListener extends + BasePresenter { + + void deleteUpload(Contribution contribution); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt new file mode 100644 index 000000000..5e3b2a998 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt @@ -0,0 +1,203 @@ +package fr.free.nrw.commons.upload + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.paging.PagedList +import androidx.recyclerview.widget.LinearLayoutManager +import fr.free.nrw.commons.CommonsApplication +import fr.free.nrw.commons.R +import fr.free.nrw.commons.auth.SessionManager +import fr.free.nrw.commons.contributions.Contribution +import fr.free.nrw.commons.databinding.FragmentPendingUploadsBinding +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment +import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.profile.ProfileActivity +import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.utils.ViewUtil +import org.apache.commons.lang3.StringUtils +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. + */ +class PendingUploadsFragment : CommonsDaggerSupportFragment(), PendingUploadsContract.View, + PendingUploadsAdapter.Callback { + var isPendingIconsVisible = false + + // TODO: Rename and change types of parameters + 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 + + @Inject + lateinit var mediaClient: MediaClient + + @Inject + lateinit var sessionManager: SessionManager + + private var userName: String? = null + + private lateinit var binding: FragmentPendingUploadsBinding + + private lateinit var uploadProgressActivity: UploadProgressActivity + + private var contributionsSize = 0 + var l = ArrayList() + + 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) { + userName = requireArguments().getString(ProfileActivity.KEY_USERNAME) + } + + if (StringUtils.isEmpty(userName)) { + userName = sessionManager!!.getUserName() + } + } + + override fun onAttach(context: Context) { + super.onAttach(context) + if (context is UploadProgressActivity) { + uploadProgressActivity = context + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + super.onCreate(savedInstanceState) + binding = FragmentPendingUploadsBinding.inflate(inflater, container, false) + pendingUploadsPresenter.onAttachView(this) + initRecyclerView() + return binding.root + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } + + fun initRecyclerView() { + binding.pendingUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context)) + pendingUploadsPresenter!!.setup( + userName, + sessionManager!!.userName == userName + ) + pendingUploadsPresenter!!.totalContributionList.observe( + viewLifecycleOwner + ) { list: PagedList -> + contributionsSize = list.size + l = ArrayList() + var x = 0; + list.forEach { + if (it!!.state == Contribution.STATE_PAUSED + || it.state == Contribution.STATE_QUEUED + || it.state == Contribution.STATE_IN_PROGRESS + ) { + l.add(it) + } + if (it!!.state == Contribution.STATE_PAUSED + || it.state == Contribution.STATE_QUEUED + ) { + x++ + } + } + Timber.tag("PRINT").e(l.size.toString()) + if (l.size == 0) { + binding.nopendingTextView.visibility = View.VISIBLE + binding.pendingUplaodsLl.visibility = View.GONE + uploadProgressActivity.hidePendingIcons() + } else { + binding.nopendingTextView.visibility = View.GONE + binding.pendingUplaodsLl.visibility = View.VISIBLE + val adapter = PendingUploadsAdapter(l, this) + binding.pendingUploadsRecyclerView.setAdapter(adapter) + binding.progressTextView.setText("0/" + l.size + " uploaded") + + if (x == l.size) { + uploadProgressActivity.setPausedIcon(true) + } + } + } + } + + override fun deleteUpload(contribution: Contribution?) { + showAlertDialog( + requireActivity(), + String.format( + Locale.getDefault(), + getString(R.string.cancelling_upload) + ), + String.format( + Locale.getDefault(), + getString(R.string.cancel_upload_dialog) + ), + String.format(Locale.getDefault(), getString(R.string.yes)), + String.format(Locale.getDefault(), getString(R.string.no)), + { + ViewUtil.showShortToast(context, R.string.cancelling_upload) + pendingUploadsPresenter.deleteUpload(contribution) + CommonsApplication.cancelledUploads.add(contribution!!.pageId) + }, + {} + ) + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment PendingUploadsFragment. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + PendingUploadsFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } + + override fun showWelcomeTip(numberOfUploads: Boolean) { + //TODO("Not yet implemented") + } + + override fun showProgress(shouldShow: Boolean) { + //TODO("Not yet implemented") + } + + override fun showNoContributionsUI(shouldShow: Boolean) { + //TODO("Not yet implemented") + } + + fun restartUpload() { + var contribution = l.get(0) + contribution.state = Contribution.STATE_QUEUED + pendingUploadsPresenter.saveContribution(contribution, this.requireContext().applicationContext) + Timber.d("Restarting for %s", contribution.toString()) + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.java b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.java new file mode 100644 index 000000000..fd4d61e00 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsPresenter.java @@ -0,0 +1,126 @@ +package fr.free.nrw.commons.upload; + + +import android.content.Context; +import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import androidx.paging.DataSource; +import androidx.paging.DataSource.Factory; +import androidx.paging.LivePagedListBuilder; +import androidx.paging.PagedList; +import androidx.work.ExistingWorkPolicy; +import fr.free.nrw.commons.contributions.Contribution; +import fr.free.nrw.commons.contributions.ContributionBoundaryCallback; +import fr.free.nrw.commons.contributions.ContributionsRemoteDataSource; +import fr.free.nrw.commons.contributions.ContributionsRepository; +import fr.free.nrw.commons.di.CommonsApplicationModule; +import fr.free.nrw.commons.upload.PendingUploadsContract.UserActionListener; +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 javax.inject.Inject; +import javax.inject.Named; + +/** + * The presenter class for Contributions + */ +public class PendingUploadsPresenter implements UserActionListener { + + private final ContributionBoundaryCallback contributionBoundaryCallback; + private final ContributionsRepository repository; + private final Scheduler ioThreadScheduler; + + private final CompositeDisposable compositeDisposable; + private final ContributionsRemoteDataSource contributionsRemoteDataSource; + + LiveData> totalContributionList; + + @Inject + PendingUploadsPresenter( + final ContributionBoundaryCallback contributionBoundaryCallback, + final ContributionsRemoteDataSource contributionsRemoteDataSource, + final ContributionsRepository repository, + @Named(CommonsApplicationModule.IO_THREAD) final Scheduler ioThreadScheduler) { + this.contributionBoundaryCallback = contributionBoundaryCallback; + this.repository = repository; + this.ioThreadScheduler = ioThreadScheduler; + this.contributionsRemoteDataSource=contributionsRemoteDataSource; + 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 + */ + void setup(String userName, boolean isSelf) { + final PagedList.Config pagedListConfig = + (new PagedList.Config.Builder()) + .setPrefetchDistance(50) + .setPageSize(10).build(); + Factory factory; + boolean shouldSetBoundaryCallback; + if (!isSelf) { + //We don't want to persist contributions for other user's, therefore + // creating a new DataSource for them + contributionsRemoteDataSource.setUserName(userName); + factory = new Factory() { + @NonNull + @Override + public DataSource create() { + return contributionsRemoteDataSource; + } + }; + shouldSetBoundaryCallback = false; + } else { + contributionBoundaryCallback.setUserName(userName); + shouldSetBoundaryCallback = true; + factory = repository.fetchContributions(); + } + + LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, pagedListConfig); + if (shouldSetBoundaryCallback) { + livePagedListBuilder.setBoundaryCallback(contributionBoundaryCallback); + } + + totalContributionList = livePagedListBuilder.build(); + } + + @Override + public void onAttachView(@NonNull View view) { + + } + + + @Override + public void onDetachView() { + compositeDisposable.clear(); + contributionsRemoteDataSource.dispose(); + contributionBoundaryCallback.dispose(); + } + + /** + * Delete a failed contribution from the local db + */ + @Override + public void deleteUpload(final Contribution contribution) { + compositeDisposable.add(repository + .deleteContributionFromDB(contribution) + .subscribeOn(ioThreadScheduler) + .subscribe()); + } + + public void saveContribution(Contribution contribution, Context context) { + compositeDisposable.add(repository + .save(contribution) + .subscribeOn(ioThreadScheduler) + .subscribe(() -> + WorkRequestHelper.Companion.makeOneTimeWorkRequest( + context, ExistingWorkPolicy.KEEP) + + )); + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt new file mode 100644 index 000000000..66bd54a00 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadProgressActivity.kt @@ -0,0 +1,158 @@ +package fr.free.nrw.commons.upload + +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.widget.Toast +import androidx.fragment.app.Fragment +import androidx.viewpager.widget.ViewPager +import androidx.work.ExistingWorkPolicy +import fr.free.nrw.commons.R +import fr.free.nrw.commons.ViewPagerAdapter +import fr.free.nrw.commons.contributions.Contribution +import fr.free.nrw.commons.databinding.ActivityUploadProgressBinding +import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.upload.fragments.FailedUploadsFragment +import fr.free.nrw.commons.upload.worker.WorkRequestHelper.Companion.makeOneTimeWorkRequest +import fr.free.nrw.commons.utils.NetworkUtils +import fr.free.nrw.commons.utils.ViewUtil +import io.reactivex.functions.Action +import timber.log.Timber +import javax.inject.Inject + + +class UploadProgressActivity : BaseActivity() { + + private lateinit var binding: ActivityUploadProgressBinding + private var pendingUploadsFragment: PendingUploadsFragment? = null + private var failedUploadsFragment: FailedUploadsFragment? = null + var viewPagerAdapter: ViewPagerAdapter? = null + var menu: Menu? = null + val fragmentList: MutableList = ArrayList() + val titleList: MutableList = ArrayList() + var isPaused = true + var isPendingIconsVisible = true + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityUploadProgressBinding.inflate(layoutInflater) + setContentView(binding.root) + viewPagerAdapter = ViewPagerAdapter(supportFragmentManager) + binding.uploadProgressViewPager.setAdapter(viewPagerAdapter) + binding.uploadProgressViewPager.setId(R.id.upload_progress_view_pager) + binding.uploadProgressTabLayout.setupWithViewPager(binding.uploadProgressViewPager) + binding.toolbarBinding.toolbar.title = "Uploads" + setSupportActionBar(binding.toolbarBinding.toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + binding.uploadProgressViewPager.addOnPageChangeListener(object : + ViewPager.OnPageChangeListener { + override fun onPageScrolled( + position: Int, positionOffset: Float, + positionOffsetPixels: Int + ) { + } + + override fun onPageSelected(position: Int) { + updateMenuItems(position) + if (position == 2) { + binding.uploadProgressViewPager.setCanScroll(false) + } else { + binding.uploadProgressViewPager.setCanScroll(true) + } + } + + override fun onPageScrollStateChanged(state: Int) { + } + }) + setTabs() + } + + fun setTabs() { + pendingUploadsFragment = PendingUploadsFragment() + failedUploadsFragment = FailedUploadsFragment() + + fragmentList.add(pendingUploadsFragment!!) + titleList.add("Pending") + fragmentList.add(failedUploadsFragment!!) + titleList.add("Failed") + viewPagerAdapter!!.setTabData(fragmentList, titleList) + viewPagerAdapter!!.notifyDataSetChanged() + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_uploads,menu) + this.menu = menu + updateMenuItems(0) + return super.onCreateOptionsMenu(menu) + } + + override fun onSupportNavigateUp(): Boolean { + onBackPressed() + return true + } + + fun updateMenuItems(currentPosition: Int) { + if (currentPosition == 0) { + if (isPendingIconsVisible){ + menu!!.removeItem(R.id.retry_icon) + menu!!.removeItem(R.id.cancel_icon) + menu!!.removeItem(R.id.pause_icon) + menu!!.removeItem(R.id.resume_icon) + if (!isPaused){ + if (menu!!.findItem(R.id.pause_icon) == null) { + menu!!.add(Menu.NONE, R.id.pause_icon, Menu.NONE, "Pause") + .setIcon(android.R.drawable.ic_media_pause).setOnMenuItemClickListener { + setPausedIcon(true) + true + } + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + } + if (menu!!.findItem(R.id.cancel_icon) == null) { + menu!!.add(Menu.NONE, R.id.cancel_icon, Menu.NONE, "Cancel") + .setIcon(android.R.drawable.ic_menu_close_clear_cancel).setOnMenuItemClickListener { + hidePendingIcons() + true + } + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + } + }else{ + if (menu!!.findItem(R.id.resume_icon) == null) { + menu!!.add(Menu.NONE, R.id.resume_icon, Menu.NONE, "Resume") + .setIcon(android.R.drawable.ic_media_play).setOnMenuItemClickListener { + pendingUploadsFragment!!.restartUpload() + setPausedIcon(false) + true + } + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + } + } + }else{ + menu!!.removeItem(R.id.retry_icon) + menu!!.removeItem(R.id.pause_icon) + menu!!.removeItem(R.id.resume_icon) + menu!!.removeItem(R.id.cancel_icon) + } + } else if (currentPosition == 1) { + menu!!.removeItem(R.id.pause_icon) + if (menu!!.findItem(R.id.retry_icon) == null) { + menu!!.add(Menu.NONE, R.id.retry_icon, Menu.NONE, "Retry") + .setIcon(R.drawable.ic_refresh_white_24dp) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) + } + } + } + + + fun hidePendingIcons() { + isPendingIconsVisible = false + updateMenuItems(binding.uploadProgressViewPager.currentItem) + } + + fun setPausedIcon(paused : Boolean){ + isPaused = paused + updateMenuItems(binding.uploadProgressViewPager.currentItem) + } + + +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/fragments/FailedUploadsFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/fragments/FailedUploadsFragment.kt new file mode 100644 index 000000000..0075502ea --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/fragments/FailedUploadsFragment.kt @@ -0,0 +1,60 @@ +package fr.free.nrw.commons.upload.fragments + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import fr.free.nrw.commons.R + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER +private const val ARG_PARAM1 = "param1" +private const val ARG_PARAM2 = "param2" + +/** + * A simple [Fragment] subclass. + * Use the [FailedUploadsFragment.newInstance] factory method to + * create an instance of this fragment. + */ +class FailedUploadsFragment : Fragment() { + // TODO: Rename and change types of parameters + private var param1: String? = null + private var param2: String? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { + param1 = it.getString(ARG_PARAM1) + param2 = it.getString(ARG_PARAM2) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_failed_uploads, container, false) + } + + companion object { + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @param param1 Parameter 1. + * @param param2 Parameter 2. + * @return A new instance of fragment FailedUploadsFragment. + */ + // TODO: Rename and change types and number of parameters + @JvmStatic + fun newInstance(param1: String, param2: String) = + FailedUploadsFragment().apply { + arguments = Bundle().apply { + putString(ARG_PARAM1, param1) + putString(ARG_PARAM2, param2) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/upload/fragments/PendingUploadsFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/fragments/PendingUploadsFragment.kt new file mode 100644 index 000000000..b05853d52 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/fragments/PendingUploadsFragment.kt @@ -0,0 +1,7 @@ +package fr.free.nrw.commons.upload.fragments + + +// TODO: Rename parameter arguments, choose names that match +// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER + + diff --git a/app/src/main/res/layout/activity_upload_progress.xml b/app/src/main/res/layout/activity_upload_progress.xml new file mode 100644 index 000000000..3a9a83340 --- /dev/null +++ b/app/src/main/res/layout/activity_upload_progress.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_contributions.xml b/app/src/main/res/layout/fragment_contributions.xml index 1390e8c28..e2264b7b2 100644 --- a/app/src/main/res/layout/fragment_contributions.xml +++ b/app/src/main/res/layout/fragment_contributions.xml @@ -18,36 +18,6 @@ android:layout_marginTop="@dimen/miniscule_margin" android:layout_margin="@dimen/very_tiny_gap"/> - - - - - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_pending_uploads.xml b/app/src/main/res/layout/fragment_pending_uploads.xml new file mode 100644 index 000000000..3f7d88aa8 --- /dev/null +++ b/app/src/main/res/layout/fragment_pending_uploads.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_pending_upload.xml b/app/src/main/res/layout/item_pending_upload.xml new file mode 100644 index 000000000..e83247f28 --- /dev/null +++ b/app/src/main/res/layout/item_pending_upload.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_contribution.xml b/app/src/main/res/layout/layout_contribution.xml index cc501d30c..9dced3ecf 100644 --- a/app/src/main/res/layout/layout_contribution.xml +++ b/app/src/main/res/layout/layout_contribution.xml @@ -104,40 +104,6 @@ android:paddingTop="@dimen/standard_gap" android:visibility="visible"> - - - - - - + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/contribution_activity_notification_menu.xml b/app/src/main/res/menu/contribution_activity_notification_menu.xml index 6afbb65b3..5ecf919d0 100644 --- a/app/src/main/res/menu/contribution_activity_notification_menu.xml +++ b/app/src/main/res/menu/contribution_activity_notification_menu.xml @@ -1,16 +1,14 @@ - - - + xmlns:app="http://schemas.android.com/apk/res-auto"> + + diff --git a/app/src/main/res/menu/menu_uploads.xml b/app/src/main/res/menu/menu_uploads.xml new file mode 100644 index 000000000..a3cdf387a --- /dev/null +++ b/app/src/main/res/menu/menu_uploads.xml @@ -0,0 +1,37 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 017404d2c..b4e914156 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -818,4 +818,6 @@ Upload your first media by tapping on the add button. 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. Note about multi-uploads + + Hello blank fragment diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt index d1b82e8ba..01dcb808d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionViewHolderUnitTests.kt @@ -136,19 +136,6 @@ class ContributionViewHolderUnitTests { method.invoke(contributionViewHolder) } - @Test - @Throws(Exception::class) - fun testOnPauseResumeButtonClickedCaseTrue() { - contributionViewHolder.onPauseResumeButtonClicked() - } - - @Test - @Throws(Exception::class) - fun testOnPauseResumeButtonClickedCaseFalse() { - bindind.pauseResumeButton.tag = "" - contributionViewHolder.onPauseResumeButtonClicked() - } - @Test @Throws(Exception::class) fun testWikipediaButtonClicked() { @@ -161,18 +148,6 @@ class ContributionViewHolderUnitTests { contributionViewHolder.imageClicked() } - @Test - @Throws(Exception::class) - fun testDeleteUpload() { - contributionViewHolder.deleteUpload() - } - - @Test - @Throws(Exception::class) - fun testRetryUpload() { - contributionViewHolder.retryUpload() - } - @Test @Throws(Exception::class) fun testChooseImageSource() {