Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1603c31 authored by Sayantan Roychowdhury's avatar Sayantan Roychowdhury
Browse files

create CentralErrorHandler and RetryMechanism

parent 2f9bf620
Loading
Loading
Loading
Loading
+131 −0
Original line number Diff line number Diff line
/*
 * Copyright MURENA SAS 2023
 * Apps  Quickly and easily install Android apps onto your device!
 *
 * 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 foundation.e.apps.domain.errors

import android.app.Activity
import android.content.Intent
import android.graphics.Paint
import android.net.Uri
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import foundation.e.apps.R
import foundation.e.apps.data.enums.User
import foundation.e.apps.databinding.DialogErrorLogBinding

class CentralErrorHandler {

    private var lastDialog: AlertDialog? = null

    fun getDialogForTimeout(
        context: Activity,
        logToDisplay: String = "",
        retryAction: () -> Unit,
    ): AlertDialog.Builder {
        val customDialogView = getDialogCustomView(context, logToDisplay)
        val dialog = AlertDialog.Builder(context).apply {
            setTitle(R.string.timeout_title)
            setMessage(R.string.timeout_desc_cleanapk)
            setView(customDialogView)
            setPositiveButton(R.string.retry) { _, _ ->
                retryAction()
            }
            setNegativeButton(R.string.close, null)
            setCancelable(true)
        }
        return dialog
    }

    fun getDialogForUnauthorized(
        context: Activity,
        logToDisplay: String = "",
        user: User,
        retryAction: () -> Unit,
        logoutAction: () -> Unit,
    ): AlertDialog.Builder {
        val customDialogView = getDialogCustomView(context, logToDisplay)
        val dialog = AlertDialog.Builder(context).apply {
            if (user == User.GOOGLE) {
                setTitle(R.string.sign_in_failed_title)
                setMessage(R.string.sign_in_failed_desc)
            } else {
                setTitle(R.string.anonymous_login_failed)
                setMessage(R.string.anonymous_login_failed_desc)
            }

            setView(customDialogView)

            setPositiveButton(R.string.retry) { _, _ ->
                retryAction()
            }
            setNegativeButton(R.string.logout) { _, _ ->
                logoutAction()
            }
            setCancelable(true)
        }
        return dialog
    }

    private fun getDialogCustomView(
        context: Activity,
        logToDisplay: String,
    ): View {
        val dialogLayout = DialogErrorLogBinding.inflate(context.layoutInflater)
        dialogLayout.apply {
            moreInfo.setOnClickListener {
                logDisplay.isVisible = true
                moreInfo.isVisible = false
            }
            setTextviewUnderlined(troubleshootingLink)
            troubleshootingLink.setOnClickListener {
                openTroubleshootingPage(context)
            }

            if (logToDisplay.isNotBlank()) {
                logDisplay.text = logToDisplay
                moreInfo.isVisible = true
            }
        }
        return dialogLayout.root
    }

    fun dismissAllAndShow(alertDialogBuilder: AlertDialog.Builder) {
        if (lastDialog?.isShowing == true) {
            lastDialog?.dismiss()
        }
        alertDialogBuilder.create().run {
            this.show()
            lastDialog = this
        }
    }

    private fun setTextviewUnderlined(textView: TextView) {
        textView.paintFlags = textView.paintFlags or Paint.UNDERLINE_TEXT_FLAG
    }

    private fun openTroubleshootingPage(context: Activity) {
        context.run {
            val troubleshootUrl = getString(R.string.troubleshootURL)
            val openUrlIntent = Intent(Intent.ACTION_VIEW)
            openUrlIntent.data = Uri.parse(troubleshootUrl)
            startActivity(openUrlIntent)
        }
    }
}
 No newline at end of file
+68 −0
Original line number Diff line number Diff line
/*
 * Copyright MURENA SAS 2023
 * Apps  Quickly and easily install Android apps onto your device!
 *
 * 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 foundation.e.apps.domain.errors

class RetryMechanism {

    private var autoRetryCount = 0

    /**
     * Wrap the code to code to retry execution.
     */
    fun wrapWithRetry(
        retryBlock: () -> Unit,
        retryFailureBlock: () -> Unit,
    ) {
        if (!retryEvaluator(retryBlock)) {
            retryFailureBlock()
        }
    }

    /**
     * Example of where this function can be called:
     * - If user presses "Log out" or "Retry" from an error dialog.
     */
    fun resetRetryCondition() {
        autoRetryCount = 0
    }

    /**
     * The actual block to do multiple retries.
     * We can do some fancy stuff like exponential back-off using recursions.
     *
     * @return true if retry conditions have not expired, false otherwise.
     */
    private fun retryEvaluator(
        retryBlock: () -> Unit,
    ): Boolean {
        if (shouldFailRetry()) return false
        retryBlock()
        updateAutoRetryCondition()
        return true
    }

    private fun updateAutoRetryCondition() {
        autoRetryCount++
    }

    private fun shouldFailRetry(): Boolean {
        return autoRetryCount > 0
    }

}
 No newline at end of file