diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt index 52be55552..9af37af36 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt @@ -1,5 +1,6 @@ package fr.free.nrw.commons.contributions +import android.annotation.SuppressLint import androidx.paging.PagedList.BoundaryCallback import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.IO_THREAD @@ -66,31 +67,34 @@ class ContributionBoundaryCallback * with either error or success. */ private fun fetchContributions(onRefreshFinish: () -> Unit = {}) { + if (sessionManager.userName != null) { - userName - ?.let { userName -> - mediaClient - .getMediaListForUser(userName) - .map { mediaList -> - mediaList.map { media -> - Contribution(media = media, state = Contribution.STATE_COMPLETED) - } - }.subscribeOn(ioThreadScheduler) - .subscribe({ list -> - saveContributionsToDB(list, onRefreshFinish) - },{ error -> - onRefreshFinish() - Timber.e( - "Failed to fetch contributions: %s", - error.message, - ) - }) - }?.let { - compositeDisposable.add( - it, - ) - } + + sessionManager.userName?.let { userName -> + + mediaClient + .getMediaListForUser(userName) + .map { mediaList -> + Timber.d("Mapping media list of size: ${mediaList.size}") + mediaList.map { media -> + Contribution(media = media, state = Contribution.STATE_COMPLETED) + } + }.subscribeOn(ioThreadScheduler) + .subscribe({ list -> + saveContributionsToDB(list, onRefreshFinish) + },{ error -> + Timber.e("Failed to fetch contributions: ${error.message}") + Timber.e(error) // Log full stack trace + onRefreshFinish() + }) + }?.let { + compositeDisposable.add(it) + } ?: run { + // Add logging if userName?.let returns null + Timber.d("userName?.let returned null") + } } else { + Timber.d("SessionManager userName is null, clearing disposable") compositeDisposable.clear() } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java index 5428c405f..219a55d48 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java @@ -129,11 +129,4 @@ public class ContributionsListPresenter implements UserActionListener { }); } - /** - * Check for new contributions by comparing the latest contribution identifier. - */ - void fetchContributions() { - contributionBoundaryCallback.refreshList(() -> Unit.INSTANCE); - } - } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index 0a9ea9f35..968f1074c 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -1,13 +1,12 @@ package fr.free.nrw.commons.contributions; -import static android.mediautil.MediaUtil.getContext; - import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -23,6 +22,7 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager.OnBackStackChangedListener; import androidx.work.ExistingWorkPolicy; import com.google.android.material.bottomnavigation.BottomNavigationView.OnNavigationItemSelectedListener; +import fr.free.nrw.commons.databinding.FragmentContributionsListBinding; import fr.free.nrw.commons.databinding.MainBinding; import fr.free.nrw.commons.R; import fr.free.nrw.commons.WelcomeActivity; @@ -42,8 +42,8 @@ import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment; import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.NearbyParentFragmentInstanceReadyCallback; import fr.free.nrw.commons.notification.NotificationActivity; -import fr.free.nrw.commons.notification.NotificationActivity.Companion; import fr.free.nrw.commons.notification.NotificationController; +import fr.free.nrw.commons.profile.ProfileActivity; import fr.free.nrw.commons.quiz.QuizChecker; import fr.free.nrw.commons.settings.SettingsFragment; import fr.free.nrw.commons.theme.BaseActivity; @@ -59,9 +59,11 @@ import io.reactivex.schedulers.Schedulers; import java.util.Calendar; import java.util.Collections; import java.util.List; +import java.util.Objects; import javax.inject.Inject; import javax.inject.Named; import kotlin.Unit; +import org.apache.commons.lang3.StringUtils; import timber.log.Timber; public class MainActivity extends BaseActivity @@ -95,8 +97,7 @@ public class MainActivity extends BaseActivity QuizChecker quizChecker; @Inject @Named("default_preferences") - public - JsonKvStore applicationKvStore; + public JsonKvStore applicationKvStore; @Inject ViewUtilWrapper viewUtilWrapper; @@ -106,6 +107,9 @@ public class MainActivity extends BaseActivity NavTabLayout tabLayout; + private FragmentContributionsListBinding refresh; + + private String userName; /** * Consumers should be simply using this method to use this activity. @@ -143,15 +147,7 @@ public class MainActivity extends BaseActivity binding.toolbarBinding.toolbar.setNavigationOnClickListener(view -> { onSupportNavigateUp(); }); - /* - "first_edit_depict" is a key for getting information about opening the depiction editor - screen for the first time after opening the app. - Getting true by the key means the depiction editor screen is opened for the first time - after opening the app. - Getting false by the key means the depiction editor screen is not opened for the first time - after opening the app. - */ applicationKvStore.putBoolean("first_edit_depict", true); if (applicationKvStore.getBoolean("login_skipped") == true) { setTitle(getString(R.string.navigation_item_explore)); @@ -162,8 +158,6 @@ public class MainActivity extends BaseActivity applicationKvStore.putBoolean("hasAlreadyLaunchedCategoriesDialog", false); } if (savedInstanceState == null) { - //starting a fresh fragment. - // Open Last opened screen if it is Contributions or Nearby, otherwise Contributions if (applicationKvStore.getBoolean("last_opened_nearby")) { setTitle(getString(R.string.nearby_fragment)); showNearby(); @@ -173,22 +167,15 @@ public class MainActivity extends BaseActivity loadFragment(ContributionsFragment.newInstance(), false); } } + refresh = FragmentContributionsListBinding.inflate(getLayoutInflater()); + if (getIntent().getExtras() != null) { + userName = getIntent().getExtras().getString(ProfileActivity.KEY_USERNAME); + } + + if (StringUtils.isEmpty(userName)) { + userName = sessionManager.getUserName(); + } setUpPager(); - /** - * Ask the user for media location access just after login - * so that location in the EXIF metadata of the images shared by the user - * is retained on devices running Android 10 or above - */ -// if (VERSION.SDK_INT >= VERSION_CODES.Q) { -// ActivityCompat.requestPermissions(this, -// new String[]{Manifest.permission.ACCESS_MEDIA_LOCATION}, 0); -// PermissionUtils.checkPermissionsAndPerformAction( -// this, -// () -> {}, -// R.string.media_location_permission_denied, -// R.string.add_location_manually, -// permission.ACCESS_MEDIA_LOCATION); -// } checkAndResumeStuckUploads(); } } @@ -201,10 +188,8 @@ public class MainActivity extends BaseActivity binding.fragmentMainNavTabLayout.setOnNavigationItemSelectedListener( navListener = (item) -> { if (!item.getTitle().equals(getString(R.string.more))) { - // do not change title for more fragment setTitle(item.getTitle()); } - // set last_opened_nearby true if item is nearby screen else set false applicationKvStore.putBoolean("last_opened_nearby", item.getTitle().equals(getString(R.string.nearby_fragment))); final Fragment fragment = NavTab.of(item.getOrder()).newInstance(); @@ -216,7 +201,6 @@ public class MainActivity extends BaseActivity loadFragment(ExploreFragment.newInstance(), false); binding.fragmentMainNavTabLayout.setOnNavigationItemSelectedListener(item -> { if (!item.getTitle().equals(getString(R.string.more))) { - // do not change title for more fragment setTitle(item.getTitle()); } Fragment fragment = NavTabLoggedOut.of(item.getOrder()).newInstance(); @@ -225,44 +209,38 @@ public class MainActivity extends BaseActivity } private boolean loadFragment(Fragment fragment, boolean showBottom) { - //showBottom so that we do not show the bottom tray again when constructing - //from the saved instance state. if (fragment instanceof ContributionsFragment) { if (activeFragment == ActiveFragment.CONTRIBUTIONS) { - // scroll to top if already on the Contributions tab contributionsFragment.scrollToTop(); return true; } contributionsFragment = (ContributionsFragment) fragment; activeFragment = ActiveFragment.CONTRIBUTIONS; } else if (fragment instanceof NearbyParentFragment) { - if (activeFragment == ActiveFragment.NEARBY) { // Do nothing if same tab + if (activeFragment == ActiveFragment.NEARBY) { return true; } nearbyParentFragment = (NearbyParentFragment) fragment; activeFragment = ActiveFragment.NEARBY; } else if (fragment instanceof ExploreFragment) { - if (activeFragment == ActiveFragment.EXPLORE) { // Do nothing if same tab + if (activeFragment == ActiveFragment.EXPLORE) { return true; } exploreFragment = (ExploreFragment) fragment; activeFragment = ActiveFragment.EXPLORE; } else if (fragment instanceof BookmarkFragment) { - if (activeFragment == ActiveFragment.BOOKMARK) { // Do nothing if same tab + if (activeFragment == ActiveFragment.BOOKMARK) { return true; } bookmarkFragment = (BookmarkFragment) fragment; activeFragment = ActiveFragment.BOOKMARK; } else if (fragment == null && showBottom) { - if (applicationKvStore.getBoolean("login_skipped") - == true) { // If logged out, more sheet is different + if (applicationKvStore.getBoolean("login_skipped") == true) { MoreBottomSheetLoggedOutFragment bottomSheet = new MoreBottomSheetLoggedOutFragment(); - bottomSheet.show(getSupportFragmentManager(), - "MoreBottomSheetLoggedOut"); + bottomSheet.show(getSupportFragmentManager(), "MoreBottomSheetLoggedOut"); } else { MoreBottomSheetFragment bottomSheet = new MoreBottomSheetFragment(); - bottomSheet.show(getSupportFragmentManager(), - "MoreBottomSheet"); + bottomSheet.show(getSupportFragmentManager(), "MoreBottomSheet"); } } @@ -284,12 +262,6 @@ public class MainActivity extends BaseActivity binding.fragmentMainNavTabLayout.setVisibility(View.VISIBLE); } - /** - * Adds number of uploads next to tab text "Contributions" then it will look like "Contributions - * (NUMBER)" - * - * @param uploadCount - */ public void setNumOfUploads(int uploadCount) { if (activeFragment == ActiveFragment.CONTRIBUTIONS) { setTitle(getResources().getString(R.string.contributions_fragment) + " " + ( @@ -301,15 +273,6 @@ public class MainActivity extends BaseActivity } } - /** - * Resume the uploads that got stuck because of the app being killed or the device being - * rebooted. - *

- * When the app is terminated or the device is restarted, contributions remain in the - * 'STATE_IN_PROGRESS' state. This status persists and doesn't change during these events. So, - * retrieving contributions labeled as 'STATE_IN_PROGRESS' from the database will provide the - * list of uploads that appear as stuck on opening the app again - */ @SuppressLint("CheckResult") private void checkAndResumeStuckUploads() { List stuckUploads = contributionDao.getContribution( @@ -333,7 +296,6 @@ public class MainActivity extends BaseActivity @Override protected void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - //quizChecker.initQuizCheck(this); } @Override @@ -371,22 +333,16 @@ public class MainActivity extends BaseActivity @Override public void onBackPressed() { if (contributionsFragment != null && activeFragment == ActiveFragment.CONTRIBUTIONS) { - // Means that contribution fragment is visible - if (!contributionsFragment.backButtonClicked()) {//If this one does not wan't to handle - // the back press, let the activity do so + if (!contributionsFragment.backButtonClicked()) { super.onBackPressed(); } } else if (nearbyParentFragment != null && activeFragment == ActiveFragment.NEARBY) { - // Means that nearby fragment is visible - /* If function nearbyParentFragment.backButtonClick() returns false, it means that the bottomsheet is - not expanded. So if the back button is pressed, then go back to the Contributions tab */ if (!nearbyParentFragment.backButtonClicked()) { getSupportFragmentManager().beginTransaction().remove(nearbyParentFragment) .commit(); setSelectedItemId(NavTab.CONTRIBUTIONS.code()); } } else if (exploreFragment != null && activeFragment == ActiveFragment.EXPLORE) { - // Means that explore fragment is visible if (!exploreFragment.onBackPressed()) { if (applicationKvStore.getBoolean("login_skipped")) { super.onBackPressed(); @@ -395,7 +351,6 @@ public class MainActivity extends BaseActivity } } } else if (bookmarkFragment != null && activeFragment == ActiveFragment.BOOKMARK) { - // Means that bookmark fragment is visible bookmarkFragment.onBackPressed(); } else { super.onBackPressed(); @@ -404,12 +359,8 @@ public class MainActivity extends BaseActivity @Override public void onBackStackChanged() { - //initBackButton(); } - /** - * Retry all failed uploads as soon as the user returns to the app - */ @SuppressLint("CheckResult") private void retryAllFailedUploads() { contributionDao. @@ -422,10 +373,6 @@ public class MainActivity extends BaseActivity }); } - /** - * Handles item selection in the options menu. This method is called when a user interacts with - * the options menu in the Top Bar. - */ @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -436,21 +383,17 @@ public class MainActivity extends BaseActivity NotificationActivity.Companion.startYourself(this, "unread"); return true; case R.id.menu_refresh: - // Refresh button handled in onCreateOptionsMenu through action layout return true; default: return super.onOptionsItemSelected(item); } } - @Override public boolean onCreateOptionsMenu(Menu menu) { - menu.clear(); // Clear the old menu to prevent duplication MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.contribution_activity_notification_menu, menu); - // Handle refresh button click MenuItem refreshItem = menu.findItem(R.id.menu_refresh); if (refreshItem != null) { View actionView = refreshItem.getActionView(); @@ -462,8 +405,22 @@ public class MainActivity extends BaseActivity Animation rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate); v.startAnimation(rotateAnimation); - // Trigger refresh logic - contributionsListPresenter.fetchContributions(); + // Initialize userName if it's null + if (userName == null) { + userName = sessionManager.getUserName(); + } + + if (Objects.equals(sessionManager.getUserName(), userName)) { + if (refresh != null && refresh.swipeRefreshLayout != null) { + refresh.swipeRefreshLayout.setRefreshing(true); + refresh.swipeRefreshLayout.setEnabled(true); + contributionsListPresenter.refreshList(refresh.swipeRefreshLayout); + } + } else { + if (refresh != null && refresh.swipeRefreshLayout != null) { + refresh.swipeRefreshLayout.setEnabled(false); + } + } }); } } @@ -486,7 +443,6 @@ public class MainActivity extends BaseActivity @Override protected void onResume() { super.onResume(); - // Check if it's the first run and the user hasn't skipped login if (applicationKvStore.getBoolean("firstrun", true) && !applicationKvStore.getBoolean("login_skipped")) { defaultKvStore.putBoolean("inAppCameraFirstRun", true); @@ -494,22 +450,33 @@ public class MainActivity extends BaseActivity } retryAllFailedUploads(); - // Background check for new contributions - contributionsListPresenter.fetchContributions(); + //check for new contributions + // Initialize userName if it's null + if (userName == null) { + userName = sessionManager.getUserName(); + } + + if (Objects.equals(sessionManager.getUserName(), userName)) { + if (refresh != null && refresh.swipeRefreshLayout != null) { + refresh.swipeRefreshLayout.setRefreshing(true); + refresh.swipeRefreshLayout.setEnabled(true); + contributionsListPresenter.refreshList(refresh.swipeRefreshLayout); + } + } else { + if (refresh != null && refresh.swipeRefreshLayout != null) { + refresh.swipeRefreshLayout.setEnabled(false); + } + } } @Override protected void onDestroy() { quizChecker.cleanup(); locationManager.unregisterLocationManager(); - // Remove ourself from hashmap to prevent memory leaks locationManager = null; super.onDestroy(); } - /** - * Public method to show nearby from the reference of this. - */ public void showNearby() { binding.fragmentMainNavTabLayout.setSelectedItemId(NavTab.NEARBY.code()); } @@ -522,9 +489,6 @@ public class MainActivity extends BaseActivity MORE } - /** - * Load default language in onCreate from SharedPreferences - */ private void loadLocale() { final SharedPreferences preferences = getSharedPreferences("Settings", Activity.MODE_PRIVATE); @@ -536,4 +500,4 @@ public class MainActivity extends BaseActivity public OnNavigationItemSelectedListener getNavListener() { return navListener; } -} +} \ No newline at end of file