blob: 6ba36698e9d8d39fa47bbaa1e8baffc2267973e8 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.lint.checks
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Implementation
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
import com.android.tools.lint.detector.api.Severity
import com.android.tools.lint.detector.api.SourceCodeScanner
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UReferenceExpression
class ChromeOsSourceDetector : Detector(), SourceCodeScanner {
override fun getApplicableMethodNames(): List<String> {
return listOf(
"setRequestedOrientation",
"hasSystemFeature"
)
}
override fun visitMethodCall(
context: JavaContext,
node: UCallExpression,
method: PsiMethod
) {
val name = method.name
if ("setRequestedOrientation" == name) {
reportUnsupportedOrientationViolations(node, context)
} else if ("hasSystemFeature" == name) {
reportRearCameraOnlySystemFeatureViolations(node, context)
}
}
private fun reportRearCameraOnlySystemFeatureViolations(
node: UCallExpression,
context: JavaContext
) {
val cameraFeatureRequested = determinePropertyString(node)
if (FEATURE_CAMERA_STRING == cameraFeatureRequested) {
val message =
"You should look for any camera available on the device, not just the rear"
val fix = fix().name("Switch to look for FEATURE_CAMERA_ANY")
.replace()
.text(cameraFeatureRequested)
.with(FEATURE_CAMERA_ANY_STRING)
.build()
context.report(
UNSUPPORTED_CAMERA_FEATURE,
node,
context.getLocation(node),
message,
fix
)
}
}
private fun reportUnsupportedOrientationViolations(
node: UCallExpression,
context: JavaContext
) {
val orientationValue = determinePropertyString(node) ?: return
if (UNSUPPORTED_ORIENTATIONS.contains(orientationValue)) {
val message = "You should not lock orientation of your activities, so that you can " +
"support a good user experience for any device or orientation"
val fix = fix().name("Set the orientation to SCREEN_ORIENTATION_UNSPECIFIED")
.replace()
.text(orientationValue)
.with(UNSPECIFIED_ORIENTATION_VALUE)
.build()
context.report(
UNSUPPORTED_LOCKED_ORIENTATION,
node,
context.getLocation(node),
message,
fix
)
}
}
private fun determinePropertyString(node: UCallExpression): String? {
val firstArgument = node.valueArguments.firstOrNull()
val reference = firstArgument as? UReferenceExpression ?: return null
return reference.resolvedName
}
companion object {
private val IMPLEMENTATION =
Implementation(ChromeOsSourceDetector::class.java, Scope.JAVA_FILE_SCOPE)
@JvmField
val UNSUPPORTED_LOCKED_ORIENTATION = Issue.create(
id = "SourceLockedOrientationActivity",
briefDescription = "Incompatible setRequestedOrientation value",
explanation = """
The `Activity` should not be locked to a portrait orientation so that users
can take advantage of the multi-window environments and larger landscape-first screens
that Android runs on such as Chrome OS. To fix the issue, consider calling
`setRequestedOrientation` with the `ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR` or
`ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED` options or removing the call
all together.
""",
category = Category.CORRECTNESS,
priority = 4,
severity = Severity.WARNING,
androidSpecific = true,
implementation = IMPLEMENTATION
).setEnabledByDefault(true)
@JvmField
val UNSUPPORTED_CAMERA_FEATURE = Issue.create(
id = "UnsupportedChromeOsCameraSystemFeature",
briefDescription = "Looking for Rear Camera only feature",
explanation = """
You should look for the `FEATURE_CAMERA_ANY` features to include all
possible cameras that may be on the device. Looking for `FEATURE_CAMERA`
only looks for a rear facing camera, which certain tablets or Chrome OS
devices don't have, as well as newer device configurations and modes may place the
device in a state where the rear camera is not available. To fix the issue,
look for `FEATURE_CAMERA_ANY` instead.
""",
category = Category.CORRECTNESS,
priority = 4,
severity = Severity.WARNING,
androidSpecific = true,
implementation = IMPLEMENTATION
).setEnabledByDefault(true)
val UNSUPPORTED_ORIENTATIONS = setOf(
"SCREEN_ORIENTATION_PORTRAIT",
"SCREEN_ORIENTATION_REVERSE_PORTRAIT",
"SCREEN_ORIENTATION_SENSOR_PORTRAIT",
"SCREEN_ORIENTATION_USER_PORTRAIT"
)
const val UNSPECIFIED_ORIENTATION_VALUE = "SCREEN_ORIENTATION_UNSPECIFIED"
const val FEATURE_CAMERA_ANY_STRING = "FEATURE_CAMERA_ANY"
const val FEATURE_CAMERA_STRING = "FEATURE_CAMERA"
}
}