I have an app that works in the following way:
Upper part of the screen is the 'primary' part of the app, the actual content that the user can interact with is displayed here.
Lower part of the screen (maybe 1/4 of the screen height or so) is a navigation wheel. The user can turn this wheel to change the upper part of the display. When turned the entire upper part is replaced by something else.
These two part are implemented using fragments. The lower part (navigation wheel) is just a static fragment that displays a view hierarchy that can be rotated. The upper part (content display area) is a fragment as well, but it can be switched to other fragments when the wheel is turned.
Now what I want is that when the app starts, we are displaying a free content section, however when the user turns the wheel to one of the other content sections a window pops up to block the content display area, saying "buy this mode on Google Play." This popup should obviously block the content area behind it, so the user can see some of the display area behind the popup, but he/she cannot interact with it before he/she has completed the Google Play transaction. It would be nice if the views of the content area could be greyed out as well. The navigation wheel in the bottom of the screen however should not be blocked so that the user is able to navigate back to where he/she came from or further on to another content section. How can I make a popup that blocks the top view hierarchy from interaction but leaves the lower one intact?
As far as I know it is not possible to use the PopupWindow class to create a popup that will only block some of the screen behind it. It seems to always block it all.
Here is an of illustration the design. 'P' for paid and 'F' for free. On the second screen the app should ignore any press on the content section (behind the grey window), but the navigation wheel should still be able to turn.
Thank you.
On the second screen the app should ignore any press on the content
section (behind the grey window), but the navigation wheel should
still be able to turn.
You could make an overlay and set it up to eat all touch events(either by setting a OnTouchListener on it or by using a custom layout with the onTouch() method overriden to return true) and put it on top of your normal fragment content. This way the actual content will be visible underneath but will not receive any touch events. You could also add some content to your overlay right like in your image.
Also, as the overlay will only cover the paid fragment the wheel at the bottom will be touchable by the user(I don't know how that wheel actually interacts with your fragments so the approach above might not work).
I've made a small rudimentary sample as an example(you can find it here).
It would be nice if the views of the content area could be greyed out
as well.
You could make a recursive method to traverse the view hierarchy of your paid fragment and call View.setEnabled(false) on the views it meets.
Seems overly complicated to me.. I cannot imagine the design..
I would create a different fragment to be triggered if the user has purchased the item, with a different interaction, like a Dialog to be shown at every click, or something like that.
In pseudocode:
if wheel.position == 4
if itemPurchased
showFullFragment
else
showMockFragment
Create a default fragment with one button, that opens the right page of Google Play to buy the content. Then, every time the navigation wheel is turned, you decide in your code if you show the fragment with content (when user has finished the Google Play transaction) or you show the default fragment with just the button to Google Play.
Only one thing: Google Play is asynchronous. There might be some time when the user actually has finished the transaction but Google Play didn't inform your App, yet.
If the transaction was initiated you must decide if you show the content without having to 100% clarity that the user actually paid, or if you show a blank fragment. In any way, you should not show the button again, because that would confuse the user ("I paid! Why do I need to go to Google Play again???")
Related
The goal is to obtain views that can be interacted with instantly (that can be clicked right now and something would happen). If the view is visible and clickable in general but hovered by another view/menu/side panel, it should be omitted.
Voice Access do that. And it seems to use Accessibility API.
The perfect example is the bottom menu in Google Maps. When it expands, "Search along the route" button underneath is still visible but it's not highlighted by the app.
So what do we have?
There is a stream of AccessibilityEvent. The most useful is
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED, so we can be notified when something is happening.
With getSource() we can get an instance of AccessibilityNodeInfo that triggered the event.
Or we can get a root of a window with AccessibilityService.getRootInActiveWindow(). And having that we are able to traverse the whole hierarchy within an app.
AccessibilityNodeInfo doesn't provide any information about z-order of views, so it's not possible to understand what is above and what is beneath.
The bottom menu is in the same window (it's not modal).
If you try to click "Search along the route" button while the bottom menu is expanded, the bottom menu collapses. So you can't actually click it, it's beneath the menu.
I've looked through all parameters of the AccessibilityNodeInfo, like isVisibleToUser(), isClickable(), isContextClickable(), isSelected(), isFocusable(), isFocused(), isAccessibilityFocused() and the button has the same parameters when the bottom menu is collapsed/expanded. It's visible to the user, focusable and clickable.
I've looked into hidden APIs and don't see anything that can be useful.
What I'm missing?
The key point is that in an AccessibilityService.onAccessibilityEvent() the tree hierarchy is not final. To get views that are interactable at the moment, AccessibilityService.getRootInActiveWindow() should be called with a delay.
AccessibilityNodeInfo#getDrawingOrder() will probably help you. Note that you need to do tree traversal to determine what is on top of what.
There are still corner cases with transparent views that will give you trouble, but that should get you 95% of the way there. We're working on a better answer for that case.
I have a master detail page setup using Prism 6.3 pre-2, I've finally got it working as I expected for navigating to "detail" pages; so the hamburger button shows the slide drawer and I have a simple ListView bound to the ViewModel, I list some items, and I use the NavigateCommand to navigate correctly.
Now, the expected behaviour (in Android), is when you've navigated to other items in the slide-out menu, you should be able to use the hardware back button to navigate back to the very first "detail" page shown.
For example, say your app has this setup, and the items listed are Inbox, Drafts, and Sent. The default view is going to be whatever you decide is the default detail view, in this example, it will most likely be Inbox. So if I navigate to Sent, and then press back, it should go back to Inbox, also, if I go to Sent -> Drafts, and then press back, it should go back to Inbox, as this is the default starting point, if you then press back again, the app should exit. At the moment, using Prism navigation, no matter what page you navigate to using this setup, it will exit straight away.
Please note, I have set it up correctly, I essentially have something like this (as an exmaple):
await NavigationService.NavigateAsync("RootPage/BaseNavigationPage/InboxPage");
When I navigate to say another page like Sent, the relative URI is like so:
"BaseNavigationPage/SentPage"
Now, when back is pressed, it should go back to the InboxPage, I'm not sure how this can be fixed... Brain...???
In addition to this, there is one other unexpected behaviour that is missing.
If I want to show a modal page whilst using a Master Detail Page, it doesn't work as any app written by Google does... This is easy to see if you open any google app that has this layout. If you show a modal window for say Settings, it pops up, has the navigation title set and a back arrow, basically looks exactly the same as if you've navigated one step further down like:
"RootPage/BaseNavigationPage/InboxPage/EmailPage"
However, I expected this functionality to be available if I did the following:
"BaseNavigationPage/SettingsPage", useModalNavigation:true
If you simply play around with any Google made application you'll see these features, and these are what I expect and they feel natural.
Any help available to achieve this...? again...Brian...?
All of this should be simple, but it's proving not to be...
To get the behavior you are wanting with the NavigationPage in a MasterDetailPage scenario, you must create a custom NavigationPage that implements INavigationPageOptions and set the ClearNavigationStackOnNavigation property to false. This will kep the NavigationPage's navigation stack in place with each navigation operation.
As to your other "expected behavior" regarding modal navigation, your understandings about modal navigation are wrong. Modal navigation does not provide a software back button. Any time you want a software back button you must have the pages wrapped in a NavigationPage,
Not sure how to explain this better, but it's mostly implemented by chat applications for Gallery picker fragments and alike. Best example is Google Allo, for which I posted GIF below. I'd like to know how is that implemented and how I could replicate the feature.
As you can see, the map fragment first transition animates from bottom to half of the screen, but then it can be dragged by finger to the top of the screen, when it actually fills the whole screen and even creates it's own toolbar. Most of all, I tested it, and this also works on API 16.
When there are multiple Fragments on the screen, how do I tell when the user interacts in ANY way with a specific one of the Fragments? Perhaps they tap a button, scroll a list, tab through fields, anything.
I had thought perhaps I would get an onClick/onTouch/onFocusChanged listener on the Fragment/FrameLayout containing the Fragment/root Layout of the Fragment UI, but they don't fire.
I'll try setting a variety of manual triggers, like "if the user clicks this button or scrolls this view or this field gains focus, then the user has interacted with this Fragment" but obviously that's more complex and highly prone to inaccurate behavior.
Not sure why I need this? Here's the use case:
Think of Gmail on a tablet: in landscape, the inbox is shown on the left and the first message on the right. If I tap a different message, it is displayed on the right. If I rotate to portrait, it will show the message I had last selected.
If I start in portrait, it will show the inbox.
Now here's the real trick: let's say I start in landscape, showing inbox on the left, the first message on the right. If I immediately rotate the device to portrait. But if I interact with the pane on the right (say, scrolling the email message) and then rotate the device, it will show that first message. Gmail guesses whether I was more interested in the inbox or the email and shows that.
My challenge is doing something similar in an app. When I'm rotating from landscape (showing two panes) to portrait (showing one pane) I'm not sure whether to show the list view or the default detail view. If I could tell if the user interacted with the detail view in some way (via keyboard or touchscreen) then I know to show that.
In the end, I could not find any good, clean way to work this.
Instead, for every individual way the user could interact with the Fragment--touching a ListView, clicking a Button and so on--I would fire off a message to the Activity (using a Listener interface the Activity implements, of course).
Then the Activity tracks whether or not the user has interacted with the default Fragment, making sure to save and restore that value.
It's messy, but it's all I could come up with. :/
The current Android YouTube application provides helpful hints for navigating the user interface. For example: the first time the user switches between tabs while a video is playing, a small "tooltip" with an arrow pops up and says "You can also switch between tabs by swiping left and right." or something to that effect. Is there a way to mimic the look and functionality of this tooltip?
The best way I could think of doing that is having something in a preferences file and when the user triggers a certain event (pressing a tab in Youtube rather than swiping) it brings up a custom view or even a Toast message. Then, after the tooltip is triggered, set the specific tag in the preferences file as triggered.