Simplify and optimize Kotlin code snippets

This commit is contained in:
Niko Diamadis
2022-12-30 18:23:50 +01:00
parent c32d6a81bd
commit 188ef24e69
25 changed files with 212 additions and 264 deletions

View File

@@ -28,8 +28,7 @@ internal object MarkwonFactory {
.usePlugin(PicassoImagesPlugin.create(picasso))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(TablePlugin.create(context))
.usePlugin(
object : AbstractMarkwonPlugin() {
.usePlugin(object : AbstractMarkwonPlugin() {
override fun configureTheme(builder: MarkwonTheme.Builder) {
builder.linkColor(ContextCompat.getColor(context, R.color.hyperLink))
.isLinkUnderlined(true)
@@ -39,17 +38,14 @@ internal object MarkwonFactory {
}
fun createForNotification(context: Context, picasso: Picasso): Markwon {
val headingSizes = floatArrayOf(
2f, 1.5f, 1.17f, 1f, .83f, .67f
)
val headingSizes = floatArrayOf(2f, 1.5f, 1.17f, 1f, .83f, .67f)
val bulletGapWidth = (8 * context.resources.displayMetrics.density + 0.5f).toInt()
return Markwon.builder(context)
.usePlugin(CorePlugin.create())
.usePlugin(PicassoImagesPlugin.create(picasso))
.usePlugin(StrikethroughPlugin.create())
.usePlugin(
object : AbstractMarkwonPlugin() {
.usePlugin(object : AbstractMarkwonPlugin() {
override fun configureSpansFactory(builder: MarkwonSpansFactory.Builder) {
builder.setFactory(Heading::class.java) { _, props: RenderProps? ->
arrayOf<Any>(

View File

@@ -32,9 +32,9 @@ internal class MissedMessageUtil(private val api: MessageApi) {
val messages = pagedMessages!!.messages
val filtered = filter(messages, till)
result.addAll(filtered)
if (messages.size != filtered.size
|| messages.size == 0
|| pagedMessages.paging.next == null) {
if (messages.size != filtered.size ||
messages.size == 0 ||
pagedMessages.paging.next == null) {
break
}
since = pagedMessages.paging.since

View File

@@ -1,3 +1,3 @@
package com.github.gotify
internal class SSLSettings(val validateSSL: Boolean, val cert: String)
internal class SSLSettings(val validateSSL: Boolean, val cert: String?)

View File

@@ -9,8 +9,8 @@ internal class Settings(context: Context) {
var url: String
get() = sharedPreferences.getString("url", "")!!
set(value) = sharedPreferences.edit().putString("url", value).apply()
var token: String
get() = sharedPreferences.getString("token", "")!!
var token: String?
get() = sharedPreferences.getString("token", null)
set(value) = sharedPreferences.edit().putString("token", value).apply()
var user: User? = null
get() {
@@ -26,8 +26,8 @@ internal class Settings(context: Context) {
var serverVersion: String
get() = sharedPreferences.getString("version", "UNKNOWN")!!
set(value) = sharedPreferences.edit().putString("version", value).apply()
var cert: String
get() = sharedPreferences.getString("cert", "")!!
var cert: String?
get() = sharedPreferences.getString("cert", null)
set(value) = sharedPreferences.edit().putString("cert", value).apply()
var validateSSL: Boolean
get() = sharedPreferences.getBoolean("validateSSL", true)
@@ -37,13 +37,13 @@ internal class Settings(context: Context) {
sharedPreferences = context.getSharedPreferences("gotify", Context.MODE_PRIVATE)
}
fun tokenExists(): Boolean = token.isNotEmpty()
fun tokenExists(): Boolean = !token.isNullOrEmpty()
fun clear() {
url = ""
token = ""
token = null
validateSSL = true
cert = ""
cert = null
}
fun setUser(name: String?, admin: Boolean) {

View File

@@ -49,7 +49,7 @@ internal object Utils {
.toString()
}
fun resolveAbsoluteUrl(baseURL: String?, target: String?): String? {
fun resolveAbsoluteUrl(baseURL: String, target: String?): String? {
return if (target == null) {
null
} else try {

View File

@@ -1,7 +1,6 @@
package com.github.gotify.api
import java.io.IOException
import java.util.*
import retrofit2.Response
internal class ApiException : Exception {
@@ -23,12 +22,5 @@ internal class ApiException : Exception {
code = 0
}
override fun toString(): String {
return String.format(
Locale.ENGLISH,
"Code(%d) Response: %s",
code,
body.substring(0, body.length.coerceAtMost(200))
)
}
override fun toString() = "Code($code) Response: ${body.take(200)}"
}

View File

@@ -17,13 +17,7 @@ internal class Callback<T> private constructor(
fun onError(t: ApiException)
}
private class RetrofitCallback<T>(callback: Callback<T>) : retrofit2.Callback<T> {
private val callback: Callback<T>
init {
this.callback = callback
}
private class RetrofitCallback<T>(private val callback: Callback<T>) : retrofit2.Callback<T> {
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful) {
callback.onSuccess.onSuccess(response.body())

View File

@@ -16,14 +16,14 @@ import okhttp3.OkHttpClient
internal object CertUtils {
@SuppressLint("CustomX509TrustManager")
private val trustAll: X509TrustManager = object : X509TrustManager {
private val trustAll = object : X509TrustManager {
@SuppressLint("TrustAllX509TrustManager")
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
@SuppressLint("TrustAllX509TrustManager")
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
override fun getAcceptedIssuers() = arrayOf<X509Certificate>()
}
fun parseCertificate(cert: String): Certificate {
@@ -45,8 +45,9 @@ internal object CertUtils {
builder.hostnameVerifier { _, _ -> true }
return
}
if (settings.cert != null) {
val trustManagers = certToTrustManager(settings.cert)
val cert = settings.cert
if (cert != null) {
val trustManagers = certToTrustManager(cert)
if (trustManagers.isNotEmpty()) {
val context = SSLContext.getInstance("TLS")
context.init(arrayOf(), trustManagers, SecureRandom())
@@ -66,7 +67,7 @@ internal object CertUtils {
private fun certToTrustManager(cert: String): Array<TrustManager> {
val certificateFactory = CertificateFactory.getInstance("X.509")
val certificates = certificateFactory.generateCertificates(Utils.stringToInputStream(cert))
require(!certificates.isEmpty()) { "expected non-empty set of trusted certificates" }
require(certificates.isNotEmpty()) { "expected non-empty set of trusted certificates" }
val caKeyStore = newEmptyKeyStore()
certificates.forEachIndexed { index, certificate ->

View File

@@ -9,23 +9,23 @@ import com.github.gotify.client.auth.ApiKeyAuth
import com.github.gotify.client.auth.HttpBasicAuth
internal object ClientFactory {
private fun unauthorized(baseUrl: String, sslSettings: SSLSettings
): ApiClient {
return defaultClient(arrayOfNulls(0), "$baseUrl/", sslSettings)
private fun unauthorized(baseUrl: String, sslSettings: SSLSettings): ApiClient {
return defaultClient(arrayOf(), "$baseUrl/", sslSettings)
}
fun basicAuth(
baseUrl: String,
sslSettings: SSLSettings,
username: String?,
password: String?
username: String,
password: String
): ApiClient {
val client = defaultClient(
arrayOf("basicAuth"),
"$baseUrl/", sslSettings
"$baseUrl/",
sslSettings
)
val auth = client.apiAuthorizations["basicAuth"] as HttpBasicAuth?
auth!!.username = username
val auth = client.apiAuthorizations["basicAuth"] as HttpBasicAuth
auth.username = username
auth.password = password
return client
}

View File

@@ -80,7 +80,7 @@ internal class InitializationActivity : AppCompatActivity() {
}
var response = exception.body
response = response.substring(0, 200.coerceAtMost(response.length))
response = response.take(200)
dialog(getString(R.string.other_error, settings.url, exception.code, response))
}

View File

@@ -2,7 +2,6 @@ package com.github.gotify.log
import android.content.Context
import com.hypertrack.hyperlog.LogFormat
import java.util.*
internal class Format(context: Context) : LogFormat(context) {
override fun getFormattedLogMessage(
@@ -13,5 +12,5 @@ internal class Format(context: Context) : LogFormat(context) {
senderName: String,
osVersion: String,
deviceUuid: String
) = String.format(Locale.ENGLISH, "%s %s: %s", timeStamp, logLevelName, message)
) = "$timeStamp $logLevelName: $message"
}

View File

@@ -83,7 +83,7 @@ internal class LoginActivity : AppCompatActivity() {
}
private fun doCheckUrl() {
val url = binding.gotifyUrl.text.toString()
var url = binding.gotifyUrl.text.toString()
val parsedUrl = HttpUrl.parse(url)
if (parsedUrl == null) {
Utils.showSnackBar(this, "Invalid URL (include http:// or https://)")
@@ -97,19 +97,17 @@ internal class LoginActivity : AppCompatActivity() {
binding.checkurlProgress.visibility = View.VISIBLE
binding.checkurl.visibility = View.GONE
val trimmedUrl = url.trim()
val fixedUrl = if (trimmedUrl.endsWith("/")) {
trimmedUrl.substring(0, trimmedUrl.length - 1)
} else trimmedUrl
url = url.trim()
if (url.endsWith("/")) url.dropLast(1)
try {
ClientFactory.versionApi(fixedUrl, tempSslSettings())
ClientFactory.versionApi(url, tempSslSettings())
?.version
?.enqueue(Callback.callInUI(this, onValidUrl(fixedUrl), onInvalidUrl(fixedUrl)))
?.enqueue(Callback.callInUI(this, onValidUrl(url), onInvalidUrl(url)))
} catch (e: Exception) {
binding.checkurlProgress.visibility = View.GONE
binding.checkurl.visibility = View.VISIBLE
val errorMsg = getString(R.string.version_failed, "$fixedUrl/version", e.message)
val errorMsg = getString(R.string.version_failed, "$url/version", e.message)
Utils.showSnackBar(this, errorMsg)
}
}
@@ -169,7 +167,7 @@ internal class LoginActivity : AppCompatActivity() {
super.onActivityResult(requestCode, resultCode, data)
try {
if (requestCode == FILE_SELECT_CODE) {
require(resultCode == RESULT_OK) { String.format("result was %d", resultCode) }
require(resultCode == RESULT_OK) { "result was $resultCode" }
requireNotNull(data) { "file path was null" }
val uri = data.data ?: throw IllegalArgumentException("file path was null")
@@ -268,7 +266,7 @@ internal class LoginActivity : AppCompatActivity() {
private fun onCreatedClient(client: Client) {
settings.token = client.token
settings.validateSSL = !disableSslValidation
settings.cert = caCertContents.toString()
settings.cert = caCertContents
Utils.showSnackBar(this, getString(R.string.created_client))
startActivity(Intent(this, InitializationActivity::class.java))
@@ -291,6 +289,6 @@ internal class LoginActivity : AppCompatActivity() {
}
private fun tempSslSettings(): SSLSettings {
return SSLSettings(!disableSslValidation, caCertContents.toString())
return SSLSettings(!disableSslValidation, caCertContents)
}
}

View File

@@ -3,9 +3,7 @@ package com.github.gotify.messages
import com.github.gotify.client.model.Message
internal object Extras {
fun useMarkdown(message: Message): Boolean {
return useMarkdown(message.extras)
}
fun useMarkdown(message: Message): Boolean = useMarkdown(message.extras)
fun useMarkdown(extras: Map<String, Any>?): Boolean {
if (extras == null) {

View File

@@ -26,9 +26,9 @@ import com.github.gotify.databinding.MessageItemCompactBinding
import com.github.gotify.messages.provider.MessageWithImage
import com.squareup.picasso.Picasso
import io.noties.markwon.Markwon
import org.threeten.bp.OffsetDateTime
import java.text.DateFormat
import java.util.*
import org.threeten.bp.OffsetDateTime
internal class ListMessageAdapter(
private val context: Context,
@@ -72,13 +72,13 @@ internal class ListMessageAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val message = items[position]
if (Extras.useMarkdown(message.message)) {
holder.message!!.autoLinkMask = 0
markwon.setMarkdown(holder.message!!, message.message.message)
holder.message.autoLinkMask = 0
markwon.setMarkdown(holder.message, message.message.message)
} else {
holder.message!!.autoLinkMask = Linkify.WEB_URLS
holder.message!!.text = message.message.message
holder.message.autoLinkMask = Linkify.WEB_URLS
holder.message.text = message.message.message
}
holder.title!!.text = message.message.title
holder.title.text = message.message.title
picasso.load(Utils.resolveAbsoluteUrl("${settings.url}/", message.image))
.error(R.drawable.ic_alarm)
.placeholder(R.drawable.ic_placeholder)
@@ -87,10 +87,10 @@ internal class ListMessageAdapter(
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
val timeFormat = prefs.getString(timeFormatPrefsKey, timeFormatRelative)
holder.setDateTime(message.message.date, timeFormat == timeFormatRelative)
holder.date!!.setOnClickListener { holder.switchTimeFormat() }
holder.date.setOnClickListener { holder.switchTimeFormat() }
holder.delete!!.setOnClickListener {
delete.delete(holder.adapterPosition, message.message!!, false)
holder.delete.setOnClickListener {
delete.delete(holder.adapterPosition, message.message, false)
}
}
@@ -102,14 +102,14 @@ internal class ListMessageAdapter(
}
class ViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {
var image: ImageView? = null
var message: TextView? = null
var title: TextView? = null
var date: TextView? = null
var delete: ImageButton? = null
lateinit var image: ImageView
lateinit var message: TextView
lateinit var title: TextView
lateinit var date: TextView
lateinit var delete: ImageButton
private var relativeTimeFormat = true
private var dateTime: OffsetDateTime? = null
private lateinit var dateTime: OffsetDateTime
init {
enableCopyToClipboard()
@@ -133,38 +133,35 @@ internal class ListMessageAdapter(
updateDate()
}
fun setDateTime(dateTime: OffsetDateTime?, relativeTimeFormatPreference: Boolean) {
fun setDateTime(dateTime: OffsetDateTime, relativeTimeFormatPreference: Boolean) {
this.dateTime = dateTime
relativeTimeFormat = relativeTimeFormatPreference
updateDate()
}
private fun updateDate() {
var text = "?"
if (dateTime != null) {
text = if (relativeTimeFormat) {
val text = if (relativeTimeFormat) {
// Relative time format
Utils.dateToRelative(dateTime!!)
Utils.dateToRelative(dateTime)
} else {
// Absolute time format
val time = dateTime!!.toInstant().toEpochMilli()
val time = dateTime.toInstant().toEpochMilli()
val date = Date(time)
if (DateUtils.isToday(time)) {
DateFormat.getTimeInstance(DateFormat.SHORT).format(date)
} else {
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT)
.format(date)
DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(date)
}
}
}
date!!.text = text
date.text = text
}
private fun enableCopyToClipboard() {
super.itemView.setOnLongClickListener { view: View ->
val clipboard = view.context
.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?
val clip = ClipData.newPlainText("GotifyMessageContent", message!!.text.toString())
val clip = ClipData.newPlainText("GotifyMessageContent", message.text.toString())
if (clipboard != null) {
clipboard.setPrimaryClip(clip)
Toast.makeText(
@@ -178,7 +175,7 @@ internal class ListMessageAdapter(
}
}
interface Delete {
fun interface Delete {
fun delete(position: Int, message: Message, listAnimation: Boolean)
}
}

View File

@@ -46,10 +46,8 @@ import com.github.gotify.init.InitializationActivity
import com.github.gotify.log.Log
import com.github.gotify.log.LogsActivity
import com.github.gotify.login.LoginActivity
import com.github.gotify.messages.ListMessageAdapter.Delete
import com.github.gotify.messages.provider.*
import com.github.gotify.service.WebSocketService
import com.github.gotify.service.WebSocketService.Companion.NEW_MESSAGE_BROADCAST
import com.github.gotify.settings.SettingsActivity
import com.github.gotify.sharing.ShareActivity
import com.google.android.material.navigation.NavigationView
@@ -86,8 +84,7 @@ internal class MessagesActivity :
super.onCreate(savedInstanceState)
binding = ActivityMessagesBinding.inflate(layoutInflater)
setContentView(binding.root)
viewModel = ViewModelProvider(this, MessagesModelFactory(this))
.get(MessagesModel::class.java)
viewModel = ViewModelProvider(this, MessagesModelFactory(this))[MessagesModel::class.java]
Log.i("Entering " + javaClass.simpleName)
initDrawer()
val layoutManager = LinearLayoutManager(this)
@@ -99,16 +96,14 @@ internal class MessagesActivity :
this,
viewModel.settings,
viewModel.picassoHandler.get(),
emptyList(),
object : Delete {
override fun delete(position: Int, message: Message, listAnimation: Boolean) {
emptyList()
) { position, message, listAnimation ->
scheduleDeletion(
position,
message,
listAnimation
)
}
})
messagesView.addItemDecoration(dividerItemDecoration)
messagesView.setHasFixedSize(true)
messagesView.layoutManager = layoutManager
@@ -191,23 +186,18 @@ internal class MessagesActivity :
viewModel.targetReferences.clear()
updateMessagesAndStopLoading(viewModel.messages[viewModel.appId])
var selectedItem = menu.findItem(R.id.nav_all_messages)
for (i in applications.indices) {
val app = applications[i]
val item = menu.add(R.id.apps, i, APPLICATION_ORDER, app.name)
applications.indices.forEach {
val app = applications[it]
val item = menu.add(R.id.apps, it, APPLICATION_ORDER, app.name)
item.isCheckable = true
if (app.id == viewModel.appId) selectedItem = item
val t = Utils.toDrawable(
resources
) { icon: Drawable? -> item.icon = icon }
val t = Utils.toDrawable(resources) { icon: Drawable? ->
item.icon = icon
}
viewModel.targetReferences.add(t)
viewModel
.picassoHandler
viewModel.picassoHandler
.get()
.load(
Utils.resolveAbsoluteUrl(
viewModel.settings.url + "/", app.image
)
)
.load(Utils.resolveAbsoluteUrl(viewModel.settings.url + "/", app.image))
.error(R.drawable.ic_alarm)
.placeholder(R.drawable.ic_placeholder)
.resize(100, 100)
@@ -239,10 +229,8 @@ internal class MessagesActivity :
version.text =
getString(R.string.versions, BuildConfig.VERSION_NAME, settings.serverVersion)
val refreshAll = headerView.findViewById<ImageButton>(R.id.refresh_all)
refreshAll.setOnClickListener { view: View? ->
onRefreshAll(
view
)
refreshAll.setOnClickListener {
onRefreshAll(it)
}
}
@@ -259,7 +247,7 @@ internal class MessagesActivity :
val id = item.itemId
if (item.groupId == R.id.apps) {
val app = viewModel.appsHolder.get()[id]
updateAppOnDrawerClose = if (app != null) app.id else MessageState.ALL_MESSAGES
updateAppOnDrawerClose = app.id
startLoading()
binding.appBarDrawer.toolbar.subtitle = item.title
} else if (id == R.id.nav_all_messages) {
@@ -273,7 +261,7 @@ internal class MessagesActivity :
.setPositiveButton(R.string.yes) { _, _ ->
doLogout()
}
.setNegativeButton(R.string.cancel) { a, b -> }
.setNegativeButton(R.string.cancel, null)
.show()
} else if (id == R.id.nav_logs) {
startActivity(Intent(this, LogsActivity::class.java))
@@ -309,22 +297,22 @@ internal class MessagesActivity :
val nManager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
nManager.cancelAll()
val filter = IntentFilter()
filter.addAction(NEW_MESSAGE_BROADCAST)
filter.addAction(WebSocketService.NEW_MESSAGE_BROADCAST)
registerReceiver(receiver, filter)
launchCoroutine {
updateMissedMessages(viewModel.messages.getLastReceivedMessage())
}
var selectedIndex: Int = R.id.nav_all_messages
var selectedIndex = R.id.nav_all_messages
val appId = viewModel.appId
if (appId != MessageState.ALL_MESSAGES) {
val apps = viewModel.appsHolder.get()
for (i in apps.indices) {
if (apps[i].id == appId) {
selectedIndex = i
apps.indices.forEach {
if (apps[it].id == appId) {
selectedIndex = it
}
}
}
listMessageAdapter!!.notifyDataSetChanged()
listMessageAdapter.notifyDataSetChanged()
selectAppInMenu(binding.navView.menu.findItem(selectedIndex))
super.onResume()
}
@@ -337,17 +325,26 @@ internal class MessagesActivity :
private fun selectAppInMenu(appItem: MenuItem?) {
if (appItem != null) {
appItem.isChecked = true
if (appItem.itemId != R.id.nav_all_messages) binding.appBarDrawer.toolbar.subtitle =
appItem.title
if (appItem.itemId != R.id.nav_all_messages) {
binding.appBarDrawer.toolbar.subtitle = appItem.title
}
}
}
private fun scheduleDeletion(position: Int, message: Message, listAnimation: Boolean) {
private fun scheduleDeletion(
position: Int,
message: Message,
listAnimation: Boolean
) {
val adapter = binding.messagesView.adapter as ListMessageAdapter
val messages = viewModel.messages
messages.deleteLocal(message)
adapter.items = messages[viewModel.appId]
if (listAnimation) adapter.notifyItemRemoved(position) else adapter.notifyDataSetChanged()
if (listAnimation) {
adapter.notifyItemRemoved(position)
} else {
adapter.notifyDataSetChanged()
}
showDeletionSnackbar()
}
@@ -358,17 +355,19 @@ internal class MessagesActivity :
val adapter = binding.messagesView.adapter as ListMessageAdapter
val appId = viewModel.appId
adapter.items = messages[appId]
val insertPosition =
if (appId == MessageState.ALL_MESSAGES) deletion.allPosition else deletion.appPosition
val insertPosition = if (appId == MessageState.ALL_MESSAGES) {
deletion.allPosition
} else {
deletion.appPosition
}
adapter.notifyItemInserted(insertPosition)
}
}
private fun showDeletionSnackbar() {
val view: View = binding.swipeRefresh
val snackbar: Snackbar =
Snackbar.make(view, R.string.snackbar_deleted, Snackbar.LENGTH_LONG)
snackbar.setAction(R.string.snackbar_undo) { v -> undoDelete() }
val snackbar = Snackbar.make(view, R.string.snackbar_deleted, Snackbar.LENGTH_LONG)
snackbar.setAction(R.string.snackbar_undo) { undoDelete() }
snackbar.addCallback(SnackbarCallback())
snackbar.show()
}
@@ -389,8 +388,9 @@ internal class MessagesActivity :
}
}
private inner class SwipeToDeleteCallback(private val adapter: ListMessageAdapter) :
ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
private inner class SwipeToDeleteCallback(
private val adapter: ListMessageAdapter
) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
private var icon: Drawable?
private val background: ColorDrawable
@@ -411,9 +411,7 @@ internal class MessagesActivity :
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
) = false
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
@@ -475,14 +473,15 @@ internal class MessagesActivity :
private inner class MessageListOnScrollListener : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(view: RecyclerView, scrollState: Int) {}
override fun onScrolled(view: RecyclerView, dx: Int, dy: Int) {
val linearLayoutManager = view.layoutManager as LinearLayoutManager?
if (linearLayoutManager != null) {
val lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition()
val totalItemCount = view.adapter!!.itemCount
if (lastVisibleItem > totalItemCount - 15 && totalItemCount != 0 && viewModel.messages.canLoadMore(
viewModel.appId
)
if (lastVisibleItem > totalItemCount - 15 &&
totalItemCount != 0 &&
viewModel.messages.canLoadMore(viewModel.appId)
) {
if (!isLoadMore) {
isLoadMore = true
@@ -540,13 +539,8 @@ internal class MessagesActivity :
client.createService(ApplicationApi::class.java)
.deleteApp(appId)
.enqueue(
Callback.callInUI(
this,
{ refreshAll() }
) {
Utils.showSnackBar(
this, getString(R.string.error_delete_app)
)
Callback.callInUI(this, { refreshAll() }) {
Utils.showSnackBar(this, getString(R.string.error_delete_app))
})
}

View File

@@ -4,7 +4,6 @@ import android.app.Activity
import androidx.lifecycle.ViewModel
import com.github.gotify.Settings
import com.github.gotify.api.ClientFactory
import com.github.gotify.client.ApiClient
import com.github.gotify.client.api.MessageApi
import com.github.gotify.messages.provider.ApplicationHolder
import com.github.gotify.messages.provider.MessageFacade
@@ -13,22 +12,14 @@ import com.github.gotify.picasso.PicassoHandler
import com.squareup.picasso.Target
internal class MessagesModel(parentView: Activity) : ViewModel() {
val settings: Settings
val picassoHandler: PicassoHandler
val client: ApiClient
val appsHolder: ApplicationHolder
val messages: MessageFacade
val settings = Settings(parentView)
val picassoHandler = PicassoHandler(parentView, settings)
val client = ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token)
val appsHolder = ApplicationHolder(parentView, client)
val messages = MessageFacade(client.createService(MessageApi::class.java), appsHolder)
// we need to keep the target references otherwise they get gc'ed before they can be called.
val targetReferences = mutableListOf<Target>()
var appId = MessageState.ALL_MESSAGES
init {
settings = Settings(parentView)
picassoHandler = PicassoHandler(parentView, settings)
client = ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token)
appsHolder = ApplicationHolder(parentView, client)
messages = MessageFacade(client.createService(MessageApi::class.java), appsHolder)
}
}

View File

@@ -9,13 +9,11 @@ import com.github.gotify.client.api.ApplicationApi
import com.github.gotify.client.model.Application
internal class ApplicationHolder(private val activity: Activity, private val client: ApiClient) {
private var state: List<Application> = listOf()
private var state = listOf<Application>()
private var onUpdate: Runnable? = null
private var onUpdateFailed: Runnable? = null
fun wasRequested(): Boolean {
return state.isNotEmpty()
}
fun wasRequested() = state.isNotEmpty()
fun request() {
client.createService(ApplicationApi::class.java)

View File

@@ -4,13 +4,8 @@ import com.github.gotify.client.api.MessageApi
import com.github.gotify.client.model.Message
internal class MessageFacade(api: MessageApi, private val applicationHolder: ApplicationHolder) {
private val requester: MessageRequester
private val state: MessageStateHolder
init {
requester = MessageRequester(api)
state = MessageStateHolder()
}
private val requester = MessageRequester(api)
private val state = MessageStateHolder()
@Synchronized
operator fun get(appId: Long): List<MessageWithImage> {

View File

@@ -11,7 +11,7 @@ import com.github.gotify.log.Log
internal class MessageRequester(private val messageApi: MessageApi) {
fun loadMore(state: MessageState): PagedMessages? {
return try {
Log.i("Loading more messages for " + state.appId)
Log.i("Loading more messages for ${state.appId}")
if (MessageState.ALL_MESSAGES == state.appId) {
Api.execute(messageApi.getMessages(LIMIT, state.nextSince))
} else {
@@ -24,7 +24,7 @@ internal class MessageRequester(private val messageApi: MessageApi) {
}
fun asyncRemoveMessage(message: Message) {
Log.i("Removing message with id " + message.id)
Log.i("Removing message with id ${message.id}")
messageApi.deleteMessage(message.id).enqueue(Callback.call())
}

View File

@@ -28,7 +28,7 @@ internal class PicassoDataRequestHandler : RequestHandler() {
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
if (bitmap == null) {
val show = if (uri.length > 50) uri.substring(0, 49) + "..." else uri
val show = if (uri.length > 50) uri.take(50) + "..." else uri
val malformed = RuntimeException("Malformed data uri: $show")
Log.e("Could not load image", malformed)
throw malformed

View File

@@ -14,10 +14,10 @@ import com.github.gotify.log.Log
import com.github.gotify.messages.provider.MessageImageCombiner
import com.squareup.picasso.OkHttp3Downloader
import com.squareup.picasso.Picasso
import okhttp3.Cache
import okhttp3.OkHttpClient
import java.io.File
import java.io.IOException
import okhttp3.Cache
import okhttp3.OkHttpClient
internal class PicassoHandler(private val context: Context, private val settings: Settings) {
companion object {
@@ -31,7 +31,7 @@ internal class PicassoHandler(private val context: Context, private val settings
)
private val picasso: Picasso = makePicasso()
private val appIdToAppImage: MutableMap<Long, String> = mutableMapOf()
private val appIdToAppImage = mutableMapOf<Long, String>()
private fun makePicasso(): Picasso {
val builder = OkHttpClient.Builder()
@@ -45,9 +45,7 @@ internal class PicassoHandler(private val context: Context, private val settings
}
@Throws(IOException::class)
fun getImageFromUrl(url: String?): Bitmap {
return picasso.load(url).get()
}
fun getImageFromUrl(url: String?): Bitmap = picasso.load(url).get()
fun getIcon(appId: Long): Bitmap {
if (appId == -1L) {

View File

@@ -19,7 +19,7 @@ import okhttp3.*
internal class WebSocketConnection(
private val baseUrl: String,
settings: SSLSettings,
private val token: String,
private val token: String?,
private val connectivityManager: ConnectivityManager,
private val alarmManager: AlarmManager
) {
@@ -33,12 +33,12 @@ internal class WebSocketConnection(
private var errorCount = 0
private var webSocket: WebSocket? = null
private var onMessage: SuccessCallback<Message>? = null
private var onClose: Runnable? = null
private var onOpen: Runnable? = null
private var onBadRequest: BadRequestRunnable? = null
private var onNetworkFailure: OnNetworkFailureRunnable? = null
private var onReconnected: Runnable? = null
private lateinit var onMessage: SuccessCallback<Message>
private lateinit var onClose: Runnable
private lateinit var onOpen: Runnable
private lateinit var onBadRequest: BadRequestRunnable
private lateinit var onNetworkFailure: OnNetworkFailureRunnable
private lateinit var onReconnected: Runnable
private var state: State? = null
init {
@@ -149,10 +149,10 @@ internal class WebSocketConnection(
syncExec {
state = State.Connected
Log.i("WebSocket($id): opened")
onOpen!!.run()
onOpen.run()
if (errorCount > 0) {
onReconnected!!.run()
onReconnected.run()
errorCount = 0
}
}
@@ -163,7 +163,7 @@ internal class WebSocketConnection(
syncExec {
Log.i("WebSocket($id): received message $text")
val message = Utils.JSON.fromJson(text, Message::class.java)
onMessage!!.onSuccess(message)
onMessage.onSuccess(message)
}
super.onMessage(webSocket, text)
}
@@ -172,7 +172,7 @@ internal class WebSocketConnection(
syncExec {
if (state == State.Connected) {
Log.w("WebSocket($id): closed")
onClose!!.run()
onClose.run()
}
state = State.Disconnected
}
@@ -186,7 +186,7 @@ internal class WebSocketConnection(
syncExec {
state = State.Disconnected
if (response != null && response.code() >= 400 && response.code() <= 499) {
onBadRequest!!.execute(message)
onBadRequest.execute(message)
close()
return@syncExec
}
@@ -200,7 +200,7 @@ internal class WebSocketConnection(
val minutes = (errorCount * 2 - 1).coerceAtMost(20)
onNetworkFailure!!.execute(minutes)
onNetworkFailure.execute(minutes)
scheduleReconnect(TimeUnit.MINUTES.toSeconds(minutes.toLong()))
}
super.onFailure(webSocket, t, response)
@@ -215,11 +215,11 @@ internal class WebSocketConnection(
}
}
internal interface BadRequestRunnable {
internal fun interface BadRequestRunnable {
fun execute(message: String)
}
internal interface OnNetworkFailureRunnable {
internal fun interface OnNetworkFailureRunnable {
fun execute(minutes: Int)
}

View File

@@ -95,16 +95,8 @@ internal class WebSocketService : Service() {
)
.onOpen { onOpen() }
.onClose { onClose() }
.onBadRequest(object : BadRequestRunnable {
override fun execute(message: String) {
onBadRequest(message)
}
})
.onNetworkFailure(object : OnNetworkFailureRunnable {
override fun execute(minutes: Int) {
onNetworkFailure(minutes)
}
})
.onBadRequest { message -> onBadRequest(message) }
.onNetworkFailure { minutes -> onNetworkFailure(minutes) }
.onMessage { if (it != null) onMessage(it) }
.onReconnected { notifyMissedNotifications() }
.start()

View File

@@ -47,7 +47,9 @@ internal class SettingsActivity : AppCompatActivity(), OnSharedPreferenceChangeL
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
if (getString(R.string.setting_key_theme) == key) {
ThemeHelper.setTheme(
this, sharedPreferences.getString(key, getString(R.string.theme_default))!!)
this,
sharedPreferences.getString(key, getString(R.string.theme_default))!!
)
}
}

View File

@@ -52,8 +52,11 @@ internal class ShareActivity : AppCompatActivity() {
}
if (!settings.tokenExists()) {
Toast.makeText(applicationContext, R.string.not_loggedin_share, Toast.LENGTH_SHORT)
.show()
Toast.makeText(
applicationContext,
R.string.not_loggedin_share,
Toast.LENGTH_SHORT
).show()
finish()
return
}