inAppCameraLocationPermissionLauncher) {
+        DialogUtil.showAlertDialog(activity, activity.getString(R.string.location_permission_title),
+            activity.getString(R.string.in_app_camera_location_permission_rationale),
+            activity.getString(android.R.string.ok),
+            activity.getString(android.R.string.cancel),
+            () -> {
+                createDialogsAndHandleLocationPermissions(activity,
+                    inAppCameraLocationPermissionLauncher);
+            },
+            () -> locationPermissionCallback.onLocationPermissionDenied(
+                activity.getString(R.string.in_app_camera_location_permission_denied)),
+            null,
+            false);
+    }
+
+    /**
+     * Suggest user to attach location information with pictures. If the user selects "Yes", then:
+     * 
+     * Location is taken from the EXIF if the default camera application does not redact location
+     * tags.
+     * 
+     * Otherwise, if the EXIF metadata does not have location information, then location captured by
+     * the app is used
+     *
+     * @param activity
+     */
+    private void askUserToAllowLocationAccess(Activity activity,
+        ActivityResultLauncher inAppCameraLocationPermissionLauncher) {
+        DialogUtil.showAlertDialog(activity,
+            activity.getString(R.string.in_app_camera_location_permission_title),
+            activity.getString(R.string.in_app_camera_location_access_explanation),
+            activity.getString(R.string.option_allow),
+            activity.getString(R.string.option_dismiss),
+            () -> {
+                defaultKvStore.putBoolean("inAppCameraLocationPref", true);
+                createDialogsAndHandleLocationPermissions(activity,
+                    inAppCameraLocationPermissionLauncher);
+            },
+            () -> {
+                ViewUtil.showLongToast(activity, R.string.in_app_camera_location_permission_denied);
+                defaultKvStore.putBoolean("inAppCameraLocationPref", false);
+                initiateCameraUpload(activity);
+            },
+            null,
+            true);
     }
 
     /**
@@ -65,44 +200,36 @@ public class ContributionController {
      * Initiate gallery picker with permission
      */
     public void initiateCustomGalleryPickWithPermission(final Activity activity) {
-        setPickerConfiguration(activity,true);
+        setPickerConfiguration(activity, true);
 
         PermissionUtils.checkPermissionsAndPerformAction(activity,
-            Manifest.permission.WRITE_EXTERNAL_STORAGE,
-            () -> {
-                if (VERSION.SDK_INT >= VERSION_CODES.Q) {
-                    PermissionUtils.checkPermissionsAndPerformAction(
-                        activity,
-                        permission.ACCESS_MEDIA_LOCATION,
-                        () -> {},
-                        R.string.media_location_permission_denied,
-                        R.string.add_location_manually
-                    );
-                }
-                FilePicker.openCustomSelector(activity, 0);
-            },
+            () -> FilePicker.openCustomSelector(activity, 0),
             R.string.storage_permission_title,
-            R.string.write_storage_permission_rationale);
+            R.string.write_storage_permission_rationale,
+            PermissionUtils.PERMISSIONS_STORAGE);
     }
 
 
     /**
      * Open chooser for gallery uploads
      */
-    private void initiateGalleryUpload(final Activity activity, final boolean allowMultipleUploads) {
+    private void initiateGalleryUpload(final Activity activity,
+        final boolean allowMultipleUploads) {
         setPickerConfiguration(activity, allowMultipleUploads);
-        FilePicker.openGallery(activity, 0);
+        boolean openDocumentIntentPreferred = defaultKvStore.getBoolean(
+            "openDocumentPhotoPickerPref", true);
+        FilePicker.openGallery(activity, 0, openDocumentIntentPreferred);
     }
 
     /**
      * Sets configuration for file picker
      */
     private void setPickerConfiguration(Activity activity,
-                                        boolean allowMultipleUploads) {
+        boolean allowMultipleUploads) {
         boolean copyToExternalStorage = defaultKvStore.getBoolean("useExternalStorage", true);
         FilePicker.configuration(activity)
-                .setCopyTakenPhotosToPublicGalleryAppFolder(copyToExternalStorage)
-                .setAllowMultiplePickInGallery(allowMultipleUploads);
+            .setCopyTakenPhotosToPublicGalleryAppFolder(copyToExternalStorage)
+            .setAllowMultiplePickInGallery(allowMultipleUploads);
     }
 
     /**
@@ -110,42 +237,50 @@ public class ContributionController {
      */
     private void initiateCameraUpload(Activity activity) {
         setPickerConfiguration(activity, false);
+        if (defaultKvStore.getBoolean("inAppCameraLocationPref", false)) {
+            locationBeforeImageCapture = locationManager.getLastLocation();
+        }
+        isInAppCameraUpload = true;
         FilePicker.openCameraForImage(activity, 0);
     }
 
     /**
      * Attaches callback for file picker.
      */
-    public void handleActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
-        FilePicker.handleActivityResult(requestCode, resultCode, data, activity, new DefaultCallback() {
+    public void handleActivityResult(Activity activity, int requestCode, int resultCode,
+        Intent data) {
+        FilePicker.handleActivityResult(requestCode, resultCode, data, activity,
+            new DefaultCallback() {
 
-            @Override
-            public void onCanceled(final ImageSource source, final int type) {
-                super.onCanceled(source, type);
-                defaultKvStore.remove(PLACE_OBJECT);
-            }
+                @Override
+                public void onCanceled(final ImageSource source, final int type) {
+                    super.onCanceled(source, type);
+                    defaultKvStore.remove(PLACE_OBJECT);
+                }
 
-            @Override
-            public void onImagePickerError(Exception e, FilePicker.ImageSource source, int type) {
-                ViewUtil.showShortToast(activity, R.string.error_occurred_in_picking_images);
-            }
+                @Override
+                public void onImagePickerError(Exception e, FilePicker.ImageSource source,
+                    int type) {
+                    ViewUtil.showShortToast(activity, R.string.error_occurred_in_picking_images);
+                }
 
-            @Override
-            public void onImagesPicked(@NonNull List imagesFiles, FilePicker.ImageSource source, int type) {
-                Intent intent = handleImagesPicked(activity, imagesFiles);
-                activity.startActivity(intent);
-            }
-        });
+                @Override
+                public void onImagesPicked(@NonNull List imagesFiles,
+                    FilePicker.ImageSource source, int type) {
+                    Intent intent = handleImagesPicked(activity, imagesFiles);
+                    activity.startActivity(intent);
+                }
+            });
     }
 
     public List handleExternalImagesPicked(Activity activity,
-                                                           Intent data) {
+        Intent data) {
         return FilePicker.handleExternalImagesPicked(data, activity);
     }
 
     /**
-     * Returns intent to be passed to upload activity
-     * Attaches place object for nearby uploads
+     * Returns intent to be passed to upload activity Attaches place object for nearby uploads and
+     * location before image capture if in-app camera is used
      */
     private Intent handleImagesPicked(Context context,
         List imagesFiles) {
@@ -159,7 +294,17 @@ public class ContributionController {
             shareIntent.putExtra(PLACE_OBJECT, place);
         }
 
+        if (locationBeforeImageCapture != null) {
+            shareIntent.putExtra(
+                UploadActivity.LOCATION_BEFORE_IMAGE_CAPTURE,
+                locationBeforeImageCapture);
+        }
+
+        shareIntent.putExtra(
+            UploadActivity.IN_APP_CAMERA_UPLOAD,
+            isInAppCameraUpload
+        );
+        isInAppCameraUpload = false;    // reset the flag for next use
         return shareIntent;
     }
-
 }
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
index e7af6bcd6..b4889b6a2 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
@@ -12,7 +12,6 @@ import androidx.room.Update;
 import io.reactivex.Completable;
 import io.reactivex.Single;
 import java.util.Calendar;
-import java.util.Date;
 import java.util.List;
 
 @Dao
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java
index a7df59462..7ea5163bb 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java
@@ -12,14 +12,12 @@ import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
 import androidx.appcompat.app.AlertDialog.Builder;
 import androidx.recyclerview.widget.RecyclerView;
-import butterknife.BindView;
-import butterknife.ButterKnife;
-import butterknife.OnClick;
 import com.facebook.drawee.view.SimpleDraweeView;
 import com.facebook.imagepipeline.request.ImageRequest;
 import com.facebook.imagepipeline.request.ImageRequestBuilder;
 import fr.free.nrw.commons.R;
 import fr.free.nrw.commons.contributions.ContributionsListAdapter.Callback;
+import fr.free.nrw.commons.databinding.LayoutContributionBinding;
 import fr.free.nrw.commons.media.MediaClient;
 import io.reactivex.android.schedulers.AndroidSchedulers;
 import io.reactivex.disposables.CompositeDisposable;
@@ -29,29 +27,8 @@ import java.io.File;
 public class ContributionViewHolder extends RecyclerView.ViewHolder {
 
     private final Callback callback;
-    @BindView(R.id.contributionImage)
-    SimpleDraweeView imageView;
-    @BindView(R.id.contributionTitle)
-    TextView titleView;
-    @BindView(R.id.authorView)
-    TextView authorView;
-    @BindView(R.id.contributionState)
-    TextView stateView;
-    @BindView(R.id.contributionSequenceNumber)
-    TextView seqNumView;
-    @BindView(R.id.contributionProgress)
-    ProgressBar progressView;
-    @BindView(R.id.image_options)
-    RelativeLayout imageOptions;
-    @BindView(R.id.wikipediaButton)
-    ImageButton addToWikipediaButton;
-    @BindView(R.id.retryButton)
-    ImageButton retryButton;
-    @BindView(R.id.cancelButton)
-    ImageButton cancelButton;
-    @BindView(R.id.pauseResumeButton)
-    ImageButton pauseResumeButton;
 
+    LayoutContributionBinding binding;
 
     private int position;
     private Contribution contribution;
@@ -67,9 +44,16 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
         super(parent);
         this.parent = parent;
         this.mediaClient = mediaClient;
-        ButterKnife.bind(this, parent);
         this.callback = callback;
 
+        binding = LayoutContributionBinding.bind(parent);
+
+        binding.retryButton.setOnClickListener(v -> retryUpload());
+        binding.cancelButton.setOnClickListener(v -> deleteUpload());
+        binding.contributionImage.setOnClickListener(v -> imageClicked());
+        binding.wikipediaButton.setOnClickListener(v -> wikipediaButtonClicked());
+        binding.pauseResumeButton.setOnClickListener(v -> onPauseResumeButtonClicked());
+
         /* Set a dialog indicating that the upload is being paused. This is needed because pausing
         an upload might take a dozen seconds. */
         AlertDialog.Builder builder = new Builder(parent.getContext());
@@ -87,14 +71,17 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
 
         this.contribution = contribution;
         this.position = position;
-        titleView.setText(contribution.getMedia().getMostRelevantCaption());
-        authorView.setText(contribution.getMedia().getAuthor());
+        binding.contributionTitle.setText(contribution.getMedia().getMostRelevantCaption());
+        binding.authorView.setText(contribution.getMedia().getAuthor());
 
         //Removes flicker of loading image.
-        imageView.getHierarchy().setFadeDuration(0);
+        binding.contributionImage.getHierarchy().setFadeDuration(0);
 
-        imageView.getHierarchy().setPlaceholderImage(R.drawable.image_placeholder);
-        imageView.getHierarchy().setFailureImage(R.drawable.image_placeholder);
+        binding.contributionImage.getHierarchy().setPlaceholderImage(R.drawable.image_placeholder);
+        binding.contributionImage.getHierarchy().setFailureImage(R.drawable.image_placeholder);
+        
+        
+        
 
         final String imageSource = chooseImageSource(contribution.getMedia().getThumbUrl(),
             contribution.getLocalUri());
@@ -103,73 +90,77 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
                 imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageSource))
                     .setProgressiveRenderingEnabled(true)
                     .build();
-            } else if(imageSource != null) {
+            }
+            else if (URLUtil.isFileUrl(imageSource)){
+                imageRequest=ImageRequest.fromUri(Uri.parse(imageSource));
+            }
+            else if(imageSource != null) {
                 final File file = new File(imageSource);
                 imageRequest = ImageRequest.fromFile(file);
             }
 
             if(imageRequest != null){
-                imageView.setImageRequest(imageRequest);
+                binding.contributionImage.setImageRequest(imageRequest);
             }
         }
 
-        seqNumView.setText(String.valueOf(position + 1));
-        seqNumView.setVisibility(View.VISIBLE);
+        binding.contributionSequenceNumber.setText(String.valueOf(position + 1));
+        binding.contributionSequenceNumber.setVisibility(View.VISIBLE);
 
-        addToWikipediaButton.setVisibility(View.GONE);
+        binding.wikipediaButton.setVisibility(View.GONE);
         switch (contribution.getState()) {
             case Contribution.STATE_COMPLETED:
-                stateView.setVisibility(View.GONE);
-                progressView.setVisibility(View.GONE);
-                imageOptions.setVisibility(View.GONE);
-                stateView.setText("");
+                binding.contributionState.setVisibility(View.GONE);
+                binding.contributionProgress.setVisibility(View.GONE);
+                binding.imageOptions.setVisibility(View.GONE);
+                binding.contributionState.setText("");
                 checkIfMediaExistsOnWikipediaPage(contribution);
                 break;
             case Contribution.STATE_QUEUED:
             case Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE:
-                progressView.setVisibility(View.GONE);
-                stateView.setVisibility(View.VISIBLE);
-                stateView.setText(R.string.contribution_state_queued);
-                imageOptions.setVisibility(View.GONE);
+                binding.contributionProgress.setVisibility(View.GONE);
+                binding.contributionState.setVisibility(View.VISIBLE);
+                binding.contributionState.setText(R.string.contribution_state_queued);
+                binding.imageOptions.setVisibility(View.GONE);
                 break;
             case Contribution.STATE_IN_PROGRESS:
-                stateView.setVisibility(View.GONE);
-                progressView.setVisibility(View.VISIBLE);
-                addToWikipediaButton.setVisibility(View.GONE);
-                pauseResumeButton.setVisibility(View.VISIBLE);
-                cancelButton.setVisibility(View.GONE);
-                retryButton.setVisibility(View.GONE);
-                imageOptions.setVisibility(View.VISIBLE);
+                binding.contributionState.setVisibility(View.GONE);
+                binding.contributionProgress.setVisibility(View.VISIBLE);
+                binding.wikipediaButton.setVisibility(View.GONE);
+                binding.pauseResumeButton.setVisibility(View.VISIBLE);
+                binding.cancelButton.setVisibility(View.GONE);
+                binding.retryButton.setVisibility(View.GONE);
+                binding.imageOptions.setVisibility(View.VISIBLE);
                 final long total = contribution.getDataLength();
                 final long transferred = contribution.getTransferred();
                 if (transferred == 0 || transferred >= total) {
-                    progressView.setIndeterminate(true);
+                    binding.contributionProgress.setIndeterminate(true);
                 } else {
-                    progressView.setIndeterminate(false);
-                    progressView.setProgress((int) (((double) transferred / (double) total) * 100));
+                    binding.contributionProgress.setIndeterminate(false);
+                    binding.contributionProgress.setProgress((int) (((double) transferred / (double) total) * 100));
                 }
                 break;
             case Contribution.STATE_PAUSED:
-                progressView.setVisibility(View.GONE);
-                stateView.setVisibility(View.VISIBLE);
-                stateView.setText(R.string.paused);
-                cancelButton.setVisibility(View.VISIBLE);
-                retryButton.setVisibility(View.GONE);
-                pauseResumeButton.setVisibility(View.VISIBLE);
-                imageOptions.setVisibility(View.VISIBLE);
+                binding.contributionProgress.setVisibility(View.GONE);
+                binding.contributionState.setVisibility(View.VISIBLE);
+                binding.contributionState.setText(R.string.paused);
+                binding.cancelButton.setVisibility(View.VISIBLE);
+                binding.retryButton.setVisibility(View.GONE);
+                binding.pauseResumeButton.setVisibility(View.VISIBLE);
+                binding.imageOptions.setVisibility(View.VISIBLE);
                 setResume();
                 if(pausingPopUp.isShowing()){
                     pausingPopUp.hide();
                 }
                 break;
             case Contribution.STATE_FAILED:
-                stateView.setVisibility(View.VISIBLE);
-                stateView.setText(R.string.contribution_state_failed);
-                progressView.setVisibility(View.GONE);
-                cancelButton.setVisibility(View.VISIBLE);
-                retryButton.setVisibility(View.VISIBLE);
-                pauseResumeButton.setVisibility(View.GONE);
-                imageOptions.setVisibility(View.VISIBLE);
+                binding.contributionState.setVisibility(View.VISIBLE);
+                binding.contributionState.setText(R.string.contribution_state_failed);
+                binding.contributionProgress.setVisibility(View.GONE);
+                binding.cancelButton.setVisibility(View.VISIBLE);
+                binding.retryButton.setVisibility(View.VISIBLE);
+                binding.pauseResumeButton.setVisibility(View.GONE);
+                binding.imageOptions.setVisibility(View.VISIBLE);
                 break;
         }
     }
@@ -203,11 +194,11 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
      */
     private void displayWikipediaButton(Boolean mediaExists) {
         if (!mediaExists) {
-            addToWikipediaButton.setVisibility(View.VISIBLE);
+            binding.wikipediaButton.setVisibility(View.VISIBLE);
             isWikipediaButtonDisplayed = true;
-            cancelButton.setVisibility(View.GONE);
-            retryButton.setVisibility(View.GONE);
-            imageOptions.setVisibility(View.VISIBLE);
+            binding.cancelButton.setVisibility(View.GONE);
+            binding.retryButton.setVisibility(View.GONE);
+            binding.imageOptions.setVisibility(View.VISIBLE);
         }
     }
 
@@ -229,7 +220,6 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
     /**
      * Retry upload when it is failed
      */
-    @OnClick(R.id.retryButton)
     public void retryUpload() {
         callback.retryUpload(contribution);
     }
@@ -237,17 +227,14 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
     /**
      * Delete a failed upload attempt
      */
-    @OnClick(R.id.cancelButton)
     public void deleteUpload() {
         callback.deleteUpload(contribution);
     }
 
-    @OnClick(R.id.contributionImage)
     public void imageClicked() {
         callback.openMediaDetail(position, isWikipediaButtonDisplayed);
     }
 
-    @OnClick(R.id.wikipediaButton)
     public void wikipediaButtonClicked() {
         callback.addImageToWikipedia(contribution);
     }
@@ -255,9 +242,8 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
     /**
      * Triggers a callback for pause/resume
      */
-    @OnClick(R.id.pauseResumeButton)
     public void onPauseResumeButtonClicked() {
-        if (pauseResumeButton.getTag().toString().equals("pause")) {
+        if (binding.pauseResumeButton.getTag().toString().equals("pause")) {
             pause();
         } else {
             resume();
@@ -279,16 +265,16 @@ public class ContributionViewHolder extends RecyclerView.ViewHolder {
      * Update pause/resume button to show pause state
      */
     private void setPaused() {
-        pauseResumeButton.setImageResource(R.drawable.pause_icon);
-        pauseResumeButton.setTag(parent.getContext().getString(R.string.pause));
+        binding.pauseResumeButton.setImageResource(R.drawable.pause_icon);
+        binding.pauseResumeButton.setTag(parent.getContext().getString(R.string.pause));
     }
 
     /**
      * Update pause/resume button to show resume state
      */
     private void setResume() {
-        pauseResumeButton.setImageResource(R.drawable.play_icon);
-        pauseResumeButton.setTag(parent.getContext().getString(R.string.resume));
+        binding.pauseResumeButton.setImageResource(R.drawable.play_icon);
+        binding.pauseResumeButton.setTag(parent.getContext().getString(R.string.resume));
     }
 
     public ImageRequest getImageRequest() {
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
index 03e3e5611..189b2665f 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
@@ -1,14 +1,21 @@
 package fr.free.nrw.commons.contributions;
 
+import static android.content.Context.SENSOR_SERVICE;
 import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
 import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED;
 import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL;
 import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME;
+import static fr.free.nrw.commons.utils.LengthUtils.computeBearing;
 import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
 
 import android.Manifest;
+import android.Manifest.permission;
 import android.annotation.SuppressLint;
 import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -21,6 +28,9 @@ import android.widget.CheckBox;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 import android.widget.Toast;
+import androidx.activity.result.ActivityResultCallback;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
@@ -29,17 +39,17 @@ import androidx.fragment.app.FragmentTransaction;
 import fr.free.nrw.commons.CommonsApplication;
 import fr.free.nrw.commons.Utils;
 import fr.free.nrw.commons.auth.SessionManager;
+import fr.free.nrw.commons.databinding.FragmentContributionsBinding;
 import fr.free.nrw.commons.notification.models.Notification;
 import fr.free.nrw.commons.notification.NotificationController;
 import fr.free.nrw.commons.profile.ProfileActivity;
 import fr.free.nrw.commons.theme.BaseActivity;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import javax.inject.Inject;
 import javax.inject.Named;
 import androidx.work.WorkManager;
-import butterknife.BindView;
-import butterknife.ButterKnife;
 import fr.free.nrw.commons.Media;
 import fr.free.nrw.commons.R;
 import fr.free.nrw.commons.campaigns.models.Campaign;
@@ -73,18 +83,27 @@ import io.reactivex.schedulers.Schedulers;
 import timber.log.Timber;
 
 public class ContributionsFragment
-        extends CommonsDaggerSupportFragment
-        implements
-        OnBackStackChangedListener,
-        LocationUpdateListener,
+    extends CommonsDaggerSupportFragment
+    implements
+    OnBackStackChangedListener,
+    LocationUpdateListener,
     MediaDetailProvider,
-    ICampaignsView, ContributionsContract.View, Callback{
-    @Inject @Named("default_preferences") JsonKvStore store;
-    @Inject NearbyController nearbyController;
-    @Inject OkHttpJsonApiClient okHttpJsonApiClient;
-    @Inject CampaignsPresenter presenter;
-    @Inject LocationServiceManager locationManager;
-    @Inject NotificationController notificationController;
+    SensorEventListener,
+    ICampaignsView, ContributionsContract.View, Callback {
+
+    @Inject
+    @Named("default_preferences")
+    JsonKvStore store;
+    @Inject
+    NearbyController nearbyController;
+    @Inject
+    OkHttpJsonApiClient okHttpJsonApiClient;
+    @Inject
+    CampaignsPresenter presenter;
+    @Inject
+    LocationServiceManager locationManager;
+    @Inject
+    NotificationController notificationController;
 
     private CompositeDisposable compositeDisposable = new CompositeDisposable();
 
@@ -92,20 +111,18 @@ public class ContributionsFragment
     private static final String CONTRIBUTION_LIST_FRAGMENT_TAG = "ContributionListFragmentTag";
     private MediaDetailPagerFragment mediaDetailPagerFragment;
     static final String MEDIA_DETAIL_PAGER_FRAGMENT_TAG = "MediaDetailFragmentTag";
+    private static final int MAX_RETRIES = 10;
 
-    @BindView(R.id.card_view_nearby) public NearbyNotificationCardView nearbyNotificationCardView;
-    @BindView(R.id.campaigns_view) CampaignView campaignView;
-    @BindView(R.id.limited_connection_enabled_layout) LinearLayout limitedConnectionEnabledLayout;
-    @BindView(R.id.limited_connection_description_text_view) TextView limitedConnectionDescriptionTextView;
+
+    public FragmentContributionsBinding binding;
 
     @Inject ContributionsPresenter contributionsPresenter;
 
     @Inject
     SessionManager sessionManager;
 
-    private LatLng curLatLng;
+    private LatLng currentLatLng;
 
-    private boolean firstLocationUpdate = true;
     private boolean isFragmentAttachedBefore = false;
     private View checkBoxView;
     private CheckBox checkBox;
@@ -117,6 +134,34 @@ public class ContributionsFragment
     String userName;
     private boolean isUserProfile;
 
+    private SensorManager mSensorManager;
+    private Sensor mLight;
+    private float direction;
+    private ActivityResultLauncher nearbyLocationPermissionLauncher = registerForActivityResult(
+        new ActivityResultContracts.RequestMultiplePermissions(),
+        new ActivityResultCallback