Merge pull request #1542 from ammargitham/feature/multistack-navigation

Migrate to navigation 2.4.0
This commit is contained in:
Austin Huang 2021-07-10 18:09:13 -04:00 committed by GitHub
commit db5f6b73fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
94 changed files with 5871 additions and 3747 deletions

View File

@ -163,7 +163,6 @@ configurations.all {
dependencies { dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
def nav_version = '2.3.5'
def exoplayer_version = '2.14.1' def exoplayer_version = '2.14.1'
implementation 'com.google.android.material:material:1.4.0' implementation 'com.google.android.material:material:1.4.0'
@ -175,8 +174,6 @@ dependencies {
implementation "androidx.recyclerview:recyclerview:1.2.1" implementation "androidx.recyclerview:recyclerview:1.2.1"
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation "androidx.viewpager2:viewpager2:1.0.0" implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation "androidx.navigation:navigation-fragment:$nav_version"
implementation "androidx.navigation:navigation-ui:$nav_version"
implementation "androidx.constraintlayout:constraintlayout:2.0.4" implementation "androidx.constraintlayout:constraintlayout:2.0.4"
implementation "androidx.preference:preference:1.1.1" implementation "androidx.preference:preference:1.1.1"
implementation 'androidx.palette:palette:1.0.0' implementation 'androidx.palette:palette:1.0.0'
@ -195,6 +192,10 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
// Navigation
implementation "androidx.navigation:navigation-fragment-ktx:$rootProject.nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$rootProject.nav_version"
// Room // Room
def room_version = "2.3.0" def room_version = "2.3.0"
implementation "androidx.room:room-runtime:$room_version" implementation "androidx.room:room-runtime:$room_version"

View File

@ -1,5 +1,7 @@
package awais.instagrabber.activities; package awais.instagrabber.activities;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
@ -66,9 +68,17 @@ public class DirectorySelectActivity extends BaseLanguageActivity {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialUri != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialUri != null) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri); intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri);
} }
try {
startActivityForResult(intent, SELECT_DIR_REQUEST_CODE); startActivityForResult(intent, SELECT_DIR_REQUEST_CODE);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "openDirectoryChooser: ", e);
showErrorDialog(getString(R.string.no_directory_picker_activity));
} catch (Exception e) {
Log.e(TAG, "openDirectoryChooser: ", e);
}
} }
@SuppressLint("StringFormatInvalid")
@Override @Override
protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) { protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);

View File

@ -1,7 +1,6 @@
package awais.instagrabber.activities package awais.instagrabber.activities
import android.animation.LayoutTransition import android.animation.LayoutTransition
import android.annotation.SuppressLint
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.content.ComponentName import android.content.ComponentName
@ -15,9 +14,6 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.Toast
import androidx.annotation.IdRes
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -28,23 +24,20 @@ import androidx.core.view.WindowInsetsCompat
import androidx.emoji.text.EmojiCompat import androidx.emoji.text.EmojiCompat
import androidx.emoji.text.EmojiCompat.InitCallback import androidx.emoji.text.EmojiCompat.InitCallback
import androidx.emoji.text.FontRequestEmojiCompatConfig import androidx.emoji.text.FontRequestEmojiCompatConfig
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavController.OnDestinationChangedListener
import androidx.navigation.NavDestination import androidx.navigation.NavDestination
import androidx.navigation.ui.NavigationUI import androidx.navigation.NavGraph
import androidx.navigation.NavGraphNavigator
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.*
import awais.instagrabber.BuildConfig import awais.instagrabber.BuildConfig
import awais.instagrabber.R import awais.instagrabber.R
import awais.instagrabber.customviews.emoji.EmojiVariantManager import awais.instagrabber.customviews.emoji.EmojiVariantManager
import awais.instagrabber.customviews.helpers.RootViewDeferringInsetsCallback import awais.instagrabber.customviews.helpers.RootViewDeferringInsetsCallback
import awais.instagrabber.customviews.helpers.TextWatcherAdapter import awais.instagrabber.customviews.helpers.TextWatcherAdapter
import awais.instagrabber.databinding.ActivityMainBinding import awais.instagrabber.databinding.ActivityMainBinding
import awais.instagrabber.fragments.PostViewV2Fragment import awais.instagrabber.fragments.main.FeedFragment
import awais.instagrabber.fragments.directmessages.DirectMessageInboxFragmentDirections
import awais.instagrabber.fragments.settings.PreferenceKeys import awais.instagrabber.fragments.settings.PreferenceKeys
import awais.instagrabber.models.IntentModel import awais.instagrabber.models.IntentModel
import awais.instagrabber.models.Resource import awais.instagrabber.models.Resource
@ -56,35 +49,28 @@ import awais.instagrabber.utils.*
import awais.instagrabber.utils.AppExecutors.tasksThread import awais.instagrabber.utils.AppExecutors.tasksThread
import awais.instagrabber.utils.DownloadUtils.ReselectDocumentTreeException import awais.instagrabber.utils.DownloadUtils.ReselectDocumentTreeException
import awais.instagrabber.utils.TextUtils.isEmpty import awais.instagrabber.utils.TextUtils.isEmpty
import awais.instagrabber.utils.TextUtils.shortcodeToId
import awais.instagrabber.utils.emoji.EmojiParser import awais.instagrabber.utils.emoji.EmojiParser
import awais.instagrabber.viewmodels.AppStateViewModel import awais.instagrabber.viewmodels.AppStateViewModel
import awais.instagrabber.viewmodels.DirectInboxViewModel 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
import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior import com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior
import com.google.android.material.appbar.CollapsingToolbarLayout import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList
import com.google.common.collect.Iterators
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.* import java.util.*
import java.util.stream.Collectors
class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedListener { class MainActivity : BaseLanguageActivity() {
private lateinit var binding: ActivityMainBinding 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 searchMenuItem: MenuItem? = null
private var firstFragmentGraphIndex = 0 private var startNavRootId: Int = 0
private var lastSelectedNavMenuId = 0 private var lastSelectedNavMenuId = 0
private var isActivityCheckerServiceBound = false private var isActivityCheckerServiceBound = false
private var isBackStackEmpty = false
private var isLoggedIn = false private var isLoggedIn = false
private var deviceUuid: String? = null private var deviceUuid: String? = null
private var csrfToken: String? = null private var csrfToken: String? = null
@ -106,8 +92,6 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
isActivityCheckerServiceBound = false isActivityCheckerServiceBound = false
} }
} }
private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }
private val graphQLRepository: GraphQLRepository by lazy { GraphQLRepository.getInstance() }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
try { try {
@ -141,8 +125,10 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
// } catch (e: Exception) { // } catch (e: Exception) {
// Log.e(TAG, "onCreate: ", e) // Log.e(TAG, "onCreate: ", e)
// } // }
val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_nav_host) as NavHostFragment
navController = navHostFragment.navController
if (savedInstanceState == null) { if (savedInstanceState == null) {
setupBottomNavigationBar(true) setupNavigation(true)
} }
if (!BuildConfig.isPre) { if (!BuildConfig.isPre) {
val checkUpdates = Utils.settingsHelper.getBoolean(PreferenceKeys.CHECK_UPDATES) val checkUpdates = Utils.settingsHelper.getBoolean(PreferenceKeys.CHECK_UPDATES)
@ -154,7 +140,6 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
if (isLoggedIn && Utils.settingsHelper.getBoolean(PreferenceKeys.CHECK_ACTIVITY)) { if (isLoggedIn && Utils.settingsHelper.getBoolean(PreferenceKeys.CHECK_ACTIVITY)) {
bindActivityCheckerService() bindActivityCheckerService()
} }
supportFragmentManager.addOnBackStackChangedListener(this)
// Initialise the internal map // Initialise the internal map
tasksThread.execute { tasksThread.execute {
EmojiParser.getInstance(this) EmojiParser.getInstance(this)
@ -233,42 +218,18 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main_menu, menu) menuInflater.inflate(R.menu.main_menu, menu)
searchMenuItem = menu.findItem(R.id.search) searchMenuItem = menu.findItem(R.id.search)
val navController = currentNavControllerLiveData?.value
if (navController != null) {
val currentDestination = navController.currentDestination val currentDestination = navController.currentDestination
if (currentDestination != null) { if (currentDestination != null) {
@SuppressLint("RestrictedApi") val backStack = navController.backStack val backStack = navController.backQueue
setupMenu(backStack.size, currentDestination.id) setupMenu(backStack.size, currentDestination.id)
} }
}
// if (binding.searchInputLayout.getVisibility() == View.VISIBLE) {
// searchMenuItem.setVisible(false).setEnabled(false);
// return true;
// }
// searchMenuItem.setVisible(true).setEnabled(true);
// if (showSearch && currentNavControllerLiveData != null) {
// final NavController navController = currentNavControllerLiveData.getValue();
// if (navController != null) {
// final NavDestination currentDestination = navController.getCurrentDestination();
// if (currentDestination != null) {
// final int destinationId = currentDestination.getId();
// showSearch = destinationId == R.id.profileFragment;
// }
// }
// }
// if (!showSearch) {
// searchMenuItem.setVisible(false);
// return true;
// }
// return setupSearchView();
return true return true
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.search) { if (item.itemId == R.id.search) {
val navController = currentNavControllerLiveData?.value ?: return false
try { try {
navController.navigate(R.id.action_global_search) navController.navigate(getSearchDeepLink())
return true return true
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "onOptionsItemSelected: ", e) Log.e(TAG, "onOptionsItemSelected: ", e)
@ -279,20 +240,13 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
outState.putString(FIRST_FRAGMENT_GRAPH_INDEX_KEY, firstFragmentGraphIndex.toString()) // outState.putString(FIRST_FRAGMENT_GRAPH_INDEX_KEY, firstFragmentGraphIndex.toString())
outState.putString(LAST_SELECT_NAV_MENU_ID, binding.bottomNavView.selectedItemId.toString()) outState.putString(LAST_SELECT_NAV_MENU_ID, binding.bottomNavView.selectedItemId.toString())
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
} }
override fun onRestoreInstanceState(savedInstanceState: Bundle) { override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState) super.onRestoreInstanceState(savedInstanceState)
val key = savedInstanceState[FIRST_FRAGMENT_GRAPH_INDEX_KEY] as String?
if (key != null) {
try {
firstFragmentGraphIndex = key.toInt()
} catch (ignored: NumberFormatException) {
}
}
val lastSelected = savedInstanceState[LAST_SELECT_NAV_MENU_ID] as String? val lastSelected = savedInstanceState[LAST_SELECT_NAV_MENU_ID] as String?
if (lastSelected != null) { if (lastSelected != null) {
try { try {
@ -300,13 +254,11 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
} catch (ignored: NumberFormatException) { } catch (ignored: NumberFormatException) {
} }
} }
setupBottomNavigationBar(false) setupNavigation(false)
} }
override fun onSupportNavigateUp(): Boolean { override fun onSupportNavigateUp(): Boolean {
if (currentNavControllerLiveData == null) return false return navController.navigateUp(appBarConfiguration)
val navController = currentNavControllerLiveData?.value ?: return false
return navController.navigateUp()
} }
override fun onNewIntent(intent: Intent) { override fun onNewIntent(intent: Intent) {
@ -330,33 +282,24 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
instance = null instance = null
} }
override fun onBackPressed() { // override fun onBackPressed() {
var currentNavControllerBackStack = 2 // Log.d(TAG, "onBackPressed: ")
currentNavControllerLiveData?.let { // navController.navigateUp()
val navController = it.value // val backStack = navController.backQueue
if (navController != null) { // val currentNavControllerBackStack = backStack.size
@SuppressLint("RestrictedApi") val backStack = navController.backStack // if (isTaskRoot && isBackStackEmpty && currentNavControllerBackStack == 2) {
currentNavControllerBackStack = backStack.size // finishAfterTransition()
} // return
} // }
if (isTaskRoot && isBackStackEmpty && currentNavControllerBackStack == 2) { // if (!isFinishing) {
finishAfterTransition() // try {
return // super.onBackPressed()
} // } catch (e: Exception) {
if (!isFinishing) { // Log.e(TAG, "onBackPressed: ", e)
try { // finish()
super.onBackPressed() // }
} catch (e: Exception) { // }
Log.e(TAG, "onBackPressed: ", e) // }
finish()
}
}
}
override fun onBackStackChanged() {
val backStackEntryCount = supportFragmentManager.backStackEntryCount
isBackStackEmpty = backStackEntryCount == 0
}
private fun createNotificationChannels() { private fun createNotificationChannels() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
@ -391,117 +334,34 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
notificationManager.createNotificationChannel(silentNotificationChannel) notificationManager.createNotificationChannel(silentNotificationChannel)
} }
private fun setupBottomNavigationBar(setDefaultTabFromSettings: Boolean) { private fun setupNavigation(setDefaultTabFromSettings: Boolean) {
currentTabs = if (!isLoggedIn) setupAnonBottomNav() else setupMainBottomNav() currentTabs = if (isLoggedIn) setupMainBottomNav() else setupAnonBottomNav()
val mainNavList = currentTabs.stream()
.map(Tab::navigationResId)
.collect(Collectors.toList())
showBottomViewDestinations = currentTabs.asSequence().map { showBottomViewDestinations = currentTabs.asSequence().map {
it.startDestinationFragmentId it.startDestinationFragmentId
}.toMutableList().apply { }.toMutableList().apply {
add(R.id.postViewFragment) add(R.id.postViewFragment)
add(R.id.favoritesFragment) add(R.id.favoritesFragment)
add(R.id.profile_non_top)
} }
if (setDefaultTabFromSettings) { if (setDefaultTabFromSettings) {
setSelectedTab(currentTabs) setSelectedTab(currentTabs)
} else { } else {
binding.bottomNavView.selectedItemId = lastSelectedNavMenuId binding.bottomNavView.selectedItemId = lastSelectedNavMenuId
} }
val navControllerLiveData = NavigationExtensions.setupWithNavController( val navigatorProvider = navController.navigatorProvider
binding.bottomNavView, val navigator = navigatorProvider.getNavigator<NavGraphNavigator>("navigation")
mainNavList, val rootNavGraph = NavGraph(navigator)
supportFragmentManager, val navInflater = navController.navInflater
R.id.main_nav_host, val topLevelDestinations = currentTabs.map { navInflater.inflate(it.navigationResId) }
intent, rootNavGraph.id = R.id.root_nav_graph
firstFragmentGraphIndex rootNavGraph.label = "root_nav_graph"
) rootNavGraph.addDestinations(topLevelDestinations)
navControllerLiveData.observe(this, { navController: NavController? -> setupNavigation(binding.toolbar, navController) }) rootNavGraph.setStartDestination(if (startNavRootId != 0) startNavRootId else R.id.profile_nav_graph)
currentNavControllerLiveData = navControllerLiveData navController.graph = rootNavGraph
} binding.bottomNavView.setupWithNavController(navController)
appBarConfiguration = AppBarConfiguration(currentTabs.map { it.startDestinationFragmentId }.toSet())
private fun setSelectedTab(tabs: List<Tab>) { setupActionBarWithNavController(navController, appBarConfiguration)
val defaultTabResNameString = Utils.settingsHelper.getString(Constants.DEFAULT_TAB) navController.addOnDestinationChangedListener { _: NavController?, destination: NavDestination, arguments: Bundle? ->
try {
var navId = 0
if (!isEmpty(defaultTabResNameString)) {
navId = resources.getIdentifier(defaultTabResNameString, "navigation", packageName)
}
val navGraph = if (isLoggedIn) R.navigation.feed_nav_graph else R.navigation.profile_nav_graph
val defaultNavId = if (navId <= 0) navGraph else navId
var index = Iterators.indexOf(tabs.iterator()) { tab: Tab? ->
if (tab == null) return@indexOf false
tab.navigationResId == defaultNavId
}
if (index < 0 || index >= tabs.size) index = 0
firstFragmentGraphIndex = index
setBottomNavSelectedTab(tabs[index])
} catch (e: Exception) {
Log.e(TAG, "Error parsing id", e)
}
}
private fun setupAnonBottomNav(): List<Tab> {
val selectedItemId = binding.bottomNavView.selectedItemId
val favoriteTab = Tab(
R.drawable.ic_star_24,
getString(R.string.title_favorites),
false,
"favorites_nav_graph",
R.navigation.favorites_nav_graph,
R.id.favorites_nav_graph,
R.id.favoritesFragment
)
val profileTab = Tab(
R.drawable.ic_person_24,
getString(R.string.profile),
false,
"profile_nav_graph",
R.navigation.profile_nav_graph,
R.id.profile_nav_graph,
R.id.profileFragment
)
val moreTab = Tab(
R.drawable.ic_more_horiz_24,
getString(R.string.more),
false,
"more_nav_graph",
R.navigation.more_nav_graph,
R.id.more_nav_graph,
R.id.morePreferencesFragment
)
val menu = binding.bottomNavView.menu
menu.clear()
menu.add(0, favoriteTab.navigationRootId, 0, favoriteTab.title).setIcon(favoriteTab.iconResId)
menu.add(0, profileTab.navigationRootId, 0, profileTab.title).setIcon(profileTab.iconResId)
menu.add(0, moreTab.navigationRootId, 0, moreTab.title).setIcon(moreTab.iconResId)
if (selectedItemId != R.id.profile_nav_graph && selectedItemId != R.id.more_nav_graph && selectedItemId != R.id.favorites_nav_graph) {
setBottomNavSelectedTab(profileTab)
}
return ImmutableList.of(favoriteTab, profileTab, moreTab)
}
private fun setupMainBottomNav(): List<Tab> {
val menu = binding.bottomNavView.menu
menu.clear()
val navTabList = Utils.getNavTabList(this).first
for ((iconResId, title, _, _, _, navigationRootId) in navTabList) {
menu.add(0, navigationRootId, 0, title).setIcon(iconResId)
}
return navTabList
}
private fun setBottomNavSelectedTab(tab: Tab) {
binding.bottomNavView.selectedItemId = tab.navigationRootId
}
private fun setBottomNavSelectedTab(@IdRes navGraphRootId: Int) {
binding.bottomNavView.selectedItemId = navGraphRootId
}
private fun setupNavigation(toolbar: Toolbar, navController: NavController?) {
if (navController == null) return
NavigationUI.setupWithNavController(toolbar, navController)
navController.addOnDestinationChangedListener(OnDestinationChangedListener { _: NavController?, destination: NavDestination, arguments: Bundle? ->
if (destination.id == R.id.directMessagesThreadFragment && arguments != null) { if (destination.id == R.id.directMessagesThreadFragment && arguments != null) {
// Set the thread title earlier for better ux // Set the thread title earlier for better ux
val title = arguments.getString("title") val title = arguments.getString("title")
@ -519,7 +379,7 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
// below is a hack to check if we are at the end of the current stack, to setup the search view // below is a hack to check if we are at the end of the current stack, to setup the search view
binding.appBarLayout.setExpanded(true, true) binding.appBarLayout.setExpanded(true, true)
val destinationId = destination.id val destinationId = destination.id
@SuppressLint("RestrictedApi") val backStack = navController.backStack val backStack = navController.backQueue
setupMenu(backStack.size, destinationId) setupMenu(backStack.size, destinationId)
val contains = showBottomViewDestinations.contains(destinationId) val contains = showBottomViewDestinations.contains(destinationId)
binding.root.post { binding.root.post {
@ -531,7 +391,65 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
// explicitly hide keyboard when we navigate // explicitly hide keyboard when we navigate
val view = currentFocus val view = currentFocus
Utils.hideKeyboard(view) Utils.hideKeyboard(view)
}) }
setupReselection()
}
private fun setupReselection() {
binding.bottomNavView.setOnItemReselectedListener {
val navHostFragment = (supportFragmentManager.primaryNavigationFragment ?: return@setOnItemReselectedListener) as NavHostFragment
val currentFragment = navHostFragment.childFragmentManager.fragments.firstOrNull() ?: return@setOnItemReselectedListener
if (currentFragment is FeedFragment) {
currentFragment.scrollToTop()
return@setOnItemReselectedListener
}
val currentDestination = navController.currentDestination ?: return@setOnItemReselectedListener
val currentTabStartDestId = (navController.getBackStackEntry(it.itemId).destination as NavGraph).startDestinationId
if (currentDestination.id == currentTabStartDestId) return@setOnItemReselectedListener
navController.popBackStack(currentTabStartDestId, false)
}
}
private fun setSelectedTab(tabs: List<Tab>) {
val defaultTabResNameString = Utils.settingsHelper.getString(Constants.DEFAULT_TAB)
try {
var navId = 0
if (defaultTabResNameString.isNotBlank()) {
navId = resources.getIdentifier(defaultTabResNameString, "id", packageName)
}
val startFragmentNavResId = if (navId <= 0) R.id.profile_nav_graph else navId
val tab = tabs.firstOrNull { it.navigationRootId == startFragmentNavResId }
// if (index < 0 || index >= tabs.size) index = 0
val firstTab = tab ?: tabs[0]
startNavRootId = firstTab.navigationRootId
binding.bottomNavView.selectedItemId = firstTab.navigationRootId
} catch (e: Exception) {
Log.e(TAG, "Error parsing id", e)
}
}
private fun setupAnonBottomNav(): List<Tab> {
val selectedItemId = binding.bottomNavView.selectedItemId
val anonNavTabs = getAnonNavTabs(this)
val menu = binding.bottomNavView.menu
menu.clear()
for (tab in anonNavTabs) {
menu.add(0, tab.navigationRootId, 0, tab.title).setIcon(tab.iconResId)
}
if (selectedItemId != R.id.profile_nav_graph && selectedItemId != R.id.more_nav_graph && selectedItemId != R.id.favorites_nav_graph) {
binding.bottomNavView.selectedItemId = R.id.profile_nav_graph
}
return anonNavTabs
}
private fun setupMainBottomNav(): List<Tab> {
val menu = binding.bottomNavView.menu
menu.clear()
val navTabList = getLoggedInNavTabs(this).first
for (tab in navTabList) {
menu.add(0, tab.navigationRootId, 0, tab.title).setIcon(tab.iconResId)
}
return navTabList
} }
private fun setupMenu(backStackSize: Int, destinationId: Int) { private fun setupMenu(backStackSize: Int, destinationId: Int) {
@ -589,47 +507,10 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
fun navigateToThread(threadId: String?, threadTitle: String?) { fun navigateToThread(threadId: String?, threadTitle: String?) {
if (threadId == null || threadTitle == null) return if (threadId == null || threadTitle == null) return
currentNavControllerLiveData?.observe(this, object : Observer<NavController?> {
override fun onChanged(navController: NavController?) {
if (navController == null) return
if (navController.graph.id != R.id.direct_messages_nav_graph) return
try { try {
val currentDestination = navController.currentDestination navController.navigate(getDirectThreadDeepLink(threadId, threadTitle))
if (currentDestination != null && currentDestination.id == R.id.directMessagesInboxFragment) { } catch (e: Exception) {
// if we are already on the inbox page, navigate to the thread Log.e(TAG, "navigateToThread: ", e)
// need handler.post() to wait for the fragment manager to be ready to navigate
Handler(Looper.getMainLooper()).post {
val action = DirectMessageInboxFragmentDirections
.actionInboxToThread(threadId, threadTitle)
navController.navigate(action)
}
return
}
// add a destination change listener to navigate to thread once we are on the inbox page
navController.addOnDestinationChangedListener(object : OnDestinationChangedListener {
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
arguments: Bundle?,
) {
if (destination.id == R.id.directMessagesInboxFragment) {
val action = DirectMessageInboxFragmentDirections
.actionInboxToThread(threadId, threadTitle)
controller.navigate(action)
controller.removeOnDestinationChangedListener(this)
}
}
})
// pop back stack until we reach the inbox page
navController.popBackStack(R.id.directMessagesInboxFragment, false)
} finally {
currentNavControllerLiveData?.removeObserver(this)
}
}
})
val selectedItemId = binding.bottomNavView.selectedItemId
if (selectedItemId != R.navigation.direct_messages_nav_graph) {
setBottomNavSelectedTab(R.id.direct_messages_nav_graph)
} }
} }
@ -652,14 +533,9 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
} }
private fun showProfileView(intentModel: IntentModel) { private fun showProfileView(intentModel: IntentModel) {
val username = intentModel.text
// Log.d(TAG, "username: " + username);
val currentNavControllerLiveData = currentNavControllerLiveData ?: return
val navController = currentNavControllerLiveData.value
val bundle = Bundle()
bundle.putString("username", "@$username")
try { try {
navController?.navigate(R.id.action_global_profileFragment, bundle) val username = intentModel.text
navController.navigate(getProfileDeepLink(username))
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "showProfileView: ", e) Log.e(TAG, "showProfileView: ", e)
} }
@ -668,65 +544,39 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
private fun showPostView(intentModel: IntentModel) { private fun showPostView(intentModel: IntentModel) {
val shortCode = intentModel.text val shortCode = intentModel.text
// Log.d(TAG, "shortCode: " + shortCode); // 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 { try {
val media = if (isLoggedIn) mediaRepository.fetch(shortcodeToId(shortCode)) else graphQLRepository.fetchPost(shortCode) navController.navigate(getPostDeepLink(shortCode))
withContext(Dispatchers.Main) {
if (media == null) {
Toast.makeText(applicationContext, R.string.post_not_found, Toast.LENGTH_SHORT).show()
return@withContext
}
val currentNavControllerLiveData = currentNavControllerLiveData ?: return@withContext
val navController = currentNavControllerLiveData.value
val bundle = Bundle()
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, media)
try {
navController?.navigate(R.id.action_global_post_view, bundle)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "showPostView: ", e) Log.e(TAG, "showPostView: ", e)
} }
} }
} catch (e: Exception) {
Log.e(TAG, "showPostView: ", e)
} finally {
withContext(Dispatchers.Main) {
alertDialog.dismiss()
}
}
}
}
private fun showLocationView(intentModel: IntentModel) { private fun showLocationView(intentModel: IntentModel) {
val locationId = intentModel.text val locationId = intentModel.text
// Log.d(TAG, "locationId: " + locationId); // Log.d(TAG, "locationId: " + locationId);
val currentNavControllerLiveData = currentNavControllerLiveData ?: return try {
val navController = currentNavControllerLiveData.value navController.navigate(getLocationDeepLink(locationId))
val bundle = Bundle() } catch (e: Exception) {
bundle.putLong("locationId", locationId.toLong()) Log.e(TAG, "showLocationView: ", e)
navController?.navigate(R.id.action_global_locationFragment, bundle) }
} }
private fun showHashtagView(intentModel: IntentModel) { private fun showHashtagView(intentModel: IntentModel) {
val hashtag = intentModel.text val hashtag = intentModel.text
// Log.d(TAG, "hashtag: " + hashtag); // Log.d(TAG, "hashtag: " + hashtag);
val currentNavControllerLiveData = currentNavControllerLiveData ?: return try {
val navController = currentNavControllerLiveData.value navController.navigate(getHashtagDeepLink(hashtag))
val bundle = Bundle() } catch (e: Exception) {
bundle.putString("hashtag", hashtag) Log.e(TAG, "showHashtagView: ", e)
navController?.navigate(R.id.action_global_hashTagFragment, bundle) }
} }
private fun showActivityView() { private fun showActivityView() {
val currentNavControllerLiveData = currentNavControllerLiveData ?: return try {
val navController = currentNavControllerLiveData.value navController.navigate(getNotificationsDeepLink("notif"))
val bundle = Bundle() } catch (e: Exception) {
bundle.putString("type", "notif") Log.e(TAG, "showActivityView: ", e)
navController?.navigate(R.id.action_global_notificationsViewerFragment, bundle) }
} }
private fun bindActivityCheckerService() { private fun bindActivityCheckerService() {
@ -743,28 +593,27 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
val bottomNavView: BottomNavigationView val bottomNavView: BottomNavigationView
get() = binding.bottomNavView get() = binding.bottomNavView
fun setCollapsingView(view: View) { // fun setCollapsingView(view: View) {
try { // try {
binding.collapsingToolbarLayout.addView(view, 0) // binding.collapsingToolbarLayout.addView(view, 0)
} catch (e: Exception) { // } catch (e: Exception) {
Log.e(TAG, "setCollapsingView: ", e) // Log.e(TAG, "setCollapsingView: ", e)
} // }
} // }
//
fun removeCollapsingView(view: View) { // fun removeCollapsingView(view: View) {
try { // try {
binding.collapsingToolbarLayout.removeView(view) // binding.collapsingToolbarLayout.removeView(view)
} catch (e: Exception) { // } catch (e: Exception) {
Log.e(TAG, "removeCollapsingView: ", e) // Log.e(TAG, "removeCollapsingView: ", e)
} // }
} // }
fun resetToolbar() { fun resetToolbar() {
binding.appBarLayout.visibility = View.VISIBLE binding.appBarLayout.visibility = View.VISIBLE
setScrollingBehaviour() setScrollingBehaviour()
setSupportActionBar(binding.toolbar) setSupportActionBar(binding.toolbar)
val currentNavControllerLiveData = currentNavControllerLiveData ?: return setupActionBarWithNavController(navController, appBarConfiguration)
setupNavigation(binding.toolbar, currentNavControllerLiveData.value)
} }
val collapsingToolbarView: CollapsingToolbarLayout val collapsingToolbarView: CollapsingToolbarLayout
@ -808,8 +657,7 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
binding.appBarLayout.visibility = View.GONE binding.appBarLayout.visibility = View.GONE
removeScrollingBehaviour() removeScrollingBehaviour()
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
if (currentNavControllerLiveData == null) return NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration)
setupNavigation(toolbar, currentNavControllerLiveData?.value)
} }
val rootView: View val rootView: View
get() = binding.root get() = binding.root
@ -839,8 +687,8 @@ class MainActivity : BaseLanguageActivity(), FragmentManager.OnBackStackChangedL
companion object { companion object {
private const val TAG = "MainActivity" private const val TAG = "MainActivity"
private const val FIRST_FRAGMENT_GRAPH_INDEX_KEY = "firstFragmentGraphIndex"
private const val LAST_SELECT_NAV_MENU_ID = "lastSelectedNavMenuId" private const val LAST_SELECT_NAV_MENU_ID = "lastSelectedNavMenuId"
private val SEARCH_VISIBLE_DESTINATIONS: List<Int> = ImmutableList.of( private val SEARCH_VISIBLE_DESTINATIONS: List<Int> = ImmutableList.of(
R.id.feedFragment, R.id.feedFragment,
R.id.profileFragment, R.id.profileFragment,

View File

@ -0,0 +1,90 @@
package awais.instagrabber.customviews
import android.content.Context
import androidx.fragment.app.FragmentManager
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavOptions
import androidx.navigation.Navigator
import androidx.navigation.fragment.FragmentNavigator
import androidx.navigation.navOptions
import awais.instagrabber.R
import awais.instagrabber.fragments.settings.PreferenceKeys
import awais.instagrabber.utils.Utils
private val defaultNavOptions = navOptions {
anim {
enter = R.anim.slide_in_right
exit = R.anim.slide_out_left
popEnter = android.R.anim.slide_in_left
popExit = android.R.anim.slide_out_right
}
}
private val emptyNavOptions = navOptions {}
/**
* Needs to replace FragmentNavigator and replacing is done with name in annotation.
* Navigation method will use defaults for fragments transitions animations.
*/
@Navigator.Name("fragment")
class BarinstaFragmentNavigator(
context: Context,
fragmentManager: FragmentManager,
containerId: Int
) : FragmentNavigator(context, fragmentManager, containerId) {
override fun navigate(
entries: List<NavBackStackEntry>,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
) {
val disableTransitions = Utils.settingsHelper.getBoolean(PreferenceKeys.PREF_DISABLE_SCREEN_TRANSITIONS)
if (disableTransitions) {
super.navigate(entries, navOptions, navigatorExtras)
return
}
// this will try to fill in empty animations with defaults when no shared element transitions
// https://developer.android.com/guide/navigation/navigation-animate-transitions#shared-element
val hasSharedElements = navigatorExtras != null && navigatorExtras is Extras
val navOptions1 = if (hasSharedElements) navOptions else navOptions.fillEmptyAnimationsWithDefaults()
super.navigate(entries, navOptions1, navigatorExtras)
}
private fun NavOptions?.fillEmptyAnimationsWithDefaults(): NavOptions =
this?.copyNavOptionsWithDefaultAnimations() ?: defaultNavOptions
private fun NavOptions.copyNavOptionsWithDefaultAnimations(): NavOptions = let { originalNavOptions ->
navOptions {
launchSingleTop = originalNavOptions.shouldLaunchSingleTop()
popUpTo(originalNavOptions.popUpToId) {
inclusive = originalNavOptions.isPopUpToInclusive()
saveState = originalNavOptions.shouldPopUpToSaveState()
}
originalNavOptions.popUpToRoute?.let {
popUpTo(it) {
inclusive = originalNavOptions.isPopUpToInclusive()
saveState = originalNavOptions.shouldPopUpToSaveState()
}
}
restoreState = originalNavOptions.shouldRestoreState()
anim {
enter =
if (originalNavOptions.enterAnim == emptyNavOptions.enterAnim) defaultNavOptions.enterAnim
else originalNavOptions.enterAnim
exit =
if (originalNavOptions.exitAnim == emptyNavOptions.exitAnim) defaultNavOptions.exitAnim
else originalNavOptions.exitAnim
popEnter =
if (originalNavOptions.popEnterAnim == emptyNavOptions.popEnterAnim) defaultNavOptions.popEnterAnim
else originalNavOptions.popEnterAnim
popExit =
if (originalNavOptions.popExitAnim == emptyNavOptions.popExitAnim) defaultNavOptions.popExitAnim
else originalNavOptions.popExitAnim
}
}
}
private companion object {
private const val TAG = "FragmentNavigator"
}
}

View File

@ -0,0 +1,14 @@
package awais.instagrabber.customviews
import androidx.navigation.NavHostController
import androidx.navigation.fragment.NavHostFragment
class BarinstaNavHostFragment : NavHostFragment() {
override fun onCreateNavHostController(navHostController: NavHostController) {
super.onCreateNavHostController(navHostController)
navHostController.navigatorProvider.addNavigator(
// this replaces FragmentNavigator
BarinstaFragmentNavigator(requireContext(), childFragmentManager, id)
)
}
}

View File

@ -9,7 +9,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import awais.instagrabber.R; import awais.instagrabber.R;

View File

@ -1,75 +0,0 @@
package awais.instagrabber.customviews;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import androidx.navigation.NavDestination;
import androidx.navigation.NavOptions;
import androidx.navigation.Navigator;
import androidx.navigation.fragment.FragmentNavigator;
import awais.instagrabber.R;
@Navigator.Name("fragment")
public class FragmentNavigatorWithDefaultAnimations extends FragmentNavigator {
private final NavOptions emptyNavOptions = new NavOptions.Builder().build();
// private final NavOptions defaultNavOptions = new NavOptions.Builder()
// .setEnterAnim(R.animator.nav_default_enter_anim)
// .setExitAnim(R.animator.nav_default_exit_anim)
// .setPopEnterAnim(R.animator.nav_default_pop_enter_anim)
// .setPopExitAnim(R.animator.nav_default_pop_exit_anim)
// .build();
private final NavOptions defaultNavOptions = new NavOptions.Builder()
.setEnterAnim(R.anim.slide_in_right)
.setExitAnim(R.anim.slide_out_left)
.setPopEnterAnim(android.R.anim.slide_in_left)
.setPopExitAnim(android.R.anim.slide_out_right)
.build();
public FragmentNavigatorWithDefaultAnimations(@NonNull final Context context,
@NonNull final FragmentManager manager,
final int containerId) {
super(context, manager, containerId);
}
@Nullable
@Override
public NavDestination navigate(@NonNull final Destination destination,
@Nullable final Bundle args,
@Nullable final NavOptions navOptions,
@Nullable final Navigator.Extras navigatorExtras) {
// this will try to fill in empty animations with defaults when no shared element transitions are set
// https://developer.android.com/guide/navigation/navigation-animate-transitions#shared-element
final boolean shouldUseTransitionsInstead = navigatorExtras != null;
final NavOptions navOptions1 = shouldUseTransitionsInstead ? navOptions : fillEmptyAnimationsWithDefaults(navOptions);
return super.navigate(destination, args, navOptions1, navigatorExtras);
}
private NavOptions fillEmptyAnimationsWithDefaults(@Nullable final NavOptions navOptions) {
if (navOptions == null) {
return defaultNavOptions;
}
return copyNavOptionsWithDefaultAnimations(navOptions);
}
@NonNull
private NavOptions copyNavOptionsWithDefaultAnimations(@NonNull final NavOptions navOptions) {
return new NavOptions.Builder()
.setLaunchSingleTop(navOptions.shouldLaunchSingleTop())
.setPopUpTo(navOptions.getPopUpTo(), navOptions.isPopUpToInclusive())
.setEnterAnim(navOptions.getEnterAnim() == emptyNavOptions.getEnterAnim()
? defaultNavOptions.getEnterAnim() : navOptions.getEnterAnim())
.setExitAnim(navOptions.getExitAnim() == emptyNavOptions.getExitAnim()
? defaultNavOptions.getExitAnim() : navOptions.getExitAnim())
.setPopEnterAnim(navOptions.getPopEnterAnim() == emptyNavOptions.getPopEnterAnim()
? defaultNavOptions.getPopEnterAnim() : navOptions.getPopEnterAnim())
.setPopExitAnim(navOptions.getPopExitAnim() == emptyNavOptions.getPopExitAnim()
? defaultNavOptions.getPopExitAnim() : navOptions.getPopExitAnim())
.build();
}
}

View File

@ -1,60 +0,0 @@
package awais.instagrabber.customviews;
import android.os.Bundle;
import androidx.annotation.NavigationRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.navigation.NavController;
import androidx.navigation.Navigator;
import androidx.navigation.fragment.FragmentNavigator;
import androidx.navigation.fragment.NavHostFragment;
public class NavHostFragmentWithDefaultAnimations extends NavHostFragment {
private static final String KEY_GRAPH_ID = "android-support-nav:fragment:graphId";
private static final String KEY_START_DESTINATION_ARGS =
"android-support-nav:fragment:startDestinationArgs";
private static final String KEY_NAV_CONTROLLER_STATE =
"android-support-nav:fragment:navControllerState";
private static final String KEY_DEFAULT_NAV_HOST = "android-support-nav:fragment:defaultHost";
@NonNull
public static NavHostFragment create(@NavigationRes int graphResId) {
return create(graphResId, null);
}
@NonNull
public static NavHostFragment create(@NavigationRes int graphResId,
@Nullable Bundle startDestinationArgs) {
Bundle b = null;
if (graphResId != 0) {
b = new Bundle();
b.putInt(KEY_GRAPH_ID, graphResId);
}
if (startDestinationArgs != null) {
if (b == null) {
b = new Bundle();
}
b.putBundle(KEY_START_DESTINATION_ARGS, startDestinationArgs);
}
final NavHostFragmentWithDefaultAnimations result = new NavHostFragmentWithDefaultAnimations();
if (b != null) {
result.setArguments(b);
}
return result;
}
@NonNull
@Override
protected Navigator<? extends FragmentNavigator.Destination> createFragmentNavigator() {
return new FragmentNavigatorWithDefaultAnimations(requireContext(), getChildFragmentManager(), getId());
}
@Override
protected void onCreateNavController(@NonNull final NavController navController) {
super.onCreateNavController(navController);
navController.getNavigatorProvider()
.addNavigator(new FragmentNavigatorWithDefaultAnimations(requireContext(), getChildFragmentManager(), getId()));
}
}

View File

@ -3,12 +3,11 @@ package awais.instagrabber.customviews;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner; import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.LinearSmoothScroller; import androidx.recyclerview.widget.LinearSmoothScroller;
@ -27,24 +26,18 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Function;
import awais.instagrabber.adapters.FeedAdapterV2; import awais.instagrabber.adapters.FeedAdapterV2;
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration; import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.customviews.helpers.PostFetcher; import awais.instagrabber.customviews.helpers.PostFetcher;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoaderAtEdge; 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.models.PostsLayoutPreferences;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.utils.KeywordsFilterUtils;
import awais.instagrabber.utils.ResponseBodyUtils; import awais.instagrabber.utils.ResponseBodyUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.MediaViewModel; import awais.instagrabber.viewmodels.MediaViewModel;
import awais.instagrabber.workers.DownloadWorker; import awais.instagrabber.workers.DownloadWorker;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class PostsRecyclerView extends RecyclerView { public class PostsRecyclerView extends RecyclerView {
private static final String TAG = "PostsRecyclerView"; private static final String TAG = "PostsRecyclerView";
@ -52,7 +45,6 @@ public class PostsRecyclerView extends RecyclerView {
private PostsLayoutPreferences layoutPreferences; private PostsLayoutPreferences layoutPreferences;
private PostFetcher.PostFetchService postFetchService; private PostFetcher.PostFetchService postFetchService;
private Transition transition; private Transition transition;
private PostFetcher postFetcher;
private ViewModelStoreOwner viewModelStoreOwner; private ViewModelStoreOwner viewModelStoreOwner;
private FeedAdapterV2 feedAdapter; private FeedAdapterV2 feedAdapter;
private LifecycleOwner lifeCycleOwner; private LifecycleOwner lifeCycleOwner;
@ -63,40 +55,9 @@ public class PostsRecyclerView extends RecyclerView {
private FeedAdapterV2.FeedItemCallback feedItemCallback; private FeedAdapterV2.FeedItemCallback feedItemCallback;
private boolean shouldScrollToTop; private boolean shouldScrollToTop;
private FeedAdapterV2.SelectionModeCallback selectionModeCallback; 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 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()) { private final RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(getContext()) {
@Override @Override
protected int getVerticalSnapPreference() { protected int getVerticalSnapPreference() {
@ -199,18 +160,22 @@ public class PostsRecyclerView extends RecyclerView {
private void initSelf() { private void initSelf() {
try { try {
mediaViewModel = new ViewModelProvider(viewModelStoreOwner).get(MediaViewModel.class); mediaViewModel = new ViewModelProvider(
viewModelStoreOwner,
new MediaViewModel.ViewModelFactory(postFetchService)
).get(MediaViewModel.class);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "initSelf: ", e); Log.e(TAG, "initSelf: ", e);
} }
if (mediaViewModel == null) return; 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); // postDelayed(this::fetchMoreIfPossible, 1000);
if (!shouldScrollToTop) return; if (!shouldScrollToTop) return;
shouldScrollToTop = false; shouldScrollToTop = false;
post(() -> smoothScrollToPosition(0)); post(() -> smoothScrollToPosition(0));
})); }));
postFetcher = new PostFetcher(postFetchService, fetchListener);
if (layoutPreferences.getHasGap()) { if (layoutPreferences.getHasGap()) {
addItemDecoration(gridSpacingItemDecoration); addItemDecoration(gridSpacingItemDecoration);
} }
@ -218,18 +183,20 @@ public class PostsRecyclerView extends RecyclerView {
setNestedScrollingEnabled(true); setNestedScrollingEnabled(true);
setItemAnimator(null); setItemAnimator(null);
lazyLoader = new RecyclerLazyLoaderAtEdge(layoutManager, (page) -> { lazyLoader = new RecyclerLazyLoaderAtEdge(layoutManager, (page) -> {
if (postFetcher.hasMore()) { if (mediaViewModel.hasMore()) {
postFetcher.fetch(); mediaViewModel.fetch();
dispatchFetchStatus(); dispatchFetchStatus();
} }
}); });
addOnScrollListener(lazyLoader); addOnScrollListener(lazyLoader);
postFetcher.fetch(); if (mediaListLiveData.getValue() == null || mediaListLiveData.getValue().isEmpty()) {
mediaViewModel.fetch();
dispatchFetchStatus(); dispatchFetchStatus();
} }
}
private void fetchMoreIfPossible() { private void fetchMoreIfPossible() {
if (!postFetcher.hasMore()) return; if (!mediaViewModel.hasMore()) return;
if (feedAdapter.getItemCount() == 0) return; if (feedAdapter.getItemCount() == 0) return;
final LayoutManager layoutManager = getLayoutManager(); final LayoutManager layoutManager = getLayoutManager();
if (!(layoutManager instanceof StaggeredGridLayoutManager)) return; if (!(layoutManager instanceof StaggeredGridLayoutManager)) return;
@ -238,7 +205,7 @@ public class PostsRecyclerView extends RecyclerView {
if (allNoPosition) return; if (allNoPosition) return;
final boolean match = Arrays.stream(itemPositions).anyMatch(position -> position == feedAdapter.getItemCount() - 1); final boolean match = Arrays.stream(itemPositions).anyMatch(position -> position == feedAdapter.getItemCount() - 1);
if (!match) return; if (!match) return;
postFetcher.fetch(); mediaViewModel.fetch();
dispatchFetchStatus(); dispatchFetchStatus();
} }
@ -268,6 +235,7 @@ public class PostsRecyclerView extends RecyclerView {
private List<String> getDisplayUrl(final Media feedModel) { private List<String> getDisplayUrl(final Media feedModel) {
List<String> urls = Collections.emptyList(); List<String> urls = Collections.emptyList();
if (feedModel == null || feedModel.getType() == null) return urls;
switch (feedModel.getType()) { switch (feedModel.getType()) {
case MEDIA_TYPE_IMAGE: case MEDIA_TYPE_IMAGE:
case MEDIA_TYPE_VIDEO: case MEDIA_TYPE_VIDEO:
@ -320,20 +288,18 @@ public class PostsRecyclerView extends RecyclerView {
} }
public void refresh() { public void refresh() {
refresh = true; shouldScrollToTop = true;
if (lazyLoader != null) { if (lazyLoader != null) {
lazyLoader.resetState(); lazyLoader.resetState();
} }
if (postFetcher != null) { if (mediaViewModel != null) {
// mediaViewModel.getList().postValue(Collections.emptyList()); mediaViewModel.refresh();
postFetcher.reset();
postFetcher.fetch();
} }
dispatchFetchStatus(); dispatchFetchStatus();
} }
public boolean isFetching() { public boolean isFetching() {
return postFetcher != null && postFetcher.isFetching(); return mediaViewModel != null && mediaViewModel.isFetching();
} }
public PostsRecyclerView addFetchStatusChangeListener(final FetchStatusChangeListener fetchStatusChangeListener) { public PostsRecyclerView addFetchStatusChangeListener(final FetchStatusChangeListener fetchStatusChangeListener) {
@ -369,6 +335,7 @@ public class PostsRecyclerView extends RecyclerView {
protected void onDetachedFromWindow() { protected void onDetachedFromWindow() {
super.onDetachedFromWindow(); super.onDetachedFromWindow();
lifeCycleOwner = null; lifeCycleOwner = null;
initCalled = false;
} }
@Override @Override

View File

@ -21,7 +21,7 @@ public class PostFetcher {
} }
public void fetch() { public void fetch() {
if (!fetching) { if (fetching) return;
fetching = true; fetching = true;
postFetchService.fetch(new FetchListener<List<Media>>() { postFetchService.fetch(new FetchListener<List<Media>>() {
@Override @Override
@ -36,7 +36,6 @@ public class PostFetcher {
} }
}); });
} }
}
public void reset() { public void reset() {
postFetchService.reset(); postFetchService.reset();

View File

@ -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()
}
}
}
}
}

View File

@ -80,7 +80,6 @@ public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
initAvatarsToggle(); initAvatarsToggle();
initCornersToggle(); initCornersToggle();
initGapToggle(); initGapToggle();
initAnimationDisableToggle();
} }
private void initLayoutToggle() { private void initLayoutToggle() {
@ -170,11 +169,6 @@ public class PostsLayoutPreferencesDialogFragment extends DialogFragment {
binding.showGapToggle.setOnCheckedChangeListener((buttonView, isChecked) -> preferencesBuilder.setHasGap(isChecked)); binding.showGapToggle.setOnCheckedChangeListener((buttonView, isChecked) -> preferencesBuilder.setHasGap(isChecked));
} }
private void initAnimationDisableToggle() {
binding.disableAnimationToggle.setChecked(preferencesBuilder.isAnimationDisabled());
binding.disableAnimationToggle.setOnCheckedChangeListener((buttonView, isChecked) -> preferencesBuilder.setAnimationDisabled(isChecked));
}
private int getSelectedLayoutId() { private int getSelectedLayoutId() {
switch (preferencesBuilder.getType()) { switch (preferencesBuilder.getType()) {
case STAGGERED_GRID: case STAGGERED_GRID:

View File

@ -4,7 +4,6 @@ import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.os.Bundle; import android.os.Bundle;
import android.util.Pair;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -28,7 +27,9 @@ import awais.instagrabber.adapters.TabsAdapter;
import awais.instagrabber.adapters.viewholder.TabViewHolder; import awais.instagrabber.adapters.viewholder.TabViewHolder;
import awais.instagrabber.fragments.settings.PreferenceKeys; import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.models.Tab; import awais.instagrabber.models.Tab;
import awais.instagrabber.utils.NavigationHelperKt;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import kotlin.Pair;
import static androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG; import static androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG;
import static androidx.recyclerview.widget.ItemTouchHelper.DOWN; import static androidx.recyclerview.widget.ItemTouchHelper.DOWN;
@ -235,8 +236,9 @@ public class TabOrderPreferenceDialogFragment extends DialogFragment {
} }
private void saveNewOrder() { private void saveNewOrder() {
final String newOrderString = newOrderTabs.stream() final String newOrderString = newOrderTabs
.map(Tab::getGraphName) .stream()
.map(tab -> NavigationHelperKt.getNavGraphNameForNavRootId(tab.getNavigationRootId()))
.collect(Collectors.joining(",")); .collect(Collectors.joining(","));
Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString); Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString);
} }
@ -258,12 +260,12 @@ public class TabOrderPreferenceDialogFragment extends DialogFragment {
itemTouchHelper.attachToRecyclerView(list); itemTouchHelper.attachToRecyclerView(list);
adapter = new TabsAdapter(tabAdapterCallback); adapter = new TabsAdapter(tabAdapterCallback);
list.setAdapter(adapter); list.setAdapter(adapter);
final Pair<List<Tab>, List<Tab>> navTabListPair = Utils.getNavTabList(context); final Pair<List<Tab>, List<Tab>> navTabListPair = NavigationHelperKt.getLoggedInNavTabs(context);
tabsInPref = navTabListPair.first; tabsInPref = navTabListPair.getFirst();
// initially set newOrderTabs and newOtherTabs same as current tabs // initially set newOrderTabs and newOtherTabs same as current tabs
newOrderTabs = navTabListPair.first; newOrderTabs = navTabListPair.getFirst();
newOtherTabs = navTabListPair.second; newOtherTabs = navTabListPair.getSecond();
adapter.submitList(navTabListPair.first, navTabListPair.second); adapter.submitList(navTabListPair.getFirst(), navTabListPair.getSecond());
return list; return list;
} }

View File

@ -16,18 +16,20 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import java.time.format.DateTimeFormatter;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import awais.instagrabber.R;
import awais.instagrabber.databinding.DialogTimeSettingsBinding; import awais.instagrabber.databinding.DialogTimeSettingsBinding;
import awais.instagrabber.utils.DateUtils;
import awais.instagrabber.utils.LocaleUtils; import awais.instagrabber.utils.LocaleUtils;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
public final class TimeSettingsDialog extends DialogFragment implements AdapterView.OnItemSelectedListener, CompoundButton.OnCheckedChangeListener, public final class TimeSettingsDialog extends DialogFragment implements AdapterView.OnItemSelectedListener, CompoundButton.OnCheckedChangeListener,
View.OnClickListener, TextWatcher { View.OnClickListener, TextWatcher {
private DialogTimeSettingsBinding timeSettingsBinding; private DialogTimeSettingsBinding binding;
private final LocalDateTime magicDate; private final LocalDateTime magicDate;
private DateTimeFormatter currentFormat; private DateTimeFormatter currentFormat;
private String selectedFormat; private String selectedFormat;
@ -55,57 +57,67 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
timeSettingsBinding = DialogTimeSettingsBinding.inflate(inflater, container, false); binding = DialogTimeSettingsBinding.inflate(inflater, container, false);
timeSettingsBinding.cbCustomFormat.setOnCheckedChangeListener(this); binding.cbCustomFormat.setOnCheckedChangeListener(this);
timeSettingsBinding.cbCustomFormat.setChecked(customDateTimeFormatEnabled); binding.cbCustomFormat.setChecked(customDateTimeFormatEnabled);
timeSettingsBinding.cbSwapTimeDate.setChecked(swapDateTimeEnabled); binding.cbSwapTimeDate.setChecked(swapDateTimeEnabled);
timeSettingsBinding.etCustomFormat.setText(customDateTimeFormat); binding.customFormatEditText.setText(customDateTimeFormat);
final String[] dateTimeFormat = dateTimeSelection.split(";"); // output = time;separator;date final String[] dateTimeFormat = dateTimeSelection.split(";"); // output = time;separator;date
timeSettingsBinding.spTimeFormat.setSelection(Integer.parseInt(dateTimeFormat[0])); binding.spTimeFormat.setSelection(Integer.parseInt(dateTimeFormat[0]));
timeSettingsBinding.spSeparator.setSelection(Integer.parseInt(dateTimeFormat[1])); binding.spSeparator.setSelection(Integer.parseInt(dateTimeFormat[1]));
timeSettingsBinding.spDateFormat.setSelection(Integer.parseInt(dateTimeFormat[2])); binding.spDateFormat.setSelection(Integer.parseInt(dateTimeFormat[2]));
timeSettingsBinding.cbSwapTimeDate.setOnCheckedChangeListener(this); binding.cbSwapTimeDate.setOnCheckedChangeListener(this);
refreshTimeFormat(); refreshTimeFormat();
timeSettingsBinding.spTimeFormat.setOnItemSelectedListener(this); binding.spTimeFormat.setOnItemSelectedListener(this);
timeSettingsBinding.spDateFormat.setOnItemSelectedListener(this); binding.spDateFormat.setOnItemSelectedListener(this);
timeSettingsBinding.spSeparator.setOnItemSelectedListener(this); binding.spSeparator.setOnItemSelectedListener(this);
timeSettingsBinding.etCustomFormat.addTextChangedListener(this); binding.customFormatEditText.addTextChangedListener(this);
timeSettingsBinding.btnConfirm.setOnClickListener(this); binding.btnConfirm.setOnClickListener(this);
timeSettingsBinding.btnInfo.setOnClickListener(this); binding.customFormatField.setEndIconOnClickListener(this);
return timeSettingsBinding.getRoot(); return binding.getRoot();
} }
private void refreshTimeFormat() { private void refreshTimeFormat() {
if (timeSettingsBinding.cbCustomFormat.isChecked()) final boolean isCustom = binding.cbCustomFormat.isChecked();
selectedFormat = timeSettingsBinding.etCustomFormat.getText().toString(); if (isCustom) {
else { final Editable text = binding.customFormatEditText.getText();
final String sepStr = String.valueOf(timeSettingsBinding.spSeparator.getSelectedItem()); if (text != null) {
final String timeStr = String.valueOf(timeSettingsBinding.spTimeFormat.getSelectedItem()); selectedFormat = text.toString();
final String dateStr = String.valueOf(timeSettingsBinding.spDateFormat.getSelectedItem()); }
} else {
final String sepStr = String.valueOf(binding.spSeparator.getSelectedItem());
final String timeStr = String.valueOf(binding.spTimeFormat.getSelectedItem());
final String dateStr = String.valueOf(binding.spDateFormat.getSelectedItem());
final boolean isSwapTime = timeSettingsBinding.cbSwapTimeDate.isChecked(); final boolean isSwapTime = binding.cbSwapTimeDate.isChecked();
final boolean isBlankSeparator = timeSettingsBinding.spSeparator.getSelectedItemPosition() <= 0; final boolean isBlankSeparator = binding.spSeparator.getSelectedItemPosition() <= 0;
selectedFormat = (isSwapTime ? dateStr : timeStr) selectedFormat = (isSwapTime ? dateStr : timeStr)
+ (isBlankSeparator ? " " : " '" + sepStr + "' ") + (isBlankSeparator ? " " : " '" + sepStr + "' ")
+ (isSwapTime ? timeStr : dateStr); + (isSwapTime ? timeStr : dateStr);
} }
timeSettingsBinding.btnConfirm.setEnabled(true); binding.btnConfirm.setEnabled(true);
try { try {
currentFormat = DateTimeFormatter.ofPattern(selectedFormat, LocaleUtils.getCurrentLocale()); currentFormat = DateTimeFormatter.ofPattern(selectedFormat, LocaleUtils.getCurrentLocale());
timeSettingsBinding.timePreview.setText(magicDate.format(currentFormat)); if (isCustom) {
final boolean valid = !TextUtils.isEmpty(selectedFormat) && DateUtils.checkFormatterValid(currentFormat);
binding.customFormatField.setError(valid ? null :getString(R.string.invalid_format));
if (!valid) {
binding.btnConfirm.setEnabled(false);
} }
catch (Exception e) { }
timeSettingsBinding.btnConfirm.setEnabled(false); binding.timePreview.setText(magicDate.format(currentFormat));
timeSettingsBinding.timePreview.setText(null); } catch (Exception e) {
binding.btnConfirm.setEnabled(false);
binding.timePreview.setText(null);
} }
} }
@ -116,16 +128,14 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
@Override @Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) { public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
if (buttonView == timeSettingsBinding.cbCustomFormat) { if (buttonView == binding.cbCustomFormat) {
final View parent = (View) timeSettingsBinding.etCustomFormat.getParent(); binding.customFormatField.setVisibility(isChecked ? View.VISIBLE : View.GONE);
parent.setVisibility(isChecked ? View.VISIBLE : View.GONE); binding.customFormatField.setEnabled(isChecked);
timeSettingsBinding.etCustomFormat.setEnabled(isChecked);
timeSettingsBinding.btnInfo.setEnabled(isChecked);
timeSettingsBinding.spTimeFormat.setEnabled(!isChecked); binding.spTimeFormat.setEnabled(!isChecked);
timeSettingsBinding.spDateFormat.setEnabled(!isChecked); binding.spDateFormat.setEnabled(!isChecked);
timeSettingsBinding.spSeparator.setEnabled(!isChecked); binding.spSeparator.setEnabled(!isChecked);
timeSettingsBinding.cbSwapTimeDate.setEnabled(!isChecked); binding.cbSwapTimeDate.setEnabled(!isChecked);
} }
refreshTimeFormat(); refreshTimeFormat();
} }
@ -137,20 +147,21 @@ public final class TimeSettingsDialog extends DialogFragment implements AdapterV
@Override @Override
public void onClick(final View v) { public void onClick(final View v) {
if (v == timeSettingsBinding.btnConfirm) { if (v == binding.btnConfirm) {
if (onConfirmListener != null) { if (onConfirmListener != null) {
onConfirmListener.onConfirm( onConfirmListener.onConfirm(
timeSettingsBinding.cbCustomFormat.isChecked(), binding.cbCustomFormat.isChecked(),
timeSettingsBinding.spTimeFormat.getSelectedItemPosition(), binding.spTimeFormat.getSelectedItemPosition(),
timeSettingsBinding.spSeparator.getSelectedItemPosition(), binding.spSeparator.getSelectedItemPosition(),
timeSettingsBinding.spDateFormat.getSelectedItemPosition(), binding.spDateFormat.getSelectedItemPosition(),
selectedFormat, selectedFormat,
timeSettingsBinding.cbSwapTimeDate.isChecked()); binding.cbSwapTimeDate.isChecked());
} }
dismiss(); dismiss();
} else if (v == timeSettingsBinding.btnInfo) { } else if (v == binding.customFormatField.findViewById(R.id.text_input_end_icon)) {
timeSettingsBinding.customPanel.setVisibility(timeSettingsBinding.customPanel binding.customPanel.setVisibility(
.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); binding.customPanel.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE
);
} }
} }

View File

@ -28,7 +28,6 @@ import androidx.appcompat.app.AlertDialog;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.graphics.ColorUtils; import androidx.core.graphics.ColorUtils;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -53,7 +52,9 @@ import awais.instagrabber.databinding.FragmentCollectionPostsBinding;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.enums.PostItemType; import awais.instagrabber.models.enums.PostItemType;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.saved.SavedCollection; import awais.instagrabber.repositories.responses.saved.SavedCollection;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
@ -76,7 +77,6 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
private Set<Media> selectedFeedModels; private Set<Media> selectedFeedModels;
private CollectionService collectionService; private CollectionService collectionService;
private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_SAVED_POSTS_LAYOUT); private PostsLayoutPreferences layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_SAVED_POSTS_LAYOUT);
private MenuItem deleteMenu, editMenu;
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) { private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(false) {
@Override @Override
@ -117,12 +117,18 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
@Override @Override
public void onCommentsClick(final Media feedModel) { public void onCommentsClick(final Media feedModel) {
final NavDirections commentsAction = CollectionPostsFragmentDirections.actionGlobalCommentsViewerFragment( final User user = feedModel.getUser();
if (user == null) return;
try {
final NavDirections commentsAction = CollectionPostsFragmentDirections.actionToComments(
feedModel.getCode(), feedModel.getCode(),
feedModel.getPk(), feedModel.getPk(),
feedModel.getUser().getPk() user.getPk()
); );
NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(commentsAction); NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(commentsAction);
} catch (Exception e) {
Log.e(TAG, "onCommentsClick: ", e);
}
} }
@Override @Override
@ -134,14 +140,24 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
@Override @Override
public void onHashtagClick(final String hashtag) { public void onHashtagClick(final String hashtag) {
final NavDirections action = CollectionPostsFragmentDirections.actionGlobalHashTagFragment(hashtag); try {
final NavDirections action = CollectionPostsFragmentDirections.actionToHashtag(hashtag);
NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action); NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onHashtagClick: ", e);
}
} }
@Override @Override
public void onLocationClick(final Media feedModel) { public void onLocationClick(final Media feedModel) {
final NavDirections action = CollectionPostsFragmentDirections.actionGlobalLocationFragment(feedModel.getLocation().getPk()); final Location location = feedModel.getLocation();
if (location == null) return;
try {
final NavDirections action = CollectionPostsFragmentDirections.actionToLocation(location.getPk());
NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action); NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onLocationClick: ", e);
}
} }
@Override @Override
@ -151,12 +167,16 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
@Override @Override
public void onNameClick(final Media feedModel) { public void onNameClick(final Media feedModel) {
navigateToProfile("@" + feedModel.getUser().getUsername()); final User user = feedModel.getUser();
if (user == null) return;
navigateToProfile("@" + user.getUsername());
} }
@Override @Override
public void onProfilePicClick(final Media feedModel) { public void onProfilePicClick(final Media feedModel) {
navigateToProfile("@" + feedModel.getUser().getUsername()); final User user = feedModel.getUser();
if (user == null) return;
navigateToProfile("@" + user.getUsername());
} }
@Override @Override
@ -170,12 +190,9 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
} }
private void openPostDialog(final Media feedModel, final int position) { private void openPostDialog(final Media feedModel, final int position) {
final NavController navController = NavHostFragment.findNavController(CollectionPostsFragment.this);
final Bundle bundle = new Bundle();
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, feedModel);
bundle.putInt(PostViewV2Fragment.ARG_SLIDER_POSITION, position);
try { try {
navController.navigate(R.id.action_global_post_view, bundle); final NavDirections action = CollectionPostsFragmentDirections.actionToPost(feedModel, position);
NavHostFragment.findNavController(CollectionPostsFragment.this).navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "openPostDialog: ", e); Log.e(TAG, "openPostDialog: ", e);
} }
@ -262,10 +279,10 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
@Override @Override
public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
inflater.inflate(R.menu.collection_posts_menu, menu); inflater.inflate(R.menu.collection_posts_menu, menu);
deleteMenu = menu.findItem(R.id.delete); final MenuItem deleteMenu = menu.findItem(R.id.delete);
if (deleteMenu != null) if (deleteMenu != null)
deleteMenu.setVisible(savedCollection.getCollectionType().equals("MEDIA")); deleteMenu.setVisible(savedCollection.getCollectionType().equals("MEDIA"));
editMenu = menu.findItem(R.id.edit); final MenuItem editMenu = menu.findItem(R.id.edit);
if (editMenu != null) if (editMenu != null)
editMenu.setVisible(savedCollection.getCollectionType().equals("MEDIA")); editMenu.setVisible(savedCollection.getCollectionType().equals("MEDIA"));
} }
@ -408,9 +425,7 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
} }
private void setupCover() { private void setupCover() {
final String coverUrl = ResponseBodyUtils.getImageUrl(savedCollection.getCoverMediaList() == null final String coverUrl = ResponseBodyUtils.getImageUrl(savedCollection.getCoverMediaList().get(0));
? savedCollection.getCoverMedia()
: savedCollection.getCoverMediaList().get(0));
final DraweeController controller = Fresco final DraweeController controller = Fresco
.newDraweeControllerBuilder() .newDraweeControllerBuilder()
.setOldController(binding.cover.getController()) .setOldController(binding.cover.getController())
@ -443,7 +458,6 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
.setFeedItemCallback(feedItemCallback) .setFeedItemCallback(feedItemCallback)
.setSelectionModeCallback(selectionModeCallback) .setSelectionModeCallback(selectionModeCallback)
.init(); .init();
binding.swipeRefreshLayout.setRefreshing(true);
} }
private void updateSwipeRefreshState() { private void updateSwipeRefreshState() {
@ -453,10 +467,12 @@ public class CollectionPostsFragment extends Fragment implements SwipeRefreshLay
} }
private void navigateToProfile(final String username) { private void navigateToProfile(final String username) {
final NavController navController = NavHostFragment.findNavController(this); try {
final Bundle bundle = new Bundle(); final NavDirections action = CollectionPostsFragmentDirections.actionToProfile().setUsername(username);
bundle.putString("username", username); NavHostFragment.findNavController(this).navigate(action);
navController.navigate(R.id.action_global_profileFragment, bundle); } catch (Exception e) {
Log.e(TAG, "navigateToProfile: ", e);
}
} }
private void showPostsLayoutPreferences() { private void showPostsLayoutPreferences() {

View File

@ -8,7 +8,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import awais.instagrabber.R import awais.instagrabber.R
@ -48,17 +48,17 @@ class FavoritesFragment : Fragment() {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
adapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT) adapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (!this::adapter.isInitialized) return if (!this::adapter.isInitialized) return
// refresh list every time in onViewStateRestored since it is cheaper than implementing pull down to refresh // refresh list every time in onViewStateRestored since it is cheaper than implementing pull down to refresh
favoritesViewModel.list.observe(viewLifecycleOwner, { favoritesViewModel.list.observe(viewLifecycleOwner, { list: List<Favorite?>? ->
list: List<Favorite?>? -> adapter.submitList(list, Runnable { adapter.submitList(list) {
adapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.ALLOW) adapter.stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.ALLOW
}) }
}) })
} }
@ -66,32 +66,31 @@ class FavoritesFragment : Fragment() {
adapter = FavoritesAdapter({ model: Favorite -> adapter = FavoritesAdapter({ model: Favorite ->
when (model.type) { when (model.type) {
FavoriteType.USER -> { FavoriteType.USER -> {
val username = model.query try {
// Log.d(TAG, "username: " + username); val username = model.query ?: return@FavoritesAdapter
val navController = NavHostFragment.findNavController(this) val actionToProfile = FavoritesFragmentDirections.actionToProfile().apply { this.username = username }
val bundle = Bundle() findNavController().navigate(actionToProfile)
bundle.putString("username", "@$username") } catch (e: Exception) {
navController.navigate(R.id.action_global_profileFragment, bundle) Log.e(TAG, "init: ", e)
}
} }
FavoriteType.LOCATION -> { FavoriteType.LOCATION -> {
val locationId = model.query ?: return@FavoritesAdapter
// Log.d(TAG, "locationId: " + locationId);
val navController = NavHostFragment.findNavController(this)
val bundle = Bundle()
try { try {
bundle.putLong("locationId", locationId.toLong()) val locationId = model.query ?: return@FavoritesAdapter
navController.navigate(R.id.action_global_locationFragment, bundle) val actionToLocation = FavoritesFragmentDirections.actionToLocation(locationId.toLong())
findNavController().navigate(actionToLocation)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "init: ", e) Log.e(TAG, "init: ", e)
} }
} }
FavoriteType.HASHTAG -> { FavoriteType.HASHTAG -> {
val hashtag = model.query try {
// Log.d(TAG, "hashtag: " + hashtag); val hashtag = model.query ?: return@FavoritesAdapter
val navController = NavHostFragment.findNavController(this) val actionToHashtag = FavoritesFragmentDirections.actionToHashtag(hashtag)
val bundle = Bundle() findNavController().navigate(actionToHashtag)
bundle.putString("hashtag", "#$hashtag") } catch (e: Exception) {
navController.navigate(R.id.action_global_hashTagFragment, bundle) Log.e(TAG, "init: ", e)
}
} }
else -> { else -> {
} }

View File

@ -1,51 +1,30 @@
package awais.instagrabber.fragments package awais.instagrabber.fragments
import android.content.Context
import android.content.res.Resources
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.view.*
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.NonNull
import androidx.annotation.Nullable
import androidx.appcompat.app.ActionBar import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import java.util.ArrayList
import awais.instagrabber.R import awais.instagrabber.R
import awais.instagrabber.adapters.FollowAdapter import awais.instagrabber.adapters.FollowAdapter
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader import awais.instagrabber.customviews.helpers.RecyclerLazyLoader
import awais.instagrabber.databinding.FragmentFollowersViewerBinding import awais.instagrabber.databinding.FragmentFollowersViewerBinding
import awais.instagrabber.models.Resource import awais.instagrabber.models.Resource
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.User
import awais.instagrabber.utils.AppExecutors
import awais.instagrabber.viewmodels.FollowViewModel import awais.instagrabber.viewmodels.FollowViewModel
import thoughtbot.expandableadapter.ExpandableGroup import thoughtbot.expandableadapter.ExpandableGroup
import java.util.*
class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener { class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
private val followModels: ArrayList<User> = ArrayList<User>()
private val followingModels: ArrayList<User> = ArrayList<User>()
private val followersModels: ArrayList<User> = ArrayList<User>()
private val allFollowing: ArrayList<User> = ArrayList<User>()
private val moreAvailable = true
private var isFollowersList = false private var isFollowersList = false
private var isCompare = false private var isCompare = false
private var shouldRefresh = true private var shouldRefresh = true
private var searching = false private var searching = false
private var profileId: Long = 0
private var username: String? = null private var username: String? = null
private var namePost: String? = null private var namePost: String? = null
private var type = 0 private var type = 0
@ -125,7 +104,7 @@ class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
type = if (isFollowersList) R.string.followers_type_followers else R.string.followers_type_following type = if (isFollowersList) R.string.followers_type_followers else R.string.followers_type_following
setSubtitle(type) setSubtitle(type)
val layoutManager = LinearLayoutManager(context) val layoutManager = LinearLayoutManager(context)
lazyLoader = RecyclerLazyLoader(layoutManager, { _, totalItemsCount -> lazyLoader = RecyclerLazyLoader(layoutManager) { _, totalItemsCount ->
binding.swipeRefreshLayout.isRefreshing = true binding.swipeRefreshLayout.isRefreshing = true
val liveData = if (searching) viewModel.search(isFollowersList) val liveData = if (searching) viewModel.search(isFollowersList)
else viewModel.fetch(isFollowersList, null) else viewModel.fetch(isFollowersList, null)
@ -133,7 +112,7 @@ class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
binding.swipeRefreshLayout.isRefreshing = it.status != Resource.Status.SUCCESS binding.swipeRefreshLayout.isRefreshing = it.status != Resource.Status.SUCCESS
layoutManager.scrollToPosition(totalItemsCount) layoutManager.scrollToPosition(totalItemsCount)
} }
}) }
binding.rvFollow.addOnScrollListener(lazyLoader) binding.rvFollow.addOnScrollListener(lazyLoader)
binding.rvFollow.layoutManager = layoutManager binding.rvFollow.layoutManager = layoutManager
viewModel.getList(isFollowersList).observe(viewLifecycleOwner) { viewModel.getList(isFollowersList).observe(viewLifecycleOwner) {
@ -167,7 +146,7 @@ class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
} }
override fun onQueryTextChange(query: String): Boolean { override fun onQueryTextChange(query: String): Boolean {
if (query.isNullOrEmpty()) { if (query.isEmpty()) {
if (!isCompare && searching) { if (!isCompare && searching) {
viewModel.setQuery(null, isFollowersList) viewModel.setQuery(null, isFollowersList)
viewModel.getSearch().removeObservers(viewLifecycleOwner) viewModel.getSearch().removeObservers(viewLifecycleOwner)
@ -216,7 +195,7 @@ class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
) { ) {
val groups: ArrayList<ExpandableGroup> = ArrayList<ExpandableGroup>(1) val groups: ArrayList<ExpandableGroup> = ArrayList<ExpandableGroup>(1)
if (isCompare && followingModels != null && followersModels != null && allFollowing != null) { if (isCompare && followingModels != null && followersModels != null && allFollowing != null) {
if (followingModels.size > 0) groups.add( if (followingModels.isNotEmpty()) groups.add(
ExpandableGroup( ExpandableGroup(
getString( getString(
R.string.followers_not_following, R.string.followers_not_following,
@ -224,7 +203,7 @@ class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
), followingModels ), followingModels
) )
) )
if (followersModels.size > 0) groups.add( if (followersModels.isNotEmpty()) groups.add(
ExpandableGroup( ExpandableGroup(
getString( getString(
R.string.followers_not_follower, R.string.followers_not_follower,
@ -232,7 +211,7 @@ class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
), followersModels ), followersModels
) )
) )
if (allFollowing.size > 0) groups.add( if (allFollowing.isNotEmpty()) groups.add(
ExpandableGroup( ExpandableGroup(
getString(R.string.followers_both_following), getString(R.string.followers_both_following),
allFollowing allFollowing
@ -244,17 +223,11 @@ class FollowViewerFragment : Fragment(), SwipeRefreshLayout.OnRefreshListener {
adapter = FollowAdapter({ v -> adapter = FollowAdapter({ v ->
val tag = v.tag val tag = v.tag
if (tag is User) { if (tag is User) {
val model = tag findNavController().navigate(FollowViewerFragmentDirections.actionToProfile().setUsername(tag.username))
val bundle = Bundle()
bundle.putString("username", model.username)
NavHostFragment.findNavController(this).navigate(R.id.action_global_profileFragment, bundle)
} }
}, groups) }, groups).also {
adapter!!.toggleGroup(0) it.toggleGroup(0)
binding.rvFollow.adapter = adapter!! binding.rvFollow.adapter = it
} }
companion object {
private const val TAG = "FollowViewerFragment"
} }
} }

View File

@ -26,7 +26,6 @@ import androidx.appcompat.app.ActionBar;
import androidx.constraintlayout.motion.widget.MotionLayout; import androidx.constraintlayout.motion.widget.MotionLayout;
import androidx.constraintlayout.motion.widget.MotionScene; import androidx.constraintlayout.motion.widget.MotionScene;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -75,13 +74,10 @@ import static awais.instagrabber.utils.Utils.settingsHelper;
public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "HashTagFragment"; private static final String TAG = "HashTagFragment";
public static final String ARG_HASHTAG = "hashtag";
private MainActivity fragmentActivity; private MainActivity fragmentActivity;
private FragmentHashtagBinding binding; private FragmentHashtagBinding binding;
private MotionLayout root; private MotionLayout root;
private boolean shouldRefresh = true; private boolean shouldRefresh = true;
// private boolean hasStories = false;
private boolean opening = false; private boolean opening = false;
private String hashtag; private String hashtag;
private Hashtag hashtagModel = null; private Hashtag hashtagModel = null;
@ -135,12 +131,18 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
@Override @Override
public void onCommentsClick(final Media feedModel) { public void onCommentsClick(final Media feedModel) {
final NavDirections commentsAction = HashTagFragmentDirections.actionGlobalCommentsViewerFragment( final User user = feedModel.getUser();
if (user == null) return;
try {
final NavDirections commentsAction = HashTagFragmentDirections.actionToComments(
feedModel.getCode(), feedModel.getCode(),
feedModel.getCode(), feedModel.getCode(),
feedModel.getUser().getPk() user.getPk()
); );
NavHostFragment.findNavController(HashTagFragment.this).navigate(commentsAction); NavHostFragment.findNavController(HashTagFragment.this).navigate(commentsAction);
} catch (Exception e) {
Log.e(TAG, "onCommentsClick: ", e);
}
} }
@Override @Override
@ -152,16 +154,24 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
@Override @Override
public void onHashtagClick(final String hashtag) { public void onHashtagClick(final String hashtag) {
final NavDirections action = HashTagFragmentDirections.actionGlobalHashTagFragment(hashtag); try {
final NavDirections action = HashTagFragmentDirections.actionToHashtag(hashtag);
NavHostFragment.findNavController(HashTagFragment.this).navigate(action); NavHostFragment.findNavController(HashTagFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onHashtagClick: ", e);
}
} }
@Override @Override
public void onLocationClick(final Media media) { public void onLocationClick(final Media media) {
final Location location = media.getLocation(); final Location location = media.getLocation();
if (location == null) return; if (location == null) return;
final NavDirections action = HashTagFragmentDirections.actionGlobalLocationFragment(location.getPk()); try {
final NavDirections action = HashTagFragmentDirections.actionToLocation(location.getPk());
NavHostFragment.findNavController(HashTagFragment.this).navigate(action); NavHostFragment.findNavController(HashTagFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onLocationClick: ", e);
}
} }
@Override @Override
@ -171,12 +181,16 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
@Override @Override
public void onNameClick(final Media feedModel) { public void onNameClick(final Media feedModel) {
navigateToProfile("@" + feedModel.getUser().getUsername()); final User user = feedModel.getUser();
if (user == null) return;
navigateToProfile("@" + user.getUsername());
} }
@Override @Override
public void onProfilePicClick(final Media feedModel) { public void onProfilePicClick(final Media feedModel) {
navigateToProfile("@" + feedModel.getUser().getUsername()); final User user = feedModel.getUser();
if (user == null) return;
navigateToProfile("@" + user.getUsername());
} }
@Override @Override
@ -196,7 +210,9 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
if (TextUtils.isEmpty(user.getUsername())) { if (TextUtils.isEmpty(user.getUsername())) {
// this only happens for anons // this only happens for anons
opening = true; opening = true;
graphQLRepository.fetchPost(feedModel.getCode(), CoroutineUtilsKt.getContinuation((media, throwable) -> { final String code = feedModel.getCode();
if (code == null) return;
graphQLRepository.fetchPost(code, CoroutineUtilsKt.getContinuation((media, throwable) -> {
opening = false; opening = false;
if (throwable != null) { if (throwable != null) {
Log.e(TAG, "Error", throwable); Log.e(TAG, "Error", throwable);
@ -208,12 +224,9 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
return; return;
} }
opening = true; opening = true;
final NavController navController = NavHostFragment.findNavController(HashTagFragment.this);
final Bundle bundle = new Bundle();
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, feedModel);
bundle.putInt(PostViewV2Fragment.ARG_SLIDER_POSITION, position);
try { try {
navController.navigate(R.id.action_global_post_view, bundle); final NavDirections action = HashTagFragmentDirections.actionToPost(feedModel, position);
NavHostFragment.findNavController(HashTagFragment.this).navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "openPostDialog: ", e); Log.e(TAG, "openPostDialog: ", e);
} }
@ -305,7 +318,7 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
@Override @Override
public void onRefresh() { public void onRefresh() {
binding.posts.refresh(); binding.posts.refresh();
// fetchStories(); // fetchStories();
} }
@Override @Override
@ -357,7 +370,6 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
.setFeedItemCallback(feedItemCallback) .setFeedItemCallback(feedItemCallback)
.setSelectionModeCallback(selectionModeCallback) .setSelectionModeCallback(selectionModeCallback)
.init(); .init();
binding.swipeRefreshLayout.setRefreshing(true);
binding.posts.addOnScrollListener(new RecyclerView.OnScrollListener() { binding.posts.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override @Override
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) { public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
@ -381,7 +393,6 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
} }
setTitle(); setTitle();
setupPosts(); setupPosts();
// fetchStories();
if (isLoggedIn) { if (isLoggedIn) {
hashtagDetailsBinding.btnFollowTag.setVisibility(View.VISIBLE); hashtagDetailsBinding.btnFollowTag.setVisibility(View.VISIBLE);
hashtagDetailsBinding.btnFollowTag.setText(hashtagModel.getFollowing() == FollowingType.FOLLOWING hashtagDetailsBinding.btnFollowTag.setText(hashtagModel.getFollowing() == FollowingType.FOLLOWING
@ -515,22 +526,23 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
); );
hashtagDetailsBinding.mainHashtagImage.setImageURI("res:/" + R.drawable.ic_hashtag); hashtagDetailsBinding.mainHashtagImage.setImageURI("res:/" + R.drawable.ic_hashtag);
final String postCount = String.valueOf(hashtagModel.getMediaCount()); final String postCount = String.valueOf(hashtagModel.getMediaCount());
final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.main_posts_count_inline, final SpannableStringBuilder span = new SpannableStringBuilder(getResources().getQuantityString(
hashtagModel.getMediaCount() > 2000000000L R.plurals.main_posts_count_inline,
? 2000000000 hashtagModel.getMediaCount() > 2000000000L ? 2000000000
: Long.valueOf(hashtagModel.getMediaCount()).intValue(), : Long.valueOf(hashtagModel.getMediaCount()).intValue(),
postCount)); postCount)
);
span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0); span.setSpan(new RelativeSizeSpan(1.2f), 0, postCount.length(), 0);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0); span.setSpan(new StyleSpan(Typeface.BOLD), 0, postCount.length(), 0);
hashtagDetailsBinding.mainTagPostCount.setText(span); hashtagDetailsBinding.mainTagPostCount.setText(span);
hashtagDetailsBinding.mainTagPostCount.setVisibility(View.VISIBLE); hashtagDetailsBinding.mainTagPostCount.setVisibility(View.VISIBLE);
// hashtagDetailsBinding.mainHashtagImage.setOnClickListener(v -> { // hashtagDetailsBinding.mainHashtagImage.setOnClickListener(v -> {
// if (!hasStories) return; // if (!hasStories) return;
// // show stories // // show stories
// final NavDirections action = HashTagFragmentDirections // final NavDirections action = HashTagFragmentDirections
// .actionHashtagFragmentToStoryViewerFragment(StoryViewerOptions.forHashtag(hashtagModel.getName())); // .actionHashtagFragmentToStoryViewerFragment(StoryViewerOptions.forHashtag(hashtagModel.getName()));
// NavHostFragment.findNavController(this).navigate(action); // NavHostFragment.findNavController(this).navigate(action);
// }); // });
} }
private void showSnackbar(final String message) { private void showSnackbar(final String message) {
@ -541,35 +553,10 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
.show(); .show();
} }
// private void fetchStories() {
// if (!isLoggedIn) return;
// storiesFetching = true;
// storiesRepository.getStories(
// StoryViewerOptions.forHashtag(hashtagModel.getName()),
// CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
// if (throwable != null) {
// Log.e(TAG, "Error", throwable);
// storiesFetching = false;
// return;
// }
// if (storyModels != null && !storyModels.isEmpty()) {
// hashtagDetailsBinding.mainHashtagImage.setStoriesBorder(1);
// hasStories = true;
// } else {
// hasStories = false;
// }
// storiesFetching = false;
// }), Dispatchers.getIO())
// );
// }
private void setTitle() { private void setTitle() {
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); final ActionBar actionBar = fragmentActivity.getSupportActionBar();
if (actionBar != null) { if (actionBar != null) {
// Log.d(TAG, "setting title: " + hashtag);
actionBar.setTitle('#' + hashtag); actionBar.setTitle('#' + hashtag);
// final Handler handler = new Handler();
// handler.postDelayed(() -> , 1000);
} }
} }
@ -580,10 +567,12 @@ public class HashTagFragment extends Fragment implements SwipeRefreshLayout.OnRe
} }
private void navigateToProfile(final String username) { private void navigateToProfile(final String username) {
final NavController navController = NavHostFragment.findNavController(this); try {
final Bundle bundle = new Bundle(); final NavDirections action = HashTagFragmentDirections.actionToProfile().setUsername(username);
bundle.putString("username", username); NavHostFragment.findNavController(this).navigate(action);
navController.navigate(R.id.action_global_profileFragment, bundle); } catch (Exception e) {
Log.e(TAG, "navigateToProfile: ", e);
}
} }
private void showPostsLayoutPreferences() { private void showPostsLayoutPreferences() {

View File

@ -10,6 +10,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -19,7 +20,6 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.util.List; import java.util.List;
import awais.instagrabber.R;
import awais.instagrabber.adapters.LikesAdapter; import awais.instagrabber.adapters.LikesAdapter;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentLikesBinding; import awais.instagrabber.databinding.FragmentLikesBinding;
@ -55,9 +55,12 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme
final Object tag = v.getTag(); final Object tag = v.getTag();
if (tag instanceof User) { if (tag instanceof User) {
User model = (User) tag; User model = (User) tag;
final Bundle bundle = new Bundle(); try {
bundle.putString("username", "@" + model.getUsername()); final NavDirections action = LikesViewerFragmentDirections.actionToProfile().setUsername(model.getUsername());
NavHostFragment.findNavController(LikesViewerFragment.this).navigate(R.id.action_global_profileFragment, bundle); NavHostFragment.findNavController(LikesViewerFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onSuccess: ", e);
}
} }
}); });
binding.rvLikes.setAdapter(likesAdapter); binding.rvLikes.setAdapter(likesAdapter);
@ -84,9 +87,12 @@ public final class LikesViewerFragment extends BottomSheetDialogFragment impleme
final Object tag = v.getTag(); final Object tag = v.getTag();
if (tag instanceof User) { if (tag instanceof User) {
User model = (User) tag; User model = (User) tag;
final Bundle bundle = new Bundle(); try {
bundle.putString("username", "@" + model.getUsername()); final NavDirections action = LikesViewerFragmentDirections.actionToProfile().setUsername(model.getUsername());
NavHostFragment.findNavController(LikesViewerFragment.this).navigate(R.id.action_global_profileFragment, bundle); NavHostFragment.findNavController(LikesViewerFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onSuccess: ", e);
}
} }
}); });
binding.rvLikes.setAdapter(likesAdapter); binding.rvLikes.setAdapter(likesAdapter);

View File

@ -24,7 +24,6 @@ import androidx.appcompat.app.ActionBar;
import androidx.constraintlayout.motion.widget.MotionLayout; import androidx.constraintlayout.motion.widget.MotionLayout;
import androidx.constraintlayout.motion.widget.MotionScene; import androidx.constraintlayout.motion.widget.MotionScene;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -63,7 +62,6 @@ import awais.instagrabber.utils.Utils;
import awais.instagrabber.webservices.GraphQLRepository; import awais.instagrabber.webservices.GraphQLRepository;
import awais.instagrabber.webservices.LocationService; import awais.instagrabber.webservices.LocationService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
//import awais.instagrabber.webservices.StoriesRepository;
import kotlinx.coroutines.Dispatchers; import kotlinx.coroutines.Dispatchers;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -75,7 +73,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
private FragmentLocationBinding binding; private FragmentLocationBinding binding;
private MotionLayout root; private MotionLayout root;
private boolean shouldRefresh = true; private boolean shouldRefresh = true;
// private boolean hasStories = false; // private boolean hasStories = false;
private boolean opening = false; private boolean opening = false;
private long locationId; private long locationId;
private Location locationModel; private Location locationModel;
@ -129,12 +127,18 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override @Override
public void onCommentsClick(final Media feedModel) { public void onCommentsClick(final Media feedModel) {
final NavDirections commentsAction = LocationFragmentDirections.actionGlobalCommentsViewerFragment( final User user = feedModel.getUser();
if (user == null) return;
try {
final NavDirections commentsAction = LocationFragmentDirections.actionToComments(
feedModel.getCode(), feedModel.getCode(),
feedModel.getPk(), feedModel.getPk(),
feedModel.getUser().getPk() user.getPk()
); );
NavHostFragment.findNavController(LocationFragment.this).navigate(commentsAction); NavHostFragment.findNavController(LocationFragment.this).navigate(commentsAction);
} catch (Exception e) {
Log.e(TAG, "onCommentsClick: ", e);
}
} }
@Override @Override
@ -146,13 +150,19 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override @Override
public void onHashtagClick(final String hashtag) { public void onHashtagClick(final String hashtag) {
final NavDirections action = LocationFragmentDirections.actionGlobalHashTagFragment(hashtag); try {
final NavDirections action = LocationFragmentDirections.actionToHashtag(hashtag);
NavHostFragment.findNavController(LocationFragment.this).navigate(action); NavHostFragment.findNavController(LocationFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onHashtagClick: ", e);
}
} }
@Override @Override
public void onLocationClick(final Media feedModel) { public void onLocationClick(final Media feedModel) {
final NavDirections action = LocationFragmentDirections.actionGlobalLocationFragment(feedModel.getLocation().getPk()); final Location location = feedModel.getLocation();
if (location == null) return;
final NavDirections action = LocationFragmentDirections.actionToLocation(location.getPk());
NavHostFragment.findNavController(LocationFragment.this).navigate(action); NavHostFragment.findNavController(LocationFragment.this).navigate(action);
} }
@ -202,12 +212,9 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
return; return;
} }
opening = true; opening = true;
final NavController navController = NavHostFragment.findNavController(LocationFragment.this);
final Bundle bundle = new Bundle();
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, feedModel);
bundle.putInt(PostViewV2Fragment.ARG_SLIDER_POSITION, position);
try { try {
navController.navigate(R.id.action_global_post_view, bundle); final NavDirections action = LocationFragmentDirections.actionToPost(feedModel, position);
NavHostFragment.findNavController(LocationFragment.this).navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "openPostDialog: ", e); Log.e(TAG, "openPostDialog: ", e);
} }
@ -301,7 +308,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
@Override @Override
public void onRefresh() { public void onRefresh() {
binding.posts.refresh(); binding.posts.refresh();
// fetchStories(); // fetchStories();
} }
@Override @Override
@ -343,7 +350,6 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
.setFeedItemCallback(feedItemCallback) .setFeedItemCallback(feedItemCallback)
.setSelectionModeCallback(selectionModeCallback) .setSelectionModeCallback(selectionModeCallback)
.init(); .init();
binding.swipeRefreshLayout.setRefreshing(true);
binding.posts.addOnScrollListener(new RecyclerView.OnScrollListener() { binding.posts.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override @Override
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) { public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
@ -382,7 +388,7 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} }
setTitle(); setTitle();
setupPosts(); setupPosts();
// fetchStories(); // fetchStories();
final long locationId = locationModel.getPk(); final long locationId = locationModel.getPk();
// binding.swipeRefreshLayout.setRefreshing(true); // binding.swipeRefreshLayout.setRefreshing(true);
locationDetailsBinding.mainLocationImage.setImageURI("res:/" + R.drawable.ic_location); locationDetailsBinding.mainLocationImage.setImageURI("res:/" + R.drawable.ic_location);
@ -527,14 +533,14 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
); );
}), Dispatchers.getIO()) }), Dispatchers.getIO())
)); ));
// locationDetailsBinding.mainLocationImage.setOnClickListener(v -> { // locationDetailsBinding.mainLocationImage.setOnClickListener(v -> {
// if (hasStories) { // if (hasStories) {
// // show stories // // show stories
// final NavDirections action = LocationFragmentDirections // final NavDirections action = LocationFragmentDirections
// .actionLocationFragmentToStoryViewerFragment(StoryViewerOptions.forLocation(locationId, locationModel.getName())); // .actionLocationFragmentToStoryViewerFragment(StoryViewerOptions.forLocation(locationId, locationModel.getName()));
// NavHostFragment.findNavController(this).navigate(action); // NavHostFragment.findNavController(this).navigate(action);
// } // }
// }); // });
} }
private void showSnackbar(final String message) { private void showSnackbar(final String message) {
@ -545,26 +551,26 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
.show(); .show();
} }
// private void fetchStories() { // private void fetchStories() {
// if (isLoggedIn) { // if (isLoggedIn) {
// storiesFetching = true; // storiesFetching = true;
// storiesRepository.getStories( // storiesRepository.getStories(
// StoryViewerOptions.forLocation(locationId, locationModel.getName()), // StoryViewerOptions.forLocation(locationId, locationModel.getName()),
// CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { // CoroutineUtilsKt.getContinuation((storyModels, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
// if (throwable != null) { // if (throwable != null) {
// Log.e(TAG, "Error", throwable); // Log.e(TAG, "Error", throwable);
// storiesFetching = false; // storiesFetching = false;
// return; // return;
// } // }
// if (storyModels != null && !storyModels.isEmpty()) { // if (storyModels != null && !storyModels.isEmpty()) {
// locationDetailsBinding.mainLocationImage.setStoriesBorder(1); // locationDetailsBinding.mainLocationImage.setStoriesBorder(1);
// hasStories = true; // hasStories = true;
// } // }
// storiesFetching = false; // storiesFetching = false;
// }), Dispatchers.getIO()) // }), Dispatchers.getIO())
// ); // );
// } // }
// } // }
private void setTitle() { private void setTitle() {
final ActionBar actionBar = fragmentActivity.getSupportActionBar(); final ActionBar actionBar = fragmentActivity.getSupportActionBar();
@ -580,10 +586,12 @@ public class LocationFragment extends Fragment implements SwipeRefreshLayout.OnR
} }
private void navigateToProfile(final String username) { private void navigateToProfile(final String username) {
final NavController navController = NavHostFragment.findNavController(this); try {
final Bundle bundle = new Bundle(); final NavDirections action = LocationFragmentDirections.actionToProfile().setUsername(username);
bundle.putString("username", username); NavHostFragment.findNavController(this).navigate(action);
navController.navigate(R.id.action_global_profileFragment, bundle); } catch (Exception e) {
Log.e(TAG, "navigateToProfile: ", e);
}
} }
private void showPostsLayoutPreferences() { private void showPostsLayoutPreferences() {

View File

@ -20,7 +20,6 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
@ -50,8 +49,6 @@ import awais.instagrabber.webservices.NewsService;
import awais.instagrabber.webservices.ServiceCallback; import awais.instagrabber.webservices.ServiceCallback;
import kotlinx.coroutines.Dispatchers; import kotlinx.coroutines.Dispatchers;
import static awais.instagrabber.utils.Utils.settingsHelper;
public final class NotificationsViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener { public final class NotificationsViewerFragment extends Fragment implements SwipeRefreshLayout.OnRefreshListener {
private static final String TAG = "NotificationsViewer"; private static final String TAG = "NotificationsViewer";
@ -98,10 +95,14 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
if (model.getType() == NotificationType.RESPONDED_STORY) { if (model.getType() == NotificationType.RESPONDED_STORY) {
final StoryViewerOptions options = StoryViewerOptions.forStory( final StoryViewerOptions options = StoryViewerOptions.forStory(
mediaId, mediaId,
model.getArgs().getUsername()); model.getArgs().getUsername()
final Bundle bundle = new Bundle(); );
bundle.putSerializable("options", options); try {
NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(R.id.action_notifications_to_story, bundle); final NavDirections action = NotificationsViewerFragmentDirections.actionToStory(options);
NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onPreviewClick: ", e);
}
} else { } else {
final AlertDialog alertDialog = new AlertDialog.Builder(context) final AlertDialog alertDialog = new AlertDialog.Builder(context)
.setCancelable(false) .setCancelable(false)
@ -116,14 +117,13 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return; return;
} }
final NavController navController = NavHostFragment.findNavController(NotificationsViewerFragment.this);
final Bundle bundle = new Bundle();
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, media);
try { try {
navController.navigate(R.id.action_global_post_view, bundle); final NavDirections action = NotificationsViewerFragmentDirections.actionToPost(media, 0);
alertDialog.dismiss(); NavHostFragment.findNavController(NotificationsViewerFragment.this).navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "onSuccess: ", e); Log.e(TAG, "onSuccess: ", e);
} finally {
alertDialog.dismiss();
} }
}), Dispatchers.getIO()) }), Dispatchers.getIO())
); );
@ -257,7 +257,7 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
type = fragmentArgs.getType(); type = fragmentArgs.getType();
targetId = fragmentArgs.getTargetId(); targetId = fragmentArgs.getTargetId();
final Context context = getContext(); final Context context = getContext();
CookieUtils.setupCookies(settingsHelper.getString(Constants.COOKIE)); CookieUtils.setupCookies(Utils.settingsHelper.getString(Constants.COOKIE));
binding.swipeRefreshLayout.setOnRefreshListener(this); binding.swipeRefreshLayout.setOnRefreshListener(this);
notificationViewModel = new ViewModelProvider(this).get(NotificationViewModel.class); notificationViewModel = new ViewModelProvider(this).get(NotificationViewModel.class);
final NotificationsAdapter adapter = new NotificationsAdapter(clickListener); final NotificationsAdapter adapter = new NotificationsAdapter(clickListener);
@ -288,7 +288,11 @@ public final class NotificationsViewerFragment extends Fragment implements Swipe
} }
private void openProfile(final String username) { private void openProfile(final String username) {
final NavDirections action = NotificationsViewerFragmentDirections.actionGlobalProfileFragment("@" + username); try {
final NavDirections action = NotificationsViewerFragmentDirections.actionToProfile().setUsername(username);
NavHostFragment.findNavController(this).navigate(action); NavHostFragment.findNavController(this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "openProfile: ", e);
}
} }
} }

View File

@ -40,6 +40,7 @@ import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.TransitionManager; import androidx.transition.TransitionManager;
@ -70,7 +71,6 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.UserSearchNavGraphDirections;
import awais.instagrabber.activities.MainActivity; import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.SliderCallbackAdapter; import awais.instagrabber.adapters.SliderCallbackAdapter;
import awais.instagrabber.adapters.SliderItemsAdapter; import awais.instagrabber.adapters.SliderItemsAdapter;
@ -103,9 +103,7 @@ import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.PostViewV2ViewModel; import awais.instagrabber.viewmodels.PostViewV2ViewModel;
import static awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_SHOWN_COUNT_TOOLTIP; import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_SHOWN_COUNT_TOOLTIP;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class PostViewV2Fragment extends Fragment implements EditTextDialogFragment.EditTextDialogFragmentCallback { public class PostViewV2Fragment extends Fragment implements EditTextDialogFragment.EditTextDialogFragmentCallback {
private static final String TAG = "PostViewV2Fragment"; private static final String TAG = "PostViewV2Fragment";
@ -217,7 +215,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
// wasPaused = true; // wasPaused = true;
if (settingsHelper.getBoolean(PreferenceKeys.PLAY_IN_BACKGROUND)) return; if (Utils.settingsHelper.getBoolean(PreferenceKeys.PLAY_IN_BACKGROUND)) return;
final Media media = viewModel.getMedia(); final Media media = viewModel.getMedia();
if (media.getType() == null) return; if (media.getType() == null) return;
switch (media.getType()) { switch (media.getType()) {
@ -333,7 +331,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
viewModel.getLikeCount().observe(getViewLifecycleOwner(), count -> { viewModel.getLikeCount().observe(getViewLifecycleOwner(), count -> {
bottom.likesCount.setNumber(getSafeCount(count)); bottom.likesCount.setNumber(getSafeCount(count));
binding.getRoot().postDelayed(() -> bottom.likesCount.setAnimateChanges(true), 1000); binding.getRoot().postDelayed(() -> bottom.likesCount.setAnimateChanges(true), 1000);
if (count > 1000 && !settingsHelper.getBoolean(PREF_SHOWN_COUNT_TOOLTIP)) { if (count > 1000 && !Utils.settingsHelper.getBoolean(PREF_SHOWN_COUNT_TOOLTIP)) {
binding.getRoot().postDelayed(this::showCountTooltip, 1000); binding.getRoot().postDelayed(this::showCountTooltip, 1000);
} }
}); });
@ -392,7 +390,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
.setDismissWhenOverlayClicked(false) .setDismissWhenOverlayClicked(false)
.build(); .build();
balloon.showAlignBottom(bottom.likesCount); balloon.showAlignBottom(bottom.likesCount);
settingsHelper.putBoolean(PREF_SHOWN_COUNT_TOOLTIP, true); Utils.settingsHelper.putBoolean(PREF_SHOWN_COUNT_TOOLTIP, true);
balloon.setOnBalloonOutsideTouchListener((view, motionEvent) -> { balloon.setOnBalloonOutsideTouchListener((view, motionEvent) -> {
if (rect.contains((int) motionEvent.getRawX(), (int) motionEvent.getRawY())) { if (rect.contains((int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
bottom.likesCount.setShowAbbreviation(false); bottom.likesCount.setShowAbbreviation(false);
@ -431,12 +429,9 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
if (user == null) return; if (user == null) return;
final NavController navController = getNavController(); final NavController navController = getNavController();
if (navController == null) return; if (navController == null) return;
final Bundle bundle = new Bundle();
bundle.putString("shortCode", media.getCode());
bundle.putString("postId", media.getPk());
bundle.putLong("postUserId", user.getPk());
try { try {
navController.navigate(R.id.action_global_commentsViewerFragment, bundle); final NavDirections action = PostViewV2FragmentDirections.actionToComments(media.getCode(), media.getPk(), user.getPk());
navController.navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "setupComment: ", e); Log.e(TAG, "setupComment: ", e);
} }
@ -445,9 +440,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
} }
private void setupDownload() { private void setupDownload() {
bottom.download.setOnClickListener(v -> { bottom.download.setOnClickListener(v -> DownloadUtils.showDownloadDialog(context, viewModel.getMedia(), sliderPosition, bottom.download));
DownloadUtils.showDownloadDialog(context, viewModel.getMedia(), sliderPosition, bottom.download);
});
TooltipCompat.setTooltipText(bottom.download, getString(R.string.action_download)); TooltipCompat.setTooltipText(bottom.download, getString(R.string.action_download));
} }
@ -470,10 +463,12 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
bottom.like.setOnLongClickListener(v -> { bottom.like.setOnLongClickListener(v -> {
final NavController navController = getNavController(); final NavController navController = getNavController();
if (navController != null && viewModel.isLoggedIn()) { if (navController != null && viewModel.isLoggedIn()) {
final Bundle bundle = new Bundle(); try {
bundle.putString("postId", viewModel.getMedia().getPk()); final NavDirections action = PostViewV2FragmentDirections.actionToLikes(viewModel.getMedia().getPk(), false);
bundle.putBoolean("isComment", false); navController.navigate(action);
navController.navigate(R.id.action_global_likesViewerFragment, bundle); } catch (Exception e) {
Log.e(TAG, "setupLike: ", e);
}
return true; return true;
} }
return true; return true;
@ -543,11 +538,14 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
handleSaveUnsaveResourceLiveData(viewModel.toggleSave()); handleSaveUnsaveResourceLiveData(viewModel.toggleSave());
}); });
bottom.save.setOnLongClickListener(v -> { bottom.save.setOnLongClickListener(v -> {
final NavController navController = NavHostFragment.findNavController(this); try {
final Bundle bundle = new Bundle(); final NavDirections action = PostViewV2FragmentDirections.actionToSavedCollections().setIsSaving(true);
bundle.putBoolean("isSaving", true); NavHostFragment.findNavController(this).navigate(action);
navController.navigate(R.id.action_global_savedCollectionsFragment, bundle);
return true; return true;
} catch (Exception e) {
Log.e(TAG, "setupSave: ", e);
}
return false;
}); });
} }
@ -668,11 +666,13 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
} }
final String postCaption = caption.getText(); final String postCaption = caption.getText();
bottom.caption.addOnHashtagListener(autoLinkItem -> { bottom.caption.addOnHashtagListener(autoLinkItem -> {
final NavController navController = NavHostFragment.findNavController(this); try {
final Bundle bundle = new Bundle();
final String originalText = autoLinkItem.getOriginalText().trim(); final String originalText = autoLinkItem.getOriginalText().trim();
bundle.putString(ARG_HASHTAG, originalText); final NavDirections action = PostViewV2FragmentDirections.actionToHashtag(originalText);
navController.navigate(R.id.action_global_hashTagFragment, bundle); NavHostFragment.findNavController(this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "setupCaption: ", e);
}
}); });
bottom.caption.addOnMentionClickListener(autoLinkItem -> { bottom.caption.addOnMentionClickListener(autoLinkItem -> {
final String originalText = autoLinkItem.getOriginalText().trim(); final String originalText = autoLinkItem.getOriginalText().trim();
@ -701,7 +701,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
case ERROR: case ERROR:
bottom.translate.setEnabled(true); bottom.translate.setEnabled(true);
String message = resource.message; String message = resource.message;
if (TextUtils.isEmpty(resource.message)) { if (TextUtils.isEmpty(message)) {
message = getString(R.string.downloader_unknown_error); message = getString(R.string.downloader_unknown_error);
} }
final Snackbar snackbar = Snackbar.make(binding.getRoot(), message, Snackbar.LENGTH_INDEFINITE); final Snackbar snackbar = Snackbar.make(binding.getRoot(), message, Snackbar.LENGTH_INDEFINITE);
@ -725,11 +725,14 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
binding.location.setText(locationName); binding.location.setText(locationName);
binding.location.setVisibility(View.VISIBLE); binding.location.setVisibility(View.VISIBLE);
binding.location.setOnClickListener(v -> { binding.location.setOnClickListener(v -> {
try {
final NavController navController = getNavController(); final NavController navController = getNavController();
if (navController == null) return; if (navController == null) return;
final Bundle bundle = new Bundle(); final NavDirections action = PostViewV2FragmentDirections.actionToLocation(location.getPk());
bundle.putLong("locationId", location.getPk()); navController.navigate(action);
navController.navigate(R.id.action_global_locationFragment, bundle); } catch (Exception e) {
Log.e(TAG, "setupLocation: ", e);
}
}); });
} }
@ -755,15 +758,14 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
popupMenu.setOnMenuItemClickListener(item -> { popupMenu.setOnMenuItemClickListener(item -> {
final int itemId = item.getItemId(); final int itemId = item.getItemId();
if (itemId == R.id.share_dm) { if (itemId == R.id.share_dm) {
if (profileModel.isPrivate()) if (profileModel.isPrivate()) Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_SHORT).show();
Toast.makeText(context, R.string.share_private_post, Toast.LENGTH_SHORT).show(); final PostViewV2FragmentDirections.ActionToUserSearch actionGlobalUserSearch = PostViewV2FragmentDirections
final UserSearchNavGraphDirections.ActionGlobalUserSearch actionGlobalUserSearch = UserSearchFragmentDirections .actionToUserSearch()
.actionGlobalUserSearch()
.setTitle(getString(R.string.share)) .setTitle(getString(R.string.share))
.setActionLabel(getString(R.string.send)) .setActionLabel(getString(R.string.send))
.setShowGroups(true) .setShowGroups(true)
.setMultiple(true) .setMultiple(true)
.setSearchMode(UserSearchFragment.SearchMode.RAVEN); .setSearchMode(UserSearchMode.RAVEN);
final NavController navController = NavHostFragment.findNavController(PostViewV2Fragment.this); final NavController navController = NavHostFragment.findNavController(PostViewV2Fragment.this);
try { try {
navController.navigate(actionGlobalUserSearch); navController.navigate(actionGlobalUserSearch);
@ -1072,7 +1074,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
// gestureDetector.onTouchEvent(event); // gestureDetector.onTouchEvent(event);
// return true; // return true;
// }); // });
final float vol = settingsHelper.getBoolean(PreferenceKeys.MUTED_VIDEOS) ? 0f : 1f; final float vol = Utils.settingsHelper.getBoolean(PreferenceKeys.MUTED_VIDEOS) ? 0f : 1f;
final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() { final VideoPlayerViewHelper.VideoPlayerCallback videoPlayerCallback = new VideoPlayerCallbackAdapter() {
@Override @Override
public void onThumbnailLoaded() { public void onThumbnailLoaded() {
@ -1409,9 +1411,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
final CollapsingToolbarLayout appbarLayout = activity.getCollapsingToolbarView(); final CollapsingToolbarLayout appbarLayout = activity.getCollapsingToolbarView();
appbarLayout.setVisibility(View.GONE); appbarLayout.setVisibility(View.GONE);
final Toolbar toolbar = activity.getToolbar(); final Toolbar toolbar = activity.getToolbar();
if (toolbar != null) {
toolbar.setVisibility(View.GONE); toolbar.setVisibility(View.GONE);
}
binding.getRoot().setPadding(binding.getRoot().getPaddingLeft(), binding.getRoot().setPadding(binding.getRoot().getPaddingLeft(),
binding.getRoot().getPaddingTop(), binding.getRoot().getPaddingTop(),
binding.getRoot().getPaddingRight(), binding.getRoot().getPaddingRight(),
@ -1434,9 +1434,7 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
final CollapsingToolbarLayout appbarLayout = activity.getCollapsingToolbarView(); final CollapsingToolbarLayout appbarLayout = activity.getCollapsingToolbarView();
appbarLayout.setVisibility(View.VISIBLE); appbarLayout.setVisibility(View.VISIBLE);
final Toolbar toolbar = activity.getToolbar(); final Toolbar toolbar = activity.getToolbar();
if (toolbar != null) {
toolbar.setVisibility(View.VISIBLE); toolbar.setVisibility(View.VISIBLE);
}
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
binding.getRoot().setPadding(binding.getRoot().getPaddingLeft(), binding.getRoot().setPadding(binding.getRoot().getPaddingLeft(),
@ -1451,9 +1449,8 @@ public class PostViewV2Fragment extends Fragment implements EditTextDialogFragme
private void navigateToProfile(final String username) { private void navigateToProfile(final String username) {
final NavController navController = getNavController(); final NavController navController = getNavController();
if (navController == null) return; if (navController == null) return;
final Bundle bundle = new Bundle(); final NavDirections actionToProfile = PostViewV2FragmentDirections.actionToProfile().setUsername(username);
bundle.putString("username", username); navController.navigate(actionToProfile);
navController.navigate(R.id.action_global_profileFragment, bundle);
} }
@Nullable @Nullable

View File

@ -21,6 +21,7 @@ import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry; import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.NavDirections;
import androidx.navigation.fragment.FragmentNavigator; import androidx.navigation.fragment.FragmentNavigator;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -153,8 +154,8 @@ public class SavedCollectionsFragment extends Fragment implements SwipeRefreshLa
try { try {
final FragmentNavigator.Extras.Builder builder = new FragmentNavigator.Extras.Builder() final FragmentNavigator.Extras.Builder builder = new FragmentNavigator.Extras.Builder()
.addSharedElement(cover, "collection-" + topicCluster.getCollectionId()); .addSharedElement(cover, "collection-" + topicCluster.getCollectionId());
final SavedCollectionsFragmentDirections.ActionSavedCollectionsFragmentToCollectionPostsFragment action = SavedCollectionsFragmentDirections final NavDirections action = SavedCollectionsFragmentDirections
.actionSavedCollectionsFragmentToCollectionPostsFragment(topicCluster, titleColor, backgroundColor); .actionToCollectionPosts(topicCluster, titleColor, backgroundColor);
navController.navigate(action, builder.build()); navController.navigate(action, builder.build());
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "setupTopics: ", e); Log.e(TAG, "setupTopics: ", e);

View File

@ -19,7 +19,6 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -37,7 +36,9 @@ import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.fragments.main.ProfileFragmentDirections; import awais.instagrabber.fragments.main.ProfileFragmentDirections;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.enums.PostItemType; import awais.instagrabber.models.enums.PostItemType;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
@ -100,12 +101,18 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
@Override @Override
public void onCommentsClick(final Media feedModel) { public void onCommentsClick(final Media feedModel) {
final NavDirections commentsAction = ProfileFragmentDirections.actionGlobalCommentsViewerFragment( final User user = feedModel.getUser();
if (user == null) return;
try {
final NavDirections commentsAction = ProfileFragmentDirections.actionToComments(
feedModel.getCode(), feedModel.getCode(),
feedModel.getPk(), feedModel.getPk(),
feedModel.getUser().getPk() user.getPk()
); );
NavHostFragment.findNavController(SavedViewerFragment.this).navigate(commentsAction); NavHostFragment.findNavController(SavedViewerFragment.this).navigate(commentsAction);
} catch (Exception e) {
Log.e(TAG, "onCommentsClick: ", e);
}
} }
@Override @Override
@ -117,14 +124,24 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
@Override @Override
public void onHashtagClick(final String hashtag) { public void onHashtagClick(final String hashtag) {
final NavDirections action = ProfileFragmentDirections.actionGlobalHashTagFragment(hashtag); try {
final NavDirections action = ProfileFragmentDirections.actionToHashtag(hashtag);
NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action); NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onHashtagClick: ", e);
}
} }
@Override @Override
public void onLocationClick(final Media feedModel) { public void onLocationClick(final Media feedModel) {
final NavDirections action = ProfileFragmentDirections.actionGlobalLocationFragment(feedModel.getLocation().getPk()); final Location location = feedModel.getLocation();
if (location == null) return;
try {
final NavDirections action = ProfileFragmentDirections.actionToLocation(location.getPk());
NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action); NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onLocationClick: ", e);
}
} }
@Override @Override
@ -139,7 +156,9 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
@Override @Override
public void onProfilePicClick(final Media feedModel) { public void onProfilePicClick(final Media feedModel) {
navigateToProfile("@" + feedModel.getUser().getUsername()); final User user = feedModel.getUser();
if (user == null) return;
navigateToProfile("@" + user.getUsername());
} }
@Override @Override
@ -153,12 +172,9 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
} }
private void openPostDialog(final Media feedModel, final int position) { private void openPostDialog(final Media feedModel, final int position) {
final NavController navController = NavHostFragment.findNavController(SavedViewerFragment.this);
final Bundle bundle = new Bundle();
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, feedModel);
bundle.putInt(PostViewV2Fragment.ARG_SLIDER_POSITION, position);
try { try {
navController.navigate(R.id.action_global_post_view, bundle); final NavDirections action = SavedViewerFragmentDirections.actionToPost(feedModel, position);
NavHostFragment.findNavController(SavedViewerFragment.this).navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "openPostDialog: ", e); Log.e(TAG, "openPostDialog: ", e);
} }
@ -316,10 +332,12 @@ public final class SavedViewerFragment extends Fragment implements SwipeRefreshL
} }
private void navigateToProfile(final String username) { private void navigateToProfile(final String username) {
final NavController navController = NavHostFragment.findNavController(this); try {
final Bundle bundle = new Bundle(); final NavDirections action = SavedViewerFragmentDirections.actionToProfile().setUsername(username);
bundle.putString("username", username); NavHostFragment.findNavController(this).navigate(action);
navController.navigate(R.id.action_global_profileFragment, bundle); } catch (Exception e) {
Log.e(TAG, "navigateToProfile: ", e);
}
} }
private void showPostsLayoutPreferences() { private void showPostsLayoutPreferences() {

View File

@ -37,7 +37,6 @@ import awais.instagrabber.adapters.HighlightStoriesListAdapter;
import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener; import awais.instagrabber.adapters.HighlightStoriesListAdapter.OnHighlightStoryClickListener;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentStoryListViewerBinding; import awais.instagrabber.databinding.FragmentStoryListViewerBinding;
import awais.instagrabber.fragments.settings.MorePreferencesFragmentDirections;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.stories.ArchiveResponse; import awais.instagrabber.repositories.responses.stories.ArchiveResponse;
import awais.instagrabber.repositories.responses.stories.Story; import awais.instagrabber.repositories.responses.stories.Story;
@ -74,9 +73,12 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr
if (feedStoryModels == null) return; if (feedStoryModels == null) return;
final int position = Iterables.indexOf(feedStoryModels, feedStoryModel -> feedStoryModel != null final int position = Iterables.indexOf(feedStoryModels, feedStoryModel -> feedStoryModel != null
&& Objects.equals(feedStoryModel.getId(), model.getId())); && Objects.equals(feedStoryModel.getId(), model.getId()));
final NavDirections action = StoryListViewerFragmentDirections try {
.actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forFeedStoryPosition(position)); final NavDirections action = StoryListViewerFragmentDirections.actionToStory(StoryViewerOptions.forFeedStoryPosition(position));
NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onFeedStoryClick: ", e);
}
} }
@Override @Override
@ -89,9 +91,12 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr
@Override @Override
public void onHighlightClick(final Story model, final int position) { public void onHighlightClick(final Story model, final int position) {
if (model == null) return; if (model == null) return;
final NavDirections action = StoryListViewerFragmentDirections try {
.actionStoryListFragmentToStoryViewerFragment(StoryViewerOptions.forStoryArchive(position)); final NavDirections action = StoryListViewerFragmentDirections.actionToStory(StoryViewerOptions.forStoryArchive(position));
NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action); NavHostFragment.findNavController(StoryListViewerFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onHighlightClick: ", e);
}
} }
@Override @Override
@ -271,8 +276,11 @@ public final class StoryListViewerFragment extends Fragment implements SwipeRefr
} }
private void openProfile(final String username) { private void openProfile(final String username) {
final NavDirections action = MorePreferencesFragmentDirections try {
.actionGlobalProfileFragment("@" + username); final NavDirections action = StoryListViewerFragmentDirections.actionToProfile().setUsername(username);
NavHostFragment.findNavController(this).navigate(action); NavHostFragment.findNavController(this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "openProfile: ", e);
}
} }
} }

View File

@ -17,7 +17,6 @@ import androidx.appcompat.view.ContextThemeWrapper
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.view.GestureDetectorCompat import androidx.core.view.GestureDetectorCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@ -639,20 +638,19 @@ class StoryViewerFragment : Fragment() {
actionBar.title = null actionBar.title = null
actionBar.subtitle = null actionBar.subtitle = null
} }
when (data.second) { val action = when (data.second) {
FavoriteType.USER -> { FavoriteType.USER -> {
bundle.putString("username", data.first) StoryViewerFragmentDirections.actionToProfile().apply { this.username = data.first!! }
navController.navigate(R.id.action_global_profileFragment, bundle)
} }
FavoriteType.HASHTAG -> { FavoriteType.HASHTAG -> {
bundle.putString("hashtag", data.first) StoryViewerFragmentDirections.actionToHashtag(data.first!!)
navController.navigate(R.id.action_global_hashTagFragment, bundle)
} }
FavoriteType.LOCATION -> { FavoriteType.LOCATION -> {
bundle.putLong("locationId", data.first!!.toLong()) StoryViewerFragmentDirections.actionToLocation(data.first!!.toLong())
navController.navigate(R.id.action_global_locationFragment, bundle)
} }
else -> null
} }
navController.navigate(action!!)
} }
private fun releasePlayer() { private fun releasePlayer() {
@ -804,12 +802,12 @@ class StoryViewerFragment : Fragment() {
} }
val actionBar = fragmentActivity.supportActionBar val actionBar = fragmentActivity.supportActionBar
if (actionBar != null) actionBar.subtitle = null if (actionBar != null) actionBar.subtitle = null
val actionGlobalUserSearch = UserSearchFragmentDirections.actionGlobalUserSearch().apply { val actionGlobalUserSearch = StoryViewerFragmentDirections.actionToUserSearch().apply {
title = getString(R.string.share) title = getString(R.string.share)
setActionLabel(getString(R.string.send)) actionLabel = getString(R.string.send)
showGroups = true showGroups = true
multiple = true multiple = true
setSearchMode(UserSearchFragment.SearchMode.RAVEN) searchMode = UserSearchMode.RAVEN
} }
try { try {
val navController = NavHostFragment.findNavController(this@StoryViewerFragment) val navController = NavHostFragment.findNavController(this@StoryViewerFragment)
@ -876,7 +874,7 @@ class StoryViewerFragment : Fragment() {
val bundle = Bundle() val bundle = Bundle()
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, it.data) bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, it.data)
try { try {
navController.navigate(R.id.action_global_post_view, bundle) navController.navigate(StoryViewerFragmentDirections.actionToPost(it.data, 0))
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "openPostDialog: ", e) Log.e(TAG, "openPostDialog: ", e)
} }

View File

@ -25,7 +25,6 @@ import androidx.annotation.Nullable;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.graphics.ColorUtils; import androidx.core.graphics.ColorUtils;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.navigation.NavController;
import androidx.navigation.NavDirections; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -48,9 +47,10 @@ import awais.instagrabber.asyncs.DiscoverPostFetchService;
import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.PrimaryActionModeCallback;
import awais.instagrabber.databinding.FragmentTopicPostsBinding; import awais.instagrabber.databinding.FragmentTopicPostsBinding;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.fragments.main.DiscoverFragmentDirections;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.discover.TopicCluster; import awais.instagrabber.repositories.responses.discover.TopicCluster;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
@ -111,10 +111,12 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
@Override @Override
public void onCommentsClick(final Media feedModel) { public void onCommentsClick(final Media feedModel) {
final NavDirections commentsAction = DiscoverFragmentDirections.actionGlobalCommentsViewerFragment( final User user = feedModel.getUser();
if (user == null) return;
final NavDirections commentsAction = TopicPostsFragmentDirections.actionToComments(
feedModel.getCode(), feedModel.getCode(),
feedModel.getPk(), feedModel.getPk(),
feedModel.getUser().getPk() user.getPk()
); );
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(commentsAction); NavHostFragment.findNavController(TopicPostsFragment.this).navigate(commentsAction);
} }
@ -128,13 +130,15 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
@Override @Override
public void onHashtagClick(final String hashtag) { public void onHashtagClick(final String hashtag) {
final NavDirections action = DiscoverFragmentDirections.actionGlobalHashTagFragment(hashtag); final NavDirections action = TopicPostsFragmentDirections.actionToHashtag(hashtag);
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action); NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action);
} }
@Override @Override
public void onLocationClick(final Media feedModel) { public void onLocationClick(final Media feedModel) {
final NavDirections action = DiscoverFragmentDirections.actionGlobalLocationFragment(feedModel.getLocation().getPk()); final Location location = feedModel.getLocation();
if (location == null) return;
final NavDirections action = TopicPostsFragmentDirections.actionToLocation(location.getPk());
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action); NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action);
} }
@ -150,7 +154,9 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
@Override @Override
public void onProfilePicClick(final Media feedModel) { public void onProfilePicClick(final Media feedModel) {
navigateToProfile("@" + feedModel.getUser().getUsername()); final User user = feedModel.getUser();
if (user == null) return;
navigateToProfile("@" + user.getUsername());
} }
@Override @Override
@ -164,12 +170,9 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
} }
private void openPostDialog(final Media feedModel, final int position) { private void openPostDialog(final Media feedModel, final int position) {
final NavController navController = NavHostFragment.findNavController(TopicPostsFragment.this);
final Bundle bundle = new Bundle();
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, feedModel);
bundle.putInt(PostViewV2Fragment.ARG_SLIDER_POSITION, position);
try { try {
navController.navigate(R.id.action_global_post_view, bundle); final NavDirections action = TopicPostsFragmentDirections.actionToPost(feedModel, position);
NavHostFragment.findNavController(TopicPostsFragment.this).navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "openPostDialog: ", e); Log.e(TAG, "openPostDialog: ", e);
} }
@ -215,11 +218,14 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
public void onCreate(@Nullable final Bundle savedInstanceState) { public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
fragmentActivity = (MainActivity) requireActivity(); fragmentActivity = (MainActivity) requireActivity();
final Context context = getContext();
if (context != null) {
final TransitionSet transitionSet = new TransitionSet(); final TransitionSet transitionSet = new TransitionSet();
transitionSet.addTransition(new ChangeBounds()) transitionSet.addTransition(new ChangeBounds())
.addTransition(TransitionInflater.from(getContext()).inflateTransition(android.R.transition.move)) .addTransition(TransitionInflater.from(context).inflateTransition(android.R.transition.move))
.setDuration(200); .setDuration(200);
setSharedElementEnterTransition(transitionSet); setSharedElementEnterTransition(transitionSet);
}
postponeEnterTransition(); postponeEnterTransition();
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@ -378,10 +384,12 @@ public class TopicPostsFragment extends Fragment implements SwipeRefreshLayout.O
} }
private void navigateToProfile(final String username) { private void navigateToProfile(final String username) {
final NavController navController = NavHostFragment.findNavController(this); try {
final Bundle bundle = new Bundle(); final NavDirections action = TopicPostsFragmentDirections.actionToProfile().setUsername(username);
bundle.putString("username", username); NavHostFragment.findNavController(this).navigate(action);
navController.navigate(R.id.action_global_profileFragment, bundle); } catch (Exception e) {
Log.e(TAG, "navigateToProfile: ", e);
}
} }
private void showPostsLayoutPreferences() { private void showPostsLayoutPreferences() {

View File

@ -1,328 +0,0 @@
package awais.instagrabber.fragments;
import android.content.Context;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.core.util.Pair;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavBackStackEntry;
import androidx.navigation.NavController;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.transition.TransitionManager;
import com.google.android.material.chip.Chip;
import com.google.android.material.snackbar.Snackbar;
import java.util.Objects;
import java.util.Set;
import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.UserSearchResultsAdapter;
import awais.instagrabber.customviews.helpers.TextWatcherAdapter;
import awais.instagrabber.databinding.FragmentUserSearchBinding;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.utils.ViewUtils;
import awais.instagrabber.viewmodels.UserSearchViewModel;
public class UserSearchFragment extends Fragment {
private static final String TAG = UserSearchFragment.class.getSimpleName();
private FragmentUserSearchBinding binding;
private UserSearchViewModel viewModel;
private UserSearchResultsAdapter resultsAdapter;
private int paddingOffset;
private final int windowWidth = Utils.displayMetrics.widthPixels;
private final int minInputWidth = Utils.convertDpToPx(50);
private String actionLabel;
private String title;
private boolean multiple;
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) {
binding = FragmentUserSearchBinding.inflate(inflater, container, false);
viewModel = new ViewModelProvider(this).get(UserSearchViewModel.class);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
paddingOffset = binding.search.getPaddingStart() + binding.search.getPaddingEnd() + binding.group
.getPaddingStart() + binding.group.getPaddingEnd() + binding.group.getChipSpacingHorizontal();
init();
}
@Override
public void onDestroyView() {
super.onDestroyView();
viewModel.cleanup();
}
private void init() {
final Bundle arguments = getArguments();
if (arguments != null) {
final UserSearchFragmentArgs fragmentArgs = UserSearchFragmentArgs.fromBundle(arguments);
actionLabel = fragmentArgs.getActionLabel();
title = fragmentArgs.getTitle();
multiple = fragmentArgs.getMultiple();
viewModel.setHideThreadIds(fragmentArgs.getHideThreadIds());
viewModel.setHideUserIds(fragmentArgs.getHideUserIds());
viewModel.setSearchMode(fragmentArgs.getSearchMode());
viewModel.setShowGroups(fragmentArgs.getShowGroups());
}
setupTitles();
setupInput();
setupResults();
setupObservers();
// show cached results
viewModel.showCachedResults();
}
private void setupTitles() {
if (!TextUtils.isEmpty(actionLabel)) {
binding.done.setText(actionLabel);
}
if (!TextUtils.isEmpty(title)) {
final MainActivity activity = (MainActivity) getActivity();
if (activity != null) {
final ActionBar actionBar = activity.getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(title);
}
}
}
}
private void setupResults() {
final Context context = getContext();
if (context == null) return;
binding.results.setLayoutManager(new LinearLayoutManager(context));
resultsAdapter = new UserSearchResultsAdapter(multiple, (position, recipient, selected) -> {
if (!multiple) {
final NavController navController = NavHostFragment.findNavController(this);
if (!setResult(navController, recipient)) return;
navController.navigateUp();
return;
}
viewModel.setSelectedRecipient(recipient, !selected);
resultsAdapter.setSelectedRecipient(recipient, !selected);
if (!selected) {
createChip(recipient);
return;
}
final View chip = findChip(recipient);
if (chip == null) return;
removeChipFromGroup(chip);
});
binding.results.setAdapter(resultsAdapter);
binding.done.setOnClickListener(v -> {
final NavController navController = NavHostFragment.findNavController(this);
if (!setResult(navController, viewModel.getSelectedRecipients())) return;
navController.navigateUp();
});
}
private boolean setResult(@NonNull final NavController navController, final RankedRecipient rankedRecipient) {
final NavBackStackEntry navBackStackEntry = navController.getPreviousBackStackEntry();
if (navBackStackEntry == null) return false;
final SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
savedStateHandle.set("result", rankedRecipient);
return true;
}
private boolean setResult(@NonNull final NavController navController, final Set<RankedRecipient> rankedRecipients) {
final NavBackStackEntry navBackStackEntry = navController.getPreviousBackStackEntry();
if (navBackStackEntry == null) return false;
final SavedStateHandle savedStateHandle = navBackStackEntry.getSavedStateHandle();
savedStateHandle.set("result", rankedRecipients);
return true;
}
private void setupInput() {
binding.search.addTextChangedListener(new TextWatcherAdapter() {
@Override
public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
// if (TextUtils.isEmpty(s)) {
// viewModel.cancelSearch();
// viewModel.clearResults();
// return;
// }
viewModel.search(s == null ? null : s.toString().trim());
}
});
binding.search.setOnKeyListener((v, keyCode, event) -> {
if (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
final View chip = getLastChip();
if (chip == null) return false;
removeChip(chip);
}
return false;
});
binding.group.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@Override
public void onChildViewAdded(final View parent, final View child) {}
@Override
public void onChildViewRemoved(final View parent, final View child) {
binding.group.post(() -> {
TransitionManager.beginDelayedTransition(binding.getRoot());
calculateInputWidth(0);
});
}
});
}
private void setupObservers() {
viewModel.getRecipients().observe(getViewLifecycleOwner(), results -> {
if (results == null) return;
switch (results.status) {
case SUCCESS:
if (results.data != null) {
resultsAdapter.submitList(results.data);
}
break;
case ERROR:
if (results.message != null) {
Snackbar.make(binding.getRoot(), results.message, Snackbar.LENGTH_LONG).show();
}
if (results.resId != 0) {
Snackbar.make(binding.getRoot(), results.resId, Snackbar.LENGTH_LONG).show();
}
if (results.data != null) {
resultsAdapter.submitList(results.data);
}
break;
case LOADING:
//noinspection DuplicateBranchesInSwitch
if (results.data != null) {
resultsAdapter.submitList(results.data);
}
break;
}
});
viewModel.showAction().observe(getViewLifecycleOwner(), showAction -> binding.done.setVisibility(showAction ? View.VISIBLE : View.GONE));
}
private void createChip(final RankedRecipient recipient) {
final Context context = getContext();
if (context == null) return;
final Chip chip = new Chip(context);
chip.setTag(recipient);
chip.setText(getRecipientText(recipient));
chip.setCloseIconVisible(true);
chip.setOnCloseIconClickListener(v -> removeChip(chip));
binding.group.post(() -> {
final Pair<Integer, Integer> measure = ViewUtils.measure(chip, binding.group);
TransitionManager.beginDelayedTransition(binding.getRoot());
calculateInputWidth(measure.second != null ? measure.second : 0);
binding.group.addView(chip, binding.group.getChildCount() - 1);
});
}
private String getRecipientText(final RankedRecipient recipient) {
if (recipient == null) return null;
if (recipient.getUser() != null) {
return recipient.getUser().getFullName();
}
if (recipient.getThread() != null) {
return recipient.getThread().getThreadTitle();
}
return null;
}
private void removeChip(@NonNull final View chip) {
final RankedRecipient recipient = (RankedRecipient) chip.getTag();
if (recipient == null) return;
viewModel.setSelectedRecipient(recipient, false);
resultsAdapter.setSelectedRecipient(recipient, false);
removeChipFromGroup(chip);
}
private View findChip(final RankedRecipient recipient) {
if (recipient == null || recipient.getUser() == null && recipient.getThread() == null) return null;
boolean isUser = recipient.getUser() != null;
final int childCount = binding.group.getChildCount();
if (childCount == 0) return null;
for (int i = childCount - 1; i >= 0; i--) {
final View child = binding.group.getChildAt(i);
if (child == null) continue;
final RankedRecipient tag = (RankedRecipient) child.getTag();
if (tag == null || isUser && tag.getUser() == null || !isUser && tag.getThread() == null) continue;
if ((isUser && tag.getUser().getPk() == recipient.getUser().getPk())
|| (!isUser && Objects.equals(tag.getThread().getThreadId(), recipient.getThread().getThreadId()))) {
return child;
}
}
return null;
}
private void removeChipFromGroup(final View chip) {
binding.group.post(() -> {
TransitionManager.beginDelayedTransition(binding.getRoot());
binding.group.removeView(chip);
});
}
private void calculateInputWidth(final int newChipWidth) {
final View lastChip = getLastChip();
int lastRight = lastChip != null ? lastChip.getRight() : 0;
final int remainingSpaceInRow = windowWidth - lastRight;
if (remainingSpaceInRow < newChipWidth) {
// next chip will go to the next row, so assume no chips present
lastRight = 0;
}
final int newRight = lastRight + newChipWidth;
final int newInputWidth = windowWidth - newRight - paddingOffset;
binding.search.getLayoutParams().width = newInputWidth < minInputWidth ? windowWidth : newInputWidth;
binding.search.requestLayout();
}
private View getLastChip() {
final int childCount = binding.group.getChildCount();
if (childCount == 0) {
return null;
}
for (int i = childCount - 1; i >= 0; i--) {
final View child = binding.group.getChildAt(i);
if (child instanceof Chip) {
return child;
}
}
return null;
}
public enum SearchMode {
USER_SEARCH("user_name"),
RAVEN("raven"),
RESHARE("reshare");
private final String name;
SearchMode(final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
}

View File

@ -0,0 +1,252 @@
package awais.instagrabber.fragments
import android.os.Bundle
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.OnHierarchyChangeListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionManager
import awais.instagrabber.activities.MainActivity
import awais.instagrabber.adapters.UserSearchResultsAdapter
import awais.instagrabber.customviews.helpers.TextWatcherAdapter
import awais.instagrabber.databinding.FragmentUserSearchBinding
import awais.instagrabber.models.Resource
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
import awais.instagrabber.utils.Utils
import awais.instagrabber.utils.extensions.trimAll
import awais.instagrabber.utils.measure
import awais.instagrabber.viewmodels.UserSearchViewModel
import com.google.android.material.chip.Chip
import com.google.android.material.snackbar.Snackbar
class UserSearchFragment : Fragment() {
private lateinit var binding: FragmentUserSearchBinding
private var resultsAdapter: UserSearchResultsAdapter? = null
private var paddingOffset = 0
private var actionLabel: String? = null
private var title: String? = null
private var multiple = false
private val viewModel: UserSearchViewModel by viewModels()
private val windowWidth = Utils.displayMetrics.widthPixels
private val minInputWidth = Utils.convertDpToPx(50f)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
binding = FragmentUserSearchBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
paddingOffset = with(binding) {
search.paddingStart + search.paddingEnd + group.paddingStart + group.paddingEnd + group.chipSpacingHorizontal
}
init()
}
override fun onDestroyView() {
super.onDestroyView()
viewModel.cleanup()
}
private fun init() {
val arguments = arguments
if (arguments != null) {
val fragmentArgs = UserSearchFragmentArgs.fromBundle(arguments)
actionLabel = fragmentArgs.actionLabel
title = fragmentArgs.title
multiple = fragmentArgs.multiple
viewModel.setHideThreadIds(fragmentArgs.hideThreadIds)
viewModel.setHideUserIds(fragmentArgs.hideUserIds)
viewModel.setSearchMode(fragmentArgs.searchMode)
viewModel.setShowGroups(fragmentArgs.showGroups)
}
setupTitles()
setupInput()
setupResults()
setupObservers()
// show cached results
viewModel.showCachedResults()
}
private fun setupTitles() {
if (!actionLabel.isNullOrBlank()) {
binding.done.text = actionLabel
}
if (title.isNullOrBlank()) return
(activity as MainActivity?)?.supportActionBar?.title = title
}
private fun setupResults() {
val context = context ?: return
binding.results.layoutManager = LinearLayoutManager(context)
resultsAdapter = UserSearchResultsAdapter(multiple) { _: Int, recipient: RankedRecipient, selected: Boolean ->
if (!multiple) {
val navController = NavHostFragment.findNavController(this)
if (!setResult(navController, recipient)) return@UserSearchResultsAdapter
navController.navigateUp()
return@UserSearchResultsAdapter
}
viewModel.setSelectedRecipient(recipient, !selected)
resultsAdapter?.setSelectedRecipient(recipient, !selected)
if (!selected) {
createChip(recipient)
return@UserSearchResultsAdapter
}
val chip = findChip(recipient) ?: return@UserSearchResultsAdapter
removeChipFromGroup(chip)
}
binding.results.adapter = resultsAdapter
binding.done.setOnClickListener {
val navController = NavHostFragment.findNavController(this)
if (!setResult(navController, viewModel.selectedRecipients)) return@setOnClickListener
navController.navigateUp()
}
}
private fun setResult(navController: NavController, rankedRecipient: RankedRecipient): Boolean {
navController.previousBackStackEntry?.savedStateHandle?.set("result", rankedRecipient) ?: return false
return true
}
private fun setResult(navController: NavController, rankedRecipients: Set<RankedRecipient>): Boolean {
navController.previousBackStackEntry?.savedStateHandle?.set("result", rankedRecipients) ?: return false
return true
}
private fun setupInput() {
binding.search.addTextChangedListener(object : TextWatcherAdapter() {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
viewModel.search(s.toString().trimAll())
}
})
binding.search.setOnKeyListener { _: View?, _: Int, event: KeyEvent? ->
if (event != null && event.action == KeyEvent.ACTION_DOWN && event.keyCode == KeyEvent.KEYCODE_DEL) {
val chip = lastChip ?: return@setOnKeyListener false
removeChip(chip)
}
false
}
binding.group.setOnHierarchyChangeListener(object : OnHierarchyChangeListener {
override fun onChildViewAdded(parent: View, child: View) {}
override fun onChildViewRemoved(parent: View, child: View) {
binding.group.post {
TransitionManager.beginDelayedTransition(binding.root)
calculateInputWidth(0)
}
}
})
}
private fun setupObservers() {
viewModel.recipients.observe(viewLifecycleOwner) {
if (it == null) return@observe
when (it.status) {
Resource.Status.SUCCESS -> if (it.data != null) {
resultsAdapter?.submitList(it.data)
}
Resource.Status.ERROR -> {
if (it.message != null) {
Snackbar.make(binding.root, it.message, Snackbar.LENGTH_LONG).show()
}
if (it.resId != 0) {
Snackbar.make(binding.root, it.resId, Snackbar.LENGTH_LONG).show()
}
if (it.data != null) {
resultsAdapter?.submitList(it.data)
}
}
Resource.Status.LOADING -> if (it.data != null) {
resultsAdapter?.submitList(it.data)
}
}
}
viewModel.showAction().observe(viewLifecycleOwner) { binding.done.visibility = if (it) View.VISIBLE else View.GONE }
}
private fun createChip(recipient: RankedRecipient) {
val context = context ?: return
val chip = Chip(context).apply {
tag = recipient
text = getRecipientText(recipient)
isCloseIconVisible = true
setOnCloseIconClickListener { removeChip(this) }
}
binding.group.post {
val measure = measure(chip, binding.group)
TransitionManager.beginDelayedTransition(binding.root)
calculateInputWidth(if (measure.second != null) measure.second else 0)
binding.group.addView(chip, binding.group.childCount - 1)
}
}
private fun getRecipientText(recipient: RankedRecipient?): String? = when {
recipient == null -> null
recipient.user != null -> recipient.user.fullName
recipient.thread != null -> recipient.thread.threadTitle
else -> null
}
private fun removeChip(chip: View) {
val recipient = chip.tag as RankedRecipient
viewModel.setSelectedRecipient(recipient, false)
resultsAdapter?.setSelectedRecipient(recipient, false)
removeChipFromGroup(chip)
}
private fun findChip(recipient: RankedRecipient?): View? {
if (recipient == null || recipient.user == null && recipient.thread == null) return null
val isUser = recipient.user != null
val childCount = binding.group.childCount
if (childCount == 0) return null
for (i in childCount - 1 downTo 0) {
val child = binding.group.getChildAt(i) ?: continue
val tag = child.tag as RankedRecipient
if (isUser && tag.user == null || !isUser && tag.thread == null) continue
if (isUser && tag.user?.pk == recipient.user?.pk || !isUser && tag.thread?.threadId == recipient.thread?.threadId) {
return child
}
}
return null
}
private fun removeChipFromGroup(chip: View) {
binding.group.post {
TransitionManager.beginDelayedTransition(binding.root)
binding.group.removeView(chip)
}
}
private fun calculateInputWidth(newChipWidth: Int) {
var lastRight = lastChip?.right ?: 0
val remainingSpaceInRow = windowWidth - lastRight
if (remainingSpaceInRow < newChipWidth) {
// next chip will go to the next row, so assume no chips present
lastRight = 0
}
val newRight = lastRight + newChipWidth
val newInputWidth = windowWidth - newRight - paddingOffset
binding.search.layoutParams.width = if (newInputWidth < minInputWidth) windowWidth else newInputWidth
binding.search.requestLayout()
}
private val lastChip: View?
get() {
val childCount = binding.group.childCount
if (childCount == 0) return null
for (i in childCount - 1 downTo 0) {
val child = binding.group.getChildAt(i)
if (child is Chip) {
return child
}
}
return null
}
}

View File

@ -0,0 +1,7 @@
package awais.instagrabber.fragments
enum class UserSearchMode(val mode: String) {
USER_SEARCH("user_name"),
RAVEN("raven"),
RESHARE("reshare");
}

View File

@ -15,6 +15,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@ -34,9 +35,11 @@ import awais.instagrabber.R;
import awais.instagrabber.adapters.CommentsAdapter; import awais.instagrabber.adapters.CommentsAdapter;
import awais.instagrabber.customviews.helpers.RecyclerLazyLoader; import awais.instagrabber.customviews.helpers.RecyclerLazyLoader;
import awais.instagrabber.databinding.FragmentCommentsBinding; import awais.instagrabber.databinding.FragmentCommentsBinding;
import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.models.Comment; import awais.instagrabber.models.Comment;
import awais.instagrabber.models.Resource; import awais.instagrabber.models.Resource;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils;
import awais.instagrabber.viewmodels.AppStateViewModel; import awais.instagrabber.viewmodels.AppStateViewModel;
import awais.instagrabber.viewmodels.CommentsViewerViewModel; import awais.instagrabber.viewmodels.CommentsViewerViewModel;
@ -78,7 +81,7 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment {
@NonNull @NonNull
@Override @Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState) {
return new BottomSheetDialog(getContext(), getTheme()) { return new BottomSheetDialog(requireContext(), getTheme()) {
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (showingReplies) { if (showingReplies) {
@ -205,10 +208,13 @@ public final class CommentsViewerFragment extends BottomSheetDialogFragment {
viewModel, viewModel,
(comment, focusInput) -> { (comment, focusInput) -> {
if (comment == null) return null; if (comment == null) return null;
final RepliesFragment repliesFragment = RepliesFragment.newInstance(comment, focusInput == null ? false : focusInput); final boolean disableTransition = Utils.settingsHelper.getBoolean(PreferenceKeys.PREF_DISABLE_SCREEN_TRANSITIONS);
getChildFragmentManager().beginTransaction() final RepliesFragment repliesFragment = RepliesFragment.newInstance(comment, focusInput != null && focusInput);
.setCustomAnimations(R.anim.slide_left, R.anim.slide_right, 0, R.anim.slide_right) final FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
.add(R.id.replies_container_view, repliesFragment) if (!disableTransition) {
transaction.setCustomAnimations(R.anim.slide_left, R.anim.slide_right, 0, R.anim.slide_right);
}
transaction.add(R.id.replies_container_view, repliesFragment)
.addToBackStack(RepliesFragment.TAG) .addToBackStack(RepliesFragment.TAG)
.commit(); .commit();
showingReplies = true; showingReplies = true;

View File

@ -1,7 +1,7 @@
package awais.instagrabber.fragments.comments; package awais.instagrabber.fragments.comments;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.graphics.drawable.Drawable;
import android.text.Editable; import android.text.Editable;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
@ -45,7 +45,10 @@ public final class Helper {
@NonNull final RecyclerView.OnScrollListener lazyLoader) { @NonNull final RecyclerView.OnScrollListener lazyLoader) {
list.setLayoutManager(layoutManager); list.setLayoutManager(layoutManager);
final DividerItemDecoration itemDecoration = new DividerItemDecoration(context, LinearLayoutManager.VERTICAL); final DividerItemDecoration itemDecoration = new DividerItemDecoration(context, LinearLayoutManager.VERTICAL);
itemDecoration.setDrawable(ContextCompat.getDrawable(context, R.drawable.pref_list_divider_material)); final Drawable drawable = ContextCompat.getDrawable(context, R.drawable.pref_list_divider_material);
if (drawable != null) {
itemDecoration.setDrawable(drawable);
}
list.addItemDecoration(itemDecoration); list.addItemDecoration(itemDecoration);
list.addOnScrollListener(lazyLoader); list.addOnScrollListener(lazyLoader);
} }
@ -68,8 +71,7 @@ public final class Helper {
public void onHashtagClick(final String hashtag) { public void onHashtagClick(final String hashtag) {
try { try {
if (navController == null) return; if (navController == null) return;
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalHashTagFragment(hashtag); navController.navigate(CommentsViewerFragmentDirections.actionToHashtag(hashtag));
navController.navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "onHashtagClick: ", e); Log.e(TAG, "onHashtagClick: ", e);
} }
@ -123,12 +125,10 @@ public final class Helper {
@Override @Override
public void onViewLikes(final Comment comment) { public void onViewLikes(final Comment comment) {
if (navController == null) return;
try { try {
final Bundle bundle = new Bundle(); if (navController == null) return;
bundle.putString("postId", comment.getPk()); final NavDirections actionToLikes = CommentsViewerFragmentDirections.actionToLikes(comment.getPk(), true);
bundle.putBoolean("isComment", true); navController.navigate(actionToLikes);
navController.navigate(R.id.action_global_likesViewerFragment, bundle);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "onViewLikes: ", e); Log.e(TAG, "onViewLikes: ", e);
} }
@ -144,10 +144,7 @@ public final class Helper {
Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.downloader_unknown_error, Toast.LENGTH_SHORT).show();
return; return;
} }
String username = ""; final String username = comment.getUser().getUsername();
if (comment.getUser() != null) {
username = comment.getUser().getUsername();
}
new MaterialAlertDialogBuilder(context) new MaterialAlertDialogBuilder(context)
.setTitle(username) .setTitle(username)
.setMessage(result) .setMessage(result)
@ -192,9 +189,9 @@ public final class Helper {
private static void openProfile(final NavController navController, private static void openProfile(final NavController navController,
@NonNull final String username) { @NonNull final String username) {
if (navController == null) return;
try { try {
final NavDirections action = CommentsViewerFragmentDirections.actionGlobalProfileFragment(username); if (navController == null) return;
final NavDirections action = CommentsViewerFragmentDirections.actionToProfile().setUsername(username);
navController.navigate(action); navController.navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "openProfile: ", e); Log.e(TAG, "openProfile: ", e);

View File

@ -83,8 +83,13 @@ public class RepliesFragment extends Fragment {
@Override @Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) { public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
if (!enter || nextAnim == 0) { if (!enter) {
return super.onCreateAnimation(transit, enter, nextAnim); return super.onCreateAnimation(transit, false, nextAnim);
}
if (nextAnim == 0) {
setupList();
setupObservers();
return super.onCreateAnimation(transit, true, nextAnim);
} }
final Animation animation = AnimationUtils.loadAnimation(getContext(), nextAnim); final Animation animation = AnimationUtils.loadAnimation(getContext(), nextAnim);
animation.setAnimationListener(new Animation.AnimationListener() { animation.setAnimationListener(new Animation.AnimationListener() {
@ -185,9 +190,11 @@ public class RepliesFragment extends Fragment {
private void setupAdapter(final long currentUserId) { private void setupAdapter(final long currentUserId) {
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
commentsAdapter = new CommentsAdapter(currentUserId, commentsAdapter = new CommentsAdapter(
currentUserId,
true, true,
Helper.getCommentCallback(context, Helper.getCommentCallback(
context,
getViewLifecycleOwner(), getViewLifecycleOwner(),
getNavController(), getNavController(),
viewModel, viewModel,
@ -196,7 +203,9 @@ public class RepliesFragment extends Fragment {
binding.commentText.setText(String.format("@%s ", comment.getUser().getUsername())); binding.commentText.setText(String.format("@%s ", comment.getUser().getUsername()));
if (focusInput) Utils.showKeyboard(binding.commentText); if (focusInput) Utils.showKeyboard(binding.commentText);
return null; return null;
})); }
)
);
binding.comments.setAdapter(commentsAdapter); binding.comments.setAdapter(commentsAdapter);
final Resource<List<Comment>> listResource = viewModel.getReplyList().getValue(); final Resource<List<Comment>> listResource = viewModel.getReplyList().getValue();
commentsAdapter.submitList(listResource != null ? listResource.data : Collections.emptyList()); commentsAdapter.submitList(listResource != null ? listResource.data : Collections.emptyList());

View File

@ -7,11 +7,9 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import android.view.* import android.view.*
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import awais.instagrabber.R import awais.instagrabber.R
@ -33,18 +31,16 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
private val viewModel: DirectInboxViewModel by activityViewModels() private val viewModel: DirectInboxViewModel by activityViewModels()
private lateinit var fragmentActivity: MainActivity private lateinit var fragmentActivity: MainActivity
private lateinit var root: CoordinatorLayout
private lateinit var binding: FragmentDirectMessagesInboxBinding private lateinit var binding: FragmentDirectMessagesInboxBinding
private lateinit var inboxAdapter: DirectMessageInboxAdapter
private lateinit var lazyLoader: RecyclerLazyLoaderAtEdge private lateinit var lazyLoader: RecyclerLazyLoaderAtEdge
private var shouldRefresh = true
private var scrollToTop = false private var scrollToTop = false
private var navigating = false private var navigating = false
private var threadsObserver: Observer<List<DirectThread?>>? = null
private var pendingRequestsMenuItem: MenuItem? = null private var pendingRequestsMenuItem: MenuItem? = null
private var pendingRequestTotalBadgeDrawable: BadgeDrawable? = null private var pendingRequestTotalBadgeDrawable: BadgeDrawable? = null
private var isPendingRequestTotalBadgeAttached = false private var isPendingRequestTotalBadgeAttached = false
private var inboxAdapter: DirectMessageInboxAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -57,17 +53,11 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle?, savedInstanceState: Bundle?,
): View { ): View {
if (this::root.isInitialized) {
shouldRefresh = false
return root
}
binding = FragmentDirectMessagesInboxBinding.inflate(inflater, container, false) binding = FragmentDirectMessagesInboxBinding.inflate(inflater, container, false)
root = binding.root return binding.root
return root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!shouldRefresh) return
init() init()
} }
@ -90,11 +80,6 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
} }
} }
override fun onResume() {
super.onResume()
setupObservers()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.dm_inbox_menu, menu) inflater.inflate(R.menu.dm_inbox_menu, menu)
pendingRequestsMenuItem = menu.findItem(R.id.pending_requests) pendingRequestsMenuItem = menu.findItem(R.id.pending_requests)
@ -103,9 +88,9 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.pending_requests) { if (item.itemId == R.id.pending_requests) {
val directions = DirectMessageInboxFragmentDirections.actionInboxToPendingInbox()
try { try {
NavHostFragment.findNavController(this).navigate(directions) val directions = DirectMessageInboxFragmentDirections.actionToPendingInbox()
findNavController().navigate(directions)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "onOptionsItemSelected: ", e) Log.e(TAG, "onOptionsItemSelected: ", e)
} }
@ -119,23 +104,14 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
init() init()
} }
override fun onDestroy() {
super.onDestroy()
removeViewModelObservers()
viewModel.onDestroy()
}
private fun setupObservers() { private fun setupObservers() {
removeViewModelObservers() viewModel.threads.observe(viewLifecycleOwner, { list: List<DirectThread?> ->
threadsObserver = Observer { list: List<DirectThread?> -> inboxAdapter?.submitList(list) {
if (!this::inboxAdapter.isInitialized) return@Observer
inboxAdapter.submitList(list) {
if (!scrollToTop) return@submitList if (!scrollToTop) return@submitList
binding.inboxList.post { binding.inboxList.smoothScrollToPosition(0) } binding.inboxList.post { binding.inboxList.smoothScrollToPosition(0) }
scrollToTop = false scrollToTop = false
} }
} })
threadsObserver?.let { viewModel.threads.observe(fragmentActivity, it) }
viewModel.inbox.observe(viewLifecycleOwner, { inboxResource: Resource<DirectInbox?>? -> viewModel.inbox.observe(viewLifecycleOwner, { inboxResource: Resource<DirectInbox?>? ->
if (inboxResource == null) return@observe if (inboxResource == null) return@observe
when (inboxResource.status) { 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() { private fun init() {
val context = context ?: return val context = context ?: return
setupObservers() setupObservers()
@ -210,18 +181,20 @@ class DirectMessageInboxFragment : Fragment(), OnRefreshListener {
if (navigating || threadId.isNullOrBlank() || threadTitle.isNullOrBlank()) return@DirectMessageInboxAdapter if (navigating || threadId.isNullOrBlank() || threadTitle.isNullOrBlank()) return@DirectMessageInboxAdapter
navigating = true navigating = true
if (isAdded) { if (isAdded) {
val directions = DirectMessageInboxFragmentDirections.actionInboxToThread(threadId, threadTitle)
try { try {
NavHostFragment.findNavController(this).navigate(directions) val directions = DirectMessageInboxFragmentDirections.actionToThread(threadId, threadTitle)
findNavController().navigate(directions)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "init: ", e) Log.e(TAG, "init: ", e)
} }
} }
navigating = false navigating = false
}.also {
it.setHasStableIds(true)
} }
inboxAdapter.setHasStableIds(true)
binding.inboxList.adapter = inboxAdapter binding.inboxList.adapter = inboxAdapter
lazyLoader = RecyclerLazyLoaderAtEdge(layoutManager) { viewModel.fetchInbox() } lazyLoader = RecyclerLazyLoaderAtEdge(layoutManager) { viewModel.fetchInbox() }.also {
lazyLoader.let { binding.inboxList.addOnScrollListener(it) } binding.inboxList.addOnScrollListener(it)
}
} }
} }

View File

@ -11,8 +11,8 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import awais.instagrabber.ProfileNavGraphDirections
import awais.instagrabber.R import awais.instagrabber.R
import awais.instagrabber.activities.MainActivity import awais.instagrabber.activities.MainActivity
import awais.instagrabber.adapters.DirectPendingUsersAdapter import awais.instagrabber.adapters.DirectPendingUsersAdapter
@ -25,13 +25,11 @@ import awais.instagrabber.dialogs.ConfirmDialogFragment
import awais.instagrabber.dialogs.ConfirmDialogFragment.ConfirmDialogFragmentCallback import awais.instagrabber.dialogs.ConfirmDialogFragment.ConfirmDialogFragmentCallback
import awais.instagrabber.dialogs.MultiOptionDialogFragment import awais.instagrabber.dialogs.MultiOptionDialogFragment
import awais.instagrabber.dialogs.MultiOptionDialogFragment.MultiOptionDialogSingleCallback import awais.instagrabber.dialogs.MultiOptionDialogFragment.MultiOptionDialogSingleCallback
import awais.instagrabber.fragments.UserSearchFragment import awais.instagrabber.fragments.UserSearchMode
import awais.instagrabber.fragments.UserSearchFragmentDirections
import awais.instagrabber.models.Resource import awais.instagrabber.models.Resource
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.User
import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse import awais.instagrabber.repositories.responses.directmessages.DirectThreadParticipantRequestsResponse
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
import awais.instagrabber.utils.TextUtils.isEmpty
import awais.instagrabber.utils.Utils import awais.instagrabber.utils.Utils
import awais.instagrabber.utils.extensions.TAG import awais.instagrabber.utils.extensions.TAG
import awais.instagrabber.viewmodels.AppStateViewModel import awais.instagrabber.viewmodels.AppStateViewModel
@ -203,19 +201,22 @@ class DirectMessageSettingsFragment : Fragment(), ConfirmDialogFragmentCallback
} }
binding.addMembers.setOnClickListener { binding.addMembers.setOnClickListener {
if (!isAdded) return@setOnClickListener if (!isAdded) return@setOnClickListener
val navController = NavHostFragment.findNavController(this) try {
val currentDestination = navController.currentDestination ?: return@setOnClickListener val navController = findNavController()
if (currentDestination.id != R.id.directMessagesSettingsFragment) return@setOnClickListener if (navController.currentDestination?.id != R.id.directMessagesSettingsFragment) return@setOnClickListener
val users = viewModel.getUsers().value val users = viewModel.getUsers().value ?: return@setOnClickListener
val currentUserIds: LongArray = users?.asSequence()?.map { obj: User -> obj.pk }?.sorted()?.toList()?.toLongArray() ?: LongArray(0) val currentUserIds = users.asSequence().map(User::pk).sorted().toList().toLongArray()
val actionGlobalUserSearch = UserSearchFragmentDirections val actionGlobalUserSearch = DirectMessageSettingsFragmentDirections.actionToUserSearch().apply {
.actionGlobalUserSearch() title = getString(R.string.add_members)
.setTitle(getString(R.string.add_members)) actionLabel = getString(R.string.add)
.setActionLabel(getString(R.string.add)) hideUserIds = currentUserIds
.setHideUserIds(currentUserIds) searchMode = UserSearchMode.RAVEN
.setSearchMode(UserSearchFragment.SearchMode.RAVEN) multiple = true
.setMultiple(true) }
navController.navigate(actionGlobalUserSearch) navController.navigate(actionGlobalUserSearch)
} catch (e: Exception) {
Log.e(TAG, "setupSettings: ", e)
}
} }
binding.muteMentionsLabel.setOnClickListener { binding.muteMentions.toggle() } binding.muteMentionsLabel.setOnClickListener { binding.muteMentions.toggle() }
binding.muteMentions.setOnCheckedChangeListener { buttonView: CompoundButton, isChecked: Boolean -> binding.muteMentions.setOnCheckedChangeListener { buttonView: CompoundButton, isChecked: Boolean ->
@ -300,10 +301,13 @@ class DirectMessageSettingsFragment : Fragment(), ConfirmDialogFragmentCallback
Utils.openURL(context, "https://facebook.com/" + user.interopMessagingUserFbid) Utils.openURL(context, "https://facebook.com/" + user.interopMessagingUserFbid)
return@DirectUsersAdapter return@DirectUsersAdapter
} }
if (isEmpty(user.username)) return@DirectUsersAdapter if (user.username.isBlank()) return@DirectUsersAdapter
val directions = ProfileNavGraphDirections try {
.actionGlobalProfileFragment("@" + user.username) val directions = DirectMessageSettingsFragmentDirections.actionToProfile().apply { this.username = user.username }
NavHostFragment.findNavController(this).navigate(directions) findNavController().navigate(directions)
} catch (e: Exception) {
Log.e(TAG, "setupMembers: ", e)
}
}, },
{ _: Int, user: User? -> { _: Int, user: User? ->
val options = viewModel.createUserOptions(user) val options = viewModel.createUserOptions(user)
@ -339,9 +343,12 @@ class DirectMessageSettingsFragment : Fragment(), ConfirmDialogFragmentCallback
binding.pendingMembers.layoutManager = LinearLayoutManager(context) binding.pendingMembers.layoutManager = LinearLayoutManager(context)
pendingUsersAdapter = DirectPendingUsersAdapter(object : PendingUserCallback { pendingUsersAdapter = DirectPendingUsersAdapter(object : PendingUserCallback {
override fun onClick(position: Int, pendingUser: PendingUser) { override fun onClick(position: Int, pendingUser: PendingUser) {
val directions = ProfileNavGraphDirections try {
.actionGlobalProfileFragment("@" + pendingUser.user.username) val directions = DirectMessageSettingsFragmentDirections.actionToProfile().apply { this.username = pendingUser.user.username }
NavHostFragment.findNavController(this@DirectMessageSettingsFragment).navigate(directions) findNavController().navigate(directions)
} catch (e: Exception) {
Log.e(TAG, "onClick: ", e)
}
} }
override fun onApprove(position: Int, pendingUser: PendingUser) { override fun onApprove(position: Int, pendingUser: PendingUser) {
@ -420,7 +427,7 @@ class DirectMessageSettingsFragment : Fragment(), ConfirmDialogFragmentCallback
if (resource == null) return@observe if (resource == null) return@observe
when (resource.status) { when (resource.status) {
Resource.Status.SUCCESS -> { Resource.Status.SUCCESS -> {
val directions = DirectMessageSettingsFragmentDirections.actionSettingsToInbox() val directions = DirectMessageSettingsFragmentDirections.actionToInbox()
NavHostFragment.findNavController(this).navigate(directions) NavHostFragment.findNavController(this).navigate(directions)
} }
Resource.Status.ERROR -> { Resource.Status.ERROR -> {

View File

@ -65,9 +65,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import awais.instagrabber.ProfileNavGraphDirections;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.UserSearchNavGraphDirections;
import awais.instagrabber.activities.CameraActivity; import awais.instagrabber.activities.CameraActivity;
import awais.instagrabber.activities.MainActivity; import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.DirectItemsAdapter; import awais.instagrabber.adapters.DirectItemsAdapter;
@ -95,9 +93,7 @@ import awais.instagrabber.customviews.helpers.TranslateDeferringInsetsAnimationC
import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding; import awais.instagrabber.databinding.FragmentDirectMessagesThreadBinding;
import awais.instagrabber.dialogs.DirectItemReactionDialogFragment; import awais.instagrabber.dialogs.DirectItemReactionDialogFragment;
import awais.instagrabber.dialogs.GifPickerBottomDialogFragment; import awais.instagrabber.dialogs.GifPickerBottomDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment; import awais.instagrabber.fragments.UserSearchMode;
import awais.instagrabber.fragments.UserSearchFragment;
import awais.instagrabber.fragments.UserSearchFragmentDirections;
import awais.instagrabber.fragments.settings.PreferenceKeys; import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.models.Resource; import awais.instagrabber.models.Resource;
import awais.instagrabber.models.enums.DirectItemType; import awais.instagrabber.models.enums.DirectItemType;
@ -107,6 +103,9 @@ import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.DirectItem; import awais.instagrabber.repositories.responses.directmessages.DirectItem;
import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction; import awais.instagrabber.repositories.responses.directmessages.DirectItemEmojiReaction;
import awais.instagrabber.repositories.responses.directmessages.DirectItemLink;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReactions;
import awais.instagrabber.repositories.responses.directmessages.DirectItemReelShare;
import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare; import awais.instagrabber.repositories.responses.directmessages.DirectItemStoryShare;
import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia; import awais.instagrabber.repositories.responses.directmessages.DirectItemVisualMedia;
import awais.instagrabber.repositories.responses.directmessages.DirectThread; import awais.instagrabber.repositories.responses.directmessages.DirectThread;
@ -136,7 +135,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
private InsetsAnimationLinearLayout root; private InsetsAnimationLinearLayout root;
private boolean shouldRefresh = true; private boolean shouldRefresh = true;
private List<DirectItemOrHeader> itemOrHeaders; private List<DirectItemOrHeader> itemOrHeaders;
private List<User> users;
private FragmentDirectMessagesThreadBinding binding; private FragmentDirectMessagesThreadBinding binding;
private Tooltip tooltip; private Tooltip tooltip;
private float initialSendX; private float initialSendX;
@ -163,13 +161,13 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
private LiveData<List<User>> usersLiveData; private LiveData<List<User>> usersLiveData;
private boolean autoMarkAsSeen = false; private boolean autoMarkAsSeen = false;
private MenuItem markAsSeenMenuItem; private MenuItem markAsSeenMenuItem;
private Media tempMedia;
private DirectItem addReactionItem; private DirectItem addReactionItem;
private TranslateDeferringInsetsAnimationCallback inputHolderAnimationCallback; private TranslateDeferringInsetsAnimationCallback inputHolderAnimationCallback;
private TranslateDeferringInsetsAnimationCallback chatsAnimationCallback; private TranslateDeferringInsetsAnimationCallback chatsAnimationCallback;
private EmojiPickerInsetsAnimationCallback emojiPickerAnimationCallback; private EmojiPickerInsetsAnimationCallback emojiPickerAnimationCallback;
private boolean hasKbOpenedOnce; private boolean hasKbOpenedOnce;
private boolean wasToggled; private boolean wasToggled;
private SwipeAndRestoreItemTouchHelperCallback touchHelperCallback;
private final AppExecutors appExecutors = AppExecutors.INSTANCE; private final AppExecutors appExecutors = AppExecutors.INSTANCE;
private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() { private final Animatable2Compat.AnimationCallback micToSendAnimationCallback = new Animatable2Compat.AnimationCallback() {
@ -189,8 +187,12 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
private final DirectItemCallback directItemCallback = new DirectItemCallback() { private final DirectItemCallback directItemCallback = new DirectItemCallback() {
@Override @Override
public void onHashtagClick(final String hashtag) { public void onHashtagClick(final String hashtag) {
final NavDirections action = DirectMessageThreadFragmentDirections.actionGlobalHashTagFragment(hashtag); try {
final NavDirections action = DirectMessageThreadFragmentDirections.actionToHashtag(hashtag);
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action); NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onHashtagClick: ", e);
}
} }
@Override @Override
@ -200,8 +202,12 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
@Override @Override
public void onLocationClick(final long locationId) { public void onLocationClick(final long locationId) {
final NavDirections action = DirectMessageThreadFragmentDirections.actionGlobalLocationFragment(locationId); try {
final NavDirections action = DirectMessageThreadFragmentDirections.actionToLocation(locationId);
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action); NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onLocationClick: ", e);
}
} }
@Override @Override
@ -221,26 +227,23 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
@Override @Override
public void onMediaClick(final Media media, final int index) { public void onMediaClick(final Media media, final int index) {
if (media.isReelMedia()) { if (media.isReelMedia()) {
final String pk = media.getPk();
try { try {
final String pk = media.getPk();
if (pk == null) return;
final long mediaId = Long.parseLong(pk); final long mediaId = Long.parseLong(pk);
final User user = media.getUser(); final User user = media.getUser();
if (user == null) return; if (user == null) return;
final String username = user.getUsername(); final String username = user.getUsername();
final NavDirections action = DirectMessageThreadFragmentDirections final NavDirections action = DirectMessageThreadFragmentDirections.actionToStory(StoryViewerOptions.forStory(mediaId, username));
.actionThreadToStory(StoryViewerOptions.forStory(mediaId, username));
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action); NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);
} catch (NumberFormatException e) { } catch (Exception e) {
Log.e(TAG, "onMediaClick (story): ", e); Log.e(TAG, "onMediaClick (story): ", e);
} }
return; return;
} }
final NavController navController = NavHostFragment.findNavController(DirectMessageThreadFragment.this);
final Bundle bundle = new Bundle();
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, media);
bundle.putInt(PostViewV2Fragment.ARG_SLIDER_POSITION, index);
try { try {
navController.navigate(R.id.action_global_post_view, bundle); final NavDirections actionToPost = DirectMessageThreadFragmentDirections.actionToPost(media, index);
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(actionToPost);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "openPostDialog: ", e); Log.e(TAG, "openPostDialog: ", e);
} }
@ -248,16 +251,18 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
@Override @Override
public void onStoryClick(final DirectItemStoryShare storyShare) { public void onStoryClick(final DirectItemStoryShare storyShare) {
final String pk = storyShare.getReelId();
try { try {
final String pk = storyShare.getReelId();
if (pk == null) return;
final long mediaId = Long.parseLong(pk); final long mediaId = Long.parseLong(pk);
final User user = storyShare.getMedia().getUser(); final Media media = storyShare.getMedia();
if (media == null) return;
final User user = media.getUser();
if (user == null) return; if (user == null) return;
final String username = user.getUsername(); final String username = user.getUsername();
final NavDirections action = DirectMessageThreadFragmentDirections final NavDirections action = DirectMessageThreadFragmentDirections.actionToStory(StoryViewerOptions.forUser(mediaId, username));
.actionThreadToStory(StoryViewerOptions.forUser(mediaId, username));
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action); NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(action);
} catch (NumberFormatException e) { } catch (Exception e) {
Log.e(TAG, "onStoryClick: ", e); Log.e(TAG, "onStoryClick: ", e);
} }
} }
@ -266,10 +271,8 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
public void onReaction(final DirectItem item, final Emoji emoji) { public void onReaction(final DirectItem item, final Emoji emoji) {
if (item == null || emoji == null) return; if (item == null || emoji == null) return;
final LiveData<Resource<Object>> resourceLiveData = viewModel.sendReaction(item, emoji); final LiveData<Resource<Object>> resourceLiveData = viewModel.sendReaction(item, emoji);
if (resourceLiveData != null) {
resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData)); resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));
} }
}
@Override @Override
public void onReactionClick(final DirectItem item, final int position) { public void onReactionClick(final DirectItem item, final int position) {
@ -284,15 +287,14 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
} }
if (itemId == R.id.forward) { if (itemId == R.id.forward) {
itemToForward = item; itemToForward = item;
final UserSearchNavGraphDirections.ActionGlobalUserSearch actionGlobalUserSearch = UserSearchFragmentDirections final NavDirections actionGlobalUserSearch = DirectMessageThreadFragmentDirections
.actionGlobalUserSearch() .actionToUserSearch()
.setTitle(getString(R.string.forward)) .setTitle(getString(R.string.forward))
.setActionLabel(getString(R.string.send)) .setActionLabel(getString(R.string.send))
.setShowGroups(true) .setShowGroups(true)
.setMultiple(true) .setMultiple(true)
.setSearchMode(UserSearchFragment.SearchMode.RAVEN); .setSearchMode(UserSearchMode.RAVEN);
final NavController navController = NavHostFragment.findNavController(DirectMessageThreadFragment.this); NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(actionGlobalUserSearch);
navController.navigate(actionGlobalUserSearch);
} }
if (itemId == R.id.download) { if (itemId == R.id.download) {
downloadItem(item); downloadItem(item);
@ -418,10 +420,10 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
public boolean onOptionsItemSelected(@NonNull final MenuItem item) { public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
final int itemId = item.getItemId(); final int itemId = item.getItemId();
if (itemId == R.id.info) { if (itemId == R.id.info) {
final DirectMessageThreadFragmentDirections.ActionThreadToSettings directions = DirectMessageThreadFragmentDirections
.actionThreadToSettings(viewModel.getThreadId(), null);
final Boolean pending = viewModel.isPending().getValue(); final Boolean pending = viewModel.isPending().getValue();
directions.setPending(pending != null && pending); final NavDirections directions = DirectMessageThreadFragmentDirections
.actionToSettings(viewModel.getThreadId(), null)
.setPending(pending != null && pending);
NavHostFragment.findNavController(this).navigate(directions); NavHostFragment.findNavController(this).navigate(directions);
return true; return true;
} }
@ -485,7 +487,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
} }
final Uri uri = data.getData(); final Uri uri = data.getData();
final String mimeType = Utils.getMimeType(uri, context.getContentResolver()); final String mimeType = Utils.getMimeType(uri, context.getContentResolver());
if (mimeType.startsWith("image")) { if (mimeType != null && mimeType.startsWith("image")) {
navigateToImageEditFragment(uri); navigateToImageEditFragment(uri);
return; return;
} }
@ -584,7 +586,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
if (getArguments() == null) return; if (getArguments() == null) return;
actionBar = fragmentActivity.getSupportActionBar(); actionBar = fragmentActivity.getSupportActionBar();
setupList(); setupList();
root.post(this::setupInput);
} }
private void setupList() { private void setupList() {
@ -612,20 +613,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
} }
}); });
binding.chats.addItemDecoration(headerItemDecoration); binding.chats.addItemDecoration(headerItemDecoration);
final SwipeAndRestoreItemTouchHelperCallback touchHelperCallback = new SwipeAndRestoreItemTouchHelperCallback(
context,
(adapterPosition, viewHolder) -> {
if (itemsAdapter == null) return;
final DirectItemOrHeader directItemOrHeader = itemsAdapter.getList().get(adapterPosition);
if (directItemOrHeader.isHeader()) return;
viewModel.setReplyToItem(directItemOrHeader.item);
}
);
final Integer inputMode = viewModel.getInputMode().getValue();
if (inputMode != null && inputMode != 1) {
itemTouchHelper = new ItemTouchHelper(touchHelperCallback);
itemTouchHelper.attachToRecyclerView(binding.chats);
}
} }
private void setObservers() { private void setObservers() {
@ -653,8 +640,12 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
inputModeLiveData = viewModel.getInputMode(); inputModeLiveData = viewModel.getInputMode();
inputModeLiveData.observe(getViewLifecycleOwner(), inputMode -> { inputModeLiveData.observe(getViewLifecycleOwner(), inputMode -> {
final Boolean isPending = viewModel.isPending().getValue(); final Boolean isPending = viewModel.isPending().getValue();
if (isPending != null && isPending) return; if (isPending != null && isPending || inputMode == null) return;
if (inputMode == null || inputMode == 0) return; setupInput(inputMode);
if (inputMode == 0) {
setupTouchHelper();
return;
}
if (inputMode == 1) { if (inputMode == 1) {
hideInput(); hideInput();
} }
@ -754,6 +745,22 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
}); });
} }
private void setupTouchHelper() {
final Context context = getContext();
if (context == null) return;
touchHelperCallback = new SwipeAndRestoreItemTouchHelperCallback(
context,
(adapterPosition, viewHolder) -> {
if (itemsAdapter == null) return;
final DirectItemOrHeader directItemOrHeader = itemsAdapter.getList().get(adapterPosition);
if (directItemOrHeader.isHeader()) return;
viewModel.setReplyToItem(directItemOrHeader.item);
}
);
itemTouchHelper = new ItemTouchHelper(touchHelperCallback);
itemTouchHelper.attachToRecyclerView(binding.chats);
}
private void removeObservers() { private void removeObservers() {
pendingLiveData.removeObservers(getViewLifecycleOwner()); pendingLiveData.removeObservers(getViewLifecycleOwner());
inputModeLiveData.removeObservers(getViewLifecycleOwner()); inputModeLiveData.removeObservers(getViewLifecycleOwner());
@ -894,19 +901,26 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
binding.gallery.setVisibility(View.GONE); binding.gallery.setVisibility(View.GONE);
} }
private String getDirectItemPreviewText(final DirectItem item) { private String getDirectItemPreviewText(@NonNull final DirectItem item) {
switch (item.getItemType()) { final DirectItemType itemType = item.getItemType();
if (itemType == null) return "";
switch (itemType) {
case TEXT: case TEXT:
return item.getText(); return item.getText();
case LINK: case LINK:
return item.getLink().getText(); final DirectItemLink link = item.getLink();
if (link == null) return "";
return link.getText();
case MEDIA: { case MEDIA: {
final Media media = item.getMedia(); final Media media = item.getMedia();
if (media == null) return "";
return getMediaPreviewTextString(media); return getMediaPreviewTextString(media);
} }
case RAVEN_MEDIA: { case RAVEN_MEDIA: {
final DirectItemVisualMedia visualMedia = item.getVisualMedia(); final DirectItemVisualMedia visualMedia = item.getVisualMedia();
if (visualMedia == null) return "";
final Media media = visualMedia.getMedia(); final Media media = visualMedia.getMedia();
if (media == null) return "";
return getMediaPreviewTextString(media); return getMediaPreviewTextString(media);
} }
case VOICE_MEDIA: case VOICE_MEDIA:
@ -914,14 +928,17 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
case MEDIA_SHARE: case MEDIA_SHARE:
return getString(R.string.post); return getString(R.string.post);
case REEL_SHARE: case REEL_SHARE:
return item.getReelShare().getText(); final DirectItemReelShare reelShare = item.getReelShare();
if (reelShare == null) return "";
return reelShare.getText();
} }
return ""; return "";
} }
@NonNull @NonNull
private String getMediaPreviewTextString(final Media media) { private String getMediaPreviewTextString(@NonNull final Media media) {
final MediaItemType mediaType = media.getType(); final MediaItemType mediaType = media.getType();
if (mediaType == null) return "";
switch (mediaType) { switch (mediaType) {
case MEDIA_TYPE_IMAGE: case MEDIA_TYPE_IMAGE:
return getString(R.string.photo); return getString(R.string.photo);
@ -932,8 +949,11 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
} }
} }
private String getDirectItemPreviewImageUrl(final DirectItem item) { @Nullable
switch (item.getItemType()) { private String getDirectItemPreviewImageUrl(@NonNull final DirectItem item) {
final DirectItemType itemType = item.getItemType();
if (itemType == null) return null;
switch (itemType) {
case TEXT: case TEXT:
case LINK: case LINK:
case VOICE_MEDIA: case VOICE_MEDIA:
@ -945,6 +965,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
} }
case RAVEN_MEDIA: { case RAVEN_MEDIA: {
final DirectItemVisualMedia visualMedia = item.getVisualMedia(); final DirectItemVisualMedia visualMedia = item.getVisualMedia();
if (visualMedia == null) return null;
final Media media = visualMedia.getMedia(); final Media media = visualMedia.getMedia();
return ResponseBodyUtils.getThumbUrl(media); return ResponseBodyUtils.getThumbUrl(media);
} }
@ -1009,7 +1030,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
itemsAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY); itemsAdapter.setStateRestorationPolicy(RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY);
binding.chats.setAdapter(itemsAdapter); binding.chats.setAdapter(itemsAdapter);
registerDataObserver(); registerDataObserver();
users = thread.getUsers();
final List<DirectItem> items = viewModel.getItems().getValue(); final List<DirectItem> items = viewModel.getItems().getValue();
if (items != null && itemsAdapter.getItems() != items) { if (items != null && itemsAdapter.getItems() != items) {
submitItemsToAdapter(items); submitItemsToAdapter(items);
@ -1032,8 +1052,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
}); });
} }
private void setupInput() { private void setupInput(@Nullable final Integer inputMode) {
final Integer inputMode = viewModel.getInputMode().getValue();
if (inputMode != null && inputMode == 1) return; if (inputMode != null && inputMode == 1) return;
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
@ -1234,9 +1253,12 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
} }
private void navigateToImageEditFragment(final Uri uri) { private void navigateToImageEditFragment(final Uri uri) {
final NavDirections navDirections = DirectMessageThreadFragmentDirections.actionThreadToImageEdit(uri); try {
final NavController navController = NavHostFragment.findNavController(this); final NavDirections navDirections = DirectMessageThreadFragmentDirections.actionToImageEdit(uri);
navController.navigate(navDirections); NavHostFragment.findNavController(this).navigate(navDirections);
} catch (Exception e) {
Log.e(TAG, "navigateToImageEditFragment: ", e);
}
} }
private void handleSentMessage(final LiveData<Resource<Object>> resourceLiveData) { private void handleSentMessage(final LiveData<Resource<Object>> resourceLiveData) {
@ -1403,11 +1425,14 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
final Context context = getContext(); final Context context = getContext();
if (context == null) return; if (context == null) return;
final DirectItemType itemType = item.getItemType(); final DirectItemType itemType = item.getItemType();
if (itemType == null) return;
//noinspection SwitchStatementWithTooFewBranches //noinspection SwitchStatementWithTooFewBranches
switch (itemType) { switch (itemType) {
case VOICE_MEDIA: case VOICE_MEDIA:
downloadItem(context, item.getVoiceMedia() == null ? null : item.getVoiceMedia().getMedia()); downloadItem(context, item.getVoiceMedia() == null ? null : item.getVoiceMedia().getMedia());
break; break;
default:
break;
} }
} }
@ -1421,15 +1446,6 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
Toast.makeText(context, R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show(); Toast.makeText(context, R.string.downloader_downloading_media, Toast.LENGTH_SHORT).show();
} }
@Nullable
private User getUser(final long userId) {
for (final User user : users) {
if (userId != user.getPk()) continue;
return user;
}
return null;
}
// Sets the translationY of views to height with animation // Sets the translationY of views to height with animation
private void animatePan(final int height, private void animatePan(final int height,
@Nullable final Function<Void, Void> onAnimationStart, @Nullable final Function<Void, Void> onAnimationStart,
@ -1476,17 +1492,22 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
final LiveData<List<User>> leftUsers = viewModel.getLeftUsers(); final LiveData<List<User>> leftUsers = viewModel.getLeftUsers();
final ArrayList<User> allUsers = new ArrayList<>(); final ArrayList<User> allUsers = new ArrayList<>();
allUsers.add(viewModel.getCurrentUser()); allUsers.add(viewModel.getCurrentUser());
if (users != null && users.getValue() != null) { if (users.getValue() != null) {
allUsers.addAll(users.getValue()); allUsers.addAll(users.getValue());
} }
if (leftUsers != null && leftUsers.getValue() != null) { if (leftUsers.getValue() != null) {
allUsers.addAll(leftUsers.getValue()); allUsers.addAll(leftUsers.getValue());
} }
reactionDialogFragment = DirectItemReactionDialogFragment final String itemId = item.getItemId();
.newInstance(viewModel.getViewerId(), if (itemId == null) return;
final DirectItemReactions reactions = item.getReactions();
if (reactions == null) return;
reactionDialogFragment = DirectItemReactionDialogFragment.newInstance(
viewModel.getViewerId(),
allUsers, allUsers,
item.getItemId(), itemId,
item.getReactions()); reactions
);
reactionDialogFragment.show(getChildFragmentManager(), "reactions_dialog"); reactionDialogFragment.show(getChildFragmentManager(), "reactions_dialog");
} }
@ -1498,9 +1519,7 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
if (itemId == null || reaction == null) return; if (itemId == null || reaction == null) return;
if (reaction.getSenderId() == viewModel.getViewerId()) { if (reaction.getSenderId() == viewModel.getViewerId()) {
final LiveData<Resource<Object>> resourceLiveData = viewModel.sendDeleteReaction(itemId); final LiveData<Resource<Object>> resourceLiveData = viewModel.sendDeleteReaction(itemId);
if (resourceLiveData != null) {
resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData)); resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));
}
return; return;
} }
// navigate to user // navigate to user
@ -1510,17 +1529,18 @@ public class DirectMessageThreadFragment extends Fragment implements DirectReact
} }
private void navigateToUser(@NonNull final String username) { private void navigateToUser(@NonNull final String username) {
final ProfileNavGraphDirections.ActionGlobalProfileFragment direction = ProfileNavGraphDirections try {
.actionGlobalProfileFragment("@" + username); final NavDirections direction = DirectMessageThreadFragmentDirections.actionToProfile().setUsername(username);
NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(direction); NavHostFragment.findNavController(DirectMessageThreadFragment.this).navigate(direction);
} catch (Exception e) {
Log.e(TAG, "navigateToUser: ", e);
}
} }
@Override @Override
public void onClick(final View view, final Emoji emoji) { public void onClick(final View view, final Emoji emoji) {
if (addReactionItem == null || emoji == null) return; if (addReactionItem == null || emoji == null) return;
final LiveData<Resource<Object>> resourceLiveData = viewModel.sendReaction(addReactionItem, emoji); final LiveData<Resource<Object>> resourceLiveData = viewModel.sendReaction(addReactionItem, emoji);
if (resourceLiveData != null) {
resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData)); resourceLiveData.observe(getViewLifecycleOwner(), directItemResource -> handleSentMessage(resourceLiveData));
} }
}
} }

View File

@ -134,7 +134,7 @@ class DirectPendingInboxFragment : Fragment(), OnRefreshListener {
val threadTitle = thread.threadTitle ?: return@DirectMessageInboxAdapter val threadTitle = thread.threadTitle ?: return@DirectMessageInboxAdapter
navigating = true navigating = true
if (isAdded) { if (isAdded) {
val directions = DirectPendingInboxFragmentDirections.actionPendingInboxToThread(threadId, threadTitle) val directions = DirectPendingInboxFragmentDirections.actionToThread(threadId, threadTitle)
directions.pending = true directions.pending = true
NavHostFragment.findNavController(this).navigate(directions) NavHostFragment.findNavController(this).navigate(directions)
} }

View File

@ -13,7 +13,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.FragmentNavigator; import androidx.navigation.fragment.FragmentNavigator;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -26,7 +26,6 @@ import awais.instagrabber.activities.MainActivity;
import awais.instagrabber.adapters.DiscoverTopicsAdapter; import awais.instagrabber.adapters.DiscoverTopicsAdapter;
import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration; import awais.instagrabber.customviews.helpers.GridSpacingItemDecoration;
import awais.instagrabber.databinding.FragmentDiscoverBinding; import awais.instagrabber.databinding.FragmentDiscoverBinding;
import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.discover.TopicCluster; import awais.instagrabber.repositories.responses.discover.TopicCluster;
import awais.instagrabber.repositories.responses.discover.TopicalExploreFeedResponse; import awais.instagrabber.repositories.responses.discover.TopicalExploreFeedResponse;
@ -98,11 +97,14 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
binding.topicsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(2))); binding.topicsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(Utils.convertDpToPx(2)));
final DiscoverTopicsAdapter.OnTopicClickListener otcl = new DiscoverTopicsAdapter.OnTopicClickListener() { final DiscoverTopicsAdapter.OnTopicClickListener otcl = new DiscoverTopicsAdapter.OnTopicClickListener() {
public void onTopicClick(final TopicCluster topicCluster, final View cover, final int titleColor, final int backgroundColor) { public void onTopicClick(final TopicCluster topicCluster, final View cover, final int titleColor, final int backgroundColor) {
try {
final FragmentNavigator.Extras.Builder builder = new FragmentNavigator.Extras.Builder() final FragmentNavigator.Extras.Builder builder = new FragmentNavigator.Extras.Builder()
.addSharedElement(cover, "cover-" + topicCluster.getId()); .addSharedElement(cover, "cover-" + topicCluster.getId());
final DiscoverFragmentDirections.ActionDiscoverFragmentToTopicPostsFragment action = DiscoverFragmentDirections final NavDirections action = DiscoverFragmentDirections.actionToTopicPosts(topicCluster, titleColor, backgroundColor);
.actionDiscoverFragmentToTopicPostsFragment(topicCluster, titleColor, backgroundColor);
NavHostFragment.findNavController(DiscoverFragment.this).navigate(action, builder.build()); NavHostFragment.findNavController(DiscoverFragment.this).navigate(action, builder.build());
} catch (Exception e) {
Log.e(TAG, "onTopicClick: ", e);
}
} }
public void onTopicLongClick(final Media coverMedia) { public void onTopicLongClick(final Media coverMedia) {
@ -123,11 +125,9 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
} catch (Throwable ignored) {} } catch (Throwable ignored) {}
return; return;
} }
final NavController navController = NavHostFragment.findNavController(DiscoverFragment.this);
final Bundle bundle = new Bundle();
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, media);
try { try {
navController.navigate(R.id.action_global_post_view, bundle); final NavDirections action = DiscoverFragmentDirections.actionToPost(media, 0);
NavHostFragment.findNavController(DiscoverFragment.this).navigate(action);
alertDialog.dismiss(); alertDialog.dismiss();
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "onTopicLongClick: ", e); Log.e(TAG, "onTopicLongClick: ", e);
@ -148,11 +148,13 @@ public class DiscoverFragment extends Fragment implements SwipeRefreshLayout.OnR
public void onSuccess(final TopicalExploreFeedResponse result) { public void onSuccess(final TopicalExploreFeedResponse result) {
if (result == null) return; if (result == null) return;
final List<TopicCluster> clusters = result.getClusters(); final List<TopicCluster> clusters = result.getClusters();
if (clusters == null || result.getItems() == null) return;
binding.swipeRefreshLayout.setRefreshing(false); binding.swipeRefreshLayout.setRefreshing(false);
if (clusters.size() == 1 && result.getItems().size() > 0) { if (clusters.size() == 1 && result.getItems().size() > 0) {
final TopicCluster cluster = clusters.get(0); final TopicCluster cluster = clusters.get(0);
if (cluster.getCoverMedia() == null) if (cluster.getCoverMedia() == null) {
cluster.setCoverMedia(result.getItems().get(0).getMedia()); cluster.setCoverMedia(result.getItems().get(0).getMedia());
}
topicClusterViewModel.getList().postValue(Collections.singletonList(cluster)); topicClusterViewModel.getList().postValue(Collections.singletonList(cluster));
return; return;
} }

View File

@ -40,10 +40,11 @@ import awais.instagrabber.asyncs.FeedPostFetchService;
import awais.instagrabber.customviews.PrimaryActionModeCallback; import awais.instagrabber.customviews.PrimaryActionModeCallback;
import awais.instagrabber.databinding.FragmentFeedBinding; import awais.instagrabber.databinding.FragmentFeedBinding;
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment; import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment;
import awais.instagrabber.fragments.PostViewV2Fragment;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.repositories.requests.StoryViewerOptions; import awais.instagrabber.repositories.requests.StoryViewerOptions;
import awais.instagrabber.repositories.responses.Location;
import awais.instagrabber.repositories.responses.Media; import awais.instagrabber.repositories.responses.Media;
import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.stories.Story; import awais.instagrabber.repositories.responses.stories.Story;
import awais.instagrabber.utils.AppExecutors; import awais.instagrabber.utils.AppExecutors;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
@ -62,7 +63,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
private FragmentFeedBinding binding; private FragmentFeedBinding binding;
private StoriesRepository storiesRepository; private StoriesRepository storiesRepository;
private boolean shouldRefresh = true; private boolean shouldRefresh = true;
private final boolean isRotate = false;
private FeedStoriesViewModel feedStoriesViewModel; private FeedStoriesViewModel feedStoriesViewModel;
private boolean storiesFetching; private boolean storiesFetching;
private ActionMode actionMode; private ActionMode actionMode;
@ -77,15 +77,20 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
public void onFeedStoryClick(Story model, int position) { public void onFeedStoryClick(Story model, int position) {
final NavController navController = NavHostFragment.findNavController(FeedFragment.this); final NavController navController = NavHostFragment.findNavController(FeedFragment.this);
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections action = FeedFragmentDirections try {
.actionFeedFragmentToStoryViewerFragment(StoryViewerOptions.forFeedStoryPosition(position)); final NavDirections action = FeedFragmentDirections.actionToStory(StoryViewerOptions.forFeedStoryPosition(position));
navController.navigate(action); navController.navigate(action);
} catch (Exception e) {
Log.e(TAG, "onFeedStoryClick: ", e);
}
} }
} }
@Override @Override
public void onFeedStoryLongClick(Story model, int position) { public void onFeedStoryLongClick(Story model, int position) {
navigateToProfile("@" + model.getUser().getUsername()); final User user = model.getUser();
if (user == null) return;
navigateToProfile("@" + user.getUsername());
} }
} }
); );
@ -104,10 +109,12 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
@Override @Override
public void onCommentsClick(final Media feedModel) { public void onCommentsClick(final Media feedModel) {
try { try {
final NavDirections commentsAction = FeedFragmentDirections.actionGlobalCommentsViewerFragment( final User user = feedModel.getUser();
if (user == null) return;
final NavDirections commentsAction = FeedFragmentDirections.actionToComments(
feedModel.getCode(), feedModel.getCode(),
feedModel.getPk(), feedModel.getPk(),
feedModel.getUser().getPk() user.getPk()
); );
NavHostFragment.findNavController(FeedFragment.this).navigate(commentsAction); NavHostFragment.findNavController(FeedFragment.this).navigate(commentsAction);
} catch (Exception e) { } catch (Exception e) {
@ -124,14 +131,24 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
@Override @Override
public void onHashtagClick(final String hashtag) { public void onHashtagClick(final String hashtag) {
final NavDirections action = FeedFragmentDirections.actionGlobalHashTagFragment(hashtag); try {
final NavDirections action = FeedFragmentDirections.actionToHashtag(hashtag);
NavHostFragment.findNavController(FeedFragment.this).navigate(action); NavHostFragment.findNavController(FeedFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onHashtagClick: ", e);
}
} }
@Override @Override
public void onLocationClick(final Media feedModel) { public void onLocationClick(final Media feedModel) {
final NavDirections action = FeedFragmentDirections.actionGlobalLocationFragment(feedModel.getLocation().getPk()); final Location location = feedModel.getLocation();
if (location == null) return;
try {
final NavDirections action = FeedFragmentDirections.actionToLocation(location.getPk());
NavHostFragment.findNavController(FeedFragment.this).navigate(action); NavHostFragment.findNavController(FeedFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onLocationClick: ", e);
}
} }
@Override @Override
@ -162,12 +179,9 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
} }
private void openPostDialog(final Media feedModel, final int position) { private void openPostDialog(final Media feedModel, final int position) {
final NavController navController = NavHostFragment.findNavController(FeedFragment.this);
final Bundle bundle = new Bundle();
bundle.putSerializable(PostViewV2Fragment.ARG_MEDIA, feedModel);
bundle.putInt(PostViewV2Fragment.ARG_SLIDER_POSITION, position);
try { try {
navController.navigate(R.id.action_global_post_view, bundle); final NavDirections action = FeedFragmentDirections.actionToPost(feedModel, position);
NavHostFragment.findNavController(FeedFragment.this).navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "openPostDialog: ", e); Log.e(TAG, "openPostDialog: ", e);
} }
@ -237,10 +251,12 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
}; };
private void navigateToProfile(final String username) { private void navigateToProfile(final String username) {
final NavController navController = NavHostFragment.findNavController(this); try {
final Bundle bundle = new Bundle(); final NavDirections action = FeedFragmentDirections.actionToProfile().setUsername(username);
bundle.putString("username", username); NavHostFragment.findNavController(this).navigate(action);
navController.navigate(R.id.action_global_profileFragment, bundle); } catch (Exception e) {
Log.e(TAG, "navigateToProfile: ", e);
}
} }
@Override @Override
@ -301,8 +317,12 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
@Override @Override
public boolean onOptionsItemSelected(@NonNull final MenuItem item) { public boolean onOptionsItemSelected(@NonNull final MenuItem item) {
if (item.getItemId() == R.id.storyList) { if (item.getItemId() == R.id.storyList) {
final NavDirections action = FeedFragmentDirections.actionGlobalStoryListViewerFragment("feed"); try {
final NavDirections action = FeedFragmentDirections.actionToStoryList("feed");
NavHostFragment.findNavController(FeedFragment.this).navigate(action); NavHostFragment.findNavController(FeedFragment.this).navigate(action);
} catch (Exception e) {
Log.e(TAG, "onOptionsItemSelected: ", e);
}
} else if (item.getItemId() == R.id.layout) { } else if (item.getItemId() == R.id.layout) {
showPostsLayoutPreferences(); showPostsLayoutPreferences();
return true; return true;
@ -316,14 +336,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
fetchStories(); fetchStories();
} }
@Override
public void onDestroyView() {
super.onDestroyView();
if (storiesRecyclerView != null) {
fragmentActivity.removeCollapsingView(storiesRecyclerView);
}
}
private void setupFeed() { private void setupFeed() {
binding.feedRecyclerView.setViewModelStoreOwner(this) binding.feedRecyclerView.setViewModelStoreOwner(this)
.setLifeCycleOwner(this) .setLifeCycleOwner(this)
@ -333,7 +345,6 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
.setFeedItemCallback(feedItemCallback) .setFeedItemCallback(feedItemCallback)
.setSelectionModeCallback(selectionModeCallback) .setSelectionModeCallback(selectionModeCallback)
.init(); .init();
binding.feedSwipeRefreshLayout.setRefreshing(true);
binding.feedRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { binding.feedRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override @Override
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) { public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
@ -397,7 +408,8 @@ public class FeedFragment extends Fragment implements SwipeRefreshLayout.OnRefre
preferences -> { preferences -> {
layoutPreferences = preferences; layoutPreferences = preferences;
new Handler().postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200); new Handler().postDelayed(() -> binding.feedRecyclerView.setLayoutPreferences(preferences), 200);
}); }
);
fragment.show(getChildFragmentManager(), "posts_layout_preferences"); fragment.show(getChildFragmentManager(), "posts_layout_preferences");
} }

View File

@ -13,13 +13,13 @@ import android.view.*
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
@ -40,10 +40,7 @@ import awais.instagrabber.dialogs.MultiOptionDialogFragment.MultiOptionDialogSin
import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option import awais.instagrabber.dialogs.MultiOptionDialogFragment.Option
import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment import awais.instagrabber.dialogs.PostsLayoutPreferencesDialogFragment
import awais.instagrabber.dialogs.ProfilePicDialogFragment import awais.instagrabber.dialogs.ProfilePicDialogFragment
import awais.instagrabber.fragments.HashTagFragment.ARG_HASHTAG import awais.instagrabber.fragments.UserSearchMode
import awais.instagrabber.fragments.PostViewV2Fragment
import awais.instagrabber.fragments.UserSearchFragment
import awais.instagrabber.fragments.UserSearchFragmentDirections
import awais.instagrabber.managers.DirectMessagesManager import awais.instagrabber.managers.DirectMessagesManager
import awais.instagrabber.models.Resource import awais.instagrabber.models.Resource
import awais.instagrabber.models.enums.PostItemType import awais.instagrabber.models.enums.PostItemType
@ -77,16 +74,26 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
private var selectedMedia: List<Media>? = null private var selectedMedia: List<Media>? = null
private var actionMode: ActionMode? = null private var actionMode: ActionMode? = null
private var disableDm: Boolean = false private var disableDm: Boolean = false
private var shouldRefresh: Boolean = true
// private var shouldRefresh: Boolean = true
private var highlightsAdapter: HighlightsAdapter? = null private var highlightsAdapter: HighlightsAdapter? = null
private var layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_PROFILE_POSTS_LAYOUT) private var layoutPreferences = Utils.getPostsLayoutPreferences(Constants.PREF_PROFILE_POSTS_LAYOUT)
private lateinit var mainActivity: MainActivity private lateinit var mainActivity: MainActivity
private lateinit var root: MotionLayout
// private lateinit var root: MotionLayout
private lateinit var binding: FragmentProfileBinding private lateinit var binding: FragmentProfileBinding
private lateinit var appStateViewModel: AppStateViewModel private lateinit var appStateViewModel: AppStateViewModel
private lateinit var viewModel: ProfileFragmentViewModel private lateinit var viewModel: ProfileFragmentViewModel
private val userRepository by lazy { UserRepository.getInstance() }
private val friendshipRepository by lazy { FriendshipRepository.getInstance() }
private val storiesRepository by lazy { StoriesRepository.getInstance() }
private val mediaRepository by lazy { MediaRepository.getInstance() }
private val graphQLRepository by lazy { GraphQLRepository.getInstance() }
private val favoriteRepository by lazy { FavoriteRepository.getInstance(requireContext()) }
private val directMessagesRepository by lazy { DirectMessagesRepository.getInstance() }
private val confirmDialogFragmentRequestCode = 100 private val confirmDialogFragmentRequestCode = 100
private val ppOptsDialogRequestCode = 101 private val ppOptsDialogRequestCode = 101
private val bioDialogRequestCode = 102 private val bioDialogRequestCode = 102
@ -105,8 +112,12 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
override fun onLocationClick(media: Media?) { override fun onLocationClick(media: Media?) {
val action = FeedFragmentDirections.actionGlobalLocationFragment(media?.location?.pk ?: return) try {
NavHostFragment.findNavController(this@ProfileFragment).navigate(action) val action = ProfileFragmentDirections.actionToLocation(media?.location?.pk ?: return)
findNavController().navigate(action)
} catch (e: Exception) {
Log.e(TAG, "onLocationClick: ", e)
}
} }
override fun onMentionClick(mention: String?) { override fun onMentionClick(mention: String?) {
@ -114,17 +125,25 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
override fun onHashtagClick(hashtag: String?) { override fun onHashtagClick(hashtag: String?) {
val action = FeedFragmentDirections.actionGlobalHashTagFragment(hashtag ?: return) try {
NavHostFragment.findNavController(this@ProfileFragment).navigate(action) val action = ProfileFragmentDirections.actionToHashtag(hashtag ?: return)
findNavController().navigate(action)
} catch (e: Exception) {
Log.e(TAG, "onHashtagClick: ", e)
}
} }
override fun onCommentsClick(media: Media?) { override fun onCommentsClick(media: Media?) {
val commentsAction = ProfileFragmentDirections.actionGlobalCommentsViewerFragment( try {
val commentsAction = ProfileFragmentDirections.actionToComments(
media?.code ?: return, media?.code ?: return,
media.pk ?: return, media.pk ?: return,
media.user?.pk ?: return media.user?.pk ?: return
) )
NavHostFragment.findNavController(this@ProfileFragment).navigate(commentsAction) findNavController().navigate(commentsAction)
} catch (e: Exception) {
Log.e(TAG, "onCommentsClick: ", e)
}
} }
override fun onDownloadClick(media: Media?, childPosition: Int, popupLocation: View) { override fun onDownloadClick(media: Media?, childPosition: Int, popupLocation: View) {
@ -216,24 +235,24 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
private val onFollowersClickListener = View.OnClickListener { private val onFollowersClickListener = View.OnClickListener {
try { try {
val action = ProfileFragmentDirections.actionProfileFragmentToFollowViewerFragment( val action = ProfileFragmentDirections.actionToFollowViewer(
viewModel.profile.value?.data?.pk ?: return@OnClickListener, viewModel.profile.value?.data?.pk ?: return@OnClickListener,
true, true,
viewModel.profile.value?.data?.username ?: return@OnClickListener viewModel.profile.value?.data?.username ?: return@OnClickListener
) )
NavHostFragment.findNavController(this).navigate(action) findNavController().navigate(action)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "onFollowersClickListener: ", e) Log.e(TAG, "onFollowersClickListener: ", e)
} }
} }
private val onFollowingClickListener = View.OnClickListener { private val onFollowingClickListener = View.OnClickListener {
try { try {
val action = ProfileFragmentDirections.actionProfileFragmentToFollowViewerFragment( val action = ProfileFragmentDirections.actionToFollowViewer(
viewModel.profile.value?.data?.pk ?: return@OnClickListener, viewModel.profile.value?.data?.pk ?: return@OnClickListener,
false, false,
viewModel.profile.value?.data?.username ?: return@OnClickListener viewModel.profile.value?.data?.username ?: return@OnClickListener
) )
NavHostFragment.findNavController(this).navigate(action) findNavController().navigate(action)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "onFollowersClickListener: ", e) Log.e(TAG, "onFollowersClickListener: ", e)
} }
@ -243,9 +262,8 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
private val onHashtagClickListener = OnHashtagClickListener { private val onHashtagClickListener = OnHashtagClickListener {
try { try {
val bundle = Bundle() val actionToHashtag = ProfileFragmentDirections.actionToHashtag(it.originalText.trimAll())
bundle.putString(ARG_HASHTAG, it.originalText.trimAll()) findNavController().navigate(actionToHashtag)
NavHostFragment.findNavController(this).navigate(R.id.action_global_hashTagFragment, bundle)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "onHashtagClickListener: ", e) Log.e(TAG, "onHashtagClickListener: ", e)
} }
@ -280,13 +298,9 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
private fun openPostDialog(media: Media, position: Int) { private fun openPostDialog(media: Media, position: Int) {
val bundle = Bundle().apply {
putSerializable(PostViewV2Fragment.ARG_MEDIA, media)
putInt(PostViewV2Fragment.ARG_SLIDER_POSITION, position)
}
try { try {
val navController = NavHostFragment.findNavController(this) val actionToPost = ProfileFragmentDirections.actionToPost(media, position)
navController.navigate(R.id.action_global_post_view, bundle) findNavController().navigate(actionToPost)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "openPostDialog: ", e) Log.e(TAG, "openPostDialog: ", e)
} }
@ -304,7 +318,15 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
viewModel = ViewModelProvider( viewModel = ViewModelProvider(
this, this,
ProfileFragmentViewModelFactory( ProfileFragmentViewModelFactory(
FavoriteRepository.getInstance(requireContext()), csrfToken,
deviceUuid,
userRepository,
friendshipRepository,
storiesRepository,
mediaRepository,
graphQLRepository,
favoriteRepository,
directMessagesRepository,
if (isLoggedIn) DirectMessagesManager else null, if (isLoggedIn) DirectMessagesManager else null,
this, this,
arguments arguments
@ -314,23 +336,12 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { 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) binding = FragmentProfileBinding.inflate(inflater, container, false)
root = binding.root return binding.root
return root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (!shouldRefresh) {
setupObservers()
return
}
init() init()
shouldRefresh = false
} }
override fun onRefresh() { override fun onRefresh() {
@ -378,17 +389,21 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
} }
override fun onDestroyView() {
super.onDestroyView()
setupPostsDone = false
}
private fun shareProfileViaDm() { private fun shareProfileViaDm() {
val actionGlobalUserSearch = UserSearchFragmentDirections.actionGlobalUserSearch().apply { try {
setTitle(getString(R.string.share)) val actionToUserSearch = ProfileFragmentDirections.actionToUserSearch().apply {
setActionLabel(getString(R.string.send)) title = getString(R.string.share)
actionLabel = getString(R.string.send)
showGroups = true showGroups = true
multiple = true multiple = true
setSearchMode(UserSearchFragment.SearchMode.RAVEN) searchMode = UserSearchMode.RAVEN
} }
try { findNavController().navigate(actionToUserSearch)
val navController = NavHostFragment.findNavController(this@ProfileFragment)
navController.navigate(actionGlobalUserSearch)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "shareProfileViaDm: ", e) Log.e(TAG, "shareProfileViaDm: ", e)
} }
@ -405,12 +420,12 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
private fun navigateToChaining() { private fun navigateToChaining() {
viewModel.currentUser.value?.data ?: return viewModel.currentUser.value?.data ?: return
val profile = viewModel.profile.value?.data ?: return val profile = viewModel.profile.value?.data ?: return
val bundle = Bundle().apply {
putString("type", "chaining")
putLong("targetId", profile.pk)
}
try { try {
NavHostFragment.findNavController(this).navigate(R.id.action_global_notificationsViewerFragment, bundle) val actionToNotifications = ProfileFragmentDirections.actionToNotifications().apply {
type = "chaining"
targetId = profile.pk
}
findNavController().navigate(actionToNotifications)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "navigateToChaining: ", e) Log.e(TAG, "navigateToChaining: ", e)
} }
@ -418,13 +433,17 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
private fun init() { private fun init() {
binding.swipeRefreshLayout.setOnRefreshListener(this) binding.swipeRefreshLayout.setOnRefreshListener(this)
disableDm = !Utils.isNavRootInCurrentTabs("direct_messages_nav_graph") disableDm = !isNavRootInCurrentTabs("direct_messages_nav_graph")
setupHighlights() setupHighlights()
setupObservers() setupObservers()
} }
private fun setupObservers() { 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) { viewModel.currentUserProfileActionLiveData.observe(viewLifecycleOwner) {
val (currentUserResource, profileResource) = it val (currentUserResource, profileResource) = it
if (currentUserResource.status == Resource.Status.ERROR || profileResource.status == Resource.Status.ERROR) { if (currentUserResource.status == Resource.Status.ERROR || profileResource.status == Resource.Status.ERROR) {
@ -449,7 +468,7 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
context?.let { ctx -> Toast.makeText(ctx, R.string.error_loading_profile, Toast.LENGTH_LONG).show() } context?.let { ctx -> Toast.makeText(ctx, R.string.error_loading_profile, Toast.LENGTH_LONG).show() }
return@observe return@observe
} }
root.loadLayoutDescription(R.xml.header_list_scene) binding.root.loadLayoutDescription(R.xml.header_list_scene)
setupFavChip(profile, currentUser) setupFavChip(profile, currentUser)
setupFavButton(currentUser, profile) setupFavButton(currentUser, profile)
setupSavedButton(currentUser, profile) setupSavedButton(currentUser, profile)
@ -529,7 +548,7 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
binding.privatePage2.visibility = VISIBLE binding.privatePage2.visibility = VISIBLE
binding.postsRecyclerView.visibility = GONE binding.postsRecyclerView.visibility = GONE
binding.swipeRefreshLayout.isRefreshing = false 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>?>) { private fun setupProfileContext(contextPair: Pair<String?, List<UserProfileContextLink>?>) {
@ -709,14 +728,14 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
binding.header.btnLiked.setOnClickListener { binding.header.btnLiked.setOnClickListener {
try { try {
val action = ProfileFragmentDirections.actionProfileFragmentToSavedViewerFragment( val action = ProfileFragmentDirections.actionToSaved(
viewModel.profile.value?.data?.username ?: return@setOnClickListener, viewModel.profile.value?.data?.username ?: return@setOnClickListener,
viewModel.profile.value?.data?.pk ?: return@setOnClickListener, viewModel.profile.value?.data?.pk ?: return@setOnClickListener,
PostItemType.LIKED PostItemType.LIKED
) )
NavHostFragment.findNavController(this).navigate(action) findNavController().navigate(action)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "setupTaggedButton: ", e) Log.e(TAG, "setupLikedButton: ", e)
} }
} }
} }
@ -730,12 +749,12 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
binding.header.btnTagged.setOnClickListener { binding.header.btnTagged.setOnClickListener {
try { try {
val action = ProfileFragmentDirections.actionProfileFragmentToSavedViewerFragment( val action = ProfileFragmentDirections.actionToSaved(
viewModel.profile.value?.data?.username ?: return@setOnClickListener, viewModel.profile.value?.data?.username ?: return@setOnClickListener,
viewModel.profile.value?.data?.pk ?: return@setOnClickListener, viewModel.profile.value?.data?.pk ?: return@setOnClickListener,
PostItemType.TAGGED PostItemType.TAGGED
) )
NavHostFragment.findNavController(this).navigate(action) findNavController().navigate(action)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "setupTaggedButton: ", e) Log.e(TAG, "setupTaggedButton: ", e)
} }
@ -751,8 +770,8 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
binding.header.btnSaved.setOnClickListener { binding.header.btnSaved.setOnClickListener {
try { try {
val action = ProfileFragmentDirections.actionGlobalSavedCollectionsFragment(false) val action = ProfileFragmentDirections.actionToSavedCollections().apply { isSaving = false }
NavHostFragment.findNavController(this).navigate(action) findNavController().navigate(action)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "setupSavedButton: ", e) Log.e(TAG, "setupSavedButton: ", e)
} }
@ -833,7 +852,7 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
} }
private fun showDefaultMessage() { 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.privatePage1.visibility = View.VISIBLE
binding.privatePage2.visibility = View.VISIBLE binding.privatePage2.visibility = View.VISIBLE
binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24) binding.privatePage1.setImageResource(R.drawable.ic_outline_info_24)
@ -843,9 +862,8 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
private fun setupHighlights() { private fun setupHighlights() {
val context = context ?: return val context = context ?: return
highlightsAdapter = HighlightsAdapter { model, position -> highlightsAdapter = HighlightsAdapter { model, position ->
val options = StoryViewerOptions.forHighlight(model.user!!.pk, "") val options = StoryViewerOptions.forHighlight(model.user!!.pk, "").apply { currentFeedStoryIndex = position }
options.currentFeedStoryIndex = position val action = ProfileFragmentDirections.actionToStory(options)
val action = ProfileFragmentDirections.actionProfileFragmentToStoryViewerFragment(options)
NavHostFragment.findNavController(this).navigate(action) NavHostFragment.findNavController(this).navigate(action)
} }
binding.header.highlightsList.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) binding.header.highlightsList.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
@ -869,7 +887,7 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy) super.onScrolled(recyclerView, dx, dy)
val canScrollVertically = recyclerView.canScrollVertically(-1) val canScrollVertically = recyclerView.canScrollVertically(-1)
root.getTransition(R.id.transition)?.setEnable(!canScrollVertically) binding.root.getTransition(R.id.transition)?.setEnable(!canScrollVertically)
} }
}) })
setupPostsDone = true setupPostsDone = true
@ -877,10 +895,9 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
private fun navigateToProfile(username: String?) { private fun navigateToProfile(username: String?) {
try { try {
val bundle = Bundle() val username1 = username ?: return
bundle.putString("username", username ?: return) val actionToProfile = ProfileFragmentDirections.actionToProfile().apply { this.username = username1 }
val navController = NavHostFragment.findNavController(this) findNavController().navigate(actionToProfile)
navController.navigate(R.id.action_global_profileFragment, bundle)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "navigateToProfile: ", e) Log.e(TAG, "navigateToProfile: ", e)
} }
@ -933,13 +950,13 @@ class ProfileFragment : Fragment(), OnRefreshListener, ConfirmDialogFragmentCall
"profile_pic" -> showProfilePicDialog() "profile_pic" -> showProfilePicDialog()
"show_stories" -> { "show_stories" -> {
try { try {
val action = ProfileFragmentDirections.actionProfileFragmentToStoryViewerFragment( val action = ProfileFragmentDirections.actionToStory(
StoryViewerOptions.forUser( StoryViewerOptions.forUser(
viewModel.profile.value?.data?.pk ?: return, viewModel.profile.value?.data?.pk ?: return,
viewModel.profile.value?.data?.username ?: return, viewModel.profile.value?.data?.username ?: return,
) )
) )
NavHostFragment.findNavController(this).navigate(action) findNavController().navigate(action)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "omPpOptionSelect: ", e) Log.e(TAG, "omPpOptionSelect: ", e)
} }

View File

@ -19,7 +19,7 @@ import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer; import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController; import androidx.navigation.NavDirections;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
@ -125,7 +125,9 @@ public class SearchFragment extends Fragment implements SearchCategoryFragment.O
mainActivity.showSearchView(); mainActivity.showSearchView();
} }
if (settingsHelper.getBoolean(PREF_SEARCH_FOCUS_KEYBOARD)) { if (settingsHelper.getBoolean(PREF_SEARCH_FOCUS_KEYBOARD)) {
if (searchInput != null) {
searchInput.requestFocus(); searchInput.requestFocus();
}
final InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE); final InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) imm.showSoftInput(searchInput, InputMethodManager.SHOW_IMPLICIT); if (imm != null) imm.showSoftInput(searchInput, InputMethodManager.SHOW_IMPLICIT);
} }
@ -205,24 +207,23 @@ public class SearchFragment extends Fragment implements SearchCategoryFragment.O
if (!searchItem.isFavorite()) { if (!searchItem.isFavorite()) {
viewModel.saveToRecentSearches(searchItem); // insert or update recent viewModel.saveToRecentSearches(searchItem); // insert or update recent
} }
final NavController navController = NavHostFragment.findNavController(this); final NavDirections action;
final Bundle bundle = new Bundle();
switch (type) { switch (type) {
case USER: case USER:
bundle.putString("username", searchItem.getUser().getUsername()); action = SearchFragmentDirections.actionToProfile().setUsername(searchItem.getUser().getUsername());
navController.navigate(R.id.action_global_profileFragment, bundle); NavHostFragment.findNavController(this).navigate(action);
break; break;
case HASHTAG: case HASHTAG:
bundle.putString("hashtag", searchItem.getHashtag().getName()); action = SearchFragmentDirections.actionToHashtag(searchItem.getHashtag().getName());
navController.navigate(R.id.action_global_hashTagFragment, bundle); NavHostFragment.findNavController(this).navigate(action);
break; break;
case LOCATION: case LOCATION:
bundle.putLong("locationId", searchItem.getPlace().getLocation().getPk()); action = SearchFragmentDirections.actionToLocation(searchItem.getPlace().getLocation().getPk());
navController.navigate(R.id.action_global_locationFragment, bundle);
break; break;
default: default:
break; return;
} }
NavHostFragment.findNavController(this).navigate(action);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "onSearchItemClick: ", e); Log.e(TAG, "onSearchItemClick: ", e);
} }

View File

@ -1,5 +1,7 @@
package awais.instagrabber.fragments.settings; package awais.instagrabber.fragments.settings;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
@ -79,9 +81,17 @@ public class DownloadsPreferencesFragment extends BasePreferencesFragment {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialUri != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && initialUri != null) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri); intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialUri);
} }
try {
startActivityForResult(intent, SELECT_DIR_REQUEST_CODE); startActivityForResult(intent, SELECT_DIR_REQUEST_CODE);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "openDirectoryChooser: ", e);
showErrorDialog(getString(R.string.no_directory_picker_activity));
} catch (Exception e) {
Log.e(TAG, "openDirectoryChooser: ", e);
}
} }
@SuppressLint("StringFormatInvalid")
@Override @Override
public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) { public void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
if (requestCode != SELECT_DIR_REQUEST_CODE) return; if (requestCode != SELECT_DIR_REQUEST_CODE) return;
@ -105,17 +115,9 @@ public class DownloadsPreferencesFragment extends BasePreferencesFragment {
try (final StringWriter sw = new StringWriter(); try (final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw)) { final PrintWriter pw = new PrintWriter(sw)) {
e.printStackTrace(pw); e.printStackTrace(pw);
final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance( showErrorDialog("com.android.externalstorage.documents".equals(data.getData().getAuthority())
123,
R.string.error,
"com.android.externalstorage.documents".equals(data.getData().getAuthority())
? "Please report this error to the developers:\n\n" + sw.toString() ? "Please report this error to the developers:\n\n" + sw.toString()
: getString(R.string.dir_select_no_download_folder, data.getData().getAuthority()), : getString(R.string.dir_select_no_download_folder, data.getData().getAuthority()));
R.string.ok,
0,
0
);
dialogFragment.show(getChildFragmentManager(), ConfirmDialogFragment.class.getSimpleName());
} catch (IOException ioException) { } catch (IOException ioException) {
Log.e(TAG, "onActivityResult: ", ioException); Log.e(TAG, "onActivityResult: ", ioException);
} }
@ -123,6 +125,18 @@ public class DownloadsPreferencesFragment extends BasePreferencesFragment {
}, 500); }, 500);
} }
private void showErrorDialog(final String message) {
final ConfirmDialogFragment dialogFragment = ConfirmDialogFragment.newInstance(
123,
R.string.error,
message,
R.string.ok,
0,
0
);
dialogFragment.show(getChildFragmentManager(), ConfirmDialogFragment.class.getSimpleName());
}
private Preference getPrependUsernameToFilenamePreference(@NonNull final Context context) { private Preference getPrependUsernameToFilenamePreference(@NonNull final Context context) {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
preference.setKey(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME); preference.setKey(PreferenceKeys.DOWNLOAD_PREPEND_USER_NAME);

View File

@ -1,7 +1,6 @@
package awais.instagrabber.fragments.settings; package awais.instagrabber.fragments.settings;
import android.content.Context; import android.content.Context;
import android.util.Pair;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.ListPreference; import androidx.preference.ListPreference;
@ -17,8 +16,9 @@ import awais.instagrabber.dialogs.TabOrderPreferenceDialogFragment;
import awais.instagrabber.models.Tab; import awais.instagrabber.models.Tab;
import awais.instagrabber.utils.Constants; import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.NavigationHelperKt;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import kotlin.Pair;
import static awais.instagrabber.utils.Utils.settingsHelper; import static awais.instagrabber.utils.Utils.settingsHelper;
@ -34,30 +34,32 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment implemen
screen.addPreference(getDefaultTabPreference(context)); screen.addPreference(getDefaultTabPreference(context));
screen.addPreference(getTabOrderPreference(context)); screen.addPreference(getTabOrderPreference(context));
} }
screen.addPreference(getDisableScreenTransitionsPreference(context));
screen.addPreference(getUpdateCheckPreference(context)); screen.addPreference(getUpdateCheckPreference(context));
screen.addPreference(getFlagSecurePreference(context)); screen.addPreference(getFlagSecurePreference(context));
screen.addPreference(getSearchFocusPreference(context)); screen.addPreference(getSearchFocusPreference(context));
final List<Preference> preferences = FlavorSettings.getInstance() final List<Preference> preferences = FlavorSettings
.getPreferences(context, .getInstance()
.getPreferences(
context,
getChildFragmentManager(), getChildFragmentManager(),
SettingCategory.GENERAL); SettingCategory.GENERAL
if (preferences != null) { );
for (final Preference preference : preferences) { for (final Preference preference : preferences) {
screen.addPreference(preference); screen.addPreference(preference);
} }
} }
}
private Preference getDefaultTabPreference(@NonNull final Context context) { private Preference getDefaultTabPreference(@NonNull final Context context) {
final ListPreference preference = new ListPreference(context); final ListPreference preference = new ListPreference(context);
preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance()); preference.setSummaryProvider(ListPreference.SimpleSummaryProvider.getInstance());
final Pair<List<Tab>, List<Tab>> listPair = Utils.getNavTabList(context); final Pair<List<Tab>, List<Tab>> listPair = NavigationHelperKt.getLoggedInNavTabs(context);
final List<Tab> tabs = listPair.first; final List<Tab> tabs = listPair.getFirst();
final String[] titles = tabs.stream() final String[] titles = tabs.stream()
.map(Tab::getTitle) .map(Tab::getTitle)
.toArray(String[]::new); .toArray(String[]::new);
final String[] navGraphFileNames = tabs.stream() final String[] navGraphFileNames = tabs.stream()
.map(Tab::getGraphName) .map(tab -> NavigationHelperKt.getNavGraphNameForNavRootId(tab.getNavigationRootId()))
.toArray(String[]::new); .toArray(String[]::new);
preference.setKey(Constants.DEFAULT_TAB); preference.setKey(Constants.DEFAULT_TAB);
preference.setTitle(R.string.pref_start_screen); preference.setTitle(R.string.pref_start_screen);
@ -81,6 +83,14 @@ public class GeneralPreferencesFragment extends BasePreferencesFragment implemen
return preference; return preference;
} }
private Preference getDisableScreenTransitionsPreference(@NonNull final Context context) {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
preference.setKey(PreferenceKeys.PREF_DISABLE_SCREEN_TRANSITIONS);
preference.setTitle(R.string.disable_screen_transitions);
preference.setIconSpaceReserved(false);
return preference;
}
private Preference getUpdateCheckPreference(@NonNull final Context context) { private Preference getUpdateCheckPreference(@NonNull final Context context) {
final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context); final SwitchPreferenceCompat preference = new SwitchPreferenceCompat(context);
preference.setKey(PreferenceKeys.CHECK_UPDATES); preference.setKey(PreferenceKeys.CHECK_UPDATES);

View File

@ -35,6 +35,7 @@ import awais.instagrabber.utils.Constants;
import awais.instagrabber.utils.CookieUtils; import awais.instagrabber.utils.CookieUtils;
import awais.instagrabber.utils.CoroutineUtilsKt; import awais.instagrabber.utils.CoroutineUtilsKt;
import awais.instagrabber.utils.FlavorTown; import awais.instagrabber.utils.FlavorTown;
import awais.instagrabber.utils.NavigationHelperKt;
import awais.instagrabber.utils.ProcessPhoenix; import awais.instagrabber.utils.ProcessPhoenix;
import awais.instagrabber.utils.TextUtils; import awais.instagrabber.utils.TextUtils;
import awais.instagrabber.utils.Utils; import awais.instagrabber.utils.Utils;
@ -174,14 +175,18 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
boolean showActivity = true; boolean showActivity = true;
boolean showExplore = false; boolean showExplore = false;
if (activity != null) { if (activity != null) {
showActivity = !Utils.isNavRootInCurrentTabs("notification_viewer_nav_graph"); showActivity = !NavigationHelperKt.isNavRootInCurrentTabs("notification_viewer_nav_graph");
showExplore = !Utils.isNavRootInCurrentTabs("discover_nav_graph"); showExplore = !NavigationHelperKt.isNavRootInCurrentTabs("discover_nav_graph");
} }
if (showActivity) { if (showActivity) {
screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> { screen.addPreference(getPreference(R.string.action_notif, R.drawable.ic_not_liked, preference -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("notif"); try {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionToNotifications().setType("notif");
navController.navigate(navDirections); navController.navigate(navDirections);
} catch (Exception e) {
Log.e(TAG, "setupPreferenceScreen: ", e);
}
} }
return true; return true;
})); }));
@ -197,15 +202,23 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> { screen.addPreference(getPreference(R.string.action_ayml, R.drawable.ic_suggested_users, preference -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalNotificationsViewerFragment("ayml"); try {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionToNotifications().setType("ayml");
navController.navigate(navDirections); navController.navigate(navDirections);
} catch (Exception e) {
Log.e(TAG, "setupPreferenceScreen: ", e);
}
} }
return true; return true;
})); }));
screen.addPreference(getPreference(R.string.action_archive, R.drawable.ic_archive, preference -> { screen.addPreference(getPreference(R.string.action_archive, R.drawable.ic_archive, preference -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionGlobalStoryListViewerFragment("archive"); try {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionToStoryList("archive");
navController.navigate(navDirections); navController.navigate(navDirections);
} catch (Exception e) {
Log.e(TAG, "setupPreferenceScreen: ", e);
}
} }
return true; return true;
})); }));
@ -214,13 +227,17 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
// Check if favorites has been added as a tab. And if so, do not add in this list // Check if favorites has been added as a tab. And if so, do not add in this list
boolean showFavorites = true; boolean showFavorites = true;
if (activity != null) { if (activity != null) {
showFavorites = !Utils.isNavRootInCurrentTabs("favorites_nav_graph"); showFavorites = !NavigationHelperKt.isNavRootInCurrentTabs("favorites_nav_graph");
} }
if (showFavorites) { if (showFavorites) {
screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> { screen.addPreference(getPreference(R.string.title_favorites, R.drawable.ic_star_24, preference -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToFavoritesFragment(); try {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionToFavorites();
navController.navigate(navDirections); navController.navigate(navDirections);
} catch (Exception e) {
Log.e(TAG, "setupPreferenceScreen: ", e);
}
} }
return true; return true;
})); }));
@ -229,28 +246,41 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
screen.addPreference(getDivider(context)); screen.addPreference(getDivider(context));
screen.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> { screen.addPreference(getPreference(R.string.action_settings, R.drawable.ic_outline_settings_24, preference -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToSettingsPreferencesFragment(); try {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionToSettings();
navController.navigate(navDirections); navController.navigate(navDirections);
} catch (Exception e) {
Log.e(TAG, "setupPreferenceScreen: ", e);
}
} }
return true; return true;
})); }));
screen.addPreference(getPreference(R.string.backup_and_restore, R.drawable.ic_settings_backup_restore_24, preference -> { screen.addPreference(getPreference(R.string.backup_and_restore, R.drawable.ic_settings_backup_restore_24, preference -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToBackupPreferencesFragment(); try {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionToBackup();
navController.navigate(navDirections); navController.navigate(navDirections);
} catch (Exception e) {
Log.e(TAG, "setupPreferenceScreen: ", e);
}
} }
return true; return true;
})); }));
screen.addPreference(getPreference(R.string.action_about, R.drawable.ic_outline_info_24, preference1 -> { screen.addPreference(getPreference(R.string.action_about, R.drawable.ic_outline_info_24, preference1 -> {
if (isSafeToNavigate(navController)) { if (isSafeToNavigate(navController)) {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionMorePreferencesFragmentToAboutFragment(); try {
final NavDirections navDirections = MorePreferencesFragmentDirections.actionToAbout();
navController.navigate(navDirections); navController.navigate(navDirections);
} catch (Exception e) {
Log.e(TAG, "setupPreferenceScreen: ", e);
}
} }
return true; return true;
})); }));
screen.addPreference(getDivider(context)); screen.addPreference(getDivider(context));
screen.addPreference(getPreference(R.string.version, screen.addPreference(getPreference(
R.string.version,
BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")", BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")",
-1, -1,
preference -> { preference -> {
@ -258,7 +288,8 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
if (activity == null) return false; if (activity == null) return false;
FlavorTown.updateCheck(activity, true); FlavorTown.updateCheck(activity, true);
return true; return true;
})); })
);
screen.addPreference(getDivider(context)); screen.addPreference(getDivider(context));
final Preference reminderPreference = getPreference(R.string.reminder, R.string.reminder_summary, R.drawable.ic_warning, null); final Preference reminderPreference = getPreference(R.string.reminder, R.string.reminder_summary, R.drawable.ic_warning, null);
@ -285,7 +316,8 @@ public class MorePreferencesFragment extends BasePreferencesFragment {
// adds cookies to database for quick access // adds cookies to database for quick access
final long uid = CookieUtils.getUserIdFromCookie(cookie); final long uid = CookieUtils.getUserIdFromCookie(cookie);
final UserRepository userRepository = UserRepository.Companion.getInstance(); final UserRepository userRepository = UserRepository.Companion.getInstance();
userRepository.getUserInfo(uid, CoroutineUtilsKt.getContinuation((user, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> { userRepository
.getUserInfo(uid, CoroutineUtilsKt.getContinuation((user, throwable) -> AppExecutors.INSTANCE.getMainThread().execute(() -> {
if (throwable != null) { if (throwable != null) {
Log.e(TAG, "Error fetching user info", throwable); Log.e(TAG, "Error fetching user info", throwable);
return; return;

View File

@ -11,6 +11,7 @@ object PreferenceKeys {
const val PREF_SHOWN_COUNT_TOOLTIP = "shown_count_tooltip" const val PREF_SHOWN_COUNT_TOOLTIP = "shown_count_tooltip"
const val PREF_SEARCH_FOCUS_KEYBOARD = "search_focus_keyboard" const val PREF_SEARCH_FOCUS_KEYBOARD = "search_focus_keyboard"
const val PREF_AUTO_BACKUP_ENABLED = "auto_backup_enabled" const val PREF_AUTO_BACKUP_ENABLED = "auto_backup_enabled"
const val PREF_DISABLE_SCREEN_TRANSITIONS = "disable_screen_transitions"
const val PREF_STORY_SHOW_LIST = "story_show_list" const val PREF_STORY_SHOW_LIST = "story_show_list"
// string prefs // string prefs

View File

@ -9,12 +9,6 @@ data class Tab(
val title: String, val title: String,
val isRemovable: Boolean, val isRemovable: Boolean,
/**
* This is name part of the navigation resource
* eg: @navigation/ **graphName**
*/
val graphName: String,
/** /**
* This is the actual resource id of the navigation resource (R.navigation.graphName = navigationResId) * This is the actual resource id of the navigation resource (R.navigation.graphName = navigationResId)
*/ */

View File

@ -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()

View File

@ -90,4 +90,5 @@ object Constants {
const val DM_THREAD_ACTION_EXTRA_THREAD_TITLE = "thread_title" const val DM_THREAD_ACTION_EXTRA_THREAD_TITLE = "thread_title"
const val X_IG_APP_ID = "936619743392459" const val X_IG_APP_ID = "936619743392459"
const val EXTRA_INITIAL_URI = "initial_uri" const val EXTRA_INITIAL_URI = "initial_uri"
const val defaultDateTimeFormat = "hh:mm:ss a 'on' dd-MM-yyyy"
} }

View File

@ -1,6 +1,9 @@
package awais.instagrabber.utils package awais.instagrabber.utils
import android.util.Log
import awais.instagrabber.utils.extensions.TAG
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
object DateUtils { object DateUtils {
@ -14,4 +17,13 @@ object DateUtils {
fun isBeforeOrEqual(localDateTime: LocalDateTime, comparedTo: LocalDateTime): Boolean { fun isBeforeOrEqual(localDateTime: LocalDateTime, comparedTo: LocalDateTime): Boolean {
return localDateTime.isBefore(comparedTo) || localDateTime.isEqual(comparedTo) return localDateTime.isBefore(comparedTo) || localDateTime.isEqual(comparedTo)
} }
@JvmStatic
fun checkFormatterValid(datetimeParser: DateTimeFormatter): Boolean = try {
LocalDateTime.now().format(datetimeParser)
true
} catch (e: Exception) {
Log.e(TAG, "checkFormatterValid: ", e)
false
}
} }

View File

@ -2,8 +2,8 @@ package awais.instagrabber.utils
import awais.instagrabber.repositories.responses.Media import awais.instagrabber.repositories.responses.Media
import java.util.* import java.util.*
import kotlin.collections.ArrayList
class KeywordsFilterUtils(private val keywords: ArrayList<String>) {
// fun filter(caption: String?): Boolean { // fun filter(caption: String?): Boolean {
// if (caption == null) return false // if (caption == null) return false
// if (keywords.isEmpty()) return false // if (keywords.isEmpty()) return false
@ -14,24 +14,17 @@ class KeywordsFilterUtils(private val keywords: ArrayList<String>) {
// return false // return false
// } // }
fun filter(media: Media?): Boolean { private fun containsAnyKeyword(keywords: List<String>, media: Media?): Boolean {
if (media == null) return false if (media == null || keywords.isEmpty()) return false
val (_, text) = media.caption ?: return false val (_, text) = media.caption ?: return false
if (keywords.isEmpty()) return false
val temp = text!!.lowercase(Locale.getDefault()) val temp = text!!.lowercase(Locale.getDefault())
for (s in keywords) { return keywords.any { temp.contains(it) }
if (temp.contains(s)) return true }
}
return false
}
fun filter(media: List<Media>?): List<Media>? { fun filter(keywords: List<String>, media: List<Media>?): List<Media>? {
if (keywords.isEmpty()) return media if (keywords.isEmpty()) return media
if (media == null) return ArrayList() if (media == null) return ArrayList()
val result: MutableList<Media> = ArrayList() val result: MutableList<Media> = ArrayList()
for (m in media) { media.filterNotTo(result) { containsAnyKeyword(keywords, it) }
if (!filter(m)) result.add(m)
}
return result return result
}
} }

View File

@ -1,247 +1,247 @@
package awais.instagrabber.utils; // package awais.instagrabber.utils;
//
import android.annotation.SuppressLint; // import android.annotation.SuppressLint;
import android.content.Intent; // import android.content.Intent;
import android.util.Log; // import android.util.Log;
import android.util.SparseArray; // import android.util.SparseArray;
//
import androidx.annotation.NonNull; // import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment; // import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; // import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; // import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.LiveData; // import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; // import androidx.lifecycle.MutableLiveData;
import androidx.navigation.NavController; // import androidx.navigation.NavController;
import androidx.navigation.NavDestination; // import androidx.navigation.NavDestination;
import androidx.navigation.NavGraph; // import androidx.navigation.NavGraph;
import androidx.navigation.fragment.NavHostFragment; // import androidx.navigation.fragment.NavHostFragment;
//
import com.google.android.material.bottomnavigation.BottomNavigationView; // import com.google.android.material.bottomnavigation.BottomNavigationView;
//
import java.util.List; // import java.util.List;
//
import awais.instagrabber.R; // import awais.instagrabber.R;
import awais.instagrabber.customviews.NavHostFragmentWithDefaultAnimations; // import awais.instagrabber.customviews.NavHostFragmentWithDefaultAnimations;
import awais.instagrabber.fragments.main.FeedFragment; // import awais.instagrabber.fragments.main.FeedFragment;
//
/** // /**
* This is a Java rewrite of <a href="https://github.com/android/architecture-components-samples/blob/master/NavigationAdvancedSample/app/src/main/java/com/example/android/navigationadvancedsample/NavigationExtensions.kt">NavigationExtensions</a> // * This is a Java rewrite of <a href="https://github.com/android/architecture-components-samples/blob/master/NavigationAdvancedSample/app/src/main/java/com/example/android/navigationadvancedsample/NavigationExtensions.kt">NavigationExtensions</a>
* from architecture-components-samples. Some modifications have been done, check git history. // * from architecture-components-samples. Some modifications have been done, check git history.
*/ // */
public class NavigationExtensions { // public class NavigationExtensions {
private static final String TAG = NavigationExtensions.class.getSimpleName(); // private static final String TAG = NavigationExtensions.class.getSimpleName();
private static String selectedItemTag; // private static String selectedItemTag;
private static boolean isOnFirstFragment; // private static boolean isOnFirstFragment;
//
@NonNull // @NonNull
public static LiveData<NavController> setupWithNavController(@NonNull final BottomNavigationView bottomNavigationView, // public static LiveData<NavController> setupWithNavController(@NonNull final BottomNavigationView bottomNavigationView,
@NonNull List<Integer> navGraphIds, // @NonNull List<Integer> navGraphIds,
@NonNull final FragmentManager fragmentManager, // @NonNull final FragmentManager fragmentManager,
final int containerId, // final int containerId,
@NonNull Intent intent, // @NonNull Intent intent,
final int firstFragmentGraphIndex) { // final int firstFragmentGraphIndex) {
final SparseArray<String> graphIdToTagMap = new SparseArray<>(); // final SparseArray<String> graphIdToTagMap = new SparseArray<>();
final MutableLiveData<NavController> selectedNavController = new MutableLiveData<>(); // final MutableLiveData<NavController> selectedNavController = new MutableLiveData<>();
int firstFragmentGraphId = 0; // int firstFragmentGraphId = 0;
for (int i = 0; i < navGraphIds.size(); i++) { // for (int i = 0; i < navGraphIds.size(); i++) {
final int navGraphId = navGraphIds.get(i); // final int navGraphId = navGraphIds.get(i);
final String fragmentTag = getFragmentTag(navGraphId); // final String fragmentTag = getFragmentTag(navGraphId);
final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId); // final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId);
final NavController navController = navHostFragment.getNavController(); // final NavController navController = navHostFragment.getNavController();
final int graphId = navController.getGraph().getId(); // final int graphId = navController.getGraph().getId();
if (i == firstFragmentGraphIndex) { // if (i == firstFragmentGraphIndex) {
firstFragmentGraphId = graphId; // firstFragmentGraphId = graphId;
} // }
graphIdToTagMap.put(graphId, fragmentTag); // graphIdToTagMap.put(graphId, fragmentTag);
if (bottomNavigationView.getSelectedItemId() == graphId) { // if (bottomNavigationView.getSelectedItemId() == graphId) {
selectedNavController.setValue(navHostFragment.getNavController()); // selectedNavController.setValue(navHostFragment.getNavController());
attachNavHostFragment(fragmentManager, navHostFragment, i == firstFragmentGraphIndex); // attachNavHostFragment(fragmentManager, navHostFragment, i == firstFragmentGraphIndex);
} else { // } else {
detachNavHostFragment(fragmentManager, navHostFragment); // detachNavHostFragment(fragmentManager, navHostFragment);
} // }
} // }
selectedItemTag = graphIdToTagMap.get(bottomNavigationView.getSelectedItemId()); // selectedItemTag = graphIdToTagMap.get(bottomNavigationView.getSelectedItemId());
final String firstFragmentTag = graphIdToTagMap.get(firstFragmentGraphId); // final String firstFragmentTag = graphIdToTagMap.get(firstFragmentGraphId);
isOnFirstFragment = selectedItemTag != null && selectedItemTag.equals(firstFragmentTag); // isOnFirstFragment = selectedItemTag != null && selectedItemTag.equals(firstFragmentTag);
bottomNavigationView.setOnItemSelectedListener(item -> { // bottomNavigationView.setOnItemSelectedListener(item -> {
if (fragmentManager.isStateSaved()) { // if (fragmentManager.isStateSaved()) {
return false; // return false;
} // }
String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId()); // String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId());
String tag = selectedItemTag; // String tag = selectedItemTag;
if (tag != null && !tag.equals(newlySelectedItemTag)) { // if (tag != null && !tag.equals(newlySelectedItemTag)) {
fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE); // fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
Fragment fragment = fragmentManager.findFragmentByTag(newlySelectedItemTag); // Fragment fragment = fragmentManager.findFragmentByTag(newlySelectedItemTag);
if (fragment == null) { // if (fragment == null) {
return false; // return false;
// throw new RuntimeException("null cannot be cast to non-null NavHostFragment"); // // throw new RuntimeException("null cannot be cast to non-null NavHostFragment");
} // }
final NavHostFragment selectedFragment = (NavHostFragment) fragment; // final NavHostFragment selectedFragment = (NavHostFragment) fragment;
if (firstFragmentTag != null && !firstFragmentTag.equals(newlySelectedItemTag)) { // if (firstFragmentTag != null && !firstFragmentTag.equals(newlySelectedItemTag)) {
FragmentTransaction fragmentTransaction = fragmentManager // FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction() // .beginTransaction()
.setCustomAnimations( // .setCustomAnimations(
R.anim.nav_default_enter_anim, // R.anim.nav_default_enter_anim,
R.anim.nav_default_exit_anim, // R.anim.nav_default_exit_anim,
R.anim.nav_default_pop_enter_anim, // R.anim.nav_default_pop_enter_anim,
R.anim.nav_default_pop_exit_anim // R.anim.nav_default_pop_exit_anim
) // )
.attach(selectedFragment) // .attach(selectedFragment)
.setPrimaryNavigationFragment(selectedFragment); // .setPrimaryNavigationFragment(selectedFragment);
for (int i = 0; i < graphIdToTagMap.size(); i++) { // for (int i = 0; i < graphIdToTagMap.size(); i++) {
final int key = graphIdToTagMap.keyAt(i); // final int key = graphIdToTagMap.keyAt(i);
final String fragmentTagForId = graphIdToTagMap.get(key); // final String fragmentTagForId = graphIdToTagMap.get(key);
if (!fragmentTagForId.equals(newlySelectedItemTag)) { // if (!fragmentTagForId.equals(newlySelectedItemTag)) {
final Fragment fragmentByTag = fragmentManager.findFragmentByTag(firstFragmentTag); // final Fragment fragmentByTag = fragmentManager.findFragmentByTag(firstFragmentTag);
if (fragmentByTag == null) { // if (fragmentByTag == null) {
continue; // continue;
} // }
fragmentTransaction.detach(fragmentByTag); // fragmentTransaction.detach(fragmentByTag);
} // }
} // }
fragmentTransaction.addToBackStack(firstFragmentTag) // fragmentTransaction.addToBackStack(firstFragmentTag)
.setReorderingAllowed(true) // .setReorderingAllowed(true)
.commit(); // .commit();
} // }
selectedItemTag = newlySelectedItemTag; // selectedItemTag = newlySelectedItemTag;
isOnFirstFragment = selectedItemTag.equals(firstFragmentTag); // isOnFirstFragment = selectedItemTag.equals(firstFragmentTag);
selectedNavController.setValue(selectedFragment.getNavController()); // selectedNavController.setValue(selectedFragment.getNavController());
return true; // return true;
} // }
return false; // return false;
}); // });
setupItemReselected(bottomNavigationView, graphIdToTagMap, fragmentManager); // setupItemReselected(bottomNavigationView, graphIdToTagMap, fragmentManager);
setupDeepLinks(bottomNavigationView, navGraphIds, fragmentManager, containerId, intent); // setupDeepLinks(bottomNavigationView, navGraphIds, fragmentManager, containerId, intent);
final int finalFirstFragmentGraphId = firstFragmentGraphId; // final int finalFirstFragmentGraphId = firstFragmentGraphId;
fragmentManager.addOnBackStackChangedListener(() -> { // fragmentManager.addOnBackStackChangedListener(() -> {
if (!isOnFirstFragment) { // if (!isOnFirstFragment) {
if (firstFragmentTag == null) { // if (firstFragmentTag == null) {
return; // return;
} // }
if (!isOnBackStack(fragmentManager, firstFragmentTag)) { // if (!isOnBackStack(fragmentManager, firstFragmentTag)) {
bottomNavigationView.setSelectedItemId(finalFirstFragmentGraphId); // bottomNavigationView.setSelectedItemId(finalFirstFragmentGraphId);
} // }
} // }
//
final NavController navController = selectedNavController.getValue(); // final NavController navController = selectedNavController.getValue();
if (navController != null && navController.getCurrentDestination() == null) { // if (navController != null && navController.getCurrentDestination() == null) {
final NavGraph navControllerGraph = navController.getGraph(); // final NavGraph navControllerGraph = navController.getGraph();
navController.navigate(navControllerGraph.getId()); // navController.navigate(navControllerGraph.getId());
} // }
}); // });
return selectedNavController; // return selectedNavController;
} // }
//
private static NavHostFragment obtainNavHostFragment(final FragmentManager fragmentManager, // private static NavHostFragment obtainNavHostFragment(final FragmentManager fragmentManager,
final String fragmentTag, // final String fragmentTag,
final int navGraphId, // final int navGraphId,
final int containerId) { // final int containerId) {
final NavHostFragment existingFragment = (NavHostFragment) fragmentManager.findFragmentByTag(fragmentTag); // final NavHostFragment existingFragment = (NavHostFragment) fragmentManager.findFragmentByTag(fragmentTag);
if (existingFragment != null) { // if (existingFragment != null) {
return existingFragment; // return existingFragment;
} // }
final NavHostFragment navHostFragment = NavHostFragmentWithDefaultAnimations.create(navGraphId); // final NavHostFragment navHostFragment = NavHostFragmentWithDefaultAnimations.create(navGraphId);
fragmentManager.beginTransaction() // fragmentManager.beginTransaction()
.setReorderingAllowed(true) // .setReorderingAllowed(true)
.add(containerId, navHostFragment, fragmentTag) // .add(containerId, navHostFragment, fragmentTag)
.commitNow(); // .commitNow();
return navHostFragment; // return navHostFragment;
} // }
//
private static void attachNavHostFragment(final FragmentManager fragmentManager, // private static void attachNavHostFragment(final FragmentManager fragmentManager,
final NavHostFragment navHostFragment, // final NavHostFragment navHostFragment,
final boolean isPrimaryNavFragment) { // final boolean isPrimaryNavFragment) {
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction() // final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
.attach(navHostFragment); // .attach(navHostFragment);
if (isPrimaryNavFragment) { // if (isPrimaryNavFragment) {
fragmentTransaction.setPrimaryNavigationFragment(navHostFragment); // fragmentTransaction.setPrimaryNavigationFragment(navHostFragment);
} // }
fragmentTransaction.commitNow(); // fragmentTransaction.commitNow();
} // }
//
private static void detachNavHostFragment(final FragmentManager fragmentManager, final NavHostFragment navHostFragment) { // private static void detachNavHostFragment(final FragmentManager fragmentManager, final NavHostFragment navHostFragment) {
fragmentManager.beginTransaction() // fragmentManager.beginTransaction()
.detach(navHostFragment) // .detach(navHostFragment)
.commitNow(); // .commitNow();
} // }
//
@SuppressLint("RestrictedApi") // @SuppressLint("RestrictedApi")
private static void setupItemReselected(final BottomNavigationView bottomNavigationView, // private static void setupItemReselected(final BottomNavigationView bottomNavigationView,
final SparseArray<String> graphIdToTagMap, // final SparseArray<String> graphIdToTagMap,
final FragmentManager fragmentManager) { // final FragmentManager fragmentManager) {
bottomNavigationView.setOnItemReselectedListener(item -> { // bottomNavigationView.setOnItemReselectedListener(item -> {
final String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId()); // final String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId());
final Fragment fragmentByTag = fragmentManager.findFragmentByTag(newlySelectedItemTag); // final Fragment fragmentByTag = fragmentManager.findFragmentByTag(newlySelectedItemTag);
if (fragmentByTag == null) { // if (fragmentByTag == null) {
return; // return;
} // }
final NavHostFragment selectedFragment = (NavHostFragment) fragmentByTag; // final NavHostFragment selectedFragment = (NavHostFragment) fragmentByTag;
final NavController navController = selectedFragment.getNavController(); // final NavController navController = selectedFragment.getNavController();
final NavGraph navControllerGraph = navController.getGraph(); // final NavGraph navControllerGraph = navController.getGraph();
final NavDestination currentDestination = navController.getCurrentDestination(); // final NavDestination currentDestination = navController.getCurrentDestination();
final int startDestination = navControllerGraph.getStartDestination(); // final int startDestination = navControllerGraph.getStartDestination();
int backStackSize = navController.getBackStack().size(); // int backStackSize = navController.getBackStack().size();
if (currentDestination != null && backStackSize == 2 && currentDestination.getId() == startDestination) { // if (currentDestination != null && backStackSize == 2 && currentDestination.getId() == startDestination) {
// scroll to top // // scroll to top
final List<Fragment> fragments = selectedFragment.getChildFragmentManager().getFragments(); // final List<Fragment> fragments = selectedFragment.getChildFragmentManager().getFragments();
if (fragments.isEmpty()) return; // if (fragments.isEmpty()) return;
final Fragment fragment = fragments.get(0); // final Fragment fragment = fragments.get(0);
if (fragment instanceof FeedFragment) { // if (fragment instanceof FeedFragment) {
((FeedFragment) fragment).scrollToTop(); // ((FeedFragment) fragment).scrollToTop();
} // }
return; // return;
} // }
final boolean popped = navController.popBackStack(startDestination, false); // final boolean popped = navController.popBackStack(startDestination, false);
backStackSize = navController.getBackStack().size(); // backStackSize = navController.getBackStack().size();
if (!popped || backStackSize > 2) { // if (!popped || backStackSize > 2) {
try { // try {
// try loop pop // // try loop pop
do { // do {
navController.popBackStack(); // navController.popBackStack();
backStackSize = navController.getBackStack().size(); // backStackSize = navController.getBackStack().size();
} while (backStackSize > 2); // } while (backStackSize > 2);
} catch (Exception e) { // } catch (Exception e) {
Log.e(TAG, "setupItemReselected: ", e); // Log.e(TAG, "setupItemReselected: ", e);
} // }
} // }
}); // });
} // }
//
private static void setupDeepLinks(final BottomNavigationView bottomNavigationView, // private static void setupDeepLinks(final BottomNavigationView bottomNavigationView,
final List<Integer> navGraphIds, // final List<Integer> navGraphIds,
final FragmentManager fragmentManager, // final FragmentManager fragmentManager,
final int containerId, // final int containerId,
final Intent intent) { // final Intent intent) {
for (int i = 0; i < navGraphIds.size(); i++) { // for (int i = 0; i < navGraphIds.size(); i++) {
final int navGraphId = navGraphIds.get(i); // final int navGraphId = navGraphIds.get(i);
final String fragmentTag = getFragmentTag(navGraphId); // final String fragmentTag = getFragmentTag(navGraphId);
final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId); // final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId);
if (navHostFragment.getNavController().handleDeepLink(intent)) { // if (navHostFragment.getNavController().handleDeepLink(intent)) {
final int selectedItemId = bottomNavigationView.getSelectedItemId(); // final int selectedItemId = bottomNavigationView.getSelectedItemId();
NavController navController = navHostFragment.getNavController(); // NavController navController = navHostFragment.getNavController();
NavGraph graph = navController.getGraph(); // NavGraph graph = navController.getGraph();
if (selectedItemId != graph.getId()) { // if (selectedItemId != graph.getId()) {
navController = navHostFragment.getNavController(); // navController = navHostFragment.getNavController();
graph = navController.getGraph(); // graph = navController.getGraph();
bottomNavigationView.setSelectedItemId(graph.getId()); // bottomNavigationView.setSelectedItemId(graph.getId());
} // }
} // }
} // }
} // }
//
private static boolean isOnBackStack(final FragmentManager fragmentManager, final String backStackName) { // private static boolean isOnBackStack(final FragmentManager fragmentManager, final String backStackName) {
int backStackCount = fragmentManager.getBackStackEntryCount(); // int backStackCount = fragmentManager.getBackStackEntryCount();
for (int i = 0; i < backStackCount; i++) { // for (int i = 0; i < backStackCount; i++) {
final FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i); // final FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i);
final String name = backStackEntry.getName(); // final String name = backStackEntry.getName();
if (name != null && name.equals(backStackName)) { // if (name != null && name.equals(backStackName)) {
return true; // return true;
} // }
} // }
return false; // return false;
} // }
//
private static String getFragmentTag(final int index) { // private static String getFragmentTag(final int index) {
return "bottomNavigation#" + index; // return "bottomNavigation#" + index;
} // }
} // }

View File

@ -0,0 +1,174 @@
package awais.instagrabber.utils
import android.content.Context
import android.content.res.Resources
import androidx.annotation.ArrayRes
import awais.instagrabber.R
import awais.instagrabber.fragments.settings.PreferenceKeys
import awais.instagrabber.models.Tab
var tabOrderString: String? = null
private val NON_REMOVABLE_NAV_ROOT_IDS: List<Int> = listOf(R.id.profile_nav_graph, R.id.more_nav_graph)
fun getLoggedInNavTabs(context: Context): Pair<List<Tab>, List<Tab>> {
val navRootIds = getArrayResIds(context.resources, R.array.logged_in_nav_root_ids)
return getTabs(context, navRootIds)
}
fun getAnonNavTabs(context: Context): List<Tab> {
val navRootIds = getArrayResIds(context.resources, R.array.anon_nav_root_ids)
val (tabs, _) = getTabs(context, navRootIds, true)
return tabs
}
private fun getTabs(
context: Context,
navRootIds: IntArray,
isAnon: Boolean = false,
): Pair<List<Tab>, MutableList<Tab>> {
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)
val (orderedGraphNames, orderedNavRootIds) = if (isAnon) navGraphNames to navRootIds.toList() else getOrderedNavRootIdsFromPref(navGraphNames)
val tabs = mutableListOf<Tab>()
val otherTabs = mutableListOf<Tab>() // Will contain tabs not in current list
for (i in navRootIds.indices) {
val navRootId = navRootIds[i]
val tab = Tab(
iconIds[i],
context.getString(titleArray[i]),
if (isAnon) false else !NON_REMOVABLE_NAV_ROOT_IDS.contains(navRootId),
navGraphResIds[i],
navRootId,
startDestFragIds[i]
)
if (!isAnon && !orderedGraphNames.contains(navGraphNames[i])) {
otherTabs.add(tab)
continue
}
tabs.add(tab)
}
val associateBy = tabs.associateBy { it.navigationRootId }
val orderedTabs = orderedNavRootIds.mapNotNull { associateBy[it] }
return orderedTabs to otherTabs
}
private fun getArrayResIds(resources: Resources, @ArrayRes arrayRes: Int): IntArray {
val typedArray = resources.obtainTypedArray(arrayRes)
val length = typedArray.length()
val navRootIds = IntArray(length)
for (i in 0 until length) {
val resourceId = typedArray.getResourceId(i, 0)
if (resourceId == 0) continue
navRootIds[i] = resourceId
}
typedArray.recycle()
return navRootIds
}
private fun <T> getResIdsForNavRootIds(navRootIds: IntArray, resMapper: Function1<Int, T>): List<T> = navRootIds
.asSequence()
.filterNot { it == 0 }
.map(resMapper)
.filterNot { it == 0 }
.toList()
private fun getTitleResIdForNavRootId(id: Int): Int = when (id) {
R.id.direct_messages_nav_graph -> R.string.title_dm
R.id.feed_nav_graph -> R.string.feed
R.id.profile_nav_graph -> R.string.profile
R.id.discover_nav_graph -> R.string.title_discover
R.id.more_nav_graph -> R.string.more
R.id.favorites_nav_graph -> R.string.title_favorites
R.id.notification_viewer_nav_graph -> R.string.title_notifications
else -> 0
}
private fun getIconResIdForNavRootId(id: Int): Int = when (id) {
R.id.direct_messages_nav_graph -> R.drawable.ic_message_24
R.id.feed_nav_graph -> R.drawable.ic_home_24
R.id.profile_nav_graph -> R.drawable.ic_person_24
R.id.discover_nav_graph -> R.drawable.ic_explore_24
R.id.more_nav_graph -> R.drawable.ic_more_horiz_24
R.id.favorites_nav_graph -> R.drawable.ic_star_24
R.id.notification_viewer_nav_graph -> R.drawable.ic_not_liked
else -> 0
}
private fun getStartDestFragIdForNavRootId(id: Int): Int = when (id) {
R.id.direct_messages_nav_graph -> R.id.directMessagesInboxFragment
R.id.feed_nav_graph -> R.id.feedFragment
R.id.profile_nav_graph -> R.id.profileFragment
R.id.discover_nav_graph -> R.id.discoverFragment
R.id.more_nav_graph -> R.id.morePreferencesFragment
R.id.favorites_nav_graph -> R.id.favoritesFragment
R.id.notification_viewer_nav_graph -> R.id.notificationsViewer
else -> 0
}
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"
R.id.discover_nav_graph -> "discover_nav_graph"
R.id.more_nav_graph -> "more_nav_graph"
R.id.favorites_nav_graph -> "favorites_nav_graph"
R.id.notification_viewer_nav_graph -> "notification_viewer_nav_graph"
else -> ""
}
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
"discover_nav_graph" -> R.id.discover_nav_graph
"more_nav_graph" -> R.id.more_nav_graph
"favorites_nav_graph" -> R.id.favorites_nav_graph
"notification_viewer_nav_graph" -> R.id.notification_viewer_nav_graph
else -> 0
}
private fun getOrderedNavRootIdsFromPref(navGraphNames: List<String>): Pair<List<String>, List<Int>> {
tabOrderString = Utils.settingsHelper.getString(PreferenceKeys.PREF_TAB_ORDER)
if (tabOrderString.isNullOrBlank()) {
// Use top 5 entries for default list
val top5navGraphNames: List<String> = navGraphNames.subList(0, 5)
val newOrderString = top5navGraphNames.joinToString(",")
Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString)
tabOrderString = newOrderString
return top5navGraphNames to top5navGraphNames.map(::getNavRootIdForGraphName)
}
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(",")
.asSequence()
.filter(String::isNotBlank)
.filter(navGraphNames::contains)
.toList()
val graphNames = if (orderGraphNames.isEmpty()) {
// Use top 5 entries for default list
navGraphNames.subList(0, 5)
} else orderGraphNames
return graphNames to graphNames.map(::getNavRootIdForGraphName)
}
fun isNavRootInCurrentTabs(navRootString: String?): Boolean {
val navRoot = navRootString ?: return false
return tabOrderString?.contains(navRoot) ?: false
}

View File

@ -37,7 +37,9 @@ class SettingsHelper(context: Context) {
} }
private fun getStringDefault(@StringSettings key: String): String { private fun getStringDefault(@StringSettings key: String): String {
if (PreferenceKeys.DATE_TIME_FORMAT == key) return "hh:mm:ss a 'on' dd-MM-yyyy" if (PreferenceKeys.DATE_TIME_FORMAT == key) {
return Constants.defaultDateTimeFormat
}
return if (PreferenceKeys.DATE_TIME_SELECTION == key) "0;3;0" else "" return if (PreferenceKeys.DATE_TIME_SELECTION == key) "0;3;0" else ""
} }

View File

@ -11,11 +11,11 @@ import java.util.*
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
object TextUtils { object TextUtils {
lateinit var datetimeParser: DateTimeFormatter var dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern(Constants.defaultDateTimeFormat)
@JvmStatic @JvmStatic
fun isEmpty(charSequence: CharSequence?): Boolean { fun isEmpty(charSequence: CharSequence?): Boolean {
if (charSequence == null || charSequence.length < 1) return true if (charSequence.isNullOrBlank()) return true
if (charSequence is String) { if (charSequence is String) {
var str = charSequence var str = charSequence
if ("" == str || "null" == str || str.isEmpty()) return true if ("" == str || "null" == str || str.isEmpty()) return true
@ -78,7 +78,8 @@ object TextUtils {
@JvmStatic @JvmStatic
fun setFormatter(datetimeParser: DateTimeFormatter) { fun setFormatter(datetimeParser: DateTimeFormatter) {
this.datetimeParser = datetimeParser if (!DateUtils.checkFormatterValid(datetimeParser)) return
this.dateTimeFormatter = datetimeParser
} }
@JvmStatic @JvmStatic
@ -86,11 +87,11 @@ object TextUtils {
return LocalDateTime.ofInstant( return LocalDateTime.ofInstant(
Instant.ofEpochSecond(epochSecond), Instant.ofEpochSecond(epochSecond),
ZoneId.systemDefault() ZoneId.systemDefault()
).format(datetimeParser) ).format(dateTimeFormatter)
} }
@JvmStatic @JvmStatic
fun nowToString(): String { fun nowToString(): String {
return LocalDateTime.now().format(datetimeParser) return LocalDateTime.now().format(dateTimeFormatter)
} }
} }

View File

@ -8,7 +8,6 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
@ -46,8 +45,6 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
import com.google.android.exoplayer2.database.ExoDatabaseProvider; import com.google.android.exoplayer2.database.ExoDatabaseProvider;
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache; import com.google.android.exoplayer2.upstream.cache.SimpleCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import org.json.JSONObject; import org.json.JSONObject;
@ -55,22 +52,15 @@ import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.fragments.settings.PreferenceKeys;
import awais.instagrabber.models.PostsLayoutPreferences; import awais.instagrabber.models.PostsLayoutPreferences;
import awais.instagrabber.models.Tab;
import awais.instagrabber.models.enums.FavoriteType; import awais.instagrabber.models.enums.FavoriteType;
import static awais.instagrabber.fragments.settings.PreferenceKeys.PREF_BARINSTA_DIR_URI;
public final class Utils { public final class Utils {
private static final String TAG = "Utils"; private static final String TAG = "Utils";
private static final int VIDEO_CACHE_MAX_BYTES = 10 * 1024 * 1024; private static final int VIDEO_CACHE_MAX_BYTES = 10 * 1024 * 1024;
@ -85,7 +75,6 @@ public final class Utils {
private static int statusBarHeight; private static int statusBarHeight;
private static int actionBarHeight; private static int actionBarHeight;
public static String cacheDir; public static String cacheDir;
public static String tabOrderString;
private static int defaultStatusBarColor; private static int defaultStatusBarColor;
private static Object[] volumes; private static Object[] volumes;
@ -421,115 +410,6 @@ public final class Utils {
} }
} }
private static final List<Integer> NON_REMOVABLE_NAV_ROOT_IDS = ImmutableList.of(R.id.profile_nav_graph, R.id.more_nav_graph);
@NonNull
public static Pair<List<Tab>, List<Tab>> getNavTabList(@NonNull final Context context) {
final Resources resources = context.getResources();
final String[] titleArray = resources.getStringArray(R.array.main_nav_titles);
TypedArray typedArray = resources.obtainTypedArray(R.array.main_nav_graphs);
int length = typedArray.length();
final String[] navGraphNames = new String[length];
final int[] navigationResIds = new int[length];
for (int i = 0; i < length; i++) {
final int resourceId = typedArray.getResourceId(i, 0);
if (resourceId == 0) continue;
navigationResIds[i] = resourceId;
navGraphNames[i] = resources.getResourceEntryName(resourceId);
}
typedArray.recycle();
typedArray = resources.obtainTypedArray(R.array.main_nav_graph_root_ids);
length = typedArray.length();
final int[] navRootIds = new int[length];
for (int i = 0; i < length; i++) {
final int resourceId = typedArray.getResourceId(i, 0);
if (resourceId == 0) continue;
navRootIds[i] = resourceId;
}
typedArray.recycle();
typedArray = resources.obtainTypedArray(R.array.main_nav_drawables);
length = typedArray.length();
final int[] iconIds = new int[length];
for (int i = 0; i < length; i++) {
final int resourceId = typedArray.getResourceId(i, 0);
if (resourceId == 0) continue;
iconIds[i] = resourceId;
}
typedArray.recycle();
typedArray = resources.obtainTypedArray(R.array.main_nav_start_dest_frag_ids);
length = typedArray.length();
final int[] startDestFragIds = new int[length];
for (int i = 0; i < length; i++) {
final int resourceId = typedArray.getResourceId(i, 0);
if (resourceId == 0) continue;
startDestFragIds[i] = resourceId;
}
typedArray.recycle();
final List<String> currentOrderGraphNames = getCurrentOrderOfGraphNamesFromPref(navGraphNames);
if (titleArray.length != iconIds.length || titleArray.length != navGraphNames.length) {
throw new RuntimeException(String.format("Array lengths don't match!: titleArray%s, navGraphNames: %s, iconIds: %s",
Arrays.toString(titleArray), Arrays.toString(navGraphNames), Arrays.toString(iconIds)));
}
final List<Tab> tabs = new ArrayList<>();
final List<Tab> otherTabs = new ArrayList<>(); // Will contain tabs not in current list
for (int i = 0; i < length; i++) {
final String navGraphName = navGraphNames[i];
final int navRootId = navRootIds[i];
final Tab tab = new Tab(iconIds[i],
titleArray[i],
!NON_REMOVABLE_NAV_ROOT_IDS.contains(navRootId),
navGraphName,
navigationResIds[i],
navRootId,
startDestFragIds[i]);
if (!currentOrderGraphNames.contains(navGraphName)) {
otherTabs.add(tab);
continue;
}
tabs.add(tab);
}
Collections.sort(tabs, Ordering.explicit(currentOrderGraphNames).onResultOf(tab -> {
if (tab == null) return null;
return tab.getGraphName();
}));
return new Pair<>(tabs, otherTabs);
}
@NonNull
private static List<String> getCurrentOrderOfGraphNamesFromPref(@NonNull final String[] navGraphNames) {
tabOrderString = settingsHelper.getString(PreferenceKeys.PREF_TAB_ORDER);
final List<String> navGraphNameList = Arrays.asList(navGraphNames);
if (TextUtils.isEmpty(tabOrderString)) {
// Use top 5 entries for default list
final List<String> top5navGraphNames = navGraphNameList.subList(0, 5);
final String newOrderString = android.text.TextUtils.join(",", top5navGraphNames);
Utils.settingsHelper.putString(PreferenceKeys.PREF_TAB_ORDER, newOrderString);
tabOrderString = newOrderString;
return top5navGraphNames;
}
// Make sure that the list from preference does not contain any invalid values
final List<String> orderGraphNames = Arrays.stream(tabOrderString.split(","))
.filter(s -> !TextUtils.isEmpty(s))
.filter(navGraphNameList::contains)
.collect(Collectors.toList());
if (orderGraphNames.isEmpty()) {
// Use top 5 entries for default list
return navGraphNameList.subList(0, 5);
}
return orderGraphNames;
}
public static boolean isNavRootInCurrentTabs(final String navRootString) {
if (navRootString == null || tabOrderString == null) return false;
return tabOrderString.contains(navRootString);
}
// public static void scanDocumentFile(@NonNull final Context context, // public static void scanDocumentFile(@NonNull final Context context,
// @NonNull final DocumentFile documentFile, // @NonNull final DocumentFile documentFile,
// @NonNull final OnScanCompletedListener callback) { // @NonNull final OnScanCompletedListener callback) {

View File

@ -1,10 +1,6 @@
package awais.instagrabber.viewmodels package awais.instagrabber.viewmodels
import androidx.lifecycle.LiveData import androidx.lifecycle.*
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import awais.instagrabber.models.Resource import awais.instagrabber.models.Resource
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.User
import awais.instagrabber.webservices.FriendshipRepository import awais.instagrabber.webservices.FriendshipRepository

View File

@ -1,19 +1,108 @@
package awais.instagrabber.viewmodels; package awais.instagrabber.viewmodels;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import java.util.ArrayList;
import java.util.List; 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.repositories.responses.Media;
import awais.instagrabber.utils.KeywordsFilterUtilsKt;
import static awais.instagrabber.utils.Utils.settingsHelper;
public class MediaViewModel extends ViewModel { public class MediaViewModel extends ViewModel {
private MutableLiveData<List<Media>> list; private static final String TAG = MediaViewModel.class.getSimpleName();
public MutableLiveData<List<Media>> getList() { private boolean refresh = true;
if (list == null) {
list = new MutableLiveData<>(); 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; 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);
}
}
} }

View File

@ -16,12 +16,9 @@ import awais.instagrabber.repositories.responses.User
import awais.instagrabber.repositories.responses.UserProfileContextLink import awais.instagrabber.repositories.responses.UserProfileContextLink
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
import awais.instagrabber.repositories.responses.stories.Story import awais.instagrabber.repositories.responses.stories.Story
import awais.instagrabber.utils.Constants
import awais.instagrabber.utils.ControlledRunner import awais.instagrabber.utils.ControlledRunner
import awais.instagrabber.utils.Event import awais.instagrabber.utils.Event
import awais.instagrabber.utils.getCsrfTokenFromCookie
import awais.instagrabber.utils.SingleRunner import awais.instagrabber.utils.SingleRunner
import awais.instagrabber.utils.Utils
import awais.instagrabber.utils.extensions.TAG import awais.instagrabber.utils.extensions.TAG
import awais.instagrabber.utils.extensions.isReallyPrivate import awais.instagrabber.utils.extensions.isReallyPrivate
import awais.instagrabber.viewmodels.ProfileFragmentViewModel.ProfileAction.* import awais.instagrabber.viewmodels.ProfileFragmentViewModel.ProfileAction.*
@ -29,30 +26,31 @@ import awais.instagrabber.viewmodels.ProfileFragmentViewModel.ProfileEvent.*
import awais.instagrabber.webservices.* import awais.instagrabber.webservices.*
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.time.LocalDateTime import java.time.LocalDateTime
class ProfileFragmentViewModel( class ProfileFragmentViewModel(
private val state: SavedStateHandle, private val state: SavedStateHandle,
private val favoriteRepository: FavoriteRepository?, private val csrfToken: String?,
private val deviceUuid: String?,
private val userRepository: UserRepository,
private val friendshipRepository: FriendshipRepository,
private val storiesRepository: StoriesRepository,
private val mediaRepository: MediaRepository,
private val graphQLRepository: GraphQLRepository,
private val favoriteRepository: FavoriteRepository,
private val directMessagesRepository: DirectMessagesRepository,
private val messageManager: DirectMessagesManager?, private val messageManager: DirectMessagesManager?,
ioDispatcher: CoroutineDispatcher, ioDispatcher: CoroutineDispatcher,
) : ViewModel() { ) : ViewModel() {
private val cookie: String = Utils.settingsHelper.getString(Constants.COOKIE)
private val csrfToken: String? = getCsrfTokenFromCookie(cookie)
private val deviceUuid: String = Utils.settingsHelper.getString(Constants.DEVICE_UUID)
private val userRepository: UserRepository by lazy { UserRepository.getInstance() }
private val friendshipRepository: FriendshipRepository by lazy { FriendshipRepository.getInstance() }
private val storiesRepository: StoriesRepository by lazy { StoriesRepository.getInstance() }
private val mediaRepository: MediaRepository by lazy { MediaRepository.getInstance() }
private val graphQLRepository: GraphQLRepository by lazy { GraphQLRepository.getInstance() }
private val directMessagesRepository: DirectMessagesRepository by lazy { DirectMessagesRepository.getInstance() }
private val _currentUser = MutableLiveData<Resource<User?>>(Resource.loading(null)) private val _currentUser = MutableLiveData<Resource<User?>>(Resource.loading(null))
private val _isFavorite = MutableLiveData(false) private val _isFavorite = MutableLiveData(false)
private val profileAction = MutableLiveData(INIT) private val profileAction = MutableLiveData(INIT)
private val _eventLiveData = MutableLiveData<Event<ProfileEvent>?>() private val _eventLiveData = MutableLiveData<Event<ProfileEvent>?>()
private var previousUsername: String? = null
enum class ProfileAction { enum class ProfileAction {
INIT, INIT,
REFRESH, REFRESH,
@ -102,8 +100,9 @@ class ProfileFragmentViewModel(
val profile: LiveData<Resource<User?>> = currentUserStateUsernameActionLiveData.switchMap { val profile: LiveData<Resource<User?>> = currentUserStateUsernameActionLiveData.switchMap {
val (currentUserResource, stateUsernameResource, action) = it val (currentUserResource, stateUsernameResource, action) = it
liveData<Resource<User?>>(context = viewModelScope.coroutineContext + ioDispatcher) { liveData<Resource<User?>>(context = viewModelScope.coroutineContext + ioDispatcher) {
if (action == INIT && previousUsername != null && stateUsernameResource.data == previousUsername) return@liveData
if (currentUserResource.status == Resource.Status.LOADING || stateUsernameResource.status == Resource.Status.LOADING) { if (currentUserResource.status == Resource.Status.LOADING || stateUsernameResource.status == Resource.Status.LOADING) {
emit(Resource.loading(null)) emit(Resource.loading(profileCopy.value?.data))
return@liveData return@liveData
} }
val currentUser = currentUserResource.data val currentUser = currentUserResource.data
@ -115,6 +114,7 @@ class ProfileFragmentViewModel(
try { try {
when (action) { when (action) {
INIT, REFRESH -> { INIT, REFRESH -> {
previousUsername = stateUsername
val fetchedUser = profileFetchControlledRunner.cancelPreviousThenRun { fetchUser(currentUser, stateUsername) } val fetchedUser = profileFetchControlledRunner.cancelPreviousThenRun { fetchUser(currentUser, stateUsername) }
emit(Resource.success(fetchedUser)) emit(Resource.success(fetchedUser))
if (fetchedUser != null) { if (fetchedUser != null) {
@ -243,7 +243,7 @@ class ProfileFragmentViewModel(
private suspend fun checkAndUpdateFavorite(fetchedUser: User) { private suspend fun checkAndUpdateFavorite(fetchedUser: User) {
try { try {
val favorite = favoriteRepository!!.getFavorite(fetchedUser.username, FavoriteType.USER) val favorite = favoriteRepository.getFavorite(fetchedUser.username, FavoriteType.USER)
if (favorite == null) { if (favorite == null) {
_isFavorite.postValue(false) _isFavorite.postValue(false)
return return
@ -291,7 +291,7 @@ class ProfileFragmentViewModel(
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
toggleFavoriteControlledRunner.afterPrevious { toggleFavoriteControlledRunner.afterPrevious {
try { try {
val favorite = favoriteRepository!!.getFavorite(username, FavoriteType.USER) val favorite = favoriteRepository.getFavorite(username, FavoriteType.USER)
if (favorite == null) { if (favorite == null) {
// insert // insert
favoriteRepository.insertOrUpdateFavorite( favoriteRepository.insertOrUpdateFavorite(
@ -326,7 +326,7 @@ class ProfileFragmentViewModel(
val currentUserId = currentUser.value?.data?.pk ?: return@afterPrevious val currentUserId = currentUser.value?.data?.pk ?: return@afterPrevious
val targetUserId = profile.value?.data?.pk ?: return@afterPrevious val targetUserId = profile.value?.data?.pk ?: return@afterPrevious
val csrfToken = csrfToken ?: return@afterPrevious val csrfToken = csrfToken ?: return@afterPrevious
val deviceUuid = deviceUuid val deviceUuid = deviceUuid ?: return@afterPrevious
if (following) { if (following) {
if (!confirmed) { if (!confirmed) {
_eventLiveData.postValue(Event(ShowConfirmUnfollowDialog)) _eventLiveData.postValue(Event(ShowConfirmUnfollowDialog))
@ -365,7 +365,7 @@ class ProfileFragmentViewModel(
val currentUserId = currentUser.value?.data?.pk ?: return@afterPrevious val currentUserId = currentUser.value?.data?.pk ?: return@afterPrevious
val targetUserId = profile.value?.data?.pk ?: return@afterPrevious val targetUserId = profile.value?.data?.pk ?: return@afterPrevious
val csrfToken = csrfToken ?: return@afterPrevious val csrfToken = csrfToken ?: return@afterPrevious
val deviceUuid = deviceUuid val deviceUuid = deviceUuid ?: return@afterPrevious
val username = profile.value?.data?.username ?: return@afterPrevious val username = profile.value?.data?.username ?: return@afterPrevious
val thread = directMessagesRepository.createThread( val thread = directMessagesRepository.createThread(
csrfToken, csrfToken,
@ -381,6 +381,7 @@ class ProfileFragmentViewModel(
} }
val threadId = thread.threadId ?: return@afterPrevious val threadId = thread.threadId ?: return@afterPrevious
_eventLiveData.postValue(Event(NavigateToThread(threadId, username))) _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) { } catch (e: Exception) {
Log.e(TAG, "sendDm: ", e) Log.e(TAG, "sendDm: ", e)
} finally { } finally {
@ -399,7 +400,7 @@ class ProfileFragmentViewModel(
val profile = profile.value?.data ?: return@afterPrevious val profile = profile.value?.data ?: return@afterPrevious
friendshipRepository.toggleRestrict( friendshipRepository.toggleRestrict(
csrfToken ?: return@afterPrevious, csrfToken ?: return@afterPrevious,
deviceUuid, deviceUuid ?: return@afterPrevious,
profile.pk, profile.pk,
!(profile.friendshipStatus?.isRestricted ?: false), !(profile.friendshipStatus?.isRestricted ?: false),
) )
@ -421,7 +422,7 @@ class ProfileFragmentViewModel(
friendshipRepository.changeBlock( friendshipRepository.changeBlock(
csrfToken ?: return@afterPrevious, csrfToken ?: return@afterPrevious,
currentUser.value?.data?.pk ?: return@afterPrevious, currentUser.value?.data?.pk ?: return@afterPrevious,
deviceUuid, deviceUuid ?: return@afterPrevious,
profile.friendshipStatus?.blocking ?: return@afterPrevious, profile.friendshipStatus?.blocking ?: return@afterPrevious,
profile.pk profile.pk
) )
@ -443,7 +444,7 @@ class ProfileFragmentViewModel(
friendshipRepository.changeMute( friendshipRepository.changeMute(
csrfToken ?: return@afterPrevious, csrfToken ?: return@afterPrevious,
currentUser.value?.data?.pk ?: return@afterPrevious, currentUser.value?.data?.pk ?: return@afterPrevious,
deviceUuid, deviceUuid ?: return@afterPrevious,
profile.friendshipStatus?.isMutingReel ?: return@afterPrevious, profile.friendshipStatus?.isMutingReel ?: return@afterPrevious,
profile.pk, profile.pk,
true true
@ -466,7 +467,7 @@ class ProfileFragmentViewModel(
friendshipRepository.changeMute( friendshipRepository.changeMute(
csrfToken ?: return@afterPrevious, csrfToken ?: return@afterPrevious,
currentUser.value?.data?.pk ?: return@afterPrevious, currentUser.value?.data?.pk ?: return@afterPrevious,
deviceUuid, deviceUuid ?: return@afterPrevious,
profile.friendshipStatus?.muting ?: return@afterPrevious, profile.friendshipStatus?.muting ?: return@afterPrevious,
profile.pk, profile.pk,
false false
@ -488,7 +489,7 @@ class ProfileFragmentViewModel(
friendshipRepository.removeFollower( friendshipRepository.removeFollower(
csrfToken ?: return@afterPrevious, csrfToken ?: return@afterPrevious,
currentUser.value?.data?.pk ?: return@afterPrevious, currentUser.value?.data?.pk ?: return@afterPrevious,
deviceUuid, deviceUuid ?: return@afterPrevious,
profile.value?.data?.pk ?: return@afterPrevious profile.value?.data?.pk ?: return@afterPrevious
) )
profileAction.postValue(REFRESH_FRIENDSHIP) profileAction.postValue(REFRESH_FRIENDSHIP)
@ -597,7 +598,15 @@ class ProfileFragmentViewModel(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
class ProfileFragmentViewModelFactory( class ProfileFragmentViewModelFactory(
private val favoriteRepository: FavoriteRepository?, private val csrfToken: String?,
private val deviceUuid: String?,
private val userRepository: UserRepository,
private val friendshipRepository: FriendshipRepository,
private val storiesRepository: StoriesRepository,
private val mediaRepository: MediaRepository,
private val graphQLRepository: GraphQLRepository,
private val favoriteRepository: FavoriteRepository,
private val directMessagesRepository: DirectMessagesRepository,
private val messageManager: DirectMessagesManager?, private val messageManager: DirectMessagesManager?,
owner: SavedStateRegistryOwner, owner: SavedStateRegistryOwner,
defaultArgs: Bundle? = null, defaultArgs: Bundle? = null,
@ -609,7 +618,15 @@ class ProfileFragmentViewModelFactory(
): T { ): T {
return ProfileFragmentViewModel( return ProfileFragmentViewModel(
handle, handle,
csrfToken,
deviceUuid,
userRepository,
friendshipRepository,
storiesRepository,
mediaRepository,
graphQLRepository,
favoriteRepository, favoriteRepository,
directMessagesRepository,
messageManager, messageManager,
Dispatchers.IO, Dispatchers.IO,
) as T ) as T

View File

@ -7,19 +7,22 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import awais.instagrabber.R import awais.instagrabber.R
import awais.instagrabber.managers.DirectMessagesManager import awais.instagrabber.managers.DirectMessagesManager
import awais.instagrabber.models.enums.FavoriteType
import awais.instagrabber.models.enums.MediaItemType
import awais.instagrabber.models.enums.StoryPaginationType
import awais.instagrabber.models.Resource import awais.instagrabber.models.Resource
import awais.instagrabber.models.Resource.Companion.error import awais.instagrabber.models.Resource.Companion.error
import awais.instagrabber.models.Resource.Companion.loading import awais.instagrabber.models.Resource.Companion.loading
import awais.instagrabber.models.Resource.Companion.success import awais.instagrabber.models.Resource.Companion.success
import awais.instagrabber.models.enums.BroadcastItemType import awais.instagrabber.models.enums.BroadcastItemType
import awais.instagrabber.models.enums.FavoriteType
import awais.instagrabber.models.enums.MediaItemType
import awais.instagrabber.models.enums.StoryPaginationType
import awais.instagrabber.repositories.requests.StoryViewerOptions import awais.instagrabber.repositories.requests.StoryViewerOptions
import awais.instagrabber.repositories.responses.Media
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient import awais.instagrabber.repositories.responses.directmessages.RankedRecipient
import awais.instagrabber.repositories.responses.stories.* import awais.instagrabber.repositories.responses.stories.*
import awais.instagrabber.repositories.responses.Media import awais.instagrabber.utils.Constants
import awais.instagrabber.utils.* import awais.instagrabber.utils.Utils
import awais.instagrabber.utils.getCsrfTokenFromCookie
import awais.instagrabber.utils.getUserIdFromCookie
import awais.instagrabber.webservices.MediaRepository import awais.instagrabber.webservices.MediaRepository
import awais.instagrabber.webservices.StoriesRepository import awais.instagrabber.webservices.StoriesRepository
import com.google.common.collect.ImmutableList import com.google.common.collect.ImmutableList

View File

@ -21,7 +21,7 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import awais.instagrabber.R; import awais.instagrabber.R;
import awais.instagrabber.fragments.UserSearchFragment; import awais.instagrabber.fragments.UserSearchMode;
import awais.instagrabber.models.Resource; import awais.instagrabber.models.Resource;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;
import awais.instagrabber.repositories.responses.directmessages.RankedRecipient; import awais.instagrabber.repositories.responses.directmessages.RankedRecipient;
@ -49,7 +49,7 @@ public class UserSearchViewModel extends ViewModel {
private Call<?> searchRequest; private Call<?> searchRequest;
private long[] hideUserIds; private long[] hideUserIds;
private String[] hideThreadIds; private String[] hideThreadIds;
private UserSearchFragment.SearchMode searchMode; private UserSearchMode searchMode;
private boolean showGroups; private boolean showGroups;
private boolean waitingForCache; private boolean waitingForCache;
private boolean showCachedResults; private boolean showCachedResults;
@ -192,7 +192,7 @@ public class UserSearchViewModel extends ViewModel {
private void rankedRecipientSearch() { private void rankedRecipientSearch() {
directMessagesRepository.rankedRecipients( directMessagesRepository.rankedRecipients(
searchMode.getName(), searchMode.getMode(),
showGroups, showGroups,
currentQuery, currentQuery,
CoroutineUtilsKt.getContinuation((response, throwable) -> { CoroutineUtilsKt.getContinuation((response, throwable) -> {
@ -290,7 +290,7 @@ public class UserSearchViewModel extends ViewModel {
return showAction; return showAction;
} }
public void setSearchMode(final UserSearchFragment.SearchMode searchMode) { public void setSearchMode(final UserSearchMode searchMode) {
this.searchMode = searchMode; this.searchMode = searchMode;
} }

View File

@ -1,6 +1,5 @@
package thoughtbot.expandableadapter; package thoughtbot.expandableadapter;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import awais.instagrabber.repositories.responses.User; import awais.instagrabber.repositories.responses.User;

View File

@ -60,9 +60,11 @@
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView
android:id="@+id/main_nav_host" android:id="@+id/main_nav_host"
android:name="awais.instagrabber.customviews.BarinstaNavHostFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
app:defaultNavHost="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<!--app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"--> <!--app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"-->
@ -71,5 +73,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_gravity="bottom"
app:labelVisibilityMode="auto" /> app:labelVisibilityMode="auto"
tools:menu="@menu/bottom_nav_menu" />
</awais.instagrabber.customviews.InsetsNotifyingCoordinatorLayout> </awais.instagrabber.customviews.InsetsNotifyingCoordinatorLayout>

View File

@ -23,8 +23,8 @@
android:id="@+id/label_layout" android:id="@+id/label_layout"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginRight="6dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:gravity="right" android:gravity="right"
android:text="@string/layout_style" android:text="@string/layout_style"
@ -74,14 +74,14 @@
android:id="@+id/label_col_count" android:id="@+id/label_col_count"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginRight="6dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:gravity="right" android:gravity="right"
android:text="@string/column_count" android:text="@string/column_count"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/col_count_toggle" app:layout_constraintBottom_toBottomOf="@id/col_count_toggle"
app:layout_constraintEnd_toStartOf="@id/guideline" app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/col_count_toggle" /> app:layout_constraintTop_toTopOf="@id/col_count_toggle" />
<com.google.android.material.button.MaterialButtonToggleGroup <com.google.android.material.button.MaterialButtonToggleGroup
@ -116,8 +116,8 @@
android:id="@+id/label_show_names_toggle" android:id="@+id/label_show_names_toggle"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginRight="6dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:gravity="right" android:gravity="right"
android:text="@string/show_names" android:text="@string/show_names"
@ -142,14 +142,14 @@
android:id="@+id/label_show_avatar_toggle" android:id="@+id/label_show_avatar_toggle"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginRight="6dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:gravity="right" android:gravity="right"
android:text="@string/show_avatars" android:text="@string/show_avatars"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/show_avatar_toggle" app:layout_constraintBottom_toBottomOf="@id/show_avatar_toggle"
app:layout_constraintEnd_toStartOf="@id/guideline" app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/show_avatar_toggle" /> app:layout_constraintTop_toTopOf="@id/show_avatar_toggle" />
<com.google.android.material.switchmaterial.SwitchMaterial <com.google.android.material.switchmaterial.SwitchMaterial
@ -168,14 +168,14 @@
android:id="@+id/label_avatar_size" android:id="@+id/label_avatar_size"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginRight="6dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:gravity="right" android:gravity="right"
android:text="@string/avatar_size" android:text="@string/avatar_size"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/avatar_size_toggle" app:layout_constraintBottom_toBottomOf="@id/avatar_size_toggle"
app:layout_constraintEnd_toStartOf="@id/guideline" app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/avatar_size_toggle" /> app:layout_constraintTop_toTopOf="@id/avatar_size_toggle" />
<com.google.android.material.button.MaterialButtonToggleGroup <com.google.android.material.button.MaterialButtonToggleGroup
@ -217,14 +217,14 @@
android:id="@+id/label_corners" android:id="@+id/label_corners"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginRight="6dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:gravity="right" android:gravity="right"
android:text="@string/corners" android:text="@string/corners"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/corners_toggle" app:layout_constraintBottom_toBottomOf="@id/corners_toggle"
app:layout_constraintEnd_toStartOf="@id/guideline" app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/corners_toggle" /> app:layout_constraintTop_toTopOf="@id/corners_toggle" />
<com.google.android.material.button.MaterialButtonToggleGroup <com.google.android.material.button.MaterialButtonToggleGroup
@ -259,14 +259,14 @@
android:id="@+id/label_gap" android:id="@+id/label_gap"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginRight="6dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:gravity="right" android:gravity="right"
android:text="@string/show_grid_gap" android:text="@string/show_grid_gap"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/show_gap_toggle" app:layout_constraintBottom_toBottomOf="@id/show_gap_toggle"
app:layout_constraintEnd_toStartOf="@id/guideline" app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/show_gap_toggle" /> app:layout_constraintTop_toTopOf="@id/show_gap_toggle" />
<com.google.android.material.switchmaterial.SwitchMaterial <com.google.android.material.switchmaterial.SwitchMaterial
@ -274,39 +274,13 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:checked="true" android:checked="true"
app:layout_constraintBottom_toTopOf="@id/disable_animation_toggle" app:layout_constraintBottom_toTopOf="@id/show_gap_toggle"
app:layout_constraintStart_toEndOf="@id/guideline" app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/corners_toggle" app:layout_constraintTop_toBottomOf="@id/corners_toggle"
app:showText="false" app:showText="false"
app:switchPadding="0dp" app:switchPadding="0dp"
app:thumbTextPadding="0dp" /> app:thumbTextPadding="0dp" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/label_disable_animation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="6dp"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:gravity="right"
android:text="@string/disable_animation"
android:layout_gravity="right"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="@id/disable_animation_toggle"
app:layout_constraintEnd_toStartOf="@id/guideline"
app:layout_constraintTop_toTopOf="@id/disable_animation_toggle" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/disable_animation_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
app:layout_constraintStart_toEndOf="@id/guideline"
app:layout_constraintTop_toBottomOf="@id/show_gap_toggle"
app:showText="false"
app:switchPadding="0dp"
app:thumbTextPadding="0dp" />
<androidx.constraintlayout.widget.Group <androidx.constraintlayout.widget.Group
android:id="@+id/staggered_or_grid_options" android:id="@+id/staggered_or_grid_options"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -33,27 +33,29 @@
android:gravity="center" /> android:gravity="center" />
</LinearLayout> </LinearLayout>
<LinearLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/custom_format_field"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:visibility="gone"
android:visibility="gone"> app:counterEnabled="false"
app:counterMaxLength="50"
app:endIconDrawable="@drawable/ic_outline_info_24"
app:endIconMode="custom"
app:hintEnabled="false"
tools:visibility="visible">
<androidx.appcompat.widget.AppCompatEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/etCustomFormat" android:id="@+id/custom_format_edit_text"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:autofillHints="no"
android:enabled="false" /> android:inputType="text"
android:maxLength="50"
android:padding="16dp"
tools:text="test" />
<androidx.appcompat.widget.AppCompatImageButton </com.google.android.material.textfield.TextInputLayout>
android:id="@+id/btnInfo"
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
app:srcCompat="@drawable/ic_outline_info_24" />
</LinearLayout>
<FrameLayout <FrameLayout
android:id="@+id/customPanel" android:id="@+id/customPanel"

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/swipeRefreshLayout" android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@id/direct_messages_nav_graph"
android:icon="@drawable/ic_message_24"
android:contentDescription="@string/title_dm"
android:title="@string/title_dm" />
<item
android:id="@id/feed_nav_graph"
android:icon="@drawable/ic_home_24"
android:contentDescription="@string/feed"
android:title="@string/feed" />
<item
android:id="@id/profile_nav_graph"
android:icon="@drawable/ic_person_24"
android:contentDescription="@string/profile"
android:title="@string/profile" />
<item
android:id="@id/discover_nav_graph"
android:icon="@drawable/ic_explore_24"
android:contentDescription="@string/title_discover"
android:title="@string/title_discover" />
<item
android:id="@id/more_nav_graph"
android:icon="@drawable/ic_more_horiz_24"
android:contentDescription="@string/more"
android:title="@string/more" />
</menu>

View File

@ -1,78 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/comments_nav_graph"
app:startDestination="@id/commentsViewerFragment">
<!--<include app:graph="@navigation/hashtag_nav_graph" />-->
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashtag_nav_graph">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<!--<include app:graph="@navigation/profile_nav_graph" />-->
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<dialog
android:id="@+id/commentsViewerFragment"
android:name="awais.instagrabber.fragments.comments.CommentsViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_comments">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</dialog>
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/commentsViewerFragment">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
</navigation>

View File

@ -5,149 +5,487 @@
android:id="@+id/direct_messages_nav_graph" android:id="@+id/direct_messages_nav_graph"
app:startDestination="@id/directMessagesInboxFragment"> app:startDestination="@id/directMessagesInboxFragment">
<include app:graph="@navigation/profile_nav_graph" />
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph" />
<include app:graph="@navigation/location_nav_graph" />
<action
android:id="@+id/action_global_locationFragment"
app:destination="@id/location_nav_graph">
<argument
android:name="locationId"
app:argType="long" />
</action>
<include app:graph="@navigation/hashtag_nav_graph" />
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashtag_nav_graph">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<include app:graph="@navigation/notification_viewer_nav_graph" />
<action
android:id="@+id/action_global_notificationsViewerFragment"
app:destination="@id/notification_viewer_nav_graph">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
</action>
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/comments_nav_graph">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
<include app:graph="@navigation/saved_nav_graph" />
<action
android:id="@+id/action_global_savedCollectionsFragment"
app:destination="@id/saved_nav_graph">
<argument
android:name="isSaving"
app:argType="boolean" />
</action>
<include app:graph="@navigation/user_search_nav_graph" />
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search_nav_graph" />
<action
android:id="@+id/action_global_search"
app:destination="@id/searchFragment" />
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<fragment <fragment
android:id="@+id/directMessagesInboxFragment" android:id="@+id/directMessagesInboxFragment"
android:name="awais.instagrabber.fragments.directmessages.DirectMessageInboxFragment" android:name="awais.instagrabber.fragments.directmessages.DirectMessageInboxFragment"
android:label="@string/action_dms" android:label="@string/action_dms"
tools:layout="@layout/fragment_direct_messages_inbox"> tools:layout="@layout/fragment_direct_messages_inbox">
<action <action
android:id="@+id/action_inbox_to_thread" android:id="@+id/action_to_thread"
app:destination="@id/directMessagesThreadFragment" /> app:destination="@id/directMessagesThreadFragment" />
<action <action
android:id="@+id/action_inbox_to_pending_inbox" android:id="@+id/action_to_pending_inbox"
app:destination="@id/directPendingInboxFragment" /> app:destination="@id/directPendingInboxFragment" />
</fragment> </fragment>
<fragment
android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment"
android:label="StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
</fragment>
<fragment
android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post"
tools:layout="@layout/dialog_post_view">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/locationFragment"
android:name="awais.instagrabber.fragments.LocationFragment"
android:label=""
tools:layout="@layout/fragment_location">
<argument
android:name="locationId"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/hashTagFragment"
android:name="awais.instagrabber.fragments.HashTagFragment"
android:label=""
tools:layout="@layout/fragment_hashtag">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/commentsViewerFragment"
android:name="awais.instagrabber.fragments.comments.CommentsViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_comments">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of profile fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/profile_non_top"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="@string/profile"
tools:layout="@layout/fragment_profile">
<argument
android:name="username"
android:defaultValue=""
app:argType="string"
app:nullable="true" />
<action
android:id="@+id/action_to_saved"
app:destination="@id/savedViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_follow_viewer"
app:destination="@id/followViewerFragment" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_notifications"
app:destination="@id/notifications_viewer_non_top" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/savedViewerFragment"
android:name="awais.instagrabber.fragments.SavedViewerFragment"
android:label="Saved"
tools:layout="@layout/fragment_saved">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="type"
app:argType="awais.instagrabber.models.enums.PostItemType"
app:nullable="false" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/followViewerFragment"
android:name="awais.instagrabber.fragments.FollowViewerFragment"
android:label=""
tools:layout="@layout/fragment_followers_viewer">
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="isFollowersList"
app:argType="boolean"
app:nullable="false" />
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/likesViewerFragment"
android:name="awais.instagrabber.fragments.LikesViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_likes">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of notification viewer fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/notifications_viewer_non_top"
android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer">
<argument
android:name="type"
android:defaultValue="notif"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment>
<fragment
android:id="@+id/savedCollectionsFragment"
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
android:label="@string/saved"
tools:layout="@layout/fragment_saved_collections">
<argument
android:name="isSaving"
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_to_collection_posts"
app:destination="@id/collectionPostsFragment" />
</fragment>
<fragment
android:id="@+id/collectionPostsFragment"
android:name="awais.instagrabber.fragments.CollectionPostsFragment"
tools:layout="@layout/fragment_collection_posts">
<argument
android:name="savedCollection"
app:argType="awais.instagrabber.repositories.responses.saved.SavedCollection" />
<argument
android:name="titleColor"
app:argType="integer" />
<argument
android:name="backgroundColor"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment <fragment
android:id="@+id/directMessagesThreadFragment" android:id="@+id/directMessagesThreadFragment"
android:name="awais.instagrabber.fragments.directmessages.DirectMessageThreadFragment" android:name="awais.instagrabber.fragments.directmessages.DirectMessageThreadFragment"
tools:layout="@layout/fragment_direct_messages_thread"> tools:layout="@layout/fragment_direct_messages_thread">
<argument <argument
android:name="threadId" android:name="threadId"
app:argType="string" /> app:argType="string" />
<argument <argument
android:name="title" android:name="title"
app:argType="string" /> app:argType="string" />
<argument <argument
android:name="pending" android:name="pending"
android:defaultValue="false" android:defaultValue="false"
app:argType="boolean" /> app:argType="boolean" />
<deepLink app:uri="barinsta://dm_thread/{threadId}/{title}?pending={pending}" />
<action <action
android:id="@+id/action_thread_to_settings" android:id="@+id/action_to_settings"
app:destination="@id/directMessagesSettingsFragment" /> app:destination="@id/directMessagesSettingsFragment" />
<action <action
android:id="@+id/action_thread_to_image_edit" android:id="@+id/action_to_image_edit"
app:destination="@id/imageEditFragment" /> app:destination="@id/imageEditFragment" />
<action <action
android:id="@+id/action_thread_to_story" android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" /> app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/directMessagesSettingsFragment" android:id="@+id/directMessagesSettingsFragment"
android:name="awais.instagrabber.fragments.directmessages.DirectMessageSettingsFragment" android:name="awais.instagrabber.fragments.directmessages.DirectMessageSettingsFragment"
@ -169,12 +507,20 @@
app:argType="boolean" /> app:argType="boolean" />
<action <action
android:id="@+id/action_settings_to_inbox" android:id="@+id/action_to_inbox"
app:destination="@id/directMessagesInboxFragment" app:destination="@id/directMessagesInboxFragment"
app:popUpTo="@+id/directMessagesInboxFragment" app:popUpTo="@+id/directMessagesInboxFragment"
app:popUpToInclusive="true" /> app:popUpToInclusive="true" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/imageEditFragment" android:id="@+id/imageEditFragment"
android:name="awais.instagrabber.fragments.imageedit.ImageEditFragment" android:name="awais.instagrabber.fragments.imageedit.ImageEditFragment"
@ -185,31 +531,59 @@
app:argType="android.net.Uri" app:argType="android.net.Uri"
app:nullable="false" /> app:nullable="false" />
</fragment> </fragment>
<fragment
android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
</fragment>
<fragment <fragment
android:id="@+id/directPendingInboxFragment" android:id="@+id/directPendingInboxFragment"
android:name="awais.instagrabber.fragments.directmessages.DirectPendingInboxFragment" android:name="awais.instagrabber.fragments.directmessages.DirectPendingInboxFragment"
android:label="@string/pending_requests" android:label="@string/pending_requests"
tools:layout="@layout/fragment_direct_pending_inbox"> tools:layout="@layout/fragment_direct_pending_inbox">
<action <action
android:id="@+id/action_pending_inbox_to_thread" android:id="@+id/action_to_thread"
app:destination="@id/directMessagesThreadFragment" /> app:destination="@id/directMessagesThreadFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/searchFragment" android:id="@+id/user_search"
android:name="awais.instagrabber.fragments.search.SearchFragment" android:name="awais.instagrabber.fragments.UserSearchFragment"
android:label="@string/search" android:label="@string/search"
tools:layout="@layout/fragment_search" /> tools:layout="@layout/fragment_user_search">
<fragment <argument
android:id="@+id/postViewFragment" android:name="multiple"
android:name="awais.instagrabber.fragments.PostViewV2Fragment" android:defaultValue="false"
android:label="@string/post" app:argType="boolean" />
tools:layout="@layout/dialog_post_view" />
<argument
android:name="title"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="action_label"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="show_groups"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="search_mode"
android:defaultValue="USER_SEARCH"
app:argType="awais.instagrabber.fragments.UserSearchMode" />
<argument
android:name="hideUserIds"
android:defaultValue="@null"
app:argType="long[]"
app:nullable="true" />
<argument
android:name="hideThreadIds"
android:defaultValue="@null"
app:argType="string[]"
app:nullable="true" />
</fragment>
</navigation> </navigation>

View File

@ -5,131 +5,352 @@
android:id="@+id/discover_nav_graph" android:id="@+id/discover_nav_graph"
app:startDestination="@id/discoverFragment"> app:startDestination="@id/discoverFragment">
<include app:graph="@navigation/hashtag_nav_graph" />
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashtag_nav_graph">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<include app:graph="@navigation/profile_nav_graph" />
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<include app:graph="@navigation/location_nav_graph" />
<action
android:id="@+id/action_global_locationFragment"
app:destination="@id/location_nav_graph">
<argument
android:name="locationId"
app:argType="long" />
</action>
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/comments_nav_graph">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
<include app:graph="@navigation/saved_nav_graph" />
<action
android:id="@+id/action_global_savedCollectionsFragment"
app:destination="@id/saved_nav_graph">
<argument
android:name="isSaving"
app:argType="boolean" />
</action>
<include app:graph="@navigation/notification_viewer_nav_graph" />
<action
android:id="@+id/action_global_notificationsViewerFragment"
app:destination="@id/notification_viewer_nav_graph">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
</action>
<action
android:id="@+id/action_global_search"
app:destination="@id/searchFragment" />
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<include app:graph="@navigation/user_search_nav_graph" />
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search_nav_graph" />
<fragment <fragment
android:id="@+id/discoverFragment" android:id="@+id/discoverFragment"
android:name="awais.instagrabber.fragments.main.DiscoverFragment" android:name="awais.instagrabber.fragments.main.DiscoverFragment"
android:label="@string/title_discover" android:label="@string/title_discover"
tools:layout="@layout/fragment_discover"> tools:layout="@layout/fragment_discover">
<action <action
android:id="@+id/action_discoverFragment_to_topicPostsFragment" android:id="@+id/action_to_topic_posts"
app:destination="@id/topicPostsFragment" /> app:destination="@id/topicPostsFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment> </fragment>
<fragment
android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment"
android:label="StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
</fragment>
<fragment
android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post"
tools:layout="@layout/dialog_post_view">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/locationFragment"
android:name="awais.instagrabber.fragments.LocationFragment"
android:label=""
tools:layout="@layout/fragment_location">
<argument
android:name="locationId"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/hashTagFragment"
android:name="awais.instagrabber.fragments.HashTagFragment"
android:label=""
tools:layout="@layout/fragment_hashtag">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/commentsViewerFragment"
android:name="awais.instagrabber.fragments.comments.CommentsViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_comments">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of profile fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/profile_non_top"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="@string/profile"
tools:layout="@layout/fragment_profile">
<argument
android:name="username"
android:defaultValue=""
app:argType="string"
app:nullable="true" />
<action
android:id="@+id/action_to_saved"
app:destination="@id/savedViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_follow_viewer"
app:destination="@id/followViewerFragment" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_notifications"
app:destination="@id/notifications_viewer_non_top" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/savedViewerFragment"
android:name="awais.instagrabber.fragments.SavedViewerFragment"
android:label="Saved"
tools:layout="@layout/fragment_saved">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="type"
app:argType="awais.instagrabber.models.enums.PostItemType"
app:nullable="false" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/followViewerFragment"
android:name="awais.instagrabber.fragments.FollowViewerFragment"
android:label=""
tools:layout="@layout/fragment_followers_viewer">
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="isFollowersList"
app:argType="boolean"
app:nullable="false" />
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/likesViewerFragment"
android:name="awais.instagrabber.fragments.LikesViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_likes">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<fragment <fragment
android:id="@+id/topicPostsFragment" android:id="@+id/topicPostsFragment"
android:name="awais.instagrabber.fragments.TopicPostsFragment" android:name="awais.instagrabber.fragments.TopicPostsFragment"
tools:layout="@layout/fragment_topic_posts"> tools:layout="@layout/fragment_topic_posts">
<argument <argument
android:name="topicCluster" android:name="topicCluster"
app:argType="awais.instagrabber.repositories.responses.discover.TopicCluster" /> app:argType="awais.instagrabber.repositories.responses.discover.TopicCluster" />
@ -141,15 +362,157 @@
<argument <argument
android:name="backgroundColor" android:name="backgroundColor"
app:argType="integer" /> app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment> </fragment>
<!-- Copy of notification viewer fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment <fragment
android:id="@+id/searchFragment" android:id="@+id/notifications_viewer_non_top"
android:name="awais.instagrabber.fragments.search.SearchFragment" android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer">
<argument
android:name="type"
android:defaultValue="notif"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment>
<fragment
android:id="@+id/savedCollectionsFragment"
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
android:label="@string/saved"
tools:layout="@layout/fragment_saved_collections">
<argument
android:name="isSaving"
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_to_collection_posts"
app:destination="@id/collectionPostsFragment" />
</fragment>
<fragment
android:id="@+id/collectionPostsFragment"
android:name="awais.instagrabber.fragments.CollectionPostsFragment"
tools:layout="@layout/fragment_collection_posts">
<argument
android:name="savedCollection"
app:argType="awais.instagrabber.repositories.responses.saved.SavedCollection" />
<argument
android:name="titleColor"
app:argType="integer" />
<argument
android:name="backgroundColor"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/user_search"
android:name="awais.instagrabber.fragments.UserSearchFragment"
android:label="@string/search" android:label="@string/search"
tools:layout="@layout/fragment_search" /> tools:layout="@layout/fragment_user_search">
<fragment <argument
android:id="@+id/postViewFragment" android:name="multiple"
android:name="awais.instagrabber.fragments.PostViewV2Fragment" android:defaultValue="false"
android:label="@string/post" app:argType="boolean" />
tools:layout="@layout/dialog_post_view" />
<argument
android:name="title"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="action_label"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="show_groups"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="search_mode"
android:defaultValue="USER_SEARCH"
app:argType="awais.instagrabber.fragments.UserSearchMode" />
<argument
android:name="hideUserIds"
android:defaultValue="@null"
app:argType="long[]"
app:nullable="true" />
<argument
android:name="hideThreadIds"
android:defaultValue="@null"
app:argType="string[]"
app:nullable="true" />
</fragment>
</navigation> </navigation>

View File

@ -5,66 +5,480 @@
android:id="@+id/favorites_nav_graph" android:id="@+id/favorites_nav_graph"
app:startDestination="@id/favoritesFragment"> app:startDestination="@id/favoritesFragment">
<include app:graph="@navigation/profile_nav_graph" />
<include app:graph="@navigation/hashtag_nav_graph" />
<include app:graph="@navigation/location_nav_graph" />
<include app:graph="@navigation/comments_nav_graph" />
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashtag_nav_graph">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_locationFragment"
app:destination="@id/location_nav_graph">
<argument
android:name="locationId"
app:argType="long" />
</action>
<action
android:id="@+id/action_global_search"
app:destination="@id/searchFragment" />
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<fragment <fragment
android:id="@+id/favoritesFragment" android:id="@+id/favoritesFragment"
android:name="awais.instagrabber.fragments.FavoritesFragment" android:name="awais.instagrabber.fragments.FavoritesFragment"
android:label="@string/title_favorites" android:label="@string/title_favorites"
tools:layout="@layout/fragment_favorites" /> tools:layout="@layout/fragment_favorites">
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
</fragment>
<fragment <fragment
android:id="@+id/searchFragment" android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.search.SearchFragment" android:name="awais.instagrabber.fragments.StoryViewerFragment"
android:label="@string/search" android:label="StoryViewerFragment"
tools:layout="@layout/fragment_search" /> tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
</fragment>
<fragment <fragment
android:id="@+id/postViewFragment" android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment" android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post" /> android:label="@string/post"
tools:layout="@layout/dialog_post_view">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/locationFragment"
android:name="awais.instagrabber.fragments.LocationFragment"
android:label=""
tools:layout="@layout/fragment_location">
<argument
android:name="locationId"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/hashTagFragment"
android:name="awais.instagrabber.fragments.HashTagFragment"
android:label=""
tools:layout="@layout/fragment_hashtag">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/commentsViewerFragment"
android:name="awais.instagrabber.fragments.comments.CommentsViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_comments">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of profile fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/profile_non_top"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="@string/profile"
tools:layout="@layout/fragment_profile">
<argument
android:name="username"
android:defaultValue=""
app:argType="string"
app:nullable="true" />
<action
android:id="@+id/action_to_saved"
app:destination="@id/savedViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_follow_viewer"
app:destination="@id/followViewerFragment" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_notifications"
app:destination="@id/notifications_viewer_non_top" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/savedViewerFragment"
android:name="awais.instagrabber.fragments.SavedViewerFragment"
android:label="Saved"
tools:layout="@layout/fragment_saved">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="type"
app:argType="awais.instagrabber.models.enums.PostItemType"
app:nullable="false" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/followViewerFragment"
android:name="awais.instagrabber.fragments.FollowViewerFragment"
android:label=""
tools:layout="@layout/fragment_followers_viewer">
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="isFollowersList"
app:argType="boolean"
app:nullable="false" />
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/likesViewerFragment"
android:name="awais.instagrabber.fragments.LikesViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_likes">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<fragment
android:id="@+id/savedCollectionsFragment"
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
android:label="@string/saved"
tools:layout="@layout/fragment_saved_collections">
<argument
android:name="isSaving"
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_to_collection_posts"
app:destination="@id/collectionPostsFragment" />
</fragment>
<fragment
android:id="@+id/collectionPostsFragment"
android:name="awais.instagrabber.fragments.CollectionPostsFragment"
tools:layout="@layout/fragment_collection_posts">
<argument
android:name="savedCollection"
app:argType="awais.instagrabber.repositories.responses.saved.SavedCollection" />
<argument
android:name="titleColor"
app:argType="integer" />
<argument
android:name="backgroundColor"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/user_search"
android:name="awais.instagrabber.fragments.UserSearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_user_search">
<argument
android:name="multiple"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="title"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="action_label"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="show_groups"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="search_mode"
android:defaultValue="USER_SEARCH"
app:argType="awais.instagrabber.fragments.UserSearchMode" />
<argument
android:name="hideUserIds"
android:defaultValue="@null"
app:argType="long[]"
app:nullable="true" />
<argument
android:name="hideThreadIds"
android:defaultValue="@null"
app:argType="string[]"
app:nullable="true" />
</fragment>
<!-- Copy of notification viewer fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/notifications_viewer_non_top"
android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer">
<argument
android:name="type"
android:defaultValue="notif"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment>
</navigation> </navigation>

View File

@ -5,154 +5,516 @@
android:id="@+id/feed_nav_graph" android:id="@+id/feed_nav_graph"
app:startDestination="@id/feedFragment"> app:startDestination="@id/feedFragment">
<include app:graph="@navigation/hashtag_nav_graph" />
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashtag_nav_graph">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<include app:graph="@navigation/profile_nav_graph" />
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<include app:graph="@navigation/location_nav_graph" />
<action
android:id="@+id/action_global_locationFragment"
app:destination="@id/location_nav_graph">
<argument
android:name="locationId"
app:argType="long" />
</action>
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/comments_nav_graph">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
<include app:graph="@navigation/saved_nav_graph" />
<action
android:id="@+id/action_global_savedCollectionsFragment"
app:destination="@id/saved_nav_graph">
<argument
android:name="isSaving"
app:argType="boolean" />
</action>
<include app:graph="@navigation/notification_viewer_nav_graph" />
<action
android:id="@+id/action_global_notificationsViewerFragment"
app:destination="@id/notification_viewer_nav_graph">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
</action>
<include app:graph="@navigation/story_list_nav_graph" />
<action
android:id="@+id/action_global_storyListViewerFragment"
app:destination="@id/story_list_nav_graph">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_search"
app:destination="@id/searchFragment" />
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<include app:graph="@navigation/user_search_nav_graph" />
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search_nav_graph" />
<fragment <fragment
android:id="@+id/feedFragment" android:id="@+id/feedFragment"
android:name="awais.instagrabber.fragments.main.FeedFragment" android:name="awais.instagrabber.fragments.main.FeedFragment"
android:label="@string/feed" android:label="@string/feed"
tools:layout="@layout/fragment_feed"> tools:layout="@layout/fragment_feed">
<action <action
android:id="@+id/action_feedFragment_to_storyViewerFragment" android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" /> app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_story_list"
app:destination="@id/storyListViewerFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/storyViewerFragment" android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment" android:name="awais.instagrabber.fragments.StoryViewerFragment"
android:label="StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer"> tools:layout="@layout/fragment_story_viewer">
<argument <argument
android:name="options" android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" /> app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
</fragment> </fragment>
<fragment
android:id="@+id/searchFragment"
android:name="awais.instagrabber.fragments.search.SearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_search" />
<fragment <fragment
android:id="@+id/postViewFragment" android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment" android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post" android:label="@string/post"
tools:layout="@layout/dialog_post_view" /> tools:layout="@layout/dialog_post_view">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/locationFragment"
android:name="awais.instagrabber.fragments.LocationFragment"
android:label=""
tools:layout="@layout/fragment_location">
<argument
android:name="locationId"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/hashTagFragment"
android:name="awais.instagrabber.fragments.HashTagFragment"
android:label=""
tools:layout="@layout/fragment_hashtag">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/commentsViewerFragment"
android:name="awais.instagrabber.fragments.comments.CommentsViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_comments">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of profile fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/profile_non_top"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="@string/profile"
tools:layout="@layout/fragment_profile">
<argument
android:name="username"
android:defaultValue=""
app:argType="string"
app:nullable="true" />
<action
android:id="@+id/action_to_saved"
app:destination="@id/savedViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_follow_viewer"
app:destination="@id/followViewerFragment" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_notifications"
app:destination="@id/notifications_viewer_non_top" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/savedViewerFragment"
android:name="awais.instagrabber.fragments.SavedViewerFragment"
android:label="Saved"
tools:layout="@layout/fragment_saved">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="type"
app:argType="awais.instagrabber.models.enums.PostItemType"
app:nullable="false" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/followViewerFragment"
android:name="awais.instagrabber.fragments.FollowViewerFragment"
android:label=""
tools:layout="@layout/fragment_followers_viewer">
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="isFollowersList"
app:argType="boolean"
app:nullable="false" />
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/likesViewerFragment"
android:name="awais.instagrabber.fragments.LikesViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_likes">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of notification viewer fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/notifications_viewer_non_top"
android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer">
<argument
android:name="type"
android:defaultValue="notif"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment>
<fragment
android:id="@+id/savedCollectionsFragment"
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
android:label="@string/saved"
tools:layout="@layout/fragment_saved_collections">
<argument
android:name="isSaving"
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_to_collection_posts"
app:destination="@id/collectionPostsFragment" />
</fragment>
<fragment
android:id="@+id/collectionPostsFragment"
android:name="awais.instagrabber.fragments.CollectionPostsFragment"
tools:layout="@layout/fragment_collection_posts">
<argument
android:name="savedCollection"
app:argType="awais.instagrabber.repositories.responses.saved.SavedCollection" />
<argument
android:name="titleColor"
app:argType="integer" />
<argument
android:name="backgroundColor"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/storyListViewerFragment"
android:name="awais.instagrabber.fragments.StoryListViewerFragment"
android:label="Stories"
tools:layout="@layout/fragment_story_list_viewer">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/user_search"
android:name="awais.instagrabber.fragments.UserSearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_user_search">
<argument
android:name="multiple"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="title"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="action_label"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="show_groups"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="search_mode"
android:defaultValue="USER_SEARCH"
app:argType="awais.instagrabber.fragments.UserSearchMode" />
<argument
android:name="hideUserIds"
android:defaultValue="@null"
app:argType="long[]"
app:nullable="true" />
<argument
android:name="hideThreadIds"
android:defaultValue="@null"
app:argType="string[]"
app:nullable="true" />
</fragment>
</navigation> </navigation>

View File

@ -1,129 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/hashtag_nav_graph"
app:startDestination="@id/hashTagFragment">
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/comments_nav_graph">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
<include app:graph="@navigation/saved_nav_graph" />
<action
android:id="@+id/action_global_savedCollectionsFragment"
app:destination="@id/saved_nav_graph">
<argument
android:name="isSaving"
app:argType="boolean" />
</action>
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<action
android:id="@+id/action_global_locationFragment"
app:destination="@id/location_nav_graph">
<argument
android:name="locationId"
app:argType="long" />
</action>
<action
android:id="@+id/action_global_search"
app:destination="@id/searchFragment" />
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<include app:graph="@navigation/user_search_nav_graph" />
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search_nav_graph" />
<fragment
android:id="@+id/hashTagFragment"
android:name="awais.instagrabber.fragments.HashTagFragment"
android:label=""
tools:layout="@layout/fragment_hashtag">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_hashtagFragment_to_storyViewerFragment"
app:destination="@id/storyViewerFragment" />
</fragment>
<fragment
android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
</fragment>
<fragment
android:id="@+id/searchFragment"
android:name="awais.instagrabber.fragments.search.SearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_search" />
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashTagFragment">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<fragment
android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post"
tools:layout="@layout/dialog_post_view" />
</navigation>

View File

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/likes_nav_graph"
app:startDestination="@id/likesViewerFragment">
<!--<include app:graph="@navigation/hashtag_nav_graph" />-->
<!--<include app:graph="@navigation/profile_nav_graph" />-->
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<dialog
android:id="@+id/likesViewerFragment"
android:name="awais.instagrabber.fragments.LikesViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_likes">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</dialog>
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likesViewerFragment">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
</navigation>

View File

@ -1,128 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/location_nav_graph"
app:startDestination="@id/locationFragment">
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/comments_nav_graph">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
<include app:graph="@navigation/saved_nav_graph" />
<action
android:id="@+id/action_global_savedCollectionsFragment"
app:destination="@id/saved_nav_graph">
<argument
android:name="isSaving"
app:argType="boolean" />
</action>
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashtag_nav_graph">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_search"
app:destination="@id/searchFragment" />
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<include app:graph="@navigation/user_search_nav_graph" />
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search_nav_graph" />
<fragment
android:id="@+id/locationFragment"
android:name="awais.instagrabber.fragments.LocationFragment"
android:label=""
tools:layout="@layout/fragment_location">
<argument
android:name="locationId"
app:argType="long" />
<action
android:id="@+id/action_locationFragment_to_storyViewerFragment"
app:destination="@id/storyViewerFragment" />
</fragment>
<action
android:id="@+id/action_global_locationFragment"
app:destination="@id/locationFragment">
<argument
android:name="locationId"
app:argType="long" />
</action>
<fragment
android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
</fragment>
<fragment
android:id="@+id/searchFragment"
android:name="awais.instagrabber.fragments.search.SearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_search" />
<fragment
android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post"
tools:layout="@layout/dialog_post_view" />
</navigation>

View File

@ -1,205 +1,539 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android" <navigation 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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/more_nav_graph" android:id="@+id/more_nav_graph"
app:startDestination="@id/morePreferencesFragment"> app:startDestination="@id/morePreferencesFragment">
<include app:graph="@navigation/profile_nav_graph" />
<include app:graph="@navigation/hashtag_nav_graph" />
<include app:graph="@navigation/location_nav_graph" />
<include app:graph="@navigation/comments_nav_graph" />
<include app:graph="@navigation/likes_nav_graph" />
<include app:graph="@navigation/notification_viewer_nav_graph" />
<include app:graph="@navigation/story_list_nav_graph" />
<include app:graph="@navigation/discover_nav_graph" />
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/comments_nav_graph">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashtag_nav_graph">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_locationFragment"
app:destination="@id/location_nav_graph">
<argument
android:name="locationId"
app:argType="long" />
</action>
<action
android:id="@+id/action_global_storyListViewerFragment"
app:destination="@id/story_list_nav_graph">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_notificationsViewerFragment"
app:destination="@id/notification_viewer_nav_graph">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
</action>
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<include app:graph="@navigation/user_search_nav_graph" />
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search_nav_graph" />
<fragment <fragment
android:id="@+id/morePreferencesFragment" android:id="@+id/morePreferencesFragment"
android:name="awais.instagrabber.fragments.settings.MorePreferencesFragment" android:name="awais.instagrabber.fragments.settings.MorePreferencesFragment"
android:label="@string/more"> android:label="@string/more">
<action <action
android:id="@+id/action_morePreferencesFragment_to_settingsPreferencesFragment" android:id="@+id/action_to_settings"
app:destination="@id/settingsPreferencesFragment" /> app:destination="@id/settings_nav_graph" />
<action <action
android:id="@+id/action_morePreferencesFragment_to_aboutFragment" android:id="@+id/action_to_about"
app:destination="@id/aboutFragment" /> app:destination="@id/aboutFragment" />
<action <action
android:id="@+id/action_morePreferencesFragment_to_favoritesFragment" android:id="@+id/action_to_favorites"
app:destination="@id/favoritesFragment" /> app:destination="@id/favorites_non_top" />
<action <action
android:id="@+id/action_morePreferencesFragment_to_backupPreferencesFragment" android:id="@+id/action_to_backup"
app:destination="@id/backupPreferencesFragment" /> app:destination="@id/backupPreferencesFragment" />
<action
android:id="@+id/action_to_notifications"
app:destination="@id/notifications_viewer_non_top" />
<action
android:id="@+id/action_to_story_list"
app:destination="@id/storyListViewerFragment" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/settingsPreferencesFragment" android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.settings.SettingsPreferencesFragment" android:name="awais.instagrabber.fragments.StoryViewerFragment"
android:label="@string/action_settings"> android:label="StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
<action <action
android:id="@+id/action_settings_to_theme" android:id="@+id/action_to_post"
app:destination="@id/themePreferencesFragment" /> app:destination="@id/postViewFragment" />
<action <action
android:id="@+id/action_settings_to_locale" android:id="@+id/action_to_profile"
app:destination="@id/localePreferencesFragment" /> app:destination="@id/profile_non_top" />
<action <action
android:id="@+id/action_settings_to_general" android:id="@+id/action_to_hashtag"
app:destination="@id/generalPreferencesFragment" /> app:destination="@id/hashTagFragment" />
<action <action
android:id="@+id/action_settings_to_downloads" android:id="@+id/action_to_location"
app:destination="@id/downloadsPreferencesFragment" /> app:destination="@id/locationFragment" />
<action <action
android:id="@+id/action_settings_to_dm" android:id="@+id/action_to_user_search"
app:destination="@id/DMPreferencesFragment" /> app:destination="@id/user_search" />
<action
android:id="@+id/action_settings_to_stories"
app:destination="@id/storiesPreferencesFragment" />
<action
android:id="@+id/action_settings_to_notifications"
app:destination="@id/notificationsPreferencesFragment" />
<action
android:id="@+id/action_settings_to_post"
app:destination="@id/postPreferencesFragment" />
</fragment> </fragment>
<fragment
android:id="@+id/aboutFragment"
android:name="awais.instagrabber.fragments.settings.AboutFragment"
android:label="@string/action_about" />
<fragment
android:id="@+id/themePreferencesFragment"
android:name="awais.instagrabber.fragments.settings.ThemePreferencesFragment"
android:label="@string/theme_settings" />
<fragment
android:id="@+id/favoritesFragment"
android:name="awais.instagrabber.fragments.FavoritesFragment"
android:label="@string/title_favorites" />
<fragment
android:id="@+id/backupPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.BackupPreferencesFragment"
android:label="@string/backup_and_restore" />
<fragment
android:id="@+id/localePreferencesFragment"
android:name="awais.instagrabber.fragments.settings.LocalePreferencesFragment"
android:label="@string/pref_category_locale" />
<fragment
android:id="@+id/generalPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.GeneralPreferencesFragment"
android:label="@string/pref_category_general" />
<fragment
android:id="@+id/downloadsPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.DownloadsPreferencesFragment"
android:label="@string/pref_category_downloads" />
<fragment
android:id="@+id/DMPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.DMPreferencesFragment"
android:label="@string/pref_category_dm" />
<fragment
android:id="@+id/storiesPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.StoriesPreferencesFragment"
android:label="@string/pref_category_stories" />
<fragment
android:id="@+id/notificationsPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.NotificationsPreferencesFragment"
android:label="@string/pref_category_notifications" />
<fragment
android:id="@+id/postPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.PostPreferencesFragment"
android:label="@string/pref_category_post" />
<fragment <fragment
android:id="@+id/postViewFragment" android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment" android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post" /> android:label="@string/post"
tools:layout="@layout/dialog_post_view">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/locationFragment"
android:name="awais.instagrabber.fragments.LocationFragment"
android:label=""
tools:layout="@layout/fragment_location">
<argument
android:name="locationId"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/hashTagFragment"
android:name="awais.instagrabber.fragments.HashTagFragment"
android:label=""
tools:layout="@layout/fragment_hashtag">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/commentsViewerFragment"
android:name="awais.instagrabber.fragments.comments.CommentsViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_comments">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of profile fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/profile_non_top"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="@string/profile"
tools:layout="@layout/fragment_profile">
<argument
android:name="username"
android:defaultValue=""
app:argType="string"
app:nullable="true" />
<action
android:id="@+id/action_to_saved"
app:destination="@id/savedViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_follow_viewer"
app:destination="@id/followViewerFragment" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_notifications"
app:destination="@id/notifications_viewer_non_top" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/savedViewerFragment"
android:name="awais.instagrabber.fragments.SavedViewerFragment"
android:label="Saved"
tools:layout="@layout/fragment_saved">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="type"
app:argType="awais.instagrabber.models.enums.PostItemType"
app:nullable="false" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/followViewerFragment"
android:name="awais.instagrabber.fragments.FollowViewerFragment"
android:label=""
tools:layout="@layout/fragment_followers_viewer">
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="isFollowersList"
app:argType="boolean"
app:nullable="false" />
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/likesViewerFragment"
android:name="awais.instagrabber.fragments.LikesViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_likes">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of favorites fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/favorites_non_top"
android:name="awais.instagrabber.fragments.FavoritesFragment"
android:label="@string/title_favorites"
tools:layout="@layout/fragment_favorites">
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
</fragment>
<!-- Copy of notification viewer fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/notifications_viewer_non_top"
android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer">
<argument
android:name="type"
android:defaultValue="notif"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment>
<fragment
android:id="@+id/savedCollectionsFragment"
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
android:label="@string/saved"
tools:layout="@layout/fragment_saved_collections">
<argument
android:name="isSaving"
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_to_collection_posts"
app:destination="@id/collectionPostsFragment" />
</fragment>
<fragment
android:id="@+id/collectionPostsFragment"
android:name="awais.instagrabber.fragments.CollectionPostsFragment"
tools:layout="@layout/fragment_collection_posts">
<argument
android:name="savedCollection"
app:argType="awais.instagrabber.repositories.responses.saved.SavedCollection" />
<argument
android:name="titleColor"
app:argType="integer" />
<argument
android:name="backgroundColor"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/user_search"
android:name="awais.instagrabber.fragments.UserSearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_user_search">
<argument
android:name="multiple"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="title"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="action_label"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="show_groups"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="search_mode"
android:defaultValue="USER_SEARCH"
app:argType="awais.instagrabber.fragments.UserSearchMode" />
<argument
android:name="hideUserIds"
android:defaultValue="@null"
app:argType="long[]"
app:nullable="true" />
<argument
android:name="hideThreadIds"
android:defaultValue="@null"
app:argType="string[]"
app:nullable="true" />
</fragment>
<include app:graph="@navigation/settings_nav_graph" />
<fragment
android:id="@+id/storyListViewerFragment"
android:name="awais.instagrabber.fragments.StoryListViewerFragment"
android:label="Stories"
tools:layout="@layout/fragment_story_list_viewer">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
</navigation> </navigation>

View File

@ -5,121 +5,491 @@
android:id="@+id/notification_viewer_nav_graph" android:id="@+id/notification_viewer_nav_graph"
app:startDestination="@id/notificationsViewer"> app:startDestination="@id/notificationsViewer">
<include app:graph="@navigation/profile_nav_graph" />
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_notificationsViewerFragment"
app:destination="@id/notificationsViewer">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
</action>
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/comments_nav_graph">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
<include app:graph="@navigation/saved_nav_graph" />
<action
android:id="@+id/action_global_savedCollectionsFragment"
app:destination="@id/saved_nav_graph">
<argument
android:name="isSaving"
app:argType="boolean" />
</action>
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<include app:graph="@navigation/user_search_nav_graph" />
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search_nav_graph" />
<fragment
android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
</fragment>
<fragment <fragment
android:id="@+id/notificationsViewer" android:id="@+id/notificationsViewer"
android:name="awais.instagrabber.fragments.NotificationsViewerFragment" android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications" android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer"> tools:layout="@layout/fragment_notifications_viewer">
<argument <argument
android:name="type" android:name="type"
android:defaultValue="notif"
app:argType="string" app:argType="string"
app:nullable="false" app:nullable="false" />
android:defaultValue="notif"/>
<argument <argument
android:name="targetId" android:name="targetId"
android:defaultValue="0L" android:defaultValue="0L"
app:argType="long" /> app:argType="long" />
<action <action
android:id="@+id/action_notifications_to_story" android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" /> app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment> </fragment>
<fragment
android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment"
android:label="StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
</fragment>
<fragment <fragment
android:id="@+id/postViewFragment" android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment" android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post" android:label="@string/post"
tools:layout="@layout/dialog_post_view" /> tools:layout="@layout/dialog_post_view">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/locationFragment"
android:name="awais.instagrabber.fragments.LocationFragment"
android:label=""
tools:layout="@layout/fragment_location">
<argument
android:name="locationId"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/hashTagFragment"
android:name="awais.instagrabber.fragments.HashTagFragment"
android:label=""
tools:layout="@layout/fragment_hashtag">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/commentsViewerFragment"
android:name="awais.instagrabber.fragments.comments.CommentsViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_comments">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of profile fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/profile_non_top"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="@string/profile"
tools:layout="@layout/fragment_profile">
<argument
android:name="username"
android:defaultValue=""
app:argType="string"
app:nullable="true" />
<action
android:id="@+id/action_to_saved"
app:destination="@id/savedViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_follow_viewer"
app:destination="@id/followViewerFragment" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_notifications"
app:destination="@id/notifications_viewer_non_top" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/savedViewerFragment"
android:name="awais.instagrabber.fragments.SavedViewerFragment"
android:label="Saved"
tools:layout="@layout/fragment_saved">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="type"
app:argType="awais.instagrabber.models.enums.PostItemType"
app:nullable="false" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/followViewerFragment"
android:name="awais.instagrabber.fragments.FollowViewerFragment"
android:label=""
tools:layout="@layout/fragment_followers_viewer">
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="isFollowersList"
app:argType="boolean"
app:nullable="false" />
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/likesViewerFragment"
android:name="awais.instagrabber.fragments.LikesViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_likes">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of notification viewer fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/notifications_viewer_non_top"
android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer">
<argument
android:name="type"
android:defaultValue="notif"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment>
<fragment
android:id="@+id/savedCollectionsFragment"
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
android:label="@string/saved"
tools:layout="@layout/fragment_saved_collections">
<argument
android:name="isSaving"
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_to_collection_posts"
app:destination="@id/collectionPostsFragment" />
</fragment>
<fragment
android:id="@+id/collectionPostsFragment"
android:name="awais.instagrabber.fragments.CollectionPostsFragment"
tools:layout="@layout/fragment_collection_posts">
<argument
android:name="savedCollection"
app:argType="awais.instagrabber.repositories.responses.saved.SavedCollection" />
<argument
android:name="titleColor"
app:argType="integer" />
<argument
android:name="backgroundColor"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/user_search"
android:name="awais.instagrabber.fragments.UserSearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_user_search">
<argument
android:name="multiple"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="title"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="action_label"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="show_groups"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="search_mode"
android:defaultValue="USER_SEARCH"
app:argType="awais.instagrabber.fragments.UserSearchMode" />
<argument
android:name="hideUserIds"
android:defaultValue="@null"
app:argType="long[]"
app:nullable="true" />
<argument
android:name="hideThreadIds"
android:defaultValue="@null"
app:argType="string[]"
app:nullable="true" />
</fragment>
</navigation> </navigation>

View File

@ -5,218 +5,563 @@
android:id="@+id/profile_nav_graph" android:id="@+id/profile_nav_graph"
app:startDestination="@id/profileFragment"> app:startDestination="@id/profileFragment">
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/comments_nav_graph">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
<include app:graph="@navigation/hashtag_nav_graph" />
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashtag_nav_graph">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
</action>
<include app:graph="@navigation/location_nav_graph" />
<action
android:id="@+id/action_global_locationFragment"
app:destination="@id/location_nav_graph">
<argument
android:name="locationId"
app:argType="long" />
</action>
<action
android:id="@+id/action_global_notificationsViewerFragment"
app:destination="@id/notificationsViewer">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
<action
android:id="@+id/action_notifications_to_story"
app:destination="@id/storyViewerFragment" />
</action>
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<include app:graph="@navigation/user_search_nav_graph" />
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search_nav_graph" />
<fragment
android:id="@+id/notificationsViewer"
android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer">
<argument
android:name="type"
android:defaultValue="notif"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
</fragment>
<include app:graph="@navigation/saved_nav_graph" />
<action
android:id="@+id/action_global_savedCollectionsFragment"
app:destination="@id/saved_nav_graph">
<argument
android:name="isSaving"
app:argType="boolean" />
</action>
<action
android:id="@+id/action_global_search"
app:destination="@id/searchFragment" />
<fragment <fragment
android:id="@+id/profileFragment" android:id="@+id/profileFragment"
android:name="awais.instagrabber.fragments.main.ProfileFragment" android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="@string/profile" android:label="@string/profile"
tools:layout="@layout/fragment_profile"> tools:layout="@layout/fragment_profile">
<argument <argument
android:name="username" android:name="username"
android:defaultValue="" android:defaultValue=""
app:argType="string" app:argType="string"
app:nullable="true" /> app:nullable="true" />
<action <action
android:id="@+id/action_profileFragment_to_savedViewerFragment" android:id="@+id/action_to_saved"
app:destination="@id/savedViewerFragment" /> app:destination="@id/savedViewerFragment" />
<action <action
android:id="@+id/action_profileFragment_to_followViewerFragment" android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_follow_viewer"
app:destination="@id/followViewerFragment" /> app:destination="@id/followViewerFragment" />
<action <action
android:id="@+id/action_profileFragment_to_storyViewerFragment" android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" /> app:destination="@id/storyViewerFragment" />
<action <action
android:id="@+id/action_profileFragment_to_dMThreadFragment" android:id="@+id/action_to_post"
app:destination="@id/directMessagesThreadFragment" /> app:destination="@id/postViewFragment" />
</fragment>
<fragment
android:id="@+id/savedViewerFragment"
android:name="awais.instagrabber.fragments.SavedViewerFragment"
android:label="Saved"
tools:layout="@layout/fragment_saved">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="type"
app:argType="awais.instagrabber.models.enums.PostItemType"
app:nullable="false" />
</fragment>
<fragment
android:id="@+id/followViewerFragment"
android:name="awais.instagrabber.fragments.FollowViewerFragment"
android:label=""
tools:layout="@layout/fragment_followers_viewer">
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="isFollowersList"
app:argType="boolean"
app:nullable="false" />
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<action <action
android:id="@+id/action_followViewerFragment_to_profileFragment" android:id="@+id/action_to_comments"
app:destination="@id/profileFragment"> app:destination="@id/commentsViewerFragment" />
<argument
android:name="username" <action
android:defaultValue="" android:id="@+id/action_to_user_search"
app:argType="string" app:destination="@id/user_search" />
app:nullable="true" />
</action> <action
android:id="@+id/action_to_notifications"
app:destination="@id/notifications_viewer_non_top" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment> </fragment>
<dialog
android:id="@+id/post_loading_dialog"
android:name="awais.instagrabber.dialogs.PostLoadingDialogFragment"
android:label="@string/direct_download_loading">
<argument
android:name="shortCode"
app:argType="string" />
<deepLink app:uri="barinsta://post/{shortCode}" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</dialog>
<fragment <fragment
android:id="@+id/storyViewerFragment" android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment" android:name="awais.instagrabber.fragments.StoryViewerFragment"
android:label="StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer"> tools:layout="@layout/fragment_story_viewer">
<argument <argument
android:name="options" android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" /> app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
</fragment> </fragment>
<fragment <fragment
android:id="@+id/searchFragment" android:id="@+id/searchFragment"
android:name="awais.instagrabber.fragments.search.SearchFragment" android:name="awais.instagrabber.fragments.search.SearchFragment"
android:label="@string/search" android:label="@string/search"
tools:layout="@layout/fragment_search" /> tools:layout="@layout/fragment_search">
<deepLink app:uri="barinsta://search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
</fragment>
<fragment <fragment
android:id="@+id/postViewFragment" android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment" android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post" android:label="@string/post"
tools:layout="@layout/dialog_post_view" /> tools:layout="@layout/dialog_post_view">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/locationFragment"
android:name="awais.instagrabber.fragments.LocationFragment"
android:label=""
tools:layout="@layout/fragment_location">
<argument
android:name="locationId"
app:argType="long" />
<deepLink app:uri="barinsta://location/{locationId}" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/hashTagFragment"
android:name="awais.instagrabber.fragments.HashTagFragment"
android:label=""
tools:layout="@layout/fragment_hashtag">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
<deepLink app:uri="barinsta://hashtag/{hashtag}" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/commentsViewerFragment"
android:name="awais.instagrabber.fragments.comments.CommentsViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_comments">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_likes"
app:destination="@id/likesViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of profile fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/profile_non_top"
android:name="awais.instagrabber.fragments.main.ProfileFragment"
android:label="@string/profile"
tools:layout="@layout/fragment_profile">
<argument
android:name="username"
android:defaultValue=""
app:argType="string"
app:nullable="true" />
<deepLink app:uri="barinsta://profile/{username}" />
<action
android:id="@+id/action_to_saved"
app:destination="@id/savedViewerFragment" />
<action
android:id="@+id/action_to_saved_collections"
app:destination="@id/savedCollectionsFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_follow_viewer"
app:destination="@id/followViewerFragment" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_user_search"
app:destination="@id/user_search" />
<action
android:id="@+id/action_to_notifications"
app:destination="@id/notifications_viewer_non_top" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/savedViewerFragment"
android:name="awais.instagrabber.fragments.SavedViewerFragment"
android:label="Saved"
tools:layout="@layout/fragment_saved">
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="type"
app:argType="awais.instagrabber.models.enums.PostItemType"
app:nullable="false" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/followViewerFragment"
android:name="awais.instagrabber.fragments.FollowViewerFragment"
android:label=""
tools:layout="@layout/fragment_followers_viewer">
<argument
android:name="profileId"
app:argType="long" />
<argument
android:name="isFollowersList"
app:argType="boolean"
app:nullable="false" />
<argument
android:name="username"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<dialog
android:id="@+id/likesViewerFragment"
android:name="awais.instagrabber.fragments.LikesViewerFragment"
android:label="Comments"
tools:layout="@layout/fragment_likes">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</dialog>
<!-- Copy of notification viewer fragment tag -->
<!-- Required to get back arrow in action bar -->
<!-- See https://issuetracker.google.com/issues/192395936 -->
<fragment
android:id="@+id/notifications_viewer_non_top"
android:name="awais.instagrabber.fragments.NotificationsViewerFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications_viewer">
<argument
android:name="type"
android:defaultValue="notif"
app:argType="string"
app:nullable="false" />
<argument
android:name="targetId"
android:defaultValue="0L"
app:argType="long" />
<deepLink app:uri="barinsta://notifications/{type}?targetId={targetId}" />
<action
android:id="@+id/action_to_story"
app:destination="@id/storyViewerFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
</fragment>
<fragment
android:id="@+id/savedCollectionsFragment"
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
android:label="@string/saved"
tools:layout="@layout/fragment_saved_collections">
<argument
android:name="isSaving"
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_to_collection_posts"
app:destination="@id/collectionPostsFragment" />
</fragment>
<fragment
android:id="@+id/collectionPostsFragment"
android:name="awais.instagrabber.fragments.CollectionPostsFragment"
tools:layout="@layout/fragment_collection_posts">
<argument
android:name="savedCollection"
app:argType="awais.instagrabber.repositories.responses.saved.SavedCollection" />
<argument
android:name="titleColor"
app:argType="integer" />
<argument
android:name="backgroundColor"
app:argType="integer" />
<action
android:id="@+id/action_to_comments"
app:destination="@id/commentsViewerFragment" />
<action
android:id="@+id/action_to_hashtag"
app:destination="@id/hashTagFragment" />
<action
android:id="@+id/action_to_location"
app:destination="@id/locationFragment" />
<action
android:id="@+id/action_to_post"
app:destination="@id/postViewFragment" />
<action
android:id="@+id/action_to_profile"
app:destination="@id/profile_non_top" />
</fragment>
<fragment
android:id="@+id/user_search"
android:name="awais.instagrabber.fragments.UserSearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_user_search">
<argument
android:name="multiple"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="title"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="action_label"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="show_groups"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="search_mode"
android:defaultValue="USER_SEARCH"
app:argType="awais.instagrabber.fragments.UserSearchMode" />
<argument
android:name="hideUserIds"
android:defaultValue="@null"
app:argType="long[]"
app:nullable="true" />
<argument
android:name="hideThreadIds"
android:defaultValue="@null"
app:argType="string[]"
app:nullable="true" />
</fragment>
</navigation> </navigation>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation>
<!--android:id="@+id/root"-->
<!--app:startDestination="@id/profile_nav_graph"-->
<!--<include app:graph="@navigation/dm_nav_graph" />-->
<!--<include app:graph="@navigation/feed_nav_graph" />-->
<!--<include app:graph="@navigation/profile_nav_graph" />-->
<!--<include app:graph="@navigation/discover_nav_graph" />-->
<!--<include app:graph="@navigation/more_nav_graph" />-->
</navigation>

View File

@ -1,119 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/saved_nav_graph"
app:startDestination="@id/savedCollectionsFragment">
<action
android:id="@+id/action_global_hashTagFragment"
app:destination="@id/hashtag_nav_graph">
<argument
android:name="hashtag"
app:argType="string"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<action
android:id="@+id/action_global_locationFragment"
app:destination="@id/location_nav_graph">
<argument
android:name="locationId"
app:argType="long" />
</action>
<include app:graph="@navigation/comments_nav_graph" />
<action
android:id="@+id/action_global_commentsViewerFragment"
app:destination="@id/comments_nav_graph">
<argument
android:name="shortCode"
app:argType="string"
app:nullable="false" />
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="postUserId"
app:argType="long" />
</action>
<include app:graph="@navigation/likes_nav_graph" />
<action
android:id="@+id/action_global_likesViewerFragment"
app:destination="@id/likes_nav_graph">
<argument
android:name="postId"
app:argType="string"
app:nullable="false" />
<argument
android:name="isComment"
app:argType="boolean"
app:nullable="false" />
</action>
<action
android:id="@+id/action_global_post_view"
app:destination="@id/postViewFragment">
<argument
android:name="media"
app:argType="awais.instagrabber.repositories.responses.Media"
app:nullable="false" />
<argument
android:name="position"
app:argType="integer" />
</action>
<include app:graph="@navigation/user_search_nav_graph" />
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search_nav_graph" />
<fragment
android:id="@+id/savedCollectionsFragment"
android:name="awais.instagrabber.fragments.SavedCollectionsFragment"
android:label="@string/saved"
tools:layout="@layout/fragment_saved_collections">
<argument
android:name="isSaving"
android:defaultValue="false"
app:argType="boolean" />
<action
android:id="@+id/action_savedCollectionsFragment_to_collectionPostsFragment"
app:destination="@id/collectionPostsFragment" />
</fragment>
<fragment
android:id="@+id/collectionPostsFragment"
android:name="awais.instagrabber.fragments.CollectionPostsFragment"
tools:layout="@layout/fragment_collection_posts">
<argument
android:name="savedCollection"
app:argType="awais.instagrabber.repositories.responses.saved.SavedCollection" />
<argument
android:name="titleColor"
app:argType="integer" />
<argument
android:name="backgroundColor"
app:argType="integer" />
</fragment>
<fragment
android:id="@+id/postViewFragment"
android:name="awais.instagrabber.fragments.PostViewV2Fragment"
android:label="@string/post"
tools:layout="@layout/dialog_post_view" />
</navigation>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/settings_nav_graph"
app:startDestination="@id/settingsPreferencesFragment">
<fragment
android:id="@+id/settingsPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.SettingsPreferencesFragment"
android:label="@string/action_settings">
<action
android:id="@+id/action_settings_to_theme"
app:destination="@id/themePreferencesFragment" />
<action
android:id="@+id/action_settings_to_locale"
app:destination="@id/localePreferencesFragment" />
<action
android:id="@+id/action_settings_to_general"
app:destination="@id/generalPreferencesFragment" />
<action
android:id="@+id/action_settings_to_downloads"
app:destination="@id/downloadsPreferencesFragment" />
<action
android:id="@+id/action_settings_to_dm"
app:destination="@id/DMPreferencesFragment" />
<action
android:id="@+id/action_settings_to_stories"
app:destination="@id/storiesPreferencesFragment" />
<action
android:id="@+id/action_settings_to_notifications"
app:destination="@id/notificationsPreferencesFragment" />
<action
android:id="@+id/action_settings_to_post"
app:destination="@id/postPreferencesFragment" />
</fragment>
<fragment
android:id="@+id/aboutFragment"
android:name="awais.instagrabber.fragments.settings.AboutFragment"
android:label="@string/action_about" />
<fragment
android:id="@+id/themePreferencesFragment"
android:name="awais.instagrabber.fragments.settings.ThemePreferencesFragment"
android:label="@string/theme_settings" />
<fragment
android:id="@+id/favoritesFragment"
android:name="awais.instagrabber.fragments.FavoritesFragment"
android:label="@string/title_favorites" />
<fragment
android:id="@+id/backupPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.BackupPreferencesFragment"
android:label="@string/backup_and_restore" />
<fragment
android:id="@+id/localePreferencesFragment"
android:name="awais.instagrabber.fragments.settings.LocalePreferencesFragment"
android:label="@string/pref_category_locale" />
<fragment
android:id="@+id/generalPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.GeneralPreferencesFragment"
android:label="@string/pref_category_general" />
<fragment
android:id="@+id/downloadsPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.DownloadsPreferencesFragment"
android:label="@string/pref_category_downloads" />
<fragment
android:id="@+id/DMPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.DMPreferencesFragment"
android:label="@string/pref_category_dm" />
<fragment
android:id="@+id/storiesPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.StoriesPreferencesFragment"
android:label="@string/pref_category_stories" />
<fragment
android:id="@+id/notificationsPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.NotificationsPreferencesFragment"
android:label="@string/pref_category_notifications" />
<fragment
android:id="@+id/postPreferencesFragment"
android:name="awais.instagrabber.fragments.settings.PostPreferencesFragment"
android:label="@string/pref_category_post" />
</navigation>

View File

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/story_list_nav_graph"
app:startDestination="@id/storyListViewerFragment">
<action
android:id="@+id/action_global_profileFragment"
app:destination="@id/profile_nav_graph">
<argument
android:name="username"
app:argType="string"
app:nullable="true" />
</action>
<fragment
android:id="@+id/storyListViewerFragment"
android:name="awais.instagrabber.fragments.StoryListViewerFragment"
android:label="Stories"
tools:layout="@layout/fragment_story_list_viewer">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
<action
android:id="@+id/action_storyListFragment_to_storyViewerFragment"
app:destination="@id/storyViewerFragment" />
</fragment>
<action
android:id="@+id/action_global_storyListViewerFragment"
app:destination="@id/storyListViewerFragment">
<argument
android:name="type"
app:argType="string"
app:nullable="false" />
</action>
<fragment
android:id="@+id/storyViewerFragment"
android:name="awais.instagrabber.fragments.StoryViewerFragment"
tools:layout="@layout/fragment_story_viewer">
<argument
android:name="options"
app:argType="awais.instagrabber.repositories.requests.StoryViewerOptions" />
</fragment>
</navigation>

View File

@ -1,80 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/user_search_nav_graph"
app:startDestination="@id/user_search">
<fragment
android:id="@+id/user_search"
android:name="awais.instagrabber.fragments.UserSearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_user_search">
<argument
android:name="multiple"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="title"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="action_label"
android:defaultValue="@null"
app:argType="string"
app:nullable="true" />
<argument
android:name="show_groups"
android:defaultValue="false"
app:argType="boolean" />
<argument
android:name="search_mode"
android:defaultValue="USER_SEARCH"
app:argType="awais.instagrabber.fragments.UserSearchFragment$SearchMode" />
<argument
android:name="hideUserIds"
android:defaultValue="@null"
app:argType="long[]"
app:nullable="true" />
<argument
android:name="hideThreadIds"
android:defaultValue="@null"
app:argType="string[]"
app:nullable="true" />
</fragment>
<action
android:id="@+id/action_global_user_search"
app:destination="@id/user_search" />
<!--<action-->
<!-- android:id="@+id/action_global_user_search"-->
<!-- app:destination="@id/user_search_nav_graph">-->
<!-- <argument-->
<!-- android:name="multiple"-->
<!-- app:argType="boolean" />-->
<!-- <argument-->
<!-- android:name="title"-->
<!-- app:argType="string"-->
<!-- app:nullable="true" />-->
<!-- <argument-->
<!-- android:name="action_label"-->
<!-- app:argType="string"-->
<!-- app:nullable="true" />-->
<!-- <argument-->
<!-- android:name="hideUserIds"-->
<!-- app:argType="long[]" />-->
<!--</action>-->
</navigation>

View File

@ -91,61 +91,23 @@
<item>HH:mm:ss</item> <item>HH:mm:ss</item>
<item>H:mm:ss</item> <item>H:mm:ss</item>
</string-array> </string-array>
<array name="main_nav_graph_root_ids">
<array name="logged_in_nav_root_ids">
<item>@id/direct_messages_nav_graph</item> <item>@id/direct_messages_nav_graph</item>
<item>@id/feed_nav_graph</item> <item>@id/feed_nav_graph</item>
<item>@id/profile_nav_graph</item> <item>@id/profile_nav_graph</item>
<item>@id/discover_nav_graph</item> <item>@id/discover_nav_graph</item>
<item>@id/more_nav_graph</item> <item>@id/more_nav_graph</item>
<!-- New graphs should go below -->
<item>@id/favorites_nav_graph</item> <item>@id/favorites_nav_graph</item>
<item>@id/notification_viewer_nav_graph</item> <item>@id/notification_viewer_nav_graph</item>
</array> </array>
<!-- Nav graphs should correspond 1-to-1 with the above nav graph ids -->
<array name="main_nav_graphs"> <array name="anon_nav_root_ids">
<item>@navigation/direct_messages_nav_graph</item> <item>@id/favorites_nav_graph</item>
<item>@navigation/feed_nav_graph</item> <item>@id/profile_nav_graph</item>
<item>@navigation/profile_nav_graph</item> <item>@id/more_nav_graph</item>
<item>@navigation/discover_nav_graph</item>
<item>@navigation/more_nav_graph</item>
<item>@navigation/favorites_nav_graph</item>
<item>@navigation/notification_viewer_nav_graph</item>
</array> </array>
<!-- Titles should correspond 1-to-1 with the above nav graphs -->
<string-array name="main_nav_titles" translatable="false">
<item>@string/title_dm</item>
<item>@string/feed</item>
<item>@string/profile</item>
<item>@string/title_discover</item>
<item>@string/more</item>
<item>@string/title_favorites</item>
<item>@string/title_notifications</item>
</string-array>
<!-- Drawable should correspond 1-to-1 with the above titles -->
<array name="main_nav_drawables" translatable="false">
<item>@drawable/ic_message_24</item>
<item>@drawable/ic_home_24</item>
<item>@drawable/ic_person_24</item>
<item>@drawable/ic_explore_24</item>
<item>@drawable/ic_more_horiz_24</item>
<item>@drawable/ic_star_24</item>
<item>@drawable/ic_not_liked</item>
</array>
<!-- fragmentIds should correspond 1-to-1 with the above drawabled -->
<!-- these are the start destination of the corresponding nav graphs -->
<array name="main_nav_start_dest_frag_ids" translatable="false">
<item>@id/directMessagesInboxFragment</item>
<item>@id/feedFragment</item>
<item>@id/profileFragment</item>
<item>@id/discoverFragment</item>
<item>@id/morePreferencesFragment</item>
<item>@id/favoritesFragment</item>
<item>@id/notificationsViewer</item>
</array>
<!--<array name="logged_out_main_nav_graphs">-->
<!-- <item>@navigation/profile_nav_graph</item>-->
<!-- <item>@navigation/more_nav_graph</item>-->
<!--</array>-->
<string-array name="light_themes" translatable="false"> <string-array name="light_themes" translatable="false">
<item>@string/light_white_theme</item> <item>@string/light_white_theme</item>
<item>@string/light_barinsta_theme</item> <item>@string/light_barinsta_theme</item>

View File

@ -8,6 +8,9 @@
<item name="share_dm" type="id" /> <item name="share_dm" type="id" />
<item name="download_current" type="id" /> <item name="download_current" type="id" />
<item name="download_all" type="id" /> <item name="download_all" type="id" />
<item name="root_nav_graph" type="id" />
<!-- story stickers --> <!-- story stickers -->
<item name="mentions" type="id" /> <item name="mentions" type="id" />
<item name="spotify" type="id" /> <item name="spotify" type="id" />

View File

@ -520,4 +520,7 @@
<string name="share_via_dm">Share via DM</string> <string name="share_via_dm">Share via DM</string>
<string name="share_link">Share link…</string> <string name="share_link">Share link…</string>
<string name="slide_to_cancel">Slide to Cancel</string> <string name="slide_to_cancel">Slide to Cancel</string>
<string name="disable_screen_transitions">Disable screen transitions</string>
<string name="invalid_format">Invalid format</string>
<string name="no_directory_picker_activity">No activity found to select directory</string>
</resources> </resources>

View File

@ -8,9 +8,7 @@ import awais.instagrabber.models.enums.FavoriteType
import awais.instagrabber.repositories.* import awais.instagrabber.repositories.*
import awais.instagrabber.repositories.responses.* import awais.instagrabber.repositories.responses.*
import awais.instagrabber.repositories.responses.directmessages.* import awais.instagrabber.repositories.responses.directmessages.*
import awais.instagrabber.repositories.responses.stories.ArchiveResponse import awais.instagrabber.repositories.responses.stories.*
import awais.instagrabber.repositories.responses.stories.ReelsTrayResponse
import awais.instagrabber.repositories.responses.stories.StoryStickerResponse
open class UserServiceAdapter : UserService { open class UserServiceAdapter : UserService {
override suspend fun getUserInfo(uid: Long): WrappedUser { override suspend fun getUserInfo(uid: Long): WrappedUser {
@ -47,7 +45,7 @@ open class FriendshipServiceAdapter : FriendshipService {
} }
open class StoriesServiceAdapter : StoriesService { open class StoriesServiceAdapter : StoriesService {
override suspend fun fetch(mediaId: Long): String { override suspend fun fetch(mediaId: Long): StoryMediaResponse {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@ -63,11 +61,19 @@ open class StoriesServiceAdapter : StoriesService {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override suspend fun getUserStory(url: String): String { override suspend fun getReelsMedia(id: String): ReelsMediaResponse {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override suspend fun respondToSticker(storyId: String, stickerId: String, action: String, form: Map<String, String>): StoryStickerResponse { override suspend fun getStories(type: String, id: String): ReelsResponse {
TODO("Not yet implemented")
}
override suspend fun getUserStories(id: Long): ReelsResponse {
TODO("Not yet implemented")
}
override suspend fun respondToSticker(storyId: Long, stickerId: Long, action: String, form: Map<String, String>): StoryStickerResponse {
TODO("Not yet implemented") TODO("Not yet implemented")
} }

View File

@ -15,7 +15,6 @@ import awais.instagrabber.repositories.requests.StoryViewerOptions
import awais.instagrabber.repositories.responses.FriendshipStatus import awais.instagrabber.repositories.responses.FriendshipStatus
import awais.instagrabber.repositories.responses.User import awais.instagrabber.repositories.responses.User
import awais.instagrabber.repositories.responses.stories.Story import awais.instagrabber.repositories.responses.stories.Story
import awais.instagrabber.repositories.responses.stories.StoryMedia
import awais.instagrabber.webservices.* import awais.instagrabber.webservices.*
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.json.JSONException import org.json.JSONException
@ -320,13 +319,13 @@ internal class ProfileFragmentViewModelTest {
"username" to testPublicUser.username "username" to testPublicUser.username
) )
) )
val testUserStories = listOf(StoryMedia()) val testUserStories = Story()
val testUserHighlights = listOf(Story()) val testUserHighlights = listOf(Story())
val userRepository = object : UserRepository(UserServiceAdapter()) { val userRepository = object : UserRepository(UserServiceAdapter()) {
override suspend fun getUsernameInfo(username: String): User = testPublicUser override suspend fun getUsernameInfo(username: String): User = testPublicUser
} }
val storiesRepository = object : StoriesRepository(StoriesServiceAdapter()) { val storiesRepository = object : StoriesRepository(StoriesServiceAdapter()) {
override suspend fun getStories(options: StoryViewerOptions): List<StoryMedia> = testUserStories override suspend fun getStories(options: StoryViewerOptions): Story = testUserStories
override suspend fun fetchHighlights(profileId: Long): List<Story> = testUserHighlights override suspend fun fetchHighlights(profileId: Long): List<Story> = testUserHighlights
} }
val viewModel = ProfileFragmentViewModel( val viewModel = ProfileFragmentViewModel(

View File

@ -1,5 +1,8 @@
buildscript { buildscript {
ext.kotlin_version = '1.5.20' ext{
kotlin_version = '1.5.20'
nav_version = "2.4.0-alpha04"
}
repositories { repositories {
google() google()
@ -9,7 +12,6 @@ buildscript {
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.2.2' classpath 'com.android.tools.build:gradle:4.2.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
def nav_version = "2.3.5"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB