Namespace autofill for custom attributes - android-studio

I have a project "CustomViews" that I'd like to use as a library in another project "Library Dependent". One of my views, ToolbarITI has a few custom attributes defined in a file which I handle in my ToolbarITI class:
attrs.xml
<resources>
<declare-styleable name="ToolbarITI">
<attr name="rightIconSrc" format="integer"/>
<attr name="leftIconSrc" format="integer"/>
<attr name="titleText" format="string"/>
<attr name="iconPadding" format="dimension"/>
<attr name="fontName" format="string"/>
</declare-styleable>
</resources>
When I add the custom view to a layout in my Library Dependent project, I am able to add these custom attributes in the xml file, and the attributes are applied. However, Android Studio does not recognize the custom: attributes pertaining to ToolbarITI when auto-filling which makes the process annoying. I have declared xmlns:custom at the top of my layout file.
I simply do not understand how these attributes are bundled with the library project and referenced by Android studio. If anyone can explain how this is done, please do provide an explanation!

I'll need someone to confirm that this was the root of the problem, but this is what I found.
I was uploading my .aar to com.tommcfarlin.lib, which with the "lib" results in resources being ignored (according to Android Studio). Though I'm not sure if this results in anything "breaking" when packaging it certainly decreases the functionality of Android Studio by not adding resource ids for autofill. Better be safe and not do this.
These are the steps I took that resulted in Android Studio recognizing the custom attributes.
1) I altered my build.gradle in my CustomViews project to:
note the com.tommcfarlin.customviews
def aarFile = file("build/${archivesBaseName}-${project.version}.aar")
artifacts {
archives aarFile
}
uploadArchives {
repositories {
mavenDeployer {
repository(url: "file:///C:/Users/tmcfarlin/AndroidProjects/myrepo")
pom.groupId = 'com.tommcfarlin.customviews'
pom.artifactId = 'CustomViews'
pom.version = '0.1.0'
}
}
}
2) I ran "gradle uploadArchives" from the terminal
3) In my Library Dependent project I had the following lines in my build.gradle:
repositories {
maven { url 'file:///C:/Users/tmcfarlin/AndroidProjects/myrepo' }
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.tommcfarlin.customviews:CustomViews:0.1.0#aar'
}
4) I synced the project with gradle files. After this, I still couldn't see the attributes. I closed the Library Dependent project and reopened it voila!!! I could now have the following block where my custom view's attributes were filled in for me when I typed "custom:" and pressed Ctrl+Space
<com.tommcfarlin.customviews.ToolbarITI
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:fontName="helvetica.ttf">
</com.tommcfarlin.customviews.ToolbarITI>
This webpage has been immensely useful to me while learning how to set up a local repository. Just be sure not to use the "lib" directory!!! http://blog.glassdiary.com/post/67134169807/how-to-share-android-archive-library-aar-across

Related

Android play store localization issue

I am working on an android project and I localized my app in two languages, the problem is that it works fine when I run my app directly from the android studio but when I upload my project in google play store and download the app from play store its localization is not working in some version of Android.
This is my java code that sets the local language.
private static Context updateResources(Context context) {
Locale locale = new Locale("fa","AF");
Locale.setDefault(locale);
Resources res = context.getResources();
Configuration config = new Configuration(res.getConfiguration());
if (Build.VERSION.SDK_INT >= 17) {
config.setLocale(locale);
context = context.createConfigurationContext(config);
} else {
config.locale = locale;
res.updateConfiguration(config, res.getDisplayMetrics());
}
return context;
}
This is my English layout file.
<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="update">Update</string>
<string name="app_version_title">New version</string>
<string name="app_version_title_second">is now available</string>
<string name="logout_msg">Are you sure you want to logout?</string>
<string name="logout_title">Log Out?</string>
<string name="languages">Languages</string>
<string name="km">Km</string>
</resources>
This is my Persion layout file.
<resources xmlns:tools="http://schemas.android.com/tools" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="update">به روز رسانی </string>
<string name="app_version_title">نسخه جدید</string>
<string name="app_version_title_second">موجود است</string>
<string name="logout_msg">آیا مطمئین هستید که خارج میشوید؟ </string>
<string name="logout_title">خروج؟</string>
<string name="languages">زبانها</string>
<string name="km">کیلومتر</string>
</resources>
This is the structure of my Values folder.
values
strings.xml
values-fa-rAF
strings.xml
If you are using app bundle to publish in the play store the android app bundle feature will split the bundle into APK(s) based on the languages for optimizations and smaller sized APK file. Read more here. So only the APK which matches the user's locale will be installed on the device. Therefore changing the language dynamically will not work because the installed APK will contain only the language which is same as user phone's locale.
To prevent the split based on the language you need to add the following in your app build.gradle file like below.
android {
...
...
bundle {
language {
enableSplit = false
}
}
}
I experienced the exact same issue.
There is nothing wrong with your code.
It gave me hard times but I finally figured it out.
When you download an app from Google play - and you are trying to set a locale that does not exist in the Language list that the user defines in the Settings - the app will use the default strings.xml
ask from someone who experiences this issue to add Persian language to the Language list in the settings - then he must Uninstall the app - and re-Install it again - the app will use your strings.xml values-fa-rAF.
When you think about it - it has some sense in it.
Anyway - if it is critical you can consider checking on runtime the texts and show a message to the user and instruct him to solve the problem.
Good luck
I think when you gonna to Generate signed bundle or APK, you select "Android App Bundle", you must Select "APK", because you are using strings.xml localization, you can Read more here.

Create external link in Javadoc using Gradle in Android Studio

I have created a Gradle task that generates a javadoc using Doclava:
My code (the arguments of some of my methods) references classes defined in Android. When Javadoc is built, these references link correctly to the Android online reference. However, when I use the #ling tag to link to Android references, it does not work and I get something like:
configurations {
jaxDoclet
classpaths
}
dependencies {
// For Doclava JavaDoc
jaxDoclet("com.google.doclava:doclava:1.0.6")
classpaths files('build/intermediates/classes/debug')
classpaths project.files(android.getBootClasspath().join(File.pathSeparator))
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.allJava
source += fileTree("build/generated/source/r/debug")
title = null
options {
docletpath = configurations.jaxDoclet.files.asType(List)
doclet "com.google.doclava.Doclava"
bootClasspath new File(System.getenv('JAVA_HOME') + "/jre/lib/rt.jar")
classpath += configurations.classpaths.files.asType(List)
addStringOption "public"
addStringOption "federate android", "http://d.android.com/reference"
addStringOption "federationxml android", "http://doclava.googlecode.com/svn/static/api/android-10.xml"
}
}
warning 101: Unresolved link/see tag "Runnable" in com....
In similar questions in SO, it was advised to use -link and -linkoffline flags. However, when I do that I get:
javadoc: error - invalid flag: -linkoffline
I am using Android Studio 1.5.1 and Gradle 2.11.
Update
It seems that Doclava may not support -link and -linksoffline according to these tickets. If I use the default doclet, links work correctly.

How to depend another project's androidTestCompile?

I have a multi-project test issue: I want one project to depend on another project's androidTestCompile.
I have tried this way:
androidTestCompile project(':CommonTest').sourceSets.androidTest
But android studio says it could not find property androidTest on SourceSet container.
Help
Here's an approach that might work. I've not tried it as I don't have a multi-project Android project. You'll need to tweak the code for your project names and specify the dependsOn in your current projects task for Android Tests.
task action(dependsOn: ":producer:action") << {
println("Consuming message: ${rootProject.producerMessage}")
}
See the topic 24.6.1.2. Declaring dependencies in https://docs.gradle.org/current/userguide/multi_project_builds.html
Another clue is in the Gradle Java Tutorial which explains how to establish dependencies between projects in a multi-project build https://docs.gradle.org/current/userguide/tutorial_java_projects.html#N14E23
dependencies {
compile project(':shared')
}
So I'd hazard a guess that something like
dependencies {
androidTestCompile project(':CommonTest')
}
(which you've tried, albeit with more parameters) is on the right track.

What the difference in applying gradle plugin

I don't understand gradle plugins block
apply plugin: 'someplugin1'
apply plugin: 'maven'
and other one:
plugins {
id 'org.hidetake.ssh' version '1.1.2'
}
In first block We have some plugin name. in second one package and version. I don't understand where I should use first block and when second one.
The plugins block is the newer method of applying plugins, and they must be available in the Gradle plugin repository. The apply approach is the older, yet more flexible method of adding a plugin to your build.
The new plugins method does not work in multi-project configurations (subprojects, allprojects), but will work on the build configuration for each child project.
I would think that as functionality progresses, the plugins configuration method will overtake the older approach, but at this point both can be and are used concurrently.
As already mentioned by #cjstehno the apply plugin is a legacy method that you should avoid.
With the introduction of the plugins DSL, users should have little reason to use the legacy method of applying plugins. It is documented here in case a build author cannot use the plugins DSL due to restrictions in how it currently works.
With the new plugins block method, you can add a plugin and control when to apply it using an optional parameter apply:
plugins {
id «plugin id» version «plugin version» [apply «false»]
}
You would still use the legacy method in situations where you want to apply an already added but not applied plugin in your plugins block. E.g, in the master project a plugin xyz is added but not applied and it should be applied only in a subproject subPro:
plugins {
id "xyz" version "1.0.0" apply false
}
subprojects { subproject ->
if (subproject.name == "subPro") {
apply plugin: 'xyz'
}
}
Notice that you don't need the version anymore. The version is required in the plugins block unless you are using one of the Core Gradle plugins, such as java, scala, ...
I spent some time understanding the difference while trying to create a Spring Boot application, and that's why I am answering this again after a while. The following example for using Spring Boot plugin helped me a lot:
What should currently be used:
plugins {
id "org.springframework.boot" version "2.0.1.RELEASE"
}
What had been used before Gradle 2.1:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE"
}
}
apply plugin: "org.springframework.boot"
These are two different ways to use Gradle plugin。
The apply plugin way: First resolve plugin you needed from root build.gradle like:
buildscript {
repositories {
// other repositories...
mavenCentral()
}
dependencies {
// other plugins...
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.44'
}
Then in the build.gradle of your Android Gradle modules apply the plugin:
apply plugin: 'com.android.application'
apply plugin: 'com.google.dagger.hilt.android'
The plugins way:combine resovle and apply where in your root build.gradle like:
plugins {
// other plugins...
id 'com.google.dagger.hilt.android' version '2.44' apply false
}
Then in the build.gradle of your Android Gradle modules apply the plugin:
plugins {
// other plugins...
id 'com.android.application'
id 'com.google.dagger.hilt.android'
}
android {
// ...
}
Now ( In Gradle 6) you can give repositories name for plugins without using build script.
In settings.gradle, we can add plugin pluginManagement
pluginManagement {
repositories {
maven {
url '../maven-repo'
}
gradlePluginPortal()
ivy {
url '../ivy-repo'
}
}
}
Reference: https://docs.gradle.org/current/userguide/plugins.html#sec:custom_plugin_repositories
I would like to point out though, that is it not required for a plugin to be published remotely to be able to use it!
It can also be a UNPUBLISHED locally available plugin (be it convention plugins or otherwise) just as well.
In case one wishes to refer to such an unpublished locally-available plugin,
you'll have to include it's so-called "build" within the desired component/build (identified via the settings.gradle(.kts)-file) like so:
pluginManagement {
includeBuild '<path-to-the-plugin-dir-containing-the-settings-file>'
}
Afther that is done, one may use the local plugin within the plugins {}-DSL-block via its pluginId.
If the plugin needs a version then it's safer to put the version number in the pluginManagement block in your settings.gradle file, rather than in plugins block.
By safer I mean that you won't encounter an error like plugin request for plugin already on the classpath must not include a version. That can happen if you includeFlat a project into another project that uses the same plugin and your plugin versions are in the plugins block.
So rather than:
plugins {
id 'pl.allegro.tech.build.axion-release' version '1.10.3'
}
Do:
plugins {
id 'pl.allegro.tech.build.axion-release'
}
and then in your settings.gradle file:
pluginManagement {
plugins {
id 'pl.allegro.tech.build.axion-release' version '1.10.3'
}
}
I'm going to add a little twist to what's been said. Gradle introduced the concept of a plugins block as a technique to speed up and optimize the build process. Here's what Gradle's documentation says:
This way of adding plugins to a project is much more than a more convenient syntax. The plugins DSL is processed in a way which allows Gradle to determine the plugins in use very early and very quickly. This allows Gradle to do smart things such as:
Optimize the loading and reuse of plugin classes.
Provide editors detailed information about the potential properties and values in the buildscript for editing assistance.
This requires that plugins be specified in a way that Gradle can easily and quickly extract, before executing the rest of the build script. It also requires that the definition of plugins to use be somewhat static.
It's not just a newer way of dealing with plugins, it's a way of improving the build process and/or user's editing experience.
In order for it to work, it needs to be specified at the top of the build, but it also needs to be specified after the buildscript block if one is included. Why is that? Because the code in the build scripts is evaluated in the order its written. The buildscript block must be evaluated before the plugins block is evaluated. Remember, the buildscript block is about setting up of the plugin environment. Hence the rule that the plugins block must be specified after the buildscript block.
The new plugins block not only specifies the plugins that the project is using, but it also specifies whether the plugin is applied. By default, all plugins in the plugins block are automatically applied, unless it is specifically declared not to be applied (i.e., adding "apply false" after the plugin declaration in the plugins block).
So why would you declare a plugin and not apply it. There are two main reasons that I can think of:
1.) so you can declare the version of the plugin you want used. After you've declared a plugin, the plugin is now on the "classpath". Once a plugin is on the classpath you no longer need to specify the version of the plugin when you apply it later. In multiproject builds, that makes supporting buildscripts a little easier. (i.e., you only have one place where the plugin version is specified.)
2.) Sometimes, you may have a plugin, that requires certain things defined before they are applied. In that case, you can declare a plugin in the plugins block, and defer the plugin from being applied until after you define the things that the plugin requires as input. For example, I have a custom plugin that looks for a configuration named "mavenResource". In the dependencies block I'll added a dependency like: "mavenResource(maven_coordinate)". That plugin will find all the dependencies contained in the mavenResource configuration and copy the associated maven artifact to the projects "src/main/resources" directory. As you can see, I don't want to apply that plugin until after the mavenResource configuration is added to that project, and the mavenResource dependencies are defined. Hence, I define my custom plugin the plugins block, and I apply it after the project dependencies have been defined. So, the concept that applying a plugin is old style and wrong is a misconception.
Some of you might wonder what it means to apply a plugin. It's pretty straightforward. It means that you call the plugin's apply function passing it the Gradle Project object for the project where the plugin is being applied. What the plugin does from there on is totally at the discretion of the plugin. Most commonly, the apply function usually creates some Gradle tasks and adds them to the Gradle build task dependency graph. When Gradle starts its execution phase, those tasks will get executed at the appropriate time in the build process. The plugin apply function can also do things like deferring some of it work until afterEvaluate. That's a way to allow other things in the build script to be setup even though they are defined later on in the buildscript. So, you might ask why I didn't do that trick in my custom plugin. What I've observed is that the next subproject starts processing after the root project finishes being evaluated. In my case, I needed the resource added before the next subproject began. So, there was a race condition, that I avoided by not doing the afterEvaluate technique and specifically applying the plugin once the things I needed setup was completed.

How do I tell ReSharper that an attribute means that a method is used?

I'm playing with SpecFlow, and ReSharper thinks that my step definitions are unused (I guess because they're used via reflection):
[Binding]
public class StepDefinitions
{
// ...
[When(#"I press add")]
public void WhenIPressAdd() // R# thinks this is unused
{
_calculator.PressAdd();
}
// ...
}
How can I tell ReSharper that methods with [Given], [When], [Then] attributes (etc.) are actually used? I don't want to use // ReSharper disable UnusedMember.Global comments.
I could also mark each method (or the whole class) with [JetBrains.Annotations.UsedImplicitly]. I don't particularly want to do that either.
You need to use JetBrains Annotations, and mark the attribute with an MeansImplicitUseAttribute. You can either reference JetBrains.Annotations.dll directly, or you can copy the annotations source code (from ReSharper / Options / Code Inspection / Code Annotations) into your solution.
If you need to annotate some external assembly you don't own, you need to create an External Annotation file (xml) in the following folder: %ReSharperInstallDir%\Bin\ExternalAnnotations. There are plenty of examples, you can just copy some.
The external annotations file can also be in the same path as the DLL if you name it DllNameWithoutExtension.ExternalAnnotations.xml.
There are plenty of examples, but I wanted to be a little more explicit in case you don't want to track down an example. :)
Create a file with the name of the attribute's assembly (.xml) in %ReSharperInstallDir%\Bin\ExternalAnnotations. For example, I made Microsoft.VisualStudio.QualityTools.CodedUITestFramework.xml and put this XML inside it:
<assembly name="Microsoft.VisualStudio.QualityTools.CodedUITestFramework">
<member name="T:Microsoft.VisualStudio.TestTools.UITesting.CodedUITestAttribute">
<attribute ctor="M:JetBrains.Annotations.MeansImplicitUseAttribute.#ctor" />
</member>
</assembly>
Restart VS and you're on your way!
these answers have helped but note worthy if you are looking to decorate an interface you will want to use the UsedImplicitly attribute
[UsedImplicitly]
public interface ISomeInterface
{
//... stuff
}

Resources