// INSPECTION_CLASS: com.android.tools.idea.lint.AndroidLintNewApiInspection // INSPECTION_CLASS2: com.android.tools.idea.lint.AndroidLintInlinedApiInspection // INSPECTION_CLASS3: com.android.tools.idea.lint.AndroidLintOverrideInspection import android.animation.RectEvaluator import android.annotation.SuppressLint import android.annotation.TargetApi import org.w3c.dom.DOMError import org.w3c.dom.DOMErrorHandler import org.w3c.dom.DOMLocator import android.view.View import android.view.ViewGroup import android.view.ViewGroup.LayoutParams import android.app.Activity import android.app.ApplicationErrorReport import android.graphics.drawable.VectorDrawable import android.graphics.Path import android.graphics.PorterDuff import android.graphics.Rect import android.os.Build import android.widget.* import dalvik.bytecode.OpcodeInfo import android.os.Build.VERSION import android.os.Build.VERSION.SDK_INT import android.os.Build.VERSION_CODES import android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH import android.os.Build.VERSION_CODES.JELLY_BEAN import android.os.Bundle import android.os.Parcelable import android.system.ErrnoException import android.widget.TextView @Suppress("SENSELESS_COMPARISON", "UNUSED_EXPRESSION", "UsePropertyAccessSyntax", "UNUSED_VARIABLE", "unused", "UNUSED_PARAMETER", "DEPRECATION", "USELESS_CAST") class ApiCallTest: Activity() { fun method(chronometer: Chronometer, locator: DOMLocator) { chronometer.setBackground(null) // Ok Bundle().getInt("") View.SYSTEM_UI_FLAG_FULLSCREEN // Virtual call getActionBar() // API 11 actionBar // API 11 // Class references (no call or field access) val error: DOMError? = null // API 8 val clz = DOMErrorHandler::class // API 8 // Method call chronometer.onChronometerTickListener // API 3 // Inherited method call (from TextView chronometer.setTextIsSelectable(true) // API 11 GridLayout::class // Field access val field = OpcodeInfo.MAXIMUM_VALUE // API 11 val fillParent = LayoutParams.FILL_PARENT // API 1 // This is a final int, which means it gets inlined val matchParent = LayoutParams.MATCH_PARENT // API 8 // Field access: non final val batteryInfo = report!!.batteryInfo // Enum access if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) { val mode = PorterDuff.Mode.OVERLAY // API 11 } } fun test(rect: Rect) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { RectEvaluator(rect); // OK } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (rect != null) { RectEvaluator(rect); // OK } } } fun test2(rect: Rect) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { RectEvaluator(rect); // OK } } fun test3(rect: Rect) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { RectEvaluator(); // ERROR } } fun test4(rect: Rect) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { System.out.println("Something"); RectEvaluator(rect); // OK } else { RectEvaluator(rect); // ERROR } } fun test5(rect: Rect) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) { RectEvaluator(rect); // ERROR } else { RectEvaluator(rect); // ERROR } } fun test(priority: Boolean, layout: ViewGroup) { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null).getOrientation(); // Flagged } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null).getOrientation(); // Flagged } if (SDK_INT >= ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null).getOrientation(); // Flagged } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null).getOrientation(); // Flagged } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Flagged } else { GridLayout(null).getOrientation(); // Not flagged } if (Build.VERSION.SDK_INT >= 14) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null).getOrientation(); // Flagged } if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null).getOrientation(); // Flagged } // Nested conditionals if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { if (priority) { GridLayout(null).getOrientation(); // Flagged } else { GridLayout(null).getOrientation(); // Flagged } } else { GridLayout(null).getOrientation(); // Flagged } // Nested conditionals 2 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { if (priority) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null).getOrientation(); // Not flagged } } else { GridLayout(null); // Flagged } } fun test2(priority: Boolean) { if (android.os.Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null); // Flagged } if (android.os.Build.VERSION.SDK_INT >= 16) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null); // Flagged } if (android.os.Build.VERSION.SDK_INT >= 13) { GridLayout(null).getOrientation(); // Flagged } else { GridLayout(null); // Flagged } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null); // Flagged } if (SDK_INT >= JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null); // Flagged } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null); // Flagged } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { GridLayout(null); // Flagged } else { GridLayout(null).getOrientation(); // Not flagged } if (Build.VERSION.SDK_INT >= 16) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null); // Flagged } if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { GridLayout(null).getOrientation(); // Not flagged } else { GridLayout(null); // Flagged } } fun test(textView: TextView) { if (textView.isSuggestionsEnabled()) { //ERROR } if (textView.isSuggestionsEnabled) { //ERROR } if (SDK_INT >= JELLY_BEAN && textView.isSuggestionsEnabled()) { //NO ERROR } if (SDK_INT >= JELLY_BEAN && textView.isSuggestionsEnabled) { //NO ERROR } if (SDK_INT >= JELLY_BEAN && (textView.text != "" || textView.isSuggestionsEnabled)) { //NO ERROR } if (SDK_INT < JELLY_BEAN && (textView.text != "" || textView.isSuggestionsEnabled)) { //ERROR } if (SDK_INT < JELLY_BEAN && textView.isSuggestionsEnabled()) { //ERROR } if (SDK_INT < JELLY_BEAN && textView.isSuggestionsEnabled) { //ERROR } if (SDK_INT < JELLY_BEAN || textView.isSuggestionsEnabled) { //NO ERROR } if (SDK_INT > JELLY_BEAN || textView.isSuggestionsEnabled) { //ERROR } // getActionBar() API 11 if (SDK_INT <= 10 || getActionBar() == null) { //NO ERROR } if (SDK_INT < 10 || getActionBar() == null) { //ERROR } if (SDK_INT < 11 || getActionBar() == null) { //NO ERROR } if (SDK_INT != 11 || getActionBar() == null) { //NO ERROR } if (SDK_INT != 12 || getActionBar() == null) { //ERROR } if (SDK_INT <= 11 || getActionBar() == null) { //NO ERROR } if (SDK_INT < 12 || getActionBar() == null) { //NO ERROR } if (SDK_INT <= 12 || getActionBar() == null) { //NO ERROR } if (SDK_INT < 9 || getActionBar() == null) { //ERROR } if (SDK_INT <= 9 || getActionBar() == null) { //ERROR } } fun testReturn() { if (SDK_INT < 11) { return } // No Error val actionBar = getActionBar() } fun testThrow() { if (SDK_INT < 11) { throw IllegalStateException() } // No Error val actionBar = getActionBar() } fun testError() { if (SDK_INT < 11) { error("Api") } // No Error val actionBar = getActionBar() } fun testWithoutAnnotation(textView: TextView) { if (textView.isSuggestionsEnabled()) { } if (textView.isSuggestionsEnabled) { } } @TargetApi(JELLY_BEAN) fun testWithTargetApiAnnotation(textView: TextView) { if (textView.isSuggestionsEnabled()) { //NO ERROR, annotation } if (textView.isSuggestionsEnabled) { //NO ERROR, annotation } } @SuppressLint("NewApi") fun testWithSuppressLintAnnotation(textView: TextView) { if (textView.isSuggestionsEnabled()) { //NO ERROR, annotation } if (textView.isSuggestionsEnabled) { //NO ERROR, annotation } } fun testCatch() { try { } catch (e: ErrnoException) { } } fun testOverload() { // this overloaded addOval available only on API Level 21 Path().addOval(0f, 0f, 0f, 0f, Path.Direction.CW) } // KT-14737 False error with short-circuit evaluation fun testShortCircuitEvaluation() { getDrawable(0) // error here as expected if(Build.VERSION.SDK_INT >= 23 && null == getDrawable(0)) // error here should not occur { getDrawable(0) // no error here as expected } } // KT-1482 Kotlin Lint: "Calling new methods on older versions" does not report call on receiver in extension function private fun Bundle.caseE1a() { getBinder("") } private fun Bundle.caseE1c() { this.getBinder("") } private fun caseE1b(bundle: Bundle) { bundle.getBinder("") } // KT-12023 Kotlin Lint: Cast doesn't trigger minSdk error fun testCast(layout: ViewGroup) { if (layout is LinearLayout) {} // OK API 1 layout as? LinearLayout // OK API 1 layout as LinearLayout // OK API 1 if (layout !is GridLayout) {} layout as? GridLayout layout as GridLayout val grid = layout as? GridLayout val linear = layout as LinearLayout // OK API 1 } abstract class ErrorVectorDravable : VectorDrawable(), Parcelable @TargetApi(21) class MyVectorDravable : VectorDrawable() fun testTypes() { GridLayout(this) val c = VectorDrawable::class.java } fun testCallWithApiAnnotation(textView: TextView) { MyVectorDravable() testWithTargetApiAnnotation(textView) } companion object : Activity() { fun test() { getDrawable(0) } } // Return type internal // API 14 val gridLayout: GridLayout? get() = null private val report: ApplicationErrorReport? get() = null } object O: Activity() { fun test() { getDrawable(0) } } fun testJava8() { // Error, Api 24, Java8 mutableListOf(1, 2, 3).removeIf { true } // Ok, Kotlin mutableListOf(1, 2, 3).removeAll { true } // Error, Api 24, Java8 mapOf(1 to 2).forEach { key, value -> key + value } // Ok, Kotlin mapOf(1 to 2).forEach { (key, value) -> key + value } } interface WithDefault { // Should be ok fun methodWithBody() { return } }