SonarLint: Suppress specific line warning in Android Studio - android

There is a special case in our code where we need to call setText with getText of the same TextView. SonarLint warn this with kotlin:S1656, to suppress it we use #SuppressWarnings annotation but it does not work. It started showing an error to the IDE and the warning from SonarLint still there. //NOSONAR is not working as well
No action is available as well for suppressing it.

Can you put the SuppressWarnings annotation before your method?
// Sonarlint plugin version 6.8.0.50884
#SuppressWarnings("kotlin:S1656")
fun yourMethod() {
// ...
withContext(coroutineDispatcherMain) {
// Set the text again to fix images overlapping text
htmlTextView.text = htmlTextView.text
}
}
I also wonder that if htmlTextView.invalidate() would work for you.
UPDATE:
SuppressWarnings annotation is also available for LOCAL_VARIABLE but I think the problem is more complicated in kotlin code.
If you examine the byte code of these lines below, you can see that some.num = ... is some.setNum(...) in byte code.
Sonar plugin's code analyser may not handle this kind of kotlin specific codes.
class Sonar {
private val some = Something(0)
private var member = 0
#Deprecated("...")
fun myNumber(): Int = 1
fun doThat() {
// local variable working
#SuppressWarnings("kotlin:S1874") // Code annotated as deprecated should not be used
val localNum = myNumber()
// not working
#SuppressWarnings("kotlin:S1874")
some.num = myNumber()
// not working
#SuppressWarnings("kotlin:S1874")
member = myNumber()
// ...
}
}

I found the docs for the issue to confirm its the right number: https://rules.sonarsource.com/kotlin/RSPEC-1656
Also read the source at https://github.com/SonarSource/sonarlint-intellij but not much help.
It seems like "// NOSONAR" is what you want (I know you said you tried it, but did you have the space? And have it at the end of the line?)
htmlTextView.text = htmlTextView.text // NOSONAR
Just a guess here, but you could also try:
#SuppressWarnings("RSPEC:S1656")
lastly to check suppress is working you can ignore everything:
#SuppressWarnings("all")
htmlTextView.text = htmlTextView.text
Big discussion here: Turning Sonar off for certain code
This article also gives you options to choose from: https://docs.codescan.io/hc/en-us/articles/360012109711-Ignoring-violations

Related

Printing Compose Semantics Tree to the Log

I am following Testing in Jetpack Compose Codelab but I am unable to print the Semantics tree using the printToLog function on a node. I am using this code inside the androidTest package.
import androidx.compose.ui.test.printToLog
...
//testing function
#Test
fun rallyTopAppBarTest() {
val allScreen = RallyScreen.values().toList()
testRule.setContent {
//the component be tested in isolation goes here
RallyTopAppBar(
allScreens = allScreen,
onTabSelected = {},
currentScreen = RallyScreen.Accounts
)
}
testRule.onRoot().printToLog("currentLabelExists")
....
}
I have checked my LogCat with both Debug and Verbose filtering using "currentLabelExists" tag but the Semantics Tree is not printing on the log.
Any ideas on what I might be missing out.
I can see the semantics tree.
Check if Android Studio Logcat has "No Filters"
Edit
It seems to be a bug as I can sometimes see the logs even if I select "Show only selected application".
Since I just ran into the same problem:
The SemanticsTree seems to appear only when you run your tests in Debug Mode

Why does Android Studio display "only be called from with the same library group" when I remove #SuppressLint("RestrictedApi")?

The Code A is from CameraX project, you can see source code.
Android Studio will display "only be called from with the same library group" when I remove #SuppressLint("RestrictedApi"), you can see Image 1.
Why can't I remove #SuppressLint("RestrictedApi") in Code A ? What deos a restriction API mean?
Code A
#SuppressLint("RestrictedApi")
private fun updateCameraUi() {
...
// Listener for button used to switch cameras
controls.findViewById<ImageButton>(R.id.camera_switch_button).setOnClickListener {
lensFacing = if (CameraX.LensFacing.FRONT == lensFacing) {
CameraX.LensFacing.BACK
} else {
CameraX.LensFacing.FRONT
}
try {
// Only bind use cases if we can query a camera with this orientation
CameraX.getCameraWithLensFacing(lensFacing)
// Unbind all use cases and bind them again with the new lens facing configuration
CameraX.unbindAll()
bindCameraUseCases()
} catch (exc: Exception) {
// Do nothing
}
}
}
Image 1
There have been breaking changes in the library since the tutorial was made.
Reverting the package version to 1.0.0-alpha06, same as the tutorial, solves the problem.
These are issues with the library that don't affect your code.
In several code examples using these APIs there is often a #SuppressLint("RestrictedApi") in the file hiding the warning.
The projects should still compile and run as they should, although you must make sure that you are using the correct dependency version. The APIs are changing quite frequently still, and if you're referencing an example it might be using an older version which has since changed.
Your best bet is to look directly at the source code and if the method you are calling is declared as public then you probably won't have a problem.

Why Kotlin throws an exception in a very simple piece of code?

There are an update in the end of the question. Now I've got to turn on and turn off the error with one line change in a tiny project with all code transcribed below.
The answer of Alexey Romanov is brilliant. Check in the end of the question. it's an amazing and unknown feature of Android Studio environment.
I've found a very kinky error in Kotlin using updated Android 3.4.2.
First I've run my test code in my computer (not Android) using main modules in any kotlin file in my only module. It always works for me, but it has started to give an error that I comment below and then another error that don't allow to run the main module anymore.
Searching in Stack Overflow, one user as claimed that the last described error ends when one uses test files under java folder (Not Android test files)
However is has continued to give the first error, which I commented above, which I managed to reduce to the following schematic example:
class Cl(
var a:Int=0
)
var vCl = arrayListOf<Cl>()
And the main module is:
fun main(){
println("start")
vCl.clear() // error points to this line
println("ok")
}
I just point to fun main() line in test file and click the green icon.
The error message
Exception in thread "main" java.lang.ExceptionInInitializerError
Suddenly the error stops when I've changed one global statement from one file to another file (in the top part, outside any class or function).
var timings = TimingLogger("MyTag", "Your")
It's crazy. I'm freaking out!
Update:
=======
I've made newerror, a new tiny project with one module to reproduce the error decribed in this question. Below is the complete code:
Gradle: No change after project creation.
AndroidManifest.xml: No change after project creation.
activity_main.xml: It is bare bone because all my views are dinamically created:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/myLayout"
android:textAllCaps="false"
android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=
"br.com.greatsolutions.paulo.myerror.MainActivity">
</RelativeLayout>
The MainActivity.kt code:
package br.com.greatsolutions.paulo.myerror
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
The compiler.kt code
package br.com.greatsolutions.paulo.myerror
import android.util.TimingLogger
var timings = TimingLogger("MyTag", "Your")
class Cl(
var a:Int=0
)
var vCl = arrayListOf<Cl>()
Finally the test.kt code:
package br.com.greatsolutions.paulo.myerror
fun main(){
println("start")
vCl.clear()
println("ok")
}
The complete error message when I run test.kt code in my computer (not in Android):
start
Exception in thread "main" java.lang.ExceptionInInitializerError
at br.com.greatsolutions.paulo.myerror.TestKt.main(test.kt:5)
at br.com.greatsolutions.paulo.myerror.TestKt.main(test.kt)
Caused by: java.lang.RuntimeException: Stub!
at android.util.TimingLogger.<init>(TimingLogger.java:59)
at br.com.greatsolutions.paulo.myerror
.CompilerKt.<clinit>(compiler.kt:5)
... 2 more
And, again, if I put the below declaration inside MainActivity.kt and run the test.kt again in my computer ...
var timings = TimingLogger("MyTag", "Your")
... and the error is gone!
start
ok
For those who want to see to believe:
Conclusion: Now I know how to avoid this crazy error, but I don't understand why it works! Ih theory, the source file that I use to make my declarations should be interchangeable, because all files are in the same module!
Solution of puzzle
Alexey Romanov has hit the nail on the head!
I've researched a little bit more and I've found that Android Studio only executes the declarations in a file if any variable of the scope was used in a computer test.
When
var timings = TimingLogger("MyTag", "Your")
is inside MainActivity.kt, no error is shown.
After I put in this file the following code:
open class Fool(val a:Int=5){
init { println("fool") }
}
class SuperFool(a:Int=8):Fool(a) {
init { println("what a big fool") }
}
var v = SuperFool()
When I put println(v.a) my code in test.kt becomes:
package br.com.greatsolutions.paulo.myerror
fun main(){
println("start")
println(v.a) // new line
vCl.clear()
println("ok")
}
And it gives the same error than before! Take out this line and no error again!
The solution is if one has any declaration of some variable in an Android class in your projet you MUST use lateinit and just initializate in other point of code that will not run in test execution.
In this case one can do
lateinit var timings:TimingLogger
And put the initialization in other place (inside onCreate in MainActivity class, for instance. In my case, immediately before the 1st. call addSplit, one of the methods from TimingLogger class
timings = TimingLogger("MyTag", "Your")
Now my test code gently prints
start
fool
what a big fool
ok
The problem has nothing to do with Kotlin; you can't just use Android classes in code running directly on your computer and not in an emulator, because the versions in the standard jar will crash on first use. They are just there to provide class files with the same names and method signatures as will exist on the device, so that your code can compile (not run!).
Use Robolectric to get a usable-for-JVM-tests version of the Android library.
if I put the below declaration inside MainActivity.kt and run the test.kt again in my computer
Then your test doesn't use any Android classes (see below for details).
It is worth remembering that, within the same module, all declarations, regardless of what file they are in, are executed.
That's wrong. What happens with top-level val/var/fun declarations in Kotlin is that they get wrapped into a single class for each file, so your code works like this:
// compiler.kt
package br.com.greatsolutions.paulo.myerror
object CompilerKt { // you can actually see the name in the stack trace
var timings = TimingLogger("MyTag", "Your")
val vCl = arrayListOf<Cl>()
}
class Cl(
var a:Int=0
)
// test.kt
package br.com.greatsolutions.paulo.myerror
object TestKt {
fun main(){
println("start")
CompilerKt.vCl.clear()
println("ok")
}
}
So calling TestKt.main() forces loading and initialization of CompilerKt because you reference vCl there. This includes calling the constructor TimingLogger("MyTag", "Your") which thrown an exception when the stub library is used.
Calling TestKt.main() does not load MainActivity (which you can confirm by adding some print to that file), so if you move var timings there, nothing in the stub library gets called.
Your missing a an quote on your first line.

Why do I need to add space before = in Kotlin?

In the following code , I have to add space before = , why ?
I think private val listofMDetail: MutableList<MDetail>= (No space before =) would be OK, but in fact, the Android Studio 3.1.3 give me an error.
private val listofMDetail: MutableList<MDetail> = //I have to add space before =
try{
myGson.fromJson<MutableList<MDetail>>(mJson)?: mutableListOf<MDetail>() //Load
}catch(e:Exception) {
e.message?.let{ logError("Paser: "+it)}
throw Exception(e)
}
The symbol >= is read as greater than or equal to, that is why the compiler show the error Expecting a '>'.
Just add the space, as recommended in the Kotlin style guide maintained by Google: https://android.github.io/kotlin-guides/style.html

Porting a Java SDK to Xamarin.Android, binding Library error regarding types

I am working on porting a Java SDK to Xamarin.Android.
After solving numerous errors, I am stuck in this one:
/Users/alejandro_l/Projects/KontaktBeaconSDK/KontaktBeaconSDK/obj/Debug/generated/src/Com.Kontakt.Sdk.Android.Ble.Configuration.Scan.EddystoneScanContext.cs(65,65): Error CS1715: Com.Kontakt.Sdk.Android.Ble.Configuration.Scan.EddystoneScanContext.EventTypes': type must beSystem.Collections.Generic.ICollection' to match overridden member `Com.Kontakt.Sdk.Android.Ble.Configuration.Scan.AbstractProfileSpecificScanContext.EventTypes' (CS1715) (KontaktBeaconSDK)
For solving the error I though that it should be OK to use the following line in the metadata.xml telling the generator to change the type of the method to match with the abstract class:
<attr path="/api/package[#name='com.kontakt.sdk.android.ble.configuration.scan']/class[#name='EddystoneScanContext']/method[#name='getEventTypes' and count(parameter)=0]" name="managedType">System.Collections.Generic.ICollection</attr>
Based on: https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/customizing-bindings/java-bindings-metadata/#managedType
The generated output still has the wrong shape:
static IntPtr id_getEventTypes;
public override unsafe global::System.Collections.ICollection EventTypes {
// Metadata.xml XPath method reference: path="/api/package[#name='com.kontakt.sdk.android.ble.configuration.scan']/class[#name='EddystoneScanContext']/method[#name='getEventTypes' and count(parameter)=0]"
[Register ("getEventTypes", "()Ljava/util/Collection;", "GetGetEventTypesHandler")]
get {
if (id_getEventTypes == IntPtr.Zero)
id_getEventTypes = JNIEnv.GetMethodID (class_ref, "getEventTypes", "()Ljava/util/Collection;");
try {
return global::Android.Runtime.JavaCollection.FromJniHandle (JNIEnv.CallObjectMethod (Handle, id_getEventTypes), JniHandleOwnership.TransferLocalRef);
} finally {
}
}
}
As you can see System.Collections.ICollection is generated and not System.Collections.Generic.ICollection.
Note: I even still did not figure it out how to also set the generic type, but that is the next step.
I am a newbie in binding and I cannot figure it out. Can someone tell me what I am doing wrong? Am I trying an impossible here? Do I need to change the source code of the SDK? (Notice that if I use "Build" and change manually the output I may get the dll in a future (still 49 errors left though) but I wonder if I can solve in a proper way).
Another useful link I used: https://gist.github.com/brendanzagaeski/9607158
Found it:
<attr path="/api/package[#name='com.kontakt.sdk.android.ble.configuration.scan']/class[#name='EddystoneScanContext']/method[#name='getEventTypes' and count(parameter)=0]" name="return">System.Collections.Generic.ICollection<Com.Kontakt.Sdk.Android.Ble.Discovery.EventType></attr>
It seems the error has disappeared due to name="return". I do not know why return does not appear in the documentations but it appears in the api.xml so it should be OK.

Categories

Resources