mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-30 22:34:02 +01:00
Convert UploadActivity to kotlin
This commit is contained in:
parent
87c8224793
commit
e4b4ceb39d
9 changed files with 962 additions and 1006 deletions
|
|
@ -1,986 +0,0 @@
|
||||||
package fr.free.nrw.commons.upload;
|
|
||||||
|
|
||||||
import static fr.free.nrw.commons.contributions.ContributionController.ACTION_INTERNAL_UPLOADS;
|
|
||||||
import static fr.free.nrw.commons.utils.PermissionUtils.checkPermissionsAndPerformAction;
|
|
||||||
import static fr.free.nrw.commons.utils.PermissionUtils.getPERMISSIONS_STORAGE;
|
|
||||||
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
|
|
||||||
import static fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE;
|
|
||||||
import static fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE_CATEGORY;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.location.Location;
|
|
||||||
import android.location.LocationManager;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Build.VERSION;
|
|
||||||
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;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
import androidx.viewpager.widget.PagerAdapter;
|
|
||||||
import androidx.viewpager.widget.ViewPager;
|
|
||||||
import androidx.work.ExistingWorkPolicy;
|
|
||||||
import fr.free.nrw.commons.R;
|
|
||||||
import fr.free.nrw.commons.auth.LoginActivity;
|
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
|
||||||
import fr.free.nrw.commons.contributions.ContributionController;
|
|
||||||
import fr.free.nrw.commons.databinding.ActivityUploadBinding;
|
|
||||||
import fr.free.nrw.commons.filepicker.Constants.RequestCodes;
|
|
||||||
import fr.free.nrw.commons.filepicker.UploadableFile;
|
|
||||||
import fr.free.nrw.commons.kvstore.BasicKvStore;
|
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
|
||||||
import fr.free.nrw.commons.location.LatLng;
|
|
||||||
import fr.free.nrw.commons.location.LocationPermissionsHelper;
|
|
||||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
|
||||||
import fr.free.nrw.commons.mwapi.UserClient;
|
|
||||||
import fr.free.nrw.commons.nearby.Place;
|
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
|
||||||
import fr.free.nrw.commons.theme.BaseActivity;
|
|
||||||
import fr.free.nrw.commons.upload.UploadBaseFragment.Callback;
|
|
||||||
import fr.free.nrw.commons.upload.categories.UploadCategoriesFragment;
|
|
||||||
import fr.free.nrw.commons.upload.depicts.DepictsFragment;
|
|
||||||
import fr.free.nrw.commons.upload.license.MediaLicenseFragment;
|
|
||||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment;
|
|
||||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.UploadMediaDetailFragmentCallback;
|
|
||||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaPresenter;
|
|
||||||
import fr.free.nrw.commons.upload.worker.WorkRequestHelper;
|
|
||||||
import fr.free.nrw.commons.utils.DialogUtil;
|
|
||||||
import fr.free.nrw.commons.utils.PermissionUtils;
|
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class UploadActivity extends BaseActivity implements
|
|
||||||
UploadContract.View, UploadBaseFragment.Callback, ThumbnailsAdapter.OnThumbnailDeletedListener {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
ContributionController contributionController;
|
|
||||||
@Inject
|
|
||||||
@Named("default_preferences")
|
|
||||||
JsonKvStore directKvStore;
|
|
||||||
@Inject
|
|
||||||
UploadContract.UserActionListener presenter;
|
|
||||||
@Inject
|
|
||||||
SessionManager sessionManager;
|
|
||||||
@Inject
|
|
||||||
UserClient userClient;
|
|
||||||
@Inject
|
|
||||||
LocationServiceManager locationManager;
|
|
||||||
|
|
||||||
private boolean isTitleExpanded = true;
|
|
||||||
|
|
||||||
private CompositeDisposable compositeDisposable;
|
|
||||||
private ProgressDialog progressDialog;
|
|
||||||
private UploadImageAdapter uploadImagesAdapter;
|
|
||||||
private List<UploadBaseFragment> fragments;
|
|
||||||
private UploadCategoriesFragment uploadCategoriesFragment;
|
|
||||||
private DepictsFragment depictsFragment;
|
|
||||||
private MediaLicenseFragment mediaLicenseFragment;
|
|
||||||
private ThumbnailsAdapter thumbnailsAdapter;
|
|
||||||
BasicKvStore store;
|
|
||||||
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;
|
|
||||||
/*
|
|
||||||
Checks for if multiple files selected
|
|
||||||
*/
|
|
||||||
private boolean isMultipleFilesSelected = false;
|
|
||||||
|
|
||||||
public static final String EXTRA_FILES = "commons_image_exta";
|
|
||||||
public static final String LOCATION_BEFORE_IMAGE_CAPTURE = "user_location_before_image_capture";
|
|
||||||
public static final String IN_APP_CAMERA_UPLOAD = "in_app_camera_upload";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores all nearby places found and related users response for
|
|
||||||
* each place while uploading media
|
|
||||||
*/
|
|
||||||
public static HashMap<Place,Boolean> nearbyPopupAnswers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A private boolean variable to control whether a permissions dialog should be shown
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @see UploadActivity#checkStoragePermissions()
|
|
||||||
*/
|
|
||||||
private boolean showPermissionsDialog = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether fragments have been saved.
|
|
||||||
*/
|
|
||||||
private boolean isFragmentsSaved = false;
|
|
||||||
|
|
||||||
public static final String keyForCurrentUploadImagesSize = "CurrentUploadImagesSize";
|
|
||||||
public static final String storeNameForCurrentUploadImagesSize = "CurrentUploadImageQualities";
|
|
||||||
|
|
||||||
private ActivityUploadBinding binding;
|
|
||||||
|
|
||||||
@SuppressLint("CheckResult")
|
|
||||||
@Override
|
|
||||||
protected void onCreate(final Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
binding = ActivityUploadBinding.inflate(getLayoutInflater());
|
|
||||||
setContentView(binding.getRoot());
|
|
||||||
|
|
||||||
/*
|
|
||||||
If Configuration of device is changed then get the new fragments
|
|
||||||
created by the system and populate the fragments ArrayList
|
|
||||||
*/
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
isFragmentsSaved = true;
|
|
||||||
final List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
|
|
||||||
fragments = new ArrayList<>();
|
|
||||||
for (final Fragment fragment : fragmentList) {
|
|
||||||
fragments.add((UploadBaseFragment) fragment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
compositeDisposable = new CompositeDisposable();
|
|
||||||
init();
|
|
||||||
binding.rlContainerTitle.setOnClickListener(v -> onRlContainerTitleClicked());
|
|
||||||
nearbyPopupAnswers = new HashMap<>();
|
|
||||||
//getting the current dpi of the device and if it is less than 320dp i.e. overlapping
|
|
||||||
//threshold, thumbnails automatically minimizes
|
|
||||||
final DisplayMetrics metrics = getResources().getDisplayMetrics();
|
|
||||||
final float dpi = (metrics.widthPixels)/(metrics.density);
|
|
||||||
if (dpi<=321) {
|
|
||||||
onRlContainerTitleClicked();
|
|
||||||
}
|
|
||||||
if (PermissionUtils.hasPermission(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) {
|
|
||||||
locationManager.registerLocationManager();
|
|
||||||
}
|
|
||||||
locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER);
|
|
||||||
locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER);
|
|
||||||
store = new BasicKvStore(this, storeNameForCurrentUploadImagesSize);
|
|
||||||
store.clearAll();
|
|
||||||
checkStoragePermissions();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init() {
|
|
||||||
initProgressDialog();
|
|
||||||
initViewPager();
|
|
||||||
initThumbnailsRecyclerView();
|
|
||||||
//And init other things you need to
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initProgressDialog() {
|
|
||||||
progressDialog = new ProgressDialog(this);
|
|
||||||
progressDialog.setMessage(getString(R.string.please_wait));
|
|
||||||
progressDialog.setCancelable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initThumbnailsRecyclerView() {
|
|
||||||
binding.rvThumbnails.setLayoutManager(new LinearLayoutManager(this,
|
|
||||||
LinearLayoutManager.HORIZONTAL, false));
|
|
||||||
thumbnailsAdapter = new ThumbnailsAdapter(() -> currentSelectedPosition);
|
|
||||||
thumbnailsAdapter.setOnThumbnailDeletedListener(this);
|
|
||||||
binding.rvThumbnails.setAdapter(thumbnailsAdapter);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initViewPager() {
|
|
||||||
uploadImagesAdapter = new UploadImageAdapter(getSupportFragmentManager());
|
|
||||||
binding.vpUpload.setAdapter(uploadImagesAdapter);
|
|
||||||
binding.vpUpload.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onPageScrolled(final int position, final float positionOffset,
|
|
||||||
final int positionOffsetPixels) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPageSelected(final int position) {
|
|
||||||
currentSelectedPosition = position;
|
|
||||||
if (position >= uploadableFiles.size()) {
|
|
||||||
binding.cvContainerTopCard.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
thumbnailsAdapter.notifyDataSetChanged();
|
|
||||||
binding.cvContainerTopCard.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPageScrollStateChanged(final int state) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLoggedIn() {
|
|
||||||
return sessionManager.isUserLoggedIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
presenter.onAttachView(this);
|
|
||||||
if (!isLoggedIn()) {
|
|
||||||
askUserToLogIn();
|
|
||||||
}
|
|
||||||
checkBlockStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes API call to check if user is blocked from Commons. If the user is blocked, a snackbar
|
|
||||||
* is created to notify the user
|
|
||||||
*/
|
|
||||||
protected void checkBlockStatus() {
|
|
||||||
compositeDisposable.add(userClient.isUserBlockedFromCommons()
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.filter(result -> result)
|
|
||||||
.subscribe(result -> DialogUtil.showAlertDialog(
|
|
||||||
this,
|
|
||||||
getString(R.string.block_notification_title),
|
|
||||||
getString(R.string.block_notification),
|
|
||||||
getString(R.string.ok),
|
|
||||||
this::finish)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkStoragePermissions() {
|
|
||||||
// Check if all required permissions are granted
|
|
||||||
final boolean hasAllPermissions = PermissionUtils.hasPermission(this, getPERMISSIONS_STORAGE());
|
|
||||||
final boolean hasPartialAccess = PermissionUtils.hasPartialAccess(this);
|
|
||||||
if (hasAllPermissions || hasPartialAccess) {
|
|
||||||
// All required permissions are granted, so enable UI elements and perform actions
|
|
||||||
receiveSharedItems();
|
|
||||||
binding.cvContainerTopCard.setVisibility(View.VISIBLE);
|
|
||||||
} else {
|
|
||||||
// Permissions are missing
|
|
||||||
binding.cvContainerTopCard.setVisibility(View.INVISIBLE);
|
|
||||||
if(showPermissionsDialog){
|
|
||||||
checkPermissionsAndPerformAction(this,
|
|
||||||
() -> {
|
|
||||||
binding.cvContainerTopCard.setVisibility(View.VISIBLE);
|
|
||||||
this.receiveSharedItems();
|
|
||||||
},() -> {
|
|
||||||
this.showPermissionsDialog = true;
|
|
||||||
this.checkStoragePermissions();
|
|
||||||
},
|
|
||||||
R.string.storage_permission_title,
|
|
||||||
R.string.write_storage_permission_rationale_for_image_share,
|
|
||||||
getPERMISSIONS_STORAGE());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* If all permissions are not granted and a dialog is already showing on screen
|
|
||||||
showPermissionsDialog will set to false making it not show dialog again onResume,
|
|
||||||
but if user Denies any permission showPermissionsDialog will be to true
|
|
||||||
and permissions dialog will be shown again.
|
|
||||||
*/
|
|
||||||
this.showPermissionsDialog = hasAllPermissions ;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
// Resetting setImageCancelled to false
|
|
||||||
setImageCancelled(false);
|
|
||||||
super.onStop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void returnToMainActivity() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* go to the uploadProgress activity to check the status of uploading
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void goToUploadProgressActivity() {
|
|
||||||
startActivity(new Intent(this, UploadProgressActivity.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show/Hide the progress dialog
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void showProgress(final boolean shouldShow) {
|
|
||||||
if (shouldShow) {
|
|
||||||
if (!progressDialog.isShowing()) {
|
|
||||||
progressDialog.show();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (progressDialog != null && !isFinishing()) {
|
|
||||||
progressDialog.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIndexInViewFlipper(final UploadBaseFragment fragment) {
|
|
||||||
return fragments.indexOf(fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTotalNumberOfSteps() {
|
|
||||||
return fragments.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isWLMUpload() {
|
|
||||||
return place!=null && place.isMonument();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showMessage(final int messageResourceId) {
|
|
||||||
ViewUtil.showLongToast(this, messageResourceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<UploadableFile> getUploadableFiles() {
|
|
||||||
return uploadableFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showHideTopCard(final boolean shouldShow) {
|
|
||||||
binding.llContainerTopCard.setVisibility(shouldShow ? View.VISIBLE : View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUploadMediaDeleted(final int index) {
|
|
||||||
fragments.remove(index);//Remove the corresponding fragment
|
|
||||||
uploadableFiles.remove(index);//Remove the files from the list
|
|
||||||
thumbnailsAdapter.notifyItemRemoved(index); //Notify the thumbnails adapter
|
|
||||||
uploadImagesAdapter.notifyDataSetChanged(); //Notify the ViewPager
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateTopCardTitle() {
|
|
||||||
binding.tvTopCardTitle.setText(getResources()
|
|
||||||
.getQuantityString(R.plurals.upload_count_title, uploadableFiles.size(), uploadableFiles.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void makeUploadRequest() {
|
|
||||||
WorkRequestHelper.Companion.makeOneTimeWorkRequest(getApplicationContext(),
|
|
||||||
ExistingWorkPolicy.APPEND_OR_REPLACE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void askUserToLogIn() {
|
|
||||||
Timber.d("current session is null, asking user to login");
|
|
||||||
ViewUtil.showLongToast(this, getString(R.string.user_not_logged_in));
|
|
||||||
final Intent loginIntent = new Intent(UploadActivity.this, LoginActivity.class);
|
|
||||||
startActivity(loginIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRequestPermissionsResult(final int requestCode,
|
|
||||||
@NonNull final String[] permissions,
|
|
||||||
@NonNull final int[] grantResults) {
|
|
||||||
boolean areAllGranted = false;
|
|
||||||
if (requestCode == RequestCodes.STORAGE) {
|
|
||||||
if (VERSION.SDK_INT >= VERSION_CODES.M) {
|
|
||||||
for (int i = 0; i < grantResults.length; i++) {
|
|
||||||
final String permission = permissions[i];
|
|
||||||
areAllGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED;
|
|
||||||
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
|
|
||||||
final boolean showRationale = shouldShowRequestPermissionRationale(permission);
|
|
||||||
if (!showRationale) {
|
|
||||||
DialogUtil.showAlertDialog(this,
|
|
||||||
getString(R.string.storage_permissions_denied),
|
|
||||||
getString(R.string.unable_to_share_upload_item),
|
|
||||||
getString(android.R.string.ok),
|
|
||||||
this::finish);
|
|
||||||
} else {
|
|
||||||
DialogUtil.showAlertDialog(this,
|
|
||||||
getString(R.string.storage_permission_title),
|
|
||||||
getString(
|
|
||||||
R.string.write_storage_permission_rationale_for_image_share),
|
|
||||||
getString(android.R.string.ok),
|
|
||||||
this::checkStoragePermissions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (areAllGranted) {
|
|
||||||
receiveSharedItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(final boolean uploadOfAPlace) {
|
|
||||||
uploadIsOfAPlace = uploadOfAPlace;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void receiveSharedItems() {
|
|
||||||
final Intent intent = getIntent();
|
|
||||||
final String action = intent.getAction();
|
|
||||||
if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
|
||||||
receiveExternalSharedItems();
|
|
||||||
} else if (ACTION_INTERNAL_UPLOADS.equals(action)) {
|
|
||||||
receiveInternalSharedItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uploadableFiles == null || uploadableFiles.isEmpty()) {
|
|
||||||
handleNullMedia();
|
|
||||||
} else {
|
|
||||||
//Show 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);
|
|
||||||
}
|
|
||||||
binding.tvTopCardTitle.setText(getResources()
|
|
||||||
.getQuantityString(R.plurals.upload_count_title, uploadableFiles.size(), uploadableFiles.size()));
|
|
||||||
|
|
||||||
|
|
||||||
if(fragments == null){
|
|
||||||
fragments = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (final UploadableFile uploadableFile : uploadableFiles) {
|
|
||||||
final UploadMediaDetailFragment uploadMediaDetailFragment = new UploadMediaDetailFragment();
|
|
||||||
|
|
||||||
if (!uploadIsOfAPlace) {
|
|
||||||
handleLocation();
|
|
||||||
uploadMediaDetailFragment.setImageToBeUploaded(uploadableFile, place, currLocation);
|
|
||||||
locationManager.unregisterLocationManager();
|
|
||||||
} else {
|
|
||||||
uploadMediaDetailFragment.setImageToBeUploaded(uploadableFile, place, currLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
final UploadMediaDetailFragmentCallback uploadMediaDetailFragmentCallback = new UploadMediaDetailFragmentCallback() {
|
|
||||||
@Override
|
|
||||||
public void deletePictureAtIndex(final int index) {
|
|
||||||
store.putInt(keyForCurrentUploadImagesSize,
|
|
||||||
(store.getInt(keyForCurrentUploadImagesSize) - 1));
|
|
||||||
presenter.deletePictureAtIndex(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the thumbnail of an UploadableFile at the specified index.
|
|
||||||
* This method updates the list of uploadableFiles by replacing the UploadableFile
|
|
||||||
* at the given index with a new UploadableFile created from the provided file path.
|
|
||||||
* After updating the list, it notifies the RecyclerView's adapter to refresh its data,
|
|
||||||
* ensuring that the thumbnail change is reflected in the UI.
|
|
||||||
*
|
|
||||||
* @param index The index of the UploadableFile to be updated.
|
|
||||||
* @param filepath The file path of the new thumbnail image.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void changeThumbnail(final int index, final String filepath) {
|
|
||||||
uploadableFiles.remove(index);
|
|
||||||
uploadableFiles.add(index, new UploadableFile(new File(filepath)));
|
|
||||||
binding.rvThumbnails.getAdapter().notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNextButtonClicked(final int index) {
|
|
||||||
UploadActivity.this.onNextButtonClicked(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPreviousButtonClicked(final int index) {
|
|
||||||
UploadActivity.this.onPreviousButtonClicked(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showProgress(final boolean shouldShow) {
|
|
||||||
UploadActivity.this.showProgress(shouldShow);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getIndexInViewFlipper(final UploadBaseFragment fragment) {
|
|
||||||
return fragments.indexOf(fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTotalNumberOfSteps() {
|
|
||||||
return fragments.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isWLMUpload() {
|
|
||||||
return place!=null && place.isMonument();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if(isFragmentsSaved){
|
|
||||||
final UploadMediaDetailFragment fragment = (UploadMediaDetailFragment) fragments.get(0);
|
|
||||||
fragment.setCallback(uploadMediaDetailFragmentCallback);
|
|
||||||
}else{
|
|
||||||
uploadMediaDetailFragment.setCallback(uploadMediaDetailFragmentCallback);
|
|
||||||
fragments.add(uploadMediaDetailFragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//If fragments are not created, create them and add them to the fragments ArrayList
|
|
||||||
if(!isFragmentsSaved){
|
|
||||||
uploadCategoriesFragment = new UploadCategoriesFragment();
|
|
||||||
if (place != null) {
|
|
||||||
final Bundle categoryBundle = new Bundle();
|
|
||||||
categoryBundle.putString(SELECTED_NEARBY_PLACE_CATEGORY, place.getCategory());
|
|
||||||
uploadCategoriesFragment.setArguments(categoryBundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadCategoriesFragment.setCallback(this);
|
|
||||||
|
|
||||||
depictsFragment = new DepictsFragment();
|
|
||||||
final Bundle placeBundle = new Bundle();
|
|
||||||
placeBundle.putParcelable(SELECTED_NEARBY_PLACE, place);
|
|
||||||
depictsFragment.setArguments(placeBundle);
|
|
||||||
depictsFragment.setCallback(this);
|
|
||||||
|
|
||||||
mediaLicenseFragment = new MediaLicenseFragment();
|
|
||||||
mediaLicenseFragment.setCallback(this);
|
|
||||||
|
|
||||||
fragments.add(depictsFragment);
|
|
||||||
fragments.add(uploadCategoriesFragment);
|
|
||||||
fragments.add(mediaLicenseFragment);
|
|
||||||
|
|
||||||
}else{
|
|
||||||
for(int i=1;i<fragments.size();i++){
|
|
||||||
fragments.get(i).setCallback(new Callback() {
|
|
||||||
@Override
|
|
||||||
public void onNextButtonClicked(final int index) {
|
|
||||||
if (index < fragments.size() - 1) {
|
|
||||||
binding.vpUpload.setCurrentItem(index + 1, false);
|
|
||||||
fragments.get(index + 1).onBecameVisible();
|
|
||||||
((LinearLayoutManager) binding.rvThumbnails.getLayoutManager())
|
|
||||||
.scrollToPositionWithOffset((index > 0) ? index-1 : 0, 0);
|
|
||||||
} else {
|
|
||||||
presenter.handleSubmit();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onPreviousButtonClicked(final int index) {
|
|
||||||
if (index != 0) {
|
|
||||||
binding.vpUpload.setCurrentItem(index - 1, true);
|
|
||||||
fragments.get(index - 1).onBecameVisible();
|
|
||||||
((LinearLayoutManager) binding.rvThumbnails.getLayoutManager())
|
|
||||||
.scrollToPositionWithOffset((index > 3) ? index-2 : 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void showProgress(final boolean shouldShow) {
|
|
||||||
if (shouldShow) {
|
|
||||||
if (!progressDialog.isShowing()) {
|
|
||||||
progressDialog.show();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (progressDialog != null && !isFinishing()) {
|
|
||||||
progressDialog.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public int getIndexInViewFlipper(final UploadBaseFragment fragment) {
|
|
||||||
return fragments.indexOf(fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getTotalNumberOfSteps() {
|
|
||||||
return fragments.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isWLMUpload() {
|
|
||||||
return place!=null && place.isMonument();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uploadImagesAdapter.setFragments(fragments);
|
|
||||||
binding.vpUpload.setOffscreenPageLimit(fragments.size());
|
|
||||||
|
|
||||||
}
|
|
||||||
// Saving size of uploadableFiles
|
|
||||||
store.putInt(keyForCurrentUploadImagesSize, uploadableFiles.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Users may uncheck Location tag from the Manage EXIF tags setting any time.
|
|
||||||
* So, their location must not be shared in this case.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private boolean isLocationTagUncheckedInTheSettings() {
|
|
||||||
final Set<String> prefExifTags = defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS);
|
|
||||||
if (prefExifTags.contains(getString(R.string.exif_tag_location))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes current image when one image upload is cancelled, to highlight next image in the top thumbnail.
|
|
||||||
* Fixes: <a href="https://github.com/commons-app/apps-android-commons/issues/5511">Issue</a>
|
|
||||||
*
|
|
||||||
* @param index Index of image to be removed
|
|
||||||
* @param maxSize Max size of the {@code uploadableFiles}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void highlightNextImageOnCancelledImage(final int index, final int maxSize) {
|
|
||||||
if (binding.vpUpload != null && index < (maxSize)) {
|
|
||||||
binding.vpUpload.setCurrentItem(index + 1, false);
|
|
||||||
binding.vpUpload.setCurrentItem(index, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to check if user has cancelled upload of any image in current upload
|
|
||||||
* so that location compare doesn't show up again in same upload.
|
|
||||||
* Fixes: <a href="https://github.com/commons-app/apps-android-commons/issues/5511">Issue</a>
|
|
||||||
*
|
|
||||||
* @param isCancelled Is true when user has cancelled upload of any image in current upload
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setImageCancelled(final boolean isCancelled) {
|
|
||||||
final BasicKvStore basicKvStore = new BasicKvStore(this,"IsAnyImageCancelled");
|
|
||||||
basicKvStore.putBoolean("IsAnyImageCancelled", isCancelled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the difference between current location and
|
|
||||||
* location recorded before capturing the image
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private float getLocationDifference(final LatLng currLocation, final LatLng prevLocation) {
|
|
||||||
if (prevLocation == null) {
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
final float[] distance = new float[2];
|
|
||||||
Location.distanceBetween(
|
|
||||||
currLocation.getLatitude(), currLocation.getLongitude(),
|
|
||||||
prevLocation.getLatitude(), prevLocation.getLongitude(), distance);
|
|
||||||
return distance[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void receiveExternalSharedItems() {
|
|
||||||
uploadableFiles = contributionController.handleExternalImagesPicked(this, getIntent());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void receiveInternalSharedItems() {
|
|
||||||
final Intent intent = getIntent();
|
|
||||||
|
|
||||||
Timber.d("Received intent %s with action %s", intent.toString(), intent.getAction());
|
|
||||||
|
|
||||||
uploadableFiles = intent.getParcelableArrayListExtra(EXTRA_FILES);
|
|
||||||
isMultipleFilesSelected = uploadableFiles.size() > 1;
|
|
||||||
Timber.i("Received multiple upload %s", uploadableFiles.size());
|
|
||||||
|
|
||||||
place = intent.getParcelableExtra(PLACE_OBJECT);
|
|
||||||
prevLocation = intent.getParcelableExtra(LOCATION_BEFORE_IMAGE_CAPTURE);
|
|
||||||
isInAppCameraUpload = intent.getBooleanExtra(IN_APP_CAMERA_UPLOAD, false);
|
|
||||||
resetDirectPrefs();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns if multiple files selected or not.
|
|
||||||
*/
|
|
||||||
public boolean getIsMultipleFilesSelected() {
|
|
||||||
return isMultipleFilesSelected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void resetDirectPrefs() {
|
|
||||||
directKvStore.remove(PLACE_OBJECT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle null URI from the received intent.
|
|
||||||
* Current implementation will simply show a toast and finish the upload activity.
|
|
||||||
*/
|
|
||||||
private void handleNullMedia() {
|
|
||||||
ViewUtil.showLongToast(this, R.string.error_processing_image);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showAlertDialog(final int messageResourceId, @NonNull final Runnable onPositiveClick) {
|
|
||||||
DialogUtil.showAlertDialog(this,
|
|
||||||
"",
|
|
||||||
getString(messageResourceId),
|
|
||||||
getString(R.string.ok),
|
|
||||||
onPositiveClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNextButtonClicked(final int index) {
|
|
||||||
if (index < fragments.size() - 1) {
|
|
||||||
binding.vpUpload.setCurrentItem(index + 1, false);
|
|
||||||
fragments.get(index + 1).onBecameVisible();
|
|
||||||
((LinearLayoutManager) binding.rvThumbnails.getLayoutManager())
|
|
||||||
.scrollToPositionWithOffset((index > 0) ? index - 1 : 0, 0);
|
|
||||||
if (index < fragments.size() - 4) {
|
|
||||||
// check image quality if next image exists
|
|
||||||
presenter.checkImageQuality(index + 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
presenter.handleSubmit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPreviousButtonClicked(final int index) {
|
|
||||||
if (index != 0) {
|
|
||||||
binding.vpUpload.setCurrentItem(index - 1, true);
|
|
||||||
fragments.get(index - 1).onBecameVisible();
|
|
||||||
((LinearLayoutManager) binding.rvThumbnails.getLayoutManager())
|
|
||||||
.scrollToPositionWithOffset((index > 3) ? index-2 : 0, 0);
|
|
||||||
if ((index != 1) && ((index - 1) < uploadableFiles.size())) {
|
|
||||||
// Shows the top card if it was hidden because of the last image being deleted and
|
|
||||||
// now the user has hit previous button to go back to the media details
|
|
||||||
showHideTopCard(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onThumbnailDeleted(final int position) {
|
|
||||||
presenter.deletePictureAtIndex(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The adapter used to show image upload intermediate fragments
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
private static class UploadImageAdapter extends FragmentStatePagerAdapter {
|
|
||||||
List<UploadBaseFragment> fragments;
|
|
||||||
|
|
||||||
public UploadImageAdapter(final FragmentManager fragmentManager) {
|
|
||||||
super(fragmentManager);
|
|
||||||
this.fragments = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFragments(final List<UploadBaseFragment> fragments) {
|
|
||||||
this.fragments = fragments;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Fragment getItem(final int position) {
|
|
||||||
return fragments.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return fragments.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemPosition(@NonNull final Object item) {
|
|
||||||
return PagerAdapter.POSITION_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void onRlContainerTitleClicked() {
|
|
||||||
binding.rvThumbnails.setVisibility(isTitleExpanded ? View.GONE : View.VISIBLE);
|
|
||||||
isTitleExpanded = !isTitleExpanded;
|
|
||||||
binding.ibToggleTopCard.setRotation(binding.ibToggleTopCard.getRotation() + 180);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
// Resetting all values in store by clearing them
|
|
||||||
store.clearAll();
|
|
||||||
presenter.onDetachView();
|
|
||||||
compositeDisposable.clear();
|
|
||||||
fragments = null;
|
|
||||||
uploadImagesAdapter = null;
|
|
||||||
if (mediaLicenseFragment != null) {
|
|
||||||
mediaLicenseFragment.setCallback(null);
|
|
||||||
}
|
|
||||||
if (uploadCategoriesFragment != null) {
|
|
||||||
uploadCategoriesFragment.setCallback(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the value of the showPermissionDialog variable.
|
|
||||||
*
|
|
||||||
* @return {@code true} if Permission Dialog should be shown, {@code false} otherwise.
|
|
||||||
*/
|
|
||||||
public boolean isShowPermissionsDialog() {
|
|
||||||
return showPermissionsDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value of the showPermissionDialog variable.
|
|
||||||
*
|
|
||||||
* @param showPermissionsDialog {@code true} to indicate to show
|
|
||||||
* Permissions Dialog if permissions are missing, {@code false} otherwise.
|
|
||||||
*/
|
|
||||||
public void setShowPermissionsDialog(final boolean showPermissionsDialog) {
|
|
||||||
this.showPermissionsDialog = showPermissionsDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the back button to make sure the user is prepared to lose their progress
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
DialogUtil.showAlertDialog(this,
|
|
||||||
getString(R.string.back_button_warning),
|
|
||||||
getString(R.string.back_button_warning_desc),
|
|
||||||
getString(R.string.back_button_continue),
|
|
||||||
getString(R.string.back_button_warning),
|
|
||||||
null,
|
|
||||||
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
|
|
||||||
final LayoutInflater inflater = getLayoutInflater();
|
|
||||||
final View view = inflater.inflate(R.layout.activity_upload_categories_dialog, null);
|
|
||||||
final CheckBox checkBox = view.findViewById(R.id.categories_checkbox);
|
|
||||||
// Create the alert dialog
|
|
||||||
final AlertDialog alertDialog = new AlertDialog.Builder(this)
|
|
||||||
.setView(view)
|
|
||||||
.setTitle(getString(R.string.multiple_files_depiction_header))
|
|
||||||
.setMessage(getString(R.string.multiple_files_depiction))
|
|
||||||
.setCancelable(false)
|
|
||||||
.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.
|
|
||||||
*/
|
|
||||||
final 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(){
|
|
||||||
final LocationPermissionsHelper locationPermissionsHelper = new LocationPermissionsHelper(
|
|
||||||
this, locationManager, null);
|
|
||||||
if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
|
|
||||||
currLocation = locationManager.getLastLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currLocation != null) {
|
|
||||||
final float locationDifference = getLocationDifference(currLocation, prevLocation);
|
|
||||||
final 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
946
app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt
Normal file
946
app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt
Normal file
|
|
@ -0,0 +1,946 @@
|
||||||
|
package fr.free.nrw.commons.upload
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.ProgressDialog
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.location.Location
|
||||||
|
import android.location.LocationManager
|
||||||
|
import android.os.Build.VERSION
|
||||||
|
import android.os.Build.VERSION_CODES
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.CheckBox
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import fr.free.nrw.commons.R
|
||||||
|
import fr.free.nrw.commons.auth.LoginActivity
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager
|
||||||
|
import fr.free.nrw.commons.contributions.ContributionController
|
||||||
|
import fr.free.nrw.commons.databinding.ActivityUploadBinding
|
||||||
|
import fr.free.nrw.commons.filepicker.Constants.RequestCodes
|
||||||
|
import fr.free.nrw.commons.filepicker.UploadableFile
|
||||||
|
import fr.free.nrw.commons.kvstore.BasicKvStore
|
||||||
|
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||||
|
import fr.free.nrw.commons.location.LatLng
|
||||||
|
import fr.free.nrw.commons.location.LocationPermissionsHelper
|
||||||
|
import fr.free.nrw.commons.location.LocationServiceManager
|
||||||
|
import fr.free.nrw.commons.mwapi.UserClient
|
||||||
|
import fr.free.nrw.commons.nearby.Place
|
||||||
|
import fr.free.nrw.commons.settings.Prefs
|
||||||
|
import fr.free.nrw.commons.theme.BaseActivity
|
||||||
|
import fr.free.nrw.commons.upload.ThumbnailsAdapter.OnThumbnailDeletedListener
|
||||||
|
import fr.free.nrw.commons.upload.categories.UploadCategoriesFragment
|
||||||
|
import fr.free.nrw.commons.upload.depicts.DepictsFragment
|
||||||
|
import fr.free.nrw.commons.upload.license.MediaLicenseFragment
|
||||||
|
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment
|
||||||
|
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.UploadMediaDetailFragmentCallback
|
||||||
|
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaPresenter
|
||||||
|
import fr.free.nrw.commons.upload.worker.WorkRequestHelper.Companion.makeOneTimeWorkRequest
|
||||||
|
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||||
|
import fr.free.nrw.commons.utils.PermissionUtils.PERMISSIONS_STORAGE
|
||||||
|
import fr.free.nrw.commons.utils.PermissionUtils.checkPermissionsAndPerformAction
|
||||||
|
import fr.free.nrw.commons.utils.PermissionUtils.hasPartialAccess
|
||||||
|
import fr.free.nrw.commons.utils.PermissionUtils.hasPermission
|
||||||
|
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||||
|
import fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT
|
||||||
|
import fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE
|
||||||
|
import fr.free.nrw.commons.wikidata.WikidataConstants.SELECTED_NEARBY_PLACE_CATEGORY
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Named
|
||||||
|
|
||||||
|
class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.Callback,
|
||||||
|
OnThumbnailDeletedListener {
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var contributionController: ContributionController? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
@field:Named("default_preferences")
|
||||||
|
var directKvStore: JsonKvStore? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var presenter: UploadContract.UserActionListener? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var sessionManager: SessionManager? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var userClient: UserClient? = null
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
@Inject
|
||||||
|
var locationManager: LocationServiceManager? = null
|
||||||
|
|
||||||
|
private var isTitleExpanded = true
|
||||||
|
|
||||||
|
private var progressDialog: ProgressDialog? = null
|
||||||
|
private var uploadImagesAdapter: UploadImageAdapter? = null
|
||||||
|
private var fragments: MutableList<UploadBaseFragment>? = null
|
||||||
|
private var uploadCategoriesFragment: UploadCategoriesFragment? = null
|
||||||
|
private var depictsFragment: DepictsFragment? = null
|
||||||
|
private var mediaLicenseFragment: MediaLicenseFragment? = null
|
||||||
|
private var thumbnailsAdapter: ThumbnailsAdapter? = null
|
||||||
|
var store: BasicKvStore? = null
|
||||||
|
private var place: Place? = null
|
||||||
|
private var prevLocation: LatLng? = null
|
||||||
|
private var currLocation: LatLng? = null
|
||||||
|
private var isInAppCameraUpload = false
|
||||||
|
private var uploadableFiles: MutableList<UploadableFile> = mutableListOf()
|
||||||
|
private var currentSelectedPosition = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if multiple files selected or not.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Checks for if multiple files selected
|
||||||
|
*/
|
||||||
|
var isMultipleFilesSelected: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the showPermissionDialog variable.
|
||||||
|
*
|
||||||
|
* @return `true` if Permission Dialog should be shown, `false` otherwise.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Set the value of the showPermissionDialog variable.
|
||||||
|
*
|
||||||
|
* @param showPermissionsDialog `true` to indicate to show
|
||||||
|
* Permissions Dialog if permissions are missing, `false` otherwise.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* A private boolean variable to control whether a permissions dialog should be shown
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @see UploadActivity.checkStoragePermissions
|
||||||
|
*/
|
||||||
|
var isShowPermissionsDialog: Boolean = true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether fragments have been saved.
|
||||||
|
*/
|
||||||
|
private var isFragmentsSaved = false
|
||||||
|
|
||||||
|
override val totalNumberOfSteps: Int
|
||||||
|
get() = fragments!!.size
|
||||||
|
|
||||||
|
override val isWLMUpload: Boolean
|
||||||
|
get() = place != null && place!!.isMonument
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Users may uncheck Location tag from the Manage EXIF tags setting any time.
|
||||||
|
* So, their location must not be shared in this case.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private val isLocationTagUncheckedInTheSettings: Boolean
|
||||||
|
get() {
|
||||||
|
val prefExifTags: Set<String> =
|
||||||
|
defaultKvStore.getStringSet(Prefs.MANAGED_EXIF_TAGS)
|
||||||
|
return !prefExifTags.contains(getString(R.string.exif_tag_location))
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _binding: ActivityUploadBinding? = null
|
||||||
|
private val binding: ActivityUploadBinding get() = _binding!!
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
_binding = ActivityUploadBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
/*
|
||||||
|
If Configuration of device is changed then get the new fragments
|
||||||
|
created by the system and populate the fragments ArrayList
|
||||||
|
*/
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
isFragmentsSaved = true
|
||||||
|
fragments = mutableListOf<UploadBaseFragment>().apply {
|
||||||
|
supportFragmentManager.fragments.forEach { fragment ->
|
||||||
|
add(fragment as UploadBaseFragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
binding.rlContainerTitle.setOnClickListener { v: View? -> onRlContainerTitleClicked() }
|
||||||
|
nearbyPopupAnswers = mutableMapOf()
|
||||||
|
//getting the current dpi of the device and if it is less than 320dp i.e. overlapping
|
||||||
|
//threshold, thumbnails automatically minimizes
|
||||||
|
val metrics = resources.displayMetrics
|
||||||
|
val dpi = (metrics.widthPixels) / (metrics.density)
|
||||||
|
if (dpi <= 321) {
|
||||||
|
onRlContainerTitleClicked()
|
||||||
|
}
|
||||||
|
if (hasPermission(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION))) {
|
||||||
|
locationManager!!.registerLocationManager()
|
||||||
|
}
|
||||||
|
locationManager!!.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER)
|
||||||
|
locationManager!!.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER)
|
||||||
|
store = BasicKvStore(this, storeNameForCurrentUploadImagesSize).apply {
|
||||||
|
clearAll()
|
||||||
|
}
|
||||||
|
checkStoragePermissions()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun init() {
|
||||||
|
initProgressDialog()
|
||||||
|
initViewPager()
|
||||||
|
initThumbnailsRecyclerView()
|
||||||
|
//And init other things you need to
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initProgressDialog() {
|
||||||
|
progressDialog = ProgressDialog(this)
|
||||||
|
progressDialog!!.setMessage(getString(R.string.please_wait))
|
||||||
|
progressDialog!!.setCancelable(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initThumbnailsRecyclerView() {
|
||||||
|
binding.rvThumbnails.layoutManager = LinearLayoutManager(
|
||||||
|
this,
|
||||||
|
LinearLayoutManager.HORIZONTAL, false
|
||||||
|
)
|
||||||
|
thumbnailsAdapter = ThumbnailsAdapter { currentSelectedPosition }
|
||||||
|
thumbnailsAdapter!!.onThumbnailDeletedListener = this
|
||||||
|
binding.rvThumbnails.adapter = thumbnailsAdapter
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initViewPager() {
|
||||||
|
uploadImagesAdapter = UploadImageAdapter(supportFragmentManager)
|
||||||
|
binding.vpUpload.adapter = uploadImagesAdapter
|
||||||
|
binding.vpUpload.addOnPageChangeListener(object : OnPageChangeListener {
|
||||||
|
override fun onPageScrolled(
|
||||||
|
position: Int, positionOffset: Float,
|
||||||
|
positionOffsetPixels: Int
|
||||||
|
) = Unit
|
||||||
|
|
||||||
|
override fun onPageSelected(position: Int) {
|
||||||
|
currentSelectedPosition = position
|
||||||
|
if (position >= uploadableFiles!!.size) {
|
||||||
|
binding.cvContainerTopCard.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
thumbnailsAdapter!!.notifyDataSetChanged()
|
||||||
|
binding.cvContainerTopCard.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPageScrollStateChanged(state: Int) = Unit
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isLoggedIn(): Boolean = sessionManager!!.isUserLoggedIn
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
presenter!!.onAttachView(this)
|
||||||
|
if (!isLoggedIn()) {
|
||||||
|
askUserToLogIn()
|
||||||
|
}
|
||||||
|
checkBlockStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes API call to check if user is blocked from Commons. If the user is blocked, a snackbar
|
||||||
|
* is created to notify the user
|
||||||
|
*/
|
||||||
|
protected fun checkBlockStatus() {
|
||||||
|
compositeDisposable.add(
|
||||||
|
userClient!!.isUserBlockedFromCommons()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.filter { result: Boolean? -> result!! }
|
||||||
|
.subscribe { result: Boolean? ->
|
||||||
|
showAlertDialog(
|
||||||
|
this,
|
||||||
|
getString(R.string.block_notification_title),
|
||||||
|
getString(R.string.block_notification),
|
||||||
|
getString(R.string.ok)
|
||||||
|
) { finish() }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkStoragePermissions() {
|
||||||
|
// Check if all required permissions are granted
|
||||||
|
val hasAllPermissions = hasPermission(this, PERMISSIONS_STORAGE)
|
||||||
|
val hasPartialAccess = hasPartialAccess(this)
|
||||||
|
if (hasAllPermissions || hasPartialAccess) {
|
||||||
|
// All required permissions are granted, so enable UI elements and perform actions
|
||||||
|
receiveSharedItems()
|
||||||
|
binding.cvContainerTopCard.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
// Permissions are missing
|
||||||
|
binding.cvContainerTopCard.visibility = View.INVISIBLE
|
||||||
|
if (isShowPermissionsDialog) {
|
||||||
|
checkPermissionsAndPerformAction(
|
||||||
|
this,
|
||||||
|
Runnable {
|
||||||
|
binding.cvContainerTopCard.visibility = View.VISIBLE
|
||||||
|
receiveSharedItems()
|
||||||
|
}, Runnable {
|
||||||
|
isShowPermissionsDialog = true
|
||||||
|
checkStoragePermissions()
|
||||||
|
},
|
||||||
|
R.string.storage_permission_title,
|
||||||
|
R.string.write_storage_permission_rationale_for_image_share,
|
||||||
|
*PERMISSIONS_STORAGE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If all permissions are not granted and a dialog is already showing on screen
|
||||||
|
showPermissionsDialog will set to false making it not show dialog again onResume,
|
||||||
|
but if user Denies any permission showPermissionsDialog will be to true
|
||||||
|
and permissions dialog will be shown again.
|
||||||
|
*/
|
||||||
|
isShowPermissionsDialog = hasAllPermissions
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
// Resetting setImageCancelled to false
|
||||||
|
setImageCancelled(false)
|
||||||
|
super.onStop()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun returnToMainActivity() = finish()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* go to the uploadProgress activity to check the status of uploading
|
||||||
|
*/
|
||||||
|
override fun goToUploadProgressActivity() =
|
||||||
|
startActivity(Intent(this, UploadProgressActivity::class.java))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show/Hide the progress dialog
|
||||||
|
*/
|
||||||
|
override fun showProgress(shouldShow: Boolean) {
|
||||||
|
if (shouldShow) {
|
||||||
|
if (!progressDialog!!.isShowing) {
|
||||||
|
progressDialog!!.show()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (progressDialog != null && !isFinishing) {
|
||||||
|
progressDialog!!.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getIndexInViewFlipper(fragment: UploadBaseFragment?): Int =
|
||||||
|
fragments!!.indexOf(fragment)
|
||||||
|
|
||||||
|
override fun showMessage(messageResourceId: Int) {
|
||||||
|
showLongToast(this, messageResourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUploadableFiles(): List<UploadableFile>? {
|
||||||
|
return uploadableFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showHideTopCard(shouldShow: Boolean) {
|
||||||
|
binding.llContainerTopCard.visibility =
|
||||||
|
if (shouldShow) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUploadMediaDeleted(index: Int) {
|
||||||
|
fragments!!.removeAt(index) //Remove the corresponding fragment
|
||||||
|
uploadableFiles.removeAt(index) //Remove the files from the list
|
||||||
|
thumbnailsAdapter!!.notifyItemRemoved(index) //Notify the thumbnails adapter
|
||||||
|
uploadImagesAdapter!!.notifyDataSetChanged() //Notify the ViewPager
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateTopCardTitle() {
|
||||||
|
binding.tvTopCardTitle.text = resources
|
||||||
|
.getQuantityString(
|
||||||
|
R.plurals.upload_count_title,
|
||||||
|
uploadableFiles!!.size,
|
||||||
|
uploadableFiles!!.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun makeUploadRequest() {
|
||||||
|
makeOneTimeWorkRequest(
|
||||||
|
applicationContext,
|
||||||
|
ExistingWorkPolicy.APPEND_OR_REPLACE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun askUserToLogIn() {
|
||||||
|
Timber.d("current session is null, asking user to login")
|
||||||
|
showLongToast(this, getString(R.string.user_not_logged_in))
|
||||||
|
val loginIntent = Intent(this@UploadActivity, LoginActivity::class.java)
|
||||||
|
startActivity(loginIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(
|
||||||
|
requestCode: Int,
|
||||||
|
permissions: Array<String>,
|
||||||
|
grantResults: IntArray
|
||||||
|
) {
|
||||||
|
var areAllGranted = false
|
||||||
|
if (requestCode == RequestCodes.STORAGE) {
|
||||||
|
if (VERSION.SDK_INT >= VERSION_CODES.M) {
|
||||||
|
for (i in grantResults.indices) {
|
||||||
|
val permission = permissions[i]
|
||||||
|
areAllGranted = grantResults[i] == PackageManager.PERMISSION_GRANTED
|
||||||
|
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
|
||||||
|
val showRationale = shouldShowRequestPermissionRationale(permission)
|
||||||
|
if (!showRationale) {
|
||||||
|
showAlertDialog(
|
||||||
|
this,
|
||||||
|
getString(R.string.storage_permissions_denied),
|
||||||
|
getString(R.string.unable_to_share_upload_item),
|
||||||
|
getString(android.R.string.ok)
|
||||||
|
) { finish() }
|
||||||
|
} else {
|
||||||
|
showAlertDialog(
|
||||||
|
this,
|
||||||
|
getString(R.string.storage_permission_title),
|
||||||
|
getString(
|
||||||
|
R.string.write_storage_permission_rationale_for_image_share
|
||||||
|
),
|
||||||
|
getString(android.R.string.ok)
|
||||||
|
) { checkStoragePermissions() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (areAllGranted) {
|
||||||
|
receiveSharedItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun receiveSharedItems() {
|
||||||
|
val intent = intent
|
||||||
|
val action = intent.action
|
||||||
|
if (Intent.ACTION_SEND == action || Intent.ACTION_SEND_MULTIPLE == action) {
|
||||||
|
receiveExternalSharedItems()
|
||||||
|
} else if (ContributionController.ACTION_INTERNAL_UPLOADS == action) {
|
||||||
|
receiveInternalSharedItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadableFiles == null || uploadableFiles!!.isEmpty()) {
|
||||||
|
handleNullMedia()
|
||||||
|
} else {
|
||||||
|
//Show 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!!.uploadableFiles = uploadableFiles
|
||||||
|
} else {
|
||||||
|
binding.llContainerTopCard.visibility = View.GONE
|
||||||
|
}
|
||||||
|
binding.tvTopCardTitle.text = resources
|
||||||
|
.getQuantityString(
|
||||||
|
R.plurals.upload_count_title,
|
||||||
|
uploadableFiles!!.size,
|
||||||
|
uploadableFiles!!.size
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if (fragments == null) {
|
||||||
|
fragments = mutableListOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (uploadableFile in uploadableFiles!!) {
|
||||||
|
val uploadMediaDetailFragment = UploadMediaDetailFragment()
|
||||||
|
|
||||||
|
if (!uploadIsOfAPlace) {
|
||||||
|
handleLocation()
|
||||||
|
uploadMediaDetailFragment.setImageToBeUploaded(
|
||||||
|
uploadableFile,
|
||||||
|
place,
|
||||||
|
currLocation
|
||||||
|
)
|
||||||
|
locationManager!!.unregisterLocationManager()
|
||||||
|
} else {
|
||||||
|
uploadMediaDetailFragment.setImageToBeUploaded(
|
||||||
|
uploadableFile,
|
||||||
|
place,
|
||||||
|
currLocation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val uploadMediaDetailFragmentCallback: UploadMediaDetailFragmentCallback =
|
||||||
|
object : UploadMediaDetailFragmentCallback {
|
||||||
|
override fun deletePictureAtIndex(index: Int) {
|
||||||
|
store!!.putInt(
|
||||||
|
keyForCurrentUploadImagesSize,
|
||||||
|
(store!!.getInt(keyForCurrentUploadImagesSize) - 1)
|
||||||
|
)
|
||||||
|
presenter!!.deletePictureAtIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the thumbnail of an UploadableFile at the specified index.
|
||||||
|
* This method updates the list of uploadableFiles by replacing the UploadableFile
|
||||||
|
* at the given index with a new UploadableFile created from the provided file path.
|
||||||
|
* After updating the list, it notifies the RecyclerView's adapter to refresh its data,
|
||||||
|
* ensuring that the thumbnail change is reflected in the UI.
|
||||||
|
*
|
||||||
|
* @param index The index of the UploadableFile to be updated.
|
||||||
|
* @param filepath The file path of the new thumbnail image.
|
||||||
|
*/
|
||||||
|
override fun changeThumbnail(index: Int, filepath: String) {
|
||||||
|
uploadableFiles.removeAt(index)
|
||||||
|
uploadableFiles.add(index, UploadableFile(File(filepath)))
|
||||||
|
binding.rvThumbnails.adapter!!.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNextButtonClicked(index: Int) {
|
||||||
|
this@UploadActivity.onNextButtonClicked(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPreviousButtonClicked(index: Int) {
|
||||||
|
this@UploadActivity.onPreviousButtonClicked(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showProgress(shouldShow: Boolean) {
|
||||||
|
this@UploadActivity.showProgress(shouldShow)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getIndexInViewFlipper(fragment: UploadBaseFragment?): Int {
|
||||||
|
return fragments!!.indexOf(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val totalNumberOfSteps: Int
|
||||||
|
get() = fragments!!.size
|
||||||
|
|
||||||
|
override val isWLMUpload: Boolean
|
||||||
|
get() = place != null && place!!.isMonument
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFragmentsSaved) {
|
||||||
|
val fragment = fragments!![0] as UploadMediaDetailFragment?
|
||||||
|
fragment!!.setCallback(uploadMediaDetailFragmentCallback)
|
||||||
|
} else {
|
||||||
|
uploadMediaDetailFragment.setCallback(uploadMediaDetailFragmentCallback)
|
||||||
|
fragments!!.add(uploadMediaDetailFragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If fragments are not created, create them and add them to the fragments ArrayList
|
||||||
|
if (!isFragmentsSaved) {
|
||||||
|
uploadCategoriesFragment = UploadCategoriesFragment()
|
||||||
|
if (place != null) {
|
||||||
|
val categoryBundle = Bundle()
|
||||||
|
categoryBundle.putString(SELECTED_NEARBY_PLACE_CATEGORY, place!!.category)
|
||||||
|
uploadCategoriesFragment!!.arguments = categoryBundle
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadCategoriesFragment!!.callback = this
|
||||||
|
|
||||||
|
depictsFragment = DepictsFragment()
|
||||||
|
val placeBundle = Bundle()
|
||||||
|
placeBundle.putParcelable(SELECTED_NEARBY_PLACE, place)
|
||||||
|
depictsFragment!!.arguments = placeBundle
|
||||||
|
depictsFragment!!.callback = this
|
||||||
|
|
||||||
|
mediaLicenseFragment = MediaLicenseFragment()
|
||||||
|
mediaLicenseFragment!!.callback = this
|
||||||
|
|
||||||
|
fragments!!.add(depictsFragment!!)
|
||||||
|
fragments!!.add(uploadCategoriesFragment!!)
|
||||||
|
fragments!!.add(mediaLicenseFragment!!)
|
||||||
|
} else {
|
||||||
|
for (i in 1 until fragments!!.size) {
|
||||||
|
fragments!![i]!!.callback = object : UploadBaseFragment.Callback {
|
||||||
|
override fun onNextButtonClicked(index: Int) {
|
||||||
|
if (index < fragments!!.size - 1) {
|
||||||
|
binding.vpUpload.setCurrentItem(index + 1, false)
|
||||||
|
fragments!![index + 1]!!.onBecameVisible()
|
||||||
|
(binding.rvThumbnails.layoutManager as LinearLayoutManager)
|
||||||
|
.scrollToPositionWithOffset(
|
||||||
|
if ((index > 0)) index - 1 else 0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
presenter!!.handleSubmit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPreviousButtonClicked(index: Int) {
|
||||||
|
if (index != 0) {
|
||||||
|
binding.vpUpload.setCurrentItem(index - 1, true)
|
||||||
|
fragments!![index - 1]!!.onBecameVisible()
|
||||||
|
(binding.rvThumbnails.layoutManager as LinearLayoutManager)
|
||||||
|
.scrollToPositionWithOffset(
|
||||||
|
if ((index > 3)) index - 2 else 0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showProgress(shouldShow: Boolean) {
|
||||||
|
if (shouldShow) {
|
||||||
|
if (!progressDialog!!.isShowing) {
|
||||||
|
progressDialog!!.show()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (progressDialog != null && !isFinishing) {
|
||||||
|
progressDialog!!.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getIndexInViewFlipper(fragment: UploadBaseFragment?): Int {
|
||||||
|
return fragments!!.indexOf(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val totalNumberOfSteps: Int
|
||||||
|
get() = fragments!!.size
|
||||||
|
|
||||||
|
override val isWLMUpload: Boolean
|
||||||
|
get() = place != null && place!!.isMonument
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadImagesAdapter!!.fragments = fragments!!
|
||||||
|
binding.vpUpload.offscreenPageLimit = fragments!!.size
|
||||||
|
}
|
||||||
|
// Saving size of uploadableFiles
|
||||||
|
store!!.putInt(keyForCurrentUploadImagesSize, uploadableFiles!!.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes current image when one image upload is cancelled, to highlight next image in the top thumbnail.
|
||||||
|
* Fixes: [Issue](https://github.com/commons-app/apps-android-commons/issues/5511)
|
||||||
|
*
|
||||||
|
* @param index Index of image to be removed
|
||||||
|
* @param maxSize Max size of the `uploadableFiles`
|
||||||
|
*/
|
||||||
|
override fun highlightNextImageOnCancelledImage(index: Int, maxSize: Int) {
|
||||||
|
if (index < maxSize) {
|
||||||
|
binding.vpUpload.setCurrentItem(index + 1, false)
|
||||||
|
binding.vpUpload.setCurrentItem(index, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to check if user has cancelled upload of any image in current upload
|
||||||
|
* so that location compare doesn't show up again in same upload.
|
||||||
|
* Fixes: [Issue](https://github.com/commons-app/apps-android-commons/issues/5511)
|
||||||
|
*
|
||||||
|
* @param isCancelled Is true when user has cancelled upload of any image in current upload
|
||||||
|
*/
|
||||||
|
override fun setImageCancelled(isCancelled: Boolean) {
|
||||||
|
val basicKvStore = BasicKvStore(this, "IsAnyImageCancelled")
|
||||||
|
basicKvStore.putBoolean("IsAnyImageCancelled", isCancelled)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the difference between current location and
|
||||||
|
* location recorded before capturing the image
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private fun getLocationDifference(currLocation: LatLng, prevLocation: LatLng?): Float {
|
||||||
|
if (prevLocation == null) {
|
||||||
|
return 0.0f
|
||||||
|
}
|
||||||
|
val distance = FloatArray(2)
|
||||||
|
Location.distanceBetween(
|
||||||
|
currLocation.latitude, currLocation.longitude,
|
||||||
|
prevLocation.latitude, prevLocation.longitude, distance
|
||||||
|
)
|
||||||
|
return distance[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun receiveExternalSharedItems() {
|
||||||
|
uploadableFiles = contributionController!!.handleExternalImagesPicked(this, intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun receiveInternalSharedItems() {
|
||||||
|
val intent = intent
|
||||||
|
|
||||||
|
Timber.d("Received intent %s with action %s", intent.toString(), intent.action)
|
||||||
|
|
||||||
|
uploadableFiles = mutableListOf<UploadableFile>().apply {
|
||||||
|
addAll(intent.getParcelableArrayListExtra(EXTRA_FILES) ?: emptyList())
|
||||||
|
}
|
||||||
|
isMultipleFilesSelected = uploadableFiles!!.size > 1
|
||||||
|
Timber.i("Received multiple upload %s", uploadableFiles!!.size)
|
||||||
|
|
||||||
|
place = intent.getParcelableExtra<Place>(PLACE_OBJECT)
|
||||||
|
prevLocation = intent.getParcelableExtra(LOCATION_BEFORE_IMAGE_CAPTURE)
|
||||||
|
isInAppCameraUpload = intent.getBooleanExtra(IN_APP_CAMERA_UPLOAD, false)
|
||||||
|
resetDirectPrefs()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetDirectPrefs() = directKvStore!!.remove(PLACE_OBJECT)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle null URI from the received intent.
|
||||||
|
* Current implementation will simply show a toast and finish the upload activity.
|
||||||
|
*/
|
||||||
|
private fun handleNullMedia() {
|
||||||
|
showLongToast(this, R.string.error_processing_image)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun showAlertDialog(messageResourceId: Int, onPositiveClick: Runnable) {
|
||||||
|
showAlertDialog(
|
||||||
|
this,
|
||||||
|
"",
|
||||||
|
getString(messageResourceId),
|
||||||
|
getString(R.string.ok),
|
||||||
|
onPositiveClick
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNextButtonClicked(index: Int) {
|
||||||
|
if (index < fragments!!.size - 1) {
|
||||||
|
binding.vpUpload.setCurrentItem(index + 1, false)
|
||||||
|
fragments!![index + 1]!!.onBecameVisible()
|
||||||
|
(binding.rvThumbnails.layoutManager as LinearLayoutManager)
|
||||||
|
.scrollToPositionWithOffset(if ((index > 0)) index - 1 else 0, 0)
|
||||||
|
if (index < fragments!!.size - 4) {
|
||||||
|
// check image quality if next image exists
|
||||||
|
presenter!!.checkImageQuality(index + 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
presenter!!.handleSubmit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPreviousButtonClicked(index: Int) {
|
||||||
|
if (index != 0) {
|
||||||
|
binding.vpUpload.setCurrentItem(index - 1, true)
|
||||||
|
fragments!![index - 1]!!.onBecameVisible()
|
||||||
|
(binding.rvThumbnails.layoutManager as LinearLayoutManager)
|
||||||
|
.scrollToPositionWithOffset(if ((index > 3)) index - 2 else 0, 0)
|
||||||
|
if ((index != 1) && ((index - 1) < uploadableFiles!!.size)) {
|
||||||
|
// Shows the top card if it was hidden because of the last image being deleted and
|
||||||
|
// now the user has hit previous button to go back to the media details
|
||||||
|
showHideTopCard(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onThumbnailDeleted(position: Int) = presenter!!.deletePictureAtIndex(position)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The adapter used to show image upload intermediate fragments
|
||||||
|
*/
|
||||||
|
private class UploadImageAdapter(fragmentManager: FragmentManager) :
|
||||||
|
FragmentStatePagerAdapter(fragmentManager) {
|
||||||
|
var fragments: List<UploadBaseFragment> = mutableListOf()
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(position: Int): Fragment {
|
||||||
|
return fragments[position]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCount(): Int {
|
||||||
|
return fragments.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemPosition(item: Any): Int {
|
||||||
|
return POSITION_NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun onRlContainerTitleClicked() {
|
||||||
|
binding.rvThumbnails.visibility =
|
||||||
|
if (isTitleExpanded) View.GONE else View.VISIBLE
|
||||||
|
isTitleExpanded = !isTitleExpanded
|
||||||
|
binding.ibToggleTopCard.rotation = binding.ibToggleTopCard.rotation + 180
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
// Resetting all values in store by clearing them
|
||||||
|
store!!.clearAll()
|
||||||
|
presenter!!.onDetachView()
|
||||||
|
compositeDisposable.clear()
|
||||||
|
fragments = null
|
||||||
|
uploadImagesAdapter = null
|
||||||
|
if (mediaLicenseFragment != null) {
|
||||||
|
mediaLicenseFragment!!.callback = null
|
||||||
|
}
|
||||||
|
if (uploadCategoriesFragment != null) {
|
||||||
|
uploadCategoriesFragment!!.callback = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the back button to make sure the user is prepared to lose their progress
|
||||||
|
*/
|
||||||
|
override fun onBackPressed() {
|
||||||
|
showAlertDialog(
|
||||||
|
this,
|
||||||
|
getString(R.string.back_button_warning),
|
||||||
|
getString(R.string.back_button_warning_desc),
|
||||||
|
getString(R.string.back_button_continue),
|
||||||
|
getString(R.string.back_button_warning),
|
||||||
|
null
|
||||||
|
) { 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 fun showAlertDialogForCategories() {
|
||||||
|
UploadMediaPresenter.isCategoriesDialogShowing = true
|
||||||
|
// Inflate the custom layout
|
||||||
|
val inflater = layoutInflater
|
||||||
|
val view = inflater.inflate(R.layout.activity_upload_categories_dialog, null)
|
||||||
|
val checkBox = view.findViewById<CheckBox>(R.id.categories_checkbox)
|
||||||
|
// Create the alert dialog
|
||||||
|
val alertDialog = AlertDialog.Builder(this)
|
||||||
|
.setView(view)
|
||||||
|
.setTitle(getString(R.string.multiple_files_depiction_header))
|
||||||
|
.setMessage(getString(R.string.multiple_files_depiction))
|
||||||
|
.setPositiveButton("OK") { dialog: DialogInterface?, which: Int ->
|
||||||
|
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 fun showAlertForBattery() {
|
||||||
|
if (VERSION.SDK_INT >= VERSION_CODES.M) {
|
||||||
|
// When battery-optimisation dialog is shown don't show the image quality dialog
|
||||||
|
UploadMediaPresenter.isBatteryDialogShowing = true
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
val batteryOptimisationSettingsIntent = 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 fun handleLocation() {
|
||||||
|
val locationPermissionsHelper = LocationPermissionsHelper(
|
||||||
|
this, locationManager!!, null
|
||||||
|
)
|
||||||
|
if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
|
||||||
|
currLocation = locationManager!!.getLastLocation()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currLocation != null) {
|
||||||
|
val locationDifference = getLocationDifference(currLocation!!, prevLocation)
|
||||||
|
val 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var uploadIsOfAPlace = false
|
||||||
|
const val EXTRA_FILES: String = "commons_image_exta"
|
||||||
|
const val LOCATION_BEFORE_IMAGE_CAPTURE: String = "user_location_before_image_capture"
|
||||||
|
const val IN_APP_CAMERA_UPLOAD: String = "in_app_camera_upload"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores all nearby places found and related users response for
|
||||||
|
* each place while uploading media
|
||||||
|
*/
|
||||||
|
@JvmField
|
||||||
|
var nearbyPopupAnswers: MutableMap<Place, Boolean>? = null
|
||||||
|
|
||||||
|
const val keyForCurrentUploadImagesSize: String = "CurrentUploadImagesSize"
|
||||||
|
const val storeNameForCurrentUploadImagesSize: String = "CurrentUploadImageQualities"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the flag indicating whether the upload is of a specific place.
|
||||||
|
*
|
||||||
|
* @param uploadOfAPlace a boolean value indicating whether the upload is of place.
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun setUploadIsOfAPlace(uploadOfAPlace: Boolean) {
|
||||||
|
uploadIsOfAPlace = uploadOfAPlace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||||
abstract class UploadBaseFragment : CommonsDaggerSupportFragment() {
|
abstract class UploadBaseFragment : CommonsDaggerSupportFragment() {
|
||||||
var callback: Callback? = null
|
var callback: Callback? = null
|
||||||
|
|
||||||
protected open fun onBecameVisible() = Unit
|
open fun onBecameVisible() = Unit
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
val totalNumberOfSteps: Int
|
val totalNumberOfSteps: Int
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ class UploadModel @Inject internal constructor(
|
||||||
Timber.d(
|
Timber.d(
|
||||||
"Created timestamp while building contribution is %s, %s",
|
"Created timestamp while building contribution is %s, %s",
|
||||||
item.createdTimestamp,
|
item.createdTimestamp,
|
||||||
Date(item.createdTimestamp)
|
Date(item.createdTimestamp!!)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (item.createdTimestamp != -1L) {
|
if (item.createdTimestamp != -1L) {
|
||||||
|
|
|
||||||
|
|
@ -95,12 +95,10 @@ class UploadCategoriesFragment : UploadBaseFragment(), CategoriesContract.View {
|
||||||
}
|
}
|
||||||
if (media == null) {
|
if (media == null) {
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
binding!!.tvTitle.text = getString(
|
binding!!.tvTitle.text = getString(R.string.step_count,
|
||||||
R.string.step_count, callback!!.getIndexInViewFlipper(
|
callback!!.getIndexInViewFlipper(this) + 1,
|
||||||
this
|
callback!!.totalNumberOfSteps,
|
||||||
) + 1,
|
getString(R.string.categories_activity_title))
|
||||||
callback!!.totalNumberOfSteps, getString(R.string.categories_activity_title)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
binding!!.tvTitle.setText(R.string.edit_categories)
|
binding!!.tvTitle.setText(R.string.edit_categories)
|
||||||
|
|
@ -220,7 +218,7 @@ class UploadCategoriesFragment : UploadBaseFragment(), CategoriesContract.View {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun goToNextScreen() {
|
override fun goToNextScreen() {
|
||||||
callback.let { it.onNextButtonClicked(it.getIndexInViewFlipper(this)) }
|
callback?.let { it.onNextButtonClicked(it.getIndexInViewFlipper(this)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun showNoCategorySelected() {
|
override fun showNoCategorySelected() {
|
||||||
|
|
@ -322,7 +320,7 @@ class UploadCategoriesFragment : UploadBaseFragment(), CategoriesContract.View {
|
||||||
mediaDetailFragment.onResume()
|
mediaDetailFragment.onResume()
|
||||||
goBackToPreviousScreen()
|
goBackToPreviousScreen()
|
||||||
} else {
|
} else {
|
||||||
callback.let { it.onPreviousButtonClicked(it.getIndexInViewFlipper(this)) }
|
callback?.let { it.onPreviousButtonClicked(it.getIndexInViewFlipper(this)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -96,11 +96,10 @@ class DepictsFragment : UploadBaseFragment(), DepictsContract.View {
|
||||||
|
|
||||||
if (media == null) {
|
if (media == null) {
|
||||||
binding.depictsTitle.text =
|
binding.depictsTitle.text =
|
||||||
String.format(
|
String.format(getString(R.string.step_count),
|
||||||
getString(R.string.step_count), callback!!.getIndexInViewFlipper(
|
callback!!.getIndexInViewFlipper(this) + 1,
|
||||||
this
|
callback!!.totalNumberOfSteps,
|
||||||
) + 1,
|
getString(R.string.depicts_step_title)
|
||||||
callback!!.totalNumberOfSteps, getString(R.string.depicts_step_title)
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
binding.depictsTitle.setText(R.string.edit_depictions)
|
binding.depictsTitle.setText(R.string.edit_depictions)
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,7 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View {
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
binding.tvTitle.text = getString(
|
binding.tvTitle.text = getString(R.string.step_count,
|
||||||
R.string.step_count,
|
|
||||||
callback!!.getIndexInViewFlipper(this) + 1,
|
callback!!.getIndexInViewFlipper(this) + 1,
|
||||||
callback!!.totalNumberOfSteps,
|
callback!!.totalNumberOfSteps,
|
||||||
getString(R.string.license_step_title)
|
getString(R.string.license_step_title)
|
||||||
|
|
|
||||||
|
|
@ -422,7 +422,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
||||||
final Activity activity = getActivity();
|
final Activity activity = getActivity();
|
||||||
|
|
||||||
if (activity instanceof UploadActivity) {
|
if (activity instanceof UploadActivity) {
|
||||||
final boolean isMultipleFilesSelected = ((UploadActivity) activity).getIsMultipleFilesSelected();
|
final boolean isMultipleFilesSelected = ((UploadActivity) activity).isMultipleFilesSelected();
|
||||||
|
|
||||||
// Determine the message based on the selection status
|
// Determine the message based on the selection status
|
||||||
String message;
|
String message;
|
||||||
|
|
@ -476,7 +476,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
|
||||||
* This method gets called whenever the next/previous button is pressed
|
* This method gets called whenever the next/previous button is pressed
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onBecameVisible() {
|
public void onBecameVisible() {
|
||||||
super.onBecameVisible();
|
super.onBecameVisible();
|
||||||
if (callback == null) {
|
if (callback == null) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -208,7 +208,7 @@ object PermissionUtils {
|
||||||
activity.getString(android.R.string.cancel),
|
activity.getString(android.R.string.cancel),
|
||||||
{
|
{
|
||||||
if (activity is UploadActivity) {
|
if (activity is UploadActivity) {
|
||||||
activity.setShowPermissionsDialog(true)
|
activity.isShowPermissionsDialog = true
|
||||||
}
|
}
|
||||||
token.continuePermissionRequest()
|
token.continuePermissionRequest()
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue