There is a crash on a Samsung Galaxy S 7 Edge when a user is interacting with an EditText that has a LengthFilter InputFilter applied. How would a user cause the method AccessibilityInteractionController.performAccessibilityActionUiThread to be called?
I looked at the source of AccessibilityInteractionController but I cannot find good documentation of how a user would trigger that method.
My crash's stack trace is similar to what is posted in these questions:
Android exception - Unknown origin (possibly widget)
My Android App has IndexOutOfBoundsException,how to solved?
Looking into Android's issue tracker, it seems that this issue is due to password managers using Accessibility events to generate passwords. However, generated password don't respect the maxLength property, causing the crash.
The suggested solution seems to work: creating a subclass, and using that instead. (Copying the code for reference)
public class SafePinEntryEditText extends EditText {
public SafePinEntryEditText(Context context) {
super(context);
}
public SafePinEntryEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SafePinEntryEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(21)
public SafePinEntryEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public void setSelection(int index) {
// prevent index out of bounds caused by AccessibilityService events
if (index > length()) {
index = length();
}
super.setSelection(index);
}
#Override
public void setSelection(int start, int stop) {
// prevent index out of bounds caused by AccessibilityService events
if (start > length()) {
start = length();
}
if (stop > length()) {
stop = length();
}
super.setSelection(start, stop);
}
}
Related
public FunGameRefreshView_Group(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
Intent i = new Intent(context,DataStreamService.class);
context.bindService(i,this,context.BIND_AUTO_CREATE);
**myLog.out("调用父类构造函数");**
}
Above codes come from an open source on github (fungamerefreash)
I want to learn how that refreashview worked, so I created an interface(Listener) to output my log.
but when I run this app, it crashed.
I'm trying to add a custom view to an Android layout, but get this error when I run it:
Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]
I thought that meant it was missing a constructor, but it doesn't appear to be. Here are the constructors I have for the class:
public ChartView(Context context) {
super(context);
init();
}
public ChartView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ChartView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
I checked the imports, and it is using android.content.Context and android.util.AttributeSet. I tried rebuilding the app, and reinstalling on my device, but no dice. The view is implemented as an inner class of the Activity that contains it, but is public. I feel think I missed something really obvious...
When I have a ProgressBar in layouts that are displayed when running some espresso-tests - then I run into:
Caused by: android.support.test.espresso.AppNotIdleException: Looped for 1670 iterations over 60 SECONDS. The following Idle Conditions failed .
What is a nice way to work around this? Found some hackish things but searching for a nice way
If the ProgressBar is invisible when the test starts, the Drawable can be replaced with by a custom ViewAction:
// Replace the drawable with a static color
onView(isAssignableFrom(ProgressBar.class)).perform(replaceProgressBarDrawable());
// Click a button (that will make the ProgressBar visible)
onView(withText("Show ProgressBar").perform(click());
The custom ViewAction:
public static ViewAction replaceProgressBarDrawable() {
return actionWithAssertions(new ViewAction() {
#Override
public Matcher<View> getConstraints() {
return isAssignableFrom(ProgressBar.class);
}
#Override
public String getDescription() {
return "replace the ProgressBar drawable";
}
#Override
public void perform(final UiController uiController, final View view) {
// Replace the indeterminate drawable with a static red ColorDrawable
ProgressBar progressBar = (ProgressBar) view;
progressBar.setIndeterminateDrawable(new ColorDrawable(0xffff0000));
uiController.loopMainThreadUntilIdle();
}
});
}
I have the same problem. I could not figure out a totally elegant solution, but I will post my approach either.
What I tried to do is to override the indeterminateDrawable on the ProgressBar. When having a simple drawable no animation takes place and the Espresso test does not ran into the Idle issue.
Unfortunately main and androidTest are treated the same. I did not find a way to override the styles for my ProgressBar.
It now ended up in combining some ideas from https://gist.github.com/Mauin/62c24c8a53593c0a605e#file-progressbar-java and How to detect whether android app is running UI test with Espresso.
At first I created to custom ProgressBar classes, one for debug and one for release. The release version only calls the super constructors and does nothing else. The debug version overrides the method setIndeterminateDrawable. With this I could set a simple drawable instead of the animated one.
Release code:
public class ProgressBar extends android.widget.ProgressBar {
public ProgressBar(Context context) {
super(context);
}
public ProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
Debug code:
public class ProgressBar extends android.widget.ProgressBar {
public ProgressBar(Context context) {
super(context);
}
public ProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ProgressBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#SuppressWarnings("deprecation")
#Override
public void setIndeterminateDrawable(Drawable d) {
if (isRunningTest()) {
d = getResources().getDrawable(R.drawable.ic_replay);
}
super.setIndeterminateDrawable(d);
}
private boolean isRunningTest() {
try {
Class.forName("base.EspressoTestBase");
return true;
} catch (ClassNotFoundException e) {
/* no-op */
}
return false;
}
}
As you can see I also added a check if my app is running an Espresso test, whereas the class I am searching for is the base of my Espresso tests.
The bad thing is that you have to update all your code to use your custom ProgressBar. But the good thing is that your release code does not have a major impact with this solution.
I have the similar issue. The test failed as early as the first call getActivity(). So the indeterminate drawable of ProgressBar have to be replaced after the activity started.
Application application = (Application)this.getInstrumentation().getTargetContext().getApplicationContext();
application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
//not here, it's too early
}
#Override
public void onActivityStarted(Activity activity) {
//find the progressBar in your activity
ProgressBar progressBar = ((ProgressBar) activity.findViewById(R.id.progress_bar));
if(progressBar != null) {
//replace progress bar drawable as not animated
progressBar.setIndeterminateDrawable(new ColorDrawable(0xffff0000));
}
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
//Now you can start the activity
getActivity();
Based on Thomas R. solution, another approach is to change the drawable of the ProgressBar in the test, to avoid modifying production code.
Example:
Activity activity = startActivity();
// override progress bar infinite animation with a simple image
ProgressBar progressBar = (ProgressBar) activity.findViewById(R.id.loading_progressbar);
progressBar.setIndeterminateDrawable(activity.getDrawable(android.R.drawable.ic_lock_lock));
// click on the button that triggers the display of the progress bar
onView(withId(R.id.login_button)).perform(click());
This answer might be late. With espresso, you have to turn off animation.
On your device, under Settings > Developer options, disable the
following 3 settings:
Window animation scale, Transition animation scale, Animator duration scale
https://developer.android.com/training/testing/espresso/setup.html#set-up-environment
There is an answer at Testing progress bar on Android with Espresso by riwnodennyk
But be cautious about UIAnimator
Caution: We recommend testing your app using UI Automator only when
your app must interact with the system to fulfill a critical use case.
Because UI Automator interacts with system apps and UIs, you need to
re-run and fix your UI Automator tests after each system update. Such
updates include Android platform version upgrades and new versions of
Google Play services. As an alternative to using UI Automator, we
recommend adding hermetic tests or separating your large test into a
suite of small and medium tests. In particular, focus on testing one
piece of inter-app communication at a time, such as sending
information to other apps and responding to intent results. The
Espresso-Intents tool can help you write these smaller tests.
https://developer.android.com/training/testing/fundamentals.html#large-tests
I'm following an example from a book and I can't understand why findViewById returns null.
This is my activity:
package it.mt.compass;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
public class CompassActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
CompassView cv = (CompassView)this.findViewById(R.id.compassView1);
// this crashes the application
//cv.setBearing(45);
// some debug code
Toast test_result;
if(cv == null) {
test_result = Toast.makeText(this, "1", Toast.LENGTH_SHORT);
test_result.show();
}
else {
test_result = Toast.makeText(this, "0", Toast.LENGTH_SHORT);
test_result.show();
}
// it shows 1
}
}
and this is the res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<it.mt.compass.CompassView
android:id="#+id/compassView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
Already cleaned (as suggested in other similar topics; what does "Clean" do?) the project with no luck.
Many thanks in advance.
Mirko
As requested, the constructors' code:
// Constructors
public CompassView(Context context) {
super(context);
initCompassView();
}
public CompassView(Context context, AttributeSet attrs) {
super(context);
initCompassView();
}
public CompassView(Context context, AttributeSet ats, int defaultStyle) {
super(context);
initCompassView();
}
That's the correct version (the problem was I didn't passed the parameters correctly to the superclass constructor):
// Constructors
public CompassView(Context context) {
super(context);
initCompassView();
}
public CompassView(Context context, AttributeSet attrs) {
super(context, attrs);
initCompassView();
}
public CompassView(Context context, AttributeSet ats, int defaultStyle) {
super(context, ats, defaultStyle);
initCompassView();
}
CompassView constructor implementation is incorrect. You're not passing the attributes to superclass and hence the id is lost.
Change here the superclass constructor invocation
public CompassView(Context context, AttributeSet attrs) {
super(context);
to super(context, attrs);
and
public CompassView(Context context, AttributeSet ats, int defaultStyle) {
super(context);
to super(context, attrs, defaultStyle); if the superclass has a ctor that accepts three args. Otherwise just use super(context, attrs). Oh, and rename the arg name from ats, even though the name doesn't matter.
In eclipse do:
Projects -> Clean.
Eefresh your app.
Run.
this will clear generated old R class.
I would try closing eclipse completely and then open it again. I've seen some really bizarre things like this happening. Another way you can try this is to just add a "textView" and try to do a findById on that and see if it's returning null. You could be loading the wrong xml view..
ie: your are loading a layout from one directory but it's actually loading a different view in a different directory with the same name...
add import it.mt.compass.R;
and try another View, like a Image or TextView instead
In my android application, I'm using a class called 'IntEditTextPreference'. This class is used when I want a user to introduce a preference as an integer.
But it has a problem. When the user leaves the field empty and press "ok", an NumberFormatException is thrown.
What could I do to avoid the user to press "ok" when the field is empty?
Thanks!
public class IntEditTextPreference extends EditTextPreference
{
public IntEditTextPreference(Context context)
{
super(context);
}
public IntEditTextPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public IntEditTextPreference(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override
protected String getPersistedString(String defaultReturnValue)
{
return String.valueOf(getPersistedInt(-1));
}
#Override
protected boolean persistString(String value)
{
return persistInt(Integer.valueOf(value));
}
}
You should probably still have a try/catch block around it to catch NumberFormatException. But there are many ways to do this. One way is you can use the buttons setClickable method to false and then true when the text is not null and is integer using onTextChangedListener. Or you could simply let it be clickable but check for empty string or non-integer when the button is clicked and use a warning message toast/alert/label to let the user know they have incorrect field before allowing the button to do anything else. Hope this helps!
In general if you are using a browser-based-application, you would use JavaScript/AJAX to display a button when the input is valid. This is already handled at client side.
To avoid the NumberFormatException, simply add a try-catch-block around the Integer.valueOf(value) statement.
Basically it depends on your client-framework. There might be better framework-specific solutions. Which one do you use?