mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-22 21:19:31 +01:00
userManager.currentUser was called too often. I was not able to prove that a bug is related to it but i think it may fix some hidden bugs. CurrentUserProviderImpl is now used throughout the code to access the current user. userManager.currentUser is only used from CurrentUserProviderImpl whenever the _currentUser was null (should only happen on app startup) To avoid multiple initialization of CurrentUserProviderImpl it was changed to be a @Singleton The handling should soon be replaced with coroutine flows. However for the v21.0.0 release it's still done with RxJava to avoid bugs. Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
471 lines
17 KiB
Kotlin
471 lines
17 KiB
Kotlin
/*
|
|
* Nextcloud Talk - Android Client
|
|
*
|
|
* SPDX-FileCopyrightText: 2023 Marcel Hibbe <dev@mhibbe.de>
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
package com.nextcloud.talk.diagnose
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.content.ClipData
|
|
import android.content.ClipboardManager
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.graphics.Typeface
|
|
import android.graphics.drawable.ColorDrawable
|
|
import android.net.Uri
|
|
import android.os.Build
|
|
import android.os.Build.MANUFACTURER
|
|
import android.os.Build.MODEL
|
|
import android.os.Bundle
|
|
import android.text.SpannableStringBuilder
|
|
import android.util.TypedValue
|
|
import android.view.Menu
|
|
import android.view.MenuItem
|
|
import android.view.ViewGroup
|
|
import android.widget.TextView
|
|
import android.widget.Toast
|
|
import androidx.core.text.bold
|
|
import androidx.core.view.updateLayoutParams
|
|
import autodagger.AutoInjector
|
|
import com.nextcloud.talk.BuildConfig
|
|
import com.nextcloud.talk.R
|
|
import com.nextcloud.talk.activities.BaseActivity
|
|
import com.nextcloud.talk.api.NcApi
|
|
import com.nextcloud.talk.application.NextcloudTalkApplication
|
|
import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager
|
|
import com.nextcloud.talk.databinding.ActivityDiagnoseBinding
|
|
import com.nextcloud.talk.users.UserManager
|
|
import com.nextcloud.talk.utils.BrandingUtils
|
|
import com.nextcloud.talk.utils.ClosedInterfaceImpl
|
|
import com.nextcloud.talk.utils.DisplayUtils
|
|
import com.nextcloud.talk.utils.NotificationUtils
|
|
import com.nextcloud.talk.utils.PushUtils.Companion.LATEST_PUSH_REGISTRATION_AT_PUSH_PROXY
|
|
import com.nextcloud.talk.utils.PushUtils.Companion.LATEST_PUSH_REGISTRATION_AT_SERVER
|
|
import com.nextcloud.talk.utils.UserIdUtils
|
|
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
|
|
import com.nextcloud.talk.utils.power.PowerManagerUtils
|
|
import javax.inject.Inject
|
|
|
|
@AutoInjector(NextcloudTalkApplication::class)
|
|
@Suppress("TooManyFunctions")
|
|
class DiagnoseActivity : BaseActivity() {
|
|
private lateinit var binding: ActivityDiagnoseBinding
|
|
|
|
@Inject
|
|
lateinit var arbitraryStorageManager: ArbitraryStorageManager
|
|
|
|
@Inject
|
|
lateinit var ncApi: NcApi
|
|
|
|
@Inject
|
|
lateinit var userManager: UserManager
|
|
|
|
@Inject
|
|
lateinit var platformPermissionUtil: PlatformPermissionUtil
|
|
|
|
private var isGooglePlayServicesAvailable: Boolean = false
|
|
|
|
private val markdownText = SpannableStringBuilder()
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
|
|
|
|
binding = ActivityDiagnoseBinding.inflate(layoutInflater)
|
|
setupActionBar()
|
|
setContentView(binding.root)
|
|
setupSystemColors()
|
|
}
|
|
|
|
override fun onResume() {
|
|
super.onResume()
|
|
supportActionBar?.show()
|
|
|
|
isGooglePlayServicesAvailable = ClosedInterfaceImpl().isGooglePlayServicesAvailable
|
|
|
|
markdownText.clear()
|
|
setupMetaValues()
|
|
setupPhoneValues()
|
|
setupAppValues()
|
|
setupAccountValues()
|
|
|
|
createLayoutFromMarkdown()
|
|
}
|
|
|
|
private fun setupActionBar() {
|
|
setSupportActionBar(binding.settingsToolbar)
|
|
binding.settingsToolbar.setNavigationOnClickListener {
|
|
onBackPressedDispatcher.onBackPressed()
|
|
}
|
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
|
supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(android.R.color.transparent, null)))
|
|
supportActionBar?.title = context.getString(R.string.nc_settings_diagnose_title)
|
|
viewThemeUtils.material.themeToolbar(binding.settingsToolbar)
|
|
}
|
|
|
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
|
menuInflater.inflate(R.menu.menu_diagnose, menu)
|
|
return true
|
|
}
|
|
|
|
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
|
super.onPrepareOptionsMenu(menu)
|
|
menu.findItem(R.id.create_issue).isVisible = BrandingUtils.isOriginalNextcloudClient(applicationContext)
|
|
return true
|
|
}
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
return when (item.itemId) {
|
|
android.R.id.home -> {
|
|
onBackPressedDispatcher.onBackPressed()
|
|
true
|
|
}
|
|
|
|
R.id.copy -> {
|
|
copyToClipboard(markdownText.toString())
|
|
true
|
|
}
|
|
|
|
R.id.share -> {
|
|
shareToOtherApps(markdownText.toString())
|
|
true
|
|
}
|
|
|
|
R.id.send_mail -> {
|
|
composeEmail(markdownText.toString())
|
|
true
|
|
}
|
|
|
|
R.id.create_issue -> {
|
|
createGithubIssue(markdownText.toString())
|
|
true
|
|
}
|
|
|
|
else -> {
|
|
super.onOptionsItemSelected(item)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun shareToOtherApps(message: String) {
|
|
val sendIntent: Intent = Intent().apply {
|
|
action = Intent.ACTION_SEND
|
|
putExtra(Intent.EXTRA_TEXT, message)
|
|
type = "text/plain"
|
|
}
|
|
val shareIntent = Intent.createChooser(sendIntent, getString(R.string.share))
|
|
startActivity(shareIntent)
|
|
}
|
|
|
|
private fun composeEmail(text: String) {
|
|
val intent = Intent(Intent.ACTION_SENDTO).apply {
|
|
val appName = context.resources.getString(R.string.nc_app_product_name)
|
|
|
|
data = Uri.parse("mailto:")
|
|
putExtra(Intent.EXTRA_SUBJECT, appName)
|
|
putExtra(Intent.EXTRA_TEXT, text)
|
|
}
|
|
if (intent.resolveActivity(packageManager) != null) {
|
|
startActivity(intent)
|
|
}
|
|
}
|
|
|
|
private fun createGithubIssue(text: String) {
|
|
copyToClipboard(text)
|
|
|
|
startActivity(
|
|
Intent(
|
|
Intent.ACTION_VIEW,
|
|
Uri.parse(resources!!.getString(R.string.nc_talk_android_issues_url))
|
|
)
|
|
)
|
|
}
|
|
|
|
private fun copyToClipboard(text: String) {
|
|
val clipboardManager =
|
|
getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
|
val clipData = ClipData.newPlainText(
|
|
resources?.getString(R.string.nc_app_product_name),
|
|
text
|
|
)
|
|
clipboardManager.setPrimaryClip(clipData)
|
|
|
|
Toast.makeText(
|
|
context,
|
|
context.resources.getString(R.string.nc_common_copy_success),
|
|
Toast.LENGTH_LONG
|
|
).show()
|
|
}
|
|
|
|
private fun setupMetaValues() {
|
|
addHeadline(context.resources.getString(R.string.nc_diagnose_meta_category_title))
|
|
addKey(context.resources.getString(R.string.nc_diagnose_meta_system_report_date))
|
|
addValue(DisplayUtils.unixTimeToHumanReadable(System.currentTimeMillis()))
|
|
}
|
|
|
|
private fun setupPhoneValues() {
|
|
addHeadline(context.resources.getString(R.string.nc_diagnose_phone_category_title))
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_device_name_title))
|
|
addValue(getDeviceName())
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_android_version_title))
|
|
addValue(Build.VERSION.SDK_INT.toString())
|
|
|
|
if (isGooglePlayServicesAvailable) {
|
|
addKey(context.resources.getString(R.string.nc_diagnose_gplay_available_title))
|
|
addValue(context.resources.getString(R.string.nc_diagnose_gplay_available_yes))
|
|
} else {
|
|
addKey(context.resources.getString(R.string.nc_diagnose_gplay_available_title))
|
|
addValue(context.resources.getString(R.string.nc_diagnose_gplay_available_no))
|
|
}
|
|
}
|
|
|
|
@SuppressLint("SetTextI18n")
|
|
@Suppress("MagicNumber")
|
|
private fun setupAppValues() {
|
|
addHeadline(context.resources.getString(R.string.nc_diagnose_app_category_title))
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_app_name_title))
|
|
addValue(context.resources.getString(R.string.nc_app_product_name))
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_app_version_title))
|
|
addValue(String.format("v" + BuildConfig.VERSION_NAME))
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_flavor))
|
|
addValue(BuildConfig.FLAVOR)
|
|
|
|
if (isGooglePlayServicesAvailable) {
|
|
setupAppValuesForGooglePlayServices()
|
|
}
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_app_users_amount))
|
|
addValue(userManager.users.blockingGet().size.toString())
|
|
}
|
|
|
|
private fun setupAppValuesForGooglePlayServices() {
|
|
addKey(context.resources.getString(R.string.nc_diagnose_battery_optimization_title))
|
|
|
|
if (PowerManagerUtils().isIgnoringBatteryOptimizations()) {
|
|
addValue(context.resources.getString(R.string.nc_diagnose_battery_optimization_ignored))
|
|
} else {
|
|
addValue(context.resources.getString(R.string.nc_diagnose_battery_optimization_not_ignored))
|
|
}
|
|
|
|
// handle notification permission on API level >= 33
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
addKey(context.resources.getString(R.string.nc_diagnose_notification_permission))
|
|
if (platformPermissionUtil.isPostNotificationsPermissionGranted()) {
|
|
addValue(context.resources.getString(R.string.nc_settings_notifications_granted))
|
|
} else {
|
|
addValue(context.resources.getString(R.string.nc_settings_notifications_declined))
|
|
}
|
|
}
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_notification_calls_channel_permission))
|
|
addValue(
|
|
translateBoolean(
|
|
NotificationUtils.isCallsNotificationChannelEnabled(this)
|
|
)
|
|
)
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_notification_messages_channel_permission))
|
|
addValue(
|
|
translateBoolean(
|
|
NotificationUtils.isMessagesNotificationChannelEnabled(this)
|
|
)
|
|
)
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_firebase_push_token_title))
|
|
if (appPreferences.pushToken.isNullOrEmpty()) {
|
|
addValue(context.resources.getString(R.string.nc_diagnose_firebase_push_token_missing))
|
|
} else {
|
|
addValue("${appPreferences.pushToken.substring(0, PUSH_TOKEN_PREFIX_END)}...")
|
|
}
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_firebase_push_token_latest_generated))
|
|
if (appPreferences.pushTokenLatestGeneration != null && appPreferences.pushTokenLatestGeneration != 0L) {
|
|
addValue(
|
|
DisplayUtils.unixTimeToHumanReadable(
|
|
appPreferences
|
|
.pushTokenLatestGeneration
|
|
)
|
|
)
|
|
} else {
|
|
addValue(context.resources.getString(R.string.nc_common_unknown))
|
|
}
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_firebase_push_token_latest_fetch))
|
|
if (appPreferences.pushTokenLatestFetch != null && appPreferences.pushTokenLatestFetch != 0L) {
|
|
addValue(DisplayUtils.unixTimeToHumanReadable(appPreferences.pushTokenLatestFetch))
|
|
} else {
|
|
addValue(context.resources.getString(R.string.nc_common_unknown))
|
|
}
|
|
}
|
|
|
|
private fun setupAccountValues() {
|
|
val currentUser = currentUserProvider.currentUser.blockingGet()
|
|
|
|
addHeadline(context.resources.getString(R.string.nc_diagnose_account_category_title))
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_account_server))
|
|
addValue(currentUser.baseUrl!!)
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_account_user_name))
|
|
addValue(currentUser.displayName!!)
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_account_user_status_enabled))
|
|
addValue(
|
|
translateBoolean(
|
|
(currentUser.capabilities?.userStatusCapability?.enabled)
|
|
)
|
|
)
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_account_server_notification_app))
|
|
addValue(
|
|
translateBoolean(currentUser.capabilities?.notificationsCapability?.features?.isNotEmpty())
|
|
)
|
|
|
|
if (isGooglePlayServicesAvailable) {
|
|
setupPushRegistrationDiagnose()
|
|
}
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_server_version))
|
|
addValue(currentUser.serverVersion?.versionString!!)
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_server_talk_version))
|
|
addValue(currentUser.capabilities?.spreedCapability?.version!!)
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_signaling_mode_title))
|
|
|
|
if (currentUser.externalSignalingServer?.externalSignalingServer?.isNotEmpty() == true) {
|
|
addValue(context.resources.getString(R.string.nc_diagnose_signaling_mode_extern))
|
|
} else {
|
|
addValue(context.resources.getString(R.string.nc_diagnose_signaling_mode_intern))
|
|
}
|
|
}
|
|
|
|
private fun setupPushRegistrationDiagnose() {
|
|
val accountId = UserIdUtils.getIdForUser(currentUserProvider.currentUser.blockingGet())
|
|
|
|
val latestPushRegistrationAtServer = arbitraryStorageManager.getStorageSetting(
|
|
accountId,
|
|
LATEST_PUSH_REGISTRATION_AT_SERVER,
|
|
""
|
|
).blockingGet()?.value
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_latest_push_registration_at_server))
|
|
if (latestPushRegistrationAtServer.isNullOrEmpty()) {
|
|
addValue(context.resources.getString(R.string.nc_diagnose_latest_push_registration_at_server_fail))
|
|
} else {
|
|
addValue(DisplayUtils.unixTimeToHumanReadable(latestPushRegistrationAtServer.toLong()))
|
|
}
|
|
|
|
val latestPushRegistrationAtPushProxy = arbitraryStorageManager.getStorageSetting(
|
|
accountId,
|
|
LATEST_PUSH_REGISTRATION_AT_PUSH_PROXY,
|
|
""
|
|
).blockingGet()?.value
|
|
|
|
addKey(context.resources.getString(R.string.nc_diagnose_latest_push_registration_at_push_proxy))
|
|
if (latestPushRegistrationAtPushProxy.isNullOrEmpty()) {
|
|
addValue(context.resources.getString(R.string.nc_diagnose_latest_push_registration_at_push_proxy_fail))
|
|
} else {
|
|
addValue(DisplayUtils.unixTimeToHumanReadable(latestPushRegistrationAtPushProxy.toLong()))
|
|
}
|
|
}
|
|
|
|
private fun getDeviceName(): String =
|
|
if (MODEL.startsWith(MANUFACTURER, ignoreCase = true)) {
|
|
MODEL
|
|
} else {
|
|
"$MANUFACTURER $MODEL"
|
|
}
|
|
|
|
private fun translateBoolean(answer: Boolean?): String {
|
|
return when (answer) {
|
|
null -> context.resources.getString(R.string.nc_common_unknown)
|
|
true -> context.resources.getString(R.string.nc_yes)
|
|
else -> context.resources.getString(R.string.nc_no)
|
|
}
|
|
}
|
|
|
|
@Suppress("MagicNumber")
|
|
private fun createLayoutFromMarkdown() {
|
|
val standardMargin = 16
|
|
val halfMargin = 8
|
|
val standardPadding = 16
|
|
|
|
binding.diagnoseContentWrapper.removeAllViews()
|
|
|
|
markdownText.lines().forEach {
|
|
if (it.startsWith(MARKDOWN_HEADLINE)) {
|
|
val headline = TextView(context, null, 0)
|
|
headline.textSize = 2.0f
|
|
headline.setTextSize(
|
|
TypedValue.COMPLEX_UNIT_PX,
|
|
context.resources.getDimension(R.dimen.headline_text_size)
|
|
)
|
|
headline.setTypeface(null, Typeface.BOLD)
|
|
headline.text = it.removeRange(0, 4)
|
|
|
|
binding.diagnoseContentWrapper.addView(headline)
|
|
|
|
headline.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
|
setMargins(0, standardMargin, 0, standardMargin)
|
|
}
|
|
headline.setPadding(0, standardPadding, 0, standardPadding)
|
|
|
|
viewThemeUtils.platform.colorTextView(headline)
|
|
} else if (it.startsWith(MARKDOWN_BOLD)) {
|
|
val key = TextView(context, null, 0)
|
|
key.setTextColor(resources.getColor(R.color.high_emphasis_text, null))
|
|
key.setTypeface(null, Typeface.BOLD)
|
|
key.text = it.replace(MARKDOWN_BOLD, "")
|
|
|
|
binding.diagnoseContentWrapper.addView(key)
|
|
|
|
key.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
|
setMargins(0, 0, 0, halfMargin)
|
|
}
|
|
} else if (it.isNotEmpty()) {
|
|
val value = TextView(context, null, 0)
|
|
value.setTextColor(resources.getColor(R.color.high_emphasis_text, null))
|
|
value.text = it
|
|
|
|
binding.diagnoseContentWrapper.addView(value)
|
|
|
|
value.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
|
setMargins(0, 0, 0, standardMargin)
|
|
}
|
|
value.setPadding(0, 0, 0, standardPadding)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun addHeadline(text: String) {
|
|
markdownText.append("$MARKDOWN_HEADLINE $text")
|
|
markdownText.append("\n\n")
|
|
}
|
|
|
|
private fun addKey(text: String) {
|
|
markdownText.bold { append("$MARKDOWN_BOLD$text$MARKDOWN_BOLD") }
|
|
markdownText.append("\n\n")
|
|
}
|
|
|
|
private fun addValue(text: String) {
|
|
markdownText.append(text)
|
|
markdownText.append("\n\n")
|
|
}
|
|
|
|
companion object {
|
|
val TAG = DiagnoseActivity::class.java.simpleName
|
|
private const val MARKDOWN_HEADLINE = "###"
|
|
private const val MARKDOWN_BOLD = "**"
|
|
private const val PUSH_TOKEN_PREFIX_END: Int = 5
|
|
private const val ORIGINAL_NEXTCLOUD_TALK_APPLICATION_ID = "com.nextcloud.talk2"
|
|
}
|
|
}
|