diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt
index 37a2823a93c57330d633642f8a5f81d64f2c2c21..f46bd085288459b1114456aac066fdf09a9337f3 100644
--- a/app/src/main/java/foundation/e/apps/MainActivity.kt
+++ b/app/src/main/java/foundation/e/apps/MainActivity.kt
@@ -43,8 +43,12 @@ import foundation.e.apps.purchase.AppPurchaseFragmentDirections
import foundation.e.apps.setup.signin.SignInViewModel
import foundation.e.apps.updates.UpdatesNotifier
import foundation.e.apps.utils.enums.Status
+import foundation.e.apps.utils.eventBus.AppEvent
+import foundation.e.apps.utils.eventBus.EventBus
import foundation.e.apps.utils.modules.CommonUtilsModule
import foundation.e.apps.utils.parentFragment.TimeoutFragment
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
@@ -199,6 +203,19 @@ class MainActivity : AppCompatActivity() {
}
viewModel.updateAppWarningList()
+
+ lifecycleScope.launchWhenResumed {
+ EventBus.events.filter { appEvent ->
+ appEvent is AppEvent.SignatureMissMatchError
+ }.collectLatest {
+ val appName = viewModel.getAppNameByPackageName(it.data.toString())
+ ApplicationDialogFragment(
+ title = getString(R.string.update_error),
+ message = getString(R.string.error_signature_mismatch, appName),
+ positiveButtonText = getString(R.string.ok)
+ ).show(supportFragmentManager, TAG)
+ }
+ }
}
private fun handleFusedDownloadQueued(
@@ -246,7 +263,7 @@ class MainActivity : AppCompatActivity() {
}
fun showSnackbarMessage(message: String) {
- Snackbar.make(binding.root, message, Snackbar.LENGTH_SHORT).show()
+ Snackbar.make(binding.root, message, Snackbar.LENGTH_LONG).show()
}
private fun showNoInternet() {
diff --git a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt
index 0156a2d7a766d24b33b4c748f67e7e77e53d63c4..83834f12ad7ed5b500c1432a67311c2c2f3cafcb 100644
--- a/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt
+++ b/app/src/main/java/foundation/e/apps/MainActivityViewModel.kt
@@ -561,4 +561,8 @@ class MainActivityViewModel @Inject constructor(
fun updateAppWarningList() {
blockedAppRepository.fetchUpdateOfAppWarningList()
}
+
+ fun getAppNameByPackageName(packageName: String): String {
+ return pkgManagerModule.getAppNameFromPackageName(packageName)
+ }
}
diff --git a/app/src/main/java/foundation/e/apps/api/database/AppDatabase.kt b/app/src/main/java/foundation/e/apps/api/database/AppDatabase.kt
index 174e825e2665a273fa75adb30e6f204fcd825acd..d52abde040bb160e0b70ed7cb440fd8ef715340a 100644
--- a/app/src/main/java/foundation/e/apps/api/database/AppDatabase.kt
+++ b/app/src/main/java/foundation/e/apps/api/database/AppDatabase.kt
@@ -6,17 +6,20 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import foundation.e.apps.api.exodus.Tracker
import foundation.e.apps.api.exodus.TrackerDao
+import foundation.e.apps.api.faultyApps.FaultyApp
+import foundation.e.apps.api.faultyApps.FaultyAppDao
import foundation.e.apps.api.fdroid.FdroidDao
import foundation.e.apps.api.fdroid.models.FdroidEntity
@Database(
- entities = [Tracker::class, FdroidEntity::class],
- version = 2,
+ entities = [Tracker::class, FdroidEntity::class, FaultyApp::class],
+ version = 3,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun trackerDao(): TrackerDao
abstract fun fdroidDao(): FdroidDao
+ abstract fun faultyAppsDao(): FaultyAppDao
companion object {
private lateinit var INSTANCE: AppDatabase
diff --git a/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyApp.kt b/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyApp.kt
new file mode 100644
index 0000000000000000000000000000000000000000..36c1af2e0386618742ee457e69179150f5d76418
--- /dev/null
+++ b/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyApp.kt
@@ -0,0 +1,27 @@
+/*
+ *
+ * * Copyright ECORP SAS 2022
+ * * 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 .
+ *
+ */
+
+package foundation.e.apps.api.faultyApps
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+@Entity
+data class FaultyApp(@PrimaryKey val packageName: String, val error: String)
diff --git a/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppDao.kt b/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppDao.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0218ebf6d2b0208bf94d5b3ded541fdd7790d8e4
--- /dev/null
+++ b/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppDao.kt
@@ -0,0 +1,38 @@
+/*
+ *
+ * * Copyright ECORP SAS 2022
+ * * 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 .
+ *
+ */
+
+package foundation.e.apps.api.faultyApps
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy.REPLACE
+import androidx.room.Query
+
+@Dao
+interface FaultyAppDao {
+ @Insert(onConflict = REPLACE)
+ suspend fun addFaultyApp(faultyApp: FaultyApp): Long
+
+ @Query("SELECT * FROM FAULTYAPP")
+ suspend fun getFaultyApps(): List
+
+ @Query("DELETE FROM FaultyApp WHERE packageName = :packageName")
+ suspend fun deleteFaultyAppByPackageName(packageName: String): Int
+}
diff --git a/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppRepository.kt b/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppRepository.kt
new file mode 100644
index 0000000000000000000000000000000000000000..aebbb2822d0c4840912ceb309afd86d6f2b74fab
--- /dev/null
+++ b/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppRepository.kt
@@ -0,0 +1,41 @@
+/*
+ *
+ * * Copyright ECORP SAS 2022
+ * * 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 .
+ *
+ */
+
+package foundation.e.apps.api.faultyApps
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class FaultyAppRepository @Inject constructor(private val faultyAppDao: FaultyAppDao) {
+
+ suspend fun addFaultyApp(packageName: String, error: String) {
+ val faultyApp = FaultyApp(packageName, error)
+ faultyAppDao.addFaultyApp(faultyApp)
+ }
+
+ suspend fun getAllFaultyApps(): List {
+ return faultyAppDao.getFaultyApps()
+ }
+
+ suspend fun deleteFaultyAppByPackageName(packageName: String) {
+ faultyAppDao.deleteFaultyAppByPackageName(packageName)
+ }
+}
diff --git a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt
index fc32cadebf3f08cc325876bc3a07cf39154ba9e5..a45d98ba7adb600b23f8f8709fe36097323e1651 100644
--- a/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt
+++ b/app/src/main/java/foundation/e/apps/api/fused/FusedAPIImpl.kt
@@ -49,11 +49,11 @@ import foundation.e.apps.api.gplay.GPlayAPIRepository
import foundation.e.apps.manager.database.fusedDownload.FusedDownload
import foundation.e.apps.manager.pkg.PkgManagerModule
import foundation.e.apps.utils.enums.AppTag
+import foundation.e.apps.utils.enums.FilterLevel
import foundation.e.apps.utils.enums.Origin
import foundation.e.apps.utils.enums.ResultStatus
import foundation.e.apps.utils.enums.Status
import foundation.e.apps.utils.enums.Type
-import foundation.e.apps.utils.enums.FilterLevel
import foundation.e.apps.utils.enums.isUnFiltered
import foundation.e.apps.utils.modules.CommonUtilsModule.timeoutDurationInMillis
import foundation.e.apps.utils.modules.PWAManagerModule
@@ -550,9 +550,11 @@ class FusedAPIImpl @Inject constructor(
by = "package_name"
).body()?.run {
if (apps.isNotEmpty() && numberOfResults == 1) {
- fusedAppList.add(apps[0].apply {
- updateFilterLevel(null)
- })
+ fusedAppList.add(
+ apps[0].apply {
+ updateFilterLevel(null)
+ }
+ )
}
}
})
@@ -591,9 +593,11 @@ class FusedAPIImpl @Inject constructor(
*/
val filter = getAppFilterLevel(app, authData)
if (filter.isUnFiltered()) {
- fusedAppList.add(app.transformToFusedApp().apply {
- filterLevel = filter
- })
+ fusedAppList.add(
+ app.transformToFusedApp().apply {
+ filterLevel = filter
+ }
+ )
}
}
})
@@ -621,9 +625,11 @@ class FusedAPIImpl @Inject constructor(
appList.forEach {
val filter = getAppFilterLevel(it, authData)
if (filter.isUnFiltered()) {
- filteredFusedApps.add(it.transformToFusedApp().apply {
- this.filterLevel = filter
- })
+ filteredFusedApps.add(
+ it.transformToFusedApp().apply {
+ this.filterLevel = filter
+ }
+ )
}
}
})
diff --git a/app/src/main/java/foundation/e/apps/di/DaoModule.kt b/app/src/main/java/foundation/e/apps/di/DaoModule.kt
index 6b2f8692a4cbc30543b93381614ce5672c537afd..4c2a77e6f2b1d00b3d7e5f4743e7988956ffcede 100644
--- a/app/src/main/java/foundation/e/apps/di/DaoModule.kt
+++ b/app/src/main/java/foundation/e/apps/di/DaoModule.kt
@@ -8,6 +8,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import foundation.e.apps.api.database.AppDatabase
import foundation.e.apps.api.exodus.TrackerDao
+import foundation.e.apps.api.faultyApps.FaultyAppDao
import foundation.e.apps.api.fdroid.FdroidDao
@InstallIn(SingletonComponent::class)
@@ -22,4 +23,9 @@ object DaoModule {
fun getFdroidDao(@ApplicationContext context: Context): FdroidDao {
return AppDatabase.getInstance(context).fdroidDao()
}
+
+ @Provides
+ fun getFaultyAppsDao(@ApplicationContext context: Context): FaultyAppDao {
+ return AppDatabase.getInstance(context).faultyAppsDao()
+ }
}
diff --git a/app/src/main/java/foundation/e/apps/manager/pkg/InstallerService.kt b/app/src/main/java/foundation/e/apps/manager/pkg/InstallerService.kt
index 660e83df1d74ed48f89a75526ff97f5fccfbb8bc..23a5e2f741db6456c43b85f0dfc74ac6bc097692 100644
--- a/app/src/main/java/foundation/e/apps/manager/pkg/InstallerService.kt
+++ b/app/src/main/java/foundation/e/apps/manager/pkg/InstallerService.kt
@@ -25,9 +25,13 @@ import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import dagger.hilt.android.AndroidEntryPoint
+import foundation.e.apps.api.faultyApps.FaultyAppRepository
import foundation.e.apps.manager.fused.FusedManagerRepository
import foundation.e.apps.utils.enums.Status
+import foundation.e.apps.utils.eventBus.AppEvent
+import foundation.e.apps.utils.eventBus.EventBus
import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@@ -41,8 +45,12 @@ class InstallerService : Service() {
@Inject
lateinit var pkgManagerModule: PkgManagerModule
+ @Inject
+ lateinit var faultyAppRepository: FaultyAppRepository
+
companion object {
const val TAG = "InstallerService"
+ private const val INSTALL_FAILED_UPDATE_INCOMPATIBLE = "INSTALL_FAILED_UPDATE_INCOMPATIBLE"
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@@ -57,8 +65,28 @@ class InstallerService : Service() {
private fun postStatus(status: Int, packageName: String?, extra: String?) {
Timber.d("postStatus: $status $packageName $extra")
- if (status != PackageInstaller.STATUS_SUCCESS) {
- updateInstallationIssue(packageName ?: "")
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ return
+ }
+
+ updateInstallationIssue(packageName ?: "")
+ if (status == PackageInstaller.STATUS_FAILURE_CONFLICT && extra?.contains(
+ INSTALL_FAILED_UPDATE_INCOMPATIBLE
+ ) == true
+ ) {
+ handleInstallFailureDueToSignatureMismatch(packageName)
+ }
+ }
+
+ private fun handleInstallFailureDueToSignatureMismatch(packageName: String?) {
+ MainScope().launch {
+ if (packageName.isNullOrEmpty()) {
+ Timber.wtf("Installation failure for an app without packagename!")
+ return@launch
+ }
+ EventBus.invokeEvent(AppEvent.SignatureMissMatchError(packageName))
+ faultyAppRepository.addFaultyApp(packageName, INSTALL_FAILED_UPDATE_INCOMPATIBLE)
+ Timber.e("INSTALL_FAILED_UPDATE_INCOMPATIBLE for $packageName")
}
}
diff --git a/app/src/main/java/foundation/e/apps/manager/pkg/PkgManagerBR.kt b/app/src/main/java/foundation/e/apps/manager/pkg/PkgManagerBR.kt
index 211e3dbcabbaede5ddb020f4ebc4cf9aefa744ce..665150bdd2dafb4474263e9d5255c6146124012a 100644
--- a/app/src/main/java/foundation/e/apps/manager/pkg/PkgManagerBR.kt
+++ b/app/src/main/java/foundation/e/apps/manager/pkg/PkgManagerBR.kt
@@ -23,28 +23,34 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import dagger.hilt.android.AndroidEntryPoint
+import foundation.e.apps.api.faultyApps.FaultyAppRepository
import foundation.e.apps.manager.fused.FusedManagerRepository
import foundation.e.apps.utils.enums.Status
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
+import javax.inject.Named
@AndroidEntryPoint
@DelicateCoroutinesApi
open class PkgManagerBR : BroadcastReceiver() {
- companion object {
- private const val TAG = "PkgManagerBR"
- }
-
@Inject
lateinit var fusedManagerRepository: FusedManagerRepository
@Inject
lateinit var pkgManagerModule: PkgManagerModule
+ @Inject
+ lateinit var faultyAppRepository: FaultyAppRepository
+
+ @Inject
+ @Named("ioCoroutineScope")
+ lateinit var coroutineScope: CoroutineScope
+
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent?.action
if (context != null && action != null) {
@@ -61,9 +67,11 @@ open class PkgManagerBR : BroadcastReceiver() {
when (action) {
Intent.ACTION_PACKAGE_ADDED -> {
updateDownloadStatus(pkgName)
+ removeFaultyAppByPackageName(pkgName)
}
Intent.ACTION_PACKAGE_REMOVED -> {
if (!isUpdating) deleteDownload(pkgName)
+ removeFaultyAppByPackageName(pkgName)
}
PkgManagerModule.ERROR_PACKAGE_INSTALL -> {
Timber.e("Installation failed due to error: $extra")
@@ -75,6 +83,12 @@ open class PkgManagerBR : BroadcastReceiver() {
}
}
+ private fun removeFaultyAppByPackageName(pkgName: String) {
+ coroutineScope.launch {
+ faultyAppRepository.deleteFaultyAppByPackageName(pkgName)
+ }
+ }
+
private fun deleteDownload(pkgName: String) {
GlobalScope.launch {
val fusedDownload = fusedManagerRepository.getFusedDownload(packageName = pkgName)
diff --git a/app/src/main/java/foundation/e/apps/manager/pkg/PkgManagerModule.kt b/app/src/main/java/foundation/e/apps/manager/pkg/PkgManagerModule.kt
index e694d89e7fb0a7a6f7c2de220bd8b360f7725422..20ca99948eae7ff07402eed882f5edafa685b276 100644
--- a/app/src/main/java/foundation/e/apps/manager/pkg/PkgManagerModule.kt
+++ b/app/src/main/java/foundation/e/apps/manager/pkg/PkgManagerModule.kt
@@ -229,4 +229,11 @@ class PkgManagerModule @Inject constructor(
fun getAllSystemApps(): List {
return packageManager.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY)
}
+
+ fun getAppNameFromPackageName(packageName: String): String {
+ val packageManager = context.packageManager
+ return packageManager.getApplicationLabel(
+ packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)
+ ).toString()
+ }
}
diff --git a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt
index 8ed984890c82e3ed7051e2c8144eb0e2f303b050..2c2ce9d2e14cf6d6b88586242e62486381e28a87 100644
--- a/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt
+++ b/app/src/main/java/foundation/e/apps/updates/manager/UpdatesManagerImpl.kt
@@ -19,6 +19,7 @@
package foundation.e.apps.updates.manager
import com.aurora.gplayapi.data.models.AuthData
+import foundation.e.apps.api.faultyApps.FaultyAppRepository
import foundation.e.apps.api.fused.FusedAPIRepository
import foundation.e.apps.api.fused.data.FusedApp
import foundation.e.apps.manager.pkg.PkgManagerModule
@@ -30,7 +31,8 @@ import javax.inject.Inject
class UpdatesManagerImpl @Inject constructor(
private val pkgManagerModule: PkgManagerModule,
- private val fusedAPIRepository: FusedAPIRepository
+ private val fusedAPIRepository: FusedAPIRepository,
+ private val faultyAppRepository: FaultyAppRepository
) {
private val TAG = UpdatesManagerImpl::class.java.simpleName
@@ -75,7 +77,9 @@ class UpdatesManagerImpl @Inject constructor(
}
}
}
- return Pair(updateList, status)
+ val faultyAppsPackageNames = faultyAppRepository.getAllFaultyApps().map { it.packageName }
+ val nonFaultyUpdateList = updateList.filter { !faultyAppsPackageNames.contains(it.package_name) }
+ return Pair(nonFaultyUpdateList, status)
}
fun getApplicationCategoryPreference(): String {
diff --git a/app/src/main/java/foundation/e/apps/utils/enums/FilterLevel.kt b/app/src/main/java/foundation/e/apps/utils/enums/FilterLevel.kt
index 7eec33730cd5d12a5eb1aeb73dd393552d07fd9f..4fd93c524fd1f7d11f5c710bb4480cf595e991c6 100644
--- a/app/src/main/java/foundation/e/apps/utils/enums/FilterLevel.kt
+++ b/app/src/main/java/foundation/e/apps/utils/enums/FilterLevel.kt
@@ -34,11 +34,11 @@ package foundation.e.apps.utils.enums
* Issue: https://gitlab.e.foundation/e/backlog/-/issues/5720
*/
enum class FilterLevel {
- UI, // Show the app in lists, but show "N/A" in the install button.
- DATA, // Filter the app out from lists and search results, don't show the app at all.
- NONE, // No restrictions
- UNKNOWN, // Not initialised yet
+ UI, // Show the app in lists, but show "N/A" in the install button.
+ DATA, // Filter the app out from lists and search results, don't show the app at all.
+ NONE, // No restrictions
+ UNKNOWN, // Not initialised yet
}
fun FilterLevel.isUnFiltered(): Boolean = this == FilterLevel.NONE
-fun FilterLevel.isInitialized(): Boolean = this != FilterLevel.UNKNOWN
\ No newline at end of file
+fun FilterLevel.isInitialized(): Boolean = this != FilterLevel.UNKNOWN
diff --git a/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt b/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a3a7306ef05fe6fc38842b2a29300c7dd66851de
--- /dev/null
+++ b/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt
@@ -0,0 +1,25 @@
+/*
+ *
+ * * Copyright ECORP SAS 2022
+ * * 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 .
+ *
+ */
+
+package foundation.e.apps.utils.eventBus
+
+sealed class AppEvent(val data: Any) {
+ class SignatureMissMatchError(packageName: String) : AppEvent(packageName)
+}
diff --git a/app/src/main/java/foundation/e/apps/utils/eventBus/EventBus.kt b/app/src/main/java/foundation/e/apps/utils/eventBus/EventBus.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ffb5fac954cbe10ef69552dc3ff40b8c9a684d47
--- /dev/null
+++ b/app/src/main/java/foundation/e/apps/utils/eventBus/EventBus.kt
@@ -0,0 +1,31 @@
+/*
+ *
+ * * Copyright ECORP SAS 2022
+ * * 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 .
+ *
+ */
+
+package foundation.e.apps.utils.eventBus
+
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+
+object EventBus {
+ private val _events = MutableSharedFlow()
+ val events = _events.asSharedFlow()
+
+ suspend fun invokeEvent(event: AppEvent) = _events.emit(event)
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 38407f25e9744a719792f2f8deac0f00a077234e..b6db8704e028d61dbc666d92a31c6a10dbc70f3f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -118,6 +118,8 @@
App updates will be installed automatically
App updates will not be installed automatically
All apps are up-to-date
+ The update cannot be applied because there is a signature mismatch between the update of %1$s and the version you\'ve installed on your phone. To remedy this you can uninstall %1$s and then install it again from App Lounge. <br /><br /> Note: This message won\'t appear again.
+ Update Error!
Discover