mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Enhancing Multi-Upload Functionality for Consistent Depiction Categorization (#5700)
* Add AlertDialog for categories and modularize receiveSharedItems * Improve nearby-place search function for a multi-upload Enhance the depiction consistency of a multi-upload by ensuring that it corresponds to a single place * Add javadoc * Update strings.xml * Renamed setImageTobeUploaded to setImageToBeUploaded * Make uploadIsOnPlace private & add a setter * Rename uploadIsOnPlace to uploadIsOfAPlace * Use singular when there is only one picture * Add a 'Do not show again' checkbox on the dialog * Update strings.xml --------- Co-authored-by: Giannis Karyotakis <110292528+karyotakisg@users.noreply.github.com> Co-authored-by: Nicolas Raoul <nicolas.raoul@gmail.com>
This commit is contained in:
parent
6aa9303d0f
commit
c178c5de41
7 changed files with 210 additions and 99 deletions
|
|
@ -142,6 +142,7 @@ public class MainActivity extends BaseActivity
|
|||
} else {
|
||||
if (applicationKvStore.getBoolean("firstrun", true)) {
|
||||
applicationKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", false);
|
||||
applicationKvStore.putBoolean("hasAlreadyLaunchedCategoriesDialog", false);
|
||||
}
|
||||
if(savedInstanceState == null){
|
||||
//starting a fresh fragment.
|
||||
|
|
|
|||
|
|
@ -20,8 +20,11 @@ import android.os.Build.VERSION_CODES;
|
|||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
|
|
@ -100,6 +103,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
private Place place;
|
||||
private LatLng prevLocation;
|
||||
private LatLng currLocation;
|
||||
private static boolean uploadIsOfAPlace = false;
|
||||
private boolean isInAppCameraUpload;
|
||||
private List<UploadableFile> uploadableFiles = Collections.emptyList();
|
||||
private int currentSelectedPosition = 0;
|
||||
|
|
@ -123,10 +127,8 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
* when necessary. Initially, it is set to `true`, indicating that the permissions dialog
|
||||
* should be displayed if permissions are missing and it is first time calling
|
||||
* `checkStoragePermissions` method.
|
||||
*
|
||||
* This variable is used in the `checkStoragePermissions` method to determine whether to
|
||||
* show a permissions dialog to the user if the required permissions are not granted.
|
||||
*
|
||||
* If `showPermissionsDialog` is set to `true` and the necessary permissions are missing,
|
||||
* a permissions dialog will be displayed to request the required permissions. If set
|
||||
* to `false`, the dialog won't be shown.
|
||||
|
|
@ -438,6 +440,15 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the flag indicating whether the upload is of a specific place.
|
||||
*
|
||||
* @param uploadOfAPlace a boolean value indicating whether the upload is of place.
|
||||
*/
|
||||
public static void setUploadIsOfAPlace(boolean uploadOfAPlace) {
|
||||
uploadIsOfAPlace = uploadOfAPlace;
|
||||
}
|
||||
|
||||
private void receiveSharedItems() {
|
||||
thumbnailsAdapter.context=this;
|
||||
Intent intent = getIntent();
|
||||
|
|
@ -452,8 +463,14 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
handleNullMedia();
|
||||
} else {
|
||||
//Show thumbnails
|
||||
if (uploadableFiles.size()
|
||||
> 1) {//If there is only file, no need to show the image thumbnails
|
||||
if (uploadableFiles.size() > 1){
|
||||
if(!defaultKvStore.getBoolean("hasAlreadyLaunchedCategoriesDialog")){//If there is only file, no need to show the image thumbnails
|
||||
showAlertDialogForCategories();
|
||||
}
|
||||
if (uploadableFiles.size() > 3 &&
|
||||
!defaultKvStore.getBoolean("hasAlreadyLaunchedBigMultiupload")){
|
||||
showAlertForBattery();
|
||||
}
|
||||
thumbnailsAdapter.setUploadableFiles(uploadableFiles);
|
||||
} else {
|
||||
binding.llContainerTopCard.setVisibility(View.GONE);
|
||||
|
|
@ -467,77 +484,17 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
}
|
||||
|
||||
|
||||
/* Suggest users to turn battery optimisation off when uploading more than a few files.
|
||||
That's because we have noticed that many-files uploads have
|
||||
a much higher probability of failing than uploads with less files.
|
||||
|
||||
Show the dialog for Android 6 and above as
|
||||
the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS intent was added in API level 23
|
||||
*/
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (uploadableFiles.size() > 3
|
||||
&& !defaultKvStore.getBoolean("hasAlreadyLaunchedBigMultiupload")) {
|
||||
// When battery-optimisation dialog is shown don't show the image quality dialog
|
||||
UploadMediaPresenter.isBatteryDialogShowing = true;
|
||||
DialogUtil.showAlertDialog(
|
||||
this,
|
||||
getString(R.string.unrestricted_battery_mode),
|
||||
getString(R.string.suggest_unrestricted_mode),
|
||||
getString(R.string.title_activity_settings),
|
||||
getString(R.string.cancel),
|
||||
() -> {
|
||||
/* Since opening the right settings page might be device dependent, using
|
||||
https://github.com/WaseemSabir/BatteryPermissionHelper
|
||||
directly appeared like a promising idea.
|
||||
However, this simply closed the popup and did not make
|
||||
the settings page appear on a Pixel as well as a Xiaomi device.
|
||||
|
||||
Used the standard intent instead of using this library as
|
||||
it shows a list of all the apps on the device and allows users to
|
||||
turn battery optimisation off.
|
||||
*/
|
||||
Intent batteryOptimisationSettingsIntent = new Intent(
|
||||
Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
|
||||
startActivity(batteryOptimisationSettingsIntent);
|
||||
// calling checkImageQuality after battery dialog is interacted with
|
||||
// so that 2 dialogs do not pop up simultaneously
|
||||
presenter.checkImageQuality(0);
|
||||
UploadMediaPresenter.isBatteryDialogShowing = false;
|
||||
},
|
||||
() -> {
|
||||
presenter.checkImageQuality(0);
|
||||
UploadMediaPresenter.isBatteryDialogShowing = false;
|
||||
}
|
||||
);
|
||||
defaultKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", true);
|
||||
}
|
||||
}
|
||||
for (UploadableFile uploadableFile : uploadableFiles) {
|
||||
UploadMediaDetailFragment uploadMediaDetailFragment = new UploadMediaDetailFragment();
|
||||
|
||||
LocationPermissionsHelper locationPermissionsHelper = new LocationPermissionsHelper(
|
||||
this, locationManager, null);
|
||||
if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
|
||||
currLocation = locationManager.getLastLocation();
|
||||
if (!uploadIsOfAPlace) {
|
||||
handleLocation();
|
||||
uploadMediaDetailFragment.setImageToBeUploaded(uploadableFile, place, currLocation);
|
||||
locationManager.unregisterLocationManager();
|
||||
} else {
|
||||
uploadMediaDetailFragment.setImageToBeUploaded(uploadableFile, place, currLocation);
|
||||
}
|
||||
|
||||
if (currLocation != null) {
|
||||
float locationDifference = getLocationDifference(currLocation, prevLocation);
|
||||
boolean isLocationTagUnchecked = isLocationTagUncheckedInTheSettings();
|
||||
/* Remove location if the user has unchecked the Location EXIF tag in the
|
||||
Manage EXIF Tags setting or turned "Record location for in-app shots" off.
|
||||
Also, location information is discarded if the difference between
|
||||
current location and location recorded just before capturing the image
|
||||
is greater than 100 meters */
|
||||
if (isLocationTagUnchecked || locationDifference > 100
|
||||
|| !defaultKvStore.getBoolean("inAppCameraLocationPref")
|
||||
|| !isInAppCameraUpload) {
|
||||
currLocation = null;
|
||||
}
|
||||
}
|
||||
uploadMediaDetailFragment.setImageToBeUploaded(uploadableFile, place, currLocation);
|
||||
locationManager.unregisterLocationManager();
|
||||
|
||||
UploadMediaDetailFragmentCallback uploadMediaDetailFragmentCallback = new UploadMediaDetailFragmentCallback() {
|
||||
@Override
|
||||
public void deletePictureAtIndex(int index) {
|
||||
|
|
@ -930,4 +887,106 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
|
|||
this::finish
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user uploads more than 1 file informs that
|
||||
* depictions/categories apply to all pictures of a multi upload.
|
||||
* This method takes no arguments and does not return any value.
|
||||
* It shows the AlertDialog and continues the flow of uploads.
|
||||
*/
|
||||
private void showAlertDialogForCategories() {
|
||||
UploadMediaPresenter.isCategoriesDialogShowing = true;
|
||||
// Inflate the custom layout
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
View view = inflater.inflate(R.layout.activity_upload_categories_dialog, null);
|
||||
CheckBox checkBox = view.findViewById(R.id.categories_checkbox);
|
||||
// Create the alert dialog
|
||||
AlertDialog alertDialog = new AlertDialog.Builder(this)
|
||||
.setView(view)
|
||||
.setTitle(getString(R.string.multiple_files_depiction_header))
|
||||
.setMessage(getString(R.string.multiple_files_depiction))
|
||||
.setPositiveButton("OK", (dialog, which) -> {
|
||||
if (checkBox.isChecked()) {
|
||||
// Save the user's choice to not show the dialog again
|
||||
defaultKvStore.putBoolean("hasAlreadyLaunchedCategoriesDialog", true);
|
||||
}
|
||||
presenter.checkImageQuality(0);
|
||||
|
||||
UploadMediaPresenter.isCategoriesDialogShowing = false;
|
||||
})
|
||||
.setNegativeButton("", null)
|
||||
.create();
|
||||
alertDialog.show();
|
||||
}
|
||||
|
||||
|
||||
/** Suggest users to turn battery optimisation off when uploading
|
||||
* more than a few files. That's because we have noticed that
|
||||
* many-files uploads have a much higher probability of failing
|
||||
* than uploads with less files. Show the dialog for Android 6
|
||||
* and above as the ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
|
||||
* intent was added in API level 23
|
||||
*/
|
||||
private void showAlertForBattery(){
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// When battery-optimisation dialog is shown don't show the image quality dialog
|
||||
UploadMediaPresenter.isBatteryDialogShowing = true;
|
||||
DialogUtil.showAlertDialog(
|
||||
this,
|
||||
getString(R.string.unrestricted_battery_mode),
|
||||
getString(R.string.suggest_unrestricted_mode),
|
||||
getString(R.string.title_activity_settings),
|
||||
getString(R.string.cancel),
|
||||
() -> {
|
||||
/* Since opening the right settings page might be device dependent, using
|
||||
https://github.com/WaseemSabir/BatteryPermissionHelper
|
||||
directly appeared like a promising idea.
|
||||
However, this simply closed the popup and did not make
|
||||
the settings page appear on a Pixel as well as a Xiaomi device.
|
||||
Used the standard intent instead of using this library as
|
||||
it shows a list of all the apps on the device and allows users to
|
||||
turn battery optimisation off.
|
||||
*/
|
||||
Intent batteryOptimisationSettingsIntent = new Intent(
|
||||
Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
|
||||
startActivity(batteryOptimisationSettingsIntent);
|
||||
// calling checkImageQuality after battery dialog is interacted with
|
||||
// so that 2 dialogs do not pop up simultaneously
|
||||
|
||||
UploadMediaPresenter.isBatteryDialogShowing = false;
|
||||
},
|
||||
() -> {
|
||||
UploadMediaPresenter.isBatteryDialogShowing = false;
|
||||
}
|
||||
);
|
||||
defaultKvStore.putBoolean("hasAlreadyLaunchedBigMultiupload", true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the permission for Location is turned on and certain
|
||||
* conditions are met, returns current location of the user.
|
||||
*/
|
||||
private void handleLocation(){
|
||||
LocationPermissionsHelper locationPermissionsHelper = new LocationPermissionsHelper(
|
||||
this, locationManager, null);
|
||||
if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
|
||||
currLocation = locationManager.getLastLocation();
|
||||
}
|
||||
|
||||
if (currLocation != null) {
|
||||
float locationDifference = getLocationDifference(currLocation, prevLocation);
|
||||
boolean isLocationTagUnchecked = isLocationTagUncheckedInTheSettings();
|
||||
/* Remove location if the user has unchecked the Location EXIF tag in the
|
||||
Manage EXIF Tags setting or turned "Record location for in-app shots" off.
|
||||
Also, location information is discarded if the difference between
|
||||
current location and location recorded just before capturing the image
|
||||
is greater than 100 meters */
|
||||
if (isLocationTagUnchecked || locationDifference > 100
|
||||
|| !defaultKvStore.getBoolean("inAppCameraLocationPref")
|
||||
|| !isInAppCameraUpload) {
|
||||
currLocation = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,8 +374,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
final boolean response = UploadActivity.nearbyPopupAnswers.get(nearbyPlace);
|
||||
if (response) {
|
||||
if (callback != null) {
|
||||
presenter.onUserConfirmedUploadIsOfPlace(nearbyPlace,
|
||||
indexOfFragment);
|
||||
presenter.onUserConfirmedUploadIsOfPlace(nearbyPlace);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -395,19 +394,41 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
final View customLayout = getLayoutInflater().inflate(R.layout.custom_nearby_found, null);
|
||||
ImageView nearbyFoundImage = customLayout.findViewById(R.id.nearbyItemImage);
|
||||
nearbyFoundImage.setImageURI(uploadItem.getMediaUri());
|
||||
DialogUtil.showAlertDialog(getActivity(),
|
||||
getString(R.string.upload_nearby_place_found_title),
|
||||
String.format(Locale.getDefault(),
|
||||
getString(R.string.upload_nearby_place_found_description),
|
||||
place.getName()),
|
||||
() -> {
|
||||
UploadActivity.nearbyPopupAnswers.put(place, true);
|
||||
presenter.onUserConfirmedUploadIsOfPlace(place, indexOfFragment);
|
||||
},
|
||||
() -> {
|
||||
UploadActivity.nearbyPopupAnswers.put(place, false);
|
||||
},
|
||||
customLayout, true);
|
||||
|
||||
final Activity activity = getActivity();
|
||||
|
||||
if (activity instanceof UploadActivity) {
|
||||
final boolean isMultipleFilesSelected = ((UploadActivity) activity).getIsMultipleFilesSelected();
|
||||
|
||||
// Determine the message based on the selection status
|
||||
String message;
|
||||
if (isMultipleFilesSelected) {
|
||||
// Use plural message if multiple files are selected
|
||||
message = String.format(Locale.getDefault(),
|
||||
getString(R.string.upload_nearby_place_found_description_plural),
|
||||
place.getName());
|
||||
} else {
|
||||
// Use singular message if only one file is selected
|
||||
message = String.format(Locale.getDefault(),
|
||||
getString(R.string.upload_nearby_place_found_description_singular),
|
||||
place.getName());
|
||||
}
|
||||
|
||||
// Show the AlertDialog with the determined message
|
||||
DialogUtil.showAlertDialog(getActivity(),
|
||||
getString(R.string.upload_nearby_place_found_title),
|
||||
message,
|
||||
() -> {
|
||||
// Execute when user confirms the upload is of the specified place
|
||||
UploadActivity.nearbyPopupAnswers.put(place, true);
|
||||
presenter.onUserConfirmedUploadIsOfPlace(place);
|
||||
},
|
||||
() -> {
|
||||
// Execute when user cancels the upload of the specified place
|
||||
UploadActivity.nearbyPopupAnswers.put(place, false);
|
||||
},
|
||||
customLayout, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -440,8 +461,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
|||
if (UploadActivity.nearbyPopupAnswers.containsKey(nearbyPlace)) {
|
||||
final boolean response = UploadActivity.nearbyPopupAnswers.get(nearbyPlace);
|
||||
if (response) {
|
||||
presenter.onUserConfirmedUploadIsOfPlace(nearbyPlace,
|
||||
indexOfFragment);
|
||||
presenter.onUserConfirmedUploadIsOfPlace(nearbyPlace);
|
||||
}
|
||||
} else {
|
||||
showNearbyPlaceFound(nearbyPlace);
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ public interface UploadMediaDetailsContract {
|
|||
|
||||
void onEditButtonClicked(int indexInViewFlipper);
|
||||
|
||||
void onUserConfirmedUploadIsOfPlace(Place place, int uploadItemPosition);
|
||||
void onUserConfirmedUploadIsOfPlace(Place place);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
|
|||
*/
|
||||
public static boolean isBatteryDialogShowing;
|
||||
|
||||
public static boolean isCategoriesDialogShowing;
|
||||
|
||||
@Inject
|
||||
public UploadMediaPresenter(UploadRepository uploadRepository,
|
||||
@Named("default_preferences") JsonKvStore defaultKVStore,
|
||||
|
|
@ -329,18 +331,28 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
|
|||
view.showEditActivity(repository.getUploads().get(indexInViewFlipper));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the information regarding the specified place for uploads
|
||||
* when the user confirms the suggested nearby place.
|
||||
*
|
||||
* @param place The place to be associated with the uploads.
|
||||
*/
|
||||
@Override
|
||||
public void onUserConfirmedUploadIsOfPlace(Place place, int uploadItemPosition) {
|
||||
final List<UploadMediaDetail> uploadMediaDetails = repository.getUploads()
|
||||
.get(uploadItemPosition)
|
||||
.getUploadMediaDetails();
|
||||
UploadItem uploadItem = repository.getUploads()
|
||||
.get(uploadItemPosition);
|
||||
uploadItem.setPlace(place);
|
||||
uploadMediaDetails.set(0, new UploadMediaDetail(place));
|
||||
view.updateMediaDetails(uploadMediaDetails);
|
||||
public void onUserConfirmedUploadIsOfPlace(Place place) {
|
||||
final List<UploadItem> uploads = repository.getUploads();
|
||||
for (UploadItem uploadItem : uploads) {
|
||||
uploadItem.setPlace(place);
|
||||
final List<UploadMediaDetail> uploadMediaDetails = uploadItem.getUploadMediaDetails();
|
||||
// Update UploadMediaDetail object for this UploadItem
|
||||
uploadMediaDetails.set(0, new UploadMediaDetail(place));
|
||||
}
|
||||
// Now that all UploadItems and their associated UploadMediaDetail objects have been updated,
|
||||
// update the view with the modified media details of the first upload item
|
||||
view.updateMediaDetails(uploads.get(0).getUploadMediaDetails());
|
||||
UploadActivity.setUploadIsOfAPlace(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the image quality
|
||||
*
|
||||
|
|
@ -410,7 +422,7 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
|
|||
}
|
||||
|
||||
if (uploadItemIndex == 0) {
|
||||
if (!isBatteryDialogShowing) {
|
||||
if (!isBatteryDialogShowing && !isCategoriesDialogShowing) {
|
||||
// if battery-optimisation dialog is not being shown, call checkImageQuality
|
||||
checkImageQuality(uploadItem, uploadItemIndex);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<?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:id="@+id/upload_root_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/categories_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Don't show this message again" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -610,7 +610,8 @@ Upload your first media by tapping on the add button.</string>
|
|||
<string name="title_for_parent_classes">PARENT CLASSES</string>
|
||||
|
||||
<string name="upload_nearby_place_found_title">Nearby Place Found</string>
|
||||
<string name="upload_nearby_place_found_description">Is this a photo of %1$s?</string>
|
||||
<string name="upload_nearby_place_found_description_plural">Are these pictures of %1$s?</string>
|
||||
<string name="upload_nearby_place_found_description_singular">Is this a picture of %1$s?</string>
|
||||
<string name="title_app_shortcut_bookmark">Bookmarks</string>
|
||||
<string name="title_app_shortcut_setting">Settings</string>
|
||||
<string name="remove_bookmark">Removed from bookmarks</string>
|
||||
|
|
@ -815,4 +816,6 @@ Upload your first media by tapping on the add button.</string>
|
|||
<item quantity="one">%d image selected</item>
|
||||
<item quantity="other">%d images selected</item>
|
||||
</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>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue