mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-04 02:09:19 +02:00
Extend sync diagnose
This commit is contained in:
parent
e69c5a081b
commit
9d93c19de3
6 changed files with 132 additions and 1 deletions
|
@ -85,6 +85,10 @@ class SyncUtil (private val logic: AppLogic) {
|
||||||
shouldSyncImportant.or(shouldSyncUnimportant).or(shouldSyncVeryUnimportant)
|
shouldSyncImportant.or(shouldSyncUnimportant).or(shouldSyncVeryUnimportant)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val lastSyncExceptionInternal = MutableLiveData<Exception?>().apply { postValue(null) }
|
||||||
|
val lastSyncException = lastSyncExceptionInternal.castDown()
|
||||||
|
val isSyncing = liveDataFromFunction (pollInterval = 100) { isSyncingLock.isLocked }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
runAsync {
|
runAsync {
|
||||||
wipeCacheIfUpdated()
|
wipeCacheIfUpdated()
|
||||||
|
@ -113,12 +117,15 @@ class SyncUtil (private val logic: AppLogic) {
|
||||||
lastSync.value = logic.timeApi.getCurrentUptimeInMillis()
|
lastSync.value = logic.timeApi.getCurrentUptimeInMillis()
|
||||||
|
|
||||||
SyncInBackgroundWorker.deschedule()
|
SyncInBackgroundWorker.deschedule()
|
||||||
|
lastSyncExceptionInternal.postValue(null)
|
||||||
|
|
||||||
// wait 2 to 3 seconds before any next sync (debounce)
|
// wait 2 to 3 seconds before any next sync (debounce)
|
||||||
logic.timeApi.sleep((2 * 1000 + random.nextInt(1000)).toLong())
|
logic.timeApi.sleep((2 * 1000 + random.nextInt(1000)).toLong())
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
// wait 10 to 15 seconds before retrying
|
// wait 10 to 15 seconds before retrying
|
||||||
|
|
||||||
|
lastSyncExceptionInternal.postValue(ex)
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.w(LOG_TAG, "sync failed", ex)
|
Log.w(LOG_TAG, "sync failed", ex)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 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
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import io.timelimit.android.R
|
||||||
|
import io.timelimit.android.extensions.showSafe
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
|
|
||||||
|
class DiagnoseExceptionDialogFragment: DialogFragment() {
|
||||||
|
companion object {
|
||||||
|
private const val DIALOG_TAG = "DiagnoseExceptionDialogFragment"
|
||||||
|
private const val EXCEPTION = "ex"
|
||||||
|
|
||||||
|
fun newInstance(exception: Exception) = DiagnoseExceptionDialogFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putSerializable(EXCEPTION, exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getStackTraceString(tr: Throwable): String = StringWriter().let { sw ->
|
||||||
|
PrintWriter(sw).let { pw ->
|
||||||
|
tr.printStackTrace(pw)
|
||||||
|
pw.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
val message = getStackTraceString(arguments!!.getSerializable(EXCEPTION) as Exception)
|
||||||
|
val clipboard = context!!.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
|
||||||
|
return AlertDialog.Builder(context!!, theme)
|
||||||
|
.setMessage(message)
|
||||||
|
.setNeutralButton(R.string.diagnose_sync_copy_to_clipboard) { _, _ ->
|
||||||
|
clipboard.primaryClip = ClipData.newPlainText("TimeLimit", message)
|
||||||
|
|
||||||
|
Toast.makeText(context, R.string.diagnose_sync_copied_to_clipboard, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
.setPositiveButton(R.string.generic_ok, null)
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun show(fragmentManager: FragmentManager) = showSafe(fragmentManager, DIALOG_TAG)
|
||||||
|
}
|
|
@ -28,6 +28,8 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import io.timelimit.android.R
|
import io.timelimit.android.R
|
||||||
import io.timelimit.android.async.Threads
|
import io.timelimit.android.async.Threads
|
||||||
import io.timelimit.android.databinding.DiagnoseSyncFragmentBinding
|
import io.timelimit.android.databinding.DiagnoseSyncFragmentBinding
|
||||||
|
import io.timelimit.android.livedata.map
|
||||||
|
import io.timelimit.android.livedata.switchMap
|
||||||
import io.timelimit.android.logic.DefaultAppLogic
|
import io.timelimit.android.logic.DefaultAppLogic
|
||||||
import io.timelimit.android.sync.actions.apply.UploadActionsUtil
|
import io.timelimit.android.sync.actions.apply.UploadActionsUtil
|
||||||
|
|
||||||
|
@ -36,6 +38,7 @@ class DiagnoseSyncFragment : Fragment() {
|
||||||
val binding = DiagnoseSyncFragmentBinding.inflate(inflater, container, false)
|
val binding = DiagnoseSyncFragmentBinding.inflate(inflater, container, false)
|
||||||
val logic = DefaultAppLogic.with(context!!)
|
val logic = DefaultAppLogic.with(context!!)
|
||||||
val adapter = PendingSyncActionAdapter()
|
val adapter = PendingSyncActionAdapter()
|
||||||
|
val sync = logic.syncUtil
|
||||||
|
|
||||||
binding.recycler.layoutManager = LinearLayoutManager(context!!)
|
binding.recycler.layoutManager = LinearLayoutManager(context!!)
|
||||||
binding.recycler.adapter = adapter
|
binding.recycler.adapter = adapter
|
||||||
|
@ -56,11 +59,31 @@ class DiagnoseSyncFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.requestSyncBtn.setOnClickListener {
|
binding.requestSyncBtn.setOnClickListener {
|
||||||
logic.syncUtil.requestImportantSync(true)
|
sync.requestImportantSync(true)
|
||||||
|
|
||||||
Toast.makeText(context!!, R.string.diagnose_sync_btn_request_sync_toast, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context!!, R.string.diagnose_sync_btn_request_sync_toast, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sync.isSyncing.switchMap { a ->
|
||||||
|
sync.lastSyncException.map { b -> a to b }
|
||||||
|
}.observe(this, Observer { (isSyncing, lastSyncException) ->
|
||||||
|
binding.hadSyncException = lastSyncException != null
|
||||||
|
|
||||||
|
if (isSyncing) {
|
||||||
|
binding.syncStatusText = getString(R.string.diagnose_sync_status_syncing)
|
||||||
|
} else if (lastSyncException != null) {
|
||||||
|
binding.syncStatusText = getString(R.string.diagnose_sync_status_had_error)
|
||||||
|
} else {
|
||||||
|
binding.syncStatusText = getString(R.string.diagnose_sync_status_idle)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
binding.showExceptionBtn.setOnClickListener {
|
||||||
|
sync.lastSyncException.value?.let { ex ->
|
||||||
|
DiagnoseExceptionDialogFragment.newInstance(ex).show(fragmentManager!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,14 @@
|
||||||
name="isListEmpty"
|
name="isListEmpty"
|
||||||
type="Boolean" />
|
type="Boolean" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="syncStatusText"
|
||||||
|
type="String" />
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="hadSyncException"
|
||||||
|
type="boolean" />
|
||||||
|
|
||||||
<import type="android.view.View" />
|
<import type="android.view.View" />
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
|
@ -50,6 +58,22 @@
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
tools:text="@string/diagnose_sync_status_idle"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:textAppearance="?android:textAppearanceMedium"
|
||||||
|
android:text="@{syncStatusText}"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:visibility="@{hadSyncException ? View.VISIBLE : View.GONE}"
|
||||||
|
android:id="@+id/show_exception_btn"
|
||||||
|
android:text="@string/diagnose_sync_btn_show_exception"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/clear_cache_btn"
|
android:id="@+id/clear_cache_btn"
|
||||||
android:text="@string/diagnose_sync_btn_clear_cache"
|
android:text="@string/diagnose_sync_btn_clear_cache"
|
||||||
|
|
|
@ -43,6 +43,10 @@
|
||||||
<string name="diagnose_sync_btn_clear_cache_toast">Cache geleert</string>
|
<string name="diagnose_sync_btn_clear_cache_toast">Cache geleert</string>
|
||||||
<string name="diagnose_sync_btn_request_sync">Synchronisation anfordern</string>
|
<string name="diagnose_sync_btn_request_sync">Synchronisation anfordern</string>
|
||||||
<string name="diagnose_sync_btn_request_sync_toast">Synchronisation wurde angefordert</string>
|
<string name="diagnose_sync_btn_request_sync_toast">Synchronisation wurde angefordert</string>
|
||||||
|
<string name="diagnose_sync_btn_show_exception">Fehlerdetails anzeigen</string>
|
||||||
|
<string name="diagnose_sync_status_idle">Es läuft gerade keine Synchronisation</string>
|
||||||
|
<string name="diagnose_sync_status_syncing">Es wird jetzt synchronisiert</string>
|
||||||
|
<string name="diagnose_sync_status_had_error">Es trat ein Fehler beim Synchronisieren auf</string>
|
||||||
|
|
||||||
<string name="diagnose_fga_title">Erkennung der aktiven App</string>
|
<string name="diagnose_fga_title">Erkennung der aktiven App</string>
|
||||||
<string name="diagnose_fga_query_range">Abfragezeitraum</string>
|
<string name="diagnose_fga_query_range">Abfragezeitraum</string>
|
||||||
|
|
|
@ -43,6 +43,10 @@
|
||||||
<string name="diagnose_sync_btn_clear_cache_toast">Cache cleared</string>
|
<string name="diagnose_sync_btn_clear_cache_toast">Cache cleared</string>
|
||||||
<string name="diagnose_sync_btn_request_sync">Request sync</string>
|
<string name="diagnose_sync_btn_request_sync">Request sync</string>
|
||||||
<string name="diagnose_sync_btn_request_sync_toast">sync was requested</string>
|
<string name="diagnose_sync_btn_request_sync_toast">sync was requested</string>
|
||||||
|
<string name="diagnose_sync_btn_show_exception">Show exception details</string>
|
||||||
|
<string name="diagnose_sync_status_idle">Idle</string>
|
||||||
|
<string name="diagnose_sync_status_syncing">Syncing now</string>
|
||||||
|
<string name="diagnose_sync_status_had_error">Idle, exception occurred</string>
|
||||||
|
|
||||||
<string name="diagnose_fga_title">Foreground-App-Detection</string>
|
<string name="diagnose_fga_title">Foreground-App-Detection</string>
|
||||||
<string name="diagnose_fga_query_range">Requested time range</string>
|
<string name="diagnose_fga_query_range">Requested time range</string>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue