mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-28 13:23:58 +01:00
When opening the app, in the background (without any UI impact such as loading spinwheel) we check whether the server's last image is the app's last image.
If not, refresh. New "refresh" button having the same effect as swipe down.
This commit is contained in:
parent
9f9938a0b7
commit
288f9aae3f
8 changed files with 189 additions and 123 deletions
|
|
@ -59,6 +59,12 @@ class ContributionBoundaryCallback
|
||||||
}
|
}
|
||||||
fetchContributions(onRefreshFinish)
|
fetchContributions(onRefreshFinish)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Public method to fetch contributions, which internally calls the private fetchContributions().
|
||||||
|
*/
|
||||||
|
fun fetchContributionsPublic(onRefreshFinish: () -> Unit = {}) {
|
||||||
|
fetchContributions(onRefreshFinish)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches contributions using the MediaWiki API
|
* Fetches contributions using the MediaWiki API
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,6 @@ package fr.free.nrw.commons.contributions;
|
||||||
|
|
||||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
@ -13,6 +10,7 @@ import androidx.paging.DataSource.Factory;
|
||||||
import androidx.paging.LivePagedListBuilder;
|
import androidx.paging.LivePagedListBuilder;
|
||||||
import androidx.paging.PagedList;
|
import androidx.paging.PagedList;
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.contributions.ContributionsListContract.UserActionListener;
|
import fr.free.nrw.commons.contributions.ContributionsListContract.UserActionListener;
|
||||||
import io.reactivex.Scheduler;
|
import io.reactivex.Scheduler;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
|
@ -22,6 +20,7 @@ import java.util.List;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The presenter class for Contributions
|
* The presenter class for Contributions
|
||||||
|
|
@ -33,7 +32,11 @@ public class ContributionsListPresenter implements UserActionListener {
|
||||||
private final Scheduler ioThreadScheduler;
|
private final Scheduler ioThreadScheduler;
|
||||||
|
|
||||||
private final CompositeDisposable compositeDisposable;
|
private final CompositeDisposable compositeDisposable;
|
||||||
private final ContributionsRemoteDataSource contributionsRemoteDataSource;
|
@Inject
|
||||||
|
ContributionsRemoteDataSource contributionsRemoteDataSource;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SessionManager sessionManager;
|
||||||
|
|
||||||
LiveData<PagedList<Contribution>> contributionList;
|
LiveData<PagedList<Contribution>> contributionList;
|
||||||
|
|
||||||
|
|
@ -41,11 +44,6 @@ public class ContributionsListPresenter implements UserActionListener {
|
||||||
|
|
||||||
private List<Contribution> existingContributions = new ArrayList<>();
|
private List<Contribution> existingContributions = new ArrayList<>();
|
||||||
|
|
||||||
// Timer for polling new contributions
|
|
||||||
private Handler pollingHandler;
|
|
||||||
private Runnable pollingRunnable;
|
|
||||||
private long pollingInterval = 24 * 60 * 60 * 1000L; // Poll every day
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ContributionsListPresenter(
|
ContributionsListPresenter(
|
||||||
final ContributionBoundaryCallback contributionBoundaryCallback,
|
final ContributionBoundaryCallback contributionBoundaryCallback,
|
||||||
|
|
@ -107,9 +105,7 @@ public class ContributionsListPresenter implements UserActionListener {
|
||||||
existingContributions.addAll(pagedList);
|
existingContributions.addAll(pagedList);
|
||||||
liveData.setValue(existingContributions); // Update liveData with the latest list
|
liveData.setValue(existingContributions); // Update liveData with the latest list
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Start polling for new contributions
|
|
||||||
startPollingForNewContributions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -117,7 +113,6 @@ public class ContributionsListPresenter implements UserActionListener {
|
||||||
compositeDisposable.clear();
|
compositeDisposable.clear();
|
||||||
contributionsRemoteDataSource.dispose();
|
contributionsRemoteDataSource.dispose();
|
||||||
contributionBoundaryCallback.dispose();
|
contributionBoundaryCallback.dispose();
|
||||||
stopPollingForNewContributions();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -134,65 +129,42 @@ public class ContributionsListPresenter implements UserActionListener {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Start polling for new contributions every 24 hour.
|
|
||||||
*/
|
|
||||||
private void startPollingForNewContributions() {
|
|
||||||
if (pollingHandler != null) {
|
|
||||||
stopPollingForNewContributions();
|
|
||||||
}
|
|
||||||
|
|
||||||
pollingHandler = new Handler(Looper.getMainLooper());
|
|
||||||
pollingRunnable = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
checkForNewContributions();
|
|
||||||
pollingHandler.postDelayed(this, pollingInterval); // Repeat after the interval
|
|
||||||
}
|
|
||||||
};
|
|
||||||
pollingHandler.post(pollingRunnable); // Start polling immediately
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the polling task when the view is detached or the activity is paused.
|
|
||||||
*/
|
|
||||||
private void stopPollingForNewContributions() {
|
|
||||||
if (pollingHandler != null && pollingRunnable != null) {
|
|
||||||
pollingHandler.removeCallbacks(pollingRunnable);
|
|
||||||
pollingHandler = null;
|
|
||||||
pollingRunnable = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private String lastKnownIdentifier = null; // Declare and initialize
|
private String lastKnownIdentifier = null; // Declare and initialize
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for new contributions by comparing the latest contribution identifier.
|
* Check for new contributions by comparing the latest contribution identifier.
|
||||||
*/
|
*/
|
||||||
private void checkForNewContributions() {
|
void checkForNewContributions() {
|
||||||
|
|
||||||
|
// Set the username before fetching contributions
|
||||||
|
contributionsRemoteDataSource.setUserName(sessionManager.getUserName());
|
||||||
|
|
||||||
contributionsRemoteDataSource.fetchLatestContributionIdentifier(latestIdentifier -> {
|
contributionsRemoteDataSource.fetchLatestContributionIdentifier(latestIdentifier -> {
|
||||||
if (latestIdentifier != null && !latestIdentifier.equals(lastKnownIdentifier)) {
|
if (latestIdentifier != null && !latestIdentifier.equals(lastKnownIdentifier)) {
|
||||||
lastKnownIdentifier = latestIdentifier;
|
lastKnownIdentifier = latestIdentifier;
|
||||||
fetchAllContributions(); // Fetch the full list of contributions
|
fetchAllContributions(); // Fetch the full list of contributions
|
||||||
}
|
}
|
||||||
return Unit.INSTANCE; // Explicitly return Unit for Kotlin compatibility
|
return Unit.INSTANCE;
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch new contributions from the server and append them to the existing list.
|
* Fetch new contributions from the server and append them to the existing list.
|
||||||
*/
|
*/
|
||||||
private void fetchAllContributions() {
|
void fetchAllContributions() {
|
||||||
contributionsRemoteDataSource.fetchAllContributions(new ContributionsRemoteDataSource.LoadCallback<Contribution>() {
|
contributionsRemoteDataSource.fetchContributions(
|
||||||
@Override
|
new ContributionsRemoteDataSource.LoadCallback<>() {
|
||||||
public void onResult(List<Contribution> newContributions) {
|
|
||||||
if (newContributions != null && !newContributions.isEmpty()) {
|
@Override
|
||||||
existingContributions.clear();
|
public void onResult(List<Contribution> newContributions) {
|
||||||
existingContributions.addAll(newContributions);
|
if (newContributions != null && !newContributions.isEmpty()) {
|
||||||
liveData.postValue(existingContributions); // Update liveData with the new list
|
existingContributions.clear();
|
||||||
|
existingContributions.addAll(newContributions);
|
||||||
|
liveData.postValue(
|
||||||
|
existingContributions); // Update liveData with the new list
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import androidx.paging.ItemKeyedDataSource
|
||||||
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.IO_THREAD
|
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.IO_THREAD
|
||||||
import fr.free.nrw.commons.media.MediaClient
|
import fr.free.nrw.commons.media.MediaClient
|
||||||
import io.reactivex.Scheduler
|
import io.reactivex.Scheduler
|
||||||
|
import io.reactivex.Single
|
||||||
import io.reactivex.disposables.CompositeDisposable
|
import io.reactivex.disposables.CompositeDisposable
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
@ -13,65 +14,65 @@ import javax.inject.Named
|
||||||
* Data-Source which acts as mediator for contributions-data from the API
|
* Data-Source which acts as mediator for contributions-data from the API
|
||||||
*/
|
*/
|
||||||
class ContributionsRemoteDataSource
|
class ContributionsRemoteDataSource
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
private val mediaClient: MediaClient,
|
private val mediaClient: MediaClient,
|
||||||
@param:Named(IO_THREAD) private val ioThreadScheduler: Scheduler,
|
@param:Named(IO_THREAD) private val ioThreadScheduler: Scheduler,
|
||||||
) : ItemKeyedDataSource<Int, Contribution>() {
|
) : ItemKeyedDataSource<Int, Contribution>() {
|
||||||
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
|
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||||
var userName: String? = null
|
var userName: String? = null
|
||||||
|
|
||||||
override fun loadInitial(
|
override fun loadInitial(
|
||||||
params: LoadInitialParams<Int>,
|
params: LoadInitialParams<Int>,
|
||||||
callback: LoadInitialCallback<Contribution>,
|
callback: LoadInitialCallback<Contribution>,
|
||||||
) {
|
) {
|
||||||
fetchAllContributions(callback)
|
fetchContributions(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadAfter(
|
||||||
|
params: LoadParams<Int>,
|
||||||
|
callback: LoadCallback<Contribution>,
|
||||||
|
) {
|
||||||
|
fetchContributions(callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun loadBefore(
|
||||||
|
params: LoadParams<Int>,
|
||||||
|
callback: LoadCallback<Contribution>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getKey(item: Contribution): Int = item.pageId.hashCode()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches contributions using the MediaWiki API
|
||||||
|
*/
|
||||||
|
fun fetchContributions(callback: LoadCallback<Contribution>) {
|
||||||
|
if (userName.isNullOrEmpty()) {
|
||||||
|
Timber.e("Failed to fetch contributions: userName is null or empty")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
Timber.d("Fetching contributions for user: $userName")
|
||||||
|
|
||||||
override fun loadAfter(
|
compositeDisposable.add(
|
||||||
params: LoadParams<Int>,
|
mediaClient
|
||||||
callback: LoadCallback<Contribution>,
|
.getMediaListForUser(userName!!)
|
||||||
) {
|
.map { mediaList ->
|
||||||
fetchAllContributions(callback)
|
mediaList.map {
|
||||||
}
|
Contribution(media = it, state = Contribution.STATE_COMPLETED)
|
||||||
|
}
|
||||||
|
}.subscribeOn(ioThreadScheduler)
|
||||||
|
.subscribe({
|
||||||
|
callback.onResult(it)
|
||||||
|
}) { error: Throwable ->
|
||||||
|
Timber.e(
|
||||||
|
"Failed to fetch contributions: %s",
|
||||||
|
error.message,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun loadBefore(
|
|
||||||
params: LoadParams<Int>,
|
|
||||||
callback: LoadCallback<Contribution>,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getKey(item: Contribution): Int = item.pageId.hashCode()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches contributions using the MediaWiki API
|
|
||||||
*/
|
|
||||||
fun fetchAllContributions(callback: LoadCallback<Contribution>) {
|
|
||||||
if (userName.isNullOrEmpty()) {
|
|
||||||
Timber.e("Failed to fetch contributions: userName is null or empty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Timber.d("Fetching contributions for user: $userName")
|
|
||||||
|
|
||||||
compositeDisposable.add(
|
|
||||||
mediaClient
|
|
||||||
.getMediaListForUser(userName!!)
|
|
||||||
.map { mediaList ->
|
|
||||||
mediaList.map {
|
|
||||||
Contribution(media = it, state = Contribution.STATE_COMPLETED)
|
|
||||||
}
|
|
||||||
}.subscribeOn(ioThreadScheduler)
|
|
||||||
.subscribe({ contributions ->
|
|
||||||
// Pass the contributions to the callback
|
|
||||||
callback.onResult(contributions)
|
|
||||||
}) { error: Throwable ->
|
|
||||||
Timber.e(
|
|
||||||
"Failed to fetch contributions: %s",
|
|
||||||
error.message,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Fetches the latest contribution identifier only
|
* Fetches the latest contribution identifier only
|
||||||
*/
|
*/
|
||||||
|
|
@ -97,7 +98,7 @@ class ContributionsRemoteDataSource
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dispose() {
|
fun dispose() {
|
||||||
compositeDisposable.dispose()
|
compositeDisposable.dispose()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package fr.free.nrw.commons.contributions;
|
package fr.free.nrw.commons.contributions;
|
||||||
|
|
||||||
|
import static android.mediautil.MediaUtil.getContext;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -7,13 +9,19 @@ import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.AnimationUtils;
|
||||||
|
import android.widget.ImageView;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
|
||||||
import androidx.work.ExistingWorkPolicy;
|
import androidx.work.ExistingWorkPolicy;
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView.OnNavigationItemSelectedListener;
|
||||||
import fr.free.nrw.commons.databinding.MainBinding;
|
import fr.free.nrw.commons.databinding.MainBinding;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.WelcomeActivity;
|
import fr.free.nrw.commons.WelcomeActivity;
|
||||||
|
|
@ -22,6 +30,7 @@ import fr.free.nrw.commons.bookmarks.BookmarkFragment;
|
||||||
import fr.free.nrw.commons.explore.ExploreFragment;
|
import fr.free.nrw.commons.explore.ExploreFragment;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||||
|
import fr.free.nrw.commons.media.MediaClient;
|
||||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||||
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment;
|
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment;
|
||||||
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment;
|
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment;
|
||||||
|
|
@ -32,6 +41,7 @@ import fr.free.nrw.commons.nearby.Place;
|
||||||
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment;
|
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment;
|
||||||
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.NearbyParentFragmentInstanceReadyCallback;
|
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.NearbyParentFragmentInstanceReadyCallback;
|
||||||
import fr.free.nrw.commons.notification.NotificationActivity;
|
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.notification.NotificationController;
|
||||||
import fr.free.nrw.commons.quiz.QuizChecker;
|
import fr.free.nrw.commons.quiz.QuizChecker;
|
||||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||||
|
|
@ -41,16 +51,20 @@ import fr.free.nrw.commons.upload.worker.WorkRequestHelper;
|
||||||
import fr.free.nrw.commons.utils.PermissionUtils;
|
import fr.free.nrw.commons.utils.PermissionUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
||||||
import io.reactivex.Completable;
|
import io.reactivex.Completable;
|
||||||
|
import io.reactivex.Scheduler;
|
||||||
|
import io.reactivex.Single;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import kotlin.Unit;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class MainActivity extends BaseActivity
|
public class MainActivity extends BaseActivity
|
||||||
implements FragmentManager.OnBackStackChangedListener {
|
implements OnBackStackChangedListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SessionManager sessionManager;
|
SessionManager sessionManager;
|
||||||
|
|
@ -59,13 +73,18 @@ public class MainActivity extends BaseActivity
|
||||||
@Inject
|
@Inject
|
||||||
ContributionDao contributionDao;
|
ContributionDao contributionDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ContributionsListPresenter contributionsListPresenter;
|
||||||
|
@Inject
|
||||||
|
ContributionsRemoteDataSource dataSource;
|
||||||
|
|
||||||
private ContributionsFragment contributionsFragment;
|
private ContributionsFragment contributionsFragment;
|
||||||
private NearbyParentFragment nearbyParentFragment;
|
private NearbyParentFragment nearbyParentFragment;
|
||||||
private ExploreFragment exploreFragment;
|
private ExploreFragment exploreFragment;
|
||||||
private BookmarkFragment bookmarkFragment;
|
private BookmarkFragment bookmarkFragment;
|
||||||
public ActiveFragment activeFragment;
|
public ActiveFragment activeFragment;
|
||||||
private MediaDetailPagerFragment mediaDetailPagerFragment;
|
private MediaDetailPagerFragment mediaDetailPagerFragment;
|
||||||
private NavTabLayout.OnNavigationItemSelectedListener navListener;
|
private OnNavigationItemSelectedListener navListener;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public LocationServiceManager locationManager;
|
public LocationServiceManager locationManager;
|
||||||
|
|
@ -413,14 +432,47 @@ public class MainActivity extends BaseActivity
|
||||||
startActivity(new Intent(this, UploadProgressActivity.class));
|
startActivity(new Intent(this, UploadProgressActivity.class));
|
||||||
return true;
|
return true;
|
||||||
case R.id.notifications:
|
case R.id.notifications:
|
||||||
// Starts notification activity on click to notification icon
|
|
||||||
NotificationActivity.Companion.startYourself(this, "unread");
|
NotificationActivity.Companion.startYourself(this, "unread");
|
||||||
return true;
|
return true;
|
||||||
|
case R.id.menu_refresh:
|
||||||
|
// Refresh button handled in onCreateOptionsMenu through action layout
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
return super.onOptionsItemSelected(item);
|
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();
|
||||||
|
if (actionView != null) {
|
||||||
|
ImageView refreshIcon = actionView.findViewById(R.id.refresh_icon);
|
||||||
|
if (refreshIcon != null) {
|
||||||
|
refreshIcon.setOnClickListener(v -> {
|
||||||
|
// Clear previous animation and start new one
|
||||||
|
v.clearAnimation();
|
||||||
|
Animation rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate);
|
||||||
|
v.startAnimation(rotateAnimation);
|
||||||
|
|
||||||
|
// Trigger refresh logic
|
||||||
|
contributionsListPresenter.checkForNewContributions();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void centerMapToPlace(Place place) {
|
public void centerMapToPlace(Place place) {
|
||||||
setSelectedItemId(NavTab.NEARBY.code());
|
setSelectedItemId(NavTab.NEARBY.code());
|
||||||
nearbyParentFragment.setNearbyParentFragmentInstanceReadyCallback(
|
nearbyParentFragment.setNearbyParentFragmentInstanceReadyCallback(
|
||||||
|
|
@ -435,14 +487,16 @@ public class MainActivity extends BaseActivity
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
// Check if it's the first run and the user hasn't skipped login
|
||||||
if ((applicationKvStore.getBoolean("firstrun", true)) &&
|
if (applicationKvStore.getBoolean("firstrun", true) &&
|
||||||
(!applicationKvStore.getBoolean("login_skipped"))) {
|
!applicationKvStore.getBoolean("login_skipped")) {
|
||||||
defaultKvStore.putBoolean("inAppCameraFirstRun", true);
|
defaultKvStore.putBoolean("inAppCameraFirstRun", true);
|
||||||
WelcomeActivity.startYourself(this);
|
WelcomeActivity.startYourself(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
retryAllFailedUploads();
|
retryAllFailedUploads();
|
||||||
|
// Background check for new contributions
|
||||||
|
contributionsListPresenter.checkForNewContributions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -480,7 +534,7 @@ public class MainActivity extends BaseActivity
|
||||||
settingsFragment.setLocale(this, language);
|
settingsFragment.setLocale(this, language);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NavTabLayout.OnNavigationItemSelectedListener getNavListener() {
|
public OnNavigationItemSelectedListener getNavListener() {
|
||||||
return navListener;
|
return navListener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
app/src/main/res/anim/rotate.xml
Normal file
9
app/src/main/res/anim/rotate.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:fromDegrees="0"
|
||||||
|
android:toDegrees="360"
|
||||||
|
android:duration="300"
|
||||||
|
android:pivotX="50%"
|
||||||
|
android:pivotY="50%" />
|
||||||
|
</set>
|
||||||
19
app/src/main/res/layout/refresh_button_icon.xml
Normal file
19
app/src/main/res/layout/refresh_button_icon.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<!-- Refresh Icon with margin from the right -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/refresh_icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:contentDescription="@string/refresh"
|
||||||
|
android:layout_marginEnd="@dimen/activity_margin_horizontal"
|
||||||
|
app:srcCompat="@android:drawable/ic_menu_rotate" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_refresh"
|
||||||
|
android:title="Refresh"
|
||||||
|
app:actionLayout="@layout/refresh_button_icon"
|
||||||
|
app:showAsAction="ifRoom|withText" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/upload_tab"
|
android:id="@+id/upload_tab"
|
||||||
android:title="Upload"
|
android:title="Upload"
|
||||||
|
|
@ -7,8 +12,7 @@
|
||||||
app:showAsAction="ifRoom|withText" />
|
app:showAsAction="ifRoom|withText" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/notifications"
|
android:id="@+id/notifications"
|
||||||
android:menuCategory="secondary"
|
android:title="Notifications"
|
||||||
android:title="@string/notifications"
|
|
||||||
app:actionLayout="@layout/notification_icon"
|
app:actionLayout="@layout/notification_icon"
|
||||||
app:showAsAction="ifRoom|withText" />
|
app:showAsAction="ifRoom|withText" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
<string name="nearby_filter_search">Search View</string>
|
<string name="nearby_filter_search">Search View</string>
|
||||||
<string name="nearby_filter_state">Place State</string>
|
<string name="nearby_filter_state">Place State</string>
|
||||||
<string name="appwidget_img">Pic of the Day</string>
|
<string name="appwidget_img">Pic of the Day</string>
|
||||||
|
<string name="refresh">Refresh</string>
|
||||||
|
|
||||||
<!--Other strings-->
|
<!--Other strings-->
|
||||||
<plurals name="uploads_pending_notification_indicator">
|
<plurals name="uploads_pending_notification_indicator">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue