NestedFragment navigation With ViewPager2 - android

I'm developing an app which is made of SingleActivity&Fragments.
The project flow is below.
Project flow
The navgraph is below.
Navigation flow
RegisterPhotoFragment has a photo button. If i click the button, it show the CameraOrGalleryBottomSheetDialog by using navigate(actionid).
In the CameraOrGalleryBottomSheetDialog, I can take a photo or get a image url from gallery.
For the process, I'm using setFragmentResultListener in RegisterPhotoFragment and setFragmentResult in CameraOrGalleryBottomSheetDialog.
But the problem is, I can't get the image uri from CameraOrGalleryBottomSheetDialog in RegisterPhotoFragment and I realized that RegisterPhotoFragment is just made by using ViewPager2 in BeginningFragment not navigate(anctionid). So, i didn't use navigate(actionid) to go to the RegisterPhotoFragment.
Therefore, I changed the navigation flow like below.
Second Navigation Flow
I restarted my app and it happened a crash.
java.lang.IllegalArgumentException: Navigation action/destination mypackage:id/action_registerPhotoFragment_to_cameraOrGalleryBottomSheetDialog cannot be found from the current destination Destination(mypackage:id/beginningFragment) label=BeginningFragment class=,mypackage.presentation.ui.beginning.BeginningFragment
because i added the RegisterPhotoFragment res/navigation/navGraph but i didn't use navigate(actionid). If i use naviagte(actionid), I can only see the screen of RegisterPhotoFragment. So, i didn't use it.
Finally, I decided to make a RegisterPhotoNavHostFragment and it has only FragmentContainerView and the Navigation Flow is changed again like below.
Sadly, I thought it works but it didn't. the same crash occured.
How can i make it? is this idea wrong?

Okay, I solved this problem. One thing that i missed was that i have to create the RegisterPhotoNavHostFragment instead of RegisterPhotoFragment when i create the FragmentStateAdapter for ViewPager2 in BeginningFragment.
before
private fun getFragmentsForViewPager2() = arrayListOf(
RegisterPhotoFragment.getNewInstance(),
RegisterNicknameFragment.getNewInstance(),
RegisterGenderFragment.getNewInstance()
)
after
private fun getFragmentsForViewPager2() = arrayListOf(
RegisterPhotoNavHostFragment.getNewInstance(),
RegisterNicknameFragment.getNewInstance(),
RegisterGenderFragment.getNewInstance()
)
the idea works well.

Related

Jetpack Compose Navigation Include start destination screen on navigation stack

I'm recently struggling a lot with some complex logic for deeplink & navigation management with Jetpack Compose Navigation. My actual problem is that when I handle my deeplinks and call navController.navigate(uri) the navigation is working properly but the startDestination screen from the graph is never added to the stack. So its created as:
Actual: Deeplink arrives > Deeplink Screen
Expected: Deeplink arrives > Open Home > Deeplink Screen
My Home screen is the startDestination of the graph where Deeplink Screen belongs to.
Some important notes:
I can't use navController.handleDeeplink because im doing my own deeplink management and this one doesn't work well with dynamic links(firebase) or external web uris. For this reason I always use navController.navigate(uri)
This solution is a workaround but I dont want to use it because im looking for a generic solution to work with nested graphs. Not a huge when clause. It's weird that compose don't support a synthetic stack and this is the "best" workaround. So possibly im missing something.
I don't want to copy/paste and re-implement the NavController. This is a solution aswell but a disgusting one.
Im attaching the code where I call my navigate method with the navOptions. But its more a theory question about Jetpack compose or what I could be missing from the library:
LaunchedEffect(pendingDeeplinkUri.value ) { // Avoid multiple executions using the intent as key
pendingDeeplinkUri.value?.let { uri ->
Timber.d("[DeepLinkManagement] LaunchedEffect pendingDeeplink $uri")
if (navController.graph.hasDeepLink(uri)) { //Check that deeplink exist
//find it in the graph
val match = navController.graph.matchDeepLink(NavDeepLinkRequest.Builder.fromUri(uri).build())
val destination = match!!.destination
navController.navigate(uri,
navOptions {
val changingGraphs = navController.graph.hierarchy.none { it == destination.parent }
Timber.d("[DeepLinkManagement] Changing graphs $changingGraphs")
if (changingGraphs) {
// If we are navigating to a 'sibling' graph (one that isn't part
// of the current destination's hierarchy), then we need to saveState
// to ensure that each graph has its own saved state that users can
// return to
Timber.d("[DeepLinkManagement] Parent ${destination.parent}")
popUpTo(destination.parent?.findStartDestination()?.id ?: 0) {
saveState = true
inclusive = true
}
// Note we specifically don't call restoreState = true
// as our deep link should support multiple instances of the
// same graph in a row
}
}
)
}
}
}
Hope that anyone can give me a hand with this. I'm possibly missing something obvious or maybe is something not supported at the moment. So any information or idea is totally welcome.
Thanks!

Xamarin Forms Maps MoveToLastRegionOnLayoutChange not working as expected

I have an issue with the Forms Maps.
When I drag the map and move away to a different screen, I see the map back to the last location set using "MoveToRegion" when I resume back the map screen. As per the documentation, setting MoveToLastRegionOnLayoutChange to False should prevent this behavior but it doesn't.
I didn't specifically test this property for screen rotation, but Renderer doesn't change on app sleep and resume.
Tried with below:
Xamarin Forms V16.6.000.1062
Android V8.1
Visual Studio 2019 V16.6.3
UPDATE: I realized that it is not merely a case of Page resume, but the page is rendered as below:
mapInstance = MapsPage.SelfInstance ?? new MapsPage();
Detail = new NavigationPage(mapInstance);
Here, I store the page instance in SelfInstance the first time it is instantiated, so the page is not initialized again. Result below:
Here is the link to code sample I created with above result.
Reported this on XF Github page:
https://github.com/xamarin/Xamarin.Forms/issues/11488

Created a Page as LoadingIndicator in Xamarin.forms?

In my application I have created my own Loading indicator with the help of Page class in xamarin.forms, and I use 'PushModalAsync' api to show Loading Indicator wherever needed like below
var loadingindicator=new LoadingIndicator();
MyApp.CurrentAppInstance.MainPage.Navigation.PushModalAsync(loadingindicator,false);
Everything works as expected and It is looking fine and I also managed to make it look like AndHUD by tweeking the alpha for the controls in that page until I hit an issue,
The issue is that, every time I show and hide the loadingindicator page 'OnAppearing()' getting called on the current top page on view stack.
I can fix this by introducing one additional functionality on all pages where I am using my LoadingIndicator, But I feel there might be some other cleaner way to solve this issue.
Can you guys suggest me if there is any cleaner approach available to solve this issue?
(I target this solution mainly for android and I want to achieve it through common code)
If I have understand the problem, I think there are some way to add Loading indicator.
Use ActivityIndicator
Use aritchie/userdialogs's Loading
using (this.Dialogs.Loading("Test Loading"))
await Task.Delay(3000);
Create a PopUp With Rg.Plugins.Popup
// Use these methods in PopupNavigation globally or Navigation in your pages
// Open new PopupPage
Task PushAsync(PopupPage page, bool animate = true) // Navigation.PushPopupAsync
// Hide last PopupPage
Task PopAsync(bool animate = true) // Navigation.PopPopupAsync
// Hide all PopupPage with animations
Task PopAllAsync(bool animate = true) // Navigation.PopAllPopupAsync
// Remove one popup page in stack
Task RemovePageAsync(PopupPage page, bool animate = true) // Navigation.RemovePopupPageAsync

Android drag and return to previous activity like Facebook and Google Photos

I have been finding a way to implement animation like Facebook and Google Photos. When in 2nd Activity, when dragging images the images follow and the 2nd Activity started to fade out and we see 1st Activity. Images of what I was trying to ask is here.
Was you able to find a full fledged solution for this ?
https://github.com/nickbutcher/plaid , It might give clues to entry transition and how that was accomplished . So it might help you there. But how about when you want to drag and dismiss that activity?
https://github.com/Commit451/ElasticDragDismissLayout , Its an interesting library. Does do drags . But doesn't remember the position where the thumbnail where it was launched from. Only handles 1 swipe direction and dismisses by sliding to the right. So its not that close to what they do in 2017 Jun Google Photos Android application. You might have to modify it.
If anyone knows a better solution to this please do share.
I have found some code from Nick Butcher sample code.
ElasticDragDismissFrameLayout.java
and when using it:
chromeFader = new ElasticDragDismissFrameLayout.SystemChromeFader(getWindow()) {
#Override
public void onDragDismissed() {
finishAfterTransition();
}
};

Recent Apps on UiAutomator

I am now working with Android UiAutomator on for UI Test on my Android app. My app has a function that requires the user to verify the email to continue, so I try to do it like this: after reach to that function -> getUiDevice.pressHome -> Browser -> try to log in email -> PressHome again -> Press RecentApps then I stuck here, I cannot press on my Apps to return to it again. I try another way by clicking on my App icon but it starts my app again, not at the state before. Can anyone suggest me a solution for this? Any help is appreciate.
Thanks in advance.
Try this :
UiObject appBackground = new UiObject(new UiSelector().description("ABC"));
appBackground.click();
It did not show any description through 'uiautomatorviewer' command but this worked for me.
I could manage to create this behavior with:
fun backgroundAndForeground() {
val device = UiDevice.getInstance(getInstrumentation())
device.pressHome()
// Pressing app switch two times makes the last app put on background come to foreground.
device.pressKeyCode(KeyEvent.KEYCODE_APP_SWITCH)
device.pressKeyCode(KeyEvent.KEYCODE_APP_SWITCH)
}
In this case, I think that android only resume app when clicking the recent app image. It does not work on clicking display text or app icon. So, we need to click image of your app in recent app list. At that time you need to write as below. I always do that for similar case.
// Take all image view by class type and click by instance no.
new UiObject(new UiSelector().className("android.widget.ImageView").instance(3)).click();
You need to count instance no of your recent app image view. Not app icon image in recent app scroll view. Please try this. Thanks.
I've spent half a day on this and concluded I needed to issue a device.click(). Since my use-case is that my app was the last one running (not switching to the browser like you), I can safely click the middle of the screen and it'll always work.
If you're the 2nd to last running app, you can probably do x: 0 and y: device.displayHeight/2.
I've not tested this on many operating systems, only 9.

Categories

Resources