From efee66023074574c3a7b86b18f98adbc3c122d3c Mon Sep 17 00:00:00 2001 From: Mohit Mali Date: Mon, 26 Oct 2020 12:02:24 +0530 Subject: [PATCH 01/19] # MicroG NE version can be downloaded from Apps now. --- app/src/main/AndroidManifest.xml | 18 +- .../e/apps/XAPK/InstallSplitApksActivity.kt | 2 +- .../e/apps/api/ListApplicationsRequest.kt | 3 +- .../foundation/e/apps/api/ListPwasRequest.kt | 1 - .../e/apps/api/MicroGDataRequest.kt | 97 ++++++++ .../e/apps/api/SystemAppDownloadedRequest.kt | 42 ++++ .../apps/application/ApplicationViewHolder.kt | 27 ++- .../e/apps/application/model/Application.kt | 126 ++++++++-- .../apps/application/model/ApplicationInfo.kt | 69 ++++-- .../e/apps/application/model/Downloader.kt | 32 ++- .../model/IntegrityVerificationTask.kt | 84 ++++--- .../e/apps/application/model/StateManager.kt | 55 ++++- .../apps/application/model/data/BasicData.kt | 27 ++- .../apps/application/model/release/Assets.kt | 24 ++ .../apps/application/model/release/Author.kt | 25 ++ .../apps/application/model/release/Commit.kt | 32 +++ .../application/model/release/Evidences.kt | 22 ++ .../application/model/release/ReleaseData.kt | 31 +++ .../apps/application/model/release/Sources.kt | 21 ++ .../apps/application/model/release/_links.kt | 21 ++ .../applicationmanager/ApplicationManager.kt | 9 + .../e/apps/categories/ApplicationsFragment.kt | 4 + .../apps/categories/CategoriesListAdapter.kt | 10 +- .../categories/category/CategoryActivity.kt | 18 +- .../category/model/CategoryModel.kt | 100 +++++--- .../e/apps/categories/model/Category.kt | 9 +- .../viewmodel/CategoriesViewModel.kt | 2 + .../e/apps/common/ApplicationListAdapter.kt | 5 +- .../systemapps/SystemAppCategoryActivity.kt | 140 +++++++++++ .../SystemApplicationListAdapter.kt | 48 ++++ .../systemapps/SystemApplicationViewHolder.kt | 224 ++++++++++++++++++ .../e/apps/utils/ApplicationParser.kt | 23 +- .../java/foundation/e/apps/utils/Constants.kt | 7 + .../main/res/drawable/ic_core_service_app.png | Bin 0 -> 3269 bytes app/src/main/res/values/strings.xml | 1 + 35 files changed, 1185 insertions(+), 174 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/api/MicroGDataRequest.kt create mode 100644 app/src/main/java/foundation/e/apps/api/SystemAppDownloadedRequest.kt create mode 100644 app/src/main/java/foundation/e/apps/application/model/release/Assets.kt create mode 100644 app/src/main/java/foundation/e/apps/application/model/release/Author.kt create mode 100644 app/src/main/java/foundation/e/apps/application/model/release/Commit.kt create mode 100644 app/src/main/java/foundation/e/apps/application/model/release/Evidences.kt create mode 100644 app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt create mode 100644 app/src/main/java/foundation/e/apps/application/model/release/Sources.kt create mode 100644 app/src/main/java/foundation/e/apps/application/model/release/_links.kt create mode 100644 app/src/main/java/foundation/e/apps/systemapps/SystemAppCategoryActivity.kt create mode 100644 app/src/main/java/foundation/e/apps/systemapps/SystemApplicationListAdapter.kt create mode 100644 app/src/main/java/foundation/e/apps/systemapps/SystemApplicationViewHolder.kt create mode 100644 app/src/main/res/drawable/ic_core_service_app.png diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 52792486b..29189d400 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,19 +16,19 @@ - + - + @@ -39,7 +39,7 @@ - + + android:theme="@style/AppTheme1" + android:windowSoftInputMode="adjustResize" /> \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/XAPK/InstallSplitApksActivity.kt b/app/src/main/java/foundation/e/apps/XAPK/InstallSplitApksActivity.kt index 60f6e49d1..e2c2ecea0 100644 --- a/app/src/main/java/foundation/e/apps/XAPK/InstallSplitApksActivity.kt +++ b/app/src/main/java/foundation/e/apps/XAPK/InstallSplitApksActivity.kt @@ -68,7 +68,7 @@ class InstallSplitApksActivity : BaseActivity() { intent.putExtra("packageName", apksBean!!.packageName) val pendingIntent = PendingIntent.getActivity(mActivity, 0, intent, 0) val statusReceiver = pendingIntent.intentSender - // Commit the session (this will start the installation workflow). + // foundation.e.apps.application.model.release.Commit the session (this will start the installation workflow). session!!.commit(statusReceiver) finish() } catch (e: IOException) { diff --git a/app/src/main/java/foundation/e/apps/api/ListApplicationsRequest.kt b/app/src/main/java/foundation/e/apps/api/ListApplicationsRequest.kt index 558c43d0f..e36468983 100644 --- a/app/src/main/java/foundation/e/apps/api/ListApplicationsRequest.kt +++ b/app/src/main/java/foundation/e/apps/api/ListApplicationsRequest.kt @@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import foundation.e.apps.MainActivity.Companion.mActivity import foundation.e.apps.application.model.Application import foundation.e.apps.application.model.data.BasicData +import foundation.e.apps.application.model.release.ReleaseData import foundation.e.apps.applicationmanager.ApplicationManager import foundation.e.apps.utils.ApplicationParser import foundation.e.apps.utils.Common @@ -59,6 +60,4 @@ class ListApplicationsRequest(private val category: String, private val page: In } } - - } diff --git a/app/src/main/java/foundation/e/apps/api/ListPwasRequest.kt b/app/src/main/java/foundation/e/apps/api/ListPwasRequest.kt index 9db933bf1..87c9edbb9 100644 --- a/app/src/main/java/foundation/e/apps/api/ListPwasRequest.kt +++ b/app/src/main/java/foundation/e/apps/api/ListPwasRequest.kt @@ -26,7 +26,6 @@ class ListPwasRequest(private val category: String, private val page: Int, priva urlConnection.disconnect() callback.invoke(null, result) - } catch (e: Exception) { callback.invoke(Error.findError(e), null) } diff --git a/app/src/main/java/foundation/e/apps/api/MicroGDataRequest.kt b/app/src/main/java/foundation/e/apps/api/MicroGDataRequest.kt new file mode 100644 index 000000000..c2ce822c5 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/api/MicroGDataRequest.kt @@ -0,0 +1,97 @@ +/* + Copyright (C) 2019 e Foundation + + 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 + +import android.content.Context +import android.util.Log +import android.view.View +import com.android.volley.Request +import com.android.volley.Response +import com.android.volley.toolbox.StringRequest +import com.android.volley.toolbox.Volley +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import com.google.gson.Gson +import com.google.gson.JsonParser +import foundation.e.apps.MainActivity.Companion.mActivity +import foundation.e.apps.application.model.Application +import foundation.e.apps.application.model.data.BasicData +import foundation.e.apps.application.model.release.ReleaseData +import foundation.e.apps.applicationmanager.ApplicationManager +import foundation.e.apps.utils.ApplicationParser +import foundation.e.apps.utils.Common +import foundation.e.apps.utils.Constants +import foundation.e.apps.utils.Error +import kotlinx.android.synthetic.main.activity_category.* +import java.io.BufferedReader +import java.io.InputStreamReader +import java.net.URLEncoder + +class MicroGDataRequest { + + companion object { + private val reader = Common.getObjectMapper().readerFor(MicroGDataRequest::class.java) + } + + fun requestGmsCoreRelease(callback: (Error?, MicroGDataRequest?) -> Unit) { + try { + val url = Constants.GMS_RELEASE_API + val urlConnection = Common.createConnection(url, Constants.REQUEST_METHOD_GET) + val isr = InputStreamReader(urlConnection.inputStream) + val element = JsonParser().parse(isr) + + val releaseList: List = Gson().fromJson(element.toString(), + Array::class.java).toList() + + + val basicData = BasicData( + id = "1", + name = "MicroG", + packageName = Constants.MICROG_PACKAGE, + lastVersionNumber = releaseList[0].tag_name, + lastVersionCode = 0, + latestDownloadableUpdate = "", + armeabi_latestDownloadableUpdate = "", + arm64_v8a_latest_latestDownloadableUpdate = "", + x86_latestDownloadableUpdate = "", + armeabi_v7a_latestDownloadableUpdate = "", + apkArchitecture = ArrayList(), + author = "e-Foundation", + iconUri = "https://gitlab.e.foundation/uploads/-/system/project/avatar/149/ic_core_service_app.png?width=64", + imagesUri = arrayOf(), + privacyRating = 0f, + ratings = BasicData.Ratings(0f, 0f), + category = "System Apps", + is_pwa = false, + x86_64_latestDownloadableUpdate = "" + ) + urlConnection.disconnect() + callback.invoke(null, MicroGDataRequest(basicData)) + } catch (e: Exception) { + callback.invoke(Error.findError(e), null) + } + } + + class MicroGDataRequest(private val data: BasicData) { + fun getApplications(applicationManager: ApplicationManager, context: Context): ArrayList { + return ApplicationParser.parseSystemAppData(applicationManager, context, data) + } + } + + +} diff --git a/app/src/main/java/foundation/e/apps/api/SystemAppDownloadedRequest.kt b/app/src/main/java/foundation/e/apps/api/SystemAppDownloadedRequest.kt new file mode 100644 index 000000000..e9a6364eb --- /dev/null +++ b/app/src/main/java/foundation/e/apps/api/SystemAppDownloadedRequest.kt @@ -0,0 +1,42 @@ +/* + Copyright (C) 2019 e Foundation + + 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 + +import com.fasterxml.jackson.annotation.JsonCreator +import foundation.e.apps.utils.Common +import foundation.e.apps.utils.Constants + +class SystemAppDownloadedRequest(private val id: String) { + companion object { + private val reader = Common.getObjectMapper().readerFor(Result::class.java) + } + + fun request() { + try { + val url = Constants.SYSTEM_APP_DOWNLOAD_URL + Constants.GMS_ID + "/jobs/artifacts/$id/raw/play-services-core/build/outputs/apk/release/play-services-core-release-unsigned.apk?job=build" + val urlConnection = Common.createConnection(url, Constants.REQUEST_METHOD_GET) + reader.readValue(urlConnection.inputStream) + urlConnection.disconnect() + } catch (e: Exception) { + e.printStackTrace() + } + } + + class Result @JsonCreator + constructor() +} diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt b/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt index 7df91db5c..8a2256e74 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt @@ -19,6 +19,7 @@ package foundation.e.apps.application import android.annotation.SuppressLint import android.app.Activity +import android.content.Context import android.graphics.Bitmap import android.graphics.Color import android.view.Gravity @@ -39,10 +40,12 @@ import foundation.e.apps.application.model.data.PwasBasicData import foundation.e.apps.application.viewmodel.ApplicationViewModel import foundation.e.apps.utils.Common import foundation.e.apps.utils.Common.toMiB +import foundation.e.apps.utils.Constants import foundation.e.apps.utils.Error import foundation.e.apps.utils.Execute import kotlinx.android.synthetic.main.application_list_item.view.* import kotlinx.android.synthetic.main.install_button_layout.view.* +import java.lang.Exception class ApplicationViewHolder(private val activity: Activity, private val view: View, accentColorOS: Int) : @@ -64,7 +67,8 @@ class ApplicationViewHolder(private val activity: Activity, private val view: Vi private var application: Application? = null private val applicationViewModel = ApplicationViewModel() private var downloader: Downloader? = null -var accentColorOS=accentColorOS; + var accentColorOS = accentColorOS; + init { pwa_icon.visibility = View.GONE view.setOnClickListener { @@ -75,7 +79,7 @@ var accentColorOS=accentColorOS; installButton.setTextColor(Color.parseColor("#ffffff")) - if(0!=this.accentColorOS){ + if (0 != this.accentColorOS) { installButton.setBackgroundColor(this.accentColorOS) } installButton?.setOnClickListener { @@ -149,15 +153,15 @@ var accentColorOS=accentColorOS; override fun stateChanged(state: State) { Execute({}, { - // installButton.setBackgroundResource(R.drawable.app_install_border_simple) + // installButton.setBackgroundResource(R.drawable.app_install_border_simple) installButton?.text = activity.getString(state.installButtonTextId) + when (state) { State.NOT_DOWNLOADED -> { - if(0!=this.accentColorOS){ + if (0 != this.accentColorOS) { installButton.setTextColor(this.accentColorOS) - } - else{ + } else { installButton.setTextColor(Color.parseColor("#0088ED")) } @@ -166,12 +170,12 @@ var accentColorOS=accentColorOS; } State.INSTALLED -> { + installButton?.isEnabled = Common.appHasLaunchActivity(activity, application!!.packageName) - if(0!=this.accentColorOS){ + if (0 != this.accentColorOS) { installButton!!.setBackgroundColor(this.accentColorOS) - } - else{ + } else { installButton!!.setBackgroundResource(R.drawable.app_install_border) } installButton.setTextColor(Color.parseColor("#FAFAFA")) @@ -183,10 +187,9 @@ var accentColorOS=accentColorOS; State.NOT_UPDATED -> { installButton.setTextColor(Color.parseColor("#FAFAFA")) //installButton!!.setBackgroundResource(R.drawable.app_install_border) - if(0!=this.accentColorOS){ + if (0 != this.accentColorOS) { installButton!!.setBackgroundColor(this.accentColorOS) - } - else{ + } else { installButton!!.setBackgroundResource(R.drawable.app_install_border) } installButton?.isEnabled = true diff --git a/app/src/main/java/foundation/e/apps/application/model/Application.kt b/app/src/main/java/foundation/e/apps/application/model/Application.kt index 67e6dbc8e..d4f38c5dd 100644 --- a/app/src/main/java/foundation/e/apps/application/model/Application.kt +++ b/app/src/main/java/foundation/e/apps/application/model/Application.kt @@ -36,6 +36,7 @@ import foundation.e.apps.api.AppDownloadedRequest import foundation.e.apps.api.PackageNameSearchRequest import foundation.e.apps.application.model.State.* import foundation.e.apps.application.model.data.* +import foundation.e.apps.application.model.release.ReleaseData import foundation.e.apps.applicationmanager.ApplicationManager import foundation.e.apps.utils.Common import foundation.e.apps.utils.Constants @@ -51,6 +52,7 @@ class Application(val packageName: String, private val applicationManager: Appli private val info = ApplicationInfo(packageName) private val stateManager = StateManager(info, this, applicationManager) var basicData: BasicData? = null + var releaseData: ReleaseData? = null var fullData: FullData? = null var pwabasicdata: PwasBasicData? = null var pwaFullData: PwaFullData? = null @@ -94,6 +96,19 @@ class Application(val packageName: String, private val applicationManager: Appli } } + fun checkForGmsStateUpdate(context: Context) { + if (basicData != null) { + stateManager.findGms(context, basicData!!) + } else if (searchAppsBasicData != null) { + if (searchAppsBasicData!!.is_pwa) { +// stateManager.pwaFind() + } else { + stateManager.searchAppsFind(context, searchAppsBasicData!!) + } + } else if (pwabasicdata != null) { +// stateManager.pwaFind() + } + } fun pwaInstall(context: Context) { var error: Error? = null @@ -149,6 +164,35 @@ class Application(val packageName: String, private val applicationManager: Appli checkForStateUpdate(context) } + @Synchronized + fun systemAppButtonClicked(context: Context, activity: Activity?) { + when (stateManager.state) { + INSTALLED -> info.launch(context) + NOT_UPDATED, NOT_DOWNLOADED -> { + if (activity != null) { + if (canWriteStorage(activity)) { + applicationManager.installSystemApp(context, this) + } + } else { + applicationManager.installSystemApp(context, this) + } + } + DOWNLOADING -> { + if (!isInstalling) { + if (downloader != null) { + downloader?.cancelDownload() + } else { + onDownloadComplete(context, DownloadManager.STATUS_FAILED) + } + } + return + } + else -> + return + } + checkForStateUpdate(context) + } + private fun canWriteStorage(activity: Activity): Boolean { return if (android.os.Build.VERSION.SDK_INT >= 23) { if (activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) @@ -165,22 +209,52 @@ class Application(val packageName: String, private val applicationManager: Appli } fun download(context: Context) { - val error = assertFullData(context) - if (error == null) { - if (isAPKArchCompatible()) { - downloader = Downloader(info, fullData!!, this) - stateManager.notifyDownloading(downloader!!) - downloader!!.download(context) - synchronized(blocker) { - blocker.wait() + if (basicData?.name == "MicroG") { + downloader = Downloader(info, FullData( + id = "1", + name = "MicroG", + packageName = Constants.MICROG_PACKAGE, + latestVersionNumber = basicData!!.lastVersionNumber, + lastVersionCode = 0, + latestDownloadableUpdate = "", + armeabi_latestDownloadableUpdate = "", + arm64_v8a_latest_latestDownloadableUpdate = "", + x86_latestDownloadableUpdate = "", + armeabi_v7a_latestDownloadableUpdate = "", + apkArchitecture = ArrayList(), + author = "e-Foundation", + iconUri = "https://gitlab.e.foundation/uploads/-/system/project/avatar/149/ic_core_service_app.png?width=64", + imagesUri = arrayOf(), + ratings = BasicData.Ratings(0f, 0f), + is_pwa = false, + x86_64_latestDownloadableUpdate = "", + categoryId = "", + description = "MicroG", + licence = "None" + ), this) + stateManager.notifyDownloading(downloader!!) + downloader!!.downloadSystemApp(context) + synchronized(blocker) { + blocker.wait() + } + } else { + val error = assertFullData(context) + if (error == null) { + if (isAPKArchCompatible()) { + downloader = Downloader(info, fullData!!, this) + stateManager.notifyDownloading(downloader!!) + downloader!!.download(context) + synchronized(blocker) { + blocker.wait() + } + } else { + stateManager.notifyError(Error.APK_INCOMPATIBLE) + onDownloadComplete(context, DownloadManager.STATUS_FAILED) } } else { - stateManager.notifyError(Error.APK_INCOMPATIBLE) + stateManager.notifyError(error) onDownloadComplete(context, DownloadManager.STATUS_FAILED) } - } else { - stateManager.notifyError(error) - onDownloadComplete(context, DownloadManager.STATUS_FAILED) } } @@ -200,14 +274,18 @@ class Application(val packageName: String, private val applicationManager: Appli override fun onDownloadComplete(context: Context, status: Int) { if (status == DownloadManager.STATUS_SUCCESSFUL) { - Execute({ - AppDownloadedRequest(basicData!!.id, fullData!!.getLastVersion()?.apkArchitecture).request() - }, {}) - if (info.isXapk(fullData!!, basicData)) { - isInstalling = true - XAPKFile(info.getxApkFile(context, basicData!!), this) + if (basicData?.packageName == Constants.MICROG_PACKAGE) { + installSystemApp(context) } else { - install(context) + Execute({ + AppDownloadedRequest(basicData!!.id, fullData!!.getLastVersion()?.apkArchitecture).request() + }, {}) + if (info.isXapk(fullData!!, basicData)) { + isInstalling = true + XAPKFile(info.getxApkFile(context, basicData!!), this) + } else { + install(context) + } } } else { synchronized(blocker) { @@ -229,6 +307,12 @@ class Application(val packageName: String, private val applicationManager: Appli info.install(context, basicData!!, this) } + private fun installSystemApp(context: Context) { + isInstalling = true + checkForGmsStateUpdate(context) + info.install(context, basicData!!, this) + } + override fun onInstallationComplete(context: Context) { synchronized(blocker) { blocker.notify() @@ -408,6 +492,10 @@ class Application(val packageName: String, private val applicationManager: Appli basicData?.loadIconAsync(this, iconLoaderCallback) } + fun loadSystemIcon(iconLoaderCallback: BasicData.IconLoaderCallback) { + basicData?.loadSystemAppIconAsync(this, iconLoaderCallback) + } + fun PwaloadIcon(iconLoaderCallback: PwasBasicData.IconLoaderCallback) { pwabasicdata?.loadIconAsync(this, iconLoaderCallback) } diff --git a/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt b/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt index dde66db6c..f1eb62ccb 100644 --- a/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt +++ b/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt @@ -21,6 +21,7 @@ import android.content.Context import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.os.Environment +import android.util.Log import foundation.e.apps.application.model.data.BasicData import foundation.e.apps.application.model.data.FullData import foundation.e.apps.utils.Common @@ -31,21 +32,41 @@ class ApplicationInfo(private val packageName: String) { fun isLastVersionInstalled(context: Context, lastVersionNumber: String): Boolean { val packageInfo = getPackageInfo(context) ?: return false - if (lastVersionNumber.isBlank() || - !lastVersionNumber.contains("(") || - !lastVersionNumber.contains(")")) { - return true - } - if (!Common.isSystemApp(context.packageManager, packageName)) { - try { - val pattern = Pattern.compile("[(]\\d+[)]") - val matcher = pattern.matcher(lastVersionNumber) - matcher.find() - val updateVersionCode = matcher.group() - .replace("(", "") - .replace(")", "") - return (updateVersionCode.toInt() <= packageInfo.versionCode) - } catch (exception: Exception) { + + if (Common.isSystemApp(context.packageManager, packageName)) { + + if (lastVersionNumber.isBlank()) + return true + else { + val currentVersion = packageInfo.versionName.replace(".", "").replace("-", "") + val mVersion = currentVersion.filter { it.isDigit() } + val newVersion = lastVersionNumber.replace(".", "") + Log.e("CurrentVersion", mVersion) + Log.e("NewVersion", newVersion) + try { + return newVersion.toLong() > mVersion.toLong() + } catch (e: Exception) { + e.printStackTrace() + } + } + } else { + + if (lastVersionNumber.isBlank() || + !lastVersionNumber.contains("(") || + !lastVersionNumber.contains(")")) { + return true + } + if (!Common.isSystemApp(context.packageManager, packageName)) { + try { + val pattern = Pattern.compile("[(]\\d+[)]") + val matcher = pattern.matcher(lastVersionNumber) + matcher.find() + val updateVersionCode = matcher.group() + .replace("(", "") + .replace(")", "") + return (updateVersionCode.toInt() <= packageInfo.versionCode) + } catch (exception: Exception) { + } } } return true @@ -91,23 +112,21 @@ class ApplicationInfo(private val packageName: String) { Installer(data.packageName, getApkFile(context, data), callback).install(context) } - fun isXapk( fullData: FullData, basicData: BasicData?): Boolean { + fun isXapk(fullData: FullData, basicData: BasicData?): Boolean { return fullData.getLastVersion()!!.is_xapk && fullData.getLastVersion()?.downloadLink!!.endsWith(".xapk") } fun getApkOrXapkFileName(fullData: FullData, basicData: BasicData): String? { - if(isXapk(fullData,basicData) ) { + if (isXapk(fullData, basicData)) { return getxApkFilename(basicData) - } - else + } else return getApkFilename(basicData) } - fun getApkOrXapkFile(context: Context,fullData: FullData, basicData: BasicData): File { - if(isXapk(fullData,basicData)){ - return getxApkFile(context,basicData) - } - else - return getApkFile(context,basicData) + fun getApkOrXapkFile(context: Context, fullData: FullData, basicData: BasicData): File { + if (isXapk(fullData, basicData)) { + return getxApkFile(context, basicData) + } else + return getApkFile(context, basicData) } } diff --git a/app/src/main/java/foundation/e/apps/application/model/Downloader.kt b/app/src/main/java/foundation/e/apps/application/model/Downloader.kt index 0748ad768..6b8b68df1 100644 --- a/app/src/main/java/foundation/e/apps/application/model/Downloader.kt +++ b/app/src/main/java/foundation/e/apps/application/model/Downloader.kt @@ -25,6 +25,7 @@ import android.content.IntentFilter import android.net.Uri import android.os.AsyncTask import android.os.Environment +import android.util.Log import foundation.e.apps.R import foundation.e.apps.application.model.data.FullData import foundation.e.apps.utils.Constants @@ -68,6 +69,16 @@ class Downloader(private val applicationInfo: ApplicationInfo, private val fullD } } + fun downloadSystemApp(context: Context) { + downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager + registerReceivers(context) + initialiseDownloadManagerRequestForSystemApps(context) + downloadId = downloadManager.enqueue(request) + Thread { + handleDownloadUpdates() + }.start() + } + private fun registerReceivers(context: Context) { context.registerReceiver(onComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) @@ -88,7 +99,25 @@ class Downloader(private val applicationInfo: ApplicationInfo, private val fullD setDestinationInExternalFilesDir( context, Environment.DIRECTORY_DOWNLOADS, - applicationInfo.getApkOrXapkFileName(fullData,fullData.basicData)) + applicationInfo.getApkOrXapkFileName(fullData, fullData.basicData)) + } + } + + private fun initialiseDownloadManagerRequestForSystemApps(context: Context) { + + Log.e("RequestCreated", "True") + Log.e("Url", "https://gitlab.e.foundation/api/v4/projects/149/jobs/artifacts/111.2.3/download?job=build") + + request = DownloadManager.Request( + Uri.parse( + "https://gitlab.e.foundation/e/apps/GmsCore/-/jobs/121231/artifacts/raw/play-services-core/build/outputs/apk/release/play-services-core-release.apk")) + .apply { + setTitle("MicroG") + setDescription(context.getString(R.string.download_notification_description)) + setDestinationInExternalFilesDir( + context, + Environment.DIRECTORY_DOWNLOADS, + applicationInfo.getApkFilename(fullData.basicData)) } } @@ -111,6 +140,7 @@ class Downloader(private val applicationInfo: ApplicationInfo, private val fullD val downloadStatus = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) cursor.close() + Log.e("DownLoadStatus", downloadStatus.toString()) if (downloadStatus == DownloadManager.STATUS_SUCCESSFUL || downloadStatus == DownloadManager.STATUS_FAILED) { break diff --git a/app/src/main/java/foundation/e/apps/application/model/IntegrityVerificationTask.kt b/app/src/main/java/foundation/e/apps/application/model/IntegrityVerificationTask.kt index 536e84553..aa70d3e0c 100644 --- a/app/src/main/java/foundation/e/apps/application/model/IntegrityVerificationTask.kt +++ b/app/src/main/java/foundation/e/apps/application/model/IntegrityVerificationTask.kt @@ -20,6 +20,7 @@ package foundation.e.apps.application.model import android.content.Context import android.os.AsyncTask import foundation.e.apps.application.model.data.FullData +import foundation.e.apps.utils.Constants import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.openpgp.PGPCompressedData import org.bouncycastle.openpgp.PGPPublicKeyRingCollection @@ -45,17 +46,21 @@ class IntegrityVerificationTask( override fun doInBackground(vararg context: Context): Context { // var file= (applicationInfo.getApkFile(context[0], // fullData.basicData).absolutePath).length - verificationSuccessful = if (!fullData.getLastVersion()!!.apkSHA.isNullOrEmpty()) { - getApkFileSha1(applicationInfo.getApkOrXapkFile(context[0],fullData,fullData.basicData)) == - fullData.getLastVersion()!!.apkSHA + if (fullData.packageName == Constants.MICROG_PACKAGE) { + verificationSuccessful = true } else { - Security.addProvider(BouncyCastleProvider()) - verifyAPKSignature( - BufferedInputStream(FileInputStream( - applicationInfo.getApkFile(context[0], - fullData.basicData).absolutePath)), - fullData.getLastVersion()!!.signature.byteInputStream(Charsets.UTF_8), - context[0].assets.open("f-droid.org-signing-key.gpg")) + verificationSuccessful = if (!fullData.getLastVersion()!!.apkSHA.isNullOrEmpty()) { + getApkFileSha1(applicationInfo.getApkOrXapkFile(context[0], fullData, fullData.basicData)) == + fullData.getLastVersion()!!.apkSHA + } else { + Security.addProvider(BouncyCastleProvider()) + verifyAPKSignature( + BufferedInputStream(FileInputStream( + applicationInfo.getApkFile(context[0], + fullData.basicData).absolutePath)), + fullData.getLastVersion()!!.signature.byteInputStream(Charsets.UTF_8), + context[0].assets.open("f-droid.org-signing-key.gpg")) + } } return context[0] } @@ -64,7 +69,7 @@ class IntegrityVerificationTask( integrityVerificationCallback.onIntegrityVerified(context, verificationSuccessful) } - private fun getApkFileSha1(file: File): String?{ + private fun getApkFileSha1(file: File): String? { val messageDigest = MessageDigest.getInstance("SHA-1") val fileInputStream = FileInputStream(file) var length = 0 @@ -77,6 +82,7 @@ class IntegrityVerificationTask( } return byteArrayToHex(messageDigest.digest()) } + private fun byteArrayToHex(a: ByteArray): String? { val sb = StringBuilder(a.size * 2) for (b in a) sb.append(String.format("%02x", b)) @@ -88,43 +94,43 @@ class IntegrityVerificationTask( apkSignatureInputStream: InputStream, publicKeyInputStream: InputStream): Boolean { - var jcaPGPObjectFactory = - JcaPGPObjectFactory(PGPUtil.getDecoderStream(apkSignatureInputStream)) - val pgpSignatureList: PGPSignatureList + var jcaPGPObjectFactory = + JcaPGPObjectFactory(PGPUtil.getDecoderStream(apkSignatureInputStream)) + val pgpSignatureList: PGPSignatureList - val pgpObject = jcaPGPObjectFactory.nextObject() - if (pgpObject is PGPCompressedData) { - jcaPGPObjectFactory = JcaPGPObjectFactory(pgpObject.dataStream) - pgpSignatureList = jcaPGPObjectFactory.nextObject() as PGPSignatureList - } else { - pgpSignatureList = pgpObject as PGPSignatureList - } + val pgpObject = jcaPGPObjectFactory.nextObject() + if (pgpObject is PGPCompressedData) { + jcaPGPObjectFactory = JcaPGPObjectFactory(pgpObject.dataStream) + pgpSignatureList = jcaPGPObjectFactory.nextObject() as PGPSignatureList + } else { + pgpSignatureList = pgpObject as PGPSignatureList + } - val pgpPublicKeyRingCollection = - PGPPublicKeyRingCollection( - PGPUtil.getDecoderStream(publicKeyInputStream), - JcaKeyFingerprintCalculator()) + val pgpPublicKeyRingCollection = + PGPPublicKeyRingCollection( + PGPUtil.getDecoderStream(publicKeyInputStream), + JcaKeyFingerprintCalculator()) - val signature = pgpSignatureList.get(0) - val key = pgpPublicKeyRingCollection.getPublicKey(signature.keyID) + val signature = pgpSignatureList.get(0) + val key = pgpPublicKeyRingCollection.getPublicKey(signature.keyID) - signature.init(BcPGPContentVerifierBuilderProvider(), key) + signature.init(BcPGPContentVerifierBuilderProvider(), key) - val buff = ByteArray(1024) - var read = apkInputStream.read(buff) - while (read != -1) { - signature.update(buff, 0, read) - read = apkInputStream.read(buff) - } + val buff = ByteArray(1024) + var read = apkInputStream.read(buff) + while (read != -1) { + signature.update(buff, 0, read) + read = apkInputStream.read(buff) + } - apkInputStream.close() - apkSignatureInputStream.close() - publicKeyInputStream.close() + apkInputStream.close() + apkSignatureInputStream.close() + publicKeyInputStream.close() - return signature.verify() + return signature.verify() - } } +} interface IntegrityVerificationCallback { fun onIntegrityVerified(context: Context, verificationSuccessful: Boolean) diff --git a/app/src/main/java/foundation/e/apps/application/model/StateManager.kt b/app/src/main/java/foundation/e/apps/application/model/StateManager.kt index 556db86b1..a200230d8 100644 --- a/app/src/main/java/foundation/e/apps/application/model/StateManager.kt +++ b/app/src/main/java/foundation/e/apps/application/model/StateManager.kt @@ -18,8 +18,10 @@ package foundation.e.apps.application.model import android.content.Context +import android.util.Log import foundation.e.apps.application.model.data.BasicData import foundation.e.apps.application.model.data.SearchAppsBasicData +import foundation.e.apps.application.model.release.ReleaseData import foundation.e.apps.applicationmanager.ApplicationManager import foundation.e.apps.utils.Error import java.util.* @@ -31,47 +33,80 @@ class StateManager(private val info: ApplicationInfo, private val app: Applicati private set fun find(context: Context, basicData: BasicData) { + if (basicData.name == "MicroG") { + val state = if (appManager.isInstalling(app) && !app.isInstalling) { + State.DOWNLOADING + } else if (appManager.isInstalling(app) && app.isInstalling) { + State.INSTALLING + } else if (info.isLastVersionInstalled(context, + basicData.getLastVersion() ?: "")) { + State.INSTALLED + } else if (info.isInstalled(context) && !info.isLastVersionInstalled(context, + basicData.getLastVersion() ?: "")) { + State.NOT_UPDATED + } else { + State.NOT_DOWNLOADED + } + Log.e("State", state.name) + changeState(state) + } else { + val state = if (appManager.isInstalling(app) && !app.isInstalling) { + State.DOWNLOADING + } else if (appManager.isInstalling(app) && app.isInstalling) { + State.INSTALLING + } else if (info.isLastVersionInstalled(context, + basicData.getLastVersion() ?: "")) { + State.NOT_DOWNLOADED + } else if (info.isInstalled(context) && !info.isLastVersionInstalled(context, + basicData.getLastVersion() ?: "")) { + State.NOT_UPDATED + } else { + State.NOT_DOWNLOADED + } + changeState(state) + } + } + + fun findGms(context: Context, basicData: BasicData) { val state = if (appManager.isInstalling(app) && !app.isInstalling) { State.DOWNLOADING } else if (appManager.isInstalling(app) && app.isInstalling) { State.INSTALLING } else if (info.isLastVersionInstalled(context, - basicData.getLastVersion() ?: "")) { + basicData.lastVersionNumber)) { State.INSTALLED - } else if (info.isInstalled(context) && !info.isLastVersionInstalled(context, + } /*else if (info.isInstalled(context) && !info.isLastVersionInstalled(context, basicData.getLastVersion() ?: "")) { State.NOT_UPDATED - } else { + }*/ else { State.NOT_DOWNLOADED } changeState(state) } - fun searchAppsFind(context: Context, basicData: SearchAppsBasicData) { val state = if (appManager.isInstalling(app) && !app.isInstalling) { State.DOWNLOADING } else if (appManager.isInstalling(app) && app.isInstalling) { State.INSTALLING - } else if(info.isLastVersionInstalled(context, - basicData.getLastVersion() ?: "")){ + } else if (info.isLastVersionInstalled(context, + basicData.getLastVersion() ?: "")) { State.INSTALLED } else if (info.isInstalled(context) && !info.isLastVersionInstalled(context, basicData.getLastVersion() ?: "")) { State.NOT_UPDATED - } - else { + } else { State.NOT_DOWNLOADED } changeState(state) } + fun pwaFind() { val state = if (appManager.isInstalling(app) && !app.isInstalling) { State.DOWNLOADING } else if (appManager.isInstalling(app) && app.isInstalling) { State.INSTALLING - } - else { + } else { State.NOT_DOWNLOADED } changeState(state) diff --git a/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt b/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt index cfdb17afe..d9b8b2d27 100644 --- a/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt +++ b/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt @@ -60,7 +60,7 @@ constructor(@param:JsonProperty("_id") val id: String, @param:JsonProperty("exodus_score") val privacyRating: Float?, @param:JsonProperty("ratings") val ratings: Ratings, @param:JsonProperty("category") val category: String, - @param:JsonProperty("is_pwa") val is_pwa: Boolean){ + @param:JsonProperty("is_pwa") val is_pwa: Boolean) { private var icon: Bitmap? = null @@ -102,11 +102,28 @@ constructor(@param:JsonProperty("_id") val id: String, } } + fun loadSystemAppIconAsync(application: Application, iconLoaderCallback: IconLoaderCallback) { + if (icon == null) { + var error: Error? = null + Execute({ + error = loadIconSynced() + }, { + if (error == null) { + icon?.let { + iconLoaderCallback.onIconLoaded(application, it) + } + } + }) + } else { + iconLoaderCallback.onIconLoaded(application, icon!!) + } + } + @Synchronized private fun loadIconSynced(): Error? { if (icon == null) { try { - val url = URL(BASE_URL + "media/" + iconUri) + val url = URL(iconUri) val urlConnection = url.openConnection() as HttpsURLConnection urlConnection.requestMethod = Constants.REQUEST_METHOD_GET urlConnection.connectTimeout = Constants.CONNECT_TIMEOUT @@ -138,11 +155,11 @@ constructor(@param:JsonProperty("_id") val id: String, } fun getLastVersion(): String? { - if(apkArchitecture!=null) { + if (apkArchitecture != null) { //An ordered list of ABIs supported by this device. The most preferred ABI is the first element in the list. val arch = android.os.Build.SUPPORTED_ABIS.toList() - when(arch[0]) { + when (arch[0]) { "arm64-v8a" -> { return largestVersion(Pair(arm64_v8a_lastVersionNumber, arm64_v8a_lastVersionCode), Pair(armeabi_v7a_lastVersionNumber, armeabi_v7a_lastVersionCode), @@ -192,6 +209,4 @@ constructor(@param:JsonProperty("_id") val id: String, } - - } diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt b/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt new file mode 100644 index 000000000..91768b9ba --- /dev/null +++ b/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt @@ -0,0 +1,24 @@ +package foundation.e.apps.application.model.release + +import com.google.gson.annotations.SerializedName +import foundation.e.apps.application.model.release.Sources + +/* +Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ + + +data class Assets ( + + @SerializedName("count") val count : Int, + @SerializedName("sources") val sources : List, + @SerializedName("links") val links : List, + @SerializedName("evidence_file_path") val evidence_file_path : String +) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Author.kt b/app/src/main/java/foundation/e/apps/application/model/release/Author.kt new file mode 100644 index 000000000..840da41a2 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/application/model/release/Author.kt @@ -0,0 +1,25 @@ +package foundation.e.apps.application.model.release + +import com.google.gson.annotations.SerializedName + +/* +Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ + + +data class Author ( + + @SerializedName("id") val id : Int=-1, + @SerializedName("name") val name : String="", + @SerializedName("username") val username : String="", + @SerializedName("state") val state : String="", + @SerializedName("avatar_url") val avatar_url : String="", + @SerializedName("web_url") val web_url : String +) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Commit.kt b/app/src/main/java/foundation/e/apps/application/model/release/Commit.kt new file mode 100644 index 000000000..4b0877f86 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/application/model/release/Commit.kt @@ -0,0 +1,32 @@ +package foundation.e.apps.application.model.release + +import com.google.gson.annotations.SerializedName + +/* +Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ + + +data class Commit ( + + @SerializedName("id") val id : String, + @SerializedName("short_id") val short_id : String, + @SerializedName("created_at") val created_at : String, + @SerializedName("parent_ids") val parent_ids : List, + @SerializedName("title") val title : String, + @SerializedName("message") val message : String, + @SerializedName("author_name") val author_name : String, + @SerializedName("author_email") val author_email : String, + @SerializedName("authored_date") val authored_date : String, + @SerializedName("committer_name") val committer_name : String, + @SerializedName("committer_email") val committer_email : String, + @SerializedName("committed_date") val committed_date : String, + @SerializedName("web_url") val web_url : String +) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Evidences.kt b/app/src/main/java/foundation/e/apps/application/model/release/Evidences.kt new file mode 100644 index 000000000..71fd0f7e1 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/application/model/release/Evidences.kt @@ -0,0 +1,22 @@ +package foundation.e.apps.application.model.release + +import com.google.gson.annotations.SerializedName + +/* +Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ + + +data class Evidences ( + + @SerializedName("sha") val sha : String, + @SerializedName("filepath") val filepath : String, + @SerializedName("collected_at") val collected_at : String +) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt b/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt new file mode 100644 index 000000000..78a700f9d --- /dev/null +++ b/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt @@ -0,0 +1,31 @@ +package foundation.e.apps.application.model.release + +import com.google.gson.annotations.SerializedName + +/* +Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ + + +class ReleaseData( + @SerializedName("name") val name: String, + @SerializedName("tag_name") val tag_name: String, + @SerializedName("description") val description: String, + @SerializedName("description_html") val description_html: String, + @SerializedName("created_at") val created_at: String, + @SerializedName("released_at") val released_at: String, + @SerializedName("author") val author: Author, + @SerializedName("commit") val commit: Commit, + @SerializedName("upcoming_release") val upcoming_release: Boolean, + @SerializedName("commit_path") val commit_path: String, + @SerializedName("tag_path") val tag_path: String, + @SerializedName("evidence_sha") val evidence_sha: String, + @SerializedName("evidences") val evidences: List +) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Sources.kt b/app/src/main/java/foundation/e/apps/application/model/release/Sources.kt new file mode 100644 index 000000000..e2183f5f8 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/application/model/release/Sources.kt @@ -0,0 +1,21 @@ +package foundation.e.apps.application.model.release + +import com.google.gson.annotations.SerializedName + +/* +Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ + + +data class Sources ( + + @SerializedName("format") val format : String, + @SerializedName("url") val url : String +) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/application/model/release/_links.kt b/app/src/main/java/foundation/e/apps/application/model/release/_links.kt new file mode 100644 index 000000000..66bb3a8cf --- /dev/null +++ b/app/src/main/java/foundation/e/apps/application/model/release/_links.kt @@ -0,0 +1,21 @@ +package foundation.e.apps.application.model.release + +import com.google.gson.annotations.SerializedName + +/* +Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ + + +data class _links ( + + @SerializedName("self") val self : String, + @SerializedName("edit_url") val edit_url : String +) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/applicationmanager/ApplicationManager.kt b/app/src/main/java/foundation/e/apps/applicationmanager/ApplicationManager.kt index 1fa9a1960..988c28ce7 100644 --- a/app/src/main/java/foundation/e/apps/applicationmanager/ApplicationManager.kt +++ b/app/src/main/java/foundation/e/apps/applicationmanager/ApplicationManager.kt @@ -46,6 +46,15 @@ class ApplicationManager { app.checkForStateUpdate(context) } + @Synchronized + fun installSystemApp(context: Context, app: Application) { + if (!queue.contains(app)) { + queue.put(app) + queue.put(app) + } + app.checkForGmsStateUpdate(context) + } + fun start(context: Context) { Thread { startInstalls(context) diff --git a/app/src/main/java/foundation/e/apps/categories/ApplicationsFragment.kt b/app/src/main/java/foundation/e/apps/categories/ApplicationsFragment.kt index ffe58a5fd..b727c0467 100644 --- a/app/src/main/java/foundation/e/apps/categories/ApplicationsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/ApplicationsFragment.kt @@ -28,6 +28,7 @@ import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.LinearLayoutManager import foundation.e.apps.R +import foundation.e.apps.categories.model.Category import foundation.e.apps.categories.viewmodel.CategoriesViewModel import kotlinx.android.synthetic.main.error_layout.view.* import kotlinx.android.synthetic.main.fragment_application_categories.view.* @@ -60,6 +61,9 @@ class ApplicationsFragment(color: Int?) : Fragment() { categoriesViewModel.getApplicationsCategories().observe(this, Observer { if (it!!.isNotEmpty()) { + //Add New Category + it.add(Category("system_apps")) + view.categories_list.adapter = CategoriesListAdapter(it, color) view.categories_list.visibility = View.VISIBLE view.progress_bar.visibility = View.GONE diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt index 72f300ccd..20d3eb745 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt @@ -30,12 +30,14 @@ import androidx.recyclerview.widget.RecyclerView import foundation.e.apps.R import foundation.e.apps.categories.category.CategoryActivity import foundation.e.apps.categories.model.Category +import foundation.e.apps.systemapps.SystemAppCategoryActivity import foundation.e.apps.utils.Constants class CategoriesListAdapter(private var categories: ArrayList, color: Int?) : RecyclerView.Adapter() { val color = color; + init { categories = ArrayList(categories.sortedWith( compareBy({ it.getTitle() }, { it.getTitle() }))) @@ -70,10 +72,16 @@ class CategoriesListAdapter(private var categories: ArrayList, color: } holder.categoryTitle.text = categories[position].getTitle() holder.categoryContainer.setOnClickListener { + /* if (categories[position].id == "system_apps") { + val intent = Intent(holder.categoryContainer.context, SystemAppCategoryActivity::class.java) + intent.putExtra(Constants.CATEGORY_KEY, categories[position]) + holder.categoryContainer.context.startActivity(intent) + } else {*/ val intent = Intent(holder.categoryContainer.context, CategoryActivity::class.java) intent.putExtra(Constants.CATEGORY_KEY, categories[position]) - intent.putExtra("POSITION",position) + intent.putExtra("POSITION", position) holder.categoryContainer.context.startActivity(intent) +// } } } } diff --git a/app/src/main/java/foundation/e/apps/categories/category/CategoryActivity.kt b/app/src/main/java/foundation/e/apps/categories/category/CategoryActivity.kt index 5a3745fff..10c0c0e88 100644 --- a/app/src/main/java/foundation/e/apps/categories/category/CategoryActivity.kt +++ b/app/src/main/java/foundation/e/apps/categories/category/CategoryActivity.kt @@ -60,8 +60,7 @@ class CategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectio ApplicationManagerServiceConnection(this) private var applicationList = ArrayList() private var isLoadingMoreApplications = false - var accentColorOS=0; - + var accentColorOS = 0; override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -85,7 +84,6 @@ class CategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectio findViewById(R.id.error_resolve).setTextColor(Color.parseColor("#ffffff")) findViewById(R.id.error_resolve).setBackgroundColor(accentColorOS) - // Initialise UI elements recyclerView.visibility = View.GONE loadMoreContainer.visibility = View.GONE @@ -94,10 +92,10 @@ class CategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectio if (!recyclerView.canScrollVertically(1)) { loadMoreContainer.visibility = View.VISIBLE recyclerView.scrollToPosition(applicationList.size - 1) - if (!isLoadingMoreApplications) { - isLoadingMoreApplications = true - categoryViewModel.loadApplications(this@CategoryActivity) - } + if (!isLoadingMoreApplications) { + isLoadingMoreApplications = true + categoryViewModel.loadApplications(this@CategoryActivity) + } } else { loadMoreContainer.visibility = View.GONE } @@ -201,6 +199,12 @@ class CategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectio * */ private fun getAccentColor() { + val typedValue = TypedValue() + val contextThemeWrapper = ContextThemeWrapper(this, + android.R.style.Theme_DeviceDefault) + contextThemeWrapper.getTheme().resolveAttribute(android.R.attr.colorAccent, + typedValue, true) + @ColorInt val color = typedValue.data accentColorOS=this.resources.getColor(R.color.colorAccent); } } diff --git a/app/src/main/java/foundation/e/apps/categories/category/model/CategoryModel.kt b/app/src/main/java/foundation/e/apps/categories/category/model/CategoryModel.kt index b8ce442a8..43d8755eb 100644 --- a/app/src/main/java/foundation/e/apps/categories/category/model/CategoryModel.kt +++ b/app/src/main/java/foundation/e/apps/categories/category/model/CategoryModel.kt @@ -18,10 +18,12 @@ package foundation.e.apps.categories.category.model import android.content.Context +import android.util.Log import androidx.lifecycle.MutableLiveData import foundation.e.apps.MainActivity import foundation.e.apps.api.ListApplicationsRequest import foundation.e.apps.api.ListPwasRequest +import foundation.e.apps.api.MicroGDataRequest import foundation.e.apps.application.model.Application import foundation.e.apps.applicationmanager.ApplicationManager import foundation.e.apps.utils.Common @@ -46,52 +48,95 @@ class CategoryModel : CategoryModelInterface { override fun loadApplications(context: Context) { var apps: ArrayList? = null if (Common.isNetworkAvailable(context)) { - Execute({ - apps = loadApplicationsSynced(context) - }, { - if (error == null && apps != null) { - val result = ArrayList() - categoryApplicationsList.value?.let { - result.addAll(it) + if (category == "system_apps") { + Execute({ + apps = loadApplicationsSynced(context) + }, { + if (error == null && apps != null) { + val result = ArrayList() + /*categoryApplicationsList.value?.let { + result.addAll(it) + }*/ + result.addAll(apps!!) + if (apps!!.size != 0) { + categoryApplicationsList.value = result + } + } else { + screenError.value = error } - result.addAll(apps!!) - if (apps!!.size != 0) { - categoryApplicationsList.value = result + }) + + } else { + + Execute({ + apps = loadApplicationsSynced(context) + }, { + if (error == null && apps != null) { + val result = ArrayList() + categoryApplicationsList.value?.let { + result.addAll(it) + } + result.addAll(apps!!) + if (apps!!.size != 0) { + categoryApplicationsList.value = result + } + } else { + screenError.value = error } - } else { - screenError.value = error - } - }) - page++ + }) + page++ + } } else { screenError.value = Error.NO_INTERNET } } - fun loadApplicationsSynced(context: Context): ArrayList? { + fun loadApplicationsSynced(context: Context): ArrayList? { var listApplications: ListApplicationsRequest.ListApplicationsResult? = null + var microGData: MicroGDataRequest.MicroGDataRequest? = null var listPwas: ListPwasRequest.ListPwasResult? = null - var appType = MainActivity.mActivity.showApplicationTypePreference() + val appType = MainActivity.mActivity.showApplicationTypePreference() + Log.e("SystemAppRequest", "Yes") + if (category == "system_apps") { + MicroGDataRequest() + .requestGmsCoreRelease { applicationError, listMicroGData -> - if(appType=="pwa"){ - ListPwasRequest(category,page,Constants.RESULTS_PER_PAGE) - .request { applicationError, listPwasResult -> - when (applicationError) { - null -> { - listPwas = listPwasResult!! + when (applicationError) { + null -> { + microGData = listMicroGData!! + } + else -> { + error = applicationError + } } - else -> { - error = applicationError + } + return if (microGData != null) { + microGData!!.getApplications(applicationManager, context) + } else { + null + } + + } + + if (appType == "pwa") { + ListPwasRequest(category, page, Constants.RESULTS_PER_PAGE) + .request { applicationError, listPwasResult -> + when (applicationError) { + null -> { + listPwas = listPwasResult!! + } + else -> { + error = applicationError + } } } - } return if (listPwas != null) { listPwas!!.getApplications(applicationManager, context) } else { null } } - ListApplicationsRequest(category,page,Constants.RESULTS_PER_PAGE) + ListApplicationsRequest(category, page, Constants.RESULTS_PER_PAGE) .request { applicationError, listApplicationsResult -> when (applicationError) { null -> { @@ -107,6 +152,7 @@ class CategoryModel : CategoryModelInterface { } else { null } + } diff --git a/app/src/main/java/foundation/e/apps/categories/model/Category.kt b/app/src/main/java/foundation/e/apps/categories/model/Category.kt index 16e0d5b99..f19cc271e 100644 --- a/app/src/main/java/foundation/e/apps/categories/model/Category.kt +++ b/app/src/main/java/foundation/e/apps/categories/model/Category.kt @@ -31,9 +31,8 @@ class Category(val id: String, val result: String = "") : Serializable { } private fun getCategoryTitle(categoryId: String): String { - if(result.isNotEmpty()) return result - - else{ + if (result.isNotEmpty()) return result + else { val title = categoryId.replace("_", " ") return title.capitalize() } @@ -63,6 +62,8 @@ class Category(val id: String, val result: String = "") : Serializable { R.drawable.ic_cat_security "system" -> R.drawable.ic_cat_system + "system_apps" -> + R.drawable.ic_cat_system "communication" -> R.drawable.ic_cat_communication "medical" -> @@ -77,7 +78,7 @@ class Category(val id: String, val result: String = "") : Serializable { R.drawable.ic_cat_productivity "house_and_home" -> R.drawable.ic_cat_house_and_home - "art_and_design"-> + "art_and_design" -> R.drawable.ic_art_and_design "photography" -> R.drawable.ic_cat_photography diff --git a/app/src/main/java/foundation/e/apps/categories/viewmodel/CategoriesViewModel.kt b/app/src/main/java/foundation/e/apps/categories/viewmodel/CategoriesViewModel.kt index 62b33e391..a83f89af0 100644 --- a/app/src/main/java/foundation/e/apps/categories/viewmodel/CategoriesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/categories/viewmodel/CategoriesViewModel.kt @@ -19,11 +19,13 @@ package foundation.e.apps.categories.viewmodel import android.content.Context import android.content.Intent +import android.util.Log import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import foundation.e.apps.categories.category.CategoryActivity import foundation.e.apps.categories.model.CategoriesModel import foundation.e.apps.categories.model.Category +import foundation.e.apps.systemapps.SystemAppCategoryActivity import foundation.e.apps.utils.Constants import foundation.e.apps.utils.Error diff --git a/app/src/main/java/foundation/e/apps/common/ApplicationListAdapter.kt b/app/src/main/java/foundation/e/apps/common/ApplicationListAdapter.kt index 337dbb1f5..db830d5c7 100644 --- a/app/src/main/java/foundation/e/apps/common/ApplicationListAdapter.kt +++ b/app/src/main/java/foundation/e/apps/common/ApplicationListAdapter.kt @@ -27,15 +27,14 @@ import foundation.e.apps.application.model.Application class ApplicationListAdapter(private val activity: Activity, private val applicationList: List, accentColorOS: Int) : RecyclerView.Adapter() { - - var accentColorOS=accentColorOS; + var accentColorOS = accentColorOS; override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ApplicationViewHolder { val listItemContainer = LayoutInflater.from(parent.context).inflate(R.layout.application_list_item, parent, false) return ApplicationViewHolder(activity, listItemContainer, accentColorOS) } override fun onBindViewHolder(holder: ApplicationViewHolder, position: Int) { - holder.createApplicationView(applicationList[position] ) + holder.createApplicationView(applicationList[position]) } override fun getItemCount() = applicationList.size diff --git a/app/src/main/java/foundation/e/apps/systemapps/SystemAppCategoryActivity.kt b/app/src/main/java/foundation/e/apps/systemapps/SystemAppCategoryActivity.kt new file mode 100644 index 000000000..0ce02aff7 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/systemapps/SystemAppCategoryActivity.kt @@ -0,0 +1,140 @@ +/* + Copyright (C) 2019 e Foundation + + 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.systemapps + +import android.content.pm.PackageManager +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.widget.Toolbar +import androidx.recyclerview.widget.LinearLayoutManager +import com.android.volley.Request +import com.android.volley.Response +import com.android.volley.toolbox.StringRequest +import com.android.volley.toolbox.Volley +import com.google.android.material.snackbar.Snackbar +import com.google.gson.Gson +import foundation.e.apps.R +import foundation.e.apps.application.model.Application +import foundation.e.apps.application.model.data.BasicData +import foundation.e.apps.application.model.release.ReleaseData +import foundation.e.apps.applicationmanager.ApplicationManager +import foundation.e.apps.applicationmanager.ApplicationManagerServiceConnection +import foundation.e.apps.applicationmanager.ApplicationManagerServiceConnectionCallback +import foundation.e.apps.utils.Constants +import kotlinx.android.synthetic.main.activity_category.* +import kotlinx.android.synthetic.main.error_layout.* + +class SystemAppCategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectionCallback { + + val systemAppsList = ArrayList() + private val applicationManagerServiceConnection = + ApplicationManagerServiceConnection(this) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_category) + + val toolbar = findViewById(R.id.toolbar) + setSupportActionBar(toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.title = getString(R.string.system_apps) + + error_container.visibility = View.GONE + progress_bar.visibility = View.VISIBLE + + app_list.layoutManager = LinearLayoutManager(this) + app_list.adapter = SystemApplicationListAdapter(this, systemAppsList) + + applicationManagerServiceConnection.bindService(this) + + loadReleaseData() + + } + + private fun loadReleaseData() { + + // Instantiate the RequestQueue. + val queue = Volley.newRequestQueue(this) + val url = "https://gitlab.e.foundation/api/v4/projects/149/releases" + + // Request a string response from the provided URL. + val stringRequest = StringRequest(Request.Method.GET, url, + Response.Listener { response -> + progress_bar.visibility = View.GONE + val releaseList: List = Gson().fromJson(response, + Array::class.java).toList() + val application = Application(Constants.MICROG_PACKAGE, ApplicationManager()) + val basicData = BasicData( + id = "1", + name = "MicroG", + packageName = Constants.MICROG_PACKAGE, + lastVersionNumber = releaseList[0].tag_name, + lastVersionCode = 0, + latestDownloadableUpdate = "", + armeabi_latestDownloadableUpdate = "", + arm64_v8a_latest_latestDownloadableUpdate = "", + x86_latestDownloadableUpdate = "", + armeabi_v7a_latestDownloadableUpdate = "", + apkArchitecture = ArrayList(), + author = "e-Foundation", + iconUri = "https://gitlab.e.foundation/uploads/-/system/project/avatar/149/ic_core_service_app.png?width=64", + imagesUri = arrayOf(), + privacyRating = 0f, + ratings = BasicData.Ratings(0f, 0f), + category = "System Apps", + is_pwa = false, + x86_64_latestDownloadableUpdate = "" + ) + application.basicData = basicData + systemAppsList.add(application) + app_list.adapter?.notifyDataSetChanged() + }, + Response.ErrorListener { + progress_bar.visibility = View.GONE + }) + + // Add the request to the RequestQueue. + queue.add(stringRequest) + + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when (item?.itemId) { + android.R.id.home -> + finish() + } + return true + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, + grantResults: IntArray) { + if (requestCode == Constants.STORAGE_PERMISSION_REQUEST_CODE && + grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_DENIED) { + Snackbar.make(container, R.string.error_storage_permission_denied, + Snackbar.LENGTH_LONG).show() + } + } + + override fun onServiceBind(applicationManager: ApplicationManager) { + + } + + +} diff --git a/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationListAdapter.kt b/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationListAdapter.kt new file mode 100644 index 000000000..b973a56a7 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationListAdapter.kt @@ -0,0 +1,48 @@ +/* + Copyright (C) 2019 e Foundation + + 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.systemapps + +import android.app.Activity +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import foundation.e.apps.R +import foundation.e.apps.application.model.Application +import foundation.e.apps.utils.Constants +import kotlinx.android.synthetic.main.application_list_item.view.* + +class SystemApplicationListAdapter(private val activity: Activity, + private val applicationList: List) : + RecyclerView.Adapter() { + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SystemApplicationViewHolder { + val listItemContainer = LayoutInflater.from(parent.context).inflate(R.layout.application_list_item, parent, false) + return SystemApplicationViewHolder(activity, listItemContainer, 0) + } + + override fun onBindViewHolder(holder: SystemApplicationViewHolder, position: Int) { + holder.createApplicationView(applicationList[position]) + } + + override fun getItemCount() = applicationList.size + + +} diff --git a/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationViewHolder.kt b/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationViewHolder.kt new file mode 100644 index 000000000..452cc72cf --- /dev/null +++ b/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationViewHolder.kt @@ -0,0 +1,224 @@ +/* + Copyright (C) 2019 e Foundation + + 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.systemapps + +import android.annotation.SuppressLint +import android.app.Activity +import android.graphics.Bitmap +import android.graphics.Color +import android.view.Gravity +import android.view.View +import android.widget.Button +import android.widget.ImageView +import android.widget.RatingBar +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.snackbar.Snackbar +import foundation.e.apps.R +import foundation.e.apps.application.model.Application +import foundation.e.apps.application.model.ApplicationStateListener +import foundation.e.apps.application.model.Downloader +import foundation.e.apps.application.model.State +import foundation.e.apps.application.model.data.BasicData +import foundation.e.apps.application.model.data.PwasBasicData +import foundation.e.apps.application.viewmodel.ApplicationViewModel +import foundation.e.apps.utils.Common +import foundation.e.apps.utils.Common.toMiB +import foundation.e.apps.utils.Error +import foundation.e.apps.utils.Execute +import kotlinx.android.synthetic.main.application_list_item.view.* +import kotlinx.android.synthetic.main.install_button_layout.view.* + + +class SystemApplicationViewHolder(private val activity: Activity, private val view: View, accentColorOS: Int) : + RecyclerView.ViewHolder(view), + ApplicationStateListener, + Downloader.DownloadProgressCallback, + BasicData.IconLoaderCallback, + PwasBasicData.IconLoaderCallback { + + + private val icon: ImageView = view.app_icon + private val title: TextView = view.app_title + private val pwa_icon: TextView = view.pwa_sympol + private val author: TextView = view.app_author + private val ratingBar: RatingBar = view.app_rating_bar + private val rating: TextView = view.app_rating + private val privacyScore: TextView = view.app_privacy_score + private var installButton: Button = view.app_install + private var application: Application? = null + private val applicationViewModel = ApplicationViewModel() + private var downloader: Downloader? = null + var accentColorOS = accentColorOS; + + init { + pwa_icon.visibility = View.GONE + view.setOnClickListener { + if (application != null) { + applicationViewModel.onApplicationClick(view.context, application!!) + } + } + + + installButton.setTextColor(Color.parseColor("#ffffff")) + if (0 != this.accentColorOS) { + installButton.setBackgroundColor(this.accentColorOS) + } + installButton?.setOnClickListener { + if (application?.fullData != null && + application!!.fullData!!.getLastVersion() == null) { + Snackbar.make(view, activity.getString( + Error.APK_UNAVAILABLE.description), + Snackbar.LENGTH_LONG).show() + } else if (application?.pwabasicdata != null) { + application?.pwaInstall(activity) + } else if (application?.searchAppsBasicData != null && application?.searchAppsBasicData!!.is_pwa) { + application?.pwaInstall(activity) + } else { + application?.systemAppButtonClicked(activity, activity) + } + } + } + + fun createApplicationView(app: Application) { + + pwa_icon.visibility = View.GONE + this.application = app + + if (app.basicData != null) { + this.application?.removeListener(this) + this.application = app + icon.setImageDrawable(view.context.resources.getDrawable(R.drawable.ic_app_default)) + application!!.loadSystemIcon(this) + application!!.addListener(this) + title.text = application!!.basicData!!.name + author.text = application!!.basicData!!.author + ratingBar.rating = application!!.basicData!!.ratings!!.rating!! + if (application!!.basicData!!.ratings!!.rating != -1f) { + rating.text = application!!.basicData!!.ratings!!.rating.toString() + } else { + rating.text = activity.getString(R.string.not_available) + } + if (application!!.basicData!!.privacyRating != null && application!!.basicData!!.privacyRating != -1f) { + privacyScore.text = application!!.basicData!!.privacyRating.toString() + } else { + privacyScore.text = activity.getString(R.string.not_available) + } + } else { + this.application?.removeListener(this) + this.application = app + icon.setImageDrawable(view.context.resources.getDrawable(R.drawable.ic_app_default)) + application!!.addListener(this) + if (application!!.searchAppsBasicData != null) { + if (application!!.searchAppsBasicData!!.is_pwa) { + pwa_icon.visibility = View.VISIBLE + } + application!!.SearchAppsloadIcon(this) + title.text = application!!.searchAppsBasicData!!.name + author.text = application!!.searchAppsBasicData!!.author + } else { + application!!.PwaloadIcon(this) + title.text = application!!.pwabasicdata!!.name + + } + } + stateChanged(application!!.state) + } + + override fun onIconLoaded(application: Application, bitmap: Bitmap) { + if (this.application != null && application == this.application) { + icon.setImageBitmap(bitmap) + } + } + + override fun stateChanged(state: State) { + Execute({}, { + + // installButton.setBackgroundResource(R.drawable.app_install_border_simple) + installButton?.text = activity.getString(state.installButtonTextId) + when (state) { + + State.NOT_DOWNLOADED -> { + if (0 != this.accentColorOS) { + installButton.setTextColor(this.accentColorOS) + } else { + + installButton.setTextColor(Color.parseColor("#0088ED")) + } + installButton.setBackgroundResource(R.drawable.app_install_border_simple) + installButton.isEnabled = true + } + + State.INSTALLED -> { + installButton?.isEnabled = + Common.appHasLaunchActivity(activity, application!!.packageName) + if (0 != this.accentColorOS) { + installButton!!.setBackgroundColor(this.accentColorOS) + } else { + installButton!!.setBackgroundResource(R.drawable.app_install_border) + } + installButton.setTextColor(Color.parseColor("#FAFAFA")) + + } + State.INSTALLING -> { + installButton?.isEnabled = false + } + State.NOT_UPDATED -> { + installButton.setTextColor(Color.parseColor("#FAFAFA")) + //installButton!!.setBackgroundResource(R.drawable.app_install_border) + if (0 != this.accentColorOS) { + installButton!!.setBackgroundColor(this.accentColorOS) + } else { + installButton!!.setBackgroundResource(R.drawable.app_install_border) + } + installButton?.isEnabled = true + } + else -> { + installButton.setTextColor(Color.parseColor("#0088ED")) + installButton?.isEnabled = true + } + } + + }) + } + + override fun downloading(downloader: Downloader) { + this.downloader = downloader + this.downloader!!.addListener(this) + } + + @SuppressLint("SetTextI18n") + override fun notifyDownloadProgress(count: Int, total: Int) { + installButton.setGravity(Gravity.CENTER) + installButton?.text = ((toMiB(count) / toMiB(total)) * 100).toInt().toString() + "%" + installButton.setTextColor(Color.parseColor("#0088ED")) + installButton.setBackgroundResource(R.drawable.app_installing_border_simple) + } + + override fun anErrorHasOccurred(error: Error) { + Snackbar.make(activity.findViewById(R.id.container), + activity.getString(error.description), + Snackbar.LENGTH_LONG).show() + } + + fun onViewRecycled() { + downloader?.removeListener(this) + downloader = null + } + +} diff --git a/app/src/main/java/foundation/e/apps/utils/ApplicationParser.kt b/app/src/main/java/foundation/e/apps/utils/ApplicationParser.kt index 5fde16d13..778bc2eec 100644 --- a/app/src/main/java/foundation/e/apps/utils/ApplicationParser.kt +++ b/app/src/main/java/foundation/e/apps/utils/ApplicationParser.kt @@ -22,6 +22,7 @@ import foundation.e.apps.application.model.Application import foundation.e.apps.application.model.data.BasicData import foundation.e.apps.application.model.data.PwasBasicData import foundation.e.apps.application.model.data.SearchAppsBasicData +import foundation.e.apps.application.model.release.ReleaseData import foundation.e.apps.applicationmanager.ApplicationManager class ApplicationParser { @@ -39,13 +40,13 @@ class ApplicationParser { fun PwaParseToApps(applicationManager: ApplicationManager, context: Context, apps: Array): ArrayList { - val result = ArrayList() - apps.forEach { - val application = applicationManager.findOrCreateApp(it.name) - application.Pwaupdate(it, context) - result.add(application) - } - return result + val result = ArrayList() + apps.forEach { + val application = applicationManager.findOrCreateApp(it.name) + application.Pwaupdate(it, context) + result.add(application) + } + return result } @@ -59,5 +60,13 @@ class ApplicationParser { return result } + fun parseSystemAppData(applicationManager: ApplicationManager, context: Context, apps: BasicData): ArrayList { + val result = ArrayList() + val application = applicationManager.findOrCreateApp(Constants.MICROG_PACKAGE) + application.update(apps, context) + result.add(application) + return result + } + } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/utils/Constants.kt b/app/src/main/java/foundation/e/apps/utils/Constants.kt index eb2b91a9d..50b3029ca 100644 --- a/app/src/main/java/foundation/e/apps/utils/Constants.kt +++ b/app/src/main/java/foundation/e/apps/utils/Constants.kt @@ -21,6 +21,8 @@ object Constants { // Global const val BASE_URL = "https://api.cleanapk.org/v2/" + const val SYSTEM_APP_DOWNLOAD_URL = "https://gitlab.e.foundation/api/v4/projects/" + const val GMS_RELEASE_API = "https://gitlab.e.foundation/api/v4/projects/149/releases" const val DOWNLOAD_URL = "https://apk.cleanapk.org/" const val STORAGE_PERMISSION_REQUEST_CODE = 0 const val CONNECT_TIMEOUT = 30000 // 30 seconds @@ -28,6 +30,11 @@ object Constants { const val REQUEST_METHOD_GET = "GET" const val REQUEST_METHOD_POST = "POST" + //microG Package + const val MICROG_PACKAGE = "com.google.android.gms" + const val GMS_ID = 149 + + // Search const val MIN_SEARCH_TERM_LENGTH = 3; const val RESULTS_PER_PAGE = 20 diff --git a/app/src/main/res/drawable/ic_core_service_app.png b/app/src/main/res/drawable/ic_core_service_app.png new file mode 100644 index 0000000000000000000000000000000000000000..dc1da2080cb11286faeaf333bbbe0d78e2f9079d GIT binary patch literal 3269 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 are offline. Tap here to retry! PWA + System Apps -- GitLab From 0415c3abeb384241db1cd4b6d02eba64a5611bcd Mon Sep 17 00:00:00 2001 From: Mohit Mali Date: Thu, 29 Oct 2020 10:59:12 +0530 Subject: [PATCH 02/19] Make System app available to be be downloaded from app store --- app/build.gradle | 5 + .../java/foundation/e/apps/MainActivity.kt | 3 +- .../e/apps/api/GitlabDataRequest.kt | 58 +++++ .../e/apps/api/MicroGDataRequest.kt | 97 -------- .../e/apps/api/SystemAppDownloadedRequest.kt | 42 ---- .../apps/application/ApplicationViewHolder.kt | 3 +- .../e/apps/application/model/Application.kt | 74 +----- .../apps/application/model/ApplicationInfo.kt | 6 +- .../e/apps/application/model/Downloader.kt | 32 +-- .../e/apps/application/model/Installer.kt | 10 +- .../model/IntegrityVerificationTask.kt | 2 - .../e/apps/application/model/StateManager.kt | 37 +-- .../apps/application/model/data/BasicData.kt | 2 +- .../e/apps/application/model/data/FullData.kt | 26 +- .../applicationmanager/ApplicationManager.kt | 9 - .../apps/categories/CategoriesListAdapter.kt | 8 - .../category/model/CategoryModel.kt | 19 +- .../viewmodel/CategoriesViewModel.kt | 2 - .../e/apps/settings/SettingsFragment.kt | 17 +- .../systemapps/SystemAppCategoryActivity.kt | 140 ----------- .../SystemApplicationListAdapter.kt | 48 ---- .../systemapps/SystemApplicationViewHolder.kt | 224 ------------------ .../java/foundation/e/apps/utils/Constants.kt | 12 +- .../e/apps/utils/PreferenceStorage.kt | 74 ++++++ .../e/apps/utils/SystemAppDataSource.kt | 68 ++++++ .../main/res/drawable/ic_core_service_app.png | Bin 3269 -> 0 bytes app/src/main/res/values/strings.xml | 4 + app/src/main/res/xml/preferences.xml | 47 ++-- 28 files changed, 334 insertions(+), 735 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt delete mode 100644 app/src/main/java/foundation/e/apps/api/MicroGDataRequest.kt delete mode 100644 app/src/main/java/foundation/e/apps/api/SystemAppDownloadedRequest.kt delete mode 100644 app/src/main/java/foundation/e/apps/systemapps/SystemAppCategoryActivity.kt delete mode 100644 app/src/main/java/foundation/e/apps/systemapps/SystemApplicationListAdapter.kt delete mode 100644 app/src/main/java/foundation/e/apps/systemapps/SystemApplicationViewHolder.kt create mode 100644 app/src/main/java/foundation/e/apps/utils/PreferenceStorage.kt create mode 100644 app/src/main/java/foundation/e/apps/utils/SystemAppDataSource.kt delete mode 100644 app/src/main/res/drawable/ic_core_service_app.png diff --git a/app/build.gradle b/app/build.gradle index 53c170180..ff8a65b73 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,6 +21,9 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } + debug{ + applicationIdSuffix ".debug" + } } dataBinding { enabled = true @@ -87,6 +90,8 @@ dependencies { implementation "com.fasterxml.jackson.core:jackson-databind:2.11.1" implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.11.1" + implementation 'com.android.volley:volley:1.1.1' + implementation 'com.github.chrisbanes:PhotoView:2.3.0' compileOnly files("e-ui-sdk.jar") diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 92ae33b26..95aa6d342 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -75,7 +75,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS companion object { lateinit var mActivity: MainActivity var sharedPreferences : SharedPreferences?=null - + val sharedPrefFile = "kotlinsharedpreference" } @@ -88,7 +88,6 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS disableCategoryIfOpenSource() - bottom_navigation_view.setOnNavigationItemSelectedListener{ if (selectFragment(it.itemId,it)) { disableCategoryIfOpenSource() diff --git a/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt b/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt new file mode 100644 index 000000000..0128892ec --- /dev/null +++ b/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt @@ -0,0 +1,58 @@ +/* + Copyright (C) 2019 e Foundation + + 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 + +import android.content.Context +import com.google.gson.Gson +import com.google.gson.JsonParser +import foundation.e.apps.application.model.Application +import foundation.e.apps.application.model.data.BasicData +import foundation.e.apps.application.model.release.ReleaseData +import foundation.e.apps.applicationmanager.ApplicationManager +import foundation.e.apps.utils.* +import java.io.InputStreamReader + +class GitlabDataRequest { + + + fun requestGmsCoreRelease(callback: (Error?, GitlabDataResult?) -> Unit) { + try { + val url = Constants.RELEASE_API + Constants.MICROG_ID + Constants.RELEASE_ENDPOINT + val urlConnection = Common.createConnection(url, Constants.REQUEST_METHOD_GET) + val isr = InputStreamReader(urlConnection.inputStream) + val element = JsonParser().parse(isr) + + val releaseList: List = Gson().fromJson(element.toString(), + Array::class.java).toList() + + urlConnection.disconnect() + callback.invoke(null, GitlabDataResult(SystemAppDataSource.createDataSource(Constants.MICROG_ID.toString(), + releaseList[0].tag_name, Constants.MICROG_ICON_URI))) + } catch (e: Exception) { + callback.invoke(Error.findError(e), null) + } + } + + class GitlabDataResult(private val data: BasicData) { + fun getApplications(applicationManager: ApplicationManager, context: Context): ArrayList { + return ApplicationParser.parseSystemAppData(applicationManager, context, data) + } + } + + +} diff --git a/app/src/main/java/foundation/e/apps/api/MicroGDataRequest.kt b/app/src/main/java/foundation/e/apps/api/MicroGDataRequest.kt deleted file mode 100644 index c2ce822c5..000000000 --- a/app/src/main/java/foundation/e/apps/api/MicroGDataRequest.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - Copyright (C) 2019 e Foundation - - 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 - -import android.content.Context -import android.util.Log -import android.view.View -import com.android.volley.Request -import com.android.volley.Response -import com.android.volley.toolbox.StringRequest -import com.android.volley.toolbox.Volley -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.annotation.JsonProperty -import com.google.gson.Gson -import com.google.gson.JsonParser -import foundation.e.apps.MainActivity.Companion.mActivity -import foundation.e.apps.application.model.Application -import foundation.e.apps.application.model.data.BasicData -import foundation.e.apps.application.model.release.ReleaseData -import foundation.e.apps.applicationmanager.ApplicationManager -import foundation.e.apps.utils.ApplicationParser -import foundation.e.apps.utils.Common -import foundation.e.apps.utils.Constants -import foundation.e.apps.utils.Error -import kotlinx.android.synthetic.main.activity_category.* -import java.io.BufferedReader -import java.io.InputStreamReader -import java.net.URLEncoder - -class MicroGDataRequest { - - companion object { - private val reader = Common.getObjectMapper().readerFor(MicroGDataRequest::class.java) - } - - fun requestGmsCoreRelease(callback: (Error?, MicroGDataRequest?) -> Unit) { - try { - val url = Constants.GMS_RELEASE_API - val urlConnection = Common.createConnection(url, Constants.REQUEST_METHOD_GET) - val isr = InputStreamReader(urlConnection.inputStream) - val element = JsonParser().parse(isr) - - val releaseList: List = Gson().fromJson(element.toString(), - Array::class.java).toList() - - - val basicData = BasicData( - id = "1", - name = "MicroG", - packageName = Constants.MICROG_PACKAGE, - lastVersionNumber = releaseList[0].tag_name, - lastVersionCode = 0, - latestDownloadableUpdate = "", - armeabi_latestDownloadableUpdate = "", - arm64_v8a_latest_latestDownloadableUpdate = "", - x86_latestDownloadableUpdate = "", - armeabi_v7a_latestDownloadableUpdate = "", - apkArchitecture = ArrayList(), - author = "e-Foundation", - iconUri = "https://gitlab.e.foundation/uploads/-/system/project/avatar/149/ic_core_service_app.png?width=64", - imagesUri = arrayOf(), - privacyRating = 0f, - ratings = BasicData.Ratings(0f, 0f), - category = "System Apps", - is_pwa = false, - x86_64_latestDownloadableUpdate = "" - ) - urlConnection.disconnect() - callback.invoke(null, MicroGDataRequest(basicData)) - } catch (e: Exception) { - callback.invoke(Error.findError(e), null) - } - } - - class MicroGDataRequest(private val data: BasicData) { - fun getApplications(applicationManager: ApplicationManager, context: Context): ArrayList { - return ApplicationParser.parseSystemAppData(applicationManager, context, data) - } - } - - -} diff --git a/app/src/main/java/foundation/e/apps/api/SystemAppDownloadedRequest.kt b/app/src/main/java/foundation/e/apps/api/SystemAppDownloadedRequest.kt deleted file mode 100644 index e9a6364eb..000000000 --- a/app/src/main/java/foundation/e/apps/api/SystemAppDownloadedRequest.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2019 e Foundation - - 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 - -import com.fasterxml.jackson.annotation.JsonCreator -import foundation.e.apps.utils.Common -import foundation.e.apps.utils.Constants - -class SystemAppDownloadedRequest(private val id: String) { - companion object { - private val reader = Common.getObjectMapper().readerFor(Result::class.java) - } - - fun request() { - try { - val url = Constants.SYSTEM_APP_DOWNLOAD_URL + Constants.GMS_ID + "/jobs/artifacts/$id/raw/play-services-core/build/outputs/apk/release/play-services-core-release-unsigned.apk?job=build" - val urlConnection = Common.createConnection(url, Constants.REQUEST_METHOD_GET) - reader.readValue(urlConnection.inputStream) - urlConnection.disconnect() - } catch (e: Exception) { - e.printStackTrace() - } - } - - class Result @JsonCreator - constructor() -} diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt b/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt index 8a2256e74..6010e1a4a 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt @@ -73,7 +73,8 @@ class ApplicationViewHolder(private val activity: Activity, private val view: Vi pwa_icon.visibility = View.GONE view.setOnClickListener { if (application != null) { - applicationViewModel.onApplicationClick(view.context, application!!) + if (application!!.packageName != Constants.MICROG_PACKAGE) + applicationViewModel.onApplicationClick(view.context, application!!) } } diff --git a/app/src/main/java/foundation/e/apps/application/model/Application.kt b/app/src/main/java/foundation/e/apps/application/model/Application.kt index d4f38c5dd..1a47d8972 100644 --- a/app/src/main/java/foundation/e/apps/application/model/Application.kt +++ b/app/src/main/java/foundation/e/apps/application/model/Application.kt @@ -24,7 +24,6 @@ import android.app.DownloadManager import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.util.Log import android.util.TypedValue import androidx.annotation.ColorInt import androidx.appcompat.view.ContextThemeWrapper @@ -38,10 +37,7 @@ import foundation.e.apps.application.model.State.* import foundation.e.apps.application.model.data.* import foundation.e.apps.application.model.release.ReleaseData import foundation.e.apps.applicationmanager.ApplicationManager -import foundation.e.apps.utils.Common -import foundation.e.apps.utils.Constants -import foundation.e.apps.utils.Error -import foundation.e.apps.utils.Execute +import foundation.e.apps.utils.* import java.util.concurrent.atomic.AtomicInteger class Application(val packageName: String, private val applicationManager: ApplicationManager) : @@ -96,17 +92,9 @@ class Application(val packageName: String, private val applicationManager: Appli } } - fun checkForGmsStateUpdate(context: Context) { + private fun checkForSystemAppStateUpdate(context: Context) { if (basicData != null) { - stateManager.findGms(context, basicData!!) - } else if (searchAppsBasicData != null) { - if (searchAppsBasicData!!.is_pwa) { -// stateManager.pwaFind() - } else { - stateManager.searchAppsFind(context, searchAppsBasicData!!) - } - } else if (pwabasicdata != null) { -// stateManager.pwaFind() + stateManager.findSystemApp(context, basicData!!) } } @@ -164,35 +152,6 @@ class Application(val packageName: String, private val applicationManager: Appli checkForStateUpdate(context) } - @Synchronized - fun systemAppButtonClicked(context: Context, activity: Activity?) { - when (stateManager.state) { - INSTALLED -> info.launch(context) - NOT_UPDATED, NOT_DOWNLOADED -> { - if (activity != null) { - if (canWriteStorage(activity)) { - applicationManager.installSystemApp(context, this) - } - } else { - applicationManager.installSystemApp(context, this) - } - } - DOWNLOADING -> { - if (!isInstalling) { - if (downloader != null) { - downloader?.cancelDownload() - } else { - onDownloadComplete(context, DownloadManager.STATUS_FAILED) - } - } - return - } - else -> - return - } - checkForStateUpdate(context) - } - private fun canWriteStorage(activity: Activity): Boolean { return if (android.os.Build.VERSION.SDK_INT >= 23) { if (activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) @@ -209,29 +168,8 @@ class Application(val packageName: String, private val applicationManager: Appli } fun download(context: Context) { - if (basicData?.name == "MicroG") { - downloader = Downloader(info, FullData( - id = "1", - name = "MicroG", - packageName = Constants.MICROG_PACKAGE, - latestVersionNumber = basicData!!.lastVersionNumber, - lastVersionCode = 0, - latestDownloadableUpdate = "", - armeabi_latestDownloadableUpdate = "", - arm64_v8a_latest_latestDownloadableUpdate = "", - x86_latestDownloadableUpdate = "", - armeabi_v7a_latestDownloadableUpdate = "", - apkArchitecture = ArrayList(), - author = "e-Foundation", - iconUri = "https://gitlab.e.foundation/uploads/-/system/project/avatar/149/ic_core_service_app.png?width=64", - imagesUri = arrayOf(), - ratings = BasicData.Ratings(0f, 0f), - is_pwa = false, - x86_64_latestDownloadableUpdate = "", - categoryId = "", - description = "MicroG", - licence = "None" - ), this) + if (basicData?.name == Constants.MICROG) { + downloader = Downloader(info, SystemAppDataSource.getFullData(), this) stateManager.notifyDownloading(downloader!!) downloader!!.downloadSystemApp(context) synchronized(blocker) { @@ -309,7 +247,7 @@ class Application(val packageName: String, private val applicationManager: Appli private fun installSystemApp(context: Context) { isInstalling = true - checkForGmsStateUpdate(context) + checkForSystemAppStateUpdate(context) info.install(context, basicData!!, this) } diff --git a/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt b/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt index f1eb62ccb..6993aa6ac 100644 --- a/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt +++ b/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt @@ -39,12 +39,10 @@ class ApplicationInfo(private val packageName: String) { return true else { val currentVersion = packageInfo.versionName.replace(".", "").replace("-", "") - val mVersion = currentVersion.filter { it.isDigit() } + val currentVersionFiltered = currentVersion.filter { it.isDigit() } val newVersion = lastVersionNumber.replace(".", "") - Log.e("CurrentVersion", mVersion) - Log.e("NewVersion", newVersion) try { - return newVersion.toLong() > mVersion.toLong() + return newVersion.toLong() > currentVersionFiltered.toLong() } catch (e: Exception) { e.printStackTrace() } diff --git a/app/src/main/java/foundation/e/apps/application/model/Downloader.kt b/app/src/main/java/foundation/e/apps/application/model/Downloader.kt index 6b8b68df1..7b8f11251 100644 --- a/app/src/main/java/foundation/e/apps/application/model/Downloader.kt +++ b/app/src/main/java/foundation/e/apps/application/model/Downloader.kt @@ -27,12 +27,16 @@ import android.os.AsyncTask import android.os.Environment import android.util.Log import foundation.e.apps.R +import foundation.e.apps.application.model.data.BasicData import foundation.e.apps.application.model.data.FullData import foundation.e.apps.utils.Constants -class Downloader(private val applicationInfo: ApplicationInfo, private val fullData: FullData, +class Downloader(private val applicationInfo: ApplicationInfo, + private val fullData: FullData, private val downloaderInterface: DownloaderInterface) : IntegrityVerificationCallback { + + private lateinit var downloadManager: DownloadManager private lateinit var request: DownloadManager.Request private var downloadId: Long = 0 @@ -56,7 +60,7 @@ class Downloader(private val applicationInfo: ApplicationInfo, private val fullD } fun download(context: Context) { - if (fullData.getLastVersion() != null) { + if (fullData?.getLastVersion() != null) { downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager registerReceivers(context) initialiseDownloadManagerRequest(context) @@ -92,7 +96,7 @@ class Downloader(private val applicationInfo: ApplicationInfo, private val fullD request = DownloadManager.Request( Uri.parse( - Constants.DOWNLOAD_URL + fullData.getLastVersion()!!.downloadLink)) + Constants.DOWNLOAD_URL + fullData?.getLastVersion()!!.downloadLink)) .apply { setTitle(fullData.basicData.name) setDescription(context.getString(R.string.download_notification_description)) @@ -105,19 +109,16 @@ class Downloader(private val applicationInfo: ApplicationInfo, private val fullD private fun initialiseDownloadManagerRequestForSystemApps(context: Context) { - Log.e("RequestCreated", "True") - Log.e("Url", "https://gitlab.e.foundation/api/v4/projects/149/jobs/artifacts/111.2.3/download?job=build") - request = DownloadManager.Request( Uri.parse( - "https://gitlab.e.foundation/e/apps/GmsCore/-/jobs/121231/artifacts/raw/play-services-core/build/outputs/apk/release/play-services-core-release.apk")) + fullData.latestVersionNumber.let { Constants.downloadBaseUrl(Constants.MICROG_ID.toString(), it) } + Constants.MICROG_ARTIFACTS_PATH)) .apply { - setTitle("MicroG") + setTitle(fullData.name) setDescription(context.getString(R.string.download_notification_description)) setDestinationInExternalFilesDir( context, Environment.DIRECTORY_DOWNLOADS, - applicationInfo.getApkFilename(fullData.basicData)) + fullData.basicData.let { applicationInfo.getApkFilename(it) }) } } @@ -140,7 +141,6 @@ class Downloader(private val applicationInfo: ApplicationInfo, private val fullD val downloadStatus = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) cursor.close() - Log.e("DownLoadStatus", downloadStatus.toString()) if (downloadStatus == DownloadManager.STATUS_SUCCESSFUL || downloadStatus == DownloadManager.STATUS_FAILED) { break @@ -158,11 +158,13 @@ class Downloader(private val applicationInfo: ApplicationInfo, private val fullD unregisterReceivers(context) val status = getDownloadStatus() if (status != null && status == DownloadManager.STATUS_SUCCESSFUL) { - IntegrityVerificationTask( - applicationInfo, - fullData, - this@Downloader) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, context) + fullData?.let { + IntegrityVerificationTask( + applicationInfo, + it, + this@Downloader) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, context) + } } else { downloaderInterface.onDownloadComplete(context, DownloadManager.STATUS_FAILED) } diff --git a/app/src/main/java/foundation/e/apps/application/model/Installer.kt b/app/src/main/java/foundation/e/apps/application/model/Installer.kt index bd398199a..a0952f3f6 100644 --- a/app/src/main/java/foundation/e/apps/application/model/Installer.kt +++ b/app/src/main/java/foundation/e/apps/application/model/Installer.kt @@ -26,7 +26,10 @@ import android.net.Uri import android.util.Log import androidx.core.content.ContextCompat import androidx.core.content.FileProvider +import foundation.e.apps.R import foundation.e.apps.XAPK.FsUtils.deleteFileOrDir +import foundation.e.apps.utils.Constants +import foundation.e.apps.utils.PreferenceStorage import java.io.File import java.io.IOException import java.io.InputStream @@ -141,9 +144,14 @@ class Installer(private val packageName: String, Log.i(TAG, "Broadcast received") var path = apk.absolutePath.split("Download") //delete all APK file after install - deleteFileOrDir(path[0]+"Download"); + deleteFileOrDir(path[0] + "Download"); callback.onInstallationComplete(p0) + + if (packageName == Constants.MICROG_PACKAGE) { + PreferenceStorage(p0).save(p0.getString(R.string.prefs_microg_vrsn_installed), true) + } + } } } diff --git a/app/src/main/java/foundation/e/apps/application/model/IntegrityVerificationTask.kt b/app/src/main/java/foundation/e/apps/application/model/IntegrityVerificationTask.kt index aa70d3e0c..d926bdbef 100644 --- a/app/src/main/java/foundation/e/apps/application/model/IntegrityVerificationTask.kt +++ b/app/src/main/java/foundation/e/apps/application/model/IntegrityVerificationTask.kt @@ -44,8 +44,6 @@ class IntegrityVerificationTask( private var verificationSuccessful: Boolean = false override fun doInBackground(vararg context: Context): Context { -// var file= (applicationInfo.getApkFile(context[0], -// fullData.basicData).absolutePath).length if (fullData.packageName == Constants.MICROG_PACKAGE) { verificationSuccessful = true } else { diff --git a/app/src/main/java/foundation/e/apps/application/model/StateManager.kt b/app/src/main/java/foundation/e/apps/application/model/StateManager.kt index a200230d8..fc9ee4741 100644 --- a/app/src/main/java/foundation/e/apps/application/model/StateManager.kt +++ b/app/src/main/java/foundation/e/apps/application/model/StateManager.kt @@ -19,35 +19,37 @@ package foundation.e.apps.application.model import android.content.Context import android.util.Log +import foundation.e.apps.R import foundation.e.apps.application.model.data.BasicData import foundation.e.apps.application.model.data.SearchAppsBasicData -import foundation.e.apps.application.model.release.ReleaseData import foundation.e.apps.applicationmanager.ApplicationManager +import foundation.e.apps.utils.Constants import foundation.e.apps.utils.Error +import foundation.e.apps.utils.PreferenceStorage import java.util.* class StateManager(private val info: ApplicationInfo, private val app: Application, private val appManager: ApplicationManager) { private var listeners = Collections.synchronizedList(ArrayList()) + var state = State.NOT_DOWNLOADED private set fun find(context: Context, basicData: BasicData) { - if (basicData.name == "MicroG") { + if (basicData.name == Constants.MICROG) { + Log.e("MicroGStatus", PreferenceStorage(context).getBoolean(context.getString(R.string.prefs_microg_vrsn_installed), false).toString()) val state = if (appManager.isInstalling(app) && !app.isInstalling) { State.DOWNLOADING } else if (appManager.isInstalling(app) && app.isInstalling) { State.INSTALLING - } else if (info.isLastVersionInstalled(context, - basicData.getLastVersion() ?: "")) { - State.INSTALLED - } else if (info.isInstalled(context) && !info.isLastVersionInstalled(context, - basicData.getLastVersion() ?: "")) { - State.NOT_UPDATED + } else if (PreferenceStorage(context).getBoolean(context.getString(R.string.prefs_microg_vrsn_installed), false)) { + if (info.isLastVersionInstalled(context, basicData.lastVersionNumber ?: "")) { + State.NOT_UPDATED + } + State.INSTALLED// update } else { - State.NOT_DOWNLOADED + State.NOT_DOWNLOADED // not installed } - Log.e("State", state.name) changeState(state) } else { val state = if (appManager.isInstalling(app) && !app.isInstalling) { @@ -67,18 +69,17 @@ class StateManager(private val info: ApplicationInfo, private val app: Applicati } } - fun findGms(context: Context, basicData: BasicData) { + fun findSystemApp(context: Context, basicData: BasicData) { val state = if (appManager.isInstalling(app) && !app.isInstalling) { State.DOWNLOADING } else if (appManager.isInstalling(app) && app.isInstalling) { State.INSTALLING - } else if (info.isLastVersionInstalled(context, - basicData.lastVersionNumber)) { - State.INSTALLED - } /*else if (info.isInstalled(context) && !info.isLastVersionInstalled(context, - basicData.getLastVersion() ?: "")) { - State.NOT_UPDATED - }*/ else { + } else if (PreferenceStorage(context).getBoolean(context.getString(R.string.prefs_microg_vrsn_installed), false)) { + if (info.isLastVersionInstalled(context, basicData.lastVersionNumber ?: "")) { + State.NOT_UPDATED + } + State.INSTALLED // update + } else { State.NOT_DOWNLOADED } changeState(state) diff --git a/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt b/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt index d9b8b2d27..3251a5e4b 100644 --- a/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt +++ b/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt @@ -55,7 +55,7 @@ constructor(@param:JsonProperty("_id") val id: String, @param:JsonProperty("armeabi-v7a_latest_version_code") var armeabi_v7a_lastVersionCode: Long = -1, @param:JsonProperty("architectures") val apkArchitecture: ArrayList?, @param:JsonProperty("author") val author: String, - @param:JsonProperty("icon_image_path") private val iconUri: String, + @param:JsonProperty("icon_image_path") val iconUri: String, @param:JsonProperty("other_images_path") val imagesUri: Array, @param:JsonProperty("exodus_score") val privacyRating: Float?, @param:JsonProperty("ratings") val ratings: Ratings, diff --git a/app/src/main/java/foundation/e/apps/application/model/data/FullData.kt b/app/src/main/java/foundation/e/apps/application/model/data/FullData.kt index a2786ec4f..d73e9353b 100644 --- a/app/src/main/java/foundation/e/apps/application/model/data/FullData.kt +++ b/app/src/main/java/foundation/e/apps/application/model/data/FullData.kt @@ -26,9 +26,9 @@ import foundation.e.apps.categories.model.Category class FullData @JsonCreator constructor( @JsonProperty("_id") id: String, - @JsonProperty("name") name: String, + @JsonProperty("name") var name: String, @JsonProperty("package_name") packageName: String, - @JsonProperty("latest_version_number") latestVersionNumber: String, + @JsonProperty("latest_version_number") var latestVersionNumber: String, @JsonProperty("latest_version_code") lastVersionCode: Long, @JsonProperty("latest_downloaded_version") latestDownloadableUpdate: String, @JsonProperty("x86_64_latest_downloaded_version") val x86_64_latestDownloadableUpdate: String = "-1", @@ -54,21 +54,21 @@ constructor( @JsonProperty("description") val description: String, @JsonProperty("licence") val licence: String, @JsonProperty("ratings") ratings: BasicData.Ratings?, - @JsonProperty("is_pwa ") val is_pwa: Boolean){ + @JsonProperty("is_pwa ") val is_pwa: Boolean) { var basicData = if (ratings == null) { - BasicData(id, name,packageName, latestVersionNumber,lastVersionCode, latestDownloadableUpdate, - x86_64_latestDownloadableUpdate,x86_64_lastVersionNumber,x86_64_lastVersionCode,armeabi_latestDownloadableUpdate, - armeabi_lastVersionNumber,armeabi_lastVersionCode,arm64_v8a_latest_latestDownloadableUpdate,arm64_v8a_lastVersionNumber,arm64_v8a_lastVersionCode, - x86_latestDownloadableUpdate,x86_lastVersionNumber,x86_lastVersionCode,armeabi_v7a_latestDownloadableUpdate,armeabi_v7a_lastVersionNumber,armeabi_v7a_lastVersionCode,apkArchitecture, - author,iconUri, imagesUri, null,BasicData.Ratings(-1f, -1f), categoryId,is_pwa) + BasicData(id, name, packageName, latestVersionNumber, lastVersionCode, latestDownloadableUpdate, + x86_64_latestDownloadableUpdate, x86_64_lastVersionNumber, x86_64_lastVersionCode, armeabi_latestDownloadableUpdate, + armeabi_lastVersionNumber, armeabi_lastVersionCode, arm64_v8a_latest_latestDownloadableUpdate, arm64_v8a_lastVersionNumber, arm64_v8a_lastVersionCode, + x86_latestDownloadableUpdate, x86_lastVersionNumber, x86_lastVersionCode, armeabi_v7a_latestDownloadableUpdate, armeabi_v7a_lastVersionNumber, armeabi_v7a_lastVersionCode, apkArchitecture, + author, iconUri, imagesUri, null, BasicData.Ratings(-1f, -1f), categoryId, is_pwa) } else { - BasicData(id, name,packageName, latestVersionNumber,lastVersionCode, latestDownloadableUpdate, - x86_64_latestDownloadableUpdate,x86_64_lastVersionNumber,x86_64_lastVersionCode,armeabi_latestDownloadableUpdate, - armeabi_lastVersionNumber,armeabi_lastVersionCode,arm64_v8a_latest_latestDownloadableUpdate,arm64_v8a_lastVersionNumber,arm64_v8a_lastVersionCode, - x86_latestDownloadableUpdate,x86_lastVersionNumber,x86_lastVersionCode,armeabi_v7a_latestDownloadableUpdate,armeabi_v7a_lastVersionNumber,armeabi_v7a_lastVersionCode,apkArchitecture, - author,iconUri, imagesUri, ratings.privacyRating, ratings, categoryId,is_pwa) + BasicData(id, name, packageName, latestVersionNumber, lastVersionCode, latestDownloadableUpdate, + x86_64_latestDownloadableUpdate, x86_64_lastVersionNumber, x86_64_lastVersionCode, armeabi_latestDownloadableUpdate, + armeabi_lastVersionNumber, armeabi_lastVersionCode, arm64_v8a_latest_latestDownloadableUpdate, arm64_v8a_lastVersionNumber, arm64_v8a_lastVersionCode, + x86_latestDownloadableUpdate, x86_lastVersionNumber, x86_lastVersionCode, armeabi_v7a_latestDownloadableUpdate, armeabi_v7a_lastVersionNumber, armeabi_v7a_lastVersionCode, apkArchitecture, + author, iconUri, imagesUri, ratings.privacyRating, ratings, categoryId, is_pwa) } var latestVersion: Version? = null; diff --git a/app/src/main/java/foundation/e/apps/applicationmanager/ApplicationManager.kt b/app/src/main/java/foundation/e/apps/applicationmanager/ApplicationManager.kt index 988c28ce7..1fa9a1960 100644 --- a/app/src/main/java/foundation/e/apps/applicationmanager/ApplicationManager.kt +++ b/app/src/main/java/foundation/e/apps/applicationmanager/ApplicationManager.kt @@ -46,15 +46,6 @@ class ApplicationManager { app.checkForStateUpdate(context) } - @Synchronized - fun installSystemApp(context: Context, app: Application) { - if (!queue.contains(app)) { - queue.put(app) - queue.put(app) - } - app.checkForGmsStateUpdate(context) - } - fun start(context: Context) { Thread { startInstalls(context) diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt index 20d3eb745..eba63c010 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt @@ -19,7 +19,6 @@ package foundation.e.apps.categories import android.annotation.SuppressLint import android.content.Intent -import android.graphics.Color import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -30,7 +29,6 @@ import androidx.recyclerview.widget.RecyclerView import foundation.e.apps.R import foundation.e.apps.categories.category.CategoryActivity import foundation.e.apps.categories.model.Category -import foundation.e.apps.systemapps.SystemAppCategoryActivity import foundation.e.apps.utils.Constants class CategoriesListAdapter(private var categories: ArrayList, color: Int?) @@ -72,16 +70,10 @@ class CategoriesListAdapter(private var categories: ArrayList, color: } holder.categoryTitle.text = categories[position].getTitle() holder.categoryContainer.setOnClickListener { - /* if (categories[position].id == "system_apps") { - val intent = Intent(holder.categoryContainer.context, SystemAppCategoryActivity::class.java) - intent.putExtra(Constants.CATEGORY_KEY, categories[position]) - holder.categoryContainer.context.startActivity(intent) - } else {*/ val intent = Intent(holder.categoryContainer.context, CategoryActivity::class.java) intent.putExtra(Constants.CATEGORY_KEY, categories[position]) intent.putExtra("POSITION", position) holder.categoryContainer.context.startActivity(intent) -// } } } } diff --git a/app/src/main/java/foundation/e/apps/categories/category/model/CategoryModel.kt b/app/src/main/java/foundation/e/apps/categories/category/model/CategoryModel.kt index 43d8755eb..999be771b 100644 --- a/app/src/main/java/foundation/e/apps/categories/category/model/CategoryModel.kt +++ b/app/src/main/java/foundation/e/apps/categories/category/model/CategoryModel.kt @@ -18,12 +18,11 @@ package foundation.e.apps.categories.category.model import android.content.Context -import android.util.Log import androidx.lifecycle.MutableLiveData import foundation.e.apps.MainActivity import foundation.e.apps.api.ListApplicationsRequest import foundation.e.apps.api.ListPwasRequest -import foundation.e.apps.api.MicroGDataRequest +import foundation.e.apps.api.GitlabDataRequest import foundation.e.apps.application.model.Application import foundation.e.apps.applicationmanager.ApplicationManager import foundation.e.apps.utils.Common @@ -54,9 +53,6 @@ class CategoryModel : CategoryModelInterface { }, { if (error == null && apps != null) { val result = ArrayList() - /*categoryApplicationsList.value?.let { - result.addAll(it) - }*/ result.addAll(apps!!) if (apps!!.size != 0) { categoryApplicationsList.value = result @@ -93,25 +89,24 @@ class CategoryModel : CategoryModelInterface { fun loadApplicationsSynced(context: Context): ArrayList? { var listApplications: ListApplicationsRequest.ListApplicationsResult? = null - var microGData: MicroGDataRequest.MicroGDataRequest? = null + var gitlabData: GitlabDataRequest.GitlabDataResult? = null var listPwas: ListPwasRequest.ListPwasResult? = null val appType = MainActivity.mActivity.showApplicationTypePreference() - Log.e("SystemAppRequest", "Yes") if (category == "system_apps") { - MicroGDataRequest() - .requestGmsCoreRelease { applicationError, listMicroGData -> + GitlabDataRequest() + .requestGmsCoreRelease { applicationError, listGitlabData -> when (applicationError) { null -> { - microGData = listMicroGData!! + gitlabData = listGitlabData!! } else -> { error = applicationError } } } - return if (microGData != null) { - microGData!!.getApplications(applicationManager, context) + return if (gitlabData != null) { + gitlabData!!.getApplications(applicationManager, context) } else { null } diff --git a/app/src/main/java/foundation/e/apps/categories/viewmodel/CategoriesViewModel.kt b/app/src/main/java/foundation/e/apps/categories/viewmodel/CategoriesViewModel.kt index a83f89af0..62b33e391 100644 --- a/app/src/main/java/foundation/e/apps/categories/viewmodel/CategoriesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/categories/viewmodel/CategoriesViewModel.kt @@ -19,13 +19,11 @@ package foundation.e.apps.categories.viewmodel import android.content.Context import android.content.Intent -import android.util.Log import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import foundation.e.apps.categories.category.CategoryActivity import foundation.e.apps.categories.model.CategoriesModel import foundation.e.apps.categories.model.Category -import foundation.e.apps.systemapps.SystemAppCategoryActivity import foundation.e.apps.utils.Constants import foundation.e.apps.utils.Error diff --git a/app/src/main/java/foundation/e/apps/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/settings/SettingsFragment.kt index e51cf5345..b66033b7e 100644 --- a/app/src/main/java/foundation/e/apps/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/settings/SettingsFragment.kt @@ -28,12 +28,13 @@ import androidx.preference.PreferenceFragmentCompat import foundation.e.apps.MainActivity import foundation.e.apps.R import foundation.e.apps.updates.UpdatesManager +import foundation.e.apps.utils.PreferenceStorage import java.util.concurrent.Executors import java.util.concurrent.TimeUnit class SettingsFragment : PreferenceFragmentCompat() { - private var oldCheckedPreference: RadioButtonPreference? = null + private var oldCheckedPreference: RadioButtonPreference? = null @SuppressLint("RestrictedApi") @@ -41,6 +42,14 @@ class SettingsFragment : PreferenceFragmentCompat() { // Create preferences setPreferencesFromResource(R.xml.preferences, rootKey) + + val microGInstallState = preferenceManager.findPreference(getString(R.string.prefs_microg_vrsn_installed)) + microGInstallState?.summary = if (context?.let { PreferenceStorage(it).getBoolean(getString(R.string.prefs_microg_vrsn_installed), false) }!!) { + getString(R.string.microg_installed) + } else { + getString(R.string.microg_not_installed) + } + // Handle update check interval changes val updateCheckInterval = preferenceManager.findPreference(getString(R.string.pref_update_interval_key)) as ListPreference @@ -100,13 +109,13 @@ class SettingsFragment : PreferenceFragmentCompat() { private var working_dialog: ProgressDialog? = null - fun backToMainActivity(){ + fun backToMainActivity() { showWorkingDialog() val worker = Executors.newSingleThreadScheduledExecutor() - val task = Runnable { + val task = Runnable { run { removeWorkingDialog() - val intent= Intent(activity,MainActivity::class.java) + val intent = Intent(activity, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK Intent.FLAG_ACTIVITY_NEW_TASK startActivity(intent) diff --git a/app/src/main/java/foundation/e/apps/systemapps/SystemAppCategoryActivity.kt b/app/src/main/java/foundation/e/apps/systemapps/SystemAppCategoryActivity.kt deleted file mode 100644 index 0ce02aff7..000000000 --- a/app/src/main/java/foundation/e/apps/systemapps/SystemAppCategoryActivity.kt +++ /dev/null @@ -1,140 +0,0 @@ -/* - Copyright (C) 2019 e Foundation - - 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.systemapps - -import android.content.pm.PackageManager -import android.os.Bundle -import android.view.MenuItem -import android.view.View -import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.Toolbar -import androidx.recyclerview.widget.LinearLayoutManager -import com.android.volley.Request -import com.android.volley.Response -import com.android.volley.toolbox.StringRequest -import com.android.volley.toolbox.Volley -import com.google.android.material.snackbar.Snackbar -import com.google.gson.Gson -import foundation.e.apps.R -import foundation.e.apps.application.model.Application -import foundation.e.apps.application.model.data.BasicData -import foundation.e.apps.application.model.release.ReleaseData -import foundation.e.apps.applicationmanager.ApplicationManager -import foundation.e.apps.applicationmanager.ApplicationManagerServiceConnection -import foundation.e.apps.applicationmanager.ApplicationManagerServiceConnectionCallback -import foundation.e.apps.utils.Constants -import kotlinx.android.synthetic.main.activity_category.* -import kotlinx.android.synthetic.main.error_layout.* - -class SystemAppCategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectionCallback { - - val systemAppsList = ArrayList() - private val applicationManagerServiceConnection = - ApplicationManagerServiceConnection(this) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_category) - - val toolbar = findViewById(R.id.toolbar) - setSupportActionBar(toolbar) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - supportActionBar?.title = getString(R.string.system_apps) - - error_container.visibility = View.GONE - progress_bar.visibility = View.VISIBLE - - app_list.layoutManager = LinearLayoutManager(this) - app_list.adapter = SystemApplicationListAdapter(this, systemAppsList) - - applicationManagerServiceConnection.bindService(this) - - loadReleaseData() - - } - - private fun loadReleaseData() { - - // Instantiate the RequestQueue. - val queue = Volley.newRequestQueue(this) - val url = "https://gitlab.e.foundation/api/v4/projects/149/releases" - - // Request a string response from the provided URL. - val stringRequest = StringRequest(Request.Method.GET, url, - Response.Listener { response -> - progress_bar.visibility = View.GONE - val releaseList: List = Gson().fromJson(response, - Array::class.java).toList() - val application = Application(Constants.MICROG_PACKAGE, ApplicationManager()) - val basicData = BasicData( - id = "1", - name = "MicroG", - packageName = Constants.MICROG_PACKAGE, - lastVersionNumber = releaseList[0].tag_name, - lastVersionCode = 0, - latestDownloadableUpdate = "", - armeabi_latestDownloadableUpdate = "", - arm64_v8a_latest_latestDownloadableUpdate = "", - x86_latestDownloadableUpdate = "", - armeabi_v7a_latestDownloadableUpdate = "", - apkArchitecture = ArrayList(), - author = "e-Foundation", - iconUri = "https://gitlab.e.foundation/uploads/-/system/project/avatar/149/ic_core_service_app.png?width=64", - imagesUri = arrayOf(), - privacyRating = 0f, - ratings = BasicData.Ratings(0f, 0f), - category = "System Apps", - is_pwa = false, - x86_64_latestDownloadableUpdate = "" - ) - application.basicData = basicData - systemAppsList.add(application) - app_list.adapter?.notifyDataSetChanged() - }, - Response.ErrorListener { - progress_bar.visibility = View.GONE - }) - - // Add the request to the RequestQueue. - queue.add(stringRequest) - - } - - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { - android.R.id.home -> - finish() - } - return true - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, - grantResults: IntArray) { - if (requestCode == Constants.STORAGE_PERMISSION_REQUEST_CODE && - grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_DENIED) { - Snackbar.make(container, R.string.error_storage_permission_denied, - Snackbar.LENGTH_LONG).show() - } - } - - override fun onServiceBind(applicationManager: ApplicationManager) { - - } - - -} diff --git a/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationListAdapter.kt b/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationListAdapter.kt deleted file mode 100644 index b973a56a7..000000000 --- a/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationListAdapter.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright (C) 2019 e Foundation - - 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.systemapps - -import android.app.Activity -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import foundation.e.apps.R -import foundation.e.apps.application.model.Application -import foundation.e.apps.utils.Constants -import kotlinx.android.synthetic.main.application_list_item.view.* - -class SystemApplicationListAdapter(private val activity: Activity, - private val applicationList: List) : - RecyclerView.Adapter() { - - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SystemApplicationViewHolder { - val listItemContainer = LayoutInflater.from(parent.context).inflate(R.layout.application_list_item, parent, false) - return SystemApplicationViewHolder(activity, listItemContainer, 0) - } - - override fun onBindViewHolder(holder: SystemApplicationViewHolder, position: Int) { - holder.createApplicationView(applicationList[position]) - } - - override fun getItemCount() = applicationList.size - - -} diff --git a/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationViewHolder.kt b/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationViewHolder.kt deleted file mode 100644 index 452cc72cf..000000000 --- a/app/src/main/java/foundation/e/apps/systemapps/SystemApplicationViewHolder.kt +++ /dev/null @@ -1,224 +0,0 @@ -/* - Copyright (C) 2019 e Foundation - - 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.systemapps - -import android.annotation.SuppressLint -import android.app.Activity -import android.graphics.Bitmap -import android.graphics.Color -import android.view.Gravity -import android.view.View -import android.widget.Button -import android.widget.ImageView -import android.widget.RatingBar -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.snackbar.Snackbar -import foundation.e.apps.R -import foundation.e.apps.application.model.Application -import foundation.e.apps.application.model.ApplicationStateListener -import foundation.e.apps.application.model.Downloader -import foundation.e.apps.application.model.State -import foundation.e.apps.application.model.data.BasicData -import foundation.e.apps.application.model.data.PwasBasicData -import foundation.e.apps.application.viewmodel.ApplicationViewModel -import foundation.e.apps.utils.Common -import foundation.e.apps.utils.Common.toMiB -import foundation.e.apps.utils.Error -import foundation.e.apps.utils.Execute -import kotlinx.android.synthetic.main.application_list_item.view.* -import kotlinx.android.synthetic.main.install_button_layout.view.* - - -class SystemApplicationViewHolder(private val activity: Activity, private val view: View, accentColorOS: Int) : - RecyclerView.ViewHolder(view), - ApplicationStateListener, - Downloader.DownloadProgressCallback, - BasicData.IconLoaderCallback, - PwasBasicData.IconLoaderCallback { - - - private val icon: ImageView = view.app_icon - private val title: TextView = view.app_title - private val pwa_icon: TextView = view.pwa_sympol - private val author: TextView = view.app_author - private val ratingBar: RatingBar = view.app_rating_bar - private val rating: TextView = view.app_rating - private val privacyScore: TextView = view.app_privacy_score - private var installButton: Button = view.app_install - private var application: Application? = null - private val applicationViewModel = ApplicationViewModel() - private var downloader: Downloader? = null - var accentColorOS = accentColorOS; - - init { - pwa_icon.visibility = View.GONE - view.setOnClickListener { - if (application != null) { - applicationViewModel.onApplicationClick(view.context, application!!) - } - } - - - installButton.setTextColor(Color.parseColor("#ffffff")) - if (0 != this.accentColorOS) { - installButton.setBackgroundColor(this.accentColorOS) - } - installButton?.setOnClickListener { - if (application?.fullData != null && - application!!.fullData!!.getLastVersion() == null) { - Snackbar.make(view, activity.getString( - Error.APK_UNAVAILABLE.description), - Snackbar.LENGTH_LONG).show() - } else if (application?.pwabasicdata != null) { - application?.pwaInstall(activity) - } else if (application?.searchAppsBasicData != null && application?.searchAppsBasicData!!.is_pwa) { - application?.pwaInstall(activity) - } else { - application?.systemAppButtonClicked(activity, activity) - } - } - } - - fun createApplicationView(app: Application) { - - pwa_icon.visibility = View.GONE - this.application = app - - if (app.basicData != null) { - this.application?.removeListener(this) - this.application = app - icon.setImageDrawable(view.context.resources.getDrawable(R.drawable.ic_app_default)) - application!!.loadSystemIcon(this) - application!!.addListener(this) - title.text = application!!.basicData!!.name - author.text = application!!.basicData!!.author - ratingBar.rating = application!!.basicData!!.ratings!!.rating!! - if (application!!.basicData!!.ratings!!.rating != -1f) { - rating.text = application!!.basicData!!.ratings!!.rating.toString() - } else { - rating.text = activity.getString(R.string.not_available) - } - if (application!!.basicData!!.privacyRating != null && application!!.basicData!!.privacyRating != -1f) { - privacyScore.text = application!!.basicData!!.privacyRating.toString() - } else { - privacyScore.text = activity.getString(R.string.not_available) - } - } else { - this.application?.removeListener(this) - this.application = app - icon.setImageDrawable(view.context.resources.getDrawable(R.drawable.ic_app_default)) - application!!.addListener(this) - if (application!!.searchAppsBasicData != null) { - if (application!!.searchAppsBasicData!!.is_pwa) { - pwa_icon.visibility = View.VISIBLE - } - application!!.SearchAppsloadIcon(this) - title.text = application!!.searchAppsBasicData!!.name - author.text = application!!.searchAppsBasicData!!.author - } else { - application!!.PwaloadIcon(this) - title.text = application!!.pwabasicdata!!.name - - } - } - stateChanged(application!!.state) - } - - override fun onIconLoaded(application: Application, bitmap: Bitmap) { - if (this.application != null && application == this.application) { - icon.setImageBitmap(bitmap) - } - } - - override fun stateChanged(state: State) { - Execute({}, { - - // installButton.setBackgroundResource(R.drawable.app_install_border_simple) - installButton?.text = activity.getString(state.installButtonTextId) - when (state) { - - State.NOT_DOWNLOADED -> { - if (0 != this.accentColorOS) { - installButton.setTextColor(this.accentColorOS) - } else { - - installButton.setTextColor(Color.parseColor("#0088ED")) - } - installButton.setBackgroundResource(R.drawable.app_install_border_simple) - installButton.isEnabled = true - } - - State.INSTALLED -> { - installButton?.isEnabled = - Common.appHasLaunchActivity(activity, application!!.packageName) - if (0 != this.accentColorOS) { - installButton!!.setBackgroundColor(this.accentColorOS) - } else { - installButton!!.setBackgroundResource(R.drawable.app_install_border) - } - installButton.setTextColor(Color.parseColor("#FAFAFA")) - - } - State.INSTALLING -> { - installButton?.isEnabled = false - } - State.NOT_UPDATED -> { - installButton.setTextColor(Color.parseColor("#FAFAFA")) - //installButton!!.setBackgroundResource(R.drawable.app_install_border) - if (0 != this.accentColorOS) { - installButton!!.setBackgroundColor(this.accentColorOS) - } else { - installButton!!.setBackgroundResource(R.drawable.app_install_border) - } - installButton?.isEnabled = true - } - else -> { - installButton.setTextColor(Color.parseColor("#0088ED")) - installButton?.isEnabled = true - } - } - - }) - } - - override fun downloading(downloader: Downloader) { - this.downloader = downloader - this.downloader!!.addListener(this) - } - - @SuppressLint("SetTextI18n") - override fun notifyDownloadProgress(count: Int, total: Int) { - installButton.setGravity(Gravity.CENTER) - installButton?.text = ((toMiB(count) / toMiB(total)) * 100).toInt().toString() + "%" - installButton.setTextColor(Color.parseColor("#0088ED")) - installButton.setBackgroundResource(R.drawable.app_installing_border_simple) - } - - override fun anErrorHasOccurred(error: Error) { - Snackbar.make(activity.findViewById(R.id.container), - activity.getString(error.description), - Snackbar.LENGTH_LONG).show() - } - - fun onViewRecycled() { - downloader?.removeListener(this) - downloader = null - } - -} diff --git a/app/src/main/java/foundation/e/apps/utils/Constants.kt b/app/src/main/java/foundation/e/apps/utils/Constants.kt index 50b3029ca..4168966f4 100644 --- a/app/src/main/java/foundation/e/apps/utils/Constants.kt +++ b/app/src/main/java/foundation/e/apps/utils/Constants.kt @@ -22,7 +22,8 @@ object Constants { // Global const val BASE_URL = "https://api.cleanapk.org/v2/" const val SYSTEM_APP_DOWNLOAD_URL = "https://gitlab.e.foundation/api/v4/projects/" - const val GMS_RELEASE_API = "https://gitlab.e.foundation/api/v4/projects/149/releases" + const val RELEASE_API = "https://gitlab.e.foundation/api/v4/projects/" + const val RELEASE_ENDPOINT = "/releases" const val DOWNLOAD_URL = "https://apk.cleanapk.org/" const val STORAGE_PERMISSION_REQUEST_CODE = 0 const val CONNECT_TIMEOUT = 30000 // 30 seconds @@ -32,8 +33,14 @@ object Constants { //microG Package const val MICROG_PACKAGE = "com.google.android.gms" - const val GMS_ID = 149 + const val MICROG_ID = 149 + const val MICROG = "MicroG" + const val MICROG_ICON_URI = "https://gitlab.e.foundation/uploads/-/system/project/avatar/149/ic_core_service_app.png?width=64" + const val MICROG_ARTIFACTS_PATH = "play-services-core/build/outputs/apk/release/play-services-core-release.apk?job=build" + fun downloadBaseUrl(projectId:String,tagName: String): String { + return "https://gitlab.e.foundation/api/v4/projects/$projectId/jobs/artifacts/$tagName/raw/" + } // Search const val MIN_SEARCH_TERM_LENGTH = 3; @@ -49,6 +56,7 @@ object Constants { // Categories const val CATEGORY_KEY = "category_key" + const val SYSTEM_APPS = "System Apps" // Home const val CURRENTLY_SELECTED_FRAGMENT_KEY = "currently_selected_fragment" diff --git a/app/src/main/java/foundation/e/apps/utils/PreferenceStorage.kt b/app/src/main/java/foundation/e/apps/utils/PreferenceStorage.kt new file mode 100644 index 000000000..833b8d7ec --- /dev/null +++ b/app/src/main/java/foundation/e/apps/utils/PreferenceStorage.kt @@ -0,0 +1,74 @@ +package foundation.e.apps.utils + +import android.content.Context +import android.content.SharedPreferences +import foundation.e.apps.MainActivity + +class PreferenceStorage(val context: Context) { + + private val sharedPref: SharedPreferences = context.getSharedPreferences(MainActivity.sharedPrefFile, Context.MODE_PRIVATE) + + + fun save(KEY_NAME: String, text: String) { + + val editor: SharedPreferences.Editor = sharedPref.edit() + + editor.putString(KEY_NAME, text) + + editor!!.commit() + } + + fun save(KEY_NAME: String, value: Int) { + val editor: SharedPreferences.Editor = sharedPref.edit() + + editor.putInt(KEY_NAME, value) + + editor.commit() + } + + fun save(KEY_NAME: String, status: Boolean) { + + val editor: SharedPreferences.Editor = sharedPref.edit() + + editor.putBoolean(KEY_NAME, status!!) + + editor.commit() + } + + fun getStringValue(KEY_NAME: String): String? { + + return sharedPref.getString(KEY_NAME, null) + + + } + + fun getInt(KEY_NAME: String): Int { + + return sharedPref.getInt(KEY_NAME, 0) + } + + fun getBoolean(KEY_NAME: String, defaultValue: Boolean): Boolean { + + return sharedPref.getBoolean(KEY_NAME, defaultValue) + + } + + fun clearSharedPreference() { + + val editor: SharedPreferences.Editor = sharedPref.edit() + + //sharedPref = PreferenceManager.getDefaultSharedPreferences(context); + + editor.clear() + editor.commit() + } + + fun removeValue(KEY_NAME: String) { + + val editor: SharedPreferences.Editor = sharedPref.edit() + + editor.remove(KEY_NAME) + editor.commit() + } + +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/utils/SystemAppDataSource.kt b/app/src/main/java/foundation/e/apps/utils/SystemAppDataSource.kt new file mode 100644 index 000000000..d41dbd5ac --- /dev/null +++ b/app/src/main/java/foundation/e/apps/utils/SystemAppDataSource.kt @@ -0,0 +1,68 @@ +package foundation.e.apps.utils + +import foundation.e.apps.application.model.data.BasicData +import foundation.e.apps.application.model.data.FullData + +object SystemAppDataSource { + + lateinit var basicData: BasicData + + fun createDataSource(id: String, tag: String, iconUri: String): BasicData { + basicData = BasicData( + id = id, + name = Constants.MICROG, + packageName = Constants.MICROG_PACKAGE, + lastVersionNumber = tag, + lastVersionCode = 0, + latestDownloadableUpdate = "", + armeabi_latestDownloadableUpdate = "", + arm64_v8a_latest_latestDownloadableUpdate = "", + x86_latestDownloadableUpdate = "", + armeabi_v7a_latestDownloadableUpdate = "", + apkArchitecture = ArrayList(), + author = "e-Foundation", + iconUri = iconUri, + imagesUri = arrayOf(), + privacyRating = 0f, + ratings = BasicData.Ratings(0f, 0f), + category = Constants.SYSTEM_APPS, + is_pwa = false, + x86_64_latestDownloadableUpdate = "" + ) + return basicData + } + + fun getFullData(): FullData { + return FullData( + basicData.id, + basicData.name, + basicData.packageName, + basicData.lastVersionNumber, + basicData.arm64_v8a_lastVersionCode, + basicData.arm64_v8a_latest_latestDownloadableUpdate, + basicData.x86_64_latestDownloadableUpdate, + basicData.x86_64_lastVersionNumber, + basicData.x86_64_lastVersionCode, + basicData.armeabi_latestDownloadableUpdate, + basicData.armeabi_lastVersionNumber, + basicData.armeabi_lastVersionCode, + basicData.arm64_v8a_latest_latestDownloadableUpdate, + basicData.arm64_v8a_lastVersionNumber, basicData.arm64_v8a_lastVersionCode, + basicData.x86_latestDownloadableUpdate, + basicData.x86_lastVersionNumber, + basicData.x86_lastVersionCode, + basicData.armeabi_v7a_latestDownloadableUpdate, + basicData.armeabi_v7a_lastVersionNumber, + basicData.armeabi_v7a_lastVersionCode, + basicData.apkArchitecture, + basicData.author, + basicData.iconUri, + basicData.imagesUri, basicData.id, "", "", basicData.ratings, false + ) + } + + fun basicData(): BasicData { + return basicData + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_core_service_app.png b/app/src/main/res/drawable/ic_core_service_app.png deleted file mode 100644 index dc1da2080cb11286faeaf333bbbe0d78e2f9079d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3269 zcmeAS@N?(olHy`uVBq!ia0y~yU`POA4mJh`hDS5XEf^ShdOTemLn`9l&dn_dxhi@5 z|Mz!y&u%)Yc5_0@1c5VNm4O*+r>#mBg|SBY7(2!nWY$ zm&Fr~y=oUb#yGKXMTED}!o%L$B`&fXXGz>kYB<8O`E=UkvubJO@88;#7f;_idvEpm zyE&Wm-^@JOuJU|d_4)th=RVs!e|qn*;`~di7^Yv_{XD@k!9;t)v)j+B z9poHliF$l~JNX^sB$ba2_d5GOPqye2o}8E|(V*g~woCAM;lh->8LO|WtnOB1n54of zbDY)F#dFdbhBU?v7uN68TI09ZW4|FoxMd%A&ugKJ9j?M@3@$UG_NsU;WthjjLCit7 zp{bDJOzK9P2(c#%-P}w62^|n+$Y=hce7umkD@>;}LC81r=OmT8Tn`i)S|_Ol>L=_x z&O5RFaf;@7K7RiC`+3{9yGD1rFuh>v$ok30@PDdG=aJ=k?$;+NpHpv;F|bVPzW?6- z`n79i=VQz7K23Ycd4|Dh;f1n!&+ApYlsf&t#ecsg-NVDk(bL--Syf#fUt%?PwmNTv zlP}l$TeoiMbc^X$O@F*&z4s&+zla?NkEE_$x5hU=|NZgX+w=GTo*EwaQCliYbixKv zZ5C@Qt2x|aIu^Q~L6!_$_D=&$=BsH4JgU5R<^KKmd6}6z^Z)+(I{(#ICNYLbFE208 zkKI+Gd9%J+&f!p+I&;D9yUy)=vQaw|54Z6jt8REUqm#9v+r>h0F+VYzp9e600Uy1u|RBbBk?*|TRyv#+hOoXNf*yX2|dgQA@^)!*O6UcGU{ zgF)z=)Dy-RSy>u~ds!Vs{%*3e$v0kdNhIxDErZH0(eHc}waW!2u_=Dote9YyV|*$w zc%vYbf1XyZ=61#(?&lYNex)zSQOD>hR}<$iQ*lG*`=P|Yl3LngOS&E2F>$b*U%2?W zBui7`=EYOF&Ugo8-frpow0;S*(>tae6<;kYYM0M+DLwN;vx{5q;F+1mGe0di2!6-( zJ96~iS%IaAYP}Wf1g;*fZ#!_gDb~VQxFv?|19wMI68oBtv&Cj1;_m|( zXQ**hGN^13V~E;XWRVe@c`9fcqr{5?9XI|7bs<`c=_z{99Mk40}gL$X{&miU6>meP)!zr9l9 z-KElbZC~W|1(qN7OcmoyJ-g^=zvx6~Mja>FHLOoptiEG)b7`LrL&1}F2lchDR(EH!E4( z?sD!f-*r|ne;2#o@oOFTHT3xSpGgF_G5?6(`KIEl(AO62&(9T1&OTdFy+p+QxBQWB z`8KPnT$lxlm>I6lQz$vV+A(0pytnSPi;vzYdFB@)s(0jm_@#B;rAaLltQkZ&E?*R0 z*E1#Qw|e=5p5+|cY1fajIxJjs*Kf9y(6T#B3mj&69k0pDP@9*(TUNIDVd~imhDFB( ztptkr89I$3B0IgeUs-BnwP@P@r;C>getgW>Vcy}*plHtM(`wmeW_{63br<^);rO4? zfq{3nKEJ~{!K+v#yX+C8hswT5_IxdO*d}lli=2ORgsI~5ZGN8*TcSRD8VVH2A4oYf zaoWv`%o7TX`X2{GM63{*_>f`O|JKHduM2JUdmH8V@f`eX$5r)@F>gz}{+aGS|8LES z`f_G*Cik{NC#B7kIU45uIJNb$fRPLX!}Qat_Qg3zS|kxoGHct9oX+6# zBWu#jwlHkyl4n)?%y{AOuDim|n^$aV`W`GYw@PTw`pJ?``#-1Eyjydn<$`roPp-WF zRQ5=n&P58J7~PJ_pO@Ft(mK_5cIQQbMcUbA`7v_TU#xf__DAD`K_A=AoVqu@j~Ej~ z?5i0sOx6}yvB~(~ccwM6^+r4jPjrtzxP7|d#KR+-SD$*|oup+g*1-5Nl|gN?XW601 ztIQ%xZRdSR;&pX;mRI$8zAfwT^yCj8_HxwaEte2)-yhbMds^njM#<=_ooopKH9tdD zo{BDA!=m7BGW}lb-xq%$9Aletb@{aSwVUm9>O!n+*Gu@{kI&<1Vp8DzsH~=uUCnT; zty-iO_X#N?$2S&BdDM?l}vkyqa=N;ocZij4UgA0rop6RxU zNEIEZF1XqAeV>wS*SBgm|8~#2+dSx5;)JPox1T1YoN%K zVundZ)=?}%J;Fka2^_x4fdPJuGuGU^_(3Q_F;AEy@Wg@-oO~L=2JdNHT)vxtzdy(>KwtNiN>-X1I>!pgs zKW+K`_V)BfzaJ^e7ybz;oSHvBY-vKhC>#I%=@m)3lCHfm$}l+ zlq~KBWq%|i^xg%YoRqhDu}#m<fp4dsHmuEYvT6)n)~uc#dfAdt|J#&Co%01 zRIhn(pz+GvKD&Yk2M)Ehwf!%Uai1?g=M%F`josVx!D%~YPGbD>^=oC>`+Iw^v0yt#XEQYtZ;NZm=q}DEOXrVC#Td$g_IpXN>)wTWBL8h&(G=7Qc_{_ zY^%Rz1qV;Qa^=dBozLCMSKru}e7xn?uV070efxI*%zXR*nUhb72)It<)4senO!-CP zzhaXXAMtn*S5JYK1?Qg|&$lYg`hII`wsgO&wOLGjynW#_Lodcy!Q+e(*VjgGFF4*O z%f5Qms%KnH0t+T9W?Zqmn%*hc;v+VHp6{aOBp)?R37%QEZ{Oa%J?G}9=4;okeOtC{ z*~DebmYrl&m?&TWr||ab_4}fBRepY!Z~O6x@c#Ad)~#FmUiHJCjcf0p@wHcb{9(hd z=Cw1QEttx&Zu92N)#vBg&Mtp@t2F8CEK}ySYuA2Rc)2BB>dITj$59;JV!B!{U%V*L ziQblzmz@0g{9M21bNSknmnm?Zik!42RrP3Miprmro{OjHPyLyEYEr+Eh^yg)j}pE{+brG4wx=~aC{{;a^_&FSg- z>$UXs#7_J(oMyZ-XQ9CDbuT^d9yxGS=J?0D{q_Zqj&!b;>u*=v_NTP8bRS6Hka=A@SrC>&pyTz$B2pPiKNq#r)J zdwaXwe|na9dwO;r&DuKaq-4MKlQn@lq5P)`W#$H|_V)F$?TORx^s`SXD~Ya4xiJ07 zVGZ`mQxgLwr9L^8s$%x{1bda#+AFTap here to retry! PWA System Apps + Microg Covid 19 Version + pref_microg_installed + Not Installed + Installed diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index addd6437a..4f297ce02 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -22,8 +22,7 @@ android:layout_height="match_parent"> - + + android:title="@string/preference_update_interval_title" /> + android:layout="@layout/preference_divider" + android:selectable="false" /> + android:title="@string/preference_update_notify_available_title" /> + android:layout="@layout/preference_divider" + android:selectable="false" /> + android:title="@string/preference_update_install_automatically_title" /> + android:layout="@layout/preference_divider" + android:selectable="false" /> + android:title="@string/preference_update_wifi_only_title" /> + android:layout="@layout/preference_divider" + android:selectable="false" /> + + + + android:selectable="false" /> + android:title="@string/Show_all_apps" /> Date: Thu, 19 Nov 2020 10:08:15 +0530 Subject: [PATCH 03/19] Some refactoring of code --- .../java/foundation/e/apps/api/GitlabDataRequest.kt | 2 +- .../e/apps/application/model/ApplicationInfo.kt | 3 +++ .../foundation/e/apps/application/model/Downloader.kt | 2 +- .../e/apps/application/model/StateManager.kt | 6 ++++-- .../e/apps/application/model/data/BasicData.kt | 3 ++- .../e/apps/application/model/data/FullData.kt | 3 ++- .../e/apps/application/model/release/Assets.kt | 2 +- .../e/apps/application/model/release/Links.kt | 11 +++++++++++ .../e/apps/application/model/release/ReleaseData.kt | 1 + .../foundation/e/apps/utils/SystemAppDataSource.kt | 8 +++++--- 10 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/foundation/e/apps/application/model/release/Links.kt diff --git a/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt b/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt index 0128892ec..2d067f94b 100644 --- a/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt +++ b/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt @@ -42,7 +42,7 @@ class GitlabDataRequest { urlConnection.disconnect() callback.invoke(null, GitlabDataResult(SystemAppDataSource.createDataSource(Constants.MICROG_ID.toString(), - releaseList[0].tag_name, Constants.MICROG_ICON_URI))) + releaseList[0].tag_name, Constants.MICROG_ICON_URI, releaseList[0].assets.links[0].url))) } catch (e: Exception) { callback.invoke(Error.findError(e), null) } diff --git a/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt b/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt index 6993aa6ac..9af7e2e67 100644 --- a/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt +++ b/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt @@ -41,6 +41,9 @@ class ApplicationInfo(private val packageName: String) { val currentVersion = packageInfo.versionName.replace(".", "").replace("-", "") val currentVersionFiltered = currentVersion.filter { it.isDigit() } val newVersion = lastVersionNumber.replace(".", "") + Log.e("CurrentVersion", currentVersionFiltered) + Log.e("NewVersion", newVersion) + Log.e("IsLarge", (newVersion.toLong() > currentVersionFiltered.toLong()).toString()) try { return newVersion.toLong() > currentVersionFiltered.toLong() } catch (e: Exception) { diff --git a/app/src/main/java/foundation/e/apps/application/model/Downloader.kt b/app/src/main/java/foundation/e/apps/application/model/Downloader.kt index 7b8f11251..6c3b51bd3 100644 --- a/app/src/main/java/foundation/e/apps/application/model/Downloader.kt +++ b/app/src/main/java/foundation/e/apps/application/model/Downloader.kt @@ -111,7 +111,7 @@ class Downloader(private val applicationInfo: ApplicationInfo, request = DownloadManager.Request( Uri.parse( - fullData.latestVersionNumber.let { Constants.downloadBaseUrl(Constants.MICROG_ID.toString(), it) } + Constants.MICROG_ARTIFACTS_PATH)) + fullData.downloadUrl)) .apply { setTitle(fullData.name) setDescription(context.getString(R.string.download_notification_description)) diff --git a/app/src/main/java/foundation/e/apps/application/model/StateManager.kt b/app/src/main/java/foundation/e/apps/application/model/StateManager.kt index fc9ee4741..9a65e0775 100644 --- a/app/src/main/java/foundation/e/apps/application/model/StateManager.kt +++ b/app/src/main/java/foundation/e/apps/application/model/StateManager.kt @@ -45,8 +45,9 @@ class StateManager(private val info: ApplicationInfo, private val app: Applicati } else if (PreferenceStorage(context).getBoolean(context.getString(R.string.prefs_microg_vrsn_installed), false)) { if (info.isLastVersionInstalled(context, basicData.lastVersionNumber ?: "")) { State.NOT_UPDATED + } else { + State.INSTALLED } - State.INSTALLED// update } else { State.NOT_DOWNLOADED // not installed } @@ -77,8 +78,9 @@ class StateManager(private val info: ApplicationInfo, private val app: Applicati } else if (PreferenceStorage(context).getBoolean(context.getString(R.string.prefs_microg_vrsn_installed), false)) { if (info.isLastVersionInstalled(context, basicData.lastVersionNumber ?: "")) { State.NOT_UPDATED + } else { + State.INSTALLED } - State.INSTALLED // update } else { State.NOT_DOWNLOADED } diff --git a/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt b/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt index 3251a5e4b..7af04f1d5 100644 --- a/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt +++ b/app/src/main/java/foundation/e/apps/application/model/data/BasicData.kt @@ -60,7 +60,8 @@ constructor(@param:JsonProperty("_id") val id: String, @param:JsonProperty("exodus_score") val privacyRating: Float?, @param:JsonProperty("ratings") val ratings: Ratings, @param:JsonProperty("category") val category: String, - @param:JsonProperty("is_pwa") val is_pwa: Boolean) { + @param:JsonProperty("is_pwa") val is_pwa: Boolean, + var downloadUrl: String? = null) { private var icon: Bitmap? = null diff --git a/app/src/main/java/foundation/e/apps/application/model/data/FullData.kt b/app/src/main/java/foundation/e/apps/application/model/data/FullData.kt index d73e9353b..62d58568f 100644 --- a/app/src/main/java/foundation/e/apps/application/model/data/FullData.kt +++ b/app/src/main/java/foundation/e/apps/application/model/data/FullData.kt @@ -54,7 +54,8 @@ constructor( @JsonProperty("description") val description: String, @JsonProperty("licence") val licence: String, @JsonProperty("ratings") ratings: BasicData.Ratings?, - @JsonProperty("is_pwa ") val is_pwa: Boolean) { + @JsonProperty("is_pwa ") val is_pwa: Boolean, + var downloadUrl: String? = null) { var basicData = if (ratings == null) { diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt b/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt index 91768b9ba..cf9f3df91 100644 --- a/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt +++ b/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt @@ -19,6 +19,6 @@ data class Assets ( @SerializedName("count") val count : Int, @SerializedName("sources") val sources : List, - @SerializedName("links") val links : List, + @SerializedName("links") val links : List, @SerializedName("evidence_file_path") val evidence_file_path : String ) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Links.kt b/app/src/main/java/foundation/e/apps/application/model/release/Links.kt new file mode 100644 index 000000000..c49360291 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/application/model/release/Links.kt @@ -0,0 +1,11 @@ +package foundation.e.apps.application.model.release + +import com.google.gson.annotations.SerializedName + +data class Links( + @field:SerializedName("id") val id: Int, + @field:SerializedName("name") val name: String, + @field:SerializedName("url") val url: String, + @field:SerializedName("direct_asset_url") val direct_asset_url: String, + @field:SerializedName("external") val external: String +) \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt b/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt index 78a700f9d..b4bff7057 100644 --- a/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt +++ b/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt @@ -23,6 +23,7 @@ class ReleaseData( @SerializedName("released_at") val released_at: String, @SerializedName("author") val author: Author, @SerializedName("commit") val commit: Commit, + @SerializedName("assets") val assets: Assets, @SerializedName("upcoming_release") val upcoming_release: Boolean, @SerializedName("commit_path") val commit_path: String, @SerializedName("tag_path") val tag_path: String, diff --git a/app/src/main/java/foundation/e/apps/utils/SystemAppDataSource.kt b/app/src/main/java/foundation/e/apps/utils/SystemAppDataSource.kt index d41dbd5ac..ac60e87f8 100644 --- a/app/src/main/java/foundation/e/apps/utils/SystemAppDataSource.kt +++ b/app/src/main/java/foundation/e/apps/utils/SystemAppDataSource.kt @@ -7,7 +7,7 @@ object SystemAppDataSource { lateinit var basicData: BasicData - fun createDataSource(id: String, tag: String, iconUri: String): BasicData { + fun createDataSource(id: String, tag: String, iconUri: String, downloadUrl: String): BasicData { basicData = BasicData( id = id, name = Constants.MICROG, @@ -27,7 +27,8 @@ object SystemAppDataSource { ratings = BasicData.Ratings(0f, 0f), category = Constants.SYSTEM_APPS, is_pwa = false, - x86_64_latestDownloadableUpdate = "" + x86_64_latestDownloadableUpdate = "", + downloadUrl = downloadUrl ) return basicData } @@ -57,7 +58,8 @@ object SystemAppDataSource { basicData.apkArchitecture, basicData.author, basicData.iconUri, - basicData.imagesUri, basicData.id, "", "", basicData.ratings, false + basicData.imagesUri, basicData.id, "", "", basicData.ratings, false, + downloadUrl = basicData.downloadUrl ) } -- GitLab From f4f3d3e3c16068c0416b8cc35040a76fb8679f6d Mon Sep 17 00:00:00 2001 From: Mohit Mali Date: Thu, 19 Nov 2020 13:21:20 +0530 Subject: [PATCH 04/19] Refactoring of code --- app/build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ff8a65b73..e81b83bb0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,9 +21,6 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } - debug{ - applicationIdSuffix ".debug" - } } dataBinding { enabled = true -- GitLab From 4b9a3121fce8658af7ba57760be6a38809ea1901 Mon Sep 17 00:00:00 2001 From: Mohit Mali Date: Fri, 27 Nov 2020 17:52:43 +0530 Subject: [PATCH 05/19] Change "Microg" app name to "MicroG with contact tracing framework" --- .../e/apps/application/model/release/Assets.kt | 12 ------------ .../e/apps/application/model/release/Author.kt | 11 ----------- .../e/apps/application/model/release/Commit.kt | 11 ----------- .../e/apps/application/model/release/Evidences.kt | 11 ----------- .../e/apps/application/model/release/ReleaseData.kt | 11 ----------- .../e/apps/application/model/release/Sources.kt | 11 ----------- .../e/apps/application/model/release/_links.kt | 11 ----------- .../main/java/foundation/e/apps/utils/Constants.kt | 6 +----- 8 files changed, 1 insertion(+), 83 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt b/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt index cf9f3df91..e1345e5ce 100644 --- a/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt +++ b/app/src/main/java/foundation/e/apps/application/model/release/Assets.kt @@ -3,18 +3,6 @@ package foundation.e.apps.application.model.release import com.google.gson.annotations.SerializedName import foundation.e.apps.application.model.release.Sources -/* -Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ - - data class Assets ( @SerializedName("count") val count : Int, diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Author.kt b/app/src/main/java/foundation/e/apps/application/model/release/Author.kt index 840da41a2..ad1a48fd2 100644 --- a/app/src/main/java/foundation/e/apps/application/model/release/Author.kt +++ b/app/src/main/java/foundation/e/apps/application/model/release/Author.kt @@ -2,17 +2,6 @@ package foundation.e.apps.application.model.release import com.google.gson.annotations.SerializedName -/* -Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ - data class Author ( diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Commit.kt b/app/src/main/java/foundation/e/apps/application/model/release/Commit.kt index 4b0877f86..14d169761 100644 --- a/app/src/main/java/foundation/e/apps/application/model/release/Commit.kt +++ b/app/src/main/java/foundation/e/apps/application/model/release/Commit.kt @@ -2,17 +2,6 @@ package foundation.e.apps.application.model.release import com.google.gson.annotations.SerializedName -/* -Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ - data class Commit ( diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Evidences.kt b/app/src/main/java/foundation/e/apps/application/model/release/Evidences.kt index 71fd0f7e1..c910b241e 100644 --- a/app/src/main/java/foundation/e/apps/application/model/release/Evidences.kt +++ b/app/src/main/java/foundation/e/apps/application/model/release/Evidences.kt @@ -2,17 +2,6 @@ package foundation.e.apps.application.model.release import com.google.gson.annotations.SerializedName -/* -Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ - data class Evidences ( diff --git a/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt b/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt index b4bff7057..836861989 100644 --- a/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt +++ b/app/src/main/java/foundation/e/apps/application/model/release/ReleaseData.kt @@ -2,17 +2,6 @@ package foundation.e.apps.application.model.release import com.google.gson.annotations.SerializedName -/* -Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ - class ReleaseData( @SerializedName("name") val name: String, diff --git a/app/src/main/java/foundation/e/apps/application/model/release/Sources.kt b/app/src/main/java/foundation/e/apps/application/model/release/Sources.kt index e2183f5f8..8444ed18a 100644 --- a/app/src/main/java/foundation/e/apps/application/model/release/Sources.kt +++ b/app/src/main/java/foundation/e/apps/application/model/release/Sources.kt @@ -2,17 +2,6 @@ package foundation.e.apps.application.model.release import com.google.gson.annotations.SerializedName -/* -Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ - data class Sources ( diff --git a/app/src/main/java/foundation/e/apps/application/model/release/_links.kt b/app/src/main/java/foundation/e/apps/application/model/release/_links.kt index 66bb3a8cf..fec4878db 100644 --- a/app/src/main/java/foundation/e/apps/application/model/release/_links.kt +++ b/app/src/main/java/foundation/e/apps/application/model/release/_links.kt @@ -2,17 +2,6 @@ package foundation.e.apps.application.model.release import com.google.gson.annotations.SerializedName -/* -Copyright (c) 2020 Kotlin Data Classes Generated from JSON powered by http://www.json2kotlin.com - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -For support, please feel free to contact me at https://www.linkedin.com/in/syedabsar */ - data class _links ( diff --git a/app/src/main/java/foundation/e/apps/utils/Constants.kt b/app/src/main/java/foundation/e/apps/utils/Constants.kt index 4168966f4..77ea2d40a 100644 --- a/app/src/main/java/foundation/e/apps/utils/Constants.kt +++ b/app/src/main/java/foundation/e/apps/utils/Constants.kt @@ -34,13 +34,9 @@ object Constants { //microG Package const val MICROG_PACKAGE = "com.google.android.gms" const val MICROG_ID = 149 - const val MICROG = "MicroG" + const val MICROG = "MicroG with contact tracing framework" const val MICROG_ICON_URI = "https://gitlab.e.foundation/uploads/-/system/project/avatar/149/ic_core_service_app.png?width=64" - const val MICROG_ARTIFACTS_PATH = "play-services-core/build/outputs/apk/release/play-services-core-release.apk?job=build" - fun downloadBaseUrl(projectId:String,tagName: String): String { - return "https://gitlab.e.foundation/api/v4/projects/$projectId/jobs/artifacts/$tagName/raw/" - } // Search const val MIN_SEARCH_TERM_LENGTH = 3; -- GitLab From e606ffbccf865e0ad21c80295184c435d5df83a8 Mon Sep 17 00:00:00 2001 From: Romain Hunault Date: Fri, 27 Nov 2020 18:36:39 +0100 Subject: [PATCH 06/19] First draft to detect the correct microG version --- .../e/apps/api/GitlabDataRequest.kt | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt b/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt index 2d067f94b..7b26507c0 100644 --- a/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt +++ b/app/src/main/java/foundation/e/apps/api/GitlabDataRequest.kt @@ -18,10 +18,12 @@ package foundation.e.apps.api import android.content.Context +import android.util.Log import com.google.gson.Gson import com.google.gson.JsonParser import foundation.e.apps.application.model.Application import foundation.e.apps.application.model.data.BasicData +import foundation.e.apps.application.model.release.Links import foundation.e.apps.application.model.release.ReleaseData import foundation.e.apps.applicationmanager.ApplicationManager import foundation.e.apps.utils.* @@ -30,22 +32,31 @@ import java.io.InputStreamReader class GitlabDataRequest { - fun requestGmsCoreRelease(callback: (Error?, GitlabDataResult?) -> Unit) { - try { - val url = Constants.RELEASE_API + Constants.MICROG_ID + Constants.RELEASE_ENDPOINT - val urlConnection = Common.createConnection(url, Constants.REQUEST_METHOD_GET) - val isr = InputStreamReader(urlConnection.inputStream) - val element = JsonParser().parse(isr) + fun requestGmsCoreRelease(callback: (Error?, GitlabDataResult?) -> Unit) = try { + val url = Constants.RELEASE_API + Constants.MICROG_ID + Constants.RELEASE_ENDPOINT + val urlConnection = Common.createConnection(url, Constants.REQUEST_METHOD_GET) + val isr = InputStreamReader(urlConnection.inputStream) + val element = JsonParser().parse(isr) - val releaseList: List = Gson().fromJson(element.toString(), - Array::class.java).toList() + val releaseList: List = Gson().fromJson(element.toString(), + Array::class.java).toList() - urlConnection.disconnect() - callback.invoke(null, GitlabDataResult(SystemAppDataSource.createDataSource(Constants.MICROG_ID.toString(), - releaseList[0].tag_name, Constants.MICROG_ICON_URI, releaseList[0].assets.links[0].url))) - } catch (e: Exception) { - callback.invoke(Error.findError(e), null) + urlConnection.disconnect() + + val osReleaseType = "test" + var releaseUrl = "" + + releaseList[0].assets.links.forEach { + if (it.name.contains(osReleaseType)) { + releaseUrl = it.url + } } + + callback.invoke(null, GitlabDataResult(SystemAppDataSource.createDataSource(Constants.MICROG_ID.toString(), + releaseList[0].tag_name, Constants.MICROG_ICON_URI, releaseUrl))) + + } catch (e: Exception) { + callback.invoke(Error.findError(e), null) } class GitlabDataResult(private val data: BasicData) { -- GitLab From 8949ce2fc436ba74f5fcd99f6b584ee611b1ee19 Mon Sep 17 00:00:00 2001 From: Romain Hunault Date: Fri, 27 Nov 2020 23:08:54 +0100 Subject: [PATCH 07/19] Add regex to extract version number from tag --- .../foundation/e/apps/application/model/ApplicationInfo.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt b/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt index 9af7e2e67..8e72b0426 100644 --- a/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt +++ b/app/src/main/java/foundation/e/apps/application/model/ApplicationInfo.kt @@ -40,7 +40,11 @@ class ApplicationInfo(private val packageName: String) { else { val currentVersion = packageInfo.versionName.replace(".", "").replace("-", "") val currentVersionFiltered = currentVersion.filter { it.isDigit() } - val newVersion = lastVersionNumber.replace(".", "") + val regex = "-v(.*)-".toRegex() + val matchResult = regex.find(lastVersionNumber) + val (tagVersionNumber) = matchResult!!.destructured + val newVersion = tagVersionNumber.replace(".", "") + Log.e("CurrentVersion", currentVersionFiltered) Log.e("NewVersion", newVersion) Log.e("IsLarge", (newVersion.toLong() > currentVersionFiltered.toLong()).toString()) -- GitLab From 9070d298bffb66705bbc4af969e5eaada2e7010e Mon Sep 17 00:00:00 2001 From: Romain Hunault Date: Fri, 27 Nov 2020 23:14:29 +0100 Subject: [PATCH 08/19] Revert "Setup /e/ colors" This reverts commit c71bbb36a9b8d94bb9fa84352457c31c3bc92d4d. --- app/build.gradle | 6 ------ app/e-ui-sdk.jar | Bin 118347 -> 0 bytes .../java/foundation/e/apps/MainActivity.kt | 11 +++++++---- .../e/apps/application/ApplicationActivity.kt | 8 +++++++- .../e/apps/application/model/Application.kt | 8 ++++++-- .../e/apps/categories/CategoriesFragment.kt | 10 ++++++---- .../categories/category/CategoryActivity.kt | 2 +- app/src/main/res/values/colors.xml | 13 +------------ 8 files changed, 28 insertions(+), 30 deletions(-) delete mode 100644 app/e-ui-sdk.jar diff --git a/app/build.gradle b/app/build.gradle index e81b83bb0..065eccfd1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,10 +36,6 @@ android { androidExtensions { experimental = true } - aaptOptions{ - additionalParameters '-I', 'app/e-ui-sdk.jar' - } - } dependencies { @@ -90,6 +86,4 @@ dependencies { implementation 'com.android.volley:volley:1.1.1' implementation 'com.github.chrisbanes:PhotoView:2.3.0' - - compileOnly files("e-ui-sdk.jar") } diff --git a/app/e-ui-sdk.jar b/app/e-ui-sdk.jar deleted file mode 100644 index 222953d20421211ecc08ca2e2f16e735263d2f58..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 118347 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 _KvD1#em|lE0a%a&y?{~MQOebCMF-x6u z=k@71zKM&^c3l>7>vc=F-{f<9fn!`|WzzM_{nMi0akYo%(=jSq&LU(Y_A zth&|8=b?Yto;%Y{w)9Hd>n+?DmEHOJ%1P~wraD<$vR7RXlH{peB9$$#YhM@e_NRSk z^+d_HmG|f0eQ7z#DrG{}^x5X;-*37nweGv*;;*fNyO+B4+T<11>Md5yxTuuQwA;C` zCoNPuH&FHyvyoT86X|^y-LA2}otJiY-VeF-o-+5K?B-ZyvvXKz6h5EE}_>;TNv0ni`&Uzv9g9d6n!bUtd)& zJ==F;&c(~0IGr}7o!#^9W8cYXy2VQ?Y670g$n@_zDLQp+^#AG6HmX0*nfG&?+*Mkv zdPZBOHhtC~r;TxY-It!?JA8b}=avVHy-VBY=P$6+`cjjA$VT1!e#R-;nLAg{D-U_4 zJ*Rxvn+_hXQ=g;uU0kvL(7l&?l?^R!Jb(3g@rKOH>bnn$Htuu$?Q(H>_>A?lcHT3Z(_{c$KU;m|0kRAk|=YsJgVh>%D{yzu4o|0h^tV_;=5rId|pG#u}#L zH765t7DRx`j^Du-7)(X{Ws88t<0#k(KSU-Scj_XYVPs#KFixKn37ef>>h_L;L^ zcg4xPcUYU2r5T^IV1~2k$1B0HPus;;y*6eLl(=#A%>s7U)7N5;Tcw^oX>WDxNVG%n zE3@Xkj!PER>MgR^(#D${w@ErF=zH5NU!$EGi|T)^35^iZxNtU$fAV||?ZmEN5zR;c zw%k}!Ta=T0J}cbipu^A9S7wvS!+*@H_u0eB!}BI@)5jTW)b6eoGd;A@DmC=UaRI)o zNt;fto3Od%YF^QdIeR$!3U0Z)G`hRiOj74uI#Xp^>YS*v>2TM+yPxM-GQYZOf;?%dtrab(a zI8AQ#r^We`mAb@UYuX>VwmYn(a@+EovIaYuoc{mNNp}bie(yd-zPrCPs6?Yyzv^pY zeX`MnYs;$cPTv*krubLp*)I?K(#dxhe(l#WN`JVh%%>-tSbi@p-yO1c*6*mY z>?c2NT;I<7==srl1BcVynTH_vYcIL@EnsrR;>f7KkP(C&lfN!+16 zk42-J8v4v;KCPK5yEb`lmup;T#xt4DF^D@WcS6gFD@@Caqx|wRN%-&iV;3uv%aV4+O%?DXoT(!#cUK&Mg zbv-?QWhU!x`H2qG=RcH4y*~3Pf5+hzy?Il$cOOYGw0p-{bir=_WwoUB+hkY#<%q}@ znP(eRc+2P465s6Tke@SGFX}C;Jy2=7@89O+>J1&$Z}(5hy*5o~_qVBcRqPK1rl@X- zycfGIPA~b!v0(dM9~7NG>)+Ws<93X#?)MnK_UzJ0^J7ow_r6hhA~JQ?^>;tbB6uYJ zq};r<`SaH2FG88OuMV4ibw*g@hexaQu1qbeQU)FZLPh?f%Nmeq5iiYT@Ber+xXD zTxYXwk!|+(Inf$*(f)hC!AY-KJPXsbHy@UGX8M-L={MicXWI5(&02T<3i-!!DCJvk z;Kk=VkKdoO`rzK850dNqZv1ylyY*+`lj+N4T>RQk%Fdd$=eYO(n%!~#YwllVdYJaS zVs-5OJ(-u+*p|+AwV#rB{aaDlz4|4~EKU>_c+^>*(mYZdr|x5zwcBc^?zwsGpZFX5 z=Em$ib*R`>aOvhlhE{7fncb33(6XBKqc_zDGXF+B6f18C}W&Z#DpZf-` z-#M?N+xM`($}+t5#IR-Ewt&)}z@<~~EMK$eX2zbk_)#fSTk*L* z?~h%%%j0tY_Udl|Q#H~qT`4X4o*u^gshRcavUpyO7par;cmD1PTzoU)d*Q8$*GgW~ z-%j3jE2$)7yF_Hin@`7&eDr=4{>}5Q?J+@h?;zeO2Y#zfYJ4h)Rrz@&GDS4T*ZbnaEiAn^cfT*TSrArweW6vg$o6IHHo9(} zq4g#I8@q-t@2a}Kb6ML~9OF1Dw6IicqKNYTM8OZ1&qY!a+XCj?GL*i4+A-|yX5H}O zuy1w2sS_k;2ZyizWU_eD=brv=K{ZZ)T;7W>eiFFcaAo!xL!)UupG+p#ZE&0ScX6jk zx6LfiK%+HN3YdK}9=@G)ZO*#eJB;S)!R5zK z9elq+b(YE3b?&dEBp>mut=$`S@$%y{0Y6pM8|G`w{llSY9eeoL&!wMRO3RYwe~y;@ zv@W4<`}TcXPZeI=u4S6_E9T7NKL?!tKJIyTy{lzI*w~ z<>DfC4-?^yMNR?9x~poQpGr%<6W#oDR?ej+)8~J;B{-X#HXS^id-0vqE`B*Nk<{Qr zvYU=?jdZ&=%a&_XzWI?Q>&#kK1->cQxg~hCp!UGcD{lfd+IYUNYFM53&UvcYcTTt5 zJ<^v_{)nhmozOkoEb!+Yd7WK0v)$RAT`?`ITy$R| z(^UHDJo`Jv0tXiL7W|yM+PTT~cA5VK*5XOKMVn*JdVDTD|Jbk6C2sXjnH9er^>4D4 z#E4`!_OPo)J=-63JH~~*J3RiZws`Gg1>;XL6OCLe{eph3D?Qk@uSjshTs3)5yY>IV zyh>$fy}$91v-^a0@x^=xx$@n7QH7hg3C(=mb2I(%)Bcq?JWjeDPj`!4^I&84wqw-i zzxjOX{CyENmD@{Sd`Q{(1ET83xA#;cw?E} z!vpJIduc4c{%=)T*{!YXp4>PVx>~P&k-9+9u||z3pWS-ei%%6l`JefqgN=7yfk=nc zx{m3hYB!o=+_~RIEq?8JYHD%oeE$s}pLNCDIM_Q;d-64nD0Q=|$5&@Z8N9O9J~j1w z*C7S}mX5BewaLfto|$rHUU#3ZIqIc3{Z9rJl}SEO$aOglQ=XzHHXcPRWoH_?V;*^T zS(!VpKI`IqOYG9biuXGu_iQ`5;QqN|k5_I{vE475shOGWdQ@S(OaJ-sYc+pwa;7go zaqD!xS(V&khs1fsZ*O|0tDn9;x$>LH%-J9GCv3CT^^Cix&3#dCzHNn{+3_Rt*E*e4 zx8`p3!MBeOcJNBk1a;^-KIC zX3m>2W8ROtsz*=09QpgQGuWk*>)!P8rT#Z2&$H09E!?@{qV{|4@JVK=D*9gj9_J!V zO^ZK%`@;D?bLXU|zrM8ahELj-a>mcozrx=3VM5iRLtlPyE;@Sqp5$Ba`5QEC3%Blh z{$J_x>W|$d++m){85`Wl^Ygs=_y;TzRuVGj@qfK-)??P zTN5>D-i}M5f|0qp&7Z#32${H;VT!0@A}dr z+2**nO0wd>mzJ;Z%AQEtPw~{={?Yf< zdre*6==$U5_1Q09*=<*=PBPZ6biFpmbCHke{MVXW!ppL^b@_kUJ8P>&{4K7QPmL*W|ox;JO>W{xh`yt0%vokp1EmOZK`o z(TnRR&MWZv{r=6DmZzT*h3-wfsu7UjusyBpr+Wj3icP{^Z-JItq z@9Z}ly$%?Asn@9N&ig;@H2br(GZ7E;{?2{#Lq8(m|Cxpw=eShA} zHD4@GR$u87-B1>UTBratqX%Tcjs|E*?s8s%=y^YQbav)WeW;q*y{g^#bYUS5Chj^pQa z*&DMA)0bI`Y?clc(OFsg^34~{;MDmlZ^VzjK6T9R#$r|PeVhITJgc#cPTRX#DfyOM z?iIZ$xgr57wuzdYdheqZR(=0EZ+p!C4yF8iTht!MR@rD4f17hgG&rSToyV!Dt>NdL zRu@jHxoCPZ^XIg@r&Gf}J$3e8pL1*K-7W8~ua~i!VAz}Cb83dw+gFD|7v)s1U#%Ir znLka(^V65Iv`f2Ib3YHb@RYfIi{~BnbuJsDZhrqX$677sp7lvvw>!$$K4krB`5Kis zId#gXt#3Ltqh@4owhFb~pi+El&X<|smpt^Z+J-``!(d%4|TQ1`L;4<6mfKfBJHeSf;d==jdcV}Jf9#H!jD^ZD-C zq~_?>EVpppgA>IYbuZp1Ka*d;8#MDd+giS!v#FmF)!tmKx^BI(EY$9K+nbA@Lzw5A zYzmvbW#L|>^iy87^9-Io{=M7Dyxjl&#YwqSECp`9-jg)2y@8($}+x2TMw;dM=y#D6aN?(g5{QavZ zr&q^aG^_aa#4Prlz^2yinI8>3f4{fB#j~+fR4Mrz(@o$0wFb;DJG)LVR){h!KKM*M zZ}M^Rhp&Ru=BRtW|GT8F?$grM|Y? zxo@4yO_A_ZEs}49)~WfNxv@PsFEJ*X{d}8iw!fv|lh_ac-*+}d+Mn+_sh_sX{J4B( zu=BdDk$o)Jqh|j6_v%cmuuHsu%WdCfa*foY_r@>JlHGs7 z`0lffb0=Q)zOpR+`)t*lAGNpKHqR)Hcs{pTBg%Pq`O@s!ld7{LcE;c1jM%Y-#d6xU z<2<7O51iheo>phzUAHm+-_yO9UC)L`T&T5bO})I8BflWrE!zI+k?AXreyP`<80&Iv z&B;Uh4QDksZ{Fwn)n?h9oHYF_xB3=I`?)vVtagH7YU#OyKW_ZG zZ2C)jgZuZK1WDVQXa5FEG)oFEzjw!e>RO%n__2rup8T zEV=*M)rNPRQSyQ>IkjVcyZ=9Mrhk(0^u7A~Z|s@7%IMhUX(}%)4XV$5tWmyy<%wx@ zy8i*|vds5KId+$Cd0DybdUeY69N(+PVQb&oRm_;T{7k_qzn%N9T3g#443^YiTZm49Mf|NIZf!F0~WyNZl%ED-f)*tdFZ*zA>hSBuP3g=VJv#3`q{ zNNH4uUVOId&Cc9zv)t;k8`5_QH(nDyAuc>U#%E5k;m5t*o$|3hF(rl{o4Y%;_0-hj zePU`13-290@^g>UA>o$q{&xB*=l4(ebFp>G%uoAI-qv5D65|oKw05`F@9rs@QC5|? z-1jSDJu*&BUBa#TQGAkk*7}LJ&GtRGdQyCndA!HHLdBmKTA!>H4^sPlDWr10^}>ID zwPw1c*~EMAa6NB2qg&WkRXv+CXv?9U#S^C$?NgtA@@8uG(ifJwpO+Y&hz?RaDYGSH z>x+-vCp*O_U61#eS*KVjyS^)G@m8kmPb;k6hW(UiulXFY)OOPI!sQw3)iX@zzFhgC zeV*5+wHh&=+KH>nGTZ++-P6fW;JPZObad{^DqY`6yPq!G@cLAq-=zMK#>r(A^RjAGTeQ)y7jMOI{bph4;nm>t_duKMP zyguXI{>AP3Zr}b!*{I&Vi;skEZ+_QzchOSsLVKSw{@1pPLu1Wq%ijM!(zD{q9|b zR=sdOb9L*=$xH_s#ELuB{5*xi9|dyFJZO&TlTX_tia43wL>a zc7^DU!+)Q={t_a;YkqqBHLZ6$PG8+r*(0ZW=kEPoO7~(DYYk4NeZJ!Vp2vS*yIkp2 z(=(G|RCle~ddl?76OE|#sY=|UrHA+K|66a;b;6;f^ZvB&A3mOquP>eY`)cz&<_90k zUk7f#c~`*r?%r3nnomVNg}1iMyKsJA^V~-DEEg9i-S<3l(TivHSIMm}C^TQ86K;1@ zr+ixUo#3v0&rU>Dxf^F4Qq5Ct8ocb~*Y74jYSw={BY0xn z`uPdh_;nu7(=xAKrg-thJGmFPYj!!kp7a0YiJ5cmxqU9MWa_piMSVnnGjQwu^d&5l15L8l%-ufa z__L&^=NHSiS-8(@H%>mW&Oamv8)%b!u|pr|IomFDI^Av)HC6bNlMD)w;)T z8XSGIEBK$oo#*djp6^V0`f|gLHV#TtLNul z@F~1_OR#0#-FcU-+~+Cvez0;~wRM?oR?i<@&TdCJ_EVo-R@N=qlIT2Nc~RxO%~$3d z9lyUaxb|GcE&1tg#};e%zSobbT6(h2EButW`%|0Vtm3ab9L*9}X#Ky}EoNvl<@qGr z((*N`(>@BR`e*WPJ?fe&Z@e^hPyftm-~Ta}-g+64g{@<({+8 zQg>hQI;ijd>@!~%=tfuZ<+Yuozbi+IdA)ID_bYX)qDzBT=ptvo^@yH6{Bq4 zz&C-5egrGtYxcgfdi~t!Pcq?Oj?|oU_MSWMr=9%Itnx(zI|NffKUbMCKVC_4#h!*#!6MZfAxUbt|`(8L%@l(vU zvUS?;nUmv|?|KsKK5PC0lf5V3I>sG8UUg#PQH$bPTkVxz>7DyHrXmtYgTl34q0V$ z=3%KrzrpmzOta%Rr48@yU(mmS?dEZ>czjnIn@%H`opMO5?lj(f*OXlfm z^?$BUPu;rgW<1T8CujbCm%x>iX4UREv)tF^=cjv%Z#}RK3tM_?aa-S?zw-@mIeO%l zRJ)ZKFMsa+P39fP$;CGgxo+t;@(aNg^jM z_x%6pC-Dy_>`zME8qw2bc?b~vqq-##9>_jf% zO^RnEWmmSiO7Z?VK4I6zys)26-Y!d|f+vf-3)r&%@Ea3J<fi4!^11nL zRnkV5nZeUf$H-pe_^IA#UG~cF<&w{R46T!L&B8U^oQ?>{Wd06 zY)&mANb`EPN2KlMJqqU^|7)+7>AEU%D%|m+U$D!PKgKWotBlV}NT#S}Ml5+1 zy5dZ5bxCvVRw5-i4zu5Sn<13wNaNe5M5l}jwTuw>1VmDNw) z8{SG~zVTH4j!N0tPx)3Zab58*-syh(qb8PlRA|Z9iSvwXf=b&mzphp7N#1pOzp!9p z*xV>TlN~a;pBCzL)l7bP!M%NrM~iOhVpliU9r+)mf~Mbo`e7n}V%QUX?~_>{_x|zT zwaIqJMCp*grK`du4y*M%-!7dp{iW;aK5mT*wxv6LMKo^MmS)eGY93;{<&CmZ<^LmfTr&(c{HBH@ zMT0BcA}=)mdsMEs+ba^TfBKed_WqDO&$}y3>m+iQ-SyGlz;E<2GGlV9(AxIJwxyLT zazvh8o1|Jjv#09z%BtAaq3=_JeG^0c`9;hYORqKB8Mu0)M&PFD`Ij$g>eBC`Q=H=Y=51;Fts{x>WL`}_53%bop1MTvJrPmy?y$A|LL!9-p!hklk|goN&keu z>W5u!uieqU{F<9z^@=@HJvuHZ9h|u9s#waD{qAd7-DlYGY_asKzo_tk(?prRgGnz= z{J*;=`1_7$%M%2}CdOYoGTm(AN&7sJlhsN;qaVJ@-oo{MZ$)6X$T9BPgo|2lCpaHp z@O7ivh7RAO>!x0*cc@csy}ZwB%j)A#zRk_s%xd`6GIsLnx&yJ-4u4iTsFadX_tW*n z_HUa`^AyPiO1u&KuBDoMaos=pd-rrK`m|!jV>dGT&p7y?(npdhZ}t1nwL1jO^N#%eSHE_ubLcdc+4p4co(P?p{rB^hO=0WqpPP{Ml6U$N&BxkL zrkuJZdE{AoT`Z@L&f43{x*S?f&cAb3v{#F(S}D}hbF%H!!#m5C%(`;l;*lr!bCZOb zCzqV=+_C9~lcHFs;p0^W?VUHJVx~qOQg~ge>w9-`zdu`?uDi6-(XHLjbFErMcBbA< zS?0BJNzK;QmdjtBeLe8OR-flw#nCI*V!8ScoUoXb`fQ`ds`V|mwsRQ26*V-ozjblp zhg$(VcXkFYU0S&5Caa<3&4$9I>v-Q6pASDG-6P1jn?F#ngZsQc&&h>Fs!{<46?@|h z4V3;~N*3bMzar>#Y?V)6u{tM{)2gK}{B}mio%|HLB;KV>WX@KtJ?3-0*TybQO*|RQ zDz{|S{!hk}U-7Q@(yr8=nm;Y9!9&@yLC842((<@g=|SOZw*Gcok19>mv`lFVJ9;*~ zEb!~Z#D9A(Sp-CPANqLcc(e@P^?ACBSWX>DKE`LYp10^us^`q58c$|5MtEJ%PCdA> z`;;o5L5ht)$~3?AlR^`YMV=148!|yx+4bQZv2ID>oz_#i-EJ2=?O$P1drYj&-F-?h zdxMl$WAO64shgZrH=Z?;{(17f*^H03cC7HvQw}xaFkbyy(O>J!7vY`uzF$;M7L|x@ zTWxoQu@i>o2NJJd#G($U=y&nuUSAJ}4h+KT} zTpOD^qiKrLTW(HT$-oKH=l4 z%fJ%UJ;N%u`eU$~i$tikk9t7G`ZcF-sC=*DE=_9tvYks+Kr-}2kBOG(M6G!@1Wk0s zoEA*WICDUB(zG7uJ38#9MK;Y|*G!L2Sy?l!>vf0f>6DNOr=^c@UzvFP@-lhOqi=35 zsj&5M=i8`OmRmY0qWb?5H5Q?ciibxN48Cf~%Fbo}{%F;az<-Z#NQPU@oY_)SyrHb{ z`rVM%)5F=_Vr!*VghmK!hZTfGWM4a38e{OC{Zm6ku&raFh*n2~?NLqp>{ewzi~dg0 z?>_7L4$jh_G0|E3PC)2}l1GOUpPo7?#_{N2`?|$^0^ZYDf+{M#M7zbNPp_)mCR~1Z zYN_U%;G$JJ6Wryyy(0bzJUv??cqK(w-(S&MBfZE#91Bv{&DuQ{RT!R zyXuz|FY!OaJ1;h&r=)+Mt&v4h;+MG>%kIcY)mp2Ct>y21A-~h&lDUz~|K5GM51f}9 zUb6gkGDG@U{E_9`E^arR{;pTdjlXve^MP)|jSt=htm>X+cu7*Yyv_KfhyDe@Ma_nW z*=0YjT{!U}quNKOuH?zyiF&hmZ}s=RleYCxDeM2zyN&7KgBO7hq+TXjF|L)K%++4ZuJ~H6Tz|xNm&uA3 zi84F54feX6df}+Hwe*WZr$$;&O+!BGyd{FndDgri(Xl z&jl{J_#}CtN z0uD{s#jn@5X~_xGrAiT2hZk2{$Tftp^d7ca)mf34@I*u7#Wbti=j#sFtz#|gd|M## zZ&Nh8`Af#%r6o#-8k?^Q8n_wwU0gT!&XW}j8IzaH(}=|WR5xxe=*kQ9TCAb|`0o>?uZA&!YARPn zCEX6^#BwcGn$LOEd6U4M1u60q9-nY6eEn#mu;}8$jzw+4vl2d*@Wt~@Og&NHd)=v%g9JcY4}t=E=v^%tx7>~cPOS@MGR z!98cs{n=Kr$Fberb*;#=+jVO~Z3+%7s$l*rwW~>SVlYF8S9-NK)B6|dNk4aeTJt1y z*# zIjM9@X=3cIiL>fHJzKov&fnC}@7HE0zrOUPxwNk=MRjxAs~;(EwBpW4tpB`E>6fK> zZj#8{MPBX#LDqinR_@fhn{#sImHP&HJ0CT+U%R-|=ckOUMPzjRo)^zDpR>iU*?#$_ z%!6wm1Me1EIn{~JT{h{O^|P3bfAaF|r)|A!vmmW|Imgr`yDBywJ7tpW+VpY%-|MA2 zV~?rXZH|_^-OB2v=gluwas8N|wsL)cS?`YAlh;;lv2N}&s+N2$H+?5-$8?!{5kc`b z;pcbf$Ek&VesuLMi@m`mcHi9>KW%H%Iqscj#M0Uq=_`HvZqMYbO-2zS&v!5?ofR{` zvq5jVsB!wE{JEjix6GV6JO5g4g`!^J^EahCA8|TFMEdX6?0L&4Hgh$TZt3Tx*5&#=-aZ$(F`h_jMz8O^on-9`Paj zr+4p6KZzh#i47_i3epme*32FJ`%^T&TbnY=+Gaw`OZEL*qhGjhe0h1Ls(#DdWt;DqU7RIbmLkCUOCd_fXXWds zx56`()pDl={gJ+;@uNw4dzSF8AJ=(jo28b`{F-8LJ9qW1jAa|Y)O5{06tsQzmWdkk zny*~G>L#O?yeDA3;k@leM}yZXvP*o)$v%9{cV@^(ne2;CFDp4lZT;x9>z>c0kCS!; zc*bp5R@2uiCak}w;%>r&q=ICt7PHgyWQ5PJo6Qhmc52~f#in`H|9&M*3I6;{UP*Cf zI!E^Q%{s3ieu><=^0eRM`6)qF)($#_%C@~yoJXH{=%zX!yxV`$g?aAsQpfZ2;>4-J91a$J>DV=*dRc7wKv)6s>j~~=nDbjdRFHUCT<#{okUYw^iX1_dGE40+LjqlR! z9M6|gGZQ{M5IdY5|4i&~tG!rXUfGMr%|UDL?etaicbfR>>ngETtvObHcNRTnEH%6S zPvLdbt*UhbW1x2i7lspTxWdLuHXT5^$>RpkDUl|rhwRGOEayL#o&vZ6Vc7km9$sV&L&Yvp>~ z_sjQv+xn^QRd)HRx>pa2%TJzPu_bbM>y(BI${!CkhTVy_>+71=G4cD9z8cnV&!+M3 zdHwEEqxG3xpAsKlp5S|HitMk-sd|MyOA`7%w%Vq;t1f5kUy>1M8T4nxF?;!$#X>)0 z{BE;(-4eNO@l5a5-Z{^1pJ6PWaDQo^yIbL*qa3f2t@c~F@12$6=eBP};`}vEe}xw; zR$=^kGu?5AvHyZiJqx$!>~vS^-zdtWXIyF+mj6Jj#dG<^yo;qV6t5DjZy zpD)6ivbTFasHL5sn75^Dv8(F3=XJ$bzQ%sB3cA(bRkcS??8^`7Jch{*8<~Ta`U$L3 z-lLj!o_Bjg{A#1QGxl%T!d`RjG2>Lj`!Tz3x|u9_%W!%5{%Pwro;dVUw)y(m6q#)g zrz`Hek+<9Tnc$|t$lu|iuBp=kY&kmjOf)u2?rPm3f97bM+_k;;R%{Covi1I-@9*2V?%T$5n|Ih=`>Ffy((2l|zmHk@_mu8E8-7pf^`Epg+4m!^ z2hO@t>TzdJ4|`4T`Twg6Cg|_uy}*9V>h>|K{R{yc-Jn67s@x@&Ao~bPpIaqw% zk6E=Jw>odwIisXY&{HJd@NI0a&EE;j)-czHcTSyIv}4w7J!*;SdBwiviSno9KW5gH ziRfS25FypIjL}s-_KMd2!e#0zF-yWHTYuMHJ871l=k3P57p%{E{f|2MW#?@BEgQee za>lB2ti;rsHIuuy)S1e;eyt z6eo75tjqglRmxXCbqiydqtvz;`x@4N_pDR(eEVj3yUW9n*YV7GC0^WKGb0`5ABkI@ z_CtHw&eaJUp4>J`m)IR2b!cV7(NY%Pms7TNF*utfMF`g&={9@*^HrqUzUG~Lksd7% z%i4~vcu=FQd$3zu=)8E?Wu}Vw$BKuOxi)^f(lRlubz}dJGWQ8nIlQfp zX0F-Y^1QP^LtQptq5rArwtZJk7ks!d^}L~j&dqb;wx_KXCHWUsc0X-8apQ1!Rg>gm ze_`3Mlb2hK>-{&pJtfZZZz5-Ffy#u1PL>Z=cgd;!3x4=t+xH=hZ~s+3xrNIn?fGx! z9Fpw+a*zJyyPD3Bd7p3R(&?lkeNFf4$oI(fogzclB=mwoy#T5Vp}DKz=sR_~Mm z`&*w{f4Ozv6I*56Vosb; zX2}bcJI`f8ZW*WBwR`Tn(;?|-u4#@=TG*G*dl&yO3kS})mL->xM@!g_#3sM>dwAm;k}dN(_h4M>R6fI zKiBeH@=Hkby`$SB7Jc>mn4R_GrJ1nKPqE#(ozvs=4!`TPNwazHaEDRWrBP+cpKO*J zBCX%K-sc~Cy7ivS-xjsw@2?3@U6<^b87by${FU2K*LS*Fx5eu6IaYa3Sa!{ldB-zh z;o8!(5eFi|B=mDTe3EbN&0MVZc*)FNEr;{hKiX9Buyy*RhDWwfnHHNgiPe57t30*9 z_GDj__vKr2u4ipg7y9;iN$6^hUw1TS&5k#7{IWOmS@XSVNB4i4>)!3*lM=O}-IzIF z&8qV2q4R7UvipT+_DDv{bpP6DW%hSzd5@vl=|>OQ!(RPV>DD^EsH-#gw$qG{?HfJ| zC+s`4d)LcS{{oTiR}{MM|4pm>Bz?}L`<|!Yp-st;ex9AEu(?Fl!RBwtB!h>xl2zA= zu2pl-5oq>S7k<9?m*9d9N$c46Padt>-|@72UYs4P&0FU`YO8qd=GU5W1p9{rNb)MqNDhO;uF*yp8d>n%+Gr(*iFa zQffW-BJ`35!>N`hvk!({3MxBZCLsK>iGeZx|npPFhH`JFzbKeW(~KKp0ezO9!}?ckGFcXanPv9N4b@>D*=7MuO3{Nq$_ zWp9&D9XXdwq_<913VXdR&CK+%qH!6o1&`tq_p%F-Q+K@GcA@xa>{8o(oe>$PjQQf< z+vGa3awl3@99AhV{l(Gw^t5*G5*_1*3%1y6d~q-|=}?+mzU1hm3&jV5ikx(8C6_FG z;>S>UarRESe_=|U-1|GMODM7Xv6W)6fsWA`PyXm z<>1|eKGTJ2wn;LJ>)$)~X*u8gCE=3SQXd(XEB<_c;-m1-xn@3#`|4uqWaBQlM03Ad z(Z4Zbo6pSflbH;04+M*!zZ6v6rpeRn&&hG*Qp5Dkw>w+8ORp?^Hc{;RpK~+Py7t8D zL>>94YGu*+^72o;gRT=5r9T$ftTU+lS@cQo?bIvv$N8n6h(2i!m?OT}o1;yIN%083 zd{?E`{by%5dp;DmM{GD?y!X-x)oW`Ulh!+*C@%Z>__&z;)QQVw9^7qWy|MQ&dU7KX=gUK z`=Z|RALZFT^L_0; zzIjzFc~biweK+~v?~|u~H~5lw>(sMk`8FH&CwW@Vp7{w+7Das0@b3OLd+Wxs$nSeq zI24vVd>*OG6270aLDBHzgZ+Mai?-;z@8$Sw=Q3pnQ_0aIx8Ck&sWYGAecSy1zJThV z(dCj?UfusAv?3*({Zzub8zzcp)YX;UAFn)lV5Q3XlV{hdOi%UqNh>q?U!H&DPJxE@ zN0GP3by~`LB&J+S`6_-R_1BT${qmLf?>Fk2JIs45Zlz%AdChg7{gUrae8%hY>Q*N; z#J+EmUm#%nYtP*Ihxe#%=`Ac>+1tf@z%sc^V7c0Cv-$hWqt}!Aq1L z9qqmBc2B;RTm1LYx!rF6P8^*6?6mC0#_yBU)*rdc`1Rk(0@qEyRj%1P_2oMrVQzWg z-?KIN&c-=q?~7|D?sp6PPj!}^Iilddy(oR3ky+vU?k}$FLD$;W-?rQ2vUC5I zdZ8xK)APC*$|gQem@g!pI!R8{q4|AygZQHE@82Kq+mZY7gkZdDmlV^P=tg(`3-^92 zpU7nXWGkM=6Zm1qt&pmD-@i@{c9n>jS$TI;gW9B=nU?}GSm)WdFJ2b$!{NoHhJ_Qt zzVTaJY_@qW)a~%L>$={9_nwPlZy)eIDLAM5f7B2AMQNVf+P-+3JZ9JVS>-YRmSv4+ z>O{Hx?oD@kstvcC-F2RUQG98q=j~U^rrgtex;VIEe^Hf`pz)1mY>}apj@Z>5O-k7i zwJ`CIt&64P1e2wDQ)Pdz^7vh!w%^XasBzzgZqY5C^*5?dH`xa7OJ(eSCcg0pFH6~B z{=Iuo?VZzaee~I(#)+z-o2?mao16!I)4R&5p+bM1F$zx+_? zbLTgoy|`NQ!Zd?-(pRUl4QFI7gx&l7oy+eJ(}Xpdnj(DF@9#{^ z<*8ohV75LtRiSC!#q#7w*LEt;&#-kkpYp2c{<~anH$Q1J-V@mZWd%a&4SFy9VATJ>>ydsja`EhY%=a$T++~?Iv-^8q`OcFk z!W~0ml(r>pS+>*e%=9O>H_iEPwfAaFgi_nIkfo*fid*kq@4lzM@9OSg<-aQ`*d>lm z@C!)gsct-by7$Qmf73X5(Ju8(&iP_D=T=>IoyIw_NqoQdoSkxWR2?UL;tBV&wQ~I4 ze)r>h@i+g1A7vO4f3tJad9C$Lu>NDvCZkvUlY_$;PAjdQI?wpa&yK6wFArx&mc82h%_Fn>>;H2q zyerCM+1~{_?LM)d&uuodV4LZQ6GwlaxV2P)-6D>E)d62C9qvZKG}rYK^H!w1o%2Pn zu+bsZ`)hVkMW8eLp`#nuwEvtrk*D#gfA5z2pDw+0Nl>*<*q~9e(#lDHFK#q`mwdT_KvptYi^B*hjXXgsGs}0HDmo{7H-+K z*$tL^K5()==rn%98E1GR*z2CGt-y&jPmeA$3F}I~Yjj1*=uRllw>G1R6F)CJq98f5 zVO!SBs_K$P>51*kJJqK+9JW{Dc|F-NL-N)^)`K4^TBELP_NY2!{P4;O_K8V9*Q?Ko z*jC27cB^ZSdQCe|f&*9MyeIq{wuBm9T7Iq8Nb{8Gdd2?Mn`if&ZjZBx)nhy%{QJk* zil1}*_e%C}{g$5dc*@dc=NIWOu5Lb~Q(N?8Nqhs_-tEPAHqG9Ax%aZ$z5@kSRh@gj zF7*m55Z{>F+I|;IEeX!57u_|0nl1W&&{LWIb zzj1<#>Ru>Em3jzmyxx3zlF(|6#h#N+1*R=(|DE)6SD}J`(cQ;l|I$Bve=cACf6M#! zyvO;!>wbJ{U$_75?~e!g_u9+-5d1G?zwhhi>MswE-?e{t@ag56?C<{W1;HlY2lgHhD7gOPA$QF0Y3a-_?w@;b|D8Nr{f|vXEi*nk z^DjL9Hg49w|M|b`ZvB63Cv`dR&xf=8@&A5^U2Z%5zESVM8X@Df{~uWMc$yFWwSRZ? z%;l2xPd@ekuK&T+@V!HN*`E)r7nPUY^^>p5Nj`Q?$W|c#rK*BjP+bnQa;!bizbkzA zIR5kBl{oJ6r{qcd#&GbxZZkBs=?e{O?iv+p!Z-&`0QB%Avnal*-_j1H$oiTVpuJtrKf^wG~~-C^r^Io9VuyV<06 z$1@H+?0Xy&n6iE?T;4KuM#YQ!hpq^E+v?ogYqHioxZ`*A%V|b3K24s2juUQXF0D~} zue&0NKbZ4zmbyr{T-v;= z1D~Jv{}xzZ%h=`euztcR&vr#MFVjDd<2sW0?Ys&%E%eSRzx;;b=hP=D3}5PcS^^Ht zJ#tKpTie1r$^4%gPoL+T+%>oN^9rZeTYFoj@cnZO)8c(4t;@-@S^RO;3l??%+T5gT zLI0S#dZz|W)!~>CVI8>JOQ-UV^n>nT%fjg)6Zf2~d+^h&v-FqFw`q4T?c1lQJK^w# zjf%FLUVVSCKaA&=`>``0>SfyE=QtYesbd!Op0DuD;Ya34+mp|>OnGh|WZrmFaaZ-x zzxhA*F`i>PDQ08(qi@j^jrG6hJ^IaYR5wWb%O5kRNi21rW%gZJJXc7tfu;8Y@y#$8e7V{=; z_Uf|5=i z{$H+aVEgyutc72uWfAPKh9QloQ<#Bdi5dZD_=J@8YeT)zxd$u z@1yqO|JHw4a58`%_i@- zVE8`4UO?)+ru~DRlj6@`d8oj?#ISzl-FMEp>pm>^uhiIbQH|H)fld0BT>12(r4zSz zSN8v$eNv6_yUK3Ok6O=c1m;xEs^#wcn*OWc--8G(_4r5>Tv;;!$S z^*2K`RYLpc?Zkik_dIi5crEmbca+}}X%YV;vaA0xh%_7yt8O`|aC60dv;3*e)fQJ| z(q-(PGrnwkZMX5$*$of;KHopA^W*&CW^0MQo=L|!7s&rw^zK*VD}&k(`{$)SXphXR zHVJ0m$|@&r9)7V@>gHLVc_~ZZq>H%EDC~K-^UuuY`|MrcrI#6Ry=#(oPfbYV<)Z?H znrivJ3%Z(a{Bwn_q@TGfx01E+_~OS0SFYjP_+a6RPQeOi_AR=#&b^9Dr}+O55SBEH z+Vm+;tkpfD@yhZXZZAuBx#IeVdkt9Aw{+K@RXDeKO7+sh9$`U=DtCpKGHFG(OSjn^ zbLcD3vpJDg;+Oq8JI&z~2XoZX;8wkP7lgMRnS6_NDNoeT|1#bgKKh?!O|;ttCRWVM zn5q9#Z+XVj+>?LWd48(!hHaVVGQT6i>AkhE>U`sgthsyCRSnl1=AYrAr^vD9%(X@d z&&~I)Nj~53{B_TEe{R2P>h;%7u^Z0)5+%)gh56K(=YOI$e^8A7(s6wf&(dv|cX?R8 ziM3yVD%;i7dkdJNh0lxW`5jWQE=hbG-MEldSf_T%{b?(%d9^ku!Ao0}D7mOkHtHu1O}+-%sm&fYY~mW?{P%SFDw( z`2Km%Nv7E~g-;jm{oL7=aX#BLNXzD+UWq}7Yv8u8)8hSEro}uhliecI-@HC`<3gqh zr+#c&Wuq|d?`o~X98v9?76j=0PK}zgaZ8h}*SG2!tAwq-%S=v6p5}kz!D-6{&c9=4 zpUV95Q{rI!!w+}uVmH=Fe2|Kqmw9F16t&QftnImze;)AG_1U0v!0vL7%oDBGOQJTN z%IaG3ed&zy^OerivOkzJzI{UjxI1FPiaQ~RtX3t!*SILyu# zboBYIJHLb6-yGIj+QezJ?tJMf?iu@p-l|7U-lTWax#Z=N^PN6ZpFG@AdWrdjX!(jy zsjscqehp6OmF2u1_SE}CSnbAG)$*nf)0}+6{Z^mh>p3s>Z+4vmLruHHJGuQeoHu>? zpS?<1@Zi%Ocd0+q6FX~8)c-EM`&jqFo(zeOpLG{6tjhe#RXx-6yumlV+UZw$t9PH) zOMd7f!d>65RI_P^5+hso*ULVh*45Ku9+bR16{4zC=zsP}L9K-9`IBwG?{E8e*);iQ z>P)x82Y>9>sNTITMsVj7`?cY-QWt$V6m??8?hr;V0lO6Q-C>F`-ffS*o=Xv^Q4{~X zE6h?ZqFGk6+V0C~q4Sel^VQvhQ~p@448OPh>!)`-+8=C+<++SRh5P=`Q4?WfuB-Ry zS6_GJ^;&bES&t?iU-@+3|L&IM#qkFgmfTgJzO?bV+Psacmb1n6tPYs9V)@3Yy_~I#;_Kc|C1p+e!WW$3UVh#6W^JqU zGv8≪P)2y)S*fPq_B` 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 #FAFAFA #FAFAFA - - - - @lineageos.platform:color/color_default_accent - @lineageos.platform:color/color_default_background - - @lineageos.platform:color/color_default_primary_text - @lineageos.platform:color/color_default_secondary_text + #000000 #CFD8DC #78909C @@ -44,8 +37,4 @@ #4CAF50 #FFEB3B #F44336 - - - - -- GitLab From bbb9968f1b4496fb62e1df33d1cef77b5203cf67 Mon Sep 17 00:00:00 2001 From: Romain Hunault Date: Fri, 27 Nov 2020 23:23:20 +0100 Subject: [PATCH 09/19] Revert "Use system accent color" This reverts commit d9f73a1a315fc09fcc3658002bbd0689eef40131. --- .../java/foundation/e/apps/MainActivity.kt | 71 +--------- .../e/apps/application/ApplicationActivity.kt | 23 +--- .../apps/application/ApplicationViewHolder.kt | 122 +++++++----------- .../application/SmallApplicationViewHolder.kt | 21 +-- .../e/apps/application/model/Application.kt | 88 ++++++------- .../e/apps/categories/ApplicationsFragment.kt | 13 +- .../e/apps/categories/CategoriesFragment.kt | 34 +---- .../apps/categories/CategoriesListAdapter.kt | 11 +- .../categories/CategoriesViewPagerAdapter.kt | 6 +- .../e/apps/categories/GamesFragment.kt | 12 +- .../e/apps/categories/PwasFragment.kt | 2 +- .../categories/category/CategoryActivity.kt | 30 +---- .../e/apps/common/ApplicationListAdapter.kt | 5 +- .../foundation/e/apps/home/HomeFragment.kt | 16 +-- .../e/apps/search/SearchFragment.kt | 11 +- .../e/apps/updates/UpdatesFragment.kt | 24 +--- 16 files changed, 124 insertions(+), 365 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 1e639330b..baa457bed 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -19,24 +19,15 @@ package foundation.e.apps //import androidx.fragment.app.ListFragment - import android.annotation.SuppressLint import android.content.* import android.content.pm.PackageManager -import android.content.res.ColorStateList -import android.graphics.Color import android.os.Bundle import android.os.Handler import android.preference.PreferenceManager -import android.util.TypedValue import android.view.MenuItem import android.widget.Toast -import androidx.annotation.ColorInt -import androidx.annotation.ColorRes import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.view.ContextThemeWrapper -import androidx.core.content.ContextCompat -import androidx.core.graphics.drawable.DrawableCompat import androidx.fragment.app.Fragment import com.google.android.material.bottomnavigation.BottomNavigationItemView import com.google.android.material.bottomnavigation.BottomNavigationMenuView @@ -69,7 +60,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS private val codeRequestPermissions = 9527 var doubleBackToExitPressedOnce = false; private var isReceiverRegistered = false - var accentColorOS = 0 + companion object { @@ -79,15 +70,12 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - //ThemeColors(this); setContentView(R.layout.activity_main) mActivity = this disableCategoryIfOpenSource() - bottom_navigation_view.setOnNavigationItemSelectedListener{ if (selectFragment(it.itemId,it)) { disableCategoryIfOpenSource() @@ -114,28 +102,6 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS } setupLangReceiver() applicationManagerServiceConnection.bindService(this) - - getAccentColor(); - bottom_navigation_view_color() - } - - private fun bottom_navigation_view_color() { - val iconsColorStates = - ColorStateList(arrayOf(intArrayOf(-android.R.attr.state_checked), - intArrayOf(android.R.attr.state_checked)), intArrayOf( - Color.parseColor("#C4CFD9"), - accentColorOS - )) - - val textColorStates = ColorStateList(arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)), intArrayOf( - Color.parseColor("#C4CFD9"), - accentColorOS - - )) - - bottom_navigation_view.setItemIconTintList(iconsColorStates) - bottom_navigation_view.setItemTextColor(textColorStates) - } private fun initialiseUpdatesWorker() { @@ -150,9 +116,9 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS } private fun initialiseFragments(applicationManager: ApplicationManager) { - homeFragment.initialise(applicationManager, accentColorOS) - searchFragment.initialise(applicationManager, accentColorOS) - updatesFragment.initialise(applicationManager, accentColorOS) + homeFragment.initialise(applicationManager) + searchFragment.initialise(applicationManager) + updatesFragment.initialise(applicationManager) } override fun onNavigationItemSelected(item: MenuItem): Boolean { @@ -178,28 +144,17 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS return "any" } - fun tintMenuIcon(context: Context, item: MenuItem, @ColorRes color: Int) { - val normalDrawable = item.icon - val wrapDrawable = DrawableCompat.wrap(normalDrawable) - - DrawableCompat.setTint(wrapDrawable, ContextCompat.getColor(context, color))//context.resources.getColor(color)) - item.icon = wrapDrawable - } private fun selectFragment(fragmentId: Int, item: MenuItem?): Boolean { - when (fragmentId) { - R.id.menu_home -> { item?.setIcon(R.drawable.ic_menu_home) showFragment(homeFragment) - return true } R.id.menu_categories -> { item?.setIcon(R.drawable.ic_menu_categories) showFragment(CategoriesFragment()) - return true } R.id.menu_search -> { @@ -310,20 +265,4 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS } }, 2000) } - - /* - * get Accent color from OS - * - * */ - private fun getAccentColor() { - - val typedValue = TypedValue() - val contextThemeWrapper = ContextThemeWrapper(this, - android.R.style.Theme_DeviceDefault) - contextThemeWrapper.getTheme().resolveAttribute(android.R.attr.colorAccent, - typedValue, true) - @ColorInt val color = typedValue.data - accentColorOS=color; - } - -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationActivity.kt b/app/src/main/java/foundation/e/apps/application/ApplicationActivity.kt index 5a4a15c39..f860a001b 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationActivity.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationActivity.kt @@ -32,7 +32,6 @@ import android.text.SpannableStringBuilder import android.text.method.LinkMovementMethod import android.text.style.ForegroundColorSpan import android.util.DisplayMetrics -import android.util.Log import android.util.TypedValue import android.view.Menu import android.view.MenuItem @@ -40,10 +39,8 @@ import android.view.View import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView -import androidx.annotation.ColorInt import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.widget.Toolbar import com.google.android.material.snackbar.Snackbar import foundation.e.apps.MainActivity.Companion.sharedPreferences @@ -93,7 +90,7 @@ class ApplicationActivity : private var imageMargin = 0 private var defaultElevation = 0f private val sharedPrefFile = "kotlinsharedpreference" - var accentColorOS = 0 + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -116,14 +113,6 @@ class ApplicationActivity : this.applicationPackageName = applicationPackageName!! applicationManagerServiceConnection.bindService(this) } - - getAccentColor() - app_install.setTextColor(Color.parseColor("#ffffff")) - app_install.setBackgroundColor(accentColorOS) - app_category.setTextColor(accentColorOS) - app_expand_description.setTextColor(accentColorOS) - - } @@ -760,14 +749,4 @@ class ApplicationActivity : applicationManagerServiceConnection.unbindService(this) } } - - private fun getAccentColor() { - val typedValue = TypedValue() - val contextThemeWrapper = ContextThemeWrapper(this, - android.R.style.Theme_DeviceDefault) - contextThemeWrapper.getTheme().resolveAttribute(android.R.attr.colorAccent, - typedValue, true) - @ColorInt val color = typedValue.data - accentColorOS=color; - } } diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt b/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt index 6010e1a4a..ddb116290 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationViewHolder.kt @@ -48,17 +48,17 @@ import kotlinx.android.synthetic.main.install_button_layout.view.* import java.lang.Exception -class ApplicationViewHolder(private val activity: Activity, private val view: View, accentColorOS: Int) : +class ApplicationViewHolder(private val activity: Activity, private val view: View) : RecyclerView.ViewHolder(view), ApplicationStateListener, Downloader.DownloadProgressCallback, BasicData.IconLoaderCallback, - PwasBasicData.IconLoaderCallback { + PwasBasicData.IconLoaderCallback{ private val icon: ImageView = view.app_icon private val title: TextView = view.app_title - private val pwa_icon: TextView = view.pwa_sympol + private val pwa_icon :TextView =view.pwa_sympol private val author: TextView = view.app_author private val ratingBar: RatingBar = view.app_rating_bar private val rating: TextView = view.app_rating @@ -67,34 +67,28 @@ class ApplicationViewHolder(private val activity: Activity, private val view: Vi private var application: Application? = null private val applicationViewModel = ApplicationViewModel() private var downloader: Downloader? = null - var accentColorOS = accentColorOS; init { - pwa_icon.visibility = View.GONE + pwa_icon.visibility=View.GONE view.setOnClickListener { if (application != null) { if (application!!.packageName != Constants.MICROG_PACKAGE) applicationViewModel.onApplicationClick(view.context, application!!) } } - - - installButton.setTextColor(Color.parseColor("#ffffff")) - if (0 != this.accentColorOS) { - installButton.setBackgroundColor(this.accentColorOS) - } installButton?.setOnClickListener { if (application?.fullData != null && application!!.fullData!!.getLastVersion() == null) { Snackbar.make(view, activity.getString( Error.APK_UNAVAILABLE.description), Snackbar.LENGTH_LONG).show() - } else if (application?.pwabasicdata != null) { + } else if(application?.pwabasicdata!=null){ application?.pwaInstall(activity) - } else if (application?.searchAppsBasicData != null && application?.searchAppsBasicData!!.is_pwa) { + }else if(application?.searchAppsBasicData!=null &&application?.searchAppsBasicData!!.is_pwa){ application?.pwaInstall(activity) - } else { + } + else { application?.buttonClicked(activity, activity) } } @@ -102,46 +96,46 @@ class ApplicationViewHolder(private val activity: Activity, private val view: Vi fun createApplicationView(app: Application) { - pwa_icon.visibility = View.GONE - this.application = app - - if (app.basicData != null) { - this.application?.removeListener(this) + pwa_icon.visibility = View.GONE this.application = app - icon.setImageDrawable(view.context.resources.getDrawable(R.drawable.ic_app_default)) - application!!.loadIcon(this) - application!!.addListener(this) - title.text = application!!.basicData!!.name - author.text = application!!.basicData!!.author - ratingBar.rating = application!!.basicData!!.ratings!!.rating!! - if (application!!.basicData!!.ratings!!.rating != -1f) { - rating.text = application!!.basicData!!.ratings!!.rating.toString() - } else { - rating.text = activity.getString(R.string.not_available) - } - if (application!!.basicData!!.privacyRating != null && application!!.basicData!!.privacyRating != -1f) { - privacyScore.text = application!!.basicData!!.privacyRating.toString() - } else { - privacyScore.text = activity.getString(R.string.not_available) - } - } else { - this.application?.removeListener(this) - this.application = app - icon.setImageDrawable(view.context.resources.getDrawable(R.drawable.ic_app_default)) - application!!.addListener(this) - if (application!!.searchAppsBasicData != null) { - if (application!!.searchAppsBasicData!!.is_pwa) { - pwa_icon.visibility = View.VISIBLE + + if (app.basicData != null) { + this.application?.removeListener(this) + this.application = app + icon.setImageDrawable(view.context.resources.getDrawable(R.drawable.ic_app_default)) + application!!.loadIcon(this) + application!!.addListener(this) + title.text = application!!.basicData!!.name + author.text = application!!.basicData!!.author + ratingBar.rating = application!!.basicData!!.ratings!!.rating!! + if (application!!.basicData!!.ratings!!.rating != -1f) { + rating.text = application!!.basicData!!.ratings!!.rating.toString() + } else { + rating.text = activity.getString(R.string.not_available) } - application!!.SearchAppsloadIcon(this) - title.text = application!!.searchAppsBasicData!!.name - author.text = application!!.searchAppsBasicData!!.author - } else { - application!!.PwaloadIcon(this) - title.text = application!!.pwabasicdata!!.name + if (application!!.basicData!!.privacyRating != null && application!!.basicData!!.privacyRating != -1f) { + privacyScore.text = application!!.basicData!!.privacyRating.toString() + } else { + privacyScore.text = activity.getString(R.string.not_available) + } + }else{ + this.application?.removeListener(this) + this.application = app + icon.setImageDrawable(view.context.resources.getDrawable(R.drawable.ic_app_default)) + application!!.addListener(this) + if (application!!.searchAppsBasicData != null) { + if (application!!.searchAppsBasicData!!.is_pwa) { + pwa_icon.visibility = View.VISIBLE + } + application!!.SearchAppsloadIcon(this) + title.text = application!!.searchAppsBasicData!!.name + author.text = application!!.searchAppsBasicData!!.author + } else { + application!!.PwaloadIcon(this) + title.text = application!!.pwabasicdata!!.name + } } - } stateChanged(application!!.state) } @@ -153,19 +147,13 @@ class ApplicationViewHolder(private val activity: Activity, private val view: Vi override fun stateChanged(state: State) { Execute({}, { - - // installButton.setBackgroundResource(R.drawable.app_install_border_simple) + installButton.setBackgroundResource(R.drawable.app_install_border_simple) installButton?.text = activity.getString(state.installButtonTextId) when (state) { - State.NOT_DOWNLOADED -> { - if (0 != this.accentColorOS) { - installButton.setTextColor(this.accentColorOS) - } else { - - installButton.setTextColor(Color.parseColor("#0088ED")) - } + State.NOT_DOWNLOADED ->{ + installButton.setTextColor(Color.parseColor("#0088ED")) installButton.setBackgroundResource(R.drawable.app_install_border_simple) installButton.isEnabled = true } @@ -174,25 +162,15 @@ class ApplicationViewHolder(private val activity: Activity, private val view: Vi installButton?.isEnabled = Common.appHasLaunchActivity(activity, application!!.packageName) - if (0 != this.accentColorOS) { - installButton!!.setBackgroundColor(this.accentColorOS) - } else { - installButton!!.setBackgroundResource(R.drawable.app_install_border) - } installButton.setTextColor(Color.parseColor("#FAFAFA")) - + installButton!!.setBackgroundResource(R.drawable.app_install_border) } State.INSTALLING -> { installButton?.isEnabled = false } State.NOT_UPDATED -> { installButton.setTextColor(Color.parseColor("#FAFAFA")) - //installButton!!.setBackgroundResource(R.drawable.app_install_border) - if (0 != this.accentColorOS) { - installButton!!.setBackgroundColor(this.accentColorOS) - } else { - installButton!!.setBackgroundResource(R.drawable.app_install_border) - } + installButton!!.setBackgroundResource(R.drawable.app_install_border) installButton?.isEnabled = true } else -> { @@ -200,7 +178,6 @@ class ApplicationViewHolder(private val activity: Activity, private val view: Vi installButton?.isEnabled = true } } - }) } @@ -227,5 +204,4 @@ class ApplicationViewHolder(private val activity: Activity, private val view: Vi downloader?.removeListener(this) downloader = null } - } diff --git a/app/src/main/java/foundation/e/apps/application/SmallApplicationViewHolder.kt b/app/src/main/java/foundation/e/apps/application/SmallApplicationViewHolder.kt index f5d4934ae..20c5d129c 100644 --- a/app/src/main/java/foundation/e/apps/application/SmallApplicationViewHolder.kt +++ b/app/src/main/java/foundation/e/apps/application/SmallApplicationViewHolder.kt @@ -53,7 +53,6 @@ class SmallApplicationViewHolder(private val activity: Activity, private val vie BasicData.IconLoaderCallback, PwasBasicData.IconLoaderCallback{ - private val icon: ImageView = view.app_icon private val title: TextView = view.app_title private val installButton: Button = view.app_install @@ -68,7 +67,6 @@ class SmallApplicationViewHolder(private val activity: Activity, private val vie applicationViewModel.onApplicationClick(view.context, application!!) } } - installButton.setOnClickListener { if (application?.fullData != null && application!!.fullData!!.getLastVersion() == null) { @@ -84,6 +82,7 @@ class SmallApplicationViewHolder(private val activity: Activity, private val vie } } } + fun createApplicationView(app: Application) { if(app.basicData!=null) { this.application?.removeListener(this) @@ -117,14 +116,11 @@ class SmallApplicationViewHolder(private val activity: Activity, private val vie installButton.text = activity.getString(state.installButtonTextId) installButton.clearAnimation() installButton.clearFocus(); - var color = application?.getAccentColor(activity); when (state) { + State.NOT_DOWNLOADED ->{ + installButton.setTextColor(Color.parseColor("#0088ED")) installButton.setBackgroundResource(R.drawable.app_install_border_simple) - if (color != null) { - - installButton.setTextColor(color) - }; installButton.isEnabled = true } @@ -132,10 +128,7 @@ class SmallApplicationViewHolder(private val activity: Activity, private val vie installButton.isEnabled = Common.appHasLaunchActivity(activity, application!!.packageName) installButton.setTextColor(Color.parseColor("#FAFAFA")) - if (color != null) { - //installButton!!.setBackgroundResource(R.drawable.app_install_border) - installButton.setBackgroundColor(color) - }; + installButton!!.setBackgroundResource(R.drawable.app_install_border) } State.INSTALLING -> { installButton.isEnabled = false @@ -144,11 +137,7 @@ class SmallApplicationViewHolder(private val activity: Activity, private val vie } State.NOT_UPDATED -> { installButton.setTextColor(Color.parseColor("#FAFAFA")) - // installButton!!.setBackgroundResource(R.drawable.app_install_border) - if (color != null) { - //installButton!!.setBackgroundResource(R.drawable.app_install_border) - installButton.setBackgroundColor(color) - }; + installButton!!.setBackgroundResource(R.drawable.app_install_border) installButton.isEnabled = true } else -> { diff --git a/app/src/main/java/foundation/e/apps/application/model/Application.kt b/app/src/main/java/foundation/e/apps/application/model/Application.kt index 9ca217361..de20278c6 100644 --- a/app/src/main/java/foundation/e/apps/application/model/Application.kt +++ b/app/src/main/java/foundation/e/apps/application/model/Application.kt @@ -18,15 +18,11 @@ package foundation.e.apps.application.model import android.Manifest -import android.R import android.app.Activity import android.app.DownloadManager import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.util.TypedValue -import androidx.annotation.ColorInt -import androidx.appcompat.view.ContextThemeWrapper import foundation.e.apps.MainActivity.Companion.mActivity import foundation.e.apps.pwa.PwaInstaller import foundation.e.apps.XAPK.XAPKFile @@ -41,7 +37,9 @@ import foundation.e.apps.utils.* import java.util.concurrent.atomic.AtomicInteger class Application(val packageName: String, private val applicationManager: ApplicationManager) : - DownloaderInterface, InstallerInterface { + DownloaderInterface, InstallerInterface{ + + private val uses = AtomicInteger(0) @@ -50,9 +48,9 @@ class Application(val packageName: String, private val applicationManager: Appli var basicData: BasicData? = null var releaseData: ReleaseData? = null var fullData: FullData? = null - var pwabasicdata: PwasBasicData? = null + var pwabasicdata : PwasBasicData? = null var pwaFullData: PwaFullData? = null - var searchAppsBasicData: SearchAppsBasicData? = null + var searchAppsBasicData : SearchAppsBasicData? =null fun addListener(listener: ApplicationStateListener) { @@ -81,13 +79,16 @@ class Application(val packageName: String, private val applicationManager: Appli fun checkForStateUpdate(context: Context) { if (basicData != null) { stateManager.find(context, basicData!!) - } else if (searchAppsBasicData != null) { - if (searchAppsBasicData!!.is_pwa) { + } + else if(searchAppsBasicData !=null){ + if(searchAppsBasicData!!.is_pwa){ // stateManager.pwaFind() - } else { + } + else{ stateManager.searchAppsFind(context, searchAppsBasicData!!) } - } else if (pwabasicdata != null) { + } + else if(pwabasicdata!=null){ // stateManager.pwaFind() } } @@ -101,16 +102,16 @@ class Application(val packageName: String, private val applicationManager: Appli fun pwaInstall(context: Context) { var error: Error? = null - Thread(Runnable { - error = assertFullData(context) + Thread(Runnable { + error=assertFullData(context) - mActivity.runOnUiThread(Runnable { + mActivity.runOnUiThread(Runnable{ run { if (error == null) { - val intent = Intent(context, PwaInstaller::class.java) - intent.putExtra("NAME", pwaFullData!!.name) - intent.putExtra("URL", pwaFullData!!.url) + val intent=Intent(context, PwaInstaller::class.java) + intent.putExtra("NAME",pwaFullData!!.name) + intent.putExtra("URL",pwaFullData!!.url) context.startActivity(intent) } else { stateManager.notifyError(error!!) @@ -216,12 +217,13 @@ class Application(val packageName: String, private val applicationManager: Appli installSystemApp(context) } else { Execute({ - AppDownloadedRequest(basicData!!.id, fullData!!.getLastVersion()?.apkArchitecture).request() + AppDownloadedRequest(basicData!!.id,fullData!!.getLastVersion()?.apkArchitecture).request() }, {}) - if (info.isXapk(fullData!!, basicData)) { - isInstalling = true - XAPKFile(info.getxApkFile(context, basicData!!), this) - } else { + if(info.isXapk(fullData!!,basicData)){ + isInstalling=true + XAPKFile(info.getxApkFile(context,basicData!!),this) + } + else { install(context) } } @@ -229,10 +231,11 @@ class Application(val packageName: String, private val applicationManager: Appli synchronized(blocker) { blocker.notify() } - if (basicData != null) { + if(basicData!=null) { info.getApkFile(context, basicData!!).delete() applicationManager.stopInstalling(context, this) - } else { + } + else{ applicationManager.stopInstalling(context, this) } } @@ -273,12 +276,16 @@ class Application(val packageName: String, private val applicationManager: Appli fun assertFullData(context: Context): Error? { if (fullData != null) { return null - } else if (pwabasicdata != null) { + } + else if(pwabasicdata != null){ return findPwaFullData(context) - } else if (searchAppsBasicData != null) { - if (searchAppsBasicData!!.is_pwa) { + } + + else if(searchAppsBasicData!=null){ + if(searchAppsBasicData!!.is_pwa){ return findSearchResultPwaFulldata(context) - } else { + } + else{ findSearchAppsFullData(context) } } @@ -317,7 +324,7 @@ class Application(val packageName: String, private val applicationManager: Appli } var error: Error? = null if (Common.isNetworkAvailable(context)) { - AppDetailRequest(basicData!!.id).request { applicationError, fullData -> + AppDetailRequest(basicData!!.id).request { applicationError, fullData-> when (applicationError) { null -> { error = Error.NO_RESULTS @@ -336,7 +343,6 @@ class Application(val packageName: String, private val applicationManager: Appli } return error } - private fun findSearchAppsFullData(context: Context): Error? { if (searchAppsBasicData == null) { val error = findBasicData(context) @@ -346,7 +352,7 @@ class Application(val packageName: String, private val applicationManager: Appli } var error: Error? = null if (Common.isNetworkAvailable(context)) { - AppDetailRequest(searchAppsBasicData!!.id).request { applicationError, fullData -> + AppDetailRequest(searchAppsBasicData!!.id).request { applicationError, fullData-> when (applicationError) { null -> { error = Error.NO_RESULTS @@ -376,7 +382,7 @@ class Application(val packageName: String, private val applicationManager: Appli } var error: Error? = null if (Common.isNetworkAvailable(context)) { - AppDetailRequest(pwabasicdata!!.id).Pwarequest { applicationError, PwaFullData -> + AppDetailRequest(pwabasicdata!!.id ).Pwarequest { applicationError, PwaFullData -> when (applicationError) { null -> { error = Error.NO_RESULTS @@ -405,7 +411,7 @@ class Application(val packageName: String, private val applicationManager: Appli } var error: Error? = null if (Common.isNetworkAvailable(context)) { - AppDetailRequest(searchAppsBasicData!!.id).Pwarequest { applicationError, PwaFullData -> + AppDetailRequest(searchAppsBasicData!!.id ).Pwarequest { applicationError, PwaFullData -> when (applicationError) { null -> { error = Error.NO_RESULTS @@ -426,6 +432,7 @@ class Application(val packageName: String, private val applicationManager: Appli } + fun loadIcon(iconLoaderCallback: BasicData.IconLoaderCallback) { basicData?.loadIconAsync(this, iconLoaderCallback) } @@ -471,19 +478,4 @@ class Application(val packageName: String, private val applicationManager: Appli Pwaupdate(pwaFullData.pwabasicdata, context) pwaFullData.pwabasicdata = pwabasicdata!! } - - /* - * get Accent color from OS - * - */ - fun getAccentColor(context: Context): Int { - val typedValue = TypedValue() - val contextThemeWrapper = ContextThemeWrapper(context, - R.style.Theme_DeviceDefault) - contextThemeWrapper.getTheme().resolveAttribute(android.R.attr.colorAccent, - typedValue, true) - @ColorInt val color = typedValue.data - return color; - - } } diff --git a/app/src/main/java/foundation/e/apps/categories/ApplicationsFragment.kt b/app/src/main/java/foundation/e/apps/categories/ApplicationsFragment.kt index b727c0467..5547f495e 100644 --- a/app/src/main/java/foundation/e/apps/categories/ApplicationsFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/ApplicationsFragment.kt @@ -17,7 +17,6 @@ package foundation.e.apps.categories -import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -33,11 +32,9 @@ import foundation.e.apps.categories.viewmodel.CategoriesViewModel import kotlinx.android.synthetic.main.error_layout.view.* import kotlinx.android.synthetic.main.fragment_application_categories.view.* -class ApplicationsFragment(color: Int?) : Fragment() { +class ApplicationsFragment : Fragment() { private lateinit var categoriesViewModel: CategoriesViewModel - val color = color; - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { categoriesViewModel = ViewModelProviders.of(activity!!).get(CategoriesViewModel::class.java) @@ -52,19 +49,13 @@ class ApplicationsFragment(color: Int?) : Fragment() { categoriesViewModel.loadCategories(context!!) } - view.error_resolve.setTextColor(Color.parseColor("#ffffff")) - if (color != null) { - view.error_resolve.setBackgroundColor(color) - } - // Bind to the list of applications categories categoriesViewModel.getApplicationsCategories().observe(this, Observer { if (it!!.isNotEmpty()) { - //Add New Category it.add(Category("system_apps")) - view.categories_list.adapter = CategoriesListAdapter(it, color) + view.categories_list.adapter = CategoriesListAdapter(it) view.categories_list.visibility = View.VISIBLE view.progress_bar.visibility = View.GONE } diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt index 2ad9d324e..aa80032b8 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesFragment.kt @@ -17,16 +17,10 @@ package foundation.e.apps.categories -import android.content.Context -import android.graphics.Color import android.os.Bundle -import android.util.Log -import android.util.TypedValue import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.annotation.ColorInt -import androidx.appcompat.view.ContextThemeWrapper import androidx.fragment.app.Fragment import androidx.viewpager.widget.ViewPager import com.google.android.material.tabs.TabLayout @@ -34,26 +28,17 @@ import foundation.e.apps.R class CategoriesFragment : Fragment() { - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_categories, container, false) val tabLayout = view.findViewById(R.id.tab_layout) val viewPager = view.findViewById(R.id.view_pager) - var color = getAccentColor(activity!!); - viewPager.adapter = CategoriesViewPagerAdapter(activity!!.supportFragmentManager, tabLayout.tabCount, color) - viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout)) - if (color != null) { - tabLayout.setSelectedTabIndicatorColor(color); - tabLayout.setTabTextColors(Color.parseColor("#000000"), color); - - }; + viewPager.adapter = CategoriesViewPagerAdapter(activity!!.supportFragmentManager, tabLayout.tabCount) + viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabLayout)) tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { override fun onTabSelected(tab: TabLayout.Tab) { viewPager.currentItem = tab.position - } override fun onTabUnselected(tab: TabLayout.Tab) { @@ -66,19 +51,4 @@ class CategoriesFragment : Fragment() { }) return view } - - /* - * get Accent color from OS - * - */ - fun getAccentColor(context: Context): Int { - val typedValue = TypedValue() - val contextThemeWrapper = ContextThemeWrapper(context, - android.R.style.Theme_DeviceDefault) - contextThemeWrapper.getTheme().resolveAttribute(android.R.attr.colorAccent, - typedValue, true) - @ColorInt val color = typedValue.data - return color; - - } } diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt index eba63c010..766d5b532 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesListAdapter.kt @@ -19,6 +19,7 @@ package foundation.e.apps.categories import android.annotation.SuppressLint import android.content.Intent +import android.graphics.Color import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -31,15 +32,12 @@ import foundation.e.apps.categories.category.CategoryActivity import foundation.e.apps.categories.model.Category import foundation.e.apps.utils.Constants -class CategoriesListAdapter(private var categories: ArrayList, color: Int?) +class CategoriesListAdapter(private var categories: ArrayList) : RecyclerView.Adapter() { - val color = color; - init { categories = ArrayList(categories.sortedWith( compareBy({ it.getTitle() }, { it.getTitle() }))) - } class CategoryViewHolder(view: View) : RecyclerView.ViewHolder(view) { @@ -64,10 +62,7 @@ class CategoriesListAdapter(private var categories: ArrayList, color: holder.categoryIcon.setImageDrawable(holder.categoryIcon.resources.getDrawable(categories[position].getIconResource())) - //holder.categoryIcon.setColorFilter(Color.parseColor("#0088ED")) - if (color != null) { - holder.categoryIcon.setColorFilter(color) - } + holder.categoryIcon.setColorFilter(Color.parseColor("#0088ED")) holder.categoryTitle.text = categories[position].getTitle() holder.categoryContainer.setOnClickListener { val intent = Intent(holder.categoryContainer.context, CategoryActivity::class.java) diff --git a/app/src/main/java/foundation/e/apps/categories/CategoriesViewPagerAdapter.kt b/app/src/main/java/foundation/e/apps/categories/CategoriesViewPagerAdapter.kt index 043ad5036..5af8588f8 100644 --- a/app/src/main/java/foundation/e/apps/categories/CategoriesViewPagerAdapter.kt +++ b/app/src/main/java/foundation/e/apps/categories/CategoriesViewPagerAdapter.kt @@ -21,11 +21,11 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentStatePagerAdapter -class CategoriesViewPagerAdapter(fragmentManager: FragmentManager, private val numberOfTabs: Int, color: Int?) : +class CategoriesViewPagerAdapter(fragmentManager: FragmentManager, private val numberOfTabs: Int) : FragmentStatePagerAdapter(fragmentManager) { - private val applicationsFragment = ApplicationsFragment(color) - private val gamesFragment = GamesFragment(color) + private val applicationsFragment = ApplicationsFragment() + private val gamesFragment = GamesFragment() private val pwasFragment = PwasFragment() diff --git a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt index 34dc4dd6f..da4155bc2 100644 --- a/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/GamesFragment.kt @@ -17,7 +17,6 @@ package foundation.e.apps.categories -import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -32,11 +31,9 @@ import foundation.e.apps.categories.viewmodel.CategoriesViewModel import kotlinx.android.synthetic.main.error_layout.view.* import kotlinx.android.synthetic.main.fragment_games_categories.view.* -class GamesFragment(color: Int?) : Fragment() { +class GamesFragment : Fragment() { private lateinit var categoriesViewModel: CategoriesViewModel - var color=color; - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { categoriesViewModel = ViewModelProviders.of(activity!!).get(CategoriesViewModel::class.java) @@ -50,16 +47,11 @@ class GamesFragment(color: Int?) : Fragment() { view.progress_bar.visibility = View.VISIBLE categoriesViewModel.loadCategories(context!!) } - view.error_resolve.setTextColor(Color.parseColor("#ffffff")) - if (color != null) { - view.error_resolve.setBackgroundColor(color!!) - } - // Bind to the list of games categories categoriesViewModel.getGamesCategories().observe(this, Observer { if (it!!.isNotEmpty()) { - view.categories_list.adapter = CategoriesListAdapter(it, color) + view.categories_list.adapter = CategoriesListAdapter(it) view.categories_list.visibility = View.VISIBLE view.progress_bar.visibility = View.GONE } diff --git a/app/src/main/java/foundation/e/apps/categories/PwasFragment.kt b/app/src/main/java/foundation/e/apps/categories/PwasFragment.kt index 260dacd83..c98e1294d 100644 --- a/app/src/main/java/foundation/e/apps/categories/PwasFragment.kt +++ b/app/src/main/java/foundation/e/apps/categories/PwasFragment.kt @@ -34,7 +34,7 @@ class PwasFragment : Fragment() { // Bind to the list of pwas categories categoriesViewModel.getPwasCategories().observe(this, Observer { if (it!!.isNotEmpty()) { - view.categories_list.adapter = CategoriesListAdapter(it, null) + view.categories_list.adapter = CategoriesListAdapter(it) view.categories_list.visibility = View.VISIBLE view.progress_bar.visibility = View.GONE } diff --git a/app/src/main/java/foundation/e/apps/categories/category/CategoryActivity.kt b/app/src/main/java/foundation/e/apps/categories/category/CategoryActivity.kt index 881480ac9..204766e38 100644 --- a/app/src/main/java/foundation/e/apps/categories/category/CategoryActivity.kt +++ b/app/src/main/java/foundation/e/apps/categories/category/CategoryActivity.kt @@ -18,25 +18,20 @@ package foundation.e.apps.categories.category import android.content.pm.PackageManager -import android.graphics.Color import android.os.Bundle -import android.util.TypedValue import android.view.MenuItem import android.view.View import android.widget.LinearLayout import android.widget.ProgressBar import android.widget.RelativeLayout import android.widget.TextView -import androidx.annotation.ColorInt import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.view.ContextThemeWrapper import androidx.appcompat.widget.Toolbar import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.snackbar.Snackbar -import foundation.e.apps.MainActivity import foundation.e.apps.R import foundation.e.apps.application.model.Application import foundation.e.apps.applicationmanager.ApplicationManager @@ -48,7 +43,6 @@ import foundation.e.apps.common.ApplicationListAdapter import foundation.e.apps.utils.Constants import foundation.e.apps.utils.Constants.CATEGORY_KEY import kotlinx.android.synthetic.main.activity_category.* -import kotlinx.android.synthetic.main.error_layout.* class CategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectionCallback { @@ -60,7 +54,8 @@ class CategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectio ApplicationManagerServiceConnection(this) private var applicationList = ArrayList() private var isLoadingMoreApplications = false - var accentColorOS = 0; + + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -80,10 +75,6 @@ class CategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectio val errorContainer = findViewById(R.id.error_container) val errorDescription = findViewById(R.id.error_description) - //set accent color to Error button (Retry ) - findViewById(R.id.error_resolve).setTextColor(Color.parseColor("#ffffff")) - findViewById(R.id.error_resolve).setBackgroundColor(accentColorOS) - // Initialise UI elements recyclerView.visibility = View.GONE loadMoreContainer.visibility = View.GONE @@ -113,7 +104,7 @@ class CategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectio // Initialise recycler view recyclerView.setHasFixedSize(true) recyclerView.layoutManager = LinearLayoutManager(this) - recyclerView.adapter = ApplicationListAdapter(this, applicationList, 0) + recyclerView.adapter = ApplicationListAdapter(this, applicationList) // Bind to the list of applications in this activity's category categoryViewModel.getApplications().observe(this, Observer { @@ -192,19 +183,4 @@ class CategoryActivity : AppCompatActivity(), ApplicationManagerServiceConnectio } applicationManagerServiceConnection.unbindService(this) } - - /* - * get Accent color from OS - * - * */ - private fun getAccentColor() { - - val typedValue = TypedValue() - val contextThemeWrapper = ContextThemeWrapper(this, - android.R.style.Theme_DeviceDefault) - contextThemeWrapper.getTheme().resolveAttribute(android.R.attr.colorAccent, - typedValue, true) - @ColorInt val color = typedValue.data - accentColorOS=color; - } } diff --git a/app/src/main/java/foundation/e/apps/common/ApplicationListAdapter.kt b/app/src/main/java/foundation/e/apps/common/ApplicationListAdapter.kt index db830d5c7..9c61792d5 100644 --- a/app/src/main/java/foundation/e/apps/common/ApplicationListAdapter.kt +++ b/app/src/main/java/foundation/e/apps/common/ApplicationListAdapter.kt @@ -25,12 +25,11 @@ import foundation.e.apps.R import foundation.e.apps.application.ApplicationViewHolder import foundation.e.apps.application.model.Application -class ApplicationListAdapter(private val activity: Activity, private val applicationList: List, accentColorOS: Int) : RecyclerView.Adapter() { +class ApplicationListAdapter(private val activity: Activity, private val applicationList: List) : RecyclerView.Adapter() { - var accentColorOS = accentColorOS; override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ApplicationViewHolder { val listItemContainer = LayoutInflater.from(parent.context).inflate(R.layout.application_list_item, parent, false) - return ApplicationViewHolder(activity, listItemContainer, accentColorOS) + return ApplicationViewHolder(activity, listItemContainer) } override fun onBindViewHolder(holder: ApplicationViewHolder, position: Int) { diff --git a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt index e9c5a1ce2..723df4b79 100644 --- a/app/src/main/java/foundation/e/apps/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/home/HomeFragment.kt @@ -19,12 +19,10 @@ package foundation.e.apps.home //import java.awt.font.ShapeGraphicAttribute.STROKE //import java.awt.AlphaComposite.SRC_IN -import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Button import android.widget.LinearLayout import android.widget.ProgressBar import android.widget.TextView @@ -40,8 +38,6 @@ import foundation.e.apps.applicationmanager.ApplicationManager import foundation.e.apps.categories.model.Category import foundation.e.apps.common.SmallApplicationListAdapter import foundation.e.apps.home.viewmodel.HomeViewModel -import kotlinx.android.synthetic.main.error_layout.* -import kotlinx.android.synthetic.main.install_button_layout.* class HomeFragment : Fragment() { @@ -51,11 +47,9 @@ class HomeFragment : Fragment() { private lateinit var categoryList: LinearLayout private lateinit var progressBar: ProgressBar private var applicationManager: ApplicationManager? = null - var accentColorOS=0; - fun initialise(applicationManager: ApplicationManager, accentColorOS: Int) { + fun initialise(applicationManager: ApplicationManager) { this.applicationManager = applicationManager - this.accentColorOS=accentColorOS; } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -65,10 +59,6 @@ class HomeFragment : Fragment() { } val view = inflater.inflate(R.layout.fragment_home, container, false) - /* if(accentColorOS!=0){ - - view.findViewById(R.id.tv_featured).setTextColor(accentColorOS); - }*/ homeViewModel = ViewModelProviders.of(activity!!).get(HomeViewModel::class.java) imageCarousel = view.findViewById(R.id.image_carousel) @@ -77,10 +67,6 @@ class HomeFragment : Fragment() { progressBar = view.findViewById(R.id.progress_bar) val errorContainer = view.findViewById(R.id.error_container) val errorDescription = view.findViewById(R.id.error_description) - //set accent color to Error button (Retry ) - - view.findViewById(R.id.error_resolve).setTextColor(Color.parseColor("#ffffff")) - view.findViewById(R.id.error_resolve).setBackgroundColor(accentColorOS) // Initialise UI elements homeViewModel.initialise(applicationManager!!) diff --git a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt index dfadd53bb..7f4922b27 100644 --- a/app/src/main/java/foundation/e/apps/search/SearchFragment.kt +++ b/app/src/main/java/foundation/e/apps/search/SearchFragment.kt @@ -19,7 +19,6 @@ package foundation.e.apps.search import android.app.Activity import android.database.MatrixCursor -import android.graphics.Color import android.os.Bundle import android.provider.BaseColumns import android.view.LayoutInflater @@ -53,11 +52,9 @@ class SearchFragment : Fragment(), SearchView.OnQueryTextListener, SearchView.On private var applicationList = ArrayList() private var applicationManager: ApplicationManager? = null private var isLoadingMoreApplications = false - var accentColorOS=0; - fun initialise(applicationManager: ApplicationManager, accentColorOS: Int) { + fun initialise(applicationManager: ApplicationManager) { this.applicationManager = applicationManager - this.accentColorOS=accentColorOS; } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -79,10 +76,6 @@ class SearchFragment : Fragment(), SearchView.OnQueryTextListener, SearchView.On val errorContainer = view.findViewById(R.id.error_container) val errorDescription = view.findViewById(R.id.error_description) val loadMoreContainer = view.findViewById(R.id.load_more_container) -//set accent color to Error button (Retry ) - error_resolve.setTextColor(Color.parseColor("#ffffff")) - error_resolve.setBackgroundColor(accentColorOS) - error_resolve.visibility=View.GONE searchViewModel.initialise(applicationManager!!) @@ -111,7 +104,7 @@ class SearchFragment : Fragment(), SearchView.OnQueryTextListener, SearchView.On // Initialise recycler view recyclerView.setHasFixedSize(true) recyclerView.layoutManager = LinearLayoutManager(context) - recyclerView.adapter = ApplicationListAdapter(activity!!, applicationList, 0) + recyclerView.adapter = ApplicationListAdapter(activity!!, applicationList) loadMoreContainer.visibility = View.GONE recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 7f76f9a31..01152a92a 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -17,9 +17,6 @@ package foundation.e.apps.updates -import android.content.res.ColorStateList -import android.graphics.Color -import android.graphics.PorterDuff import android.os.Bundle import android.os.Handler import android.view.LayoutInflater @@ -40,20 +37,15 @@ import foundation.e.apps.application.model.State import foundation.e.apps.applicationmanager.ApplicationManager import foundation.e.apps.common.ApplicationListAdapter import foundation.e.apps.updates.viewmodel.UpdatesViewModel -import kotlinx.android.synthetic.main.error_layout.* -import kotlinx.android.synthetic.main.fragment_updates.* - -class UpdatesFragment() : Fragment() { +class UpdatesFragment : Fragment() { private lateinit var updatesViewModel: UpdatesViewModel private var applicationManager: ApplicationManager? = null private lateinit var recyclerView: RecyclerView private var applicationList = ArrayList() - var accentColorOS=0; - fun initialise(applicationManager: ApplicationManager, accentColorOS: Int) { + fun initialise(applicationManager: ApplicationManager) { this.applicationManager = applicationManager - this.accentColorOS=accentColorOS; } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -66,22 +58,12 @@ class UpdatesFragment() : Fragment() { updatesViewModel = ViewModelProviders.of(activity!!).get(UpdatesViewModel::class.java) recyclerView = view.findViewById(R.id.app_list) val updateAll = view.findViewById