diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 033b293c830a3bb478bb39809b45a5937aa0dba4..006e99059ea1bbf4dca8c00fe97159a3ad902fd1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -69,4 +69,4 @@ android:theme="@style/AppTheme1"/> - + \ No newline at end of file diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 9774b8bb4583e0b7b61f648e715d629c051e1e62..5aadf6ec0ce4a99ab5052ea228625d308161ff61 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -18,8 +18,9 @@ package foundation.e.apps //import androidx.fragment.app.ListFragment + import android.annotation.SuppressLint -import android.content.SharedPreferences +import android.content.* import android.content.pm.PackageManager import android.os.Bundle import android.os.Handler @@ -27,6 +28,7 @@ import android.preference.PreferenceManager import android.view.MenuItem import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import com.google.android.material.bottomnavigation.BottomNavigationItemView import com.google.android.material.bottomnavigation.BottomNavigationMenuView @@ -83,6 +85,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS return@setOnNavigationItemSelectedListener false } + disableShiftingOfNabBarItems() initialiseUpdatesWorker() @@ -97,7 +100,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS } else { R.id.menu_home } - + setupLangReceiver() applicationManagerServiceConnection.bindService(this) } @@ -172,13 +175,27 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS } return false } + private var mLangReceiver: BroadcastReceiver? = null + protected fun setupLangReceiver(): BroadcastReceiver? { + if (mLangReceiver == null) { + mLangReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + finish() +// selectFragment(currentFragmentId,null) + } + } + val filter = IntentFilter(Intent.ACTION_LOCALE_CHANGED) + registerReceiver(mLangReceiver, filter) + } + return mLangReceiver + } private fun showFragment(fragment: Fragment) { bottom_navigation_view.menu.findItem(currentFragmentId).isChecked = true supportFragmentManager .beginTransaction() .replace(R.id.frame_layout, fragment) - .commit() + .commitAllowingStateLoss(); } @SuppressLint("RestrictedApi") diff --git a/app/src/main/java/foundation/e/apps/api/HomePwaRequest.kt b/app/src/main/java/foundation/e/apps/api/HomePwaRequest.kt index fdff42546b9e5b921dd817509c3143e4b8ae37ce..7e804bca6ded19911d3663f0377bcddc33ca87cb 100644 --- a/app/src/main/java/foundation/e/apps/api/HomePwaRequest.kt +++ b/app/src/main/java/foundation/e/apps/api/HomePwaRequest.kt @@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore import com.fasterxml.jackson.annotation.JsonProperty import foundation.e.apps.MainActivity 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.applicationmanager.ApplicationManager import foundation.e.apps.categories.model.Category @@ -18,15 +19,15 @@ import foundation.e.apps.utils.Error class HomePwaRequest { companion object { - private val reader = Common.getObjectMapper().readerFor(HomeResult::class.java) + private val mapper = Common.getObjectMapper() } - fun request(callback: (Error?, HomeResult?) -> Unit) { + fun request(callback: (Error?, Result?) -> Unit) { try { var appType = MainActivity.mActivity.showApplicationTypePreference() val url = Constants.BASE_URL + "apps?action=list_home&type=$appType" val urlConnection = Common.createConnection(url, Constants.REQUEST_METHOD_GET) - val result = reader.readValue(urlConnection.inputStream) + val result = mapper.readValue(urlConnection.inputStream, Result::class.java) urlConnection.disconnect() callback.invoke(null, result) } catch (e: Exception) { @@ -34,56 +35,50 @@ class HomePwaRequest { } } - class HomeResult @JsonCreator - constructor(@JsonProperty("home") private val home: PwasSubHomeResult) { + + data class Result(val success: Boolean, val home: Home) + + data class Home( + @JsonProperty("headings") + val headings: Map?, + @JsonProperty(BANNER_APPS_KEY) + val bannerApps: List, + @JsonProperty(POPULAR_APPS_KEY) + val topUpdatedApps: List, + @JsonProperty(POPULAR_GAMES_KEY) + val topUpdatedGames: List, + @JsonProperty(DISCOVER_KEY) + val discover: List + ) { + + companion object { + private const val BANNER_APPS_KEY = "banner_apps" + private const val POPULAR_APPS_KEY = "popular_apps" + private const val POPULAR_GAMES_KEY = "popular_games" + private const val DISCOVER_KEY = "discover" + private val KEYS = setOf(POPULAR_APPS_KEY, + POPULAR_GAMES_KEY, DISCOVER_KEY) + } + fun getBannerApps(applicationManager: ApplicationManager, context: Context): ArrayList { - return ApplicationParser.PwaParseToApps(applicationManager, context, home.bannerApps) + return ApplicationParser.parseToApps(applicationManager, context, bannerApps.toTypedArray()) } fun getApps(applicationManager: ApplicationManager, context: Context): LinkedHashMap> { val apps = LinkedHashMap>() - for (pair in home.apps) { - if(pair.value .isEmpty() ){ - apps.remove(pair.key) - }else { - - apps[pair.key] = ApplicationParser.PwaParseToApps(applicationManager, context, pair.value.toTypedArray()) + KEYS.forEach { + var heading = headings?.get(it) + heading = heading + ?: "" // Use default heading as empty to let it generate from the key itself. + val parsedApps = when (it) { + POPULAR_APPS_KEY -> ApplicationParser.parseToApps(applicationManager, context, topUpdatedApps.toTypedArray()) + POPULAR_GAMES_KEY -> ApplicationParser.parseToApps(applicationManager, context, topUpdatedGames.toTypedArray()) + DISCOVER_KEY -> ApplicationParser.parseToApps(applicationManager, context, discover.toTypedArray()) + else -> throw IllegalArgumentException("Unrecognised key $it encountered") } + apps[Category(it, heading)] = parsedApps } return apps } } - - class PwasSubHomeResult @JsonCreator constructor() { - @JsonIgnore - val apps = LinkedHashMap>() - lateinit var bannerApps: Array - - @JsonAnySetter - fun append(key: String, value: Any) { - val apps = value as ArrayList<*> - val appsData = ArrayList() - apps.forEach { - val data = it as LinkedHashMap<*, *> - val appData = PwasBasicData( - data["_id"] as String, - data["name"] as String, - data["description"] as String?, - data["is_pwa"] as Boolean, - data["is_web_app"] as Boolean, - data["has_https"] as Boolean, - data["url"] as String?, - data["category"] as String, - data["icon_image_path"] as String, - (data["other_images_path"]as List).toTypedArray(), - data["created_on"] as String?) - appsData.add(appData) - } - if (key == "banner_apps") { - bannerApps = appsData.toTypedArray() - } else { - this.apps[Category(key)] = appsData - } - } - } -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/apps/api/HomeRequest.kt b/app/src/main/java/foundation/e/apps/api/HomeRequest.kt index 35c34f86444d842ea4433c63c03712163257d91a..accc4001b7e7256240e55bf24ce3eb540a3b477d 100644 --- a/app/src/main/java/foundation/e/apps/api/HomeRequest.kt +++ b/app/src/main/java/foundation/e/apps/api/HomeRequest.kt @@ -54,6 +54,8 @@ class HomeRequest { data class Result(val success: Boolean, val home: Home) data class Home( + @JsonProperty("headings") + val headings: Map?, @JsonProperty(BANNER_APPS_KEY) val bannerApps: List, @JsonProperty(TOP_UPDATED_APPS_KEY) @@ -80,15 +82,16 @@ class HomeRequest { POPULAR_GAMES_24_HOUR_KEY, DISCOVER_KEY) } - fun getBannerApps(applicationManager: ApplicationManager, context: Context): ArrayList { return ApplicationParser.parseToApps(applicationManager, context, bannerApps.toTypedArray()) - } fun getApps(applicationManager: ApplicationManager, context: Context): LinkedHashMap> { val apps = LinkedHashMap>() KEYS.forEach { + var heading = headings?.get(it) + heading = heading + ?: "" // Use default heading as empty to let it generate from the key itself. val parsedApps = when (it) { TOP_UPDATED_APPS_KEY -> ApplicationParser.parseToApps(applicationManager, context, topUpdatedApps.toTypedArray()) TOP_UPDATED_GAMES_KEY -> ApplicationParser.parseToApps(applicationManager, context, topUpdatedGames.toTypedArray()) @@ -97,11 +100,9 @@ class HomeRequest { DISCOVER_KEY -> ApplicationParser.parseToApps(applicationManager, context, discover.toTypedArray()) else -> throw IllegalArgumentException("Unrecognised key $it encountered") } - apps[Category(it)] = parsedApps - + apps[Category(it, heading)] = parsedApps } return apps } - } } diff --git a/app/src/main/java/foundation/e/apps/api/ListCategoriesRequest.kt b/app/src/main/java/foundation/e/apps/api/ListCategoriesRequest.kt index 82f6427ad77f3a6b7b8f5ee5fa410652a802fec3..3283dea051fbc11917767dfb44634e5000ffeece 100644 --- a/app/src/main/java/foundation/e/apps/api/ListCategoriesRequest.kt +++ b/app/src/main/java/foundation/e/apps/api/ListCategoriesRequest.kt @@ -20,14 +20,16 @@ package foundation.e.apps.api import com.fasterxml.jackson.annotation.JsonCreator import com.fasterxml.jackson.annotation.JsonProperty import foundation.e.apps.MainActivity.Companion.mActivity +import foundation.e.apps.categories.model.Category import foundation.e.apps.utils.Common import foundation.e.apps.utils.Constants import foundation.e.apps.utils.Error +import java.io.Serializable class ListCategoriesRequest { companion object { - private val reader = Common.getObjectMapper().readerFor(ListCategoriesResult::class.java) + private val mapper = Common.getObjectMapper() } fun request(callback: (Error?, ListCategoriesResult?) -> Unit) { @@ -35,15 +37,37 @@ class ListCategoriesRequest { var appType = mActivity.showApplicationTypePreference() val url = Constants.BASE_URL + "apps?action=list_cat&type=$appType" val urlConnection = Common.createConnection(url, Constants.REQUEST_METHOD_GET) - val result = reader.readValue(urlConnection.inputStream) + val result = mapper.readValue(urlConnection.inputStream, ListCategoriesResult::class.java) urlConnection.disconnect() callback.invoke(null, result) } catch (e: Exception) { + e.printStackTrace() callback.invoke(Error.findError(e), null) } } class ListCategoriesResult @JsonCreator constructor(@JsonProperty("apps") val appsCategories: Array, - @JsonProperty("games") val gamesCategories: Array) + @JsonProperty("games") val gamesCategories: Array, + @JsonProperty ("translations") val translations : Map ) : Serializable { + + + fun appsParseResult(): ArrayList { + val appCategories = ArrayList() + appsCategories.forEach { id -> + val translations = translations[id] + appCategories.add(Category(id, translations!!)) + } + return appCategories + } + + fun gameParseResult(): ArrayList { + val gameCategories = ArrayList() + gamesCategories.forEach { id -> + val translations = translations[id] + gameCategories.add(Category(id, translations!!)) + } + return gameCategories + } + } } 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 5134317f8484b1fcf6f063c4930510f15aa36f66..a2786ec4ff3f0be946522b9208b770fddf015835 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 @@ -86,7 +86,7 @@ constructor( val category: Category init { - this.category = Category(categoryId) + this.category = Category(categoryId, "") } @Suppress("unused") diff --git a/app/src/main/java/foundation/e/apps/application/model/data/PwaFullData.kt b/app/src/main/java/foundation/e/apps/application/model/data/PwaFullData.kt index 8d12bafce4dfc115ad4067782838ddc7da0b995d..190bbbeb165d30c7751cf602d3d8409e53f3a1a4 100644 --- a/app/src/main/java/foundation/e/apps/application/model/data/PwaFullData.kt +++ b/app/src/main/java/foundation/e/apps/application/model/data/PwaFullData.kt @@ -25,7 +25,7 @@ constructor( val category: Category init { - this.category = Category(categoryId) + this.category = Category(categoryId, "") } } diff --git a/app/src/main/java/foundation/e/apps/categories/model/CategoriesModel.kt b/app/src/main/java/foundation/e/apps/categories/model/CategoriesModel.kt index 3db6abed368af8f81ec934a878246337237b6b5f..7d03951b5c0430e11c6193fca89c0bc05c6fbc62 100644 --- a/app/src/main/java/foundation/e/apps/categories/model/CategoriesModel.kt +++ b/app/src/main/java/foundation/e/apps/categories/model/CategoriesModel.kt @@ -69,15 +69,9 @@ class CategoriesModel : CategoriesModelInterface { } private fun parseResult(result: ListCategoriesRequest.ListCategoriesResult) { - val appsCategories = ArrayList() - val gamesCategories = ArrayList() - result.appsCategories.forEach { id -> - appsCategories.add(Category(id)) - } - result.gamesCategories.forEach { id -> - gamesCategories.add(Category(id)) - } - applicationsCategoriesList.value = appsCategories - gamesCategoriesList.value = gamesCategories + val apps=result.appsParseResult() + val games=result.gameParseResult() + applicationsCategoriesList.value = apps + gamesCategoriesList.value = games } } 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 ebc7e8c3d5623f919d87ce0e5d5a695414fda4f2..16e0d5b99aa16a6d7497427d63ca57d4a24af140 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 @@ -20,7 +20,7 @@ package foundation.e.apps.categories.model import foundation.e.apps.R import java.io.Serializable -class Category(val id: String, val defaultTitle: String? = "") : Serializable { +class Category(val id: String, val result: String = "") : Serializable { private val title: String private val iconResource: Int @@ -31,15 +31,12 @@ class Category(val id: String, val defaultTitle: String? = "") : Serializable { } private fun getCategoryTitle(categoryId: String): String { - if(defaultTitle!!.isNotEmpty()) return defaultTitle.toString() + if(result.isNotEmpty()) return result - val title = categoryId.replace("_", " ") - if (title.contains("game ")) { - return title.removePrefix("game ").capitalize() - }else if(title.contains("pwa ")){ - return title.removePrefix("pwa ").capitalize() + else{ + val title = categoryId.replace("_", " ") + return title.capitalize() } - return title.capitalize() } private fun getCategoryIconResource(categoryId: String): Int { @@ -199,3 +196,4 @@ class Category(val id: String, val defaultTitle: String? = "") : Serializable { return iconResource } } + diff --git a/app/src/main/java/foundation/e/apps/home/model/ApplicationsLoader.kt b/app/src/main/java/foundation/e/apps/home/model/ApplicationsLoader.kt index 3a92275a40e50689ce022f45e6e0dc9299778951..3c20fb456291d5c1fa9d18f2affe7503ba45d48c 100644 --- a/app/src/main/java/foundation/e/apps/home/model/ApplicationsLoader.kt +++ b/app/src/main/java/foundation/e/apps/home/model/ApplicationsLoader.kt @@ -43,8 +43,8 @@ class ApplicationsLoader(private val homeModel: HomeModel) : AsyncTask when (applicationError) { null -> { - bannerApps = pwaHomeResult!!.getBannerApps(homeModel.getInstallManager(), context) - applications = pwaLoadApplications(pwaHomeResult, context) + bannerApps = pwaHomeResult!!.home.getBannerApps(homeModel.getInstallManager(), context) + applications = pwaLoadApplications(pwaHomeResult.home, context) } else -> { error = applicationError @@ -59,6 +59,7 @@ class ApplicationsLoader(private val homeModel: HomeModel) : AsyncTask { error = applicationError @@ -82,12 +83,9 @@ class ApplicationsLoader(private val homeModel: HomeModel) : AsyncTask> { - val parsedApplications = result.getApps(homeModel.getInstallManager(), context) - val applications = LinkedHashMap>() - for (parsedApplication in parsedApplications) { - applications[parsedApplication.key] = parsedApplication.value - } - return applications + + private fun pwaLoadApplications(home: HomePwaRequest.Home, context: Context): LinkedHashMap> { + return home.getApps(homeModel.getInstallManager(), context) + } } diff --git a/app/src/main/java/foundation/e/apps/utils/Common.kt b/app/src/main/java/foundation/e/apps/utils/Common.kt index 75bc0ef670a57632eee1a8fe410e7648eb5ba443..896620899023accffff41e7b7893e36309ac8e8e 100644 --- a/app/src/main/java/foundation/e/apps/utils/Common.kt +++ b/app/src/main/java/foundation/e/apps/utils/Common.kt @@ -21,9 +21,19 @@ import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.net.ConnectivityManager +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.core.os.LocaleListCompat +import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.KeyDeserializer import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import foundation.e.apps.categories.model.Category import java.net.URL +import java.nio.file.Paths +import java.util.* import java.util.concurrent.Executors import javax.net.ssl.HttpsURLConnection import kotlin.math.roundToInt @@ -46,7 +56,9 @@ object Common { } fun createConnection(url: String, requestMethod: String): HttpsURLConnection { + val preferredLanguage =getAcceptedLanguageHeaderValue() val connection = URL(url).openConnection() as HttpsURLConnection + connection.setRequestProperty("Accept-Language", preferredLanguage) connection.requestMethod = requestMethod connection.connectTimeout = Constants.CONNECT_TIMEOUT connection.readTimeout = Constants.READ_TIMEOUT @@ -86,8 +98,40 @@ object Common { fun getObjectMapper(): ObjectMapper { val objectMapper = ObjectMapper() + var simpleModule = SimpleModule() + simpleModule.addKeyDeserializer(Category::class.java,keyDeserializer()) + objectMapper.registerModule(simpleModule); + objectMapper.registerKotlinModule() objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) objectMapper.registerKotlinModule() return objectMapper } + + fun getAcceptedLanguageHeaderValue(): String { + var weight = 1.0F + return getPreferredLocaleList() + .map { it.toLanguageTag() } + .reduce { accumulator, languageTag -> + weight -= 0.1F + "$accumulator,$languageTag;q=$weight" + } + } + + fun getPreferredLocaleList(): List { + val adjustedLocaleListCompat = LocaleListCompat.getAdjustedDefault() + val preferredLocaleList = mutableListOf() + for (index in 0 until adjustedLocaleListCompat.size()) { + preferredLocaleList.add(adjustedLocaleListCompat.get(index)) + } + return preferredLocaleList + } +} + +class keyDeserializer : KeyDeserializer() { + @RequiresApi(Build.VERSION_CODES.O) + override fun deserializeKey(p0: String?, p1: DeserializationContext?): Any? { + return Paths.get(p0) + } + + } diff --git a/app/src/main/res/layout/activity_application.xml b/app/src/main/res/layout/activity_application.xml index b1082d71a74696dbe92ad2d691d4df5b5ae9e755..ffb4bb42e4da61ee461bd661382e8c81e2d0a426 100644 --- a/app/src/main/res/layout/activity_application.xml +++ b/app/src/main/res/layout/activity_application.xml @@ -150,7 +150,7 @@ @@ -211,11 +211,12 @@ @@ -226,12 +227,13 @@ android:layout_width="@dimen/install_button_width3" android:layout_height="@dimen/install_button_height" android:layout_alignParentEnd="true" + android:ellipsize="end" + android:layout_centerVertical="true" android:layout_marginStart="@dimen/layout_margin_medium2" - android:layout_marginTop="@dimen/layout_margin_small" - layout="@layout/install_button_layout" + layout="@layout/install_button_layout" /> + - /> @@ -828,3 +830,4 @@ app:layout_constraintBottom_toBottomOf="parent" /> + diff --git a/app/src/main/res/layout/application_list_item.xml b/app/src/main/res/layout/application_list_item.xml index 29b84855c27c41acfff36a7b7672a63f42b426ea..b19722d8f040bb7130b6c2b7595b8eb833cff1e4 100644 --- a/app/src/main/res/layout/application_list_item.xml +++ b/app/src/main/res/layout/application_list_item.xml @@ -137,7 +137,7 @@ android:orientation="vertical"> . --> -