Merge pull request #1542 from ammargitham/feature/multistack-navigation
Migrate to navigation 2.4.0renovate/androidx.fragment-fragment-ktx-1.x
-
7app/build.gradle
-
12app/src/main/java/awais/instagrabber/activities/DirectorySelectActivity.java
-
470app/src/main/java/awais/instagrabber/activities/MainActivity.kt
-
90app/src/main/java/awais/instagrabber/customviews/BarinstaFragmentNavigator.kt
-
14app/src/main/java/awais/instagrabber/customviews/BarinstaNavHostFragment.kt
-
1app/src/main/java/awais/instagrabber/customviews/ChatMessageLayout.java
-
75app/src/main/java/awais/instagrabber/customviews/FragmentNavigatorWithDefaultAnimations.java
-
60app/src/main/java/awais/instagrabber/customviews/NavHostFragmentWithDefaultAnimations.java
-
77app/src/main/java/awais/instagrabber/customviews/PostsRecyclerView.java
-
29app/src/main/java/awais/instagrabber/customviews/helpers/PostFetcher.java
-
78app/src/main/java/awais/instagrabber/dialogs/PostLoadingDialogFragment.kt
-
6app/src/main/java/awais/instagrabber/dialogs/PostsLayoutPreferencesDialogFragment.java
-
20app/src/main/java/awais/instagrabber/dialogs/TabOrderPreferenceDialogFragment.java
-
113app/src/main/java/awais/instagrabber/dialogs/TimeSettingsDialog.java
-
74app/src/main/java/awais/instagrabber/fragments/CollectionPostsFragment.java
-
47app/src/main/java/awais/instagrabber/fragments/FavoritesFragment.kt
-
57app/src/main/java/awais/instagrabber/fragments/FollowViewerFragment.kt
-
121app/src/main/java/awais/instagrabber/fragments/HashTagFragment.java
-
20app/src/main/java/awais/instagrabber/fragments/LikesViewerFragment.java
-
112app/src/main/java/awais/instagrabber/fragments/LocationFragment.java
-
34app/src/main/java/awais/instagrabber/fragments/NotificationsViewerFragment.java
-
95app/src/main/java/awais/instagrabber/fragments/PostViewV2Fragment.java
-
5app/src/main/java/awais/instagrabber/fragments/SavedCollectionsFragment.java
-
60app/src/main/java/awais/instagrabber/fragments/SavedViewerFragment.java
-
28app/src/main/java/awais/instagrabber/fragments/StoryListViewerFragment.java
-
22app/src/main/java/awais/instagrabber/fragments/StoryViewerFragment.kt
-
50app/src/main/java/awais/instagrabber/fragments/TopicPostsFragment.java
-
328app/src/main/java/awais/instagrabber/fragments/UserSearchFragment.java
-
252app/src/main/java/awais/instagrabber/fragments/UserSearchFragment.kt
-
7app/src/main/java/awais/instagrabber/fragments/UserSearchMode.kt
-
20app/src/main/java/awais/instagrabber/fragments/comments/CommentsViewerFragment.java
-
27app/src/main/java/awais/instagrabber/fragments/comments/Helper.java
-
37app/src/main/java/awais/instagrabber/fragments/comments/RepliesFragment.java
-
59app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageInboxFragment.kt
-
57app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageSettingsFragment.kt
-
204app/src/main/java/awais/instagrabber/fragments/directmessages/DirectMessageThreadFragment.java
-
2app/src/main/java/awais/instagrabber/fragments/directmessages/DirectPendingInboxFragment.kt
-
26app/src/main/java/awais/instagrabber/fragments/main/DiscoverFragment.java
-
78app/src/main/java/awais/instagrabber/fragments/main/FeedFragment.java
-
175app/src/main/java/awais/instagrabber/fragments/main/ProfileFragment.kt
-
23app/src/main/java/awais/instagrabber/fragments/search/SearchFragment.java
-
38app/src/main/java/awais/instagrabber/fragments/settings/DownloadsPreferencesFragment.java
-
36app/src/main/java/awais/instagrabber/fragments/settings/GeneralPreferencesFragment.java
-
136app/src/main/java/awais/instagrabber/fragments/settings/MorePreferencesFragment.java
-
1app/src/main/java/awais/instagrabber/fragments/settings/PreferenceKeys.kt
-
6app/src/main/java/awais/instagrabber/models/Tab.kt
-
23app/src/main/java/awais/instagrabber/utils/BarinstaDeepLinkHelper.kt
-
1app/src/main/java/awais/instagrabber/utils/Constants.kt
-
12app/src/main/java/awais/instagrabber/utils/DateUtils.kt
-
33app/src/main/java/awais/instagrabber/utils/KeywordsFilterUtils.kt
-
494app/src/main/java/awais/instagrabber/utils/NavigationExtensions.java
-
174app/src/main/java/awais/instagrabber/utils/NavigationHelper.kt
-
4app/src/main/java/awais/instagrabber/utils/SettingsHelper.kt
-
11app/src/main/java/awais/instagrabber/utils/TextUtils.kt
-
120app/src/main/java/awais/instagrabber/utils/Utils.java
-
6app/src/main/java/awais/instagrabber/viewmodels/FollowViewModel.kt
-
97app/src/main/java/awais/instagrabber/viewmodels/MediaViewModel.java
-
67app/src/main/java/awais/instagrabber/viewmodels/ProfileFragmentViewModel.kt
-
13app/src/main/java/awais/instagrabber/viewmodels/StoryFragmentViewModel.kt
-
8app/src/main/java/awais/instagrabber/viewmodels/UserSearchViewModel.java
-
1app/src/main/java/thoughtbot/expandableadapter/ExpandableGroup.java
-
5app/src/main/res/layout/activity_main.xml
-
52app/src/main/res/layout/dialog_post_layout_preferences.xml
-
36app/src/main/res/layout/dialog_time_settings.xml
-
1app/src/main/res/layout/fragment_followers_viewer.xml
-
28app/src/main/res/menu/bottom_nav_menu.xml
-
78app/src/main/res/navigation/comments_nav_graph.xml
-
546app/src/main/res/navigation/direct_messages_nav_graph.xml
-
525app/src/main/res/navigation/discover_nav_graph.xml
-
494app/src/main/res/navigation/favorites_nav_graph.xml
-
538app/src/main/res/navigation/feed_nav_graph.xml
-
129app/src/main/res/navigation/hashtag_nav_graph.xml
-
48app/src/main/res/navigation/likes_nav_graph.xml
-
128app/src/main/res/navigation/location_nav_graph.xml
-
618app/src/main/res/navigation/more_nav_graph.xml
-
492app/src/main/res/navigation/notification_viewer_nav_graph.xml
-
581app/src/main/res/navigation/profile_nav_graph.xml
-
12app/src/main/res/navigation/root_nav_graph.xml
-
119app/src/main/res/navigation/saved_nav_graph.xml
-
80app/src/main/res/navigation/settings_nav_graph.xml
-
48app/src/main/res/navigation/story_list_nav_graph.xml
-
80app/src/main/res/navigation/user_search_nav_graph.xml
-
54app/src/main/res/values/arrays.xml
-
3app/src/main/res/values/ids.xml
-
3app/src/main/res/values/strings.xml
-
18app/src/test/java/awais/instagrabber/common/Adapters.kt
-
5app/src/test/java/awais/instagrabber/viewmodels/ProfileFragmentViewModelTest.kt
-
6build.gradle
-
BINfastlane/metadata/android/en-US/images/phoneScreenshots/1.jpg
-
BINfastlane/metadata/android/en-US/images/phoneScreenshots/2.jpg
-
BINfastlane/metadata/android/en-US/images/phoneScreenshots/3.jpg
-
BINfastlane/metadata/android/en-US/images/phoneScreenshots/4.jpg
-
BINfastlane/metadata/android/en-US/images/phoneScreenshots/5.jpg
-
BINfastlane/metadata/android/en-US/images/phoneScreenshots/6.jpg
@ -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" |
|||
} |
|||
} |
@ -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) |
|||
) |
|||
} |
|||
} |
@ -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(); |
|||
} |
|||
} |
@ -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())); |
|||
} |
|||
} |
@ -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() |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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 |
|||
} |
|||
} |
@ -0,0 +1,7 @@ |
|||
package awais.instagrabber.fragments |
|||
|
|||
enum class UserSearchMode(val mode: String) { |
|||
USER_SEARCH("user_name"), |
|||
RAVEN("raven"), |
|||
RESHARE("reshare"); |
|||
} |
@ -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() |
@ -1,247 +1,247 @@ |
|||
package awais.instagrabber.utils; |
|||
|
|||
import android.annotation.SuppressLint; |
|||
import android.content.Intent; |
|||
import android.util.Log; |
|||
import android.util.SparseArray; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.fragment.app.Fragment; |
|||
import androidx.fragment.app.FragmentManager; |
|||
import androidx.fragment.app.FragmentTransaction; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.navigation.NavController; |
|||
import androidx.navigation.NavDestination; |
|||
import androidx.navigation.NavGraph; |
|||
import androidx.navigation.fragment.NavHostFragment; |
|||
|
|||
import com.google.android.material.bottomnavigation.BottomNavigationView; |
|||
|
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.R; |
|||
import awais.instagrabber.customviews.NavHostFragmentWithDefaultAnimations; |
|||
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> |
|||
* from architecture-components-samples. Some modifications have been done, check git history. |
|||
*/ |
|||
public class NavigationExtensions { |
|||
private static final String TAG = NavigationExtensions.class.getSimpleName(); |
|||
private static String selectedItemTag; |
|||
private static boolean isOnFirstFragment; |
|||
|
|||
@NonNull |
|||
public static LiveData<NavController> setupWithNavController(@NonNull final BottomNavigationView bottomNavigationView, |
|||
@NonNull List<Integer> navGraphIds, |
|||
@NonNull final FragmentManager fragmentManager, |
|||
final int containerId, |
|||
@NonNull Intent intent, |
|||
final int firstFragmentGraphIndex) { |
|||
final SparseArray<String> graphIdToTagMap = new SparseArray<>(); |
|||
final MutableLiveData<NavController> selectedNavController = new MutableLiveData<>(); |
|||
int firstFragmentGraphId = 0; |
|||
for (int i = 0; i < navGraphIds.size(); i++) { |
|||
final int navGraphId = navGraphIds.get(i); |
|||
final String fragmentTag = getFragmentTag(navGraphId); |
|||
final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId); |
|||
final NavController navController = navHostFragment.getNavController(); |
|||
final int graphId = navController.getGraph().getId(); |
|||
if (i == firstFragmentGraphIndex) { |
|||
firstFragmentGraphId = graphId; |
|||
} |
|||
graphIdToTagMap.put(graphId, fragmentTag); |
|||
if (bottomNavigationView.getSelectedItemId() == graphId) { |
|||
selectedNavController.setValue(navHostFragment.getNavController()); |
|||
attachNavHostFragment(fragmentManager, navHostFragment, i == firstFragmentGraphIndex); |
|||
} else { |
|||
detachNavHostFragment(fragmentManager, navHostFragment); |
|||
} |
|||
} |
|||
selectedItemTag = graphIdToTagMap.get(bottomNavigationView.getSelectedItemId()); |
|||
final String firstFragmentTag = graphIdToTagMap.get(firstFragmentGraphId); |
|||
isOnFirstFragment = selectedItemTag != null && selectedItemTag.equals(firstFragmentTag); |
|||
bottomNavigationView.setOnItemSelectedListener(item -> { |
|||
if (fragmentManager.isStateSaved()) { |
|||
return false; |
|||
} |
|||
String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId()); |
|||
String tag = selectedItemTag; |
|||
if (tag != null && !tag.equals(newlySelectedItemTag)) { |
|||
fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE); |
|||
Fragment fragment = fragmentManager.findFragmentByTag(newlySelectedItemTag); |
|||
if (fragment == null) { |
|||
return false; |
|||
// throw new RuntimeException("null cannot be cast to non-null NavHostFragment"); |
|||
} |
|||
final NavHostFragment selectedFragment = (NavHostFragment) fragment; |
|||
if (firstFragmentTag != null && !firstFragmentTag.equals(newlySelectedItemTag)) { |
|||
FragmentTransaction fragmentTransaction = fragmentManager |
|||
.beginTransaction() |
|||
.setCustomAnimations( |
|||
R.anim.nav_default_enter_anim, |
|||
R.anim.nav_default_exit_anim, |
|||
R.anim.nav_default_pop_enter_anim, |
|||
R.anim.nav_default_pop_exit_anim |
|||
) |
|||
.attach(selectedFragment) |
|||
.setPrimaryNavigationFragment(selectedFragment); |
|||
for (int i = 0; i < graphIdToTagMap.size(); i++) { |
|||
final int key = graphIdToTagMap.keyAt(i); |
|||
final String fragmentTagForId = graphIdToTagMap.get(key); |
|||
if (!fragmentTagForId.equals(newlySelectedItemTag)) { |
|||
final Fragment fragmentByTag = fragmentManager.findFragmentByTag(firstFragmentTag); |
|||
if (fragmentByTag == null) { |
|||
continue; |
|||
} |
|||
fragmentTransaction.detach(fragmentByTag); |
|||
} |
|||
} |
|||
fragmentTransaction.addToBackStack(firstFragmentTag) |
|||
.setReorderingAllowed(true) |
|||
.commit(); |
|||
} |
|||
selectedItemTag = newlySelectedItemTag; |
|||
isOnFirstFragment = selectedItemTag.equals(firstFragmentTag); |
|||
selectedNavController.setValue(selectedFragment.getNavController()); |
|||
return true; |
|||
} |
|||
return false; |
|||
}); |
|||
setupItemReselected(bottomNavigationView, graphIdToTagMap, fragmentManager); |
|||
setupDeepLinks(bottomNavigationView, navGraphIds, fragmentManager, containerId, intent); |
|||
final int finalFirstFragmentGraphId = firstFragmentGraphId; |
|||
fragmentManager.addOnBackStackChangedListener(() -> { |
|||
if (!isOnFirstFragment) { |
|||
if (firstFragmentTag == null) { |
|||
return; |
|||
} |
|||
if (!isOnBackStack(fragmentManager, firstFragmentTag)) { |
|||
bottomNavigationView.setSelectedItemId(finalFirstFragmentGraphId); |
|||
} |
|||
} |
|||
|
|||
final NavController navController = selectedNavController.getValue(); |
|||
if (navController != null && navController.getCurrentDestination() == null) { |
|||
final NavGraph navControllerGraph = navController.getGraph(); |
|||
navController.navigate(navControllerGraph.getId()); |
|||
} |
|||
}); |
|||
return selectedNavController; |
|||
} |
|||
|
|||
private static NavHostFragment obtainNavHostFragment(final FragmentManager fragmentManager, |
|||
final String fragmentTag, |
|||
final int navGraphId, |
|||
final int containerId) { |
|||
final NavHostFragment existingFragment = (NavHostFragment) fragmentManager.findFragmentByTag(fragmentTag); |
|||
if (existingFragment != null) { |
|||
return existingFragment; |
|||
} |
|||
final NavHostFragment navHostFragment = NavHostFragmentWithDefaultAnimations.create(navGraphId); |
|||
fragmentManager.beginTransaction() |
|||
.setReorderingAllowed(true) |
|||
.add(containerId, navHostFragment, fragmentTag) |
|||
.commitNow(); |
|||
return navHostFragment; |
|||
} |
|||
|
|||
private static void attachNavHostFragment(final FragmentManager fragmentManager, |
|||
final NavHostFragment navHostFragment, |
|||
final boolean isPrimaryNavFragment) { |
|||
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction() |
|||
.attach(navHostFragment); |
|||
if (isPrimaryNavFragment) { |
|||
fragmentTransaction.setPrimaryNavigationFragment(navHostFragment); |
|||
} |
|||
fragmentTransaction.commitNow(); |
|||
} |
|||
|
|||
private static void detachNavHostFragment(final FragmentManager fragmentManager, final NavHostFragment navHostFragment) { |
|||
fragmentManager.beginTransaction() |
|||
.detach(navHostFragment) |
|||
.commitNow(); |
|||
} |
|||
|
|||
@SuppressLint("RestrictedApi") |
|||
private static void setupItemReselected(final BottomNavigationView bottomNavigationView, |
|||
final SparseArray<String> graphIdToTagMap, |
|||
final FragmentManager fragmentManager) { |
|||
bottomNavigationView.setOnItemReselectedListener(item -> { |
|||
final String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId()); |
|||
final Fragment fragmentByTag = fragmentManager.findFragmentByTag(newlySelectedItemTag); |
|||
if (fragmentByTag == null) { |
|||
return; |
|||
} |
|||
final NavHostFragment selectedFragment = (NavHostFragment) fragmentByTag; |
|||
final NavController navController = selectedFragment.getNavController(); |
|||
final NavGraph navControllerGraph = navController.getGraph(); |
|||
final NavDestination currentDestination = navController.getCurrentDestination(); |
|||
final int startDestination = navControllerGraph.getStartDestination(); |
|||
int backStackSize = navController.getBackStack().size(); |
|||
if (currentDestination != null && backStackSize == 2 && currentDestination.getId() == startDestination) { |
|||
// scroll to top |
|||
final List<Fragment> fragments = selectedFragment.getChildFragmentManager().getFragments(); |
|||
if (fragments.isEmpty()) return; |
|||
final Fragment fragment = fragments.get(0); |
|||
if (fragment instanceof FeedFragment) { |
|||
((FeedFragment) fragment).scrollToTop(); |
|||
} |
|||
return; |
|||
} |
|||
final boolean popped = navController.popBackStack(startDestination, false); |
|||
backStackSize = navController.getBackStack().size(); |
|||
if (!popped || backStackSize > 2) { |
|||
try { |
|||
// try loop pop |
|||
do { |
|||
navController.popBackStack(); |
|||
backStackSize = navController.getBackStack().size(); |
|||
} while (backStackSize > 2); |
|||
} catch (Exception e) { |
|||
Log.e(TAG, "setupItemReselected: ", e); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private static void setupDeepLinks(final BottomNavigationView bottomNavigationView, |
|||
final List<Integer> navGraphIds, |
|||
final FragmentManager fragmentManager, |
|||
final int containerId, |
|||
final Intent intent) { |
|||
for (int i = 0; i < navGraphIds.size(); i++) { |
|||
final int navGraphId = navGraphIds.get(i); |
|||
final String fragmentTag = getFragmentTag(navGraphId); |
|||
final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId); |
|||
if (navHostFragment.getNavController().handleDeepLink(intent)) { |
|||
final int selectedItemId = bottomNavigationView.getSelectedItemId(); |
|||
NavController navController = navHostFragment.getNavController(); |
|||
NavGraph graph = navController.getGraph(); |
|||
if (selectedItemId != graph.getId()) { |
|||
navController = navHostFragment.getNavController(); |
|||
graph = navController.getGraph(); |
|||
bottomNavigationView.setSelectedItemId(graph.getId()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static boolean isOnBackStack(final FragmentManager fragmentManager, final String backStackName) { |
|||
int backStackCount = fragmentManager.getBackStackEntryCount(); |
|||
for (int i = 0; i < backStackCount; i++) { |
|||
final FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i); |
|||
final String name = backStackEntry.getName(); |
|||
if (name != null && name.equals(backStackName)) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
private static String getFragmentTag(final int index) { |
|||
return "bottomNavigation#" + index; |
|||
} |
|||
} |
|||
// package awais.instagrabber.utils; |
|||
// |
|||
// import android.annotation.SuppressLint; |
|||
// import android.content.Intent; |
|||
// import android.util.Log; |
|||
// import android.util.SparseArray; |
|||
// |
|||
// import androidx.annotation.NonNull; |
|||
// import androidx.fragment.app.Fragment; |
|||
// import androidx.fragment.app.FragmentManager; |
|||
// import androidx.fragment.app.FragmentTransaction; |
|||
// import androidx.lifecycle.LiveData; |
|||
// import androidx.lifecycle.MutableLiveData; |
|||
// import androidx.navigation.NavController; |
|||
// import androidx.navigation.NavDestination; |
|||
// import androidx.navigation.NavGraph; |
|||
// import androidx.navigation.fragment.NavHostFragment; |
|||
// |
|||
// import com.google.android.material.bottomnavigation.BottomNavigationView; |
|||
// |
|||
// import java.util.List; |
|||
// |
|||
// import awais.instagrabber.R; |
|||
// import awais.instagrabber.customviews.NavHostFragmentWithDefaultAnimations; |
|||
// 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> |
|||
// * from architecture-components-samples. Some modifications have been done, check git history. |
|||
// */ |
|||
// public class NavigationExtensions { |
|||
// private static final String TAG = NavigationExtensions.class.getSimpleName(); |
|||
// private static String selectedItemTag; |
|||
// private static boolean isOnFirstFragment; |
|||
// |
|||
// @NonNull |
|||
// public static LiveData<NavController> setupWithNavController(@NonNull final BottomNavigationView bottomNavigationView, |
|||
// @NonNull List<Integer> navGraphIds, |
|||
// @NonNull final FragmentManager fragmentManager, |
|||
// final int containerId, |
|||
// @NonNull Intent intent, |
|||
// final int firstFragmentGraphIndex) { |
|||
// final SparseArray<String> graphIdToTagMap = new SparseArray<>(); |
|||
// final MutableLiveData<NavController> selectedNavController = new MutableLiveData<>(); |
|||
// int firstFragmentGraphId = 0; |
|||
// for (int i = 0; i < navGraphIds.size(); i++) { |
|||
// final int navGraphId = navGraphIds.get(i); |
|||
// final String fragmentTag = getFragmentTag(navGraphId); |
|||
// final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId); |
|||
// final NavController navController = navHostFragment.getNavController(); |
|||
// final int graphId = navController.getGraph().getId(); |
|||
// if (i == firstFragmentGraphIndex) { |
|||
// firstFragmentGraphId = graphId; |
|||
// } |
|||
// graphIdToTagMap.put(graphId, fragmentTag); |
|||
// if (bottomNavigationView.getSelectedItemId() == graphId) { |
|||
// selectedNavController.setValue(navHostFragment.getNavController()); |
|||
// attachNavHostFragment(fragmentManager, navHostFragment, i == firstFragmentGraphIndex); |
|||
// } else { |
|||
// detachNavHostFragment(fragmentManager, navHostFragment); |
|||
// } |
|||
// } |
|||
// selectedItemTag = graphIdToTagMap.get(bottomNavigationView.getSelectedItemId()); |
|||
// final String firstFragmentTag = graphIdToTagMap.get(firstFragmentGraphId); |
|||
// isOnFirstFragment = selectedItemTag != null && selectedItemTag.equals(firstFragmentTag); |
|||
// bottomNavigationView.setOnItemSelectedListener(item -> { |
|||
// if (fragmentManager.isStateSaved()) { |
|||
// return false; |
|||
// } |
|||
// String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId()); |
|||
// String tag = selectedItemTag; |
|||
// if (tag != null && !tag.equals(newlySelectedItemTag)) { |
|||
// fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE); |
|||
// Fragment fragment = fragmentManager.findFragmentByTag(newlySelectedItemTag); |
|||
// if (fragment == null) { |
|||
// return false; |
|||
// // throw new RuntimeException("null cannot be cast to non-null NavHostFragment"); |
|||
// } |
|||
// final NavHostFragment selectedFragment = (NavHostFragment) fragment; |
|||
// if (firstFragmentTag != null && !firstFragmentTag.equals(newlySelectedItemTag)) { |
|||
// FragmentTransaction fragmentTransaction = fragmentManager |
|||
// .beginTransaction() |
|||
// .setCustomAnimations( |
|||
// R.anim.nav_default_enter_anim, |
|||
// R.anim.nav_default_exit_anim, |
|||
// R.anim.nav_default_pop_enter_anim, |
|||
// R.anim.nav_default_pop_exit_anim |
|||
// ) |
|||
// .attach(selectedFragment) |
|||
// .setPrimaryNavigationFragment(selectedFragment); |
|||
// for (int i = 0; i < graphIdToTagMap.size(); i++) { |
|||
// final int key = graphIdToTagMap.keyAt(i); |
|||
// final String fragmentTagForId = graphIdToTagMap.get(key); |
|||
// if (!fragmentTagForId.equals(newlySelectedItemTag)) { |
|||
// final Fragment fragmentByTag = fragmentManager.findFragmentByTag(firstFragmentTag); |
|||
// if (fragmentByTag == null) { |
|||
// continue; |
|||
// } |
|||
// fragmentTransaction.detach(fragmentByTag); |
|||
// } |
|||
// } |
|||
// fragmentTransaction.addToBackStack(firstFragmentTag) |
|||
// .setReorderingAllowed(true) |
|||
// .commit(); |
|||
// } |
|||
// selectedItemTag = newlySelectedItemTag; |
|||
// isOnFirstFragment = selectedItemTag.equals(firstFragmentTag); |
|||
// selectedNavController.setValue(selectedFragment.getNavController()); |
|||
// return true; |
|||
// } |
|||
// return false; |
|||
// }); |
|||
// setupItemReselected(bottomNavigationView, graphIdToTagMap, fragmentManager); |
|||
// setupDeepLinks(bottomNavigationView, navGraphIds, fragmentManager, containerId, intent); |
|||
// final int finalFirstFragmentGraphId = firstFragmentGraphId; |
|||
// fragmentManager.addOnBackStackChangedListener(() -> { |
|||
// if (!isOnFirstFragment) { |
|||
// if (firstFragmentTag == null) { |
|||
// return; |
|||
// } |
|||
// if (!isOnBackStack(fragmentManager, firstFragmentTag)) { |
|||
// bottomNavigationView.setSelectedItemId(finalFirstFragmentGraphId); |
|||
// } |
|||
// } |
|||
// |
|||
// final NavController navController = selectedNavController.getValue(); |
|||
// if (navController != null && navController.getCurrentDestination() == null) { |
|||
// final NavGraph navControllerGraph = navController.getGraph(); |
|||
// navController.navigate(navControllerGraph.getId()); |
|||
// } |
|||
// }); |
|||
// return selectedNavController; |
|||
// } |
|||
// |
|||
// private static NavHostFragment obtainNavHostFragment(final FragmentManager fragmentManager, |
|||
// final String fragmentTag, |
|||
// final int navGraphId, |
|||
// final int containerId) { |
|||
// final NavHostFragment existingFragment = (NavHostFragment) fragmentManager.findFragmentByTag(fragmentTag); |
|||
// if (existingFragment != null) { |
|||
// return existingFragment; |
|||
// } |
|||
// final NavHostFragment navHostFragment = NavHostFragmentWithDefaultAnimations.create(navGraphId); |
|||
// fragmentManager.beginTransaction() |
|||
// .setReorderingAllowed(true) |
|||
// .add(containerId, navHostFragment, fragmentTag) |
|||
// .commitNow(); |
|||
// return navHostFragment; |
|||
// } |
|||
// |
|||
// private static void attachNavHostFragment(final FragmentManager fragmentManager, |
|||
// final NavHostFragment navHostFragment, |
|||
// final boolean isPrimaryNavFragment) { |
|||
// final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction() |
|||
// .attach(navHostFragment); |
|||
// if (isPrimaryNavFragment) { |
|||
// fragmentTransaction.setPrimaryNavigationFragment(navHostFragment); |
|||
// } |
|||
// fragmentTransaction.commitNow(); |
|||
// } |
|||
// |
|||
// private static void detachNavHostFragment(final FragmentManager fragmentManager, final NavHostFragment navHostFragment) { |
|||
// fragmentManager.beginTransaction() |
|||
// .detach(navHostFragment) |
|||
// .commitNow(); |
|||
// } |
|||
// |
|||
// @SuppressLint("RestrictedApi") |
|||
// private static void setupItemReselected(final BottomNavigationView bottomNavigationView, |
|||
// final SparseArray<String> graphIdToTagMap, |
|||
// final FragmentManager fragmentManager) { |
|||
// bottomNavigationView.setOnItemReselectedListener(item -> { |
|||
// final String newlySelectedItemTag = graphIdToTagMap.get(item.getItemId()); |
|||
// final Fragment fragmentByTag = fragmentManager.findFragmentByTag(newlySelectedItemTag); |
|||
// if (fragmentByTag == null) { |
|||
// return; |
|||
// } |
|||
// final NavHostFragment selectedFragment = (NavHostFragment) fragmentByTag; |
|||
// final NavController navController = selectedFragment.getNavController(); |
|||
// final NavGraph navControllerGraph = navController.getGraph(); |
|||
// final NavDestination currentDestination = navController.getCurrentDestination(); |
|||
// final int startDestination = navControllerGraph.getStartDestination(); |
|||
// int backStackSize = navController.getBackStack().size(); |
|||
// if (currentDestination != null && backStackSize == 2 && currentDestination.getId() == startDestination) { |
|||
// // scroll to top |
|||
// final List<Fragment> fragments = selectedFragment.getChildFragmentManager().getFragments(); |
|||
// if (fragments.isEmpty()) return; |
|||
// final Fragment fragment = fragments.get(0); |
|||
// if (fragment instanceof FeedFragment) { |
|||
// ((FeedFragment) fragment).scrollToTop(); |
|||
// } |
|||
// return; |
|||
// } |
|||
// final boolean popped = navController.popBackStack(startDestination, false); |
|||
// backStackSize = navController.getBackStack().size(); |
|||
// if (!popped || backStackSize > 2) { |
|||
// try { |
|||
// // try loop pop |
|||
// do { |
|||
// navController.popBackStack(); |
|||
// backStackSize = navController.getBackStack().size(); |
|||
// } while (backStackSize > 2); |
|||
// } catch (Exception e) { |
|||
// Log.e(TAG, "setupItemReselected: ", e); |
|||
// } |
|||
// } |
|||
// }); |
|||
// } |
|||
// |
|||
// private static void setupDeepLinks(final BottomNavigationView bottomNavigationView, |
|||
// final List<Integer> navGraphIds, |
|||
// final FragmentManager fragmentManager, |
|||
// final int containerId, |
|||
// final Intent intent) { |
|||
// for (int i = 0; i < navGraphIds.size(); i++) { |
|||
// final int navGraphId = navGraphIds.get(i); |
|||
// final String fragmentTag = getFragmentTag(navGraphId); |
|||
// final NavHostFragment navHostFragment = obtainNavHostFragment(fragmentManager, fragmentTag, navGraphId, containerId); |
|||
// if (navHostFragment.getNavController().handleDeepLink(intent)) { |
|||
// final int selectedItemId = bottomNavigationView.getSelectedItemId(); |
|||
// NavController navController = navHostFragment.getNavController(); |
|||
// NavGraph graph = navController.getGraph(); |
|||
// if (selectedItemId != graph.getId()) { |
|||
// navController = navHostFragment.getNavController(); |
|||
// graph = navController.getGraph(); |
|||
// bottomNavigationView.setSelectedItemId(graph.getId()); |
|||
// } |
|||
// } |
|||
// } |
|||
// } |
|||
// |
|||
// private static boolean isOnBackStack(final FragmentManager fragmentManager, final String backStackName) { |
|||
// int backStackCount = fragmentManager.getBackStackEntryCount(); |
|||
// for (int i = 0; i < backStackCount; i++) { |
|||
// final FragmentManager.BackStackEntry backStackEntry = fragmentManager.getBackStackEntryAt(i); |
|||
// final String name = backStackEntry.getName(); |
|||
// if (name != null && name.equals(backStackName)) { |
|||
// return true; |
|||
// } |
|||
// } |
|||
// return false; |
|||
// } |
|||
// |
|||
// private static String getFragmentTag(final int index) { |
|||
// return "bottomNavigation#" + index; |
|||
// } |
|||
// } |
@ -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 |
|||
} |
@ -1,19 +1,108 @@ |
|||
package awais.instagrabber.viewmodels; |
|||
|
|||
import android.util.Log; |
|||
|
|||
import androidx.annotation.NonNull; |
|||
import androidx.lifecycle.LiveData; |
|||
import androidx.lifecycle.MutableLiveData; |
|||
import androidx.lifecycle.ViewModel; |
|||
import androidx.lifecycle.ViewModelProvider; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
import awais.instagrabber.customviews.helpers.PostFetcher; |
|||
import awais.instagrabber.fragments.settings.PreferenceKeys; |
|||
import awais.instagrabber.interfaces.FetchListener; |
|||
import awais.instagrabber.repositories.responses.Media; |
|||
import awais.instagrabber.utils.KeywordsFilterUtilsKt; |
|||
|
|||
import static awais.instagrabber.utils.Utils.settingsHelper; |
|||
|
|||
public class MediaViewModel extends ViewModel { |
|||
private MutableLiveData<List<Media>> list; |
|||
private static final String TAG = MediaViewModel.class.getSimpleName(); |
|||
|
|||
private boolean refresh = true; |
|||
|
|||
private final PostFetcher postFetcher; |
|||
private final MutableLiveData<List<Media>> list = new MutableLiveData<>(); |
|||
|
|||
public MediaViewModel(@NonNull final PostFetcher.PostFetchService postFetchService) { |
|||
final FetchListener<List<Media>> fetchListener = new FetchListener<List<Media>>() { |
|||
@Override |
|||
public void onResult(final List<Media> result) { |
|||
if (refresh) { |
|||
list.postValue(filterResult(result, true)); |
|||
refresh = false; |
|||
return; |
|||
} |
|||
list.postValue(filterResult(result, false)); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(final Throwable t) { |
|||
Log.e(TAG, "onFailure: ", t); |
|||
} |
|||
}; |
|||
postFetcher = new PostFetcher(postFetchService, fetchListener); |
|||
} |
|||
|
|||
public MutableLiveData<List<Media>> getList() { |
|||
if (list == null) { |
|||
list = new MutableLiveData<>(); |
|||
@NonNull |
|||
private List<Media> filterResult(final List<Media> result, final boolean isRefresh) { |
|||
final List<Media> models = list.getValue(); |
|||
final List<Media> modelsCopy = models == null || isRefresh ? new ArrayList<>() : new ArrayList<>(models); |
|||
if (settingsHelper.getBoolean(PreferenceKeys.TOGGLE_KEYWORD_FILTER)) { |
|||
final List<String> keywords = new ArrayList<>(settingsHelper.getStringSet(PreferenceKeys.KEYWORD_FILTERS)); |
|||
final List<Media> filter = KeywordsFilterUtilsKt.filter(keywords, result); |
|||
if (filter != null) { |
|||
modelsCopy.addAll(filter); |
|||
} |
|||
return modelsCopy; |
|||
} |
|||
modelsCopy.addAll(result); |
|||
return modelsCopy; |
|||
} |
|||
|
|||
public LiveData<List<Media>> getList() { |
|||
return list; |
|||
} |
|||
|
|||
public boolean hasMore() { |
|||
return postFetcher.hasMore(); |
|||
} |
|||
|
|||
public void fetch() { |
|||
postFetcher.fetch(); |
|||
} |
|||
|
|||
public void reset() { |
|||
postFetcher.reset(); |
|||
} |
|||
|
|||
public boolean isFetching() { |
|||
return postFetcher.isFetching(); |
|||
} |
|||
|
|||
public void refresh() { |
|||
refresh = true; |
|||
reset(); |
|||
fetch(); |
|||
} |
|||
|
|||
public static class ViewModelFactory implements ViewModelProvider.Factory { |
|||
|
|||
@NonNull |
|||
private final PostFetcher.PostFetchService postFetchService; |
|||
|
|||
public ViewModelFactory(@NonNull final PostFetcher.PostFetchService postFetchService) { |
|||
this.postFetchService = postFetchService; |
|||
} |
|||
|
|||
@NonNull |
|||
@Override |
|||
public <T extends ViewModel> T create(@NonNull final Class<T> modelClass) { |
|||
//noinspection unchecked |
|||
return (T) new MediaViewModel(postFetchService); |
|||
} |
|||
} |
|||
} |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -1,205 +1,539 @@ |
|||
<?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/more_nav_graph" |
|||
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" /> |
|||
<fragment |
|||
android:id="@+id/morePreferencesFragment" |
|||
android:name="awais.instagrabber.fragments.settings.MorePreferencesFragment" |
|||
android:label="@string/more"> |
|||
|
|||
<action |
|||
android:id="@+id/action_to_settings" |
|||
app:destination="@id/settings_nav_graph" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_to_about" |
|||
app:destination="@id/aboutFragment" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_to_favorites" |
|||
app:destination="@id/favorites_non_top" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_to_backup" |
|||
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 |
|||
android:id="@+id/storyViewerFragment" |
|||
android:name="awais.instagrabber.fragments.StoryViewerFragment" |
|||
android:label="StoryViewerFragment" |
|||
tools:layout="@layout/fragment_story_viewer"> |
|||
|
|||
<action |
|||
android:id="@+id/action_global_commentsViewerFragment" |
|||
app:destination="@id/comments_nav_graph"> |
|||
<argument |
|||
android:name="shortCode" |
|||
app:argType="string" |
|||
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="postId" |
|||
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="postUserId" |
|||
app:argType="long" /> |
|||
</action> |
|||
android:name="shortCode" |
|||
app:argType="string" |
|||
app:nullable="false" /> |
|||
|
|||
<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> |
|||
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"> |
|||
|
|||
<action |
|||
android:id="@+id/action_global_profileFragment" |
|||
app:destination="@id/profile_nav_graph"> |
|||
<argument |
|||
android:name="username" |
|||
android:defaultValue="" |
|||
app:argType="string" |
|||
app:nullable="true" /> |
|||
</action> |
|||
|
|||
<action |
|||
android:id="@+id/action_global_hashTagFragment" |
|||
app:destination="@id/hashtag_nav_graph"> |
|||
<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="hashtag" |
|||
android:name="username" |
|||
app:argType="string" |
|||
app:nullable="false" /> |
|||
</action> |
|||
|
|||
<action |
|||
android:id="@+id/action_global_locationFragment" |
|||
app:destination="@id/location_nav_graph"> |
|||
<argument |
|||
android:name="locationId" |
|||
android:name="profileId" |
|||
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:argType="awais.instagrabber.models.enums.PostItemType" |
|||
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" /> |
|||
<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="targetId" |
|||
android:defaultValue="0L" |
|||
android:name="profileId" |
|||
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" |
|||
android:name="isFollowersList" |
|||
app:argType="boolean" |
|||
app:nullable="false" /> |
|||
|
|||
<argument |
|||
android:name="position" |
|||
app:argType="integer" /> |
|||
</action> |
|||
android:name="username" |
|||
app:argType="string" |
|||
app:nullable="false" /> |
|||
|
|||
<include app:graph="@navigation/user_search_nav_graph" /> |
|||
<action |
|||
android:id="@+id/action_to_profile" |
|||
app:destination="@id/profile_non_top" /> |
|||
</fragment> |
|||
|
|||
<action |
|||
android:id="@+id/action_global_user_search" |
|||
app:destination="@id/user_search_nav_graph" /> |
|||
<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" /> |
|||
|
|||
<fragment |
|||
android:id="@+id/morePreferencesFragment" |
|||
android:name="awais.instagrabber.fragments.settings.MorePreferencesFragment" |
|||
android:label="@string/more"> |
|||
<action |
|||
android:id="@+id/action_morePreferencesFragment_to_settingsPreferencesFragment" |
|||
app:destination="@id/settingsPreferencesFragment" /> |
|||
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_morePreferencesFragment_to_aboutFragment" |
|||
app:destination="@id/aboutFragment" /> |
|||
android:id="@+id/action_to_profile" |
|||
app:destination="@id/profile_non_top" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_morePreferencesFragment_to_favoritesFragment" |
|||
app:destination="@id/favoritesFragment" /> |
|||
android:id="@+id/action_to_location" |
|||
app:destination="@id/locationFragment" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_morePreferencesFragment_to_backupPreferencesFragment" |
|||
app:destination="@id/backupPreferencesFragment" /> |
|||
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/settingsPreferencesFragment" |
|||
android:name="awais.instagrabber.fragments.settings.SettingsPreferencesFragment" |
|||
android:label="@string/action_settings"> |
|||
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_settings_to_theme" |
|||
app:destination="@id/themePreferencesFragment" /> |
|||
android:id="@+id/action_to_story" |
|||
app:destination="@id/storyViewerFragment" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_settings_to_locale" |
|||
app:destination="@id/localePreferencesFragment" /> |
|||
android:id="@+id/action_to_profile" |
|||
app:destination="@id/profile_non_top" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_settings_to_general" |
|||
app:destination="@id/generalPreferencesFragment" /> |
|||
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_settings_to_downloads" |
|||
app:destination="@id/downloadsPreferencesFragment" /> |
|||
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_settings_to_dm" |
|||
app:destination="@id/DMPreferencesFragment" /> |
|||
android:id="@+id/action_to_comments" |
|||
app:destination="@id/commentsViewerFragment" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_settings_to_stories" |
|||
app:destination="@id/storiesPreferencesFragment" /> |
|||
android:id="@+id/action_to_hashtag" |
|||
app:destination="@id/hashTagFragment" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_settings_to_notifications" |
|||
app:destination="@id/notificationsPreferencesFragment" /> |
|||
android:id="@+id/action_to_location" |
|||
app:destination="@id/locationFragment" /> |
|||
|
|||
<action |
|||
android:id="@+id/action_settings_to_post" |
|||
app:destination="@id/postPreferencesFragment" /> |
|||
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/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" /> |
|||
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/postViewFragment" |
|||
android:name="awais.instagrabber.fragments.PostViewV2Fragment" |
|||
android:label="@string/post" /> |
|||
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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
After ![]() Width: 1 | Height: 960 | Size: 1.5 KiB |
After ![]() Width: 1 | Height: 960 | Size: 1.5 KiB |
After ![]() Width: 1 | Height: 960 | Size: 1.5 KiB |
After ![]() Width: 1 | Height: 960 | Size: 1.5 KiB |
After ![]() Width: 1 | Height: 960 | Size: 1.5 KiB |
After ![]() Width: 1 | Height: 960 | Size: 1.5 KiB |
xxxxxxxxxx