I am trying to achieve a floating draggable view that will be displayed across all the activities of a single app.
Meaning there will be a view of 30% height and 50% width of the screen that the user will be able to drag on the screen and it will be drawn above any activity in the app. When the user will switch to another activity within the same app the floating view should remain in the same place.
I know it is possible using SYSTEM_ALERT_WINDOW permission, which I want to avoid because this view is only required to be displayed within the app and I don't want the users to be asked to approve such permission.
Another important point:
This view is going to be part of an SDK.
Meaning that I am not responsible for the activities/fragments/layouts of the app.
I can have/assume:
Base Application that the hosting app will subclass
All the root layouts have some identifier and are of some specific type (e.g. all the root layouts are RelativeLayout and have an id root_layout)
Similar questions that was asked 4-6 years ago with 0 satisfying answers:
Floating view over the whole application
Android floating view across activities
i can suggest you using a service running in the background holding that specific view. The use of a service will answer your need in terms where you want that specific view to remain operative until the user should choose to disable or close it manually. You need to use the asynchronous service which is the IntentService so it will not affect you ui main thread (don't forget the activitiy's life cycle and hierarchy).
see the following links please.
1 - link to google developers
https://developer.android.com/training/run-background-service/create-service
2 - link to an example of such implementation
https://medium.com/exploring-code/create-chat-heads-like-facebook-messenger-32f7f1a62064
Related
In my Android app I have a custom layout that is being used as a button - it consists of some TextViews and an ImageView, additionally it has some gradient background.
I'm aligning my app now to conform to the Accessibility rules. In order to do so, I would need to convert this layout into a button, so that TalkBack can correctly indicate the action, that this whole layout is clickable and serves like a button.
I know that on iOS there is a possibility to set the UIAccessibilityTraits to treat such view as a button - this kind of solution would save me a huge amount of work in terms of migration.
Is there any similar solution on Android for that? What approach should I follow in order to make this layout recognized correctly by TalkBack?
No, there's no concept of accessibility traits on Android - but you can still get a good accessibility experience without needing to specifically convert your layout into a Button.
Generally, it's most important that TalkBack (or whatever accessibility service is being used - remember, it's not just TalkBack) is able to detect that the widget is clickable and to be able to read a coherent description of what it does. The additional information that it's a button, specifically, isn't super useful, especially because there are so many different kinds of UI elements that it's often a very ambiguous question whether something even is a button.
You can test this by selecting it in TalkBack and confirming that it reads the content description properly, says something along the lines of "Double tap to activate," and performs the correct action when you double tap.
If it's not correct, make sure the content description, clickable flag, and click action are set correctly on the widget's AccessibilityNodeInfo.
It is common knowledge that we can observe or query the view hierarchy of any app with an AccessibilityService:
Create your own accessibility service.
It is also possible to perform actions on behalf of the user:
Developing an Accessibility Service for Android.
My question is, can we modify the view hierarchy of a foreground app with an AccessibilityService?
I have already referred the following questions:
How do I add and remove a layout programmatically from an accessibility service?
Get view of AccessibilityNodeInfo to create overlay.
What they're doing is using the WindowManager and the SYSTEM_ALERT_WINDOW permission to overlay a view on top of the app in the foreground. The problem with this approach is that if the user presses BACK or HOME, the app is dismissed, but the view remains visible on the screen, even after the app is gone. The view is on TOP of the view hierarchy, and not a part of it.
Is there a way to add / modify the AccessibilityNodeInfo objects?
Are these the same as a View or ViewGroup?
Are we allowed to add views or modify existing views with an AccessibilityService?
My requirement is to display a small view within the app itself.
It has to be part of the view hierarchy of the app, so that it stays
or goes with the app. For this I need to modify the view hierarchy of
the app, namely the AccessibilityNodeInfo objects retrieved from the
service.
What I want is something similar to addView(), but add the View to the view hierarchy of the app itself, not on top of it.
How can we do this? Is this possible?
UPDATE:
Apps that support Custom Views for Accessibility
No, you can't modify the view hierarchy of another app because it exists in a separate process.
This is similar to not being able to modify accessibility nodes from within an accessibility service.
1) You can exploit draw over other apps permission. That solution will allow you only to draw overlays over another apps and not to change another apps behavior.
2) You can exploit instrumentation test mechanism. If you have enough information about the app(app id, activity name) and enough privileges (Run an instrument test from within app and wait for result), or root privileges. Here is an example:
#RunWith(AndroidJUnit4::class)
class InjectView {
#get:Rule
val activityRule = ActivityTestRule<MainActivity>(MainActivity::class.java)
#Test
fun injectView() {
val rootLayout = activityRule.activity.findViewById<ViewGroup>(android.R.id.content)
activityRule.runOnUiThread {
rootLayout.addView(TextView(activityRule.activity).apply {
text = "Injected View"
})
}
Thread.sleep(10_000)
}
}
You can still go with the approach of drawing on top of the app, not as part of its view hierarchy (which is impossible) - using SYSTEM_ALERT_WINDOW permission.
In order to know when the app is dismissed and dismiss your own overlay - listen to the accessibility event TYPE_WINDOW_STATE_CHANGED and check that there's a package change.
You can also go further with listening to TYPE_WINDOW_CONTENT_CHANGED and determining that there was some layout update.
More accessibility events might come in handy or fine-tuning your overlay's accuracy.
In short - as long as you have the appropriate information to know about the underlying app's layout and when things happen, you can draw on top as if it's part of the app.
Might be tricky and require some calculations since you're not able to just push views into the hierarchy, but totally doable.
I am not the best at android programming so please correct any errors I make.
According to this developer.android.com.
Android view is:
This class represents the basic building block for user interface
components. A View occupies a rectangular area on the screen and is
responsible for drawing and event handling. View is the base class for
widgets, which are used to create interactive UI components (buttons,
text fields, etc.).
However, I have not found an article about how to get a full view. I mean as in an entire screen as a view. The image I used below is to show what I want to include in that view. I want to include everything on screen. Everything On that Screen. (I can't repeat enough). It includes the chat heads, background, and menu bar. Everything that is on screen.
I can't figure out how to get a view like that from a service or from an activity. Any help is appreciated. I don't have exact code because I don't know how to do this.
Edit:
I know that I am very bad at explaining, so if you are confused, I am going to explain what I want to do with my view.
So basically, I want to make a screen tapper that taps for you so you can king of "cheat" games like cookie clicker. Therefore I need a view that contains the entire screen so then I can fake "touches" on the screen.
Hope this cleared any confusion
So you have a few things to deal with. Generally, the menu bar (and possibly soft-key buttons on the bottom) will remain visible. However, you can hide the menu bar:
hide the top menu bar in my android device & Tablet
You may want gestures to allow users to see it. Or, since it appears you are building a game, you just leave it hidden until they hit "home" or "back" or "exit" or whatever.
The "chat heads" are a horrible idea, but some developers think they are awesome. You can see more about that here:
Creating a system overlay window (always on top)
You might find something like this will work (but if new notifications pop-up, it may not help):
http://goobbe.com/questions/1390940/how-to-close-cancel-dismiss-a-system-dialog-programmatically-android
Sorry, I know that this topic has been covered a bit. I've read the related posts and am still a bit confused. I am working on an app that while the prototype will have 3 main screens, it will eventually have dozens. Each screen will present either dynmically changing status or take user input. To visualize, it is required to be laid out similar to how MS Word or a typical PC is. It has a status bar at the top and a navigation bar at the bottom that is common to all screens (slight tweaks for some screens, like different icons) in the middle is what I would call a view pane that needs to be updated with a applicable layout.
The status, nav bar, and each screen are defined in their own layout xml file. For my first swag at it I just used a ViewFlipper and loaded the 3 screen layouts into it. However that means that currently I have one main Activity which will not be maintainable as I continue to add screens.
It feels right to me that each screen layout should have an associated Activity class that understands how to control that screen. I need to figure out how to load that into the center pane dynamically. However I thought I read in another post that using multiple Activities can be a CPU and RAM drain.
Currently I tried making one of the screens it's own Activity and kick that off from the main Activity by creating an Intent and than calling startActivity. However that causes the new screen Activity to reside on top of the main Activity. The interesting thing is that then pressing the back button dismissed that activity and returns me to the main.
So far I haven't figured out how to setup having a different Activity control what happens in the center pane.
If I continue down the multiple Activity path, should my main Activity be inheriting from ActivityGroup?
Are using View classes more applicable in this case?
I know this has been a long post. I'd appreciate any advice.
Thanks!
CB
As you noticed, Android will implicitly track a stack of started activities in a task, and the 'back' button ends the top one, reactivating the next one down. I would advise you to think about which kinds of things the user might expect the back button to do, and make it so that activities are separated along those lines.
I haven't played with ActivityGroup so I can't advise you there. If you go with completely separate activities, you can have them all use the same "shell" content view with the common nav/status bar. Have a superclass or utility class handle populating and managing that from there. Then use a a LayoutInflater (you can call getLayoutInflater()) to fill in the middle with your Activity-specific view.
If you want one of the activities to have multiple screens, you might still end up with a ViewFlipper in the center slot. Again, you want to have an Activity transition wherever you want the user to be able to go "back"; that also means you may NOT want to have a change of activities in cases where screens are closely related or part of the same logical thing-being-done. (You can override the back button's behavior, but unless you have a good reason to, it's best to just arrange the app so that Android's basic setup helps your app's UI rather than working at cross purposes.)
If you want to use activities in the fashion you talked about, you might look into using a tab activity. It actually works in the way you want, you just need to hide the tab widget and put your navigation bar there instead. Or, you could go a little deeper and make you own similar tab-like ActivityGroup like Walter mentioned if you have more time.
You could use a view pager with fragments to accomplish the flip between the different views but still allow your activity to have full control over it. The activity can control the menus while the fragment controls your viewing area. This way your back button will properly dismiss the activity containing all pages related to the activity instead of walking down the stack.
I noticed that when I swipe my finger from left to right across a home screen widget, in simulator on a AVD, Android switch to the left home screen.
I was wondering if its possible to prevent this behavior and allow home screen gadgets to listen and react to finger gestures (like swipe)? Its very critical for my widget UI design.
Sorry I am posting this reply as a separate answer because it wouldn't fit as a comment. :)
So interaction with widgets is very limited.
The thing is, widgets aren't "mini applications," they are... widgets. They live in the world of, and are dependent upon, the real application containing them.
There is a continual demand for widgets to support more and more complicated interactions, but every time we start down that path it ends up making the interaction with the containing application much more complicated as well, and seems likely to negatively impact overall usability.
For example, if widgets can process swipes, then the user is now very uncertain about how they can swipe between screens because they will get different behavior depending where they swipe. They could even get stuck in a state where it is nearly impossible to swipe between screens because of the widgets covering the touchable area.
So the way I would recommend thinking of widgets is as a window into the key information associated with your app, to jump into more details of that information by tapping on the widget and switching to the app, and possibly some additional buttons to perform key operations.
Also as of 2.0, with the new wallpaper themes, you can make the UI of the activity that the user visits from your widget sit on top of the same wallpaper as home. This makes the switch between it and home feel much more like an interaction inside of home rather than with another application.
Starting with API level 11 (Android 3.0.x) you can use "android.widget.RemoteViewsService.RemoteViewsFactory" and and assign a widget to use (from docs):
ListView: A view that shows items in a vertically scrolling list. For an example, see the Gmail app widget.
GridView: A view that shows items in two-dimensional scrolling grid. For an example, see the Bookmarks app widget.
StackView: A stacked card view (kind of like a rolodex), where the user can flick the front card up/down to see the previous/next card, respectively. Examples include the YouTube and Books app widgets.
See http://developer.android.com/guide/topics/appwidgets/index.html section Using App Widgets with Collections.
So yes - if you use one of the above layouts your widget will accept gestures.
One more is:
AdapterViewFlipper: An adapter-backed simple ViewAnimator that animates between two or more views. Only one child is shown at a time.
Doc: https://developer.android.com/reference/android/widget/AdapterViewFlipper
Example: https://github.com/sunphiz/adapterviewflipper-widget