diff --git a/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt b/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt index 1ffcfd05799e3fe261196829c0c7ef434590da63..913c23a065bc3ed9dc24af63807531c226c2a839 100644 --- a/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt +++ b/app/src/main/java/foundation/e/apps/AppLoungeApplication.kt @@ -78,7 +78,7 @@ class AppLoungeApplication : Application(), Configuration.Provider { Telemetry.init(BuildConfig.SENTRY_DSN, this) plant(object : Timber.Tree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { - if (priority < Log.WARN) { + if (priority <= Log.WARN) { return } Log.println(priority, tag, message) diff --git a/app/src/main/java/foundation/e/apps/data/database/fusedDownload/FusedDatabase.kt b/app/src/main/java/foundation/e/apps/data/database/fusedDownload/FusedDatabase.kt index 88df8af12fbd583be673ad8c056082836930c1f4..4c0a35b77326aab63d5f7e94be897d5641d8b071 100644 --- a/app/src/main/java/foundation/e/apps/data/database/fusedDownload/FusedDatabase.kt +++ b/app/src/main/java/foundation/e/apps/data/database/fusedDownload/FusedDatabase.kt @@ -9,7 +9,7 @@ import foundation.e.apps.data.database.AppDatabase import foundation.e.apps.data.fusedDownload.FusedDownloadDAO import foundation.e.apps.data.fusedDownload.models.FusedDownload -@Database(entities = [FusedDownload::class], version = 3, exportSchema = false) +@Database(entities = [FusedDownload::class], version = 4, exportSchema = false) @TypeConverters(FusedConverter::class) abstract class FusedDatabase : RoomDatabase() { abstract fun fusedDownloadDao(): FusedDownloadDAO diff --git a/app/src/main/java/foundation/e/apps/data/fusedDownload/models/FusedDownload.kt b/app/src/main/java/foundation/e/apps/data/fusedDownload/models/FusedDownload.kt index 148de9d884db83f2dea67673e22fe10ba85476ff..0d933302122129947eb4849466225934fbd138bc 100644 --- a/app/src/main/java/foundation/e/apps/data/fusedDownload/models/FusedDownload.kt +++ b/app/src/main/java/foundation/e/apps/data/fusedDownload/models/FusedDownload.kt @@ -1,8 +1,10 @@ package foundation.e.apps.data.fusedDownload.models import androidx.room.Entity +import androidx.room.Ignore import androidx.room.PrimaryKey import com.aurora.gplayapi.data.models.File +import foundation.e.apps.data.cleanapk.CleanApkRetrofit import foundation.e.apps.data.enums.Origin import foundation.e.apps.data.enums.Status import foundation.e.apps.data.enums.Type @@ -18,7 +20,7 @@ data class FusedDownload( var downloadIdMap: MutableMap = mutableMapOf(), val orgStatus: Status = Status.UNAVAILABLE, val type: Type = Type.NATIVE, - val iconByteArray: String = String(), + val iconImageUrl: String = String(), val versionCode: Int = 1, val offerType: Int = -1, val isFree: Boolean = true, @@ -26,9 +28,24 @@ data class FusedDownload( var files: List = mutableListOf(), var signature: String = String() ) { - fun isAppInstalling() = listOf(Status.AWAITING, Status.DOWNLOADING, Status.DOWNLOADED, Status.INSTALLING).contains(status) + @Ignore + private val installingStatusList = listOf( + Status.AWAITING, + Status.DOWNLOADING, + Status.DOWNLOADED, + Status.INSTALLING + ) + + fun isAppInstalling() = installingStatusList.contains(status) fun isAwaiting() = status == Status.AWAITING fun areFilesDownloaded() = downloadIdMap.isNotEmpty() && !downloadIdMap.values.contains(false) + + fun getAppIconUrl(): String { + if (this.origin == Origin.CLEANAPK) { + return "${CleanApkRetrofit.ASSET_URL}${this.iconImageUrl}" + } + return this.iconImageUrl + } } diff --git a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt index 1ad5e201b922336f397e346af768a2b39ea13ec8..33a3b837011870e1321ebc3981bbdf2fa677dba7 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt @@ -22,7 +22,7 @@ package foundation.e.apps.data.gplay.utils import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.network.IHttpClient import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.utils.CommonUtilsFunctions +import foundation.e.apps.utils.SystemInfoProvider import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.MainScope @@ -88,7 +88,7 @@ class GPlayHttpClient @Inject constructor( override fun postAuth(url: String, body: ByteArray): PlayResponse { val requestBody = body.toRequestBody("application/json".toMediaType(), 0, body.size) val headers = mapOf( - "User-Agent" to CommonUtilsFunctions.getAppBuildInfo() + "User-Agent" to SystemInfoProvider.getAppBuildInfo() ) val request = Request.Builder() .headers(headers.toHeaders()) diff --git a/app/src/main/java/foundation/e/apps/install/pkg/PWAManagerModule.kt b/app/src/main/java/foundation/e/apps/install/pkg/PWAManagerModule.kt index 628ad0df16bbc9002cdce7597924813ce9fc0e45..56e27c74fbba7a8ea293f45c986c2ee225e959fc 100644 --- a/app/src/main/java/foundation/e/apps/install/pkg/PWAManagerModule.kt +++ b/app/src/main/java/foundation/e/apps/install/pkg/PWAManagerModule.kt @@ -7,7 +7,6 @@ import android.content.Intent import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri -import android.util.Base64 import androidx.core.content.pm.ShortcutInfoCompat import androidx.core.content.pm.ShortcutManagerCompat import androidx.core.graphics.drawable.IconCompat @@ -18,6 +17,10 @@ import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.FusedDownloadRepository import foundation.e.apps.data.fusedDownload.models.FusedDownload import kotlinx.coroutines.delay +import timber.log.Timber +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.net.URL import javax.inject.Inject import javax.inject.Singleton @@ -25,7 +28,7 @@ import javax.inject.Singleton @OpenForTesting class PWAManagerModule @Inject constructor( @ApplicationContext private val context: Context, - private val fusedDownloadRepository: FusedDownloadRepository + private val fusedDownloadRepository: FusedDownloadRepository, ) { companion object { @@ -100,9 +103,15 @@ class PWAManagerModule @Inject constructor( fusedDownloadRepository.updateDownload(fusedDownload) // Get bitmap and byteArray for icon - val iconByteArray = Base64.decode(fusedDownload.iconByteArray, Base64.DEFAULT) - val iconBitmap = BitmapFactory.decodeByteArray(iconByteArray, 0, iconByteArray.size) + val iconBitmap = getIconImageBitmap(fusedDownload.getAppIconUrl()) + if (iconBitmap == null) { + fusedDownload.status = Status.INSTALLATION_ISSUE + fusedDownloadRepository.updateDownload(fusedDownload) + return + } + + val iconByteArray = iconBitmap.toByteArray() val values = ContentValues() values.apply { put(URL, fusedDownload.downloadURLList[0]) @@ -117,7 +126,27 @@ class PWAManagerModule @Inject constructor( } } - private suspend fun publishShortcut(fusedDownload: FusedDownload, bitmap: Bitmap, databaseID: Long) { + fun getIconImageBitmap(url: String): Bitmap? { + return try { + val stream = URL(url).openStream() + BitmapFactory.decodeStream(stream) + } catch (e: IOException) { + Timber.e(e) + null + } + } + + fun Bitmap.toByteArray(): ByteArray { + val byteArrayOS = ByteArrayOutputStream() + this.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOS) + return byteArrayOS.toByteArray() + } + + private suspend fun publishShortcut( + fusedDownload: FusedDownload, + bitmap: Bitmap, + databaseID: Long + ) { // Update status fusedDownload.status = Status.INSTALLING fusedDownloadRepository.updateDownload(fusedDownload) diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt index d2399e4cf093bf12dbbc86dbebbfcb282d10abd4..543fca6f30d96309cc0ecda701b55907342329a8 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt @@ -3,11 +3,8 @@ package foundation.e.apps.install.updates import android.Manifest import android.content.Context import android.content.pm.PackageManager -import android.graphics.Bitmap -import android.graphics.BitmapFactory import android.net.ConnectivityManager import android.net.NetworkCapabilities -import android.util.Base64 import androidx.hilt.work.HiltWorker import androidx.preference.PreferenceManager import androidx.work.CoroutineWorker @@ -20,8 +17,6 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme -import foundation.e.apps.data.cleanapk.CleanApkRetrofit -import foundation.e.apps.data.enums.Origin import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Type import foundation.e.apps.data.enums.User @@ -39,8 +34,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.withContext import timber.log.Timber -import java.io.ByteArrayOutputStream -import java.net.URL @HiltWorker class UpdatesWorker @AssistedInject constructor( @@ -228,8 +221,6 @@ class UpdatesWorker @AssistedInject constructor( return@forEach } - val iconBase64 = getIconImageToBase64(fusedApp) - val fusedDownload = FusedDownload( fusedApp._id, fusedApp.origin, @@ -240,7 +231,7 @@ class UpdatesWorker @AssistedInject constructor( mutableMapOf(), fusedApp.status, fusedApp.type, - iconBase64, + fusedApp.icon_image_path, fusedApp.latest_version_code, fusedApp.offer_type, fusedApp.isFree, @@ -316,16 +307,6 @@ class UpdatesWorker @AssistedInject constructor( } } - private fun getIconImageToBase64(fusedApp: FusedApp): String { - val url = - if (fusedApp.origin == Origin.CLEANAPK) "${CleanApkRetrofit.ASSET_URL}${fusedApp.icon_image_path}" else fusedApp.icon_image_path - val stream = URL(url).openStream() - val bitmap = BitmapFactory.decodeStream(stream) - val byteArrayOS = ByteArrayOutputStream() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOS) - return Base64.encodeToString(byteArrayOS.toByteArray(), Base64.DEFAULT) - } - /* * Checks if the device is connected to a metered connection or not * @param context current Context diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt index fb76120bdaff69d9b3b16f7905f61e3ab00dbc4f..5854f40b57e96c89d2b7c44eea370b1c5fd82d30 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt @@ -53,7 +53,7 @@ import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.purchase.AppPurchaseFragmentDirections import foundation.e.apps.ui.settings.SettingsFragment import foundation.e.apps.ui.setup.signin.SignInViewModel -import foundation.e.apps.utils.CommonUtilsFunctions +import foundation.e.apps.utils.SystemInfoProvider import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.flow.collectLatest @@ -134,7 +134,7 @@ class MainActivity : AppCompatActivity() { val email = otherPayload.toString() viewModel.uploadFaultyTokenToEcloud( email, - CommonUtilsFunctions.getAppBuildInfo() + SystemInfoProvider.getAppBuildInfo() ) } } diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt index 07ccbacc0f45444a6ccd6d81427bf02be8971d8a..762937476cfd649eb9019d62d927e9b46c59861e 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt @@ -20,17 +20,13 @@ package foundation.e.apps.ui import android.content.Context import android.content.Intent -import android.graphics.Bitmap import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.os.Build -import android.util.Base64 -import android.widget.ImageView import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog -import androidx.core.graphics.drawable.toBitmap import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -60,7 +56,6 @@ import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.launch -import java.io.ByteArrayOutputStream import javax.inject.Inject @HiltViewModel @@ -196,7 +191,7 @@ class MainActivityViewModel @Inject constructor( } } - fun getApplication(app: FusedApp, imageView: ImageView?) { + fun getApplication(app: FusedApp) { if (shouldShowPaidAppsSnackBar(app)) { return } @@ -204,7 +199,6 @@ class MainActivityViewModel @Inject constructor( viewModelScope.launch { val fusedDownload: FusedDownload try { - val appIcon = imageView?.let { getImageBase64(it) } ?: "" fusedDownload = FusedDownload( app._id, app.origin, @@ -215,7 +209,7 @@ class MainActivityViewModel @Inject constructor( mutableMapOf(), app.status, app.type, - appIcon, + app.icon_image_path, app.latest_version_code, app.offer_type, app.isFree, @@ -224,7 +218,7 @@ class MainActivityViewModel @Inject constructor( updateFusedDownloadWithAppDownloadLink(app, fusedDownload) } catch (e: Exception) { if (e is ApiException.AppNotPurchased) { - handleAppNotPurchased(imageView, app) + handleAppNotPurchased(app) return@launch } _errorMessage.value = e @@ -239,10 +233,8 @@ class MainActivityViewModel @Inject constructor( } private fun handleAppNotPurchased( - imageView: ImageView?, app: FusedApp ) { - val appIcon = imageView?.let { getImageBase64(it) } ?: "" val fusedDownload = FusedDownload( app._id, app.origin, @@ -253,7 +245,7 @@ class MainActivityViewModel @Inject constructor( mutableMapOf(), app.status, app.type, - appIcon, + app.icon_image_path, app.latest_version_code, app.offer_type, app.isFree, @@ -330,13 +322,6 @@ class MainActivityViewModel @Inject constructor( } } - private fun getImageBase64(imageView: ImageView): String { - val byteArrayOS = ByteArrayOutputStream() - val bitmap = imageView.drawable.toBitmap() - bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOS) - return Base64.encodeToString(byteArrayOS.toByteArray(), Base64.DEFAULT) - } - fun setupConnectivityManager(context: Context) { connectivityManager = context.getSystemService(ConnectivityManager::class.java) as ConnectivityManager diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index 5c40b3b2f1b07758e8cd15c58a26ffc3aab73761..1c75ecfc8027e3dff839ff07063744c651ec55c7 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -521,7 +521,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { enableInstallButton(R.string.retry) setOnClickListener { applicationIcon?.let { - mainActivityViewModel.getApplication(fusedApp, it) + mainActivityViewModel.getApplication(fusedApp) } } } @@ -669,11 +669,11 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { message = getString(R.string.may_not_work_warning_message), positiveButtonText = getString(R.string.install_anyway), positiveButtonAction = { - mainActivityViewModel.getApplication(fusedApp, it) + mainActivityViewModel.getApplication(fusedApp) } ).show(childFragmentManager, "ApplicationFragment") } else { - mainActivityViewModel.getApplication(fusedApp, it) + mainActivityViewModel.getApplication(fusedApp) } } @@ -697,7 +697,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { return@setOnClickListener } applicationIcon?.let { - mainActivityViewModel.getApplication(fusedApp, it) + mainActivityViewModel.getApplication(fusedApp) } } } diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt index 3d607d1374b9166c43c6becf194d99e5ea076c1c..bd8c32c9ec2c11b824c60895e8bcaaf069beb9b9 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListFragment.kt @@ -338,7 +338,7 @@ class ApplicationListFragment : } override fun getApplication(app: FusedApp, appIcon: ImageView?) { - mainActivityViewModel.getApplication(app, appIcon) + mainActivityViewModel.getApplication(app) } override fun cancelDownload(app: FusedApp) { diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt index e77176ba2d62ca9c7db617a3bc8d459a9ee8c5f8..d7f8aa7bba288c31fddcd4edad2c7628a0623ac5 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt @@ -285,7 +285,7 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), FusedAPIInterface } override fun getApplication(app: FusedApp, appIcon: ImageView?) { - mainActivityViewModel.getApplication(app, appIcon) + mainActivityViewModel.getApplication(app) } override fun cancelDownload(app: FusedApp) { diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt index f97ec131c017f16ee46d8d7178f3946cf35386ff..39a946790d27b48361aa691ba4f7456a5d96df00 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchFragment.kt @@ -447,7 +447,7 @@ class SearchFragment : } override fun getApplication(app: FusedApp, appIcon: ImageView?) { - mainActivityViewModel.getApplication(app, appIcon) + mainActivityViewModel.getApplication(app) } override fun cancelDownload(app: FusedApp) { diff --git a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt index cdff64f529a834f7a97919f5228d0ca0e325af16..07abaab9bcbddf1ee9f0665a795a9f4b14b8e461 100644 --- a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt @@ -18,6 +18,7 @@ package foundation.e.apps.ui.settings +import android.content.ClipData import android.content.ClipboardManager import android.content.Intent import android.os.Bundle @@ -45,7 +46,7 @@ import foundation.e.apps.databinding.CustomPreferenceBinding import foundation.e.apps.install.updates.UpdatesWorkManager import foundation.e.apps.ui.MainActivity import foundation.e.apps.ui.MainActivityViewModel -import foundation.e.apps.utils.CommonUtilsFunctions +import foundation.e.apps.utils.SystemInfoProvider import timber.log.Timber import javax.inject.Inject @@ -109,14 +110,14 @@ class SettingsFragment : PreferenceFragmentCompat() { summary = BuildConfig.VERSION_NAME setOnLongClickListener { - val osVersion = CommonUtilsFunctions.getSystemProperty("ro.lineage.version") + val osVersion = SystemInfoProvider.getSystemProperty(SystemInfoProvider.KEY_LINEAGE_VERSION) val appVersionLabel = getString(R.string.app_version_label) var contents = "$appVersionLabel: $summary" if (!osVersion.isNullOrBlank()) { contents += "\n${context.getString(R.string.os_version)}: $osVersion" } - CommonUtilsFunctions.copyTextToClipboard( + copyTextToClipboard( clipboardManager, appVersionLabel, contents @@ -279,6 +280,15 @@ class SettingsFragment : PreferenceFragmentCompat() { } } + private fun copyTextToClipboard( + clipboard: ClipboardManager, + label: String, + text: String, + ) { + val clip = ClipData.newPlainText(label, text) + clipboard.setPrimaryClip(clip) + } + override fun onDestroyView() { if (sourcesChangedFlag) { UpdatesDao.addItemsForUpdate(emptyList()) diff --git a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt index 4baf3adbf0b29ca84019abecae076428d41cb1f6..41a6214c3e9bb93eeae61504634989ee9b2d281a 100644 --- a/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/updates/UpdatesFragment.kt @@ -376,7 +376,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte } override fun getApplication(app: FusedApp, appIcon: ImageView?) { - mainActivityViewModel.getApplication(app, appIcon) + mainActivityViewModel.getApplication(app) } override fun cancelDownload(app: FusedApp) { diff --git a/app/src/main/java/foundation/e/apps/utils/CommonUtilsFunctions.kt b/app/src/main/java/foundation/e/apps/utils/SystemInfoProvider.kt similarity index 73% rename from app/src/main/java/foundation/e/apps/utils/CommonUtilsFunctions.kt rename to app/src/main/java/foundation/e/apps/utils/SystemInfoProvider.kt index f3f93b989fe05341ea563b18967dbaddf8020e79..68b72945dd783c447141506141e13068da1db55e 100644 --- a/app/src/main/java/foundation/e/apps/utils/CommonUtilsFunctions.kt +++ b/app/src/main/java/foundation/e/apps/utils/SystemInfoProvider.kt @@ -18,27 +18,13 @@ package foundation.e.apps.utils import android.annotation.SuppressLint -import android.content.ClipData -import android.content.ClipboardManager import android.os.Build import foundation.e.apps.BuildConfig import org.json.JSONObject -object CommonUtilsFunctions { +object SystemInfoProvider { - /** - * Copy anything to system clipboard. - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5653 - */ - fun copyTextToClipboard( - clipboard: ClipboardManager, - label: String, - text: String, - ) { - // https://developer.android.com/guide/topics/text/copy-paste#Copying - val clip = ClipData.newPlainText(label, text) - clipboard.setPrimaryClip(clip) - } + const val KEY_LINEAGE_VERSION = "ro.lineage.version" @SuppressLint("PrivateApi") fun getSystemProperty(key: String?): String? { @@ -58,7 +44,7 @@ object CommonUtilsFunctions { put("version", BuildConfig.VERSION_NAME) put("device", Build.DEVICE) put("api", Build.VERSION.SDK_INT) - put("os_version", getSystemProperty("ro.lineage.version")) + put("os_version", getSystemProperty(KEY_LINEAGE_VERSION)) put("build_id", BuildConfig.BUILD_ID) } return descriptionJson.toString()