Added magisk log screen
This commit is contained in:
parent
94f0c61619
commit
67c50d7504
@ -164,4 +164,9 @@ open class MainActivity : CompatActivity<MainViewModel, ActivityMainMd2Binding>(
|
||||
.start()
|
||||
}
|
||||
|
||||
fun invalidateToolbar() {
|
||||
//binding.mainToolbar.startAnimations()
|
||||
binding.mainToolbar.invalidate()
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,17 @@
|
||||
package com.topjohnwu.magisk.redesign.log
|
||||
|
||||
import android.graphics.Insets
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.databinding.FragmentLogMd2Binding
|
||||
import com.topjohnwu.magisk.redesign.MainActivity
|
||||
import com.topjohnwu.magisk.redesign.compat.CompatFragment
|
||||
import com.topjohnwu.magisk.redesign.hide.MotionRevealHelper
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
class LogFragment : CompatFragment<LogViewModel, FragmentLogMd2Binding>() {
|
||||
@ -11,14 +19,58 @@ class LogFragment : CompatFragment<LogViewModel, FragmentLogMd2Binding>() {
|
||||
override val layoutRes = R.layout.fragment_log_md2
|
||||
override val viewModel by viewModel<LogViewModel>()
|
||||
|
||||
private var actionSave: MenuItem? = null
|
||||
private var isMagiskLogVisible
|
||||
get() = binding.logFilter.isVisible
|
||||
set(value) {
|
||||
MotionRevealHelper.withViews(binding.logFilter, binding.logFilterToggle, value)
|
||||
actionSave?.isVisible = value
|
||||
(activity as MainActivity).invalidateToolbar()
|
||||
}
|
||||
|
||||
override fun consumeSystemWindowInsets(insets: Insets) = insets
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
setHasOptionsMenu(true)
|
||||
activity.title = resources.getString(R.string.section_log)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.logFilterToggle.setOnClickListener {
|
||||
isMagiskLogVisible = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.menu_log_md2, menu)
|
||||
actionSave = menu.findItem(R.id.action_save)?.also {
|
||||
it.isVisible = isMagiskLogVisible
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_save -> viewModel.saveMagiskLog()
|
||||
R.id.action_clear ->
|
||||
if (isMagiskLogVisible) viewModel.clearMagiskLog()
|
||||
else viewModel.clearLog()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
|
||||
override fun onPreBind(binding: FragmentLogMd2Binding) = Unit
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
if (binding.logFilter.isVisible) {
|
||||
isMagiskLogVisible = false
|
||||
return true
|
||||
}
|
||||
return super.onBackPressed()
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +1,52 @@
|
||||
package com.topjohnwu.magisk.redesign.log
|
||||
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.Config
|
||||
import com.topjohnwu.magisk.Const
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.data.repository.LogRepository
|
||||
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
||||
import com.topjohnwu.magisk.extensions.subscribeK
|
||||
import com.topjohnwu.magisk.model.binding.BindingAdapter
|
||||
import com.topjohnwu.magisk.model.entity.recycler.ConsoleRvItem
|
||||
import com.topjohnwu.magisk.model.entity.recycler.LogItem
|
||||
import com.topjohnwu.magisk.model.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
|
||||
import com.topjohnwu.magisk.redesign.home.itemBindingOf
|
||||
import com.topjohnwu.magisk.redesign.superuser.diffListOf
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class LogViewModel(
|
||||
private val repo: LogRepository
|
||||
) : CompatViewModel() {
|
||||
|
||||
// --- main view
|
||||
|
||||
val items = diffListOf<LogItem>()
|
||||
val itemBinding = itemBindingOf<LogItem> {
|
||||
it.bindExtra(BR.viewModel, this)
|
||||
}
|
||||
|
||||
override fun refresh() = repo.fetchLogsNowrap()
|
||||
// --- console
|
||||
|
||||
val consoleAdapter = BindingAdapter()
|
||||
val itemsConsole = diffListOf<ComparableRvItem<*>>()
|
||||
val itemConsoleBinding = itemBindingOf<ComparableRvItem<*>> {}
|
||||
|
||||
override fun refresh(): Disposable {
|
||||
val logs = repo.fetchLogsNowrap()
|
||||
.map { it.map { LogItem(it) } }
|
||||
.observeOn(Schedulers.computation())
|
||||
.map { it to items.calculateDiff(it) }
|
||||
.subscribeK {
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess {
|
||||
items.firstOrNull()?.isTop = false
|
||||
items.lastOrNull()?.isBottom = false
|
||||
|
||||
@ -29,5 +55,53 @@ class LogViewModel(
|
||||
items.firstOrNull()?.isTop = true
|
||||
items.lastOrNull()?.isBottom = true
|
||||
}
|
||||
.ignoreElement()
|
||||
|
||||
val console = repo.fetchMagiskLogs()
|
||||
.map { it.map { ConsoleRvItem(it) } }
|
||||
.observeOn(Schedulers.computation())
|
||||
.map { it to itemsConsole.calculateDiff(it) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess { itemsConsole.update(it.first, it.second) }
|
||||
.ignoreElement()
|
||||
|
||||
return Completable.merge(listOf(logs, console)).subscribeK()
|
||||
}
|
||||
|
||||
fun saveMagiskLog() {
|
||||
val now = Calendar.getInstance()
|
||||
val filename = "magisk_log_%04d%02d%02d_%02d%02d%02d.log".format(
|
||||
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
||||
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||
now.get(Calendar.MINUTE), now.get(Calendar.SECOND)
|
||||
)
|
||||
|
||||
val logFile = File(Config.downloadDirectory, filename)
|
||||
runCatching {
|
||||
logFile.createNewFile()
|
||||
}.onFailure {
|
||||
Timber.e(it)
|
||||
return
|
||||
}
|
||||
|
||||
Shell.su("cat ${Const.MAGISK_LOG} > $logFile").submit {
|
||||
SnackbarEvent(logFile.path).publish()
|
||||
}
|
||||
}
|
||||
|
||||
fun clearMagiskLog() = repo.clearMagiskLogs()
|
||||
.ignoreElement()
|
||||
.subscribeK {
|
||||
SnackbarEvent(R.string.logs_cleared).publish()
|
||||
requestRefresh()
|
||||
}
|
||||
.add()
|
||||
|
||||
fun clearLog() = repo.clearLogs()
|
||||
.subscribeK {
|
||||
SnackbarEvent(R.string.logs_cleared).publish()
|
||||
requestRefresh()
|
||||
}
|
||||
.add()
|
||||
|
||||
}
|
10
app/src/main/res/drawable/ic_folder_list.xml
Normal file
10
app/src/main/res/drawable/ic_folder_list.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M20,18H4V8H20M20,6H12L10,4H4A2,2 0 0,0 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8A2,2 0 0,0 20,6M15,16H6V14H15V16M18,12H6V10H18V12Z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/ic_save_md2.xml
Normal file
10
app/src/main/res/drawable/ic_save_md2.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?colorOnSurface"
|
||||
android:pathData="M17 3H5C3.89 3 3 3.9 3 5V19C3 20.1 3.89 21 5 21H19C20.1 21 21 20.1 21 19V7L17 3M19 19H5V5H16.17L19 7.83V19M12 12C10.34 12 9 13.34 9 15S10.34 18 12 18 15 16.66 15 15 13.66 12 12 12M6 6H15V10H6V6Z" />
|
||||
</vector>
|
@ -11,6 +11,10 @@
|
||||
|
||||
</data>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
itemBinding="@{viewModel.itemBinding}"
|
||||
items="@{viewModel.items}"
|
||||
@ -24,6 +28,39 @@
|
||||
android:paddingBottom="@{viewModel.insets.bottom}"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_log_access_md2"
|
||||
tools:paddingTop="40dp" />
|
||||
tools:paddingTop="24dp" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/log_filter_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginStart="@dimen/l1"
|
||||
android:layout_marginEnd="@dimen/l1"
|
||||
android:layout_marginBottom="@{viewModel.insets.bottom + (int) @dimen/l1}"
|
||||
app:backgroundTint="?colorSurfaceSurfaceVariant"
|
||||
app:srcCompat="@drawable/ic_folder_list"
|
||||
app:tint="?colorPrimary"
|
||||
tools:layout_marginBottom="64dp" />
|
||||
|
||||
<com.google.android.material.circularreveal.cardview.CircularRevealCardView
|
||||
android:id="@+id/log_filter"
|
||||
style="?styleCardVariant"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="bottom"
|
||||
android:visibility="invisible"
|
||||
app:cardCornerRadius="0dp">
|
||||
|
||||
<include
|
||||
android:id="@+id/log_filter_include"
|
||||
layout="@layout/include_log_magisk"
|
||||
viewModel="@{viewModel}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</com.google.android.material.circularreveal.cardview.CircularRevealCardView>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</layout>
|
34
app/src/main/res/layout/include_log_magisk.xml
Normal file
34
app/src/main/res/layout/include_log_magisk.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="com.topjohnwu.magisk.redesign.log.LogViewModel" />
|
||||
|
||||
</data>
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
adapter="@{viewModel.consoleAdapter}"
|
||||
itemBinding="@{viewModel.itemConsoleBinding}"
|
||||
items="@{viewModel.itemsConsole}"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="@{viewModel.insets.top + (int) @dimen/internal_action_bar_size}"
|
||||
android:paddingBottom="@{viewModel.insets.bottom}"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_console"
|
||||
tools:paddingTop="24dp" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
</layout>
|
17
app/src/main/res/menu/menu_log_md2.xml
Normal file
17
app/src/main/res/menu/menu_log_md2.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_save"
|
||||
android:icon="@drawable/ic_save_md2"
|
||||
android:title="@string/menuSaveLog"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_clear"
|
||||
android:icon="@drawable/ic_delete_md2"
|
||||
android:title="@string/menuClearLog"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
Loading…
Reference in New Issue
Block a user