I'm trying to customize Android 10 AOSP Settings app (com.android.settings) using a Runtime Resource Overlay (RRO).
When using a RRO, I can successfully customize any string in Settings app by putting a new value in the RRO's file ./res/values/strings.xml.
However, I cannot manage to overlay not a string but a layout by putting the layout file in the RRO's ./res/layout/ folder.
The RRO gets built, but the ids are different than the ones in the original layout, so after installing the RRO on device, the Settings app will fail to find the elements it expects in the layout file using findViewById().
For instance, I create a RRO containing the following bluetooth_pin_confirm.xml layout file (copied from Settings app source code and simplified) in its res/layout/folder:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/pairing_caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
...
</LinearLayout>
After installing the RRO on device, the Settings app will successfully inflate this file in BluetoothPairingDialogFragment.java
View view = getActivity().getLayoutInflater().inflate(R.layout.bluetooth_pin_confirm, null);
But then it won't be able to find the views by id:
TextView pairingViewCaption = (TextView) view.findViewById(R.id.pairing_caption);
R.id.pairing_caption cannot be found so pairingViewCaption will be null
So it seems that the IDs in the layout file from the RRO are different than the original ones.
Indeed, when running aap2 dump on Settings app apk I can see:
resource 0x7f0a02e0 com.android.settings:id/pairing_caption: t=0x12 d=0x00000000 (s=0x0008 r=0x00)
While in the RRO I can see:
resource 0x7f010000 com.foo.settings:id/pairing_caption: t=0x12 d=0x00000000 (s=0x0008 r=0x00)
So yes, the Ids are different.
Now, it should be the whole point of the RRO when overlaying a layout to be able to match the Ids so to keep original Ids but change the layout around them. But how?
I've played a bit with aapt2 options --emit-ids and --stable-ids to try to force generated id to be equal to the one in Settings.apk, but that fails miserably as well:
error: can't assign ID 0x7f0a0303 to resource com.foo.settings:id/pairing_caption with conflicting ID 0x7f010000.
Note that this issue is specific to Android 10 and earlier. It seems that in Android 11 there is a new & better way to define mapping between ids in target and overlay package.
I'm pretty stuck here. Any suggestion would be greatly appreciated
On the link rro
you can find the following
Note: If you overlay a layout file, make sure all the IDs and app namespace attributes are included in both overlays.xml and overlayable.xml. For example:
<overlay>
<item target="layout/car_ui_base_layout_toolbar"
value="#layout/car_ui_base_layout_toolbar" />
<item target="id/car_ui_toolbar_background"
value="#id/car_ui_toolbar_background" />
<item target="attr/layout_constraintTop_toBottomOf"
value="#attr/layout_constraintTop_toBottomOf" />
</overlay>
Related
I understand that in Android anything I place in my res/ directory will get compiled into the appropriate R class:
res/drawable -> R.drawable
res/layout -> R.layout
etc.
I know I can also use items that are in the android package:
android.R.id.text1
If I want to use my resources in xml I can do the following:
<TextView
...
android:text="#string/my_text" />
If I want to use something in the android package I can do the following:
<TextView
...
android:id="#android:id/text1" />
This doesn't always seem to be the case and I cannot figure out the distinction.
When using AppCompat Themes:
<TextView
android:theme="#style/Theme.AppCompat.Light"
android:textAppearance="#style/TextAppearance.AppCompat.Subhead" />
instead of
<TextView
android:theme="#android:style/Theme.AppCompat.Light"
android:textAppearance="#android:style/TextAppearance.AppCompat.Subhead" />
Theme.AppCompat.Light and TextAppearance.AppCompat.Subhead are not defined anywhere in my res/ directory. They are included as part of the support.v7.appcompat support library. Why don't I need to use the android prefix in this case?
Because they are part of your app, not part of the framework. It's not that you don't need it; if you try it, you will get a compile error.
The fact that appcompat-v7 happens to be a library is immaterial. It is no different than if you typed in the code yourself. Anything that is in your app does not use the android prefix. Resources that are part of the framework — and therefore are on the device, not in your app — get the android prefix.
I have many lists in my android app. I'm using strings as samples (android:text) for the layout preview.
Example:
<TextView
android:id="#+id/firstname"
android:layout_width="0dp"
style="#style/TextListStyle"
android:text="Peter"/>
Android studio is marking this as a warning but I don't want to put every sample string into the strings.xml file.
What is the best way to deal with this?
If you want it just for preview consider this:
tools:text="John Doe"
More Infos on Designtime Layout Attributes.
Add tools:ignore="HardcodedText" in your root layout, like:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="HardcodedText" >
However this is quite bad practice, for the reasons stated here:
Hardcoding text attributes directly in layout files is bad for several
reasons:
When creating configuration variations (for example for landscape or
portrait) you have to repeat the actual text (and keep it up to date when
making changes)
The application cannot be translated to other languages by just adding new
translations for existing string resources.
I installed packages from the Android SDK manager, but when I open an xml file to edit a layout, I get unresolved errors When I drag and drop items from the sidebar, I get a log saying:
Couldn't resolve resource #dimen/activity_horizontal_margin
Couldn't resolve resource #dimen/activity_vertical_margin
"#dimen/activity_horizontal_margin" in attribute "paddingLeft" is not a valid format.
"#dimen/activity_vertical_margin" in attribute "paddingTop" is not a valid format.
"#dimen/activity_horizontal_margin" in attribute "paddingRight" is not a valid format.
"#dimen/activity_vertical_margin" in attribute "paddingBottom" is not a valid format.
Also, no properties show up when I view the menu. When I right click on the layout to set a different layout manager, I see the following. I installed the correct packages from the SDK Manager, including all tools, the Android 19 API, Android 18 API, and the Android Support library.
Here's my xml file
<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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
</RelativeLayout>
Where can I start by fixing these errors?
This is probably caused by a mix-and-match of some large, medium and small resolutions in your project but render the layout in a view not considered large enough. In this case, just change the render parameter in the graphical layout tool. Hope it helps.
Such layout errors usually appears after you've created new Activities/Layouts using IDE build-in tools/wizards (like New->Activity->Blank Activity with Fragment in Android Studio). At this moment Java and XML code is already created, but auto-generated code (like R.java file) isn't generated yet. What's why layout editing feature is not working - it requires valid R.java file with all your resources (in your case it's #dimen/*).
To fix this errors simply compile your project (which will force auto-generated code to be generated) and refresh layout editor window.
I've been having this problem for a while:
Here is my header.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:orientation="vertical">
<ImageView android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="#drawable/logo_text"/>
</LinearLayout>
And this what the Eclipse/Android XML viewer is giving me:
Unable to resolve drawable "C:\Users\Me\MyApp\res\drawable-hdpi\logo_text.png" in attribute "src"
This happens in other projects as well for seemingly random images. Project->Clean doesn't help either. I know this has to be an issue with my machine or eclipse installation because it doesn't happen on my work machine, only my home laptop.
There are no problems when the app is installed, but it would be nice to be able to see layout changes without having to install the app over and over again.
EDIT
I also get these messages anytime I use a custom View in a layout file:
error!
UnsupportedClassVersionError: Bad version number in .class file
Unable to resolve drawable "C:\Users\Me\MyApp\res\drawable-hdpi\logo_text.png" in attribute "src"
Exception details are logged in Window > Show View > Error Log
I sometimes have weird issues like this when importing new images into my project. Do you have any other res-image directories with different versions of this file? You may try removing the png and re-adding to the project.
The error message explicitly mentions the -hdpi version of the drawable directory. Have you got that image in all of the different versions of the drawable directory? Perhaps it is in res/drawable-ldpi or res/drawable-mdpi but not res/drawable-hdpi?
If you don't already have one, you could create a plain res/drawable folder and add the image there, then one would expect the system to fall back to that whenever it didn't find a definition-specific version.
Actually try to put an id tag on ImageView : android:id=:"#+id/imageView".Tell me if it is working or not.
After taking a look at theming for Fede's UberMusic I came across the file album_screen.xml. Below is the source of that file. Basically I noticed that his themes have the ability to use custom views that are a part of his application, and that they have this XML namespace at the top theme. I am missing the connection as to how he is able to apply his attributes to views that he does not control, and how Eclipse will compile the cod below without fail. I placed the URL http://schemas.uberdroidstudio.com/theme into my browser's address bar but nothing came up, and I cannot figure out where/ how Eclipse knows the attributes that the namespace supports. Thank you ahead of time for your help.
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:theme="http://schemas.uberdroidstudio.com/theme">
<TextView
android:id="#id/artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.0"
theme:textFilter="uppercase" /> <!-- This is the interesting line -->
I suspect that the theme:textFilter="uppercase" line isn't actually having an effect on the (apparently vanilla) TextView.
As for the URL of the namespace, it is interesting that you can't access it, since it does not appear to be a local styleable (Android would have you refer to a local styleable namespace as http://schemas.android.com/apk/res/your.package). +1 for novelty.
The solution is actually not as complicated as I originally thought. XML namespaces are arbitrary strings that just need to be unique. If your namespace does not start with http://schemas.android.com/apk/res then it is not validated and the APK package is not checked for declare-styleable or the like.
Now a custom namespace if very simple to implement, as illustrated by this code snippet in GitHub. Finally, applying custom XML attributes to "normal" widgets can be accomplished by using the LayoutInflater.Factory and calling setFactory before you inflate your views. Throw it all together and you have a highly theme-able, XML driven application like Fede's UberMusic.