forked from github/szkolny
[App] Implement basic app validation and related UI.
This commit is contained in:
parent
726a37d5d6
commit
6824960731
@ -147,6 +147,7 @@
|
||||
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:theme="@style/Base.Theme.AppCompat" />
|
||||
<activity android:name=".ui.modules.base.BuildInvalidActivity" />
|
||||
|
||||
<!-- _____ _
|
||||
| __ \ (_)
|
||||
|
@ -290,6 +290,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
||||
setLanguage(it)
|
||||
}
|
||||
|
||||
app.buildManager.validateBuild(this)
|
||||
|
||||
if (App.profileId == 0) {
|
||||
onProfileListEmptyEvent(ProfileListEmptyEvent())
|
||||
return
|
||||
|
@ -110,6 +110,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
||||
get() { mArchiverEnabled = mArchiverEnabled ?: values.get("archiverEnabled", true); return mArchiverEnabled ?: true }
|
||||
set(value) { set("archiverEnabled", value); mArchiverEnabled = value }
|
||||
|
||||
private var mValidation: String? = null
|
||||
var validation: String?
|
||||
get() { mValidation = mValidation ?: values["buildValidation"]; return mValidation }
|
||||
set(value) { set("buildValidation", value); mValidation = value }
|
||||
|
||||
private var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
|
||||
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
|
||||
init {
|
||||
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2021-3-27.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.base
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import com.mikepenz.iconics.utils.colorInt
|
||||
import pl.szczodrzynski.edziennik.databinding.ActivityBuildInvalidBinding
|
||||
import pl.szczodrzynski.edziennik.onClick
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
|
||||
class BuildInvalidActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setTheme(Themes.themeInt)
|
||||
val b = ActivityBuildInvalidBinding.inflate(layoutInflater, null, false)
|
||||
setContentView(b.root)
|
||||
|
||||
setSupportActionBar(b.toolbar)
|
||||
|
||||
b.icon.icon?.colorInt = intent.getIntExtra("color", Color.GREEN)
|
||||
b.message.text = intent.getStringExtra("message")
|
||||
b.closeButton.isVisible = !intent.getBooleanExtra("isCritical", true)
|
||||
b.closeButton.onClick {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
@ -96,6 +96,8 @@ class LoginActivity : AppCompatActivity(), CoroutineScope {
|
||||
setContentView(b.root)
|
||||
errorSnackbar.setCoordinator(b.coordinator, b.snackbarAnchor)
|
||||
|
||||
app.buildManager.validateBuild(this)
|
||||
|
||||
launch {
|
||||
app.config.loginFinished = app.db.profileDao().count > 0
|
||||
if (!app.config.loginFinished) {
|
||||
|
@ -4,12 +4,17 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.utils.managers
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.BuildInvalidActivity
|
||||
|
||||
class BuildManager(val app: App) {
|
||||
class BuildManager(val app: App) : CoroutineScope {
|
||||
|
||||
override val coroutineContext = Job() + Dispatchers.Main
|
||||
|
||||
val buildFlavor = BuildConfig.FLAVOR
|
||||
val buildType = BuildConfig.BUILD_TYPE
|
||||
@ -27,6 +32,8 @@ class BuildManager(val app: App) {
|
||||
val gitTag = BuildConfig.GIT_INFO["tag"]
|
||||
val gitIsDirty = BuildConfig.GIT_INFO["dirty"] !== "false"
|
||||
val gitRemotes = BuildConfig.GIT_INFO["remotes"]?.split("; ")
|
||||
var gitRemote: String? = ""
|
||||
var gitAuthor: String? = ""
|
||||
|
||||
val isSigned = Signing.appCertificate.md5() == "d8bab5259fda7d72121fe5db526a3d4d"
|
||||
|
||||
@ -85,4 +92,132 @@ class BuildManager(val app: App) {
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
enum class InvalidBuildReason(
|
||||
val message: Int,
|
||||
val color: Int,
|
||||
val isCritical: Boolean = true
|
||||
) {
|
||||
NO_REMOTE_REPO(R.string.build_invalid_no_remote_repo, R.color.md_orange_500),
|
||||
NO_COMMIT_HASH(R.string.build_invalid_no_commit_hash, R.color.md_orange_500),
|
||||
REMOTE_NO_COMMIT(R.string.build_invalid_remote_no_commit, R.color.md_red_500),
|
||||
OFFICIAL_UNSIGNED(R.string.build_invalid_official_unsigned, R.color.md_red_500),
|
||||
UNSTAGED_CHANGES(R.string.build_invalid_unstaged_changes, R.color.md_amber_800),
|
||||
DEBUG(R.string.build_invalid_debug, R.color.md_yellow_500, false),
|
||||
VALID(R.string.build_valid_unofficial, R.color.md_yellow_500, false)
|
||||
}
|
||||
|
||||
private fun getRemoteRepo(): String? {
|
||||
if (gitRemotes == null)
|
||||
return null
|
||||
return gitRemotes.map {
|
||||
it.substringAfter("(").substringBefore(")")
|
||||
}.firstOrNull {
|
||||
it != "szkolny-eu/szkolny-android"
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun validateRepo(
|
||||
repo: String,
|
||||
commitHash: String
|
||||
) = withContext(Dispatchers.IO) {
|
||||
true
|
||||
}
|
||||
|
||||
fun validateBuild(activity: AppCompatActivity) {
|
||||
launch {
|
||||
gitRemote = getRemoteRepo()
|
||||
if (gitRemote == null && !isDebug) {
|
||||
invalidateBuild(activity, null, InvalidBuildReason.NO_REMOTE_REPO)
|
||||
return@launch
|
||||
}
|
||||
if (gitHash == null) {
|
||||
invalidateBuild(activity, null, InvalidBuildReason.NO_COMMIT_HASH)
|
||||
return@launch
|
||||
}
|
||||
|
||||
// official, signed package
|
||||
if (isOfficial)
|
||||
return@launch
|
||||
|
||||
// seems official, but unsigned
|
||||
if (isPlayRelease || isApkRelease) {
|
||||
invalidateBuild(activity, null, InvalidBuildReason.OFFICIAL_UNSIGNED)
|
||||
return@launch
|
||||
}
|
||||
|
||||
// debug build, invalidate once
|
||||
if (isDebug) {
|
||||
if (app.config.validation != "debug${Signing.appCertificate}".md5()) {
|
||||
app.config.validation = "debug${Signing.appCertificate}".md5()
|
||||
invalidateBuild(activity, null, InvalidBuildReason.DEBUG)
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
// release version with unstaged changes
|
||||
if (gitIsDirty) {
|
||||
invalidateBuild(activity, null, InvalidBuildReason.UNSTAGED_CHANGES)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val validation = Signing.appCertificate + gitHash + gitRemotes?.join(";")
|
||||
|
||||
// app already validated
|
||||
if (app.config.validation == validation.md5())
|
||||
return@launch
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.please_wait)
|
||||
.setMessage(R.string.build_validate_progress)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
|
||||
val isRepoValid = if (app.config.validation == "invalid$gitRemote$gitHash".md5())
|
||||
false
|
||||
else
|
||||
validateRepo(gitRemote!!, gitHash)
|
||||
|
||||
// release build with no public repository or not published changes
|
||||
if (!isRepoValid) {
|
||||
app.config.validation = "invalid$gitRemote$gitHash".md5()
|
||||
invalidateBuild(activity, dialog, InvalidBuildReason.REMOTE_NO_COMMIT)
|
||||
return@launch
|
||||
}
|
||||
|
||||
// release, unofficial, published build
|
||||
app.config.validation = validation.md5()
|
||||
invalidateBuild(activity, dialog, InvalidBuildReason.VALID)
|
||||
}
|
||||
}
|
||||
|
||||
private fun invalidateBuild(
|
||||
activity: AppCompatActivity,
|
||||
progressDialog: AlertDialog?,
|
||||
reason: InvalidBuildReason
|
||||
) {
|
||||
progressDialog?.dismiss()
|
||||
|
||||
val message = activity.getString(
|
||||
reason.message,
|
||||
gitRemote,
|
||||
gitBranch,
|
||||
gitAuthor
|
||||
)
|
||||
|
||||
val color = reason.color.resolveColor(activity)
|
||||
|
||||
val intent = Intent(
|
||||
activity,
|
||||
BuildInvalidActivity::class.java,
|
||||
"message" to message,
|
||||
"color" to color,
|
||||
"isCritical" to reason.isCritical
|
||||
)
|
||||
|
||||
activity.startActivity(intent)
|
||||
|
||||
if (reason.isCritical)
|
||||
activity.finish()
|
||||
}
|
||||
}
|
||||
|
65
app/src/main/res/layout/activity_build_invalid.xml
Normal file
65
app/src/main/res/layout/activity_build_invalid.xml
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2021-3-27.
|
||||
-->
|
||||
|
||||
<LinearLayout 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"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorPrimary"
|
||||
android:gravity="center_horizontal"
|
||||
app:title="@string/app_name" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="32dp">
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
app:iiv_icon="cmd-alert-outline" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/build_invalid_title"
|
||||
android:textAppearance="@style/NavView.TextView.Title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"
|
||||
tools:text="@string/build_invalid_remote_no_commit" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/closeButton"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/close" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
@ -1400,4 +1400,13 @@
|
||||
<string name="build_rev_count">Rewizje od ostatniego tagu</string>
|
||||
<string name="build_remote">Repozytorium zdalne</string>
|
||||
<string name="build_details">Informacje o kompilacji</string>
|
||||
<string name="build_validate_progress">Trwa weryfikowanie kompilacji…</string>
|
||||
<string name="build_invalid_no_remote_repo">Nie znaleziono odniesienia do repozytorium zdalnego. Upewnij się, że korzystasz z fork\'a oficjalnego repozytorium oraz zweryfikuj konfigurację Gradle.</string>
|
||||
<string name="build_invalid_no_commit_hash">Nie znaleziono wartości skrótu aktualnej rewizji. Sprawdź konfigurację Gradle.</string>
|
||||
<string name="build_invalid_remote_no_commit">Posiadasz kompilację aplikacji zawierającą nieopublikowane zmiany. Kompilacja znajduje się w repozytorium %1$s (%2$s), które jest prywatne lub nie zawiera najnowszych zmian.\n\nDla bezpieczeństwa oraz ze względów zgodności z licencją, korzystanie z aplikacji zostało zablokowane.</string>
|
||||
<string name="build_invalid_official_unsigned">Nie możesz modyfikować tego rodzaju kompilacji aplikacji Szkolny.eu.\n\nAby wprowadzić własne zmiany, skorzystaj z kodu źródłowego dostępnego na GitHubie oraz zapoznaj się z README i informacją o licencji.\n\nhttps://szkolny.eu/github/android</string>
|
||||
<string name="build_invalid_unstaged_changes">Ta kompilacja zawiera zmiany niezatwierdzone do żadnej rewizji. Zapisz oraz opublikuj wszystkie zmiany przed wydaniem wersji release.\n\nDla bezpieczeństwa oraz ze względów zgodności z licencją, korzystanie z aplikacji zostało zablokowane.</string>
|
||||
<string name="build_invalid_debug">Korzystasz z kompilacji typu \"debug\". Ta informacja zostanie wyświetlona tylko jeden raz dla aktualnego urządzenia.</string>
|
||||
<string name="build_valid_unofficial">Korzystasz z nieoficjalnej kompilacji aplikacji Szkolny.eu. Zalecamy używanie wyłącznie oficjalnych wersji aplikacji.\n\nOstatnie zmiany w tej wersji zostały wprowadzone przez %3$s w repozytorium %2$s (%1$s).\n\nTo okno nie wyświetli się ponownie.</string>
|
||||
<string name="build_invalid_title">Informacja dotycząca wersji aplikacji</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user