Using Android Annotations. My prefs:
#SharedPref(value = SharedPref.Scope.APPLICATION_DEFAULT)
public interface MyPreferences {
#DefaultBoolean(true)
boolean myOption();
}
The preferences.xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="myOption"
android:title="My Option Name"/>
</PreferenceScreen>
My PreferencesActivity:
public class MyPreferencesActivity extends PreferenceActivity{
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
The problem is: in spite of the default value of myOption is true (and indeed - it is when calling MyPreferences_.myOption().get()) the checkbox in the preferences activity is unchecked by default.
The same happens for String preferences. They return default string given in #DefaultString annotation but it is not displayed in the PreferenceActivity. Only after I change the preference value from the activity, it is properly displayed.
I resolved default values duplication by:
values/preferences_defaults.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="default_myOption">true</bool>
<string name="default_myString">my string</string>
</resources>
Prefs:
#SharedPref(value = SharedPref.Scope.APPLICATION_DEFAULT)
public interface MyPreferences {
#DefaultRes(R.bool.default_myOption)
boolean myOption();
#DefaultRes(R.string.default_myString)
String myString();
}
And the preferences.xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
android:key="myOption"
android:defaultValue="#bool/default_myOption"
android:title="My Option Name"/>
<EditTextPreference
android:key="myString"
android:defaultValue="#string/default_myString"
android:title="My String Option"/>
</PreferenceScreen>
Now I can define default values for both annotations and PreferenceActivity in one XML file.
Related
I am writing an app which has both light and dark modes as declared here:
styles.xml
<style name="Noon" parent="Theme.AppCompat.NoActionBar">
<item name="upper_bg">#drawable/day_sky_top</item>
<item name="lower_bg">#drawable/day_sky</item>
<item name="android:statusBarColor">#color/colorPrimaryDark</item>
</style>
<style name="Night" parent="Theme.AppCompat.NoActionBar">
<item name="upper_bg">#drawable/night_sky_top</item>
<item name="lower_bg">#drawable/night_sky</item>
<item name="android:statusBarColor">#color/colorNightDark</item>
</style>
By following this answer, I created the following file:
/res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="customAttrs">
<attr name="upper_bg" format="reference" />
<attr name="lower_bg" format="reference" />
</declare-styleable>
</resources>
And customized my ImageViews like this:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/top_bg"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.65"
android:src="?attr/upper_bg"/>
<ImageView
android:id="#+id/bottom_bg"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.35"
android:src="?attr/lower_bg"/>
</LinearLayout>
(Note that this is part of the code, all tags are properly closed.)
Everything works well provided I have:
boolean night = true;
setTheme(night ? R.style.Night : R.style.Noon);
setContentView(R.layout.activity_main); // or whatever activity I'm in.
on every single Activity of my app. Is there a way to run this code ONCE so that my theme changes globally?
You can still add a BaseActivity to then override override fun onCreate(), delegating it the responsibility of setTheme() for any other Activity that inherits from it.
override fun onCreate(savedInstanceState: Bundle?) {
val night = true
setTheme(if (night) R.style.Night else R.style.Noon)
}
if you don't prefer BaseActivity, you can create an extension function somewhere in charged of set theme according user preferences:
fun Activity.setTheme() {
val night = true
// Or even have more than two theme styles
this.setTheme(if (night) R.style.Night else R.style.Noon)
}
To then be called like this:
override fun onCreate(savedInstanceState: Bundle?) {
setTheme()
}
UPDATE: Java code
This class would be your base class for each required Activity
public abstract class BaseActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
boolean night = true;
setTheme(night ? R.style.Night : R.style.Noon);
}
}
So now, imagine you have to implement "Feature 1" and "Feature 2", so that, you just inherits them from BaseActivity.
"Feature 1":
public class Feature1Activity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // <- BaseActivity's onCreate() will set theme for you
setContentView(R.layout.activity_feature_1);
}
}
"Feature 2":
public class Feature2Activity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // <- BaseActivity's onCreate() will set theme for you
setContentView(R.layout.activity_feature_2);
}
}
Yes you can !
You have to use method setDefaultNightMode() of class AppCompatDelegate that take the value of the theme you want to apply , the values available is MODE_NIGHT_NO & MODE_NIGHT_YES & MODE_NIGHT_FOLLOW_SYSTEM for light , night and phone default theme respectively , you can do something like this :
AppCompatDelegate.setDefaultNightMode(THE_MODE_YOU_WANT);
Its important to note that starting with AppCompat v1.0.0 , when this method get called it recreates all running activity to apply the theme .
If you are running Api 29 or higher , you should consider using Force Dark , you can check it all in the official document to get better understanding of it .
Enjoy !
My app could have a couple of checkboxes that I'd rather the user didn't have to re-check each time the app is chased from memory. So I've been reading about preferences.
I'm not sure I've seen anything as complicated as "a modern fragment-based PreferenceActivity", so I was looking for guidance/example from AS 1.1.0, but I got code with a total of 9 references in 4 hunks of code to addPreferencesFromResource, getPreferenceScreen, and findPreference, all of which carry this blurb:
This method was deprecated in API level 11. This function is not relevant for a modern fragment-based PreferenceActivity.
To have the code presented with deprecated code by two IDEs is one thing; that there's no indication of what each method has been replaced by is another (but there's nothing replacing it, as it happens).
Would it really be that hard for AS to produce "a modern fragment-based PreferenceActivity"? If so, I guess I'm right about the complexity.
Am I missing something? Am I supposed to just write/extend deprecated code? Turn off compiler checking of deprecated stuff?
Does anyone know of a nice tutorial for (or an example of) a simple (like two checkboxes) "modern fragment-based PreferenceActivity"? Or ANYthing that saves preferences but isn't deprecated?
I asked, "Does anyone know of a nice tutorial for (or an example of) a simple (like two checkboxes) modern fragment-based PreferenceActivity? Or ANYthing that saves preferences but isn't deprecated?"
I do as of 9pm last night. * EDITED 5/16/15 *
Here is a complete, working preferences activity, though it doesn't have all drawable, mipmap, dimens, and values folders and files. I downloaded it from advice in Sams Teach Yourself Android App Development in 24 Hours and modified it. Emphasis mine.
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.whatever.prefs"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="16" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.whatever.prefs.MainActivity"
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.whatever.prefs.SettingsActivity"
android:label="#string/title_activity_settings" >
</activity>
</application>
</manifest>
MainActivity.java:
package com.whatever.prefs;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
public class MainActivity extends Activity {
public static final String SETTINGS = "com.whatever.prefs.settings";
public static final String FIRST_USE = "com.whatever.prefs.firstUse";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* BAD IDEA--CREATES NEW SET OF PREFS
SharedPreferences preferences = getSharedPreferences(SETTINGS, MODE_PRIVATE);
Do this v v v v v v v v v v v instead! */
preferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean firstUse = preferences.getBoolean(FIRST_USE, true);
if (firstUse){
Toast helloMessage = Toast.makeText(getApplicationContext(), "Hello First Time
User",Toast.LENGTH_LONG);
helloMessage.show();
Editor editor = preferences.edit();
editor.putBoolean(FIRST_USE, false);
editor.commit();
}
else {
Toast.makeText(getApplicationContext(), "Second+ use", Toast.LENGTH_SHORT).show();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_settings:
Intent intent = new Intent(this, SettingsActivity.class);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
SettingsFragment.java:
package com.whatever.prefs;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.util.Log;
public class SettingsFragment extends PreferenceFragment
implements OnSharedPreferenceChangeListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
#Override
public void onResume() {
super.onResume();
getPreferenceScreen().getSharedPreferences().
registerOnSharedPreferenceChangeListener(this);
}
#Override
public void onPause() {
super.onPause();
getPreferenceScreen().getSharedPreferences().
unregisterOnSharedPreferenceChangeListener(this);
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
SharedPreferences sharedPref =
PreferenceManager.getDefaultSharedPreferences(getActivity());
if (key.equalsIgnoreCase("pie_type")){
Log.w("Settings", sharedPref.getString(key, ""));
}
}
}
res\menu\activity_main.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<item
android:id="#+id/menu_settings"
android:orderInCategory="100"
app:showAsAction="ifRoom"
android:title="#string/menu_settings"/>
</menu>
res\layout\activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Hi world" />
</RelativeLayout>
res\layout\activity_settings.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.whatever.prefs.SettingsFragment"
android:id="#+id/settings_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Hour11App</string>
<string name="hello_world">Hello world!</string>
<string name="menu_settings">Settings</string>
<string name="title_activity_settings">Settings</string>
<string-array name="pie_array">
<item>apple</item>
<item>blueberry</item>
<item>cherry</item>
<item>coconut cream</item>
</string-array>
</resources>
styles.xml:
<resources>
<!-- Base application theme, dependent on API level. This theme is replaced
by AppBaseTheme from res/values-vXX/styles.xml on newer devices. -->
<style name="AppBaseTheme" parent="android:Holo.ButtonBar">
<!-- Theme customizations available in newer API levels can go in
res/values-vXX/styles.xml, while customizations related to
backward-compatibility can go here.-->
</style>
<!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
</resources>
SettingsActivity.java:
package com.whatever.prefs;
import android.app.Activity;
import android.os.Bundle;
public class SettingsActivity extends Activity {
public static final String SETTINGS = "com.whatever.prefs.settings";
public static final String FIRST_USE = "com.whatever.prefs.firstUse";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
}
}
dimens.xml:
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>
res\xml\preferences.xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="Images">
<CheckBoxPreference
android:key="hires"
android:title="Hi-Res Images"
android:summary="Show high quality images. These take longer to load"
android:defaultValue="False" />
</PreferenceCategory>
<PreferenceCategory
android:title="Pie Info">
<CheckBoxPreference
android:key="pie"
android:title="Pie"
android:summary="Like Pie"
android:defaultValue="true" />
<ListPreference
android:dependency="pie"
android:key="pie_type"
android:title="Pie Type"
android:summary="Preferred pie type for eating"
android:dialogTitle="Type of Pie"
android:entries="#array/pie_array"
android:entryValues="#array/pie_array"
android:defaultValue="apple" />
<EditTextPreference
android:key="more_info"
android:title="More Info"
android:summary="More about pies"
android:defaultValue="" />
</PreferenceCategory>
<PreferenceScreen
android:key="second_preferencescreen"
android:title="Second Screen of Settings">
<EditTextPreference
android:key="extraA"
android:title="More Data"
android:summary="Another EditTextPreference"
android:defaultValue="" />
<EditTextPreference
android:key="ExtraB"
android:title="Even More Info"
android:summary="What more can we say"
android:defaultValue="" />
</PreferenceScreen>
</PreferenceScreen>
I am working of settings activity for my application. I seted up two headers for preferences and then i made fragment for each header. Problem is when i click on header, nothing shows up. Thank you for your help.
Here is my preference activity
public class Preferences extends PreferenceActivity {
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.prefheads, target);
}
}
Here is first fragment
public class Prefs1 extends PreferenceFragment {
public static class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fragment_prefs1);
}
}
}
Here is second fragment:
public class Prefs2 extends PreferenceFragment {
public static class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fragment_prefs2);
}
}
}
XML files:
Headers
<?xml version="1.0" encoding="utf-8"?>
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<header
android:fragment="com.example.lubys.prefs.prefs1"
android:title="First category"
android:summary="This doesnt need summary" />
<header
android:fragment="com.example.lubys.prefs.prefs2"
android:title="Second cat"
android:summary="This is awful summary">
</header>
</preference-headers>
Fragment1:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.lubys.prefs.prefs1"
android:layout_width="match_parent"
android:layout_height="match_parent">
<PreferenceCategory
android:title="the title"
android:key="key2">
<EditTextPreference android:title="Your Name"
android:key="username"
android:summary="Please provide your username"></EditTextPreference>
<CheckBoxPreference android:title="Application Updates"
android:defaultValue="false"
android:summary="This option if selected will allow the application to check for latest versions."
android:key="applicationUpdates" />
<ListPreference
android:title="Download Details"
android:summary="Select the kind of data that you would like to download"
android:key="downloadType"
android:defaultValue="1"
android:entries="#array/c"
android:entryValues="#array/c" />
</PreferenceCategory>
</PreferenceScreen>
Fragment2:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.lubys.prefs.prefs1"
android:layout_width="match_parent"
android:layout_height="match_parent">
<PreferenceCategory
android:title="sms"
android:key="pref_key_storage_settings">
<CheckBoxPreference
android:key="saas"
android:summary="asde"
android:title="Something"
android:defaultValue="false"/>
</PreferenceCategory>
</PreferenceScreen>
check if u didn't get:
java.lang.RuntimeException:
Subclasses of PreferenceActivity must override isValidFragment(String) to verify that the Fragment class is valid!
XXX has not checked if fragment YYY is valid.
isValidFragment Android API 19
when android's isValidFragment() from PreferenceActivity gets called?
http://securityintelligence.com/new-vulnerability-android-framework-fragment-injection#.VRGSv1V_NBc
I want to get a pick file dialog in my PreferenceActivity how do I get to that? Can I override onClick for PreferenceActivity somehow? Android API 14.
Here's my PreferensActivity:
import android.preference.PreferenceActivity;
import java.util.List;
public class SettingsActivity extends PreferenceActivity {
#Override
public boolean onIsMultiPane() {
return true;
}
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.pref_head, target);
}
}
Header xml is:
<?xml version="1.0" encoding="utf-8"?>
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="com.bfx.rfid.FragmentSetApp"
android:icon="#android:drawable/ic_menu_call"
android:title="Application"
android:summary="Application settings">
</header>
<header
android:fragment="com.bfx.rfid.FragmentSetConnection"
android:icon="#android:drawable/ic_menu_call"
android:title="Connectivity"
android:summary="Connection settings">
</header>
</preference-headers>
PreferenceFragment class:
import android.os.Bundle;
import android.preference.PreferenceFragment;
public class FragmentSetApp extends PreferenceFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fragment_set_app);
}
}
PreferenceFragment xml is:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:key="work_directory"
android:title="Folder to work with">
</Preference>
<PreferenceCategory
android:title="Work with a database file">
<CheckBoxPreference
android:key="DB_default"
android:summary="Choose a default database file or pick one"
android:title="Choose a database file"
android:defaultValue="true"/>
<EditTextPreference
android:key="DB_URI"
android:title="Database file"
android:dependency="DB_default">
</EditTextPreference>
</PreferenceCategory>
</PreferenceScreen>
I don't think Andorid have a native file chooser, so you'll have to implement one yourself, or find a library.
You could then use the android:onClick property in PreferenceFragment.xml:
<EditTextPreference
android:key="DB_URI"
android:title="Database file"
android:onClick="startFileChooser"
android:dependency="DB_default">
and put this in you PreferenceFragment:
public void startFileChooser(MenuItem i){
// Start the file chooser here
}
Of course, how you would to that depends on what file chooser you decide to go with. You would probably end up with the path to the selected file, which you would insert into SharedPreference.
I have created an PreferenceActivity with a PreferenceFragment in it. But the content with all the settings is shown outside of it layout bounderies, what am I doing wrong?
SettingsActivity:
public class SettingsActivity extends PreferenceActivity {
#Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
}
public static class MyPreferenceFragment extends PreferenceFragment
{
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
}
preferences.xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="My list of Preferences">
<CheckBoxPreference android:title="Checkbox Preference"
android:defaultValue="false" android:summary="This preference can be true or false"
android:key="checkboxPref" />
<EditTextPreference android:name="EditText Preference"
android:summary="This allows you to enter a string"
android:defaultValue="Nothing" android:title="Edit This Text"
android:key="editTextPref" />
<RingtonePreference android:name="Ringtone Preference"
android:summary="Select a ringtone" android:title="Ringtones"
android:key="ringtonePref" />
<PreferenceScreen android:key="SecondPrefScreen"
android:title="Secondary Level" android:summary="This is a sub PreferenceScreen">
<intent android:action="android.intent.action.VIEW"
android:targetPackage="com.as400samplecode" android:targetClass="com.as400samplecode.Preferences2" />
</PreferenceScreen>
<Preference android:title="Custom Preference"
android:summary="This works almost like a button" android:key="customPref" />
</PreferenceCategory>
</PreferenceScreen>
I solved it by using a normal Activity instead of PreferenceActivity.
You are not code added for Preferences using SharedPreferences Class
I have adding one use your checkbox preference example check below code
public class PrefsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
//xml Preference.xml
<PreferenceCategory
android:title="PreferenceCategory A">
<CheckBoxPreference
android:key="checkbox_preference"
android:title="title_checkbox_preference"
android:summary="summary_checkbox_preference" />
</PreferenceCategory>
<PreferenceCategory
android:title="PreferenceCategory B">
<EditTextPreference
android:key="edittext_preference"
android:title="title_edittext_preference"
android:summary="summary_edittext_preference"
android:dialogTitle="dialog_title_edittext_preference" />
</PreferenceCategory>