mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
Add exit reason screen
This commit is contained in:
parent
7edb636c67
commit
c3b9efa37a
13 changed files with 338 additions and 5 deletions
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package io.timelimit.android.integration.platform
|
||||
|
||||
import android.app.ApplicationExitInfo
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
|
||||
data class ExitLogItem (
|
||||
val timestamp: Long,
|
||||
val reason: ExitReason,
|
||||
val description: String?
|
||||
) {
|
||||
companion object {
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
fun fromApplicationExitInfo(item: ApplicationExitInfo): ExitLogItem = ExitLogItem(
|
||||
timestamp = item.timestamp,
|
||||
reason = ExitReasonConverter.fromApplicationExitInfo(item),
|
||||
description = item.description
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class ExitReason {
|
||||
AppNotResponding,
|
||||
Crash,
|
||||
CrashNative,
|
||||
DependencyDied,
|
||||
TooMuchRessourceUsage,
|
||||
Self,
|
||||
InitFailure,
|
||||
LowMemory,
|
||||
SystemOther,
|
||||
PermissionChange,
|
||||
Signaled,
|
||||
SystemUnknown,
|
||||
UserRequest,
|
||||
UserStopped,
|
||||
Unknown
|
||||
}
|
||||
|
||||
object ExitReasonConverter {
|
||||
fun fromApplicationExitInfo(input: ApplicationExitInfo): ExitReason = when (input.reason) {
|
||||
ApplicationExitInfo.REASON_ANR -> ExitReason.AppNotResponding
|
||||
ApplicationExitInfo.REASON_CRASH -> ExitReason.Crash
|
||||
ApplicationExitInfo.REASON_CRASH_NATIVE -> ExitReason.CrashNative
|
||||
ApplicationExitInfo.REASON_DEPENDENCY_DIED -> ExitReason.DependencyDied
|
||||
ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE -> ExitReason.TooMuchRessourceUsage
|
||||
ApplicationExitInfo.REASON_EXIT_SELF -> ExitReason.Self
|
||||
ApplicationExitInfo.REASON_INITIALIZATION_FAILURE -> ExitReason.InitFailure
|
||||
ApplicationExitInfo.REASON_LOW_MEMORY -> ExitReason.LowMemory
|
||||
ApplicationExitInfo.REASON_OTHER -> ExitReason.SystemOther
|
||||
ApplicationExitInfo.REASON_PERMISSION_CHANGE -> ExitReason.PermissionChange
|
||||
ApplicationExitInfo.REASON_SIGNALED -> ExitReason.Signaled
|
||||
ApplicationExitInfo.REASON_UNKNOWN -> ExitReason.SystemUnknown
|
||||
ApplicationExitInfo.REASON_USER_REQUESTED -> ExitReason.UserRequest
|
||||
ApplicationExitInfo.REASON_USER_STOPPED -> ExitReason.UserStopped
|
||||
else -> ExitReason.Unknown
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -86,6 +86,8 @@ abstract class PlatformIntegration(
|
|||
confirmationLevel: SystemPermissionConfirmationLevel = SystemPermissionConfirmationLevel.None
|
||||
): Boolean
|
||||
|
||||
abstract fun getExitLog(): List<ExitLogItem>
|
||||
|
||||
var installedAppsChangeListener: Runnable? = null
|
||||
var systemClockChangeListener: Runnable? = null
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -793,4 +793,11 @@ class AndroidIntegration(context: Context): PlatformIntegration(maximumProtectio
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getExitLog(): List<ExitLogItem> {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
activityManager.getHistoricalProcessExitReasons(context.packageName, 0, 0)
|
||||
.map { ExitLogItem.fromApplicationExitInfo(it) }
|
||||
} else emptyList()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -185,4 +185,6 @@ class DummyIntegration(
|
|||
permission: SystemPermission,
|
||||
confirmationLevel: SystemPermissionConfirmationLevel
|
||||
): Boolean = false
|
||||
|
||||
override fun getExitLog(): List<ExitLogItem> = emptyList()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -100,6 +100,13 @@ class DiagnoseMainFragment : Fragment(), FragmentWithCustomTitle {
|
|||
}
|
||||
}
|
||||
|
||||
binding.diagnoseExitReasonsButton.setOnClickListener {
|
||||
navigation.safeNavigate(
|
||||
DiagnoseMainFragmentDirections.actionDiagnoseMainFragmentToDiagnoseExitReasonFragment(),
|
||||
R.id.diagnoseMainFragment
|
||||
)
|
||||
}
|
||||
|
||||
AuthenticationFab.manageAuthenticationFab(
|
||||
fab = binding.fab,
|
||||
shouldHighlight = auth.shouldHighlightAuthenticationButton,
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package io.timelimit.android.ui.diagnose.exitreason
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.timelimit.android.databinding.DiagnoseExitReasonItemBinding
|
||||
import io.timelimit.android.integration.platform.ExitLogItem
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
class DiagnoseExitReasonAdapter: RecyclerView.Adapter<DiagnoseExitReasonHolder>() {
|
||||
var content: List<ExitLogItem> by Delegates.observable(emptyList()) { _, _, _ -> notifyDataSetChanged() }
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DiagnoseExitReasonHolder = DiagnoseExitReasonHolder(
|
||||
DiagnoseExitReasonItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: DiagnoseExitReasonHolder, position: Int) {
|
||||
val item = content[position]
|
||||
val view = holder.binding
|
||||
|
||||
view.timeString = DateFormat.getDateTimeInstance().format(Date(item.timestamp))
|
||||
view.summaryString = item.reason.toString()
|
||||
view.detailString = item.description
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = content.size
|
||||
}
|
||||
|
||||
class DiagnoseExitReasonHolder(val binding: DiagnoseExitReasonItemBinding): RecyclerView.ViewHolder(binding.root)
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package io.timelimit.android.ui.diagnose.exitreason
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import io.timelimit.android.R
|
||||
import io.timelimit.android.databinding.DiagnoseExitReasonFragmentBinding
|
||||
import io.timelimit.android.livedata.liveDataFromNullableValue
|
||||
import io.timelimit.android.logic.DefaultAppLogic
|
||||
import io.timelimit.android.ui.main.FragmentWithCustomTitle
|
||||
|
||||
class DiagnoseExitReasonFragment: Fragment(), FragmentWithCustomTitle {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val binding = DiagnoseExitReasonFragmentBinding.inflate(inflater, container, false)
|
||||
val data = DefaultAppLogic.with(requireContext()).platformIntegration.getExitLog()
|
||||
val recycler = binding.recycler
|
||||
val adapter = DiagnoseExitReasonAdapter()
|
||||
|
||||
adapter.content = data
|
||||
recycler.layoutManager = LinearLayoutManager(requireContext())
|
||||
recycler.adapter = adapter
|
||||
binding.isEmpty = data.isEmpty()
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun getCustomTitle(): LiveData<String?> = liveDataFromNullableValue("${getString(R.string.diagnose_er_title)} < ${getString(
|
||||
R.string.about_diagnose_title)} < ${getString(R.string.main_tab_overview)}")
|
||||
}
|
48
app/src/main/res/layout/diagnose_exit_reason_fragment.xml
Normal file
48
app/src/main/res/layout/diagnose_exit_reason_fragment.xml
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation version 3 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="isEmpty"
|
||||
type="boolean" />
|
||||
|
||||
<import type="android.view.View" />
|
||||
</data>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView
|
||||
android:visibility="@{isEmpty ? View.VISIBLE : View.GONE}"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:padding="16dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/diagnose_er_empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
</layout>
|
76
app/src/main/res/layout/diagnose_exit_reason_item.xml
Normal file
76
app/src/main/res/layout/diagnose_exit_reason_item.xml
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation version 3 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<variable
|
||||
name="timeString"
|
||||
type="String" />
|
||||
|
||||
<variable
|
||||
name="summaryString"
|
||||
type="String" />
|
||||
|
||||
<variable
|
||||
name="detailString"
|
||||
type="String" />
|
||||
|
||||
<import type="android.text.TextUtils" />
|
||||
<import type="android.view.View" />
|
||||
</data>
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
app:cardUseCompatPadding="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<LinearLayout
|
||||
android:padding="8dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceLarge"
|
||||
tools:text="vom Benutzer beendet"
|
||||
android:text="@{summaryString}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
tools:text="er will keine Begrenzungen"
|
||||
android:text="@{detailString}"
|
||||
android:visibility="@{TextUtils.isEmpty(detailString) ? View.GONE : View.VISIBLE}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
tools:text="vor fünf Minuten"
|
||||
android:text="@{timeString}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
</layout>
|
|
@ -1,5 +1,5 @@
|
|||
<!--
|
||||
TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
|
||||
TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation version 3 of the License.
|
||||
|
@ -87,6 +87,13 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<Button
|
||||
style="?materialButtonOutlinedStyle"
|
||||
android:id="@+id/diagnose_exit_reasons_button"
|
||||
android:text="@string/diagnose_er_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
|
|
|
@ -405,6 +405,13 @@
|
|||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
<action
|
||||
android:id="@+id/action_diagnoseMainFragment_to_diagnoseExitReasonFragment"
|
||||
app:destination="@id/diagnoseExitReasonFragment"
|
||||
app:enterAnim="@anim/nav_default_enter_anim"
|
||||
app:exitAnim="@anim/nav_default_exit_anim"
|
||||
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
|
||||
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/diagnoseClockFragment"
|
||||
|
@ -584,4 +591,8 @@
|
|||
android:name="childId"
|
||||
app:argType="string" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/diagnoseExitReasonFragment"
|
||||
android:name="io.timelimit.android.ui.diagnose.exitreason.DiagnoseExitReasonFragment"
|
||||
android:label="DiagnoseExitReasonFragment" />
|
||||
</navigation>
|
||||
|
|
|
@ -530,6 +530,9 @@
|
|||
<string name="diagnose_don_no_owner_toast">TimeLimit ist KEIN Geräte-Besitzer</string>
|
||||
<string name="diagnose_don_not_supported_toast">Ihr System ermöglicht es nicht, einen Organisationsnamen zu hinterlegen</string>
|
||||
|
||||
<string name="diagnose_er_title">Gründe für die Beendung</string>
|
||||
<string name="diagnose_er_empty">es liegen keine Daten vor; diese Daten sind nur bei Android 11 und neuer verfügbar</string>
|
||||
|
||||
<string name="dont_ask_password_on_device_title">Passwortabfrage</string>
|
||||
<string name="dont_ask_password_on_device_text_enabled">
|
||||
Die Passwortabfrage ist an diesem Gerät aktiviert.
|
||||
|
|
|
@ -583,6 +583,9 @@
|
|||
<string name="diagnose_don_no_owner_toast">TimeLimit is NOT set as device owner</string>
|
||||
<string name="diagnose_don_not_supported_toast">Your system does not support setting the organization name</string>
|
||||
|
||||
<string name="diagnose_er_title">Exit Reasons</string>
|
||||
<string name="diagnose_er_empty">there is no data; this data is only available at Android 11 and newer</string>
|
||||
|
||||
<string name="dont_ask_password_on_device_title">Password prompts</string>
|
||||
<string name="dont_ask_password_on_device_text_enabled">
|
||||
Password prompts are enabled at the device.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue