mirror of
https://github.com/KokaKiwi/BarInsta
synced 2026-03-14 00:11:40 +00:00
Fix re-init of fragments on tab change
This commit is contained in:
parent
64600ceb04
commit
74434aa3b3
27 changed files with 3527 additions and 704 deletions
|
|
@ -14,8 +14,6 @@ import android.view.Menu
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
|
|
@ -26,9 +24,7 @@ import androidx.core.view.WindowInsetsCompat
|
|||
import androidx.emoji.text.EmojiCompat
|
||||
import androidx.emoji.text.EmojiCompat.InitCallback
|
||||
import androidx.emoji.text.FontRequestEmojiCompatConfig
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.NavGraph
|
||||
|
|
@ -36,7 +32,6 @@ import androidx.navigation.NavGraphNavigator
|
|||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.*
|
||||
import awais.instagrabber.BuildConfig
|
||||
import awais.instagrabber.NavGraphDirections
|
||||
import awais.instagrabber.R
|
||||
import awais.instagrabber.customviews.emoji.EmojiVariantManager
|
||||
import awais.instagrabber.customviews.helpers.RootViewDeferringInsetsCallback
|
||||
|
|
@ -54,37 +49,28 @@ import awais.instagrabber.utils.*
|
|||
import awais.instagrabber.utils.AppExecutors.tasksThread
|
||||
import awais.instagrabber.utils.DownloadUtils.ReselectDocumentTreeException
|
||||
import awais.instagrabber.utils.TextUtils.isEmpty
|
||||
import awais.instagrabber.utils.TextUtils.shortcodeToId
|
||||
import awais.instagrabber.utils.emoji.EmojiParser
|
||||
import awais.instagrabber.viewmodels.AppStateViewModel
|
||||
import awais.instagrabber.viewmodels.DirectInboxViewModel
|
||||
import awais.instagrabber.webservices.GraphQLRepository
|
||||
import awais.instagrabber.webservices.MediaRepository
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.google.common.collect.ImmutableList
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
||||
|
||||
class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedListener {
|
||||
class MainActivity : BaseLanguageActivity() {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var navController: NavController
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
|
||||
// private var currentNavControllerLiveData: LiveData<NavController>? = null
|
||||
private var searchMenuItem: MenuItem? = null
|
||||
private var startNavRootId: Int = 0
|
||||
|
||||
// private var firstFragmentGraphIndex = 0
|
||||
private var lastSelectedNavMenuId = 0
|
||||
private var isActivityCheckerServiceBound = false
|
||||
private var isBackStackEmpty = false
|
||||
private var isLoggedIn = false
|
||||
private var deviceUuid: String? = null
|
||||
private var csrfToken: String? = null
|
||||
|
|
@ -106,8 +92,6 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
isActivityCheckerServiceBound = false
|
||||
}
|
||||
}
|
||||
private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }
|
||||
private val graphQLRepository: GraphQLRepository by lazy { GraphQLRepository.getInstance() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
try {
|
||||
|
|
@ -156,7 +140,6 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
if (isLoggedIn && Utils.settingsHelper.getBoolean(PreferenceKeys.CHECK_ACTIVITY)) {
|
||||
bindActivityCheckerService()
|
||||
}
|
||||
supportFragmentManager.addOnBackStackChangedListener(this)
|
||||
// Initialise the internal map
|
||||
tasksThread.execute {
|
||||
EmojiParser.getInstance(this)
|
||||
|
|
@ -235,7 +218,6 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.main_menu, menu)
|
||||
searchMenuItem = menu.findItem(R.id.search)
|
||||
// val navController = currentNavControllerLiveData?.value
|
||||
val currentDestination = navController.currentDestination
|
||||
if (currentDestination != null) {
|
||||
val backStack = navController.backQueue
|
||||
|
|
@ -246,9 +228,8 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.search) {
|
||||
// val navController = currentNavControllerLiveData?.value ?: return false
|
||||
try {
|
||||
navController.navigate(R.id.action_global_search)
|
||||
navController.navigate(getSearchDeepLink())
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "onOptionsItemSelected: ", e)
|
||||
|
|
@ -301,27 +282,24 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
instance = null
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
val backStack = navController.backQueue
|
||||
val currentNavControllerBackStack = backStack.size
|
||||
if (isTaskRoot && isBackStackEmpty && currentNavControllerBackStack == 2) {
|
||||
finishAfterTransition()
|
||||
return
|
||||
}
|
||||
if (!isFinishing) {
|
||||
try {
|
||||
super.onBackPressed()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "onBackPressed: ", e)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackStackChanged() {
|
||||
val backStackEntryCount = supportFragmentManager.backStackEntryCount
|
||||
isBackStackEmpty = backStackEntryCount == 0
|
||||
}
|
||||
// override fun onBackPressed() {
|
||||
// Log.d(TAG, "onBackPressed: ")
|
||||
// navController.navigateUp()
|
||||
// val backStack = navController.backQueue
|
||||
// val currentNavControllerBackStack = backStack.size
|
||||
// if (isTaskRoot && isBackStackEmpty && currentNavControllerBackStack == 2) {
|
||||
// finishAfterTransition()
|
||||
// return
|
||||
// }
|
||||
// if (!isFinishing) {
|
||||
// try {
|
||||
// super.onBackPressed()
|
||||
// } catch (e: Exception) {
|
||||
// Log.e(TAG, "onBackPressed: ", e)
|
||||
// finish()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun createNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
||||
|
|
@ -374,16 +352,10 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
val navigator = navigatorProvider.getNavigator<NavGraphNavigator>("navigation")
|
||||
val rootNavGraph = NavGraph(navigator)
|
||||
val navInflater = navController.navInflater
|
||||
val destinations = currentTabs.map {
|
||||
val navGraph = navInflater.inflate(R.navigation.nav_graph)
|
||||
navGraph.id = it.navigationRootId
|
||||
navGraph.label = "${it.title}_nav_graph".lowercase(Locale.getDefault())
|
||||
navGraph.setStartDestination(it.startDestinationFragmentId)
|
||||
return@map navGraph
|
||||
}
|
||||
val topLevelDestinations = currentTabs.map { navInflater.inflate(it.navigationResId) }
|
||||
rootNavGraph.id = R.id.root_nav_graph
|
||||
rootNavGraph.label = "root_nav_graph"
|
||||
rootNavGraph.addDestinations(destinations)
|
||||
rootNavGraph.addDestinations(topLevelDestinations)
|
||||
rootNavGraph.setStartDestination(if (startNavRootId != 0) startNavRootId else R.id.profile_nav_graph)
|
||||
navController.graph = rootNavGraph
|
||||
binding.bottomNavView.setupWithNavController(navController)
|
||||
|
|
@ -536,8 +508,7 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
fun navigateToThread(threadId: String?, threadTitle: String?) {
|
||||
if (threadId == null || threadTitle == null) return
|
||||
try {
|
||||
val action = NavGraphDirections.actionGlobalDirectThread(threadId, threadTitle)
|
||||
navController.navigate(action)
|
||||
navController.navigate(getDirectThreadDeepLink(threadId, threadTitle))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "navigateToThread: ", e)
|
||||
}
|
||||
|
|
@ -564,8 +535,7 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
private fun showProfileView(intentModel: IntentModel) {
|
||||
try {
|
||||
val username = intentModel.text
|
||||
val action = NavGraphDirections.actionGlobalProfile().setUsername(username)
|
||||
navController.navigate(action)
|
||||
navController.navigate(getProfileDeepLink(username))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showProfileView: ", e)
|
||||
}
|
||||
|
|
@ -574,33 +544,10 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
private fun showPostView(intentModel: IntentModel) {
|
||||
val shortCode = intentModel.text
|
||||
// Log.d(TAG, "shortCode: " + shortCode);
|
||||
val alertDialog = AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setView(R.layout.dialog_opening_post)
|
||||
.create()
|
||||
alertDialog.show()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val media = if (isLoggedIn) mediaRepository.fetch(shortcodeToId(shortCode)) else graphQLRepository.fetchPost(shortCode)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (media == null) {
|
||||
Toast.makeText(applicationContext, R.string.post_not_found, Toast.LENGTH_SHORT).show()
|
||||
return@withContext
|
||||
}
|
||||
try {
|
||||
val action = NavGraphDirections.actionGlobalPost(media, 0)
|
||||
navController.navigate(action)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showPostView: ", e)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showPostView: ", e)
|
||||
} finally {
|
||||
withContext(Dispatchers.Main) {
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
}
|
||||
try {
|
||||
navController.navigate(getPostDeepLink(shortCode))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showPostView: ", e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -608,8 +555,7 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
val locationId = intentModel.text
|
||||
// Log.d(TAG, "locationId: " + locationId);
|
||||
try {
|
||||
val action = NavGraphDirections.actionGlobalLocation(locationId.toLong())
|
||||
navController.navigate(action)
|
||||
navController.navigate(getLocationDeepLink(locationId))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showLocationView: ", e)
|
||||
}
|
||||
|
|
@ -619,8 +565,7 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
val hashtag = intentModel.text
|
||||
// Log.d(TAG, "hashtag: " + hashtag);
|
||||
try {
|
||||
val action = NavGraphDirections.actionGlobalHashTag(hashtag)
|
||||
navController.navigate(action)
|
||||
navController.navigate(getHashtagDeepLink(hashtag))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showHashtagView: ", e)
|
||||
}
|
||||
|
|
@ -628,8 +573,7 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
|
||||
private fun showActivityView() {
|
||||
try {
|
||||
val action = NavGraphDirections.actionGlobalNotifications().apply { type = "notif" }
|
||||
navController.navigate(action)
|
||||
navController.navigate(getNotificationsDeepLink("notif"))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showActivityView: ", e)
|
||||
}
|
||||
|
|
@ -649,21 +593,21 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
|
|||
val bottomNavView: BottomNavigationView
|
||||
get() = binding.bottomNavView
|
||||
|
||||
fun setCollapsingView(view: View) {
|
||||
try {
|
||||
binding.collapsingToolbarLayout.addView(view, 0)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "setCollapsingView: ", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeCollapsingView(view: View) {
|
||||
try {
|
||||
binding.collapsingToolbarLayout.removeView(view)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "removeCollapsingView: ", e)
|
||||
}
|
||||
}
|
||||
// fun setCollapsingView(view: View) {
|
||||
// try {
|
||||
// binding.collapsingToolbarLayout.addView(view, 0)
|
||||
// } catch (e: Exception) {
|
||||
// Log.e(TAG, "setCollapsingView: ", e)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fun removeCollapsingView(view: View) {
|
||||
// try {
|
||||
// binding.collapsingToolbarLayout.removeView(view)
|
||||
// } catch (e: Exception) {
|
||||
// Log.e(TAG, "removeCollapsingView: ", e)
|
||||
// }
|
||||
// }
|
||||
|
||||
fun resetToolbar() {
|
||||
binding.appBarLayout.visibility = View.VISIBLE
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@ package awais.instagrabber.customviews;
|
|||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelStoreOwner;
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||
|
|
@ -27,24 +26,18 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import awais.instagrabber.adapters.FeedAdapterV2;
|
||||
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
|
||||
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge;
|
||||
import awais.instagrabber.fragments.settings.PreferenceKeys;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.models.PostsLayoutPreferences;
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
import awais.instagrabber.utils.KeywordsFilterUtils;
|
||||
import awais.instagrabber.utils.ResponseBodyUtils;
|
||||
import awais.instagrabber.utils.Utils;
|
||||
import awais.instagrabber.viewmodels.MediaViewModel;
|
||||
import awais.instagrabber.workers.DownloadWorker;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class PostsRecyclerView extends RecyclerView {
|
||||
private static final String TAG = "PostsRecyclerView";
|
||||
|
||||
|
|
@ -52,7 +45,6 @@ public class PostsRecyclerView extends RecyclerView {
|
|||
private PostsLayoutPreferences layoutPreferences;
|
||||
private PostFetcher.PostFetchService postFetchService;
|
||||
private Transition transition;
|
||||
private PostFetcher postFetcher;
|
||||
private ViewModelStoreOwner viewModelStoreOwner;
|
||||
private FeedAdapterV2 feedAdapter;
|
||||
private LifecycleOwner lifeCycleOwner;
|
||||
|
|
@ -63,40 +55,9 @@ public class PostsRecyclerView extends RecyclerView {
|
|||
private FeedAdapterV2.FeedItemCallback feedItemCallback;
|
||||
private boolean shouldScrollToTop;
|
||||
private FeedAdapterV2.SelectionModeCallback selectionModeCallback;
|
||||
private Function<ViewGroup, View> headerViewCreator;
|
||||
private Function<View, Void> headerBinder;
|
||||
private boolean refresh = true;
|
||||
|
||||
private final List<FetchStatusChangeListener> fetchStatusChangeListeners = new ArrayList<>();
|
||||
|
||||
private final FetchListener<List<Media>> fetchListener = new FetchListener<List<Media>>() {
|
||||
@Override
|
||||
public void onResult(final List<Media> result) {
|
||||
if (refresh) {
|
||||
refresh = false;
|
||||
mediaViewModel.getList().postValue(result);
|
||||
shouldScrollToTop = true;
|
||||
dispatchFetchStatus();
|
||||
return;
|
||||
}
|
||||
final List<Media> models = mediaViewModel.getList().getValue();
|
||||
final List<Media> modelsCopy = models == null ? new ArrayList<>() : new ArrayList<>(models);
|
||||
if (settingsHelper.getBoolean(PreferenceKeys.TOGGLE_KEYWORD_FILTER)) {
|
||||
final ArrayList<String> items = new ArrayList<>(settingsHelper.getStringSet(PreferenceKeys.KEYWORD_FILTERS));
|
||||
modelsCopy.addAll(new KeywordsFilterUtils(items).filter(result));
|
||||
} else {
|
||||
modelsCopy.addAll(result);
|
||||
}
|
||||
mediaViewModel.getList().postValue(modelsCopy);
|
||||
dispatchFetchStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
}
|
||||
};
|
||||
|
||||
private final RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) {
|
||||
@Override
|
||||
protected int getVerticalSnapPreference() {
|
||||
|
|
@ -199,18 +160,22 @@ public class PostsRecyclerView extends RecyclerView {
|
|||
|
||||
private void initSelf() {
|
||||
try {
|
||||
mediaViewModel = new ViewModelProvider(viewModelStoreOwner).get(MediaViewModel.class);
|
||||
mediaViewModel = new ViewModelProvider(
|
||||
viewModelStoreOwner,
|
||||
new MediaViewModel.ViewModelFactory(postFetchService)
|
||||
).get(MediaViewModel.class);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "initSelf: ", e);
|
||||
}
|
||||
if (mediaViewModel == null) return;
|
||||
mediaViewModel.getList().observe(lifeCycleOwner, list -> feedAdapter.submitList(list, () -> {
|
||||
final LiveData<List<Media>> mediaListLiveData = mediaViewModel.getList();
|
||||
mediaListLiveData.observe(lifeCycleOwner, list -> feedAdapter.submitList(list, () -> {
|
||||
dispatchFetchStatus();
|
||||
// postDelayed(this::fetchMoreIfPossible, 1000);
|
||||
if (!shouldScrollToTop) return;
|
||||
shouldScrollToTop = false;
|
||||
post(() -> smoothScrollToPosition(0));
|
||||
}));
|
||||
postFetcher = new PostFetcher(postFetchService, fetchListener);
|
||||
if (layoutPreferences.getHasGap()) {
|
||||
addItemDecoration(gridSpacingItemDecoration);
|
||||
}
|
||||
|
|
@ -218,18 +183,20 @@ public class PostsRecyclerView extends RecyclerView {
|
|||
setNestedScrollingEnabled(true);
|
||||
setItemAnimator(null);
|
||||
lazyLoader = new RecyclerLazyLoaderAtEdge(layoutManager, (page) -> {
|
||||
if (postFetcher.hasMore()) {
|
||||
postFetcher.fetch();
|
||||
if (mediaViewModel.hasMore()) {
|
||||
mediaViewModel.fetch();
|
||||
dispatchFetchStatus();
|
||||
}
|
||||
});
|
||||
addOnScrollListener(lazyLoader);
|
||||
postFetcher.fetch();
|
||||
dispatchFetchStatus();
|
||||
if (mediaListLiveData.getValue() == null || mediaListLiveData.getValue().isEmpty()) {
|
||||
mediaViewModel.fetch();
|
||||
dispatchFetchStatus();
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchMoreIfPossible() {
|
||||
if (!postFetcher.hasMore()) return;
|
||||
if (!mediaViewModel.hasMore()) return;
|
||||
if (feedAdapter.getItemCount() == 0) return;
|
||||
final LayoutManager layoutManager = getLayoutManager();
|
||||
if (!(layoutManager instanceof StaggeredGridLayoutManager)) return;
|
||||
|
|
@ -238,7 +205,7 @@ public class PostsRecyclerView extends RecyclerView {
|
|||
if (allNoPosition) return;
|
||||
final boolean match = Arrays.stream(itemPositions).anyMatch(position -> position == feedAdapter.getItemCount() - 1);
|
||||
if (!match) return;
|
||||
postFetcher.fetch();
|
||||
mediaViewModel.fetch();
|
||||
dispatchFetchStatus();
|
||||
}
|
||||
|
||||
|
|
@ -268,6 +235,7 @@ public class PostsRecyclerView extends RecyclerView {
|
|||
|
||||
private List<String> getDisplayUrl(final Media feedModel) {
|
||||
List<String> urls = Collections.emptyList();
|
||||
if (feedModel == null || feedModel.getType() == null) return urls;
|
||||
switch (feedModel.getType()) {
|
||||
case MEDIA_TYPE_IMAGE:
|
||||
case MEDIA_TYPE_VIDEO:
|
||||
|
|
@ -320,20 +288,18 @@ public class PostsRecyclerView extends RecyclerView {
|
|||
}
|
||||
|
||||
public void refresh() {
|
||||
refresh = true;
|
||||
shouldScrollToTop = true;
|
||||
if (lazyLoader != null) {
|
||||
lazyLoader.resetState();
|
||||
}
|
||||
if (postFetcher != null) {
|
||||
// mediaViewModel.getList().postValue(Collections.emptyList());
|
||||
postFetcher.reset();
|
||||
postFetcher.fetch();
|
||||
if (mediaViewModel != null) {
|
||||
mediaViewModel.refresh();
|
||||
}
|
||||
dispatchFetchStatus();
|
||||
}
|
||||
|
||||
public boolean isFetching() {
|
||||
return postFetcher != null && postFetcher.isFetching();
|
||||
return mediaViewModel != null && mediaViewModel.isFetching();
|
||||
}
|
||||
|
||||
public PostsRecyclerView addFetchStatusChangeListener(final FetchStatusChangeListener fetchStatusChangeListener) {
|
||||
|
|
@ -369,6 +335,7 @@ public class PostsRecyclerView extends RecyclerView {
|
|||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
lifeCycleOwner = null;
|
||||
initCalled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -21,21 +21,20 @@ public class PostFetcher {
|
|||
}
|
||||
|
||||
public void fetch() {
|
||||
if (!fetching) {
|
||||
fetching = true;
|
||||
postFetchService.fetch(new FetchListener<List<Media>>() {
|
||||
@Override
|
||||
public void onResult(final List<Media> result) {
|
||||
fetching = false;
|
||||
fetchListener.onResult(result);
|
||||
}
|
||||
if (fetching) return;
|
||||
fetching = true;
|
||||
postFetchService.fetch(new FetchListener<List<Media>>() {
|
||||
@Override
|
||||
public void onResult(final List<Media> result) {
|
||||
fetching = false;
|
||||
fetchListener.onResult(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
package awais.instagrabber.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import awais.instagrabber.R
|
||||
import awais.instagrabber.utils.*
|
||||
import awais.instagrabber.utils.extensions.TAG
|
||||
import awais.instagrabber.webservices.GraphQLRepository
|
||||
import awais.instagrabber.webservices.MediaRepository
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.util.*
|
||||
|
||||
class PostLoadingDialogFragment : DialogFragment() {
|
||||
private var isLoggedIn: Boolean = false
|
||||
|
||||
private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }
|
||||
private val graphQLRepository: GraphQLRepository by lazy { GraphQLRepository.getInstance() }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val cookie = Utils.settingsHelper.getString(Constants.COOKIE)
|
||||
var userId: Long = 0
|
||||
var csrfToken: String? = null
|
||||
if (cookie.isNotBlank()) {
|
||||
userId = getUserIdFromCookie(cookie)
|
||||
csrfToken = getCsrfTokenFromCookie(cookie)
|
||||
}
|
||||
if (cookie.isBlank() || userId == 0L || csrfToken.isNullOrBlank()) {
|
||||
isLoggedIn = false
|
||||
return
|
||||
}
|
||||
isLoggedIn = true
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setCancelable(false)
|
||||
.setView(R.layout.dialog_opening_post)
|
||||
.create()
|
||||
}
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
val arguments = PostLoadingDialogFragmentArgs.fromBundle(arguments ?: return)
|
||||
val shortCode = arguments.shortCode
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val media = if (isLoggedIn) mediaRepository.fetch(TextUtils.shortcodeToId(shortCode)) else graphQLRepository.fetchPost(shortCode)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (media == null) {
|
||||
Toast.makeText(context, R.string.post_not_found, Toast.LENGTH_SHORT).show()
|
||||
return@withContext
|
||||
}
|
||||
try {
|
||||
findNavController().navigate(PostLoadingDialogFragmentDirections.actionToPost(media, 0))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showPostView: ", e)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "showPostView: ", e)
|
||||
} finally {
|
||||
withContext(Dispatchers.Main) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -238,7 +238,7 @@ public class TabOrderPreferenceDialogFragment extends DialogFragment {
|
|||
private void saveNewOrder() {
|
||||
final String newOrderString = newOrderTabs
|
||||
.stream()
|
||||
.map(tab -> NavigationHelperKt.geNavGraphNameForNavRootId(tab.getNavigationRootId()))
|
||||
.map(tab -> NavigationHelperKt.getNavGraphNameForNavRootId(tab.getNavigationRootId()))
|
||||
.collect(Collectors.joining(","));
|
||||
Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
|
|||
|
||||
private void openPostDialog(final Media feedModel, final int position) {
|
||||
try {
|
||||
final NavDirections action = TopicPostsFragmentDirections.actionGlobalPost(feedModel, position);
|
||||
final NavDirections action = TopicPostsFragmentDirections.actionToPost(feedModel, position);
|
||||
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "openPostDialog: ", e);
|
||||
|
|
|
|||
|
|
@ -7,10 +7,8 @@ import android.os.Handler
|
|||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
|
|
@ -33,18 +31,16 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
|
|||
private val viewModel: DirectInboxViewModel by activityViewModels()
|
||||
|
||||
private lateinit var fragmentActivity: MainActivity
|
||||
private lateinit var root: CoordinatorLayout
|
||||
private lateinit var binding: FragmentDirectMessagesInboxBinding
|
||||
private lateinit var inboxAdapter: DirectMessageInboxAdapter
|
||||
private lateinit var lazyLoader: RecyclerLazyLoaderAtEdge
|
||||
|
||||
private var shouldRefresh = true
|
||||
private var scrollToTop = false
|
||||
private var navigating = false
|
||||
private var threadsObserver: Observer<List<DirectThread?>>? = null
|
||||
|
||||
private var pendingRequestsMenuItem: MenuItem? = null
|
||||
private var pendingRequestTotalBadgeDrawable: BadgeDrawable? = null
|
||||
private var isPendingRequestTotalBadgeAttached = false
|
||||
private var inboxAdapter: DirectMessageInboxAdapter? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -57,17 +53,11 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
|
|||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
if (this::root.isInitialized) {
|
||||
shouldRefresh = false
|
||||
return root
|
||||
}
|
||||
binding = FragmentDirectMessagesInboxBinding.inflate(inflater, container, false)
|
||||
root = binding.root
|
||||
return root
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
if (!shouldRefresh) return
|
||||
init()
|
||||
}
|
||||
|
||||
|
|
@ -90,11 +80,6 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
setupObservers()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.dm_inbox_menu, menu)
|
||||
pendingRequestsMenuItem = menu.findItem(R.id.pending_requests)
|
||||
|
|
@ -119,23 +104,14 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
|
|||
init()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
removeViewModelObservers()
|
||||
viewModel.onDestroy()
|
||||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
removeViewModelObservers()
|
||||
threadsObserver = Observer { list: List<DirectThread?> ->
|
||||
if (!this::inboxAdapter.isInitialized) return@Observer
|
||||
inboxAdapter.submitList(list) {
|
||||
viewModel.threads.observe(viewLifecycleOwner, { list: List<DirectThread?> ->
|
||||
inboxAdapter?.submitList(list) {
|
||||
if (!scrollToTop) return@submitList
|
||||
binding.inboxList.post { binding.inboxList.smoothScrollToPosition(0) }
|
||||
scrollToTop = false
|
||||
}
|
||||
}
|
||||
threadsObserver?.let { viewModel.threads.observe(fragmentActivity, it) }
|
||||
})
|
||||
viewModel.inbox.observe(viewLifecycleOwner, { inboxResource: Resource<DirectInbox?>? ->
|
||||
if (inboxResource == null) return@observe
|
||||
when (inboxResource.status) {
|
||||
|
|
@ -191,11 +167,6 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
|
|||
}
|
||||
}
|
||||
|
||||
private fun removeViewModelObservers() {
|
||||
threadsObserver?.let { viewModel.threads.removeObserver(it) }
|
||||
// no need to explicitly remove observers whose lifecycle owner is getViewLifecycleOwner
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
val context = context ?: return
|
||||
setupObservers()
|
||||
|
|
@ -218,10 +189,12 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
|
|||
}
|
||||
}
|
||||
navigating = false
|
||||
}.also {
|
||||
it.setHasStableIds(true)
|
||||
}
|
||||
inboxAdapter.setHasStableIds(true)
|
||||
binding.inboxList.adapter = inboxAdapter
|
||||
lazyLoader = RecyclerLazyLoaderAtEdge(layoutManager) { viewModel.fetchInbox() }
|
||||
lazyLoader.let { binding.inboxList.addOnScrollListener(it) }
|
||||
lazyLoader = RecyclerLazyLoaderAtEdge(layoutManager) { viewModel.fetchInbox() }.also {
|
||||
binding.inboxList.addOnScrollListener(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ import android.view.*
|
|||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
|
|
@ -75,12 +74,14 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||
private var selectedMedia: List<Media>? = null
|
||||
private var actionMode: ActionMode? = null
|
||||
private var disableDm: Boolean = false
|
||||
private var shouldRefresh: Boolean = true
|
||||
|
||||
// private var shouldRefresh: Boolean = true
|
||||
private var highlightsAdapter: HighlightsAdapter? = null
|
||||
private var layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_PROFILE_POSTS_LAYOUT)
|
||||
|
||||
private lateinit var mainActivity: MainActivity
|
||||
private lateinit var root: MotionLayout
|
||||
|
||||
// private lateinit var root: MotionLayout
|
||||
private lateinit var binding: FragmentProfileBinding
|
||||
private lateinit var appStateViewModel: AppStateViewModel
|
||||
private lateinit var viewModel: ProfileFragmentViewModel
|
||||
|
|
@ -335,23 +336,12 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
if (this::root.isInitialized) {
|
||||
shouldRefresh = false
|
||||
return root
|
||||
}
|
||||
appStateViewModel.currentUserLiveData.observe(viewLifecycleOwner, viewModel::setCurrentUser)
|
||||
binding = FragmentProfileBinding.inflate(inflater, container, false)
|
||||
root = binding.root
|
||||
return root
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
if (!shouldRefresh) {
|
||||
setupObservers()
|
||||
return
|
||||
}
|
||||
init()
|
||||
shouldRefresh = false
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
|
|
@ -399,6 +389,11 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
setupPostsDone = false
|
||||
}
|
||||
|
||||
private fun shareProfileViaDm() {
|
||||
try {
|
||||
val actionToUserSearch = ProfileFragmentDirections.actionToUserSearch().apply {
|
||||
|
|
@ -444,7 +439,11 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||
}
|
||||
|
||||
private fun setupObservers() {
|
||||
viewModel.isLoggedIn.observe(viewLifecycleOwner) {} // observe so that `isLoggedIn.value` is correct
|
||||
appStateViewModel.currentUserLiveData.observe(viewLifecycleOwner, viewModel::setCurrentUser)
|
||||
viewModel.isLoggedIn.observe(viewLifecycleOwner) {
|
||||
// observe so that `isLoggedIn.value` is correct
|
||||
Log.d(TAG, "setupObservers: $it")
|
||||
}
|
||||
viewModel.currentUserProfileActionLiveData.observe(viewLifecycleOwner) {
|
||||
val (currentUserResource, profileResource) = it
|
||||
if (currentUserResource.status == Resource.Status.ERROR || profileResource.status == Resource.Status.ERROR) {
|
||||
|
|
@ -469,7 +468,7 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||
context?.let { ctx -> Toast.makeText(ctx, R.string.error_loading_profile, Toast.LENGTH_LONG).show() }
|
||||
return@observe
|
||||
}
|
||||
root.loadLayoutDescription(R.xml.header_list_scene)
|
||||
binding.root.loadLayoutDescription(R.xml.header_list_scene)
|
||||
setupFavChip(profile, currentUser)
|
||||
setupFavButton(currentUser, profile)
|
||||
setupSavedButton(currentUser, profile)
|
||||
|
|
@ -549,7 +548,7 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||
binding.privatePage2.visibility = VISIBLE
|
||||
binding.postsRecyclerView.visibility = GONE
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
root.getTransition(R.id.transition)?.setEnable(false)
|
||||
binding.root.getTransition(R.id.transition)?.setEnable(false)
|
||||
}
|
||||
|
||||
private fun setupProfileContext(contextPair: Pair<String?, List<UserProfileContextLink>?>) {
|
||||
|
|
@ -853,7 +852,7 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||
}
|
||||
|
||||
private fun showDefaultMessage() {
|
||||
root.loadLayoutDescription(R.xml.profile_fragment_no_acc_layout)
|
||||
binding.root.loadLayoutDescription(R.xml.profile_fragment_no_acc_layout)
|
||||
binding.privatePage1.visibility = View.VISIBLE
|
||||
binding.privatePage2.visibility = View.VISIBLE
|
||||
binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24)
|
||||
|
|
@ -884,7 +883,7 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
|
|||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
val canScrollVertically = recyclerView.canScrollVertically(-1)
|
||||
root.getTransition(R.id.transition)?.setEnable(!canScrollVertically)
|
||||
binding.root.getTransition(R.id.transition)?.setEnable(!canScrollVertically)
|
||||
}
|
||||
})
|
||||
setupPostsDone = true
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment implemen
|
|||
.map(Tab::getTitle)
|
||||
.toArray(String[]::new);
|
||||
final String[] navGraphFileNames = tabs.stream()
|
||||
.map(tab -> NavigationHelperKt.geNavGraphNameForNavRootId(tab.getNavigationRootId()))
|
||||
.map(tab -> NavigationHelperKt.getNavGraphNameForNavRootId(tab.getNavigationRootId()))
|
||||
.toArray(String[]::new);
|
||||
preference.setKey(Constants.DEFAULT_TAB);
|
||||
preference.setTitle(R.string.pref_start_screen);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,18 @@ package awais.instagrabber.models
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.NavigationRes
|
||||
|
||||
data class Tab(
|
||||
@param:DrawableRes val iconResId: Int,
|
||||
val title: String,
|
||||
val isRemovable: Boolean,
|
||||
|
||||
/**
|
||||
* This is the actual resource id of the navigation resource (R.navigation.graphName = navigationResId)
|
||||
*/
|
||||
@param:NavigationRes val navigationResId: Int,
|
||||
|
||||
/**
|
||||
* This is the resource id of the root navigation tag of the navigation resource.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package awais.instagrabber.utils
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
|
||||
private const val domain = "barinsta"
|
||||
|
||||
fun getDirectThreadDeepLink(threadId: String, threadTitle: String, isPending: Boolean = false): Uri =
|
||||
"$domain://dm_thread/$threadId/$threadTitle?pending=${isPending}".toUri()
|
||||
|
||||
fun getProfileDeepLink(username: String): Uri = "$domain://profile/$username".toUri()
|
||||
|
||||
fun getPostDeepLink(shortCode: String): Uri = "$domain://post/$shortCode".toUri()
|
||||
|
||||
fun getLocationDeepLink(locationId: Long): Uri = "$domain://location/$locationId".toUri()
|
||||
|
||||
fun getLocationDeepLink(locationId: String): Uri = "$domain://location/$locationId".toUri()
|
||||
|
||||
fun getHashtagDeepLink(hashtag: String): Uri = "$domain://hashtag/$hashtag".toUri()
|
||||
|
||||
fun getNotificationsDeepLink(type: String, targetId: Long = 0): Uri = "$domain://notifications/$type?targetId=$targetId".toUri()
|
||||
|
||||
fun getSearchDeepLink(): Uri = "$domain://search".toUri()
|
||||
|
|
@ -2,8 +2,8 @@ package awais.instagrabber.utils
|
|||
|
||||
import awais.instagrabber.repositories.responses.Media
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class KeywordsFilterUtils(private val keywords: ArrayList<String>) {
|
||||
// fun filter(caption: String?): Boolean {
|
||||
// if (caption == null) return false
|
||||
// if (keywords.isEmpty()) return false
|
||||
|
|
@ -14,24 +14,17 @@ class KeywordsFilterUtils(private val keywords: ArrayList<String>) {
|
|||
// return false
|
||||
// }
|
||||
|
||||
fun filter(media: Media?): Boolean {
|
||||
if (media == null) return false
|
||||
val (_, text) = media.caption ?: return false
|
||||
if (keywords.isEmpty()) return false
|
||||
val temp = text!!.lowercase(Locale.getDefault())
|
||||
for (s in keywords) {
|
||||
if (temp.contains(s)) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
private fun containsAnyKeyword(keywords: List<String>, media: Media?): Boolean {
|
||||
if (media == null || keywords.isEmpty()) return false
|
||||
val (_, text) = media.caption ?: return false
|
||||
val temp = text!!.lowercase(Locale.getDefault())
|
||||
return keywords.any { temp.contains(it) }
|
||||
}
|
||||
|
||||
fun filter(media: List<Media>?): List<Media>? {
|
||||
if (keywords.isEmpty()) return media
|
||||
if (media == null) return ArrayList()
|
||||
val result: MutableList<Media> = ArrayList()
|
||||
for (m in media) {
|
||||
if (!filter(m)) result.add(m)
|
||||
}
|
||||
return result
|
||||
}
|
||||
fun filter(keywords: List<String>, media: List<Media>?): List<Media>? {
|
||||
if (keywords.isEmpty()) return media
|
||||
if (media == null) return ArrayList()
|
||||
val result: MutableList<Media> = ArrayList()
|
||||
media.filterNotTo(result) { containsAnyKeyword(keywords, it) }
|
||||
return result
|
||||
}
|
||||
|
|
@ -28,7 +28,8 @@ private fun getTabs(
|
|||
navRootIds: IntArray,
|
||||
isAnon: Boolean = false,
|
||||
): Pair<List<Tab>, MutableList<Tab>> {
|
||||
val navGraphNames = getResIdsForNavRootIds(navRootIds, ::geNavGraphNameForNavRootId)
|
||||
val navGraphNames = getResIdsForNavRootIds(navRootIds, ::getNavGraphNameForNavRootId)
|
||||
val navGraphResIds = getResIdsForNavRootIds(navRootIds, ::getNavGraphResIdForNavRootId)
|
||||
val titleArray = getResIdsForNavRootIds(navRootIds, ::getTitleResIdForNavRootId)
|
||||
val iconIds = getResIdsForNavRootIds(navRootIds, ::getIconResIdForNavRootId)
|
||||
val startDestFragIds = getResIdsForNavRootIds(navRootIds, ::getStartDestFragIdForNavRootId)
|
||||
|
|
@ -37,15 +38,15 @@ private fun getTabs(
|
|||
val otherTabs = mutableListOf<Tab>() // Will contain tabs not in current list
|
||||
for (i in navRootIds.indices) {
|
||||
val navRootId = navRootIds[i]
|
||||
val navGraphName = navGraphNames[i]
|
||||
val tab = Tab(
|
||||
iconIds[i],
|
||||
context.getString(titleArray[i]),
|
||||
if(isAnon) false else !NON_REMOVABLE_NAV_ROOT_IDS.contains(navRootId),
|
||||
if (isAnon) false else !NON_REMOVABLE_NAV_ROOT_IDS.contains(navRootId),
|
||||
navGraphResIds[i],
|
||||
navRootId,
|
||||
startDestFragIds[i]
|
||||
)
|
||||
if (!isAnon && !orderedGraphNames.contains(navGraphName)) {
|
||||
if (!isAnon && !orderedGraphNames.contains(navGraphNames[i])) {
|
||||
otherTabs.add(tab)
|
||||
continue
|
||||
}
|
||||
|
|
@ -109,7 +110,7 @@ private fun getStartDestFragIdForNavRootId(id: Int): Int = when (id) {
|
|||
else -> 0
|
||||
}
|
||||
|
||||
fun geNavGraphNameForNavRootId(id: Int): String = when (id) {
|
||||
fun getNavGraphNameForNavRootId(id: Int): String = when (id) {
|
||||
R.id.direct_messages_nav_graph -> "direct_messages_nav_graph"
|
||||
R.id.feed_nav_graph -> "feed_nav_graph"
|
||||
R.id.profile_nav_graph -> "profile_nav_graph"
|
||||
|
|
@ -120,7 +121,18 @@ fun geNavGraphNameForNavRootId(id: Int): String = when (id) {
|
|||
else -> ""
|
||||
}
|
||||
|
||||
private fun geNavGraphNameForNavRootId(navGraphName: String): Int = when (navGraphName) {
|
||||
fun getNavGraphResIdForNavRootId(id: Int): Int = when (id) {
|
||||
R.id.direct_messages_nav_graph -> R.navigation.direct_messages_nav_graph
|
||||
R.id.feed_nav_graph -> R.navigation.feed_nav_graph
|
||||
R.id.profile_nav_graph -> R.navigation.profile_nav_graph
|
||||
R.id.discover_nav_graph -> R.navigation.discover_nav_graph
|
||||
R.id.more_nav_graph -> R.navigation.more_nav_graph
|
||||
R.id.favorites_nav_graph -> R.navigation.favorites_nav_graph
|
||||
R.id.notification_viewer_nav_graph -> R.navigation.notification_viewer_nav_graph
|
||||
else -> 0
|
||||
}
|
||||
|
||||
private fun getNavRootIdForGraphName(navGraphName: String): Int = when (navGraphName) {
|
||||
"direct_messages_nav_graph" -> R.id.direct_messages_nav_graph
|
||||
"feed_nav_graph" -> R.id.feed_nav_graph
|
||||
"profile_nav_graph" -> R.id.profile_nav_graph
|
||||
|
|
@ -139,9 +151,9 @@ private fun getOrderedNavRootIdsFromPref(navGraphNames: List<String>): Pair<List
|
|||
val newOrderString = top5navGraphNames.joinToString(",")
|
||||
Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString)
|
||||
tabOrderString = newOrderString
|
||||
return top5navGraphNames to top5navGraphNames.map(::geNavGraphNameForNavRootId)
|
||||
return top5navGraphNames to top5navGraphNames.map(::getNavRootIdForGraphName)
|
||||
}
|
||||
val orderString = tabOrderString ?: return navGraphNames to navGraphNames.subList(0, 5).map(::geNavGraphNameForNavRootId)
|
||||
val orderString = tabOrderString ?: return navGraphNames to navGraphNames.subList(0, 5).map(::getNavRootIdForGraphName)
|
||||
// Make sure that the list from preference does not contain any invalid values
|
||||
val orderGraphNames = orderString
|
||||
.split(",")
|
||||
|
|
@ -153,7 +165,7 @@ private fun getOrderedNavRootIdsFromPref(navGraphNames: List<String>): Pair<List
|
|||
// Use top 5 entries for default list
|
||||
navGraphNames.subList(0, 5)
|
||||
} else orderGraphNames
|
||||
return graphNames to graphNames.map(::geNavGraphNameForNavRootId)
|
||||
return graphNames to graphNames.map(::getNavRootIdForGraphName)
|
||||
}
|
||||
|
||||
fun isNavRootInCurrentTabs(navRootString: String?): Boolean {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,108 @@
|
|||
package awais.instagrabber.viewmodels;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import awais.instagrabber.customviews.helpers.PostFetcher;
|
||||
import awais.instagrabber.fragments.settings.PreferenceKeys;
|
||||
import awais.instagrabber.interfaces.FetchListener;
|
||||
import awais.instagrabber.repositories.responses.Media;
|
||||
import awais.instagrabber.utils.KeywordsFilterUtilsKt;
|
||||
|
||||
import static awais.instagrabber.utils.Utils.settingsHelper;
|
||||
|
||||
public class MediaViewModel extends ViewModel {
|
||||
private MutableLiveData<List<Media>> list;
|
||||
private static final String TAG = MediaViewModel.class.getSimpleName();
|
||||
|
||||
public MutableLiveData<List<Media>> getList() {
|
||||
if (list == null) {
|
||||
list = new MutableLiveData<>();
|
||||
private boolean refresh = true;
|
||||
|
||||
private final PostFetcher postFetcher;
|
||||
private final MutableLiveData<List<Media>> list = new MutableLiveData<>();
|
||||
|
||||
public MediaViewModel(@NonNull final PostFetcher.PostFetchService postFetchService) {
|
||||
final FetchListener<List<Media>> fetchListener = new FetchListener<List<Media>>() {
|
||||
@Override
|
||||
public void onResult(final List<Media> result) {
|
||||
if (refresh) {
|
||||
list.postValue(filterResult(result, true));
|
||||
refresh = false;
|
||||
return;
|
||||
}
|
||||
list.postValue(filterResult(result, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable t) {
|
||||
Log.e(TAG, "onFailure: ", t);
|
||||
}
|
||||
};
|
||||
postFetcher = new PostFetcher(postFetchService, fetchListener);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<Media> filterResult(final List<Media> result, final boolean isRefresh) {
|
||||
final List<Media> models = list.getValue();
|
||||
final List<Media> modelsCopy = models == null || isRefresh ? new ArrayList<>() : new ArrayList<>(models);
|
||||
if (settingsHelper.getBoolean(PreferenceKeys.TOGGLE_KEYWORD_FILTER)) {
|
||||
final List<String> keywords = new ArrayList<>(settingsHelper.getStringSet(PreferenceKeys.KEYWORD_FILTERS));
|
||||
final List<Media> filter = KeywordsFilterUtilsKt.filter(keywords, result);
|
||||
if (filter != null) {
|
||||
modelsCopy.addAll(filter);
|
||||
}
|
||||
return modelsCopy;
|
||||
}
|
||||
modelsCopy.addAll(result);
|
||||
return modelsCopy;
|
||||
}
|
||||
|
||||
public LiveData<List<Media>> getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public boolean hasMore() {
|
||||
return postFetcher.hasMore();
|
||||
}
|
||||
|
||||
public void fetch() {
|
||||
postFetcher.fetch();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
postFetcher.reset();
|
||||
}
|
||||
|
||||
public boolean isFetching() {
|
||||
return postFetcher.isFetching();
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
refresh = true;
|
||||
reset();
|
||||
fetch();
|
||||
}
|
||||
|
||||
public static class ViewModelFactory implements ViewModelProvider.Factory {
|
||||
|
||||
@NonNull
|
||||
private final PostFetcher.PostFetchService postFetchService;
|
||||
|
||||
public ViewModelFactory(@NonNull final PostFetcher.PostFetchService postFetchService) {
|
||||
this.postFetchService = postFetchService;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) {
|
||||
//noinspection unchecked
|
||||
return (T) new MediaViewModel(postFetchService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ import awais.instagrabber.viewmodels.ProfileFragmentViewModel.ProfileEvent.*
|
|||
import awais.instagrabber.webservices.*
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.time.LocalDateTime
|
||||
|
||||
|
|
@ -380,6 +381,7 @@ class ProfileFragmentViewModel(
|
|||
}
|
||||
val threadId = thread.threadId ?: return@afterPrevious
|
||||
_eventLiveData.postValue(Event(NavigateToThread(threadId, username)))
|
||||
delay(200) // Add delay so that the postValue in finally does not overwrite the NavigateToThread event
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "sendDm: ", e)
|
||||
} finally {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue