Android resource shrinking removes refs.xml from external library - android

i have made a library where i am using a refs.xml files in the values folder, which overrides a material resource file using tools:override to be able to rearrange the dialog buttons.
<resources xmlns:tools="http://schemas.android.com/tools">
<item name="mtrl_alert_dialog_actions" type="layout" tools:override="true">#layout/mtrl_alert_dialog_actions_custom</item>
</resources>
Using this library and building the app in debug mode without shrinking the resources, everything works properly.
As soon as i enable resource shrinking, the refs.xml file is removed or not considered anymore.
When i place the refs.xml inside my app, the mtrl_alert_dialog_actions is overriden by mtrl_alert_dialog_actions_custom too.
But how can i achieve that i do not need to place the refs.xml file inside my app and tell the app to use the refs.xml file from the library when shrinkResources is enabled?
The build outputs do not mention anything that the refs.xml from the library is removed.

Related

Irreproducible builds in Android when using AppCompat library

I was trying to create reproducible Android builds, but blocked by drawable resources from AppCompat library.
The drawables, abc_ic_menu_cut_mtrl_alpha.png and abc_ic_menu_copy_mtrl_am_alpha.png appear and disappear from folders res/drawable-ldrtl-xxxhdpi-v17 and res/drawable-xxxhdpi-v4 in the artifact apk files across different builds against same code base.
I'm using androidx.appcompat:appcompat:1.1.0, com.android.tools.build:gradle:3.5.1 and Gradle 5.4.1, and not directly referencing these resources.
shrinkResources wouldn't affect the result as I tested with both true and false. When it was true, I tried to add keep.xml to discard or keep these resources, but the results are the same, neither of them were fully removed.
I suspected it was caused by Gradle merge resources, but couldn't find logs or mapping reports for that.
I noticed that it is Rtl folder that these drawables may be shown, so I also tried android:supportsRtl="false". But the issue persists and rtl resources from AppCompat are still there.
It turned out to be I have used booster framework in my project, which randomly removed the resources when creating artifact apks.
I've created an issue for them https://github.com/didi/booster/issues/103, and have this question resolved.

Compiled APK file contains a lot of unknown (A)XML layouts

When I unzip the apk file, in the res\layout folder I found the following unknown xml files:
abc_action_bar_title_item.xml
abc_action_bar_up_container.xml
abc_action_menu_item_layout.xml
abc_action_menu_layout.xml
abc_action_mode_bar.xml
abc_action_mode_close_item_material.xml
abc_activity_chooser_view.xml
abc_activity_chooser_view_list_item.xml
abc_alert_dialog_button_bar_material.xml
abc_alert_dialog_material.xml
abc_alert_dialog_title_material.xml
abc_cascading_menu_item_layout.xml
abc_dialog_title_material.xml
abc_expanded_menu_layout.xml
abc_list_menu_item_checkbox.xml
abc_list_menu_item_icon.xml
abc_list_menu_item_layout.xml
abc_list_menu_item_radio.xml
abc_popup_menu_header_item_layout.xml
abc_popup_menu_item_layout.xml
abc_screen_content_include.xml
abc_screen_simple.xml
abc_screen_simple_overlay_action_mode.xml
abc_screen_toolbar.xml
abc_search_dropdown_item_icons_2line.xml
abc_search_view.xml
abc_select_dialog_material.xml
abc_tooltip.xml
notification_template_part_chronometer.xml
notification_template_part_time.xml
select_dialog_item_material.xml
select_dialog_multichoice_material.xml
select_dialog_singlechoice_material.xml
support_simple_spinner_dropdown_item.xml
None of them is my layout file. My layout xml file were there also, but all above listed are unknown to me.
Why are those files in my apk file? Do I need them there? Or is there a way to not having them in my apk file and have my apk file smaller?
Why are those files in my apk file?
You are using AppCompat, or at least added it as a dependency. There are some resources there that might be from other dependencies, but all the abc ones are from AppCompat.
Do I need them there?
If you are using AppCompat, yes. If not, no. If you are not using AppCompat, remove the dependency (and make sure nothing else that you are using needs AppCompat or otherwise pulls in AppCompat as a transitive dependency).
It is possible that some of those resources you will not need, even though you are using other aspects of AppCompat. You can look into the resource shrinking settings of a release build to see if it can identify some to remove.
Or is there a way to not having them in my apk file and have my apk file smaller?
You could not use AppCompat. In modern Android app development, that is rather difficult.

How to make Android library resources private?

How do I make the resources (string, dimen, color, etc.) in my Android library module private?
I've tried both documented ways of doing this and neither work...
Following the official Android doc of creating a
res/values/public.xml does not work; all of the resources remain
public in the app that uses this library.
Following Chris Banes's instruction (and reiterated in this StackOverflow answer) of creating a
res-public/values/public.xml folder does not work either; all of
the resources are made private, but those listed in the public.xml
file are not made public as they should be.
Does anyone have the definitive instructions for making library resources private? Are there any open-source libraries out there that have properly made their resources private?
I'm using...
Android Studio v2.2.3
buildToolsVersion "24.0.2"
com.android.tools.build:gradle:2.2.3
Your resources are assumed to be public in any module (library, etc) unless you start, explicitly, declaring any resource in the module to be public. At that point you basically opt-in to every resource (within the module) being private except what you mark as public. This preserves backward compatibility and let's you incrementally tighten down access in large projects with many modules.
To make all resources private simply add <public /> to any of your existing resource files.
The above answer talks about adding a specific resource directory just to manage the public/private modifiers. While that works, I might suggest you manage the visibility and declaration in the main resource files next to where they are declared. Here's a sample strings.xml resource file I used with a brand new library module. The public/private prefix in the string names is for illustrative purposes only.
res/values/strings.xml
<resources>
<string name="app_name">My Library2</string>
<public type="string" name="public_string_in_lib2" />
<string name="public_string_in_lib2">public string in lib2</string>
<string name="private_string_in_lib2">private string in lib2</string>
</resources>
Fundamentally, these qualifications are being used to create a public.txt file that is embedded in the AAR that will be depended upon by another module. Various pieces of tooling like Android Studio will use this information to flag/warn, but technically, a consumer of the library isn't prevented from using the resource unless their tooling is being really strict.
Option #2 actually works. I had not properly defined my sourceSets in my build.gradle...
sourceSets {
main.res.srcDirs = [
'src/main/res',
'src/main/res-public'
]
}

How to disable gradle minify for html files in raw directory

when using
minifyEnabled true
in my build.gradle file, html files are somehow obfuscated and can no more be accessed by WebView , files are not found because the files names changed i guess.
Any way to get around this behavour ?
Add the following lines to proguard:
-keepclassmembers class **.R$* {public static <fields>;}
-keep class **.R$*
Update
If there are specific resources you wish to keep or discard, create an XML file in your project with a <resources> tag and specify each resource to keep in the tools:keep attribute and each resource to discard in the tools:discard attribute. Both attributes accept a comma-separated list of resource names. You can use the asterisk character as a wild card.
For example:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="#layout/l_used*_c,#layout/l_used_a,#layout/l_used_b*"
tools:discard="#layout/unused2" />
Save this file in your project resources, for example, at res/raw/keep.xml. The build does not package this file into your APK.
Specifying which resources to discard might seem silly when you could instead delete them, but this can be useful when using build variants. For example, you might put all your resources into the common project directory, then create a different keep.xml file for each build variant when you know that a given resource appears to be used in code (and therefore not removed by the shrinker) but you know it actually won't be used for the given build variant.
https://developer.android.com/studio/build/shrink-code.html

Specifying Android project dependencies (in Eclipse)

I have two Android projects, a 'library project' containing a custom layout, and an 'application project' containing an application which uses the layout.
Everything seems to build and execute fine, except that the visual layout editor throws a ClassNotFoundException (which I assume is a bug in the plug-in), but when I try to start to make use of the attributes I defined for the custom layout in the xml, I can no longer build. That is; this works:
<?xml version="1.0" encoding="utf-8"?>
<se.fnord.android.layout.PredicateLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="asdfasdf"
/>
</se.fnord.android.layout.PredicateLayout>
Whereas this does not:
<?xml version="1.0" encoding="utf-8"?>
<se.fnord.android.layout.PredicateLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:fnord="http://schemas.android.com/apk/res/se.fnord.android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
fnord:layout_horizontalSpacing="1px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="asdfasdf"
/>
</se.fnord.android.layout.PredicateLayout>
The build fails with a message from aapt:
ERROR No resource identifier found for attribute 'layout_horizontalSpacing' in package 'se.fnord.android'
The resource identifier does exist in the R-file and attrs.xml contained the library project, and if I put the layout code and resources directly in the application project everything works fine. The layout_horizontalSpacing attribute (and layout_verticalSpacing) is a custom attribute used in the PredicateLayout.LayoutParam class to specify the distance to the next widget.
So far I've tried the standard eclipse ways by specifying project references and build path project dependencies. I was also told to try the tag in the application manifest, which did not help.
So, what do I need to do for the references in the xml-file to work?
I don't know if it's relevant, but the 'library' manifest looks like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.fnord.android"
android:versionCode="1" android:versionName="1.0.0">
</manifest>
The 'application' manifest like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.fnord.appname"
android:versionCode="1" android:versionName="1.0.0">
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".AppName"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
(The 'PredicateLayout', btw, is a cleaned-up version of this).
The earliest versions of Android sdk did not support sharing at the source code level in a nice way. You could jar up your .class files and then add that into the lib/ folder, but this solution did not allow direct sharing of source code and just as importantly did not support the sharing of resources or aidl files.
Then in May 2010, Android introduced the Library Project mechanism. A Library Project is structured very similar to a normal Android project, but rather than being used to produce an apk, it serves only to provide code and resources to other projects. Like an ordinary project, a Library Project usually contains src and res folders, along with an AndroidManifest.xml file; however the manifest should be mostly empty with the exception of the manifest element and the package attribute (no longer true - you can now declare Activities and other components in the manifest of your Library Project). In addition, the project.properties file for a Library Project needs to contain the property:
"android.library=true"
To make a reference from an ordinary (apk-producing) project to a Library Project, you need to add a "android.library.reference.N" line into the project.properties file of the ordinary project. For example, if my main project wants to point to two Library Projects, my project.properties file for the main project would include the following:
android.library.reference.1=../LibraryA
android.library.reference.2=../../LibraryB
where the ../ and the ../../ are the respective paths from the main project to the Library Projects (this is just an example). Note this list of references is 1-based and it should not contain gaps or duplicates. Google is well aware that this is not a perfect system but it was a reasonable solution that was compatible with Ant and Eclipse. Generally speaking, your IDE will attempt to maintain these files for you, but sometimes you may need to edit them by hand.
At first Library Projects did not support the following:
Library Projects pointing to other Library Projects
Library Projects containing aidl files
Library Projects containing assets folder
However subsequent sdk releases solved all of these problems.
For more information on Library Projects, see the official documentation.
Export the project as a JAR is not the right way to link a library project to your app project through Properties -> Java Build Path -> Library.
Neither it is to link the project as a required project through Properties -> Java Build Path -> Projects.
First of all, read the Library projects topic at Android developers -> Developing -> Managing projects: http://developer.android.com/guide/developing/projects/index.html#LibraryProjects
After this, read the Setting up a Library Project and Referencing a library project topics again at Android Developers -> Developing -> Managing projects -> From Eclipse With ADT
So... the steps are:
Create your Library project normally as a Android Project;
Set "is library" in the project properties -> Android Create your App
project normally;
Add a reference to your library in the project properties -> Android -> Add.
After this you can use all classes, components (activities, services, providers, receivers), resources etc.
Ex.: to reference any resources in a xml layout, for example, you should use #mylib:id/my_id or #mylib:layout/my_lib_layout
Obs.: If you use components of your library in app project, you must replicate them in your app manifest.
Also, I got the attributes working, but not in the way it should work I think.
You must use as the namespace in the element that uses your custom attributes, the namespace of your main app, not that of the library project. So, in your example, if you specify for the value of "xmlns:fnord" the namespace of your app project, it works.
Also, when reading the custom attributes in your custom PredicateLayout(Context,AttributeSet) constructor, you must specify the app's namespace as well in calls to attributes.getAttributeValue().
Which is a pain, since that code is in your library app which doesn't/shouldn't know about the app project it is used in. I worked around that by having the app call a static method ViewUtil.setAttributeNamespace(appNamespace) in my app's onCreate(), and the library's custom views use that namespace to retrieve the custom attributes. The attrs.xml file can then remain in the library project as well. Now the only ugly thing is that the layout XML must use the app's namespace on custom views, so you can't put those layout XML's in the library project.
Export your library project as a JAR and reference it in your application project's "Java Build Path" as a JAR.
The "Export your library as a jar" solution" only works if your library project contains source code only. In this case the OP's question mentions that his library project contains ui-related stuff.
We have the exact same issue on my team of wanting to have library projects that hold ui-related source and resources. We ended up overhauling our Ant build system in order to have applications engulf the library projects during build time. Unfortunately no solution of this sort seems to be compatible with Eclipse and this is a major source of frustration for the developers. We are still able to use Eclipse, but we have to jump through hoops to get it working and have to put up with diminished productivity.

Categories

Resources