How to set default theme for LatinIME in AOSP - android

I need some guidance with setting default theme for LatinIME on AOSP. I am not quite sure where this value is stored.
First I tried setting the theme in ThemeSettingsFragment.java located in LatinIME. So now everytime a theme was set or changed it would always pick mine. Later on I found out this class is only called when we open Keyboard themes in Settings (Language & Input -> Android Keyboard (AOSP) -> Appearance & layouts -> Theme). Resulting in theme being changed only IF we opened these view. My goal is to have my theme set when I build AOSP.
Next I suspected the value could be stored in some global configuration and that led me to class InputMethodManagerService.java where I found constant Settings.Secure.DEFAULT_INPUT_METHOD. But that didn't lead me anywhere worth while.
Anyone ever worked on something similar or knows the solution to my problem?

You can try making changes here : https://github.com/LineageOS/android_packages_inputmethods_LatinIME/blob/cm-14.1/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java#L56-L58
I hope it helps.

Sanyam Jain is right to the point, adding few more details to it.
packages/inputmethods/LatinIME/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java to be modified to change the default keyboard layout or you can add your custom layout there and also make sure the options added/changed correctly in packages/inputmethods/LatinIME/java/res/values/keyboard-themes.xml
In my case, I want material dark theme as default and first option in the keyboard layout settings. below are the changes I made,
KeyboardTheme.java
public static final int DEFAULT_THEME_ID = THEME_ID_LXX_DARK;
private static KeyboardTheme[] AVAILABLE_KEYBOARD_THEMES;
/* package private for testing */
static final KeyboardTheme[] KEYBOARD_THEMES = {
new KeyboardTheme(THEME_ID_LXX_DARK, "LXXDark", R.style.KeyboardTheme_LXX_Dark,
// This has never been selected as default theme.
Build.VERSION_CODES.LOLLIPOP),
new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS,
// This has never been selected because we support ICS or later.
VERSION_CODES.BASE),
new KeyboardTheme(THEME_ID_KLP, "KLP", R.style.KeyboardTheme_KLP,
// Default theme for ICS, JB, and KLP.
VERSION_CODES.ICE_CREAM_SANDWICH),
new KeyboardTheme(THEME_ID_LXX_LIGHT, "LXXLight", R.style.KeyboardTheme_LXX_Light,
// Default theme for LXX.
VERSION_CODES.BASE),
};
In keyboard-themes.xml
<string-array name="keyboard_theme_names" translatable="false">
<item>#string/keyboard_theme_material_dark</item>
<item>#string/keyboard_theme_material_light</item>
<item>#string/keyboard_theme_holo_white</item>
<item>#string/keyboard_theme_holo_blue</item>
</string-array>
<!-- An element must be a keyboard theme id of
{#link com.android.inputmethod.keyboard.KeyboardTheme#THEME_ID_ICS} etc. -->
<integer-array name="keyboard_theme_ids" translatable="false">
<item>4</item>
<item>3</item>
<item>2</item>
<item>0</item>
</integer-array>

Related

Change values inside an xml file at runtime [duplicate]

Let's say, on my API call I have a parameter that's called color. Is it possible to edit or modify an existent R.colors.color to assign the color from the API result?
As an example:
I make a call to my API and it returns green, now I want to load my app with i.e (green Toolbar, green TextView color, etc.), is that possible?
My first thought was:
Create a item on colors.xml called demo then assign it a default color, then use this demo color wherever I want (Button, TextView, etc.) Then I thought it could be possible to change this value programmatically with the result from the API so I wouldn't need to create a SharedPreferences or something like that and for avoiding more code.
As #Y.S. said to me
Unfortunately, you WILL have to set the color of the text or view manually everywhere ... :(
I would like if there is other way to do it, since I don't know how many Activities my project will contain, so if is there other way to do it I'm glad to hear other guesses.
EDIT
I'm trying the #Jared Rummler answer and maybe i'm doing something wrong... I've created a simple Json and I put on my Assets I parse the Json and I put it on a GlobalConstant then I made a "simple app".
First of all I have a TextView and a Button which contains the "your_special_color", and the return of it I put the GlobalConstant int as follows :
case "your_special_color":
return GlobalConstant.color;
Then what I tried is my first Activity has 1 TextView and 1 Button as I said before and they have the color "your_special_color" that I don't want to change it, BUT I have an Intent on my Button to open the other Activity that contains the same but with the GlobalConstant.color and it doesn't change.
I tried it doing this (my second Activity):
public class Main2Activity extends AppCompatActivity {
private Res res;
#Override public Resources getResources() {
if (res == null) {
res = new Res(super.getResources());
}
return res;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
Did I miss something?
Oh.. I figured it out I guess is doing this on my MainActivity2 ?
Button btn = (Button)findViewById(R.id.button2);
btn.setBackgroundColor(res.getColor(R.color.your_special_color));
You can create a class which extends Resources and override the methods getColor(int) and getColor(int, Theme).
Example:
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="your_special_color">#FF0099CC</color>
</resources>
Res.java
public class Res extends Resources {
public Res(Resources original) {
super(original.getAssets(), original.getDisplayMetrics(), original.getConfiguration());
}
#Override public int getColor(int id) throws NotFoundException {
return getColor(id, null);
}
#Override public int getColor(int id, Theme theme) throws NotFoundException {
switch (getResourceEntryName(id)) {
case "your_special_color":
// You can change the return value to an instance field that loads from SharedPreferences.
return Color.RED; // used as an example. Change as needed.
default:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return super.getColor(id, theme);
}
return super.getColor(id);
}
}
}
BaseActivity.java
public class BaseActivity extends AppCompatActivity {
...
private Res res;
#Override public Resources getResources() {
if (res == null) {
res = new Res(super.getResources());
}
return res;
}
...
}
This is the approach I have used in one of my apps, Root Check. If you override getResources in your activities and main application class you can change the theme programmatically (even though themes are immutable). If you want, download the app and see how you can set the primary, accent, and background colors from preferences.
If you take a look at the Accessing Resources document, what it says is that ...
Once you provide a resource in your application, you can apply it by referencing its resource ID. All resource IDs are defined in your project's R class, which the aapt tool automatically generates.
Furthermore,
When your application is compiled, aapt generates the R class,
which contains resource IDs for all the resources in your res/
directory. For each type of resource, there is an R subclass (for
example, R.drawable for all drawable resources), and for each
resource of that type, there is a static integer (for example,
R.drawable.icon). This integer is the resource ID that you can use
to retrieve your resource.
What this is saying, essentially, is that pretty much everything held as a resource in the res/ directory is compiled and referenced as an unchangeable constant. It is for this reason that the values of resource elements cannot be changed programmatically/at runtime, because they are compiled. As opposed to local/global variables & SharedPreferences, resource elements are represented in program memory as fixed, unchangeable objects. They are held in a special read-only region of program memory. In this regard, see also Changing value of R.String Programmatically.
What you can do is, to avoid using the same code at a thousand places in your project, create a common function that changes the value of the color in the SharedPreferences and use this method everywhere. I'm sure you knew this already, of course.
To reduce the amount of code you need to add to the project, there is an alternative. I have previously used the calligraphy library which allowed me to fix the font style & color throughout the app. This may be of some good use to you, check it out ...
R class is not supposed to be edited. It merely contains references to your resources.
You will need to set it manually. However, to reduce the burden of setting it manually you can try to use special libraries for preference saving, for instance:
Saber - https://github.com/jug6ernaut/saber
PreferenceBinder - https://github.com/denley/preferencebinder
(full list of similar libraries https://android-arsenal.com/tag/75)
Also, you might want to think about another way of applying styles and passing parameters - consider you would want to add some other parameters like height, width etc. For that purpose, you can define custom attribute in themes.xml/styles.xml:
<attr name="demoColor" format="reference|color" />
then define styles:
<style name="BaseActivity">
</style>
<style name="GreenActivity" parent="#style/BaseActivity">
<item name="demoColor">#00cd00</item>
</style>
<style name="RedActivity" parent="#style/BaseActivity">
<item name="demoColor">#ff0000</item>
</style>
then use that color in your xml like this:
... android:background="?demoColor" ...
and switch between GreenActivity and RedActivity styles in Activity.onCreate:
setTheme(isGreenStyle() ? R.style.GreenActivity : R.style.RedActivity)
setContentView(...)
With the above approach, you will be able to easily configure your styles in xml and it should be less code and easier to refactor in future. (You will still need to have one variable in preference to save whether you have green or red style)
Another way, if you want to show demos of your app with different colors is to use build variants / flavors for loading your app with different colors and styles (it is for build time - not runtime):
app/src/main/res/colors.xml
<resources>
<color name="demoColor">#00cd00</color>
</resources>
app/src/buildVariant/res/colors.xml
<resources>
<color name="demoColor">#ff0000</color>
</resources>
Now you can quickly switch between "main" and "buildVariant" in Build Variants menu and launch your app with different "demo" colors. The same way you can customize a lot of other attributes.
Search for "Build Variants" here http://developer.android.com/tools/building/configuring-gradle.html
You can't change an app's resources, they are all constants. Instead you can save your color in SharedPrefences and use the color from there.
See How to use SharedPreferences in Android to store, fetch and edit values.
If your app already has a R.color.green defined and you just want to access it based on what API returned you use:
int resourceID = getResources().getIdentifier("green", "color", getPackageName());
store hex color codes into sharedpreferences and then use parsecolor function store your all hexcodes of colors into sessions as a string and whenever you want to change color of perticular button ,textview..just retrive that color code from session and use it as
for ex.
session.setString("white","#FFFFFF");
String colorname=session.getString("white");yourtextview.setBackgroundColor(Color.parseColor(colorname);

Theme Dependent Android Strings

Let's say I have an application with 2 themes: masculine and feminine. The themes simply change out the color palette and a few drawables to appeal to the user's preferred tastes.
Many thanks to http://www.androidengineer.com/2010/06/using-themes-in-android-applications.html for his hints at making that work.
But now lets say I want to get a little cuter with the app and not only change the colors and drawables, but I also want to change the strings. For instance, I might want to add a pirate theme and then "Submit" would be "Arrrrgh!"
So, my basic question is: How can I change the strings throughout my app via user selectable themes?
Edit:
Making this up: the app has 12 buttons and 32 text views I'd like to have theme dependent and I'd like to accomplish this without a giant mapping or a slew of custom attrs.
All 3 of the current solutions will work. Looking for something cleaner though I don't know that such a beast exists.
Yes, it can be done, and here's how: first you'll have to define a theme attribute, like so:
<attr name="myStringAttr" format="string|reference" />
Then, in your themes, add this line
<item name="myStringAttr">Yarrrrr!</item>
or
<item name="myStringAttr">#string/yarrrrr</item>
You can then use this attribute in an XML file like so (note the ? instead of #).
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="?attr/myStringAttr" />
or, from code, like so:
public CharSequence resolveMyStringAttr(Context context)
{
Theme theme = context.getTheme();
TypedValue value = new TypedValue();
if (!theme.resolveAttribute(R.attr.myStringAttr, value, true)) {
return null;
}
return value.string;
}
Let's say I have an application with 2 themes: masculine and feminine. The themes simply change out the color palette and a few drawables to appeal to the user's preferred tastes.
How about we pretend that you're doing something else? This is a design anti-pattern, associating particular colors based on gender (e.g., "girls like pink").
This is not to say that your technical objective is bad, just that this is a really stereotypical example.
For instance, I might want to add a pirate theme and then "Submit" would be "Arrrrgh!"
Only if "Cancel" maps to "Avast!".
How can I change the strings throughout my app via user selectable themes?
You have not said where those strings are coming from. Are they string resources? Database entries? Ones that you are retrieving from a Web service? Something else?
I will assume for the moment that these are string resources. By definition, you will need to have N copies of the strings, one per theme.
Since gender and piratical status are not things tracked by Android as possible resource set qualifiers, you can't have those string resources be in different resource sets. While they could be in different files (e.g., res/values/strings_theme1.xml), filenames are not part of resource identifiers for strings. So, you will wind up having to use some sort of prefix/suffix to keep track of which strings belong with which themes (e.g., #string/btn_submit_theme1).
If these strings are not changing at runtime -- it's just whatever is in your layout resource -- you could take a page from Chris Jenkins' Calligraphy library. He has his own subclass of LayoutInflater, used to overload some of the standard XML attributes. In his case, his focus is on android:fontFamily, where he supports that mapping to a font file in assets.
In your case, you could overload android:text. In your layout file, rather than it pointing to any of your actual strings, you could have it be the base name of your desired string resource, sans any theme identifier (e.g., if the real strings are #string/btn_submit_theme1 and kin, you could have android:text="btn_submit"). Your LayoutInflater subclass would grab that value, append the theme name suffix, use getIdentifier() on your Resources to look up the actual string resource ID, and from there get the string tied to your theme.
A variation on this would be to put the base name in android:tag instead of android:text. android:text might point to one of your real string resources, to help with GUI design and such. Your LayoutInflater would grab the tag and use that to derive the right string at runtime.
If you will be replacing text with other text pulled from theme-based string resources, you could isolate your get-the-string-given-the-base-name logic into a static utility method somewhere that you could apply.
While getting this right initially will take a bit of work, it will scale to arbitrary complexity, in terms of the number of affected UI widgets and strings. You still have to remember to add values for all themes for any new strings you define (bonus points for creating a custom Lint check or Gradle task for validating this).
Since a resource is just an int at heart you could store a number of them at runtime and them substitute them in procedurally as you use them.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="OK_NORMAL">Okay</string>
<string name="OK_PIRATE">Yaaarrrr!</string>
<string name="OK_NINJA">Hooooaaa!</string>
</resources>
public enum ThemeMode {
PIRATE,
NINJA,
NORMAL;
}
public class MyThemeStrings {
public static int OK_PIRATE = R.string.OK_PIRATE;
public static int OK_NINJA = R.string.OK_NINJA;
public static int OK_NORMAL = R.string.OK_NORMAL;
}
public setOkButtonText(ThemeMode themeMode) {
// buttonOk is instantiated elsewhere
switch (themeMode) {
case PIRATE:
buttonOk.setText(MyThemeStrings.OK_PIRATE);
break;
case NINJA:
buttonOk.setText(MyThemeStrings.OK_NINJA);
break;
default:
Log.e(TAG, "Unhandled ThemeMode: " + themeMode.name());
// no break here so it flows into the NORMAL base case as a default
case NORMAL:
buttonOk.setText(MyThemeStrings.OK_NORMAL);
break;
}
}
Although, having written all that, there is probably a better way to do all this through separate XML files. I'll look into it now and write a second solution if I find one.
Ok, I have a second option which may actually be easier to maintain and keep your code cleaner although it may be more resource hungry due to loading an array for each String. I've not benchmarked it but will offer it as another choice but I wouldn't use it if you offer too many theme choices.
public enum ThemeMode {
NORMAL(0),
PIRATE(1),
NINJA(2);
private int index;
private ThemeMode(int index) {
this.index = index;
}
public int getIndex() {
return this.index;
}
}
<resources>
<!-- ALWAYS define strings in the correct order according to
the index values defined in the enum -->
<string-array
name="OK_ARRAY">
<item>OK</item>
<item>Yaarrrr!</item>
<item>Hooaaaa!</item>
</string-array>
<string-array
name="CANCEL_ARRAY">
<item>Cancel</item>
<item>Naarrrrr!</item>
<item>Wha!</item>
</string-array>
</resources>
public setButtonTexts(Context context, ThemeMode themeMode) {
// buttons are instantiated elsewhere
buttonOk.setText(context.getResources()
.getStringArray(R.array.CANCEL_ARRAY)[themeMode.getIndex()]);
buttonCancel.setText(context.getResources()
.getStringArray(R.array.OK_ARRAY)[themeMode.getIndex()]);
}
So, I have not had a chance to test this, but from reading the file on Locale it looks like you can create your own location.
http://developer.android.com/reference/java/util/Locale.html
and help from another stackoverflow
Set Locale programmatically
A little bit of combination leads me to:
Locale pirate = new Locale("Pirate");
Configuration config = new Configuration();
config.locale = pirate;
this.getActivity().getBaseContext().getResources()
.updateConfiguration(config,
this.getActivity().getBaseContext().getResources().getDisplayMetrics());
I do believe this would let you have res/values-pirate/strings as an actual valid resource that would get used when you are a pirate. Any strings or settings you don't override would then revert to the res/values/... by default so you could do this for as many themes as you want. Again assuming it works.

How to set multiple default values in a MultiSelectListPreference?

I have preference.xml like this
<MultiSelectListPreference
android:key="store_select"
android:title="#string/setting_store_title"
android:summary="#string/setting_store_summary"
android:dialogTitle="#string/setting_store_dialog_title"
android:entries="#array/store_names"
android:entryValues="#array/stores"
android:defaultValue="#array/stores"
/>
with my two arrays:
<string-array name="stores">
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
</string-array>
<string-array name="store_names">
<item>foodbasics</item>
<item>nofrills</item>
<item>metro</item>
<item>loblaws</item>
<item>sobeys</item>
</string-array>
I want the default behaviour to be all of the options selected, but currently nothing is selected by default. Am I doing something wrong?
To make all MultiSelectListPreference items selected (on) by default, then include the attribute defaultsValue for the Preference, e.g.
android:defaultValue="#array/stores"
If it's not working, then make sure that you clear the application data as this will only take effect the first time the application is run.
I think you forgot calling PreferenceManager.setDefaultValues(this, R.xml.preference, false);
in the onCreate() method of your mainActivity.
This method will read your preference.xml file and set the default values defined there. Setting the readAgain argument to false means this will only set the default values if this method has never been called in the past so you don't need to worry about overriding the user's settings each time your Activity is created.
I know I am late but may be my answer helps someone else in future...
set
android:defaultValue="#array/empty_array"
where empty_array is an empty array.
If you are adding MultiSelectListPreference programmatically then you can simply call multiSelectListPreference.setDefaultValue():
e.g.
val preference = MultiSelectListPreference(context)
preference.setDefaultValue(setOf("US, "CN"))

Android: Default preference value not being set from XML

I have a simple Preferences Activity that I populate via XML, defining the values as array resources.
In the MAIN Activity of the application, I get a handle to this via:
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
In the XML for the only preference I have, which is a display setting, I have the following XML that defines the ListPreference:
<ListPreference
android:title="#string/pref_title_sort"
android:summary="#string/pref_summary_sort"
android:key="#string/pref_key_sort"
android:defaultValue="modified"
android:entries="#array/sort_order"
android:entryValues="#array/sort_order_values" />
You can see I am trying to set the default value to 'modified', which is a value found in #array/sort_order_values:
<string-array name="sort_order_values">
<item>modified</item>
<item>created</item>
<item>name</item>
</string-array>
However, when the Preferences Activity is launched, none of the items are selected by default.
I've tried adding the following line to my Activity, but it did not change anything (where pref_main is the XML file that defines the preferences):
PreferenceManager.setDefaultValues(this, R.xml.pref_main, false);
Any help appreciated!
Paul
Maybe You have just set a wrong value (without corresponding item in the value array) at the first time you run application. Now Android remembers your first choice. Try to manually uninstall app (Menu >> Settings >> Applications >> Manage Applications >> >> Uninstall ). This should help.

Android: SharedPreference: Defaults not set at startup

I have Listpreferences in my app. They don't appear to be setting to their defaults right after installation - they appear to be null. I'm trying to figure out why my default preferences are not being set right after installation. In my main code I have:
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
InUnits = sp.getString("List1", "defValue");
InAngs = sp.getString("List2", "defValue");
OutUnits = sp.getString("List3", "defValue");
OutAngs = sp.getString("List4", "defValue");
Right after the above code executes, each variable contains "defValue" instead of the actual values I have assigned in my ListPreference below.
My preference xml file is called, "settings.xml". Here's what one of the ListPreferences there looks like:
<ListPreference
android:key="List1"
android:title="Input: Alph"
android:summary="Choose Alph or Ralph"
android:entries="#array/inputAlph"
android:entryValues="#array/input_Alph_codes"
android:dialogTitle="Input Alph"
android:defaultValue="ININ"/>
Here's what some of my strings.xml file looks like:
<string-array name="inputUnits">
<item>Alph</item>
<item>Ralph</item>
</string-array>
<string-array name="input_Alph_codes">
<item>ININ</item>
<item>INMM</item>
</string-array>
When I go to menu, and then settings, I can see my defaults checked (radiobuttoned). Then when I go back from the settings menu to my main screen - all is well - for life! ...then each var above is assigned the proper default value.
This only happens when I first install my app on the phone. After I go to the settings screen once and then right out of it, the app is fine and accepts any setting changes.
By the way, as you can see, "List1" is the android:key within a file called settings.xml in my res/xml folder.
They don't appear to be setting to
their defaults right after
installation - they appear to be null.
That's what's supposed to happen.
I'm trying to figure out why my
default preferences are not being set
right after installation.
They're not supposed to be. The preference XML you have listed there is only used for populating a PreferenceActivity, nothing more. Until the user opens the PreferenceActivity, the preferences will be null, and the defaults you supply to the SharedPreferences getters will be returned.
UPDATE
You can use setDefaultValues() on PreferenceManager to assign the defaults from your preference XML to a SharedPreferences. However, be careful of the timing -- this will do disk I/O, and therefore ideally is performed on a background thread.
Set the default values to SharedPreferences from your preference XML.
PreferenceManager.setDefaultValues(Context context, int resourceId, boolean readAgain)
PreferenceManager.setDefaultValues
You can specify a default value like this
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
pref.getString("thePrefKey", "theDefaultValue");
The android:defaultValue="..." in the "layout" settings.xml is only a visual help for user

Categories

Resources