Block all dialogs of Android, it means no dialog will appear, either of app or Android systems, until my service is running. Is there a way to do it programmatically?
I don't think it's possible to just block all popups.
For me it makes sense that android doesn't allow that normally.
But you can try (if you really want:) )to make your app an Accessibility Service which will react on popup displayed and immediately close it. To close the popup you can find some Cancel button on it and perform click or performGlobalAction(GLOBAL_ACTION_BACK); (if its cancelable).
Check out some code here to find a popup: Android unable read window content on few devices using accessibility service (I don't know if that will work)
You can also review this to get some more inspiration on how to find a view and make clicks on any app with the Accessibility Service: Programmatically enabling/disabling accessibility settings on Android device
EDITED: Some more details
You will need to follow this standard tutorial to add the service to your app: https://developer.android.com/training/accessibility/service.html
First thing to note is that you should decide to use xml configuration and include android:canRetrieveWindowContent="true"like in the tutorial:
<accessibility-service
android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
android:canRetrieveWindowContent="true"
/>
and I think you will not need the line android:packageName
Then you need to experiment what should happen in the callback method - here is just my rough suggestion:
#Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if(event.getEventType()==AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)
if(isAlert(source)) //explore the view (maybe recursively) to find if there is an alert
performGlobalAction(GLOBAL_ACTION_BACK);
}
and the recursive method can be like
private boolean isAlert(AccessibilityNodeInfo view){
int count = view.getChildCount();
boolean result = false;
for(int i=0; i<count; i++){
AccessibilityNodeInfo child = view.getChild(i);
if(child.getClassName().contains("Alert")){
return true;
}
if (explore(child));
result = true;
child.recycle();
return result;
}
Related
I'm new to Xamarin.Forms and mobile app development, so patience & kindness is appreciated! Am building a barcode scanner app with Xamarin.Forms PCL, trying to use MVVM. The scanner is an EXTERNAL bluetooth device (so can't use ZXing).
This project has a fixed requirement to use the scanner as a keyboard-type input and for the user to be able to quickly swap out one bluetooth device for another brand (so no device-specific APIs can be used). A second requirement is for the user to never be allowed to type anything directly into the Entry control. Input should come from the scanner and only the scanner, so therefore we don't ever want the keyboard showing on the scanning page.
There are other pages that have Entry controls where the user WILL need access to the keyboard, and the scanner should be able to stay connected to bluetooth even when a non-scanning screen is displayed. Therefore, I need a reliable way to set the soft keyboard to never be displayed on the scanning page (there is only one input control on this page, and it's intended for scanner use only), but to allow the keyboard to be accessed on other pages.
When on the scanning page, we want focus to always be set on the scanner's Entry control, so when the control gets a Completed event, we do stuff with the value received, then clear out the control and re-set focus on it to prepare for the next scan.
I have been stumbling around writing custom controls and android renderers, and with setting up dependencies (preferred), both with partial success. Either way, there's a timing issue related to how soon focus is set on the control. If there's not enough of a delay before focus is set, the soft keyboard stays visible. In the code sample provided, I added a short sleep delay, which mostly works to keep the keyboard hidden. However, the keyboard still "flashes" on the screen briefly with each scan, which looks terrible. Would really prefer a solution that is less hacky and ugly.
Is there a good, simple way to remove the soft keyboard entirely for a page, while still allowing an input control to receive focus, so that a scanned barcode can be received? And/or any other suggestions that will allow me to still meet the requirements?
(PS: the scanning page does not currently use MVVM binding. Just trying to get the keyboard to go away first, then will work on binding.)
Below is one way I tried to solve it. There were others as well. NOTE: Ultimately I went with a completely different approach which I'll post as an answer.
The custom control (in PCL):
using Xamarin.Forms;
namespace MyPCL.Views
{
//See ScanEntryRenderer in the Android project.
public class ScanEntryControl : Entry
{
public ScanEntryControl() { }
}
}
The Xaml page (notice InputTransparent = "True" on the custom control. This is so the user cannot directly enter input on the android device. All input must come from the bluetooth scanner).
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyPCL.Views"
x:Class="MyPCL.Views.ScanTestPage"
Title="Scan Test Page" >
<ContentPage.Content>
<StackLayout>
<Label Text="Scanner Test" />
<local:ScanEntryControl x:Name="BarcodeEntry"
Completed="BarcodeEntryCompleted"
InputTransparent="True"/>
<Label x:Name="ResultLabel" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
The code behind for the form:
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace MyPCL.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ScanTestPage : ContentPage
{
public ScanTestPage()
{
InitializeComponent();
BarcodeEntry.Focus();
}
protected override void OnAppearing()
{
base.OnAppearing();
BarcodeEntry.Focus();
}
private void BarcodeEntryCompleted(object sender, EventArgs e)
{
if (!string.IsNullOrWhiteSpace(BarcodeEntry.Text))
{
ResultLabel.Text = "You entered: " + BarcodeEntry.Text;
BarcodeEntry.Text = string.Empty;
}
BarcodeEntry.Focus();
}
}
}
The Android renderer:
using Android.Content;
using Xamarin.Forms;
using MyPCL.Views;
using MyPCL.Droid;
using Xamarin.Forms.Platform.Android;
using Android.Views.InputMethods;
[assembly: ExportRenderer(typeof(ScanEntryControl), typeof(ScanEntryRenderer))]
namespace MyPCL.Droid
{
public class ScanEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
((ScanEntryControl)e.NewElement).PropertyChanging += OnPropertyChanging;
}
if (e.OldElement != null)
{
((ScanEntryControl)e.OldElement).PropertyChanging -= OnPropertyChanging;
}
// Disable the Keyboard on Focus
this.Control.ShowSoftInputOnFocus = false;
}
private void OnPropertyChanging(object sender, PropertyChangingEventArgs propertyChangingEventArgs)
{
// Check if the view is about to get Focus
if (propertyChangingEventArgs.PropertyName == VisualElement.IsFocusedProperty.PropertyName)
{
// Dismiss the Keyboard
InputMethodManager imm = (InputMethodManager)this.Context.GetSystemService(Context.InputMethodService);
imm.HideSoftInputFromWindow(this.Control.WindowToken, 0);
}
}
}
}
I have been stumbling around writing custom controls and android renderers, and with setting up dependencies (preferred), both with partial success.
You can use EditText.ShowSoftInputOnFocus to achieve it in your scanning page, then the keyboard will not appear when your entry gets the focus:
using Android.Content;
using Android.Views.InputMethods;
using Edi;
using Edi.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ScanEntryControl), typeof(ScanEntryRenderer))]
namespace Edi.Droid
{
public class ScanEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
((ScanEntryControl)e.NewElement).PropertyChanging += OnPropertyChanging;
}
if (e.OldElement != null)
{
((ScanEntryControl)e.OldElement).PropertyChanging -= OnPropertyChanging;
}
// Disable the Keyboard on Focus
this.Control.ShowSoftInputOnFocus = false;
}
private void OnPropertyChanging(object sender, PropertyChangingEventArgs propertyChangingEventArgs)
{
// Check if the view is about to get Focus
if (propertyChangingEventArgs.PropertyName == VisualElement.IsFocusedProperty.PropertyName)
{
// incase if the focus was moved from another Entry
// Forcefully dismiss the Keyboard
InputMethodManager imm = (InputMethodManager)this.Context.GetSystemService(Context.InputMethodService);
imm.HideSoftInputFromWindow(this.Control.WindowToken, 0);
}
}
}
}
In other pages you can still use Entry, so the keyboard will be appear.
UPDATE:
ScanEntryControl class in PCL:
using Xamarin.Forms;
namespace Edi
{
public class ScanEntryControl : Entry
{
}
}
.xaml file:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Edi"
x:Class="Edi.MainPage">
<ContentPage.Content>
<StackLayout>
<local:ScanEntryControl Text="ScanEntryControl"/>
<Entry Text="Entry"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
This answer does not solve the original issue directly, in the sense that it does not involve an Entry control. However, it was the only thing that worked for me, and ended up being a more elegant solution:
The Bluetooth scanner was in HID mode (Human Interface Device) by default, meaning the only way it could interact with the app was by imitating key presses, thereby necessitating an Entry (EditText) control, or similar. I switched the scanner to SPP mode (Serial Port Profile) and adapted the code from this page (see also the GitHub repo here, and for more info on HID vs SPP see this document).
The resulting code activates the scanner and then "listens" for input. When input is received, it is displayed in a Label rather than an Entry control.
There were other problems with the Entry control that I didn't mention prior: often it would add a repeat character to the front of the barcode and/or chop off one or more characters from the end. The SPP solution solved all that as well. If anyone wants the code I came up with, let me know. It will take some work to put together in a generic example, so not posting it at the moment.
I was facing the same problem. I had found one sample over in the Xamarin forums that IMHO contained the key solution:
You must override Focus() and must not call the base method. This gives you full control over the virtual keyboard. In all other solutions I have seen the virtual keyboard appears sometimes.
Of course your custom Entry needs methods to show/hide the keyboard. You would call them in your OnFocus() method. My sample control (see below) also has a bindable property that allows you to show the virtual keyboard automatically on Focus. So you may decide for every field if the keyboard should appear automatically or not.
In addition I have included another object that informs you if the virtual keyboard is currently visible and its size in case you need to size your layout accordingly.
Since this is quite a common question in several different forums I have decided to create a sample control and a small app to show the features.
In addition I wrote a detailed Readme that explains all crucial points of the implementation.
You will find it here: https://github.com/UweReisewitz/XamarinAndroidEntry
protected override void OnAppearing()
{
base.OnAppearing();
txtLotID.Focus();
}
private void OnLoad()
{
Init();
swScanMode.IsToggled = Global.IsScannable;
txtLotID.EnableKeyboard = !Global.IsScannable;
txtLotID.OnEntryScanned += BtnSearch_Clicked;
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
if (txtLotID.IsReadOnly)
{
txtLotID.Text = "";
**txtLotID.IsReadOnly = false;**
txtLotID.GetFocus();
}
return true;
});
}
I want to implement an Accessibility service in my Android app which can do the following things:
=>Get the onscreen taps/clicks across any apps.
=>Get the view which was tapped/clicked.
Initially I thought it would not be possible to do so because of security reasons but while doing some research I came across an app (Native clipboard) which could do following things:
=>Detect taps on EditTexts from any app
=>Add value (string) to those EditTexts.
I also saw Google's talkback which speaks whatever you tap on. For it to speak, it needs to access the view (to get the text) across apps.
These apps obviously makes use of 'Accessibility services' to do so, but I would like to know how can I implement that?
I mostly find tutorials or stuffs for things I need to achieve but I am struggling finding some for implementing Accessibility service for my app. I did visit the Android official documentation which is too technical for a newbie like me. (I initially prefer to learn from Youtube, SO and tutorial websites). It will also be great if you can pin point me to some other tutorials which covers these things.
Accessibility services are pretty poorly documented, but I have created some accessibility service boilerplate code, that sets up a starter project and logs the base callbacks. Here is a bit of code that I think you care about given your specific questions. The scaffolding, project set up and such I leave for the repo.
Below is the onAccessibilityEvent callback. This is where you will listen for different types of a events, and the most convenient place to grab onto screen content for most scenarios. Though, as an accessibility service you also don't have to wait for events. You could just as easily spawn an AsynTask and grab on to it on an interval of some kind.
public void onAccessibilityEvent(AccessibilityEvent event) {
CLog.d(event.toString());
switch (event.getEventType()) {
//On Gesture events print out the entire view heirarchy!
case AccessibilityEvent.TYPE_GESTURE_DETECTION_START:
CLog.d(A11yNodeInfo.wrap(getRootInActiveWindow()).toViewHeirarchy());
case AccessibilityEvent.TYPE_VIEW_CLICKED:
CLog.d(event.getSource().toString());
default: {
//The event has different types, for you, you want to look for "action clicked"
if (event.getSource() != null) {
CLog.d(A11yNodeInfo.wrap(event.getSource()).toViewHeirarchy());
}
}
}
}
I will point out one bit of configuration for this, because it is super important. Accessibility services are best configured through an XML file connected to your service through the Manifest file. The contents of this file are:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="#string/accessibility_service_description"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagReportViewIds"
android:canRetrieveWindowContent="true"
android:canRequestTouchExplorationMode="true"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:settingsActivity="com.moba11y.basicaccessibilityservice.SettingsActivity"
/>
For you the important bits are canRetrieveWindowContent="true" and accessibilityEventTypes="typeAllMask". A design bit I like, is ensuring that your grabbing onto the minimal set of event types you want. Different Accessibility Events report broadly different results. For example, many events return "null" from getSource(). This forces you to add a lot of filters for this, or risk null pointer exceptions. It's quite annoying.
The last bit you need is Accessibility Actions. This is what allows you to simulate clicks, long clicks AND add text to an editable text view. Below is code that allows you to do this.
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo source = event.getSource();
if (source != null & event.getClassName().equals("android.widget.EditText")) {
Bundle arguments = new Bundle();
arguments.putCharSequence(
AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,"some value");
source.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
}
}
https://github.com/chriscm2006/Android-Accessibility-Service-Boilerplate
Android enables apps to draw over other apps with android.permission.SYSTEM_ALERT_WINDOW and it's called a floating/overlaying app. For example Facebook Messenger has always visible chat bubbles at the edges of screen.
My question is: Is it possible to detect or block in Java code any app which draws over my app?
There is a View#onFilterTouchEventForSecurity() method you can override to detect if the motion event has the FLAG_WINDOW_IS_OBSCURED. This will let you know if something is drawn on top of your view.
#Override
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
if ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) == MotionEvent.FLAG_WINDOW_IS_OBSCURED){
// show error message
return false;
}
return super.onFilterTouchEventForSecurity(event);
}
If you just want to protect your app from tap jacking due to another app drawing over your app you can add setFilterTouchesWhenObscured to your views via XML or programmatically.
I'm porting an existing QML/C++ application to the Android system. The application is already running on the Android tablet, but I have issues with Android keyboard.
Since my QML/C++ application has implemented its own keyboard, I would like to disable an Android one.
I've tried to add android:windowSoftInputMode="stateAlwaysHidden" line in AndroidManifest.xml file, but keyboard still appears when I press an edit box.
Since I'm porting an existing application, I do not want to edit the code of application itself. The only things I can edit are AndroidManifest.xml, QtApplication.java and QtActivity.java files. QtApplication and QtActivity are derived from Application and Activity Android classes.
Is it possible to disable the Android keyboard globally for whole app at the startup of application(with editing manifest file or overriding onCreate, onStart or similar functions)?
Are there any functions in Application and Activity classes that I can override them and consequently disable native keyboard?
After some time I found a solution, actually workaround for this problem. The idea was to consume an event that requests Software Input Panel (QEvent::RequestSoftwareInputPanel). This event is sent by QML/C++ application to the host Android system.
So, I implemented an event filter called SIPRequestEater.
class SIPRequestEater: public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::RequestSoftwareInputPanel)
{
// filter out RequestSoftwareInputPanel event
return true;
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
};
This filter has to be installed to QCoreApplication berfore the QCoreApplication::run is called.
QCoreApplication *coreApp = QCoreApplication::instance();
SIPRequestEater *sipRequestEater = new SIPRequestEater();
coreApp->installEventFilter(sipRequestEater);
It can be installed also on QApllication.
The problem is, that this filter does not catch QEvent::RequestSoftwareInputPanel event. My explanation for this is that filters, which are installed with QCoreApplication::installEventFilter(<filter>) are filters only for input events, from Android to QML application. QEvent::RequestSoftwareInputPanel is actually going in other direction, from QML application to the Android system. I didn't find out if it is possible to filter/disable output events. Because of this I decided to filter out the focus in event QEvent::FocusIn which actually causes QEvent::RequestSoftwareInputPanel.For our application this works as it should. The Android keyboard is not appearing anymore and our edit text fields still get focus, because we have our own implementation of focus and keyboard. I believe that this is not the perfect solution for everyone, that's why I called it workaround.
If someone knows, how to filter out output events, specially QEvent::RequestSoftwareInputPanel, please post it here.
The final implementation of filter is:
class SIPRequestEater: public QObject
{
Q_OBJECT
protected:
bool eventFilter(QObject *obj, QEvent *event)
{
if(event->type() == QEvent::FocusIn)
{
// filter out FocusIn event
return true;
}
else
{
// standard event processing
return QObject::eventFilter(obj, event);
}
}
};
QApplication::setAutoSipEnabled(false) disables the software virtual keyboard from popping up automatically. You can use the "Q_OS_ANDROID" preprocessor directive to avoid modifying behavior on your other target platforms:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
#ifdef Q_OS_ANDROID
a.setAutoSipEnabled(false);
#endif
(...)
}
You can programmatically display or hide the virtual keyboard using this code:
QInputMethod* input;
input = QGuiApplication::inputMethod();
if(input->isVisible())
{
input->setVisible(false);
}
else
{
input->setVisible(true);
}
Here is another approach using Qt.inputMethod - hide virtual keyboard immediately when it get visible for example by adding visibleChanged handler in qml root item
Component.onCompleted: {
Qt.inputMethod.visibleChanged.connect(function () {
if (Qt.inputMethod.visible)
Qt.inputMethod.hide()
})
}
With this approach it is also possible to add user interface setting to choose what keyboard to use (system or built in app).
Update:
Have figured out that sometimes there can be a blink of system virtual keyboard before hiding, especially when tapping to text input field to much. Handling visibleChanged in C++ don't solve this issue but seems to make it less frequently.
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::visibleChanged, [] {
QGuiApplication::inputMethod()->hide();
});
I'm writing a Android NDK application, (targeted at API 8), which is based on the hello-gl2 example and so uses a single full-screen OpenGL2.0 view.
I need to bring up the on-screen keyboard and so from the info available on this site i can now bring up the keyboard, send the keyup events to my native application for processing and close the keyboard. All is well until i switch to Swype or any other more-advanced-then-stock keyboard input manager and it doesn't work.
After reading the KeyEvent developer doc class overview what i'm seeing seems to be the expected behaviour:
You should never rely on receiving KeyEvents for any key on a soft
input method.
So i've been trying to work out a way to capture the output of a IME which i can then pass down to my native application. Unfortunately i'm not getting very far with this at all.
I've got this code in my extended GLSurfaceView class:
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
{
outAttrs.actionLabel = "";
outAttrs.hintText = "";
outAttrs.initialCapsMode = 0;
outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
outAttrs.label = "";
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
outAttrs.inputType = InputType.TYPE_NULL;
return new BaseInputConnection(this, false);
}
#Override
public boolean onCheckIsTextEditor()
{
return true;
}
And i'm calling:
public boolean showSoftInput(View view, int flags)
passing in the view returned from getCurrentFocus, from native code to display the keyboard.
Is it possible to be able to extract the text from the on-screen software keyboard not using the keyup event? Is it possible to update what i've got to be able to do this or should i be looking at a completely different method?
Apologies for the questions, as you can tell, the non-native Java side of Android development is quite new to me.
As a summary my current implementation is based on this answer which allows the soft-keyboard text to be captured using key events however i'd like to capture the text not using key events.. (possibly a TextWatcher.. though my View isn't editable?).
Many thanks for any help or advice offered,
Andy