Skip to content
Snippets Groups Projects
Commit 47ccb900 authored by Adrien Béraud's avatar Adrien Béraud Committed by Adrien Béraud
Browse files

logs: improve visibility and updates

Change-Id: I7f01f9cf24fb3b7d0c3763f4b5fc0e99ffafa508
parent f1ca3892
Branches
Tags
No related merge requests found
...@@ -16,9 +16,11 @@ ...@@ -16,9 +16,11 @@
*/ */
package cx.ring.client package cx.ring.client
import android.animation.ValueAnimator
import android.app.ActivityManager import android.app.ActivityManager
import android.app.ApplicationExitInfo import android.app.ApplicationExitInfo
import android.content.Intent import android.content.Intent
import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
...@@ -31,11 +33,12 @@ import android.view.ViewGroup ...@@ -31,11 +33,12 @@ import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import cx.ring.R import cx.ring.R
...@@ -91,8 +94,16 @@ class LogsActivity : AppCompatActivity() { ...@@ -91,8 +94,16 @@ class LogsActivity : AppCompatActivity() {
} }
binding.fab.setOnClickListener { if (disposable == null) startLogging() else stopLogging() } binding.fab.setOnClickListener { if (disposable == null) startLogging() else stopLogging() }
binding.logRecyclerView.adapter = LogAdapter() val highlightColor = getColor(R.color.colorSecondaryTranslucent)
binding.logRecyclerView.layoutManager = LinearLayoutManager(this) val bgColor = getColor(R.color.transparent)
binding.logRecyclerView.apply {
setHasFixedSize(true)
itemAnimator = FadeInItemAnimator(highlightColor, bgColor)
adapter = LogAdapter(highlightColor, bgColor).apply {
stateRestorationPolicy = RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
}
}
// Check for previous crash reasons, if any. // Check for previous crash reasons, if any.
if (savedInstanceState == null) if (savedInstanceState == null)
...@@ -260,7 +271,7 @@ class LogsActivity : AppCompatActivity() { ...@@ -260,7 +271,7 @@ class LogsActivity : AppCompatActivity() {
.subscribe({ messages: List<String> -> .subscribe({ messages: List<String> ->
val adapter = binding.logRecyclerView.adapter as LogAdapter val adapter = binding.logRecyclerView.adapter as LogAdapter
adapter.addLogs(messages.map { LogMessage(it) }) adapter.addLogs(messages.map { LogMessage(it) })
binding.logRecyclerView.smoothScrollToPosition(adapter.itemCount - 1) binding.logRecyclerView.scrollToPosition(adapter.itemCount - 1)
}) { e -> Log.w(TAG, "Error in logger", e) } }) { e -> Log.w(TAG, "Error in logger", e) }
.apply { disposable = this }) .apply { disposable = this })
setButtonState(true) setButtonState(true)
...@@ -291,27 +302,83 @@ class LogsActivity : AppCompatActivity() { ...@@ -291,27 +302,83 @@ class LogsActivity : AppCompatActivity() {
super.onDestroy() super.onDestroy()
} }
companion object { data class LogMessage(val message: String)
private val TAG = LogsActivity::class.simpleName!!
class FadeInItemAnimator(@ColorInt val highlightColor: Int, @ColorInt val bgColor: Int): SimpleItemAnimator() {
init {
supportsChangeAnimations = false
addDuration = 1000
} }
override fun animateAdd(holder: RecyclerView.ViewHolder): Boolean {
val logHolder = holder as LogAdapter.LogViewHolder
val pos = holder.adapterPosition
Log.w(TAG, "animateAdd ${pos}")
val view = holder.itemView
view.setBackgroundColor(highlightColor)
logHolder.animation.setDuration(addDuration)
logHolder.animation.addUpdateListener { animation ->
view.setBackgroundColor(animation.getAnimatedValue() as Int)
}
logHolder.animation.start()
return false
} }
data class LogMessage(val message: String) override fun animateRemove(holder: RecyclerView.ViewHolder): Boolean {
//dispatchRemoveFinished(holder)
return false;
}
override fun animateMove(holder: RecyclerView.ViewHolder, fromX: Int, fromY: Int, toX: Int, toY: Int): Boolean {
// Implement if you need move animations
//dispatchMoveFinished(holder)
return false;
}
override fun animateChange(oldHolder: RecyclerView.ViewHolder, newHolder: RecyclerView.ViewHolder,
fromLeft: Int, fromTop: Int, toLeft: Int, toTop: Int): Boolean {
// Implement if you need change animations
return false;
}
override fun runPendingAnimations() {
// No-op for this example
}
override fun endAnimation(item: RecyclerView.ViewHolder) {
Log.w(TAG, "endAnimation ${item.adapterPosition}")
val logHolder = item as LogAdapter.LogViewHolder
logHolder.animation.cancel()
item.itemView.setBackgroundColor(bgColor)
dispatchAnimationFinished(item)
}
override fun endAnimations() {
// No-op for this example
}
class LogAdapter : override fun isRunning(): Boolean {
return false; // Return true if animations are running
}
}
class LogAdapter(@ColorInt val highlightColor: Int, @ColorInt val bgColor: Int) :
RecyclerView.Adapter<LogAdapter.LogViewHolder>() { RecyclerView.Adapter<LogAdapter.LogViewHolder>() {
private val logList = mutableListOf<LogMessage>() private val logList = mutableListOf<LogMessage>()
fun getLogs(): String = logList.joinToString(separator = "\n") { it.message } fun getLogs(): String = logList.joinToString(separator = "\n") { it.message }
class LogViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { class LogViewHolder(itemView: View, @ColorInt highlightColor: Int, @ColorInt bgColor: Int) : RecyclerView.ViewHolder(itemView) {
val messageTextView: TextView = itemView.findViewById(R.id.log_item_text) val messageTextView: TextView = itemView.findViewById(R.id.log_item_text)
val animation: ValueAnimator = ValueAnimator.ofArgb(highlightColor, bgColor)
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LogViewHolder = override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LogViewHolder =
LogViewHolder( LogViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_log, parent, false) LayoutInflater.from(parent.context).inflate(R.layout.item_log, parent, false),
highlightColor,
bgColor
) )
override fun onBindViewHolder(holder: LogViewHolder, position: Int) { override fun onBindViewHolder(holder: LogViewHolder, position: Int) {
...@@ -330,3 +397,7 @@ class LogAdapter : ...@@ -330,3 +397,7 @@ class LogAdapter :
notifyDataSetChanged() notifyDataSetChanged()
} }
} }
companion object {
private val TAG = LogsActivity::class.simpleName!!
}
}
...@@ -30,6 +30,9 @@ import androidx.core.content.ContextCompat ...@@ -30,6 +30,9 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import cx.ring.R import cx.ring.R
import cx.ring.client.LogsActivity.FadeInItemAnimator
import cx.ring.client.LogsActivity.LogAdapter
import cx.ring.client.LogsActivity.LogMessage
import cx.ring.databinding.ActivityPushNotificationLogsBinding import cx.ring.databinding.ActivityPushNotificationLogsBinding
import cx.ring.utils.AndroidFileUtils import cx.ring.utils.AndroidFileUtils
import cx.ring.utils.ContentUri import cx.ring.utils.ContentUri
...@@ -52,8 +55,13 @@ class PushNotificationLogsActivity : AppCompatActivity() { ...@@ -52,8 +55,13 @@ class PushNotificationLogsActivity : AppCompatActivity() {
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
private var disposable: Disposable? = null private var disposable: Disposable? = null
private lateinit var logAdapter: LogAdapter private lateinit var logAdapter: LogAdapter
private lateinit var fileSaver: ActivityResultLauncher<String>
private lateinit var logFile: File private lateinit var logFile: File
private var fileSaver: ActivityResultLauncher<String> = registerForActivityResult(ActivityResultContracts
.CreateDocument("text/plain")) { result: Uri? ->
if (result != null) {
copyFileToUri(logFile, result)
}
}
@Inject @Inject
@Singleton @Singleton
...@@ -65,7 +73,10 @@ class PushNotificationLogsActivity : AppCompatActivity() { ...@@ -65,7 +73,10 @@ class PushNotificationLogsActivity : AppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
setSupportActionBar(binding.toolbar) setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
logAdapter = LogAdapter() val highlightColor = getColor(R.color.colorSecondaryTranslucent)
val bgColor = getColor(R.color.transparent)
logAdapter = LogAdapter(highlightColor, bgColor)
binding.logRecyclerView.itemAnimator = FadeInItemAnimator(highlightColor, bgColor)
binding.logRecyclerView.adapter = logAdapter binding.logRecyclerView.adapter = logAdapter
binding.logRecyclerView.layoutManager = LinearLayoutManager(this) binding.logRecyclerView.layoutManager = LinearLayoutManager(this)
val pushSummaryTextView = findViewById<TextView>(R.id.pushSummaryTextView) val pushSummaryTextView = findViewById<TextView>(R.id.pushSummaryTextView)
...@@ -81,15 +92,7 @@ class PushNotificationLogsActivity : AppCompatActivity() { ...@@ -81,15 +92,7 @@ class PushNotificationLogsActivity : AppCompatActivity() {
if (disposable == null) startLogging() else stopLogging() if (disposable == null) startLogging() else stopLogging()
} }
fileSaver = registerForActivityResult(ActivityResultContracts
.CreateDocument("text/plain")) { result: Uri? ->
if (result != null) {
copyFileToUri(logFile, result)
}
}
if (mHardwareService.loggingStatus) startLogging() if (mHardwareService.loggingStatus) startLogging()
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
......
...@@ -15,10 +15,8 @@ ...@@ -15,10 +15,8 @@
<com.google.android.material.appbar.MaterialToolbar <com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
style="@style/Widget.Material3.Toolbar.Surface"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:background="@color/background"
tools:menu="@menu/logs_menu" tools:menu="@menu/logs_menu"
tools:title="Logs" /> tools:title="Logs" />
...@@ -34,14 +32,19 @@ ...@@ -34,14 +32,19 @@
android:id="@+id/log_recycler_view" android:id="@+id/log_recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1"
android:orientation="vertical"
app:stackFromEnd="true"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_log"/>
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal" android:layout_gravity="bottom|center_horizontal"
android:layout_margin="@dimen/fab_margin" android:layout_marginTop="8dp"
android:layout_marginBottom="@dimen/fab_margin"
android:gravity="center" android:gravity="center"
android:text="@string/pref_logs_start" android:text="@string/pref_logs_start"
app:icon="@drawable/baseline_article_24" /> app:icon="@drawable/baseline_article_24" />
......
...@@ -13,7 +13,6 @@ GNU General Public License for more details. ...@@ -13,7 +13,6 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/log_item_text" android:id="@+id/log_item_text"
...@@ -22,6 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. ...@@ -22,6 +21,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:breakStrategy="simple" android:breakStrategy="simple"
android:hyphenationFrequency="none" android:hyphenationFrequency="none"
android:paddingHorizontal="@dimen/padding_medium" android:paddingHorizontal="@dimen/padding_medium"
android:paddingTop="@dimen/padding_small" android:paddingVertical="@dimen/padding_xsmall"
android:textIsSelectable="true" android:textIsSelectable="true"
tools:text="@tools:sample/lorem/random" /> tools:text="@tools:sample/first_names"/>
\ No newline at end of file \ No newline at end of file
...@@ -9,6 +9,9 @@ ...@@ -9,6 +9,9 @@
<color name="colorPrimary">@color/white</color> <color name="colorPrimary">@color/white</color>
<color name="colorPrimaryTranslucent">#C0FFFFFF</color> <color name="colorPrimaryTranslucent">#C0FFFFFF</color>
<color name="colorOnPrimary">@color/color_primary_dark</color> <color name="colorOnPrimary">@color/color_primary_dark</color>
<color name="colorSecondaryTranslucent">@color/color_primary_dark_translucent</color>
<color name="transparent">#00000000</color>
<!-- Text color --> <!-- Text color -->
<color name="colorOnSurface">@color/abc_primary_text_material_dark</color> <color name="colorOnSurface">@color/abc_primary_text_material_dark</color>
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
<color name="colorPrimary">@color/color_primary_dark</color> <color name="colorPrimary">@color/color_primary_dark</color>
<color name="colorPrimaryTranslucent">@color/color_primary_dark_translucent</color> <color name="colorPrimaryTranslucent">@color/color_primary_dark_translucent</color>
<color name="colorOnPrimary">@color/white</color> <color name="colorOnPrimary">@color/white</color>
<color name="colorSecondaryTranslucent">@color/color_primary_light_translucent</color>
<color name="colorSecondary">@color/color_primary_light</color> <color name="colorSecondary">@color/color_primary_light</color>
<color name="colorSecondaryContainer">@color/color_primary_light_container</color> <color name="colorSecondaryContainer">@color/color_primary_light_container</color>
...@@ -32,7 +33,7 @@ ...@@ -32,7 +33,7 @@
<color name="darker_gray">#c4c4c4</color> <color name="darker_gray">#c4c4c4</color>
<color name="lighter_gray">#d3d3d3</color> <color name="lighter_gray">#d3d3d3</color>
<color name="light">#eee</color> <color name="light">#eee</color>
<color name="transparent">#00000000</color> <color name="transparent">#00FFFFFF</color>
<color name="white">#FFF</color> <color name="white">#FFF</color>
<color name="black">#000000</color> <color name="black">#000000</color>
......
...@@ -166,7 +166,6 @@ abstract class HardwareService( ...@@ -166,7 +166,6 @@ abstract class HardwareService(
var unknownPriorityPushCount = 0 var unknownPriorityPushCount = 0
var startTime: String? = null var startTime: String? = null
@get:Synchronized @get:Synchronized
val isLogging: Boolean val isLogging: Boolean
get() = logs != null get() = logs != null
...@@ -186,6 +185,7 @@ abstract class HardwareService( ...@@ -186,6 +185,7 @@ abstract class HardwareService(
} }
} }
.buffer(500, TimeUnit.MILLISECONDS) .buffer(500, TimeUnit.MILLISECONDS)
.filter { it.isNotEmpty() }
.replay() .replay()
.autoConnect() .autoConnect()
.apply { logs = this } .apply { logs = this }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment