Added pending uploads screen

This commit is contained in:
Kanahia 2024-06-08 17:05:32 +05:30
parent 9a2a56c1cf
commit 13b4987a54
30 changed files with 1329 additions and 514 deletions

View file

@ -1,262 +1,247 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="com.google.android.apps.photos.permission.GOOGLE_PHOTOS" />
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
xmlns:tools="http://schemas.android.com/tools">
<queries>
<!-- Browser -->
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
<!-- Google Maps -->
<package android:name="com.google.android.apps.maps" />
</queries>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="com.google.android.apps.photos.permission.GOOGLE_PHOTOS" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<queries>
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
<!-- Browser -->
<intent>
<action android:name="android.intent.action.VIEW" />
<application
android:name=".CommonsApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/LightAppTheme"
android:largeHeap="true"
android:supportsRtl="true"
tools:replace="android:appComponentFactory"
android:appComponentFactory="commons"
android:requestLegacyExternalStorage = "true"
tools:ignore="GoogleAppIndexingWarning">
<category android:name="android.intent.category.BROWSABLE" />
<activity
android:theme="@style/EditActivityTheme"
android:name=".description.DescriptionEditActivity"
android:exported="true" />
<data android:scheme="https" />
</intent>
<!-- Google Maps -->
<package android:name="com.google.android.apps.maps" />
</queries> <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
<activity
android:name=".edit.EditActivity"
android:exported="false" />
<application
android:name=".CommonsApplication"
android:appComponentFactory="commons"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:requestLegacyExternalStorage="true"
android:supportsRtl="true"
android:theme="@style/LightAppTheme"
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:appComponentFactory">
<activity
android:name=".upload.UploadProgressActivity"
android:exported="false" />
<activity
android:name=".description.DescriptionEditActivity"
android:exported="true"
android:theme="@style/EditActivityTheme" />
<activity
android:name=".edit.EditActivity"
android:exported="false" />
<activity
android:name="org.acra.dialog.CrashReportDialog"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true"
android:launchMode="singleInstance"
android:process=":acra" />
<activity
android:name=".media.ZoomableActivity"
android:configChanges="screenSize|keyboard|orientation"
android:label="Zoomable Activity"
android:parentActivityName=".customselector.ui.selector.CustomSelectorActivity" />
<activity
android:name=".auth.LoginActivity"
android:exported="true">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER" />
<activity android:name="org.acra.dialog.CrashReportDialog"
android:process=":acra"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true" />
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<activity
android:name=".media.ZoomableActivity"
android:label="Zoomable Activity"
android:configChanges="screenSize|keyboard|orientation"
android:parentActivityName=".customselector.ui.selector.CustomSelectorActivity" />
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>
<activity android:name=".WelcomeActivity" />
<activity
android:name=".upload.UploadActivity"
android:configChanges="orientation|screenSize|keyboard"
android:exported="true"
android:hardwareAccelerated="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:windowSoftInputMode="adjustResize">
<intent-filter android:label="@string/intent_share_upload_label">
<action android:name="android.intent.action.SEND" />
<activity android:name=".auth.LoginActivity"
android:exported="true">
<intent-filter>
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.MAIN" />
</intent-filter>
<data android:mimeType="image/*" />
<data android:mimeType="audio/ogg" />
</intent-filter>
<intent-filter android:label="@string/intent_share_upload_label">
<action android:name="android.intent.action.SEND_MULTIPLE" />
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
<category android:name="android.intent.category.DEFAULT" />
</activity>
<activity android:name=".WelcomeActivity" />
<data android:mimeType="image/*" />
<data android:mimeType="audio/ogg" />
</intent-filter>
</activity>
<activity
android:name=".contributions.MainActivity"
android:configChanges="screenSize|keyboard|orientation"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" />
<activity
android:name=".settings.SettingsActivity"
android:label="@string/title_activity_settings" />
<activity
android:name=".AboutActivity"
android:label="@string/title_activity_about"
android:parentActivityName=".contributions.MainActivity" />
<activity
android:name=".auth.SignupActivity"
android:configChanges="orientation|screenLayout|screenSize"
android:label="@string/title_activity_signup" />
<activity
android:name=".notification.NotificationActivity"
android:label="@string/navigation_item_notification" />
<activity
android:name=".quiz.QuizActivity"
android:label="@string/quiz" />
<activity
android:name=".quiz.QuizResultActivity"
android:label="@string/result" />
<activity
android:name=".customselector.ui.selector.CustomSelectorActivity"
android:configChanges="screenSize|keyboard|orientation"
android:label="@string/title_activity_custom_selector"
android:parentActivityName=".contributions.MainActivity" />
<activity
android:name=".category.CategoryDetailsActivity"
android:configChanges="screenSize|keyboard|orientation"
android:label="@string/title_activity_featured_images"
android:parentActivityName=".contributions.MainActivity" />
<activity
android:name=".explore.depictions.WikidataItemDetailsActivity"
android:configChanges="screenSize|keyboard|orientation"
android:label="@string/title_activity_featured_images"
android:parentActivityName=".contributions.MainActivity" />
<activity
android:name=".explore.SearchActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="@string/title_activity_search"
android:launchMode="singleTop"
android:parentActivityName=".contributions.MainActivity" />
<activity
android:name=".profile.ProfileActivity"
android:configChanges="orientation|screenSize|keyboard"
android:label="@string/Profile" />
<activity
android:name=".review.ReviewActivity"
android:label="@string/title_activity_review" />
<activity
android:name=".LocationPicker.LocationPickerActivity"
android:label="Location Picker" />
<activity
android:hardwareAccelerated="false"
android:name=".upload.UploadActivity"
android:exported="true"
android:configChanges="orientation|screenSize|keyboard"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:windowSoftInputMode="adjustResize"
>
<intent-filter android:label="@string/intent_share_upload_label">
<action android:name="android.intent.action.SEND" />
<service
android:name=".auth.WikiAccountAuthenticatorService"
android:exported="true"
android:process=":auth">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<service
android:name="org.acra.sender.SenderService"
android:exported="false"
android:process=":acra" />
<data android:mimeType="image/*" />
<data android:mimeType="audio/ogg" />
</intent-filter>
<intent-filter android:label="@string/intent_share_upload_label">
<action android:name="android.intent.action.SEND_MULTIPLE" />
<provider
android:name=".filepicker.ExtendedFileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<provider
android:name=".category.CategoryContentProvider"
android:authorities="${applicationId}.categories.contentprovider"
android:exported="false"
android:label="@string/provider_categories"
android:syncable="false" />
<provider
android:name=".explore.recentsearches.RecentSearchesContentProvider"
android:authorities="${applicationId}.explore.recentsearches.contentprovider"
android:exported="false"
android:label="@string/provider_searches"
android:syncable="false" />
<provider
android:name=".recentlanguages.RecentLanguagesContentProvider"
android:authorities="${applicationId}.recentlanguages.contentprovider"
android:exported="false"
android:label="@string/provider_recent_languages"
android:syncable="false" />
<provider
android:name=".bookmarks.pictures.BookmarkPicturesContentProvider"
android:authorities="${applicationId}.bookmarks.contentprovider"
android:exported="false"
android:label="@string/provider_bookmarks"
android:syncable="false" />
<provider
android:name=".bookmarks.locations.BookmarkLocationsContentProvider"
android:authorities="${applicationId}.bookmarks.locations.contentprovider"
android:exported="false"
android:label="@string/provider_bookmarks_location"
android:syncable="false" />
<provider
android:name=".bookmarks.items.BookmarkItemsContentProvider"
android:authorities="${applicationId}.bookmarks.items.contentprovider"
android:exported="false"
android:label="@string/provider_bookmarks_location"
android:syncable="false" />
<category android:name="android.intent.category.DEFAULT" />
<receiver
android:name=".widget.PicOfDayAppWidget"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<data android:mimeType="image/*" />
<data android:mimeType="audio/ogg" />
</intent-filter>
</activity>
<activity
android:name=".contributions.MainActivity"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:configChanges="screenSize|keyboard|orientation" />
<activity
android:name=".settings.SettingsActivity"
android:label="@string/title_activity_settings" />
<activity
android:name=".AboutActivity"
android:label="@string/title_activity_about"
android:parentActivityName=".contributions.MainActivity" />
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/pic_of_day_app_widget_info" />
</receiver>
<activity
android:name=".auth.SignupActivity"
android:configChanges="orientation|screenLayout|screenSize"
android:label="@string/title_activity_signup" />
<activity
android:name=".notification.NotificationActivity"
android:label="@string/navigation_item_notification" />
<activity android:name=".quiz.QuizActivity"
android:label="@string/quiz"/>
<activity android:name=".quiz.QuizResultActivity"
android:label="@string/result"/>
<activity
android:name=".customselector.ui.selector.CustomSelectorActivity"
android:label="@string/title_activity_custom_selector"
android:configChanges="screenSize|keyboard|orientation"
android:parentActivityName=".contributions.MainActivity" />
<activity
android:name=".category.CategoryDetailsActivity"
android:label="@string/title_activity_featured_images"
android:configChanges="screenSize|keyboard|orientation"
android:parentActivityName=".contributions.MainActivity" />
<activity
android:name=".explore.depictions.WikidataItemDetailsActivity"
android:label="@string/title_activity_featured_images"
android:configChanges="screenSize|keyboard|orientation"
android:parentActivityName=".contributions.MainActivity" />
<activity
android:name=".explore.SearchActivity"
android:label="@string/title_activity_search"
android:launchMode="singleTop"
android:configChanges="orientation|keyboardHidden|screenSize"
android:parentActivityName=".contributions.MainActivity"
/>
<activity
android:name=".profile.ProfileActivity"
android:configChanges="orientation|screenSize|keyboard"
android:label="@string/Profile" />
<activity
android:name=".review.ReviewActivity"
android:label="@string/title_activity_review" />
<activity
android:name=".LocationPicker.LocationPickerActivity"
android:label="Location Picker" />
<service
android:name=".auth.WikiAccountAuthenticatorService"
android:exported="true"
android:process=":auth">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<service
android:name="org.acra.sender.SenderService"
android:exported="false"
android:process=":acra" />
<provider
android:name=".filepicker.ExtendedFileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<provider
android:name=".category.CategoryContentProvider"
android:authorities="${applicationId}.categories.contentprovider"
android:exported="false"
android:label="@string/provider_categories"
android:syncable="false" />
<provider
android:name=".explore.recentsearches.RecentSearchesContentProvider"
android:authorities="${applicationId}.explore.recentsearches.contentprovider"
android:exported="false"
android:label="@string/provider_searches"
android:syncable="false" />
<provider
android:name=".recentlanguages.RecentLanguagesContentProvider"
android:authorities="${applicationId}.recentlanguages.contentprovider"
android:exported="false"
android:label="@string/provider_recent_languages"
android:syncable="false" />
<provider
android:name=".bookmarks.pictures.BookmarkPicturesContentProvider"
android:authorities="${applicationId}.bookmarks.contentprovider"
android:exported="false"
android:label="@string/provider_bookmarks"
android:syncable="false" />
<provider
android:name=".bookmarks.locations.BookmarkLocationsContentProvider"
android:authorities="${applicationId}.bookmarks.locations.contentprovider"
android:exported="false"
android:label="@string/provider_bookmarks_location"
android:syncable="false" />
<provider
android:name=".bookmarks.items.BookmarkItemsContentProvider"
android:authorities="${applicationId}.bookmarks.items.contentprovider"
android:exported="false"
android:label="@string/provider_bookmarks_location"
android:syncable="false" />
<receiver android:name=".widget.PicOfDayAppWidget"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/pic_of_day_app_widget_info" />
</receiver>
<uses-library android:name="org.apache.http.legacy" android:required="false" />
</application>
<uses-library
android:name="org.apache.http.legacy"
android:required="false" />
</application>
</manifest>

View file

@ -20,6 +20,10 @@ public abstract class ContributionDao {
@Query("SELECT * FROM contribution order by media_dateUploaded DESC")
abstract DataSource.Factory<Integer, Contribution> fetchContributions();
@Query("SELECT * FROM contribution WHERE state = -1 ORDER BY media_dateUploaded DESC")
abstract DataSource.Factory<Integer, Contribution> fetchContributionsWithStateCompleted();
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract void saveSynchronous(Contribution contribution);

View file

@ -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. */
@ -80,9 +77,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());
if (!TextUtils.isEmpty(imageSource)) {
@ -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;
}

View file

@ -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.
*/

View file

@ -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<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(),
new RequestMultiplePermissions(),
new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> 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);
}
}

View file

@ -26,6 +26,7 @@ public class ContributionsListPresenter implements UserActionListener {
private final ContributionsRemoteDataSource contributionsRemoteDataSource;
LiveData<PagedList<Contribution>> contributionList;
LiveData<PagedList<Contribution>> 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<Integer, Contribution> 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();
}
}

View file

@ -67,6 +67,10 @@ class ContributionsLocalDataSource {
return contributionDao.fetchContributions();
}
public Factory<Integer, Contribution> getCompletedContributions() {
return contributionDao.fetchContributionsWithStateCompleted();
}
public Single<List<Long>> saveContributions(final List<Contribution> contributions) {
final List<Contribution> contributionList = new ArrayList<>();
for(final Contribution contribution: contributions) {

View file

@ -49,6 +49,10 @@ public class ContributionsRepository {
return localDataSource.getContributions();
}
public Factory<Integer, Contribution> fetchCompletedContributions() {
return localDataSource.getCompletedContributions();
}
public Single<List<Long>> save(List<Contribution> contributions) {
return localDataSource.saveContributions(contributions);
}

View file

@ -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");

View file

@ -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();
}

View file

@ -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();
}

View file

@ -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)

View file

@ -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<Contribution>, callback: Callback) :
RecyclerView.Adapter<PendingUploadsAdapter.ViewHolder>() {
private val items: List<Contribution> = 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<TextView>(R.id.titleTextView)
var itemProgress: ProgressBar = itemView.findViewById<ProgressBar>(R.id.itemProgress)
var errorTextView: TextView = itemView.findViewById<TextView>(R.id.errorTextView)
var deleteButton: ImageView = itemView.findViewById(R.id.deleteButton)
}
interface Callback {
fun deleteUpload(contribution: Contribution?)
}
}

View file

@ -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<fr.free.nrw.commons.upload.PendingUploadsContract.View> {
void deleteUpload(Contribution contribution);
}
}

View file

@ -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<Contribution>()
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<Contribution?> ->
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())
}
}

View file

@ -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<PagedList<Contribution>> 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<Integer, Contribution> 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<Integer, Contribution>() {
@NonNull
@Override
public DataSource<Integer, Contribution> 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)
));
}
}

View file

@ -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<Fragment> = ArrayList()
val titleList: MutableList<String> = 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)
}
}

View file

@ -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)
}
}
}
}

View file

@ -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

View file

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".upload.UploadProgressActivity">
<include
android:id="@+id/toolbarBinding"
layout="@layout/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/upload_progress_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/card_light_grey">
<com.google.android.material.tabs.TabLayout
android:id="@+id/upload_progress_tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/toolbar"
android:background="?attr/tabBackground"
app:tabIndicatorColor="?attr/tabIndicatorColor"
app:tabMode="fixed"
app:tabSelectedTextColor="?attr/tabSelectedTextColor"
app:tabTextColor="?attr/tabTextColor" />
</com.google.android.material.appbar.AppBarLayout>
<!-- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"-->
<!-- android:id="@+id/mediaContainer"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent"-->
<!-- android:layout_below="@id/toolbar_layout"-->
<!-- android:orientation="horizontal"-->
<!-- android:visibility="gone" />-->
<fr.free.nrw.commons.explore.ParentViewPager
android:id="@+id/upload_progress_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/upload_progress_toolbar_layout"
android:background="?attr/mainBackground" />
</RelativeLayout>
</LinearLayout>

View file

@ -18,36 +18,6 @@
android:layout_marginTop="@dimen/miniscule_margin"
android:layout_margin="@dimen/very_tiny_gap"/>
<LinearLayout
android:id="@+id/limited_connection_enabled_layout"
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/miniscule_margin"
android:padding="@dimen/standard_gap"
android:orientation="vertical"
android:clickable="true"
android:focusable="true"
android:background="@color/wikimedia_green">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:textColor="@android:color/white"
android:layout_marginBottom="@dimen/tiny_gap"
android:textSize="@dimen/subheading_text_size"
android:text="@string/limited_connection_is_on"
app:drawableTint="@color/white"
app:drawableStartCompat="@drawable/ic_baseline_cloud_off_24"/>
<TextView
android:id="@+id/limited_connection_description_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="@dimen/description_text_size"
android:text="@string/limited_connection_explanation"/>
</LinearLayout>
<FrameLayout
android:id="@+id/explore_container"
android:layout_width="match_parent"

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".upload.fragments.FailedUploadsFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</FrameLayout>

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".upload.PendingUploadsFragment">
<TextView
android:id="@+id/nopendingTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="You do not have any pending Uploads!" />
<LinearLayout
android:id="@+id/pendingUplaodsLl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="bottom"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="Progress:"
android:textSize="22sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/progress_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="0/2 uploaded" />
<ProgressBar
android:id="@+id/progressBarPending"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/pending_uploads_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginHorizontal="10dp" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fresco="http://schemas.android.com/tools"
android:paddingBottom="8dp"
android:gravity="center"
android:orientation="horizontal">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/itemImage"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="?attr/mainBackground"
app:actualImageScaleType="centerCrop"
fresco:placeholderImage="@drawable/ic_image_black_24dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="6dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/titleTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="RandomTest" />
<ProgressBar
android:id="@+id/itemProgress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/errorTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Queued"
android:visibility="gone" />
</LinearLayout>
<ImageView
android:id="@+id/deleteButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_menu_close_clear_cancel" />
</LinearLayout>

View file

@ -104,40 +104,6 @@
android:paddingTop="@dimen/standard_gap"
android:visibility="visible">
<ImageButton
android:id="@+id/pauseResumeButton"
android:layout_width="@dimen/dimen_40"
android:layout_height="@dimen/dimen_40"
android:layout_marginEnd="@dimen/tiny_padding"
android:layout_toStartOf="@id/cancelButton"
android:background="@android:color/transparent"
android:tag="@string/pause"
app:srcCompat="@drawable/pause_icon" />
<ImageButton
android:id="@+id/cancelButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="@dimen/tiny_padding"
android:layout_toStartOf="@id/retryButton"
android:background="@android:color/transparent"
android:padding="@dimen/activity_margin_horizontal"
android:src="@drawable/ic_cancel_white"
android:tint="?attr/contributionsListTextSecondary"
android:text="@string/menu_cancel_upload" />
<ImageButton
android:id="@+id/retryButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="@dimen/tiny_padding"
android:layout_toStartOf="@id/wikipediaButton"
android:background="@android:color/transparent"
android:padding="@dimen/activity_margin_horizontal"
android:src="@drawable/ic_retry_white"
android:tint="?attr/contributionsListTextSecondary"
android:text="@string/menu_retry_upload" />
<ImageButton
android:id="@+id/wikipediaButton"
android:layout_width="48dp"

View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:clickable="true"
android:focusable="true"
android:gravity="center"
tools:background="@color/black">
<ImageView
android:id="@+id/pending_uploads_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:gravity="center"
app:srcCompat="@android:drawable/stat_sys_upload" />
<TextView
android:id="@+id/pending_uploads_count_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/pending_uploads_image_view"
android:layout_alignEnd="@id/pending_uploads_image_view"
android:layout_alignRight="@id/pending_uploads_image_view"
android:background="@drawable/notification_badge"
android:gravity="center"
android:padding="@dimen/miniscule_margin"
android:textColor="?attr/notification_icon_text_color"
android:textSize="7sp"
android:textStyle="bold"
android:visibility="gone"
tools:text="9+"
tools:visibility="visible" />
<TextView
android:id="@+id/uploads_error_count_badge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/pending_uploads_count_badge"
android:layout_alignEnd="@id/pending_uploads_image_view"
android:layout_alignRight="@id/pending_uploads_image_view"
android:layout_marginTop="1dp"
android:layout_marginEnd="0dp"
android:layout_marginRight="0dp"
android:background="@drawable/notification_badge"
android:backgroundTint="@color/button_blue"
android:gravity="center"
android:padding="@dimen/miniscule_margin"
android:textColor="@color/white"
android:textSize="7sp"
android:textStyle="bold"
android:visibility="gone"
tools:text="9+"
tools:visibility="visible" />
</RelativeLayout>

View file

@ -1,16 +1,14 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/toggle_limited_connection_mode"
android:title="@string/limited_connection_mode"
app:showAsAction="always"
android:checkable="true"
android:icon="@drawable/ic_baseline_cloud_queue_24"
/>
<item android:id="@+id/notifications"
android:title="@string/notifications"
app:showAsAction="ifRoom|withText"
android:menuCategory="secondary"
app:actionLayout="@layout/notification_icon"
/>
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/upload_tab"
android:title="Upload"
app:actionLayout="@layout/pending_uploads_icon"
app:showAsAction="ifRoom|withText" />
<item
android:id="@+id/notifications"
android:menuCategory="secondary"
android:title="@string/notifications"
app:actionLayout="@layout/notification_icon"
app:showAsAction="ifRoom|withText" />
</menu>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".upload.UploadProgressActivity"
>
<item
android:id="@+id/resume_icon"
android:title="Resume"
android:icon="@android:drawable/ic_media_play"
android:orderInCategory="1"
app:showAsAction="ifRoom"
/>
<item
android:id="@+id/pause_icon"
android:title="Pause"
android:icon="@android:drawable/ic_media_pause"
android:orderInCategory="1"
app:showAsAction="ifRoom"
/>
<item
android:id="@+id/retry_icon"
android:title="Retry"
android:icon="@drawable/ic_refresh_white_24dp"
android:orderInCategory="1"
app:showAsAction="ifRoom"
/>
<item
android:id="@+id/cancel_icon"
android:title="Cancel"
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:orderInCategory="1"
app:showAsAction="ifRoom"
/>
</menu>

View file

@ -818,4 +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>
</resources>

View file

@ -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() {