mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-01-18 12:56:45 -06:00
[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"
|
<activity android:name="com.theartofdev.edmodo.cropper.CropImageActivity"
|
||||||
android:configChanges="orientation|keyboardHidden"
|
android:configChanges="orientation|keyboardHidden"
|
||||||
android:theme="@style/Base.Theme.AppCompat" />
|
android:theme="@style/Base.Theme.AppCompat" />
|
||||||
|
<activity android:name=".ui.modules.base.BuildInvalidActivity" />
|
||||||
|
|
||||||
<!-- _____ _
|
<!-- _____ _
|
||||||
| __ \ (_)
|
| __ \ (_)
|
||||||
|
@ -290,6 +290,8 @@ class MainActivity : AppCompatActivity(), CoroutineScope {
|
|||||||
setLanguage(it)
|
setLanguage(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.buildManager.validateBuild(this)
|
||||||
|
|
||||||
if (App.profileId == 0) {
|
if (App.profileId == 0) {
|
||||||
onProfileListEmptyEvent(ProfileListEmptyEvent())
|
onProfileListEmptyEvent(ProfileListEmptyEvent())
|
||||||
return
|
return
|
||||||
|
@ -110,6 +110,11 @@ class Config(val db: AppDb) : CoroutineScope, AbstractConfig {
|
|||||||
get() { mArchiverEnabled = mArchiverEnabled ?: values.get("archiverEnabled", true); return mArchiverEnabled ?: true }
|
get() { mArchiverEnabled = mArchiverEnabled ?: values.get("archiverEnabled", true); return mArchiverEnabled ?: true }
|
||||||
set(value) { set("archiverEnabled", value); mArchiverEnabled = value }
|
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 var rawEntries: List<ConfigEntry> = db.configDao().getAllNow()
|
||||||
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
|
private val profileConfigs: HashMap<Int, ProfileConfig> = hashMapOf()
|
||||||
init {
|
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)
|
setContentView(b.root)
|
||||||
errorSnackbar.setCoordinator(b.coordinator, b.snackbarAnchor)
|
errorSnackbar.setCoordinator(b.coordinator, b.snackbarAnchor)
|
||||||
|
|
||||||
|
app.buildManager.validateBuild(this)
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
app.config.loginFinished = app.db.profileDao().count > 0
|
app.config.loginFinished = app.db.profileDao().count > 0
|
||||||
if (!app.config.loginFinished) {
|
if (!app.config.loginFinished) {
|
||||||
|
@ -4,12 +4,17 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.utils.managers
|
package pl.szczodrzynski.edziennik.utils.managers
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.*
|
||||||
import pl.szczodrzynski.edziennik.*
|
import pl.szczodrzynski.edziennik.*
|
||||||
import pl.szczodrzynski.edziennik.data.api.szkolny.interceptor.Signing
|
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 buildFlavor = BuildConfig.FLAVOR
|
||||||
val buildType = BuildConfig.BUILD_TYPE
|
val buildType = BuildConfig.BUILD_TYPE
|
||||||
@ -27,6 +32,8 @@ class BuildManager(val app: App) {
|
|||||||
val gitTag = BuildConfig.GIT_INFO["tag"]
|
val gitTag = BuildConfig.GIT_INFO["tag"]
|
||||||
val gitIsDirty = BuildConfig.GIT_INFO["dirty"] !== "false"
|
val gitIsDirty = BuildConfig.GIT_INFO["dirty"] !== "false"
|
||||||
val gitRemotes = BuildConfig.GIT_INFO["remotes"]?.split("; ")
|
val gitRemotes = BuildConfig.GIT_INFO["remotes"]?.split("; ")
|
||||||
|
var gitRemote: String? = ""
|
||||||
|
var gitAuthor: String? = ""
|
||||||
|
|
||||||
val isSigned = Signing.appCertificate.md5() == "d8bab5259fda7d72121fe5db526a3d4d"
|
val isSigned = Signing.appCertificate.md5() == "d8bab5259fda7d72121fe5db526a3d4d"
|
||||||
|
|
||||||
@ -85,4 +92,132 @@ class BuildManager(val app: App) {
|
|||||||
.setPositiveButton(R.string.ok, null)
|
.setPositiveButton(R.string.ok, null)
|
||||||
.show()
|
.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_rev_count">Rewizje od ostatniego tagu</string>
|
||||||
<string name="build_remote">Repozytorium zdalne</string>
|
<string name="build_remote">Repozytorium zdalne</string>
|
||||||
<string name="build_details">Informacje o kompilacji</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>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user