Android testing preferences fragment by Expresso - android

I have got a problem with testing my code by Expresso. I wrote this code:
public class SettingsActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
public class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
XML:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/preferences_xml">
<PreferenceCategory
android:title="#string/application_settings"
android:id="#+id/application_settings_preferences">
<CheckBoxPreference
android:key="#string/key_processing_private_data_preference"
android:title="#string/title_processing_private_data_preference"
android:summary="#string/summary_processing_private_data_preference"
android:defaultValue="true"
android:dependency="#string/key_recording_audio_preference"
android:id="#+id/private_data_check_box_preference"/>
</PreferenceCategory>
<PreferenceCategory
android:title="#string/device_settings"
android:id="#+id/device_settings_preferences">
<CheckBoxPreference
android:key="#string/key_recording_audio_preference"
android:title="#string/title_recording_audio_preference"
android:summary="#string/summary_recording_audio_preference"
android:defaultValue="true"
android:id="#+id/recording_audio_check_box_preference"/>
</PreferenceCategory>
</PreferenceScreen>
My test rule:
#Rule
public ActivityTestRule<SettingsActivity> mActivityRule = new ActivityTestRule<>(
SettingsActivity.class);
I try test first checkbox:
onData(anything())
.inAdapterView(allOf(
isDescendantOfA(withId(R.id.preferences_xml)),
withId(R.id.application_settings_preferences)))
.atPosition(1)
.check(matches(isDisplayed()));
Test always fail with NoMatchingViewException: No views in hierarchy found matching:
http://prntscr.com/bv8xlb
And also I try:
String workingInBackgroundSummary = mActivityRule.getActivity().getBaseContext().getString(R.string.summary_working_in_background_preference);
onData(withSummaryText(workingInBackgroundSummary)).check(matches(isDisplayed()));
Test always fail with NullPointerException:
http://prntscr.com/bv8w04.
onView(withId(R.id.working_in_background_check_box_preference)).check(matches(isDisplayed()));
Also fail with NoMatchingViewException.
Would someone show an example with correct test case?

(Posted on behalf of the OP).
Solved:
onData(allOf(
is(instanceOf(Preference.class)),
withKey(key),
withSummary(R.string.summary_working_in_background_preference),
withTitle(R.string.title_working_in_background_preference)))
.onChildView(withText(title))
.check(matches(isCompletelyDisplayed()));

Related

How to have ActionBar in PreferenceActivity?

I have an android app with a UserSettingsActivity derived from PreferenceActivity. When I open this activity, no menu bar is shown.
In order to have the 'bar' or 'action bar' on top of the settings screen, I searched for it in google on found this link and this link. Because the first answer seems outdated, I tried the suggestion in the second link which does not work. Therefore I am posting this question again.. Here are the pieces of code I am using:
preferences.xml ( I added just the android.support.v7.widget.Toolbar part):
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:navigationContentDescription="#string/abc_action_bar_up_description"
android:background="?attr/colorPrimary"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="#string/action_settings"
/>
<ListPreference
android:title="#string/UpdateInterval"
android:summary="#string/UpdateIntervalText"
android:key="updateInterval"
android:defaultValue="24"
android:entries="#array/listArray"
android:entryValues="#array/listValues" />
<ListPreference
android:title="#string/LimitSetting"
android:summary="#string/LimitSettingText"
android:key="limitSetting"
android:entries="#array/limitArray"
android:entryValues="#array/limitValues" />
<Preference
android:key="reset"
android:title="Reset database"
android:summary="This will remove every entry in the database"
/>
</PreferenceScreen>
and UserSettingsActivity.java (I added the onPostCreate part):
public class UserSettingActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.xml.preferences, root, false);
root.addView(bar, 0); // insert at top
bar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
public class MyPreferenceFragment extends PreferenceFragment {
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences); // <<-- ERROR HERE
// doing something here
}
}
}
However, I get the following error:
Caused by: java.lang.ClassCastException: android.support.v7.widget.Toolbar cannot be cast to android.preference.Preference
on the marked line. Without the added pieces in those two files the app did work, without the (action)-bar, however...
What seems to be the problem here?
Try this.
Extend your activity with AppCompatActivity
Then add toolbar. Refer here: -http://coderzpassion.com/android-working-with-material-design/
PreferenceActivity is now deprecated. Use PreferenceFragment inside any other activity, which can be extended from any parent activity other than PreferenceActivity

Keep the actionbar displayed in when changing PreferenceScreen

I'm trying to display a actionbar in my preference screen.
In order to do so I added the following code in my SettingActivity
public class PreferencesActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preferences_activity);
getFragmentManager().beginTransaction()
.replace(R.id.container, new PreferencesFragment()).commit();
getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
then the rest of my code in PreferencesFragment.
This works fine, but as soon as I press on a PreferenceScreen preference, the actionbar is hidden. If I go back to the preference main screen I can see it again.
Any idea how to keep the actionbar displayed (and updated with the PreferenceScreen label) ?
Edit: Looking at the PreferenceScreen code it looks like a full screen Dialog is opened when the PreferenceScreen is clicked on. Because my preference has a title the Dialog should display a title as well... but it doesn't
// Set the title bar if title is available, else no title bar
final CharSequence title = getTitle();
Dialog dialog = mDialog = new Dialog(context, context.getThemeResId());
if (TextUtils.isEmpty(title)) {
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
} else {
dialog.setTitle(title);
}
I finally managed to find a way to do this.
It's kind of ugly but it works.
First I add an the same Intent to every PreferenceScreen definition in my preferences.xml file (make sure to update the value of the extra parameter)
<PreferenceScreen
android:key="pref1"
android:summary="Summary1"
android:title="Title1" >
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="my.package"
android:targetClass="my.package.activity.PreferencesActivity" >
<extra android:name="page" android:value="pref1" />
</intent>
...
</PreferenceScreen>
BTW my.package.activity.PreferencesActivity is my current Preference Activity
Then I add an intent-filter in the Manifest
<activity
android:name=".activity.PreferencesActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="#string/settings" >
<intent-filter android:label="Pref" >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.PREFERENCE" />
</intent-filter>
</activity>
I add some code in the PreferenceActivity to handle this
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preferences_activity);
this.fragment = new PreferencesFragment();
this.fragment.setActivityIntent(getIntent());
getFragmentManager().beginTransaction()
.replace(R.id.container, this.fragment).commit();
}
Finally I add the following code in my PreferencesFragment class
public void setActivityIntent(final Intent activityIntent) {
if (activityIntent != null) {
if (Intent.ACTION_VIEW.equals(activityIntent.getAction())) {
if (intent.getExtras() != null) {
final String page = intent.getExtras().getString("page");
if (!TextUtils.isEmpty(page)) {
openPreferenceScreen(page);
}
}
}
}
private void openPreferenceScreen(final String screenName) {
final Preference pref = findPreference(screenName);
if (pref instanceof PreferenceScreen) {
final PreferenceScreen preferenceScreen = (PreferenceScreen) pref;
((PreferencesActivity) getActivity()).setTitle(preferenceScreen.getTitle());
setPreferenceScreen((PreferenceScreen) pref);
}
}
Had the same issue. Nested PreferenceScreens did not have an ActionBar. After stepping through the code, it appears to be caused by a conflict between AppCompatActivity and PreferenceScreen.
On one hand AppCompatActivity provides its own action bar, and therefore requires a theme descending from Theme.AppCompat which specifies windowNoTitle = true somewhere (could not pinpoint exactly where). On the other -- PreferenceScreen uses platform Dialog with the activity theme (rather than sub-theme, e.g., dialogTheme). Could be a bug.
If you don't care about Theme.AppCompat, here's a simple workaround that works on API 21+:
use android.preference.PreferenceActivity as the base class for your activity
create a theme for that activity:
<!-- This theme is used to show ActionBar on sub-PreferenceScreens -->
<style name="PreferenceTheme" parent="">
<item name="android:windowActionBar">true</item>
<item name="android:windowNoTitle">false</item>
</style>
specify android:theme="#style/PreferenceTheme" for this activity in the AndroidManifest.xml
What you'll get is more like a standard window title than a full ActionBar. I haven't yet figured out how to add a working back button, etc.
If you want to remain compatible with AppCompatActivity and the related themes, you'll need to request FEATURE_NO_TITLE on the activity window. Otherwise, you'll end up with two action bars (the built-in on top, and the support on bottom) in the top-level PreferenceScreen.
Since google sadly didn't fixed it until now, there is actually one much easier solution:
Set your SettingsActivity class to extend from just "Activity".
public class SettingsActivity extends Activity { ...
Create a new Theme in your v21/styles folder for your SettingsActivty and set the parent to "Theme.Material.*"
<style name="CustomThemeSettings" parent="android:Theme.Material">
<item name="android:colorPrimary">#color/...</item>
<item name="android:colorPrimaryDark">#color/...</item>
<item name="android:colorAccent">#color/...</item>
</style>
Set your new theme in your Manifest.xml file:
<activity
android:name=".SettingsActivity"
android:label="#string/title_activity_settingsactivity"
android:theme="#style/CustomThemeSettings" >
</activity>
It just works :)
(Optional) If you want to provide Material Design support for older Devices
you can put the SettingsActivity in an v21+ folder and create a other
SettingsActivity for older devices which has the parent AppCompat.
I have made an app that does have an action bar in the preferences activity. I can't seem to see the key to doing that, although I do remember it took me some time to nail it right.
It seems like our codes are quite similar. The only thing that gets to my attention is this import: import android.support.v7.app.ActionBarActivity;
Let me know if that helps any
public class SettingsActivity extends ActionBarActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new PrefsFragment() ).commit();
} // End of onCreate
static public class PrefsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences); // Load the preferences from an XML resource
}
} // end of PrefsFragment
}
Addition: do you have this in your styles.xml?
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
</style>
but as soon as I press on a PreferenceScreen preference, the actionbar is hidden. If I go back to the preference main screen I can see it again.
I guess you are launching a new activity when clicked on the preference
Your Preference fragment should look like this
public static class PreferencesFragment extends PreferenceFragment {
public PlaceholderFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.your_preferences);
}
}
Then sample your_preferences as below
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent" >
<PreferenceCategory
android:title="#string/pref_category_title">
<PreferenceScreen
android:title="#string/title"
android:summary="#string/summary">
<intent
android:targetClass="com.your.package.Youractivity "
android:targetPackage="com.your.package"/>
</PreferenceScreen>
<PreferenceScreen
android:title="#string/another_title">
<intent
android:targetClass="com.your.package.activity2"
android:targetPackage="com.your.package.activity"/>
</PreferenceScreen>
</PreferenceCategory>
And finally the main thing Youractivity should extend from ActionBarActivity
public class Youractivity extends ActionBarActivity {
}
The above code works for me.
As you mentioned in the question the fullscreen Dialog might be a problem.
Try to change the Dialog style to:
<style name="Theme.Holo.Dialog">
or to style which has these properties:
<item name="windowActionBar">true</item>
<item name="windowNoTitle">false</item>
Here you can read how to get reference to the Dialog.
Note: Only for API>=14
Source1 and Source2
PreferenceActivity1.java
public class PreferenceActivity1 extends android.preference.PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref1);
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
root.addView(bar, 0); // insert at top
bar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
}
PreferenceActivity2.java
public class PreferenceActivity2 extends android.preference.PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref2);
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
root.addView(bar, 0); // insert at top
bar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
}
settings_toolbar.xml(layout)
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:navigationContentDescription="#string/abc_action_bar_up_description"
android:background="?attr/colorPrimary"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="#string/app_name"
/>
pref1.xml(xml)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="#android:style/Theme.Light" >
<PreferenceCategory android:title="Main Preferences" >
<CheckBoxPreference
android:key="wifi enabled"
android:title="WiFi" />
</PreferenceCategory>
<PreferenceScreen
android:key="key1"
android:summary=""
android:title="Wifi Settings" >
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.example.PreferenceActivity2"
android:targetPackage="com.example" />
</PreferenceScreen>
</PreferenceScreen>
pref2.xml(xml)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="#android:style/Theme.Light" >
<PreferenceCategory android:title="Wifi Settings" >
<CheckBoxPreference
android:key="prefer wifi"
android:title="Prefer WiFi" />
</PreferenceCategory>
</PreferenceScreen>
Manifest
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name="com.example.PreferenceActivity1"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.PreferenceActivity2"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
Result
I created PreferenceCompatActivity that is an AndroidX-compatible drop-in replacement for the old PreferenceActivity.
It uses a compat fragment internally. Be sure to use the import only the androidx.preference.XXX classes instead of the android.preference.XXX ones.
public class PreferenceCompatActivity extends AppCompatActivity implements AppCompatCallback, PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
private PreferenceFragmentCompat fragment;
public void addPreferencesFromResource(#XmlRes int preferencesResId) {
fragment = new RootPreferencesFragment(preferencesResId);
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commitNow();
}
public PreferenceManager getPreferenceManager() {
return fragment.getPreferenceManager();
}
public PreferenceScreen getPreferenceScreen() {
return fragment.getPreferenceScreen();
}
public Preference findPreference(CharSequence key) {
return fragment.findPreference(key);
}
#Override
public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
LowerPreferencesFragment lowerFragment = new LowerPreferencesFragment(pref);
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, lowerFragment).addToBackStack("lower").commit();
return true;
}
public static class RootPreferencesFragment extends PreferenceFragmentCompat {
private int preferencesResId;
public RootPreferencesFragment(int preferencesResId) {
this.preferencesResId = preferencesResId;
}
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(preferencesResId);
}
}
public static class LowerPreferencesFragment extends PreferenceFragmentCompat {
private PreferenceScreen prefs;
public LowerPreferencesFragment() {
}
public LowerPreferencesFragment(PreferenceScreen prefs) {
this.prefs = prefs;
}
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
if (prefs != null) {
setPreferenceScreen(prefs);
prefs = null;
}
}
}
}
Or see this gist.

Daydream's setting page is crashing on load

I am making a Plain Text Daydream on android and cannot open the settings screen. The rest of the daydream runs fine, but the settings don't. This is the settings xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
android:key="pref_text"
android:title="Text on daydream"
android:dialogTitle="Text on daydream:"
android:defaultValue="Hello world!" />
</PreferenceScreen>
I'm using PreferenceFragments too. I assume it's the XML, but just in case here are the fragment and activity too:
public class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.settings);
}
}
and
public class SettingsActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
}
How do I fix it?
All I had to do was set the settings activity as the default. Then it's fixed!

PreferenceActivity: set icon in action bar

I use a PreferenceActivity with encapsulated preference screens.
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="prefs" >
<PreferenceScreen android:title="#string/pref_user_1" >
some text preference
</PreferenceScreen>
<PreferenceScreen android:title="#string/pref_user_2" >
some prefs
<PreferenceScreen android:title="#string/pref_user_2_1" >
some prefs
</PreferenceScreen>
</PreferenceScreen>
</PreferenceScreen>
my inherited preferenceActivity class has the folowing onCreate method
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new PrefsFragment()).commit();
}
and has an inner class
public static class PrefsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener {
public PrefsFragment() {
super();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.myprefs);
...
}
in the first screen I get the correct icon in the action bar (I call it Action Bar even if I haven't the <) but I would like to change the icon in the following screen.
I have tried getActivity().getActionBar().setIcon(R.drawable.ic_act); in the inner class and also in the preferenceActivity, but it doesn't change anything.
Does someone know how to do it, if possible?

Android SwitchPreference using Fragment

I've an activity to represent a preference screen using one FragmentPreference.
Activity layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<fragment
android:id="#+id/frag"
android:name="com.test.preference.Frag"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Preference xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="General">
<SwitchPreference android:title="Enable module" android:key="asd" android:summaryOff="Turn on module" android:summaryOn="Turn off module"/>
<SwitchPreference android:title="Post in application" android:key="qwe" android:summary="Show posts in application"/>
<SwitchPreference android:title="Post in widget" android:key="zxc" android:summary="Show posts in widget"/>
</PreferenceCategory>
Activity class:
public class TestActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
and finally Fragment class:
public class Frag extends PreferenceFragment{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.test);
}
}
Activity starts normally but when I click on a first SwitchPreference, its status remains equal and the other two SwitchPreference change their status!
If I use deprecated method (PreferenceActivity without fragment) it works.
Someone can help me?
Giulio

Categories

Resources