diff --git a/OsmAnd-telegram/AndroidManifest.xml b/OsmAnd-telegram/AndroidManifest.xml
index 7b2a96c236..2146f9b89a 100644
--- a/OsmAnd-telegram/AndroidManifest.xml
+++ b/OsmAnd-telegram/AndroidManifest.xml
@@ -20,7 +20,7 @@
android:screenOrientation="unspecified"
android:supportsRtl="true"
android:theme="@style/AppTheme">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml b/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml
index 928ad6f319..1c5738a313 100644
--- a/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml
+++ b/OsmAnd-telegram/res/layout/fragement_settings_dialog.xml
@@ -447,6 +447,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OsmAnd-telegram/res/layout/item_description_long.xml b/OsmAnd-telegram/res/layout/item_description_long.xml
new file mode 100644
index 0000000000..4dbc4d7165
--- /dev/null
+++ b/OsmAnd-telegram/res/layout/item_description_long.xml
@@ -0,0 +1,18 @@
+
+
diff --git a/OsmAnd-telegram/res/values/dimens.xml b/OsmAnd-telegram/res/values/dimens.xml
index 7f4942dc3e..c7e0bd6b9c 100644
--- a/OsmAnd-telegram/res/values/dimens.xml
+++ b/OsmAnd-telegram/res/values/dimens.xml
@@ -27,7 +27,7 @@
89dp
48dp
- 42dp
+ 44dp
56dp
48dp
diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml
index 2b5eca334e..8bccf957ce 100644
--- a/OsmAnd-telegram/res/values/strings.xml
+++ b/OsmAnd-telegram/res/values/strings.xml
@@ -1,5 +1,9 @@
+ Send report
+ Check and share detailed logs of the app
+ Logcat buffer
+ Export
ERR
Last update from Telegram: %1$s
Last response: %1$s
diff --git a/OsmAnd-telegram/src/net/osmand/TrackerLogcatActivity.kt b/OsmAnd-telegram/src/net/osmand/TrackerLogcatActivity.kt
new file mode 100644
index 0000000000..5ed551636a
--- /dev/null
+++ b/OsmAnd-telegram/src/net/osmand/TrackerLogcatActivity.kt
@@ -0,0 +1,270 @@
+package net.osmand
+
+import android.os.AsyncTask
+import android.os.Bundle
+import android.view.*
+import android.widget.ProgressBar
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.appcompat.widget.Toolbar
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import net.osmand.telegram.R
+import net.osmand.telegram.TelegramApplication
+import java.io.*
+import java.lang.ref.WeakReference
+import java.util.*
+
+private const val LOGCAT_PATH = "logcat.log"
+private const val MAX_BUFFER_LOG = 10000
+private const val SHARE_ID = 0
+private const val LEVEL_ID = 1
+private val log = PlatformUtil.getLog(TrackerLogcatActivity::class.java)
+
+class TrackerLogcatActivity : AppCompatActivity() {
+ private var logcatAsyncTask: LogcatAsyncTask? = null
+ private val logs: MutableList = ArrayList()
+ private var adapter: LogcatAdapter? = null
+ private val LEVELS = arrayOf("D", "I", "W", "E")
+ private var filterLevel = 1
+ private var recyclerView: RecyclerView? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ val app: TelegramApplication = getApplication() as TelegramApplication
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_tracker_logcat)
+
+ log.debug("text to test")
+
+ val toolbar = findViewById(R.id.toolbar).apply {
+ navigationIcon = app.uiUtils.getThemedIcon(R.drawable.ic_arrow_back)
+ setNavigationOnClickListener { onBackPressed() }
+ }
+ setSupportActionBar(toolbar)
+ setupIntermediateProgressBar()
+
+ adapter = LogcatAdapter()
+ recyclerView = findViewById(R.id.recycler_view) as RecyclerView?
+ recyclerView!!.layoutManager = LinearLayoutManager(this)
+ recyclerView!!.adapter = adapter
+ }
+
+ protected fun setupIntermediateProgressBar() {
+ val progressBar = ProgressBar(this)
+ progressBar.visibility = View.GONE
+ progressBar.isIndeterminate = true
+ val supportActionBar = supportActionBar
+ if (supportActionBar != null) {
+ supportActionBar.setDisplayShowCustomEnabled(true)
+ supportActionBar.customView = progressBar
+ setSupportProgressBarIndeterminateVisibility(false)
+ }
+ }
+
+ override fun setSupportProgressBarIndeterminateVisibility(visible: Boolean) {
+ val supportActionBar = supportActionBar
+ if (supportActionBar != null) {
+ supportActionBar.customView.visibility = if (visible) View.VISIBLE else View.GONE
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ startLogcatAsyncTask()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ stopLogcatAsyncTask()
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ val app: TelegramApplication = applicationContext as TelegramApplication
+ val share: MenuItem = menu.add(0, SHARE_ID, 0, R.string.shared_string_export)
+ share.icon = app.uiUtils.getThemedIcon(R.drawable.ic_action_share)
+ share.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
+ val level = menu.add(0, LEVEL_ID, 0, "")
+ level.title = getFilterLevel()
+ level.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ private fun getFilterLevel(): String {
+ return "*:" + LEVELS[filterLevel]
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ val itemId = item.itemId
+ when (itemId) {
+ android.R.id.home -> {
+ finish()
+ return true
+ }
+ LEVEL_ID -> {
+ filterLevel++
+ if (filterLevel >= LEVELS.size) {
+ filterLevel = 0
+ }
+ item.title = getFilterLevel()
+ stopLogcatAsyncTask()
+ logs.clear()
+ adapter!!.notifyDataSetChanged()
+ startLogcatAsyncTask()
+ return true
+ }
+ SHARE_ID -> {
+ startSaveLogsAsyncTask()
+ return true
+ }
+ }
+ return false
+ }
+
+ private fun startSaveLogsAsyncTask() {
+ val saveLogsAsyncTask = SaveLogsAsyncTask(this, logs)
+ saveLogsAsyncTask.execute()
+ }
+
+ private fun startLogcatAsyncTask() {
+ logcatAsyncTask = LogcatAsyncTask(this, getFilterLevel())
+ logcatAsyncTask!!.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
+ }
+
+ private fun stopLogcatAsyncTask() {
+ if (logcatAsyncTask != null && logcatAsyncTask!!.status == AsyncTask.Status.RUNNING) {
+ logcatAsyncTask!!.cancel(false)
+ logcatAsyncTask!!.stopLogging()
+ }
+ }
+
+ private inner class LogcatAdapter : RecyclerView.Adapter() {
+ override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ val inflater = LayoutInflater.from(viewGroup.context)
+ val itemView = inflater.inflate(R.layout.item_description_long, viewGroup, false) as TextView
+ itemView.gravity = Gravity.CENTER_VERTICAL
+ return LogViewHolder(itemView)
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ if (holder is LogViewHolder) {
+ val log = getLog(position)
+ holder.logTextView.text = log
+ }
+ }
+
+ override fun getItemCount(): Int {
+ return logs.size
+ }
+
+ private fun getLog(position: Int): String {
+ return logs[position]
+ }
+
+ private inner class LogViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val logTextView: TextView = itemView.findViewById(R.id.description)
+ }
+ }
+
+ class SaveLogsAsyncTask internal constructor(logcatActivity: TrackerLogcatActivity, logs: Collection) : AsyncTask() {
+ private val logcatActivity: WeakReference
+ private val logs: Collection
+
+ override fun onPreExecute() {
+ val activity = logcatActivity.get()
+ activity?.setSupportProgressBarIndeterminateVisibility(true)
+ }
+
+ override fun doInBackground(vararg voids: Void?): File {
+ val app: TelegramApplication = logcatActivity.get()?.applicationContext as TelegramApplication
+ val file = File(app.getExternalFilesDir(null), LOGCAT_PATH)
+ try {
+ if (file.exists()) {
+ file.delete()
+ }
+ val stringBuilder = StringBuilder()
+ for (log in logs) {
+ stringBuilder.append(log)
+ stringBuilder.append("\n")
+ }
+ if (file.parentFile.canWrite()) {
+ val writer = BufferedWriter(FileWriter(file, true))
+ writer.write(stringBuilder.toString())
+ writer.close()
+ }
+ } catch (e: Exception) {
+ log.error(e)
+ }
+ return file
+ }
+
+ override fun onPostExecute(file: File?) {
+ val activity = logcatActivity.get()
+ if (activity != null && file != null) {
+ val app: TelegramApplication = activity.applicationContext as TelegramApplication
+ activity.setSupportProgressBarIndeterminateVisibility(false)
+ app.sendCrashLog(file)
+ }
+ }
+
+ init {
+ this.logcatActivity = WeakReference(logcatActivity)
+ this.logs = logs
+ }
+ }
+
+ class LogcatAsyncTask internal constructor(logcatActivity: TrackerLogcatActivity?, filterLevel: String) : AsyncTask() {
+ private var processLogcat: Process? = null
+ private val logcatActivity: WeakReference
+ private val filterLevel: String
+
+ override fun doInBackground(vararg voids: Void?): Void? {
+ try {
+ val filter = android.os.Process.myPid().toString()
+ val command = arrayOf("logcat", filterLevel, "--pid=$filter", "-T", MAX_BUFFER_LOG.toString())
+ processLogcat = Runtime.getRuntime().exec(command)
+ val bufferedReader = BufferedReader(InputStreamReader(processLogcat?.inputStream))
+ var line: String?
+ while (bufferedReader.readLine().also { line = it } != null && logcatActivity.get() != null) {
+ if (isCancelled) {
+ break
+ }
+ publishProgress(line)
+ }
+ stopLogging()
+ } catch (e: IOException) { // ignore
+ } catch (e: Exception) {
+ log.error(e)
+ }
+ return null
+ }
+
+ override fun onProgressUpdate(vararg values: String?) {
+ if (values.size > 0 && !isCancelled) {
+ val activity = logcatActivity.get()
+ if (activity != null) {
+ val autoscroll = !activity.recyclerView!!.canScrollVertically(1)
+ for (s in values) {
+ if (s != null) {
+ activity.logs.add(s)
+ }
+ }
+ activity.adapter!!.notifyDataSetChanged()
+ if (autoscroll) {
+ activity.recyclerView!!.scrollToPosition(activity.logs.size - 1)
+ }
+ }
+ }
+ }
+
+ fun stopLogging() {
+ if (processLogcat != null) {
+ processLogcat!!.destroy()
+ }
+ }
+
+ init {
+ this.logcatActivity = WeakReference(logcatActivity)
+ this.filterLevel = filterLevel
+ }
+ }
+}
diff --git a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt
index ee6bbb29d4..97652148b2 100644
--- a/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt
+++ b/OsmAnd-telegram/src/net/osmand/telegram/TelegramApplication.kt
@@ -3,16 +3,20 @@ package net.osmand.telegram
import android.app.Application
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.os.Build
import android.os.Handler
+import net.osmand.PlatformUtil
+import net.osmand.TrackerLogcatActivity
import net.osmand.telegram.helpers.*
import net.osmand.telegram.helpers.OsmandAidlHelper.OsmandHelperListener
import net.osmand.telegram.helpers.OsmandAidlHelper.UpdatesListener
import net.osmand.telegram.notifications.NotificationHelper
import net.osmand.telegram.utils.AndroidUtils
import net.osmand.telegram.utils.UiUtils
+import java.io.File
class TelegramApplication : Application() {
@@ -200,4 +204,33 @@ class TelegramApplication : Application() {
fun runInUIThread(action: (() -> Unit), delay: Long) {
uiHandler.postDelayed(action, delay)
}
+
+ fun sendCrashLog(file: File) {
+ val intent = Intent(Intent.ACTION_SEND)
+ intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("crash@osmand.net"))
+ intent.putExtra(Intent.EXTRA_STREAM, AndroidUtils.getUriForFile(this, file))
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ intent.type = "vnd.android.cursor.dir/email"
+ intent.putExtra(Intent.EXTRA_SUBJECT, "OsmAnd bug")
+ val text = StringBuilder()
+ text.append("\nDevice : ").append(Build.DEVICE)
+ text.append("\nBrand : ").append(Build.BRAND)
+ text.append("\nModel : ").append(Build.MODEL)
+ text.append("\nProduct : ").append(Build.PRODUCT)
+ text.append("\nBuild : ").append(Build.DISPLAY)
+ text.append("\nVersion : ").append(Build.VERSION.RELEASE)
+ text.append("\nApp : ").append(getString(R.string.app_name_short))
+ try {
+ val info = packageManager.getPackageInfo(packageName, 0)
+ if (info != null) {
+ text.append("\nApk Version : ").append(info.versionName).append(" ").append(info.versionCode)
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ PlatformUtil.getLog(TrackerLogcatActivity::class.java).error("", e)
+ }
+ intent.putExtra(Intent.EXTRA_TEXT, text.toString())
+ val chooserIntent = Intent.createChooser(intent, getString(R.string.send_report))
+ chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ startActivity(chooserIntent)
+ }
}
diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt
index a5bf5993e9..2e8793bcb3 100644
--- a/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt
+++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/SettingsDialogFragment.kt
@@ -14,6 +14,7 @@ import android.widget.*
import androidx.appcompat.widget.ListPopupWindow
import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat
+import net.osmand.TrackerLogcatActivity
import net.osmand.telegram.R
import net.osmand.telegram.TelegramSettings
import net.osmand.telegram.TelegramSettings.ListPreference
@@ -213,6 +214,12 @@ class SettingsDialogFragment : BaseDialogFragment() {
DisconnectTelegramBottomSheet.showInstance(childFragmentManager)
}
+ mainView.findViewById(R.id.logcat_row).setOnClickListener {
+ val intent = Intent(activity, TrackerLogcatActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ app.startActivity(intent)
+ }
+
return mainView
}