NumberFormatException with IntEditTextPreference - android

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?

Related

What triggers a call to AccessibilityInteractionController.performAccessibilityActionUiThread on Android?

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);
}
}

Prevent PreferenceFragment from writing default preferences to SharedPreferences

Current behavior of PreferenceFragment: Upon first display on screen PreferenceFragment writes to associated SharedPreferences all default values defined in PreferenceScreen XML resource. I tested this couple times and PreferenceFragment as well as PreferenceActivity writes all preferences defaults to SharedPreferences when user opens Settings activity, even if he immediately close it without touching anything.
Problem: When in the next version of my app I decide to change some default user preferences, they will not apply to the devices where user at least once opened app preferences, because PreferenceFragment wrote all default values to SharedPreferences. I know that I can reapply new default values by overwriting all values in SharedPreferences, not only default, but user chosen too. But resetting user preferences in app update is completely unacceptable. So the problem is that we cant distinguish when some particular preference was set explicity by user or its just default preference written by PreferenceFragment upon first display on screen.
What I want: If the user explicity set some preference, whatever he has chosen, I should not touch this with my updated app defaults, even if user choice is coincides with my old default. But if user was not explicity chose preference I want that my new default preferences start working for him with app update.
So: How to prevent write of default preferences values by PreferenceFragment to associated SharedPreferences?
After studying sources I found a way to achieve requested behavior.
The only place where real write to SharedPreferences occurs it's bunch of persist[Type] methods in Preference class. And subclasses of Preference usually call persist[Type] method only in single internal method, that has similar structure across all subclasses. For example, method from TwoStatePreference, that is superclass of ChekBoxPreference and SwitchPreference:
public void setChecked(boolean checked) {
boolean changed = this.mChecked != checked;
if(changed || !this.mCheckedSet) {
this.mChecked = checked;
this.mCheckedSet = true;
this.persistBoolean(checked);
if(changed) {
this.notifyDependencyChange(this.shouldDisableDependents());
this.notifyChanged();
}
}
}
Next, setChecked method of TwoStatePreference is called in about five other methods, and two of these calls can produce default value to be committed to SharedPreferences. Here is first:
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setChecked(restoreValue ? getPersistedBoolean(mChecked)
: (Boolean) defaultValue);
}
And second:
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save state for us in onSaveInstanceState
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
setChecked(myState.checked);
}
And here is the solution, custom class, that subclasses SwitchPreferenceCompat and preventing commit in two above calls:
public class MySwitchPref extends SwitchPreferenceCompat
{
private boolean mAllowPersist;
#Override
protected boolean persistBoolean(boolean value) {
if (mAllowPersist) {
return super.persistBoolean(value);
}
return false;
}
#Override
protected void onSetInitialValue(boolean restoreValue,
Object defaultValue) {
mAllowPersist = false;
super.onSetInitialValue(restoreValue, defaultValue);
mAllowPersist = true;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
mAllowPersist = false;
super.onRestoreInstanceState(state);
mAllowPersist = true;
}
public MySwitchPref(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public MySwitchPref(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MySwitchPref(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MySwitchPref(Context context) {
super(context);
}
}
You should replace your SwitchPreferenceCompat declaration in xml PreferenceScreen to this subclass and all should work, I have tested it. And if you use other then SwitchPreference types of preferences, you as well should similarly subclass and override their behavior.
Beware: This solution relies upon internal realization of current, com.android.support:preference-v7:23.4.0 library. It might change with future releases, so if you use other library version, or use non support implementation you should look in to source, and ensure there is no other calls to persist values in SharedPreferences except that two, that I overriden. As well if you use other subclasses of Preference, not only SwitchPreference, you should check for other calls to persist values in SharedPreferences.

ProgressBars and Espresso

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

Android nullexception constructor

Hello i have a problem with the elimination of NullEx ...
I set mContext = context and now i have errors:
Implicit super constructor LinearLayout() is undefined. Must explicitly invoke another constructor
Constructor call must be the first statement in a constructor
public DigitalClock(Context context) {
mContext=context;
this(context, null);
}
Earlier thread Android alarm Clock which shows problem.
You need a superclass constructor call.
public DigitalClock(Context context) {
super(context); // Add a line like this.
// Consult constructor documentation for correct usage.
this(context, null); // this line must also be at the top.
mContext=context;
}
I would assume that you are extending View, in that case you need at least two constructors.
//...Override Constructors...
public DigitalClock(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DigitalClock(Context context){
super(context);
}
See if that helps.

How to set the values of PreferenceActivity(R.xml.preferences) dynamically loading it from SharedPreferences?

I have a MyPreferenceActivity that extends PreferenceActivity like this :
public class MyPreferenceActivity extends PreferenceActivity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
I also have custom shared preferences that R.xml.preferences are tied to by individual preference classes...for e.g.
preferences.xml has
<!--EditTextPreference-->
<com.myapp.preferences.PrimaryNumberPreference
android:key="PREFS_PRIMARY_NUMBER"
android:title="#string/primary_number_preference_title"
android:summary="#string/primary_number_preference_summary"
android:dialogTitle="#string/primary_number_preference_dialog_title"
android:dialogMessage="#string/primary_number_preference_dialog_message"
/>
PrimaryNumberPreference.java :
public class PrimaryNumberPreference extends EditTextPreference {
Context ctx = null;
public PrimaryNumberPreference(Context context) {
super(context);
this.ctx = context;
}
public PrimaryNumberPreference(Context context, AttributeSet attrs) {
super(context, attrs);
this.ctx = context;
}
public PrimaryNumberPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.ctx = context;
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult){
customPreferenceibrary.setPrefsPrimaryNumber(getText());
}
}
}
OK so all seems to be in place. Now what I want is everytime the PreferenceActivity is loaded, it should fetch the current sharedpreference values that are stored in their respective key and prepopulate the elements of the PreferenceActivity...
Eg: PrimaryNumber shared preference is set to 1234 when my app is installed. Now if I go to MyPreferenceActivity and browse the PrimaryNumber EditTextPreference, the text box should be prepopulated by 1234.
Something(pseudo code) like :
MyPreferenceActivity.PrimaryNumberPreference.Val = getSharedPreferences(prefsPrimaryNumber)
How can I do that?
EDIT
Please let me know if the question is unclear and needs better explanantion. I am sure the answer is a simple implementation of something very standard in android. All PreferenceActivity elements pick the current value to be displayed from the stored SharedPreferences only, right?
I think I see two potential problems:
Problem One
If you have a custom preference such as the PrimaryNumberPreference.java you need to manually save the preference when the dialog closes. Is that what you are doing in customPreferenceibrary.setPrefsPrimaryNumber(getText()); ??
If they are not committed in this function then I think they need to be. (Or committed somewhere else in onDialogClosed)
Problem Two
Assuming you are commiting the preferences in setPrefsPrimaryNumber, I think the setPrefsPrimaryNumber needs to receive two paramenters,
The preference name
The actual value
I dont think it is enough just to have the key name set in XML (android:key="PREFS_PRIMARY_NUMBER is not enough)
Hope this helps.
I figured out the answer with the help of suggestions provided by #Mel and Its very easy.
In the class that extends the corresponding PreferenceActivity element(e.g. shown below) just add this.setValue in the constructor of that preference element class. Thats it!
public class selectColorPreference extends ListPreference{
Context ctx = null;
private String error = null;
private int prefsRemoteSMSAccess;
MyPreferencesLibrary myPreferencesLibrary;
public selectColorPreference(Context context) {
super(context);
this.ctx = context;
myPreferencesLibrary = new MyPreferencesLibrary(ctx);
this.setValue(myPreferencesLibrary.getSharedPreferenceValueForColor());
}
}

Categories

Resources