refactor: Prefer System.currentTimeMillis() to Date().getTime() (#645)
Avoids unnecessary object creation. Add a lint rule to catch this.
This commit is contained in:
parent
3e1d94ded2
commit
12eccb63ae
|
@ -34,7 +34,6 @@ import app.pachli.util.StatusDisplayOptions
|
||||||
import app.pachli.util.getRelativeTimeSpanString
|
import app.pachli.util.getRelativeTimeSpanString
|
||||||
import app.pachli.viewdata.NotificationViewData
|
import app.pachli.viewdata.NotificationViewData
|
||||||
import at.connyduck.sparkbutton.helpers.Utils
|
import at.connyduck.sparkbutton.helpers.Utils
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
class ReportNotificationViewHolder(
|
class ReportNotificationViewHolder(
|
||||||
private val binding: ItemReportNotificationBinding,
|
private val binding: ItemReportNotificationBinding,
|
||||||
|
@ -90,7 +89,7 @@ class ReportNotificationViewHolder(
|
||||||
)
|
)
|
||||||
binding.notificationSummary.text = itemView.context.getString(
|
binding.notificationSummary.text = itemView.context.getString(
|
||||||
R.string.notification_summary_report_format,
|
R.string.notification_summary_report_format,
|
||||||
getRelativeTimeSpanString(itemView.context, report.createdAt.time, Date().time),
|
getRelativeTimeSpanString(itemView.context, report.createdAt.time, System.currentTimeMillis()),
|
||||||
report.statusIds?.size ?: 0,
|
report.statusIds?.size ?: 0,
|
||||||
)
|
)
|
||||||
binding.notificationCategory.text = getTranslatedCategory(itemView.context, report.category)
|
binding.notificationCategory.text = getTranslatedCategory(itemView.context, report.category)
|
||||||
|
|
|
@ -42,7 +42,6 @@ import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.Date
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -271,7 +270,7 @@ class MediaUploader @Inject constructor(
|
||||||
val fileExtension = map.getExtensionFromMimeType(mimeType)
|
val fileExtension = map.getExtensionFromMimeType(mimeType)
|
||||||
val filename = "%s_%s_%s.%s".format(
|
val filename = "%s_%s_%s.%s".format(
|
||||||
context.getString(R.string.app_name),
|
context.getString(R.string.app_name),
|
||||||
Date().time.toString(),
|
System.currentTimeMillis().toString(),
|
||||||
randomAlphanumericString(10),
|
randomAlphanumericString(10),
|
||||||
fileExtension,
|
fileExtension,
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,7 +26,6 @@ import app.pachli.databinding.ItemSeveredRelationshipsBinding
|
||||||
import app.pachli.util.StatusDisplayOptions
|
import app.pachli.util.StatusDisplayOptions
|
||||||
import app.pachli.util.getRelativeTimeSpanString
|
import app.pachli.util.getRelativeTimeSpanString
|
||||||
import app.pachli.viewdata.NotificationViewData
|
import app.pachli.viewdata.NotificationViewData
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
class SeveredRelationshipsViewHolder(
|
class SeveredRelationshipsViewHolder(
|
||||||
private val binding: ItemSeveredRelationshipsBinding,
|
private val binding: ItemSeveredRelationshipsBinding,
|
||||||
|
@ -42,7 +41,7 @@ class SeveredRelationshipsViewHolder(
|
||||||
HtmlCompat.FROM_HTML_MODE_LEGACY,
|
HtmlCompat.FROM_HTML_MODE_LEGACY,
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.datetime.text = getRelativeTimeSpanString(itemView.context, event.createdAt.time, Date().time)
|
binding.datetime.text = getRelativeTimeSpanString(itemView.context, event.createdAt.time, System.currentTimeMillis())
|
||||||
|
|
||||||
binding.notificationSummary.text = itemView.context.resources.getQuantityString(
|
binding.notificationSummary.text = itemView.context.resources.getQuantityString(
|
||||||
R.plurals.notification_severed_relationships_summary_report_fmt,
|
R.plurals.notification_severed_relationships_summary_report_fmt,
|
||||||
|
@ -59,7 +58,7 @@ class SeveredRelationshipsViewHolder(
|
||||||
binding.notificationCategory.text = itemView.context.getString(resourceId)
|
binding.notificationCategory.text = itemView.context.getString(resourceId)
|
||||||
} else {
|
} else {
|
||||||
if (payloads.any { it == StatusBaseViewHolder.Key.KEY_CREATED }) {
|
if (payloads.any { it == StatusBaseViewHolder.Key.KEY_CREATED }) {
|
||||||
binding.datetime.text = getRelativeTimeSpanString(itemView.context, event.createdAt.time, Date().time)
|
binding.datetime.text = getRelativeTimeSpanString(itemView.context, event.createdAt.time, System.currentTimeMillis())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ internal class StatusNotificationViewHolder(
|
||||||
val readoutAloud: CharSequence
|
val readoutAloud: CharSequence
|
||||||
if (createdAt != null) {
|
if (createdAt != null) {
|
||||||
val then = createdAt.time
|
val then = createdAt.time
|
||||||
val now = Date().time
|
val now = System.currentTimeMillis()
|
||||||
readout = getRelativeTimeSpanString(binding.statusMetaInfo.context, then, now)
|
readout = getRelativeTimeSpanString(binding.statusMetaInfo.context, then, now)
|
||||||
readoutAloud = DateUtils.getRelativeTimeSpanString(
|
readoutAloud = DateUtils.getRelativeTimeSpanString(
|
||||||
then,
|
then,
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Pachli Association
|
||||||
|
*
|
||||||
|
* This file is a part of Pachli.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||||
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||||
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with Pachli; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package app.pachli.lint.checks
|
||||||
|
|
||||||
|
import com.android.tools.lint.checks.DataFlowAnalyzer
|
||||||
|
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.LintFix
|
||||||
|
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.UMethod
|
||||||
|
import org.jetbrains.uast.getParentOfType
|
||||||
|
|
||||||
|
class DateDotTimeDetector : Detector(), SourceCodeScanner {
|
||||||
|
override fun getApplicableConstructorTypes(): List<String> = listOf(CLASS_DATE)
|
||||||
|
|
||||||
|
val fix = LintFix.create()
|
||||||
|
.name("Replace with `System.currentTimeMillis()`")
|
||||||
|
.replace()
|
||||||
|
.text("Date().time")
|
||||||
|
.with("System.currentTimeMillis()")
|
||||||
|
.independent(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
// Look for a Date() constructor call followed by a getTime() method call
|
||||||
|
override fun visitConstructor(context: JavaContext, node: UCallExpression, constructor: PsiMethod) {
|
||||||
|
val method = node.getParentOfType(UMethod::class.java) ?: return
|
||||||
|
val analyzer = object : DataFlowAnalyzer(listOf(node)) {
|
||||||
|
var reported = false
|
||||||
|
|
||||||
|
override fun receiver(call: UCallExpression) {
|
||||||
|
if (reported) return
|
||||||
|
if (call.methodName != METHOD_GET_TIME) return
|
||||||
|
|
||||||
|
reported = true
|
||||||
|
context.report(
|
||||||
|
issue = ISSUE,
|
||||||
|
scope = node,
|
||||||
|
location = context.getCallLocation(call, includeReceiver = true, includeArguments = true),
|
||||||
|
message = "Use `System.currentTimeMillis()`",
|
||||||
|
quickfixData = fix,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
method.accept(analyzer)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val CLASS_DATE = "java.util.Date"
|
||||||
|
private const val METHOD_GET_TIME = "getTime"
|
||||||
|
|
||||||
|
val ISSUE = Issue.create(
|
||||||
|
id = "DateDotTimeDetector",
|
||||||
|
briefDescription = "Don't use `Date().time`, use `System.currentTimeMillis()`",
|
||||||
|
explanation = """
|
||||||
|
Calling `Date().time` / `Date().getTime()` is unnecessary object creation. Use
|
||||||
|
`System.currentTimeMillis()` to avoid this.
|
||||||
|
""",
|
||||||
|
category = Category.CORRECTNESS,
|
||||||
|
priority = 6,
|
||||||
|
severity = Severity.WARNING,
|
||||||
|
implementation = Implementation(
|
||||||
|
DateDotTimeDetector::class.java,
|
||||||
|
Scope.JAVA_FILE_SCOPE,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ class LintRegistry : IssueRegistry() {
|
||||||
override val issues: List<Issue>
|
override val issues: List<Issue>
|
||||||
get() = listOf(
|
get() = listOf(
|
||||||
AndroidxToolbarDetector.ISSUE,
|
AndroidxToolbarDetector.ISSUE,
|
||||||
|
DateDotTimeDetector.ISSUE,
|
||||||
IntentDetector.ISSUE,
|
IntentDetector.ISSUE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Pachli Association
|
||||||
|
*
|
||||||
|
* This file is a part of Pachli.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the terms of the
|
||||||
|
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Pachli is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||||
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with Pachli; if not,
|
||||||
|
* see <http://www.gnu.org/licenses>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package app.pachli.lint.checks
|
||||||
|
|
||||||
|
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
|
||||||
|
import com.android.tools.lint.checks.infrastructure.TestMode
|
||||||
|
import com.android.tools.lint.detector.api.Detector
|
||||||
|
import com.android.tools.lint.detector.api.Issue
|
||||||
|
|
||||||
|
@Suppress("ktlint:standard:function-naming")
|
||||||
|
class DateDotTimeDetectorTest : LintDetectorTest() {
|
||||||
|
override fun getDetector(): Detector = DateDotTimeDetector()
|
||||||
|
|
||||||
|
override fun getIssues(): List<Issue> = listOf(DateDotTimeDetector.ISSUE)
|
||||||
|
|
||||||
|
fun `test Intent component constructor emits warning`() {
|
||||||
|
lint().files(
|
||||||
|
kotlin(
|
||||||
|
"""
|
||||||
|
package test.pkg
|
||||||
|
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
fun getTimeMills() = Date().time
|
||||||
|
""",
|
||||||
|
|
||||||
|
).indented(),
|
||||||
|
).allowMissingSdk().testModes(TestMode.DEFAULT).run().expect(
|
||||||
|
"""src/test/pkg/test.kt:5: Warning: Use System.currentTimeMillis() [DateDotTimeDetector]
|
||||||
|
fun getTimeMills() = Date().time
|
||||||
|
~~~~~~~~~~~
|
||||||
|
0 errors, 1 warnings""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue