🆕 Adding Notification support
This commit is contained in:
parent
ce6dca5363
commit
c5fc622d8c
7 changed files with 352 additions and 26 deletions
|
@ -1,7 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.mylloon.mobidl" >
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
@ -23,6 +27,16 @@
|
|||
<meta-data
|
||||
android:name="preloaded_fonts"
|
||||
android:resource="@array/preloaded_fonts" />
|
||||
|
||||
<receiver
|
||||
android:name="BootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
</manifest>
|
4
app/src/main/assets/appNames.json
Normal file
4
app/src/main/assets/appNames.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"tiktok": "musically",
|
||||
"twitch livestream multiplayer": "twitch"
|
||||
}
|
|
@ -4,6 +4,7 @@ package com.mylloon.mobidl
|
|||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlertDialog
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
|
@ -16,20 +17,73 @@ import android.os.Looper
|
|||
import android.text.Editable
|
||||
import android.text.InputType
|
||||
import android.text.TextWatcher
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.*
|
||||
import android.widget.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.work.*
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.*
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.util.DisplayMetrics
|
||||
import java.util.concurrent.ExecutionException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
private fun isWorkScheduled(context: Context): Boolean {
|
||||
val instance = WorkManager.getInstance(context)
|
||||
val statuses = instance.getWorkInfosByTag("com.mylloon.mobidl.BackgroundUpdateCheck")
|
||||
return try {
|
||||
var running = false
|
||||
val workInfoList = statuses.get()
|
||||
for (workInfo in workInfoList) {
|
||||
val state = workInfo.state
|
||||
running = (state == WorkInfo.State.RUNNING) or (state == WorkInfo.State.ENQUEUED)
|
||||
}
|
||||
running
|
||||
} catch (e: ExecutionException) {
|
||||
e.printStackTrace()
|
||||
println("No job already running")
|
||||
false
|
||||
} catch (e: InterruptedException) {
|
||||
e.printStackTrace()
|
||||
println("No job already running")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun newJob(context: Context) {
|
||||
val myConstraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED) //checks whether device should have Network Connection
|
||||
.build()
|
||||
val job = PeriodicWorkRequestBuilder<BackgroundUpdateCheck>(15, TimeUnit.MINUTES)
|
||||
.setConstraints(myConstraints)
|
||||
.build()
|
||||
WorkManager.getInstance(context).enqueue(job)
|
||||
}
|
||||
|
||||
class BootReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (!isWorkScheduled(context)) {
|
||||
println("Create a update check job in background...")
|
||||
when (intent.action) {
|
||||
Intent.ACTION_DATE_CHANGED -> {
|
||||
newJob(context)
|
||||
}
|
||||
Intent.ACTION_BOOT_COMPLETED -> {
|
||||
val sharedPref = context.getSharedPreferences("com.mylloon.MobiDL",
|
||||
AppCompatActivity.MODE_PRIVATE)
|
||||
sharedPref.edit().putInt("notifID", 0).apply()
|
||||
newJob(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private var settingsButton: Menu? = null // before starting the app there is no settings button
|
||||
|
@ -37,8 +91,8 @@ class MainActivity : AppCompatActivity() {
|
|||
private var inSettings: Boolean = false // by default your not in settings page
|
||||
private var inAppList: Boolean = false // by default your not in app list page
|
||||
private var inAppInfo: Boolean = false // by default your not in app info page
|
||||
private var prefs: SharedPreferences? = null // first run detection
|
||||
private val sharedPref = "com.mylloon.MobiDL" // shared pref name
|
||||
private var sharedPref: SharedPreferences? = null // first run detection
|
||||
private val sharedPrefName = "com.mylloon.MobiDL" // shared pref name
|
||||
private var timeOfLastToast: Long = System.currentTimeMillis() - 2000
|
||||
private var listInfos: MutableList<Map<String, String?>>? = null
|
||||
private var appMobilismInfos: Map<String, String?>? = null
|
||||
|
@ -64,15 +118,54 @@ class MainActivity : AppCompatActivity() {
|
|||
val context = applicationContext() // get app context
|
||||
|
||||
// read apps from the app preference
|
||||
val sharedPref = context.getSharedPreferences(sharedPref, MODE_PRIVATE)
|
||||
val sharedPref = context.getSharedPreferences(sharedPrefName, MODE_PRIVATE)
|
||||
val data: Set<String>? = sharedPref.getStringSet("apps", null)
|
||||
|
||||
return data?.toMutableList() ?: mutableListOf()
|
||||
}
|
||||
|
||||
|
||||
private fun getValuesAppNeedToBeUpdated(): MutableMap<String, Boolean> { // list of the apps (from the storage if exists)
|
||||
val context = applicationContext() // get app context
|
||||
|
||||
// read apps from the app preference
|
||||
val sharedPref = context.getSharedPreferences(sharedPrefName, MODE_PRIVATE)
|
||||
val dataApp: Set<String>? = sharedPref.getStringSet("appsUpdate", null)
|
||||
val dataBool: Set<String>? = sharedPref.getStringSet("boolUpdate", null)
|
||||
val dataAppList = dataApp?.toList()
|
||||
val dataBoolList = dataBool?.toList()
|
||||
|
||||
val finalMap = mutableMapOf<String, Boolean>()
|
||||
if ((dataAppList != null) and (dataBoolList != null)) {
|
||||
for (i in dataAppList!!.indices) {
|
||||
if (i in dataBoolList!!.indices) {
|
||||
finalMap[dataAppList[i]] =
|
||||
dataBoolList[i].substring(i.toString().length).toBoolean()
|
||||
}
|
||||
}
|
||||
}
|
||||
return finalMap
|
||||
}
|
||||
|
||||
private fun storeValuesAppNeedToBeUpdated(updateApps: MutableMap<String, Boolean>) {
|
||||
val updateAppsName: Set<String> = updateApps.keys
|
||||
val updateAppsBool: MutableCollection<Boolean> = updateApps.values
|
||||
val updateAppsBoolList: List<Boolean> = updateAppsBool.toList()
|
||||
val updateAppsBoolSet: MutableList<String> = mutableListOf()
|
||||
for (i in updateAppsBoolList.indices) {
|
||||
updateAppsBoolSet.add(i.toString() + updateAppsBoolList[i].toString())
|
||||
}
|
||||
|
||||
with(applicationContext().getSharedPreferences(sharedPrefName, MODE_PRIVATE).edit()) {
|
||||
this?.putStringSet("appsUpdate", updateAppsName)
|
||||
this?.putStringSet("boolUpdate", updateAppsBoolSet.toSet())
|
||||
this?.apply()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) { // Main function
|
||||
super.onCreate(savedInstanceState)
|
||||
prefs = getSharedPreferences("com.mylloon.MobiDL", MODE_PRIVATE)
|
||||
sharedPref = getSharedPreferences(sharedPrefName, MODE_PRIVATE)
|
||||
|
||||
try {
|
||||
if (Credentials().get(0) == null) { // test if credentials have already been registered
|
||||
|
@ -128,6 +221,7 @@ class MainActivity : AppCompatActivity() {
|
|||
private fun mainPage(toDelete: String? = null) {
|
||||
if (toDelete == null) {
|
||||
setContentView(R.layout.activity_main) // display main page
|
||||
if (!isWorkScheduled(applicationContext)) newJob(applicationContext) // run background job if not already running
|
||||
if (!settingsButtonVisible) { // check if the settings button isn't already showed
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
toggleSettingsButtonVisibility()
|
||||
|
@ -137,6 +231,7 @@ class MainActivity : AppCompatActivity() {
|
|||
val user = Credentials().get(0).toString()
|
||||
this.title = "${getString(R.string.app_name)} (${getString(R.string.connected_as)} $user)"
|
||||
val valuesRecyclerView = getValuesRecyclerView() // list of all the element in the main page
|
||||
val checkedItemListOfApps = getValuesAppNeedToBeUpdated()
|
||||
|
||||
class Adapter(private val values: List<String>) :
|
||||
RecyclerView.Adapter<Adapter.ViewHolder>() {
|
||||
|
@ -163,20 +258,30 @@ class MainActivity : AppCompatActivity() {
|
|||
button?.setOnLongClickListener {
|
||||
val builder: AlertDialog.Builder =
|
||||
AlertDialog.Builder(instance)
|
||||
builder.setTitle("${getString(R.string.remove)} ${button?.text} ?")
|
||||
builder.setTitle("${getString(R.string.config)} ${button?.text} ?")
|
||||
builder.setPositiveButton(R.string.remove) { _, _ ->
|
||||
instance?.mainPage(
|
||||
button?.text.toString()
|
||||
)
|
||||
checkedItemListOfApps[button?.text.toString()] = false
|
||||
storeValuesAppNeedToBeUpdated(checkedItemListOfApps)
|
||||
}
|
||||
val appInstalled = isItAnInstalledApp(button?.text.toString())
|
||||
if (appInstalled != null) {
|
||||
val checkedItems = booleanArrayOf(
|
||||
false // get this info from the app files somewhere
|
||||
checkedItemListOfApps[button?.text.toString()] == true
|
||||
)
|
||||
builder.setMultiChoiceItems(
|
||||
arrayOf(getString(R.string.updateCheck)),
|
||||
checkedItems
|
||||
) { _, _, isChecked ->
|
||||
println("Update for ${button?.text.toString()}: " + if (isChecked) "enabled" else "disabled" + "")
|
||||
checkedItemListOfApps[button?.text.toString()] = isChecked
|
||||
storeValuesAppNeedToBeUpdated(checkedItemListOfApps)
|
||||
}
|
||||
builder.setNegativeButton(R.string.forceUpdate) { _, _ ->
|
||||
Notif().work(applicationContext,
|
||||
button?.text.toString())
|
||||
}
|
||||
}
|
||||
builder.setNeutralButton(R.string.cancel) { dialog, _ -> dialog.cancel() }
|
||||
builder.show()
|
||||
|
@ -205,11 +310,8 @@ class MainActivity : AppCompatActivity() {
|
|||
if (toDelete != null) {
|
||||
valuesRecyclerView.remove(toDelete)
|
||||
|
||||
val context = applicationContext() // get app context
|
||||
|
||||
// read apps from the app preference
|
||||
val sharedPref = context.getSharedPreferences(sharedPref, MODE_PRIVATE)
|
||||
sharedPref.edit().putStringSet("apps", valuesRecyclerView.toSet()).apply()
|
||||
sharedPref!!.edit().putStringSet("apps", valuesRecyclerView.toSet()).apply()
|
||||
recyclerView.adapter = Adapter(valuesRecyclerView)
|
||||
}
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
|
@ -219,11 +321,8 @@ class MainActivity : AppCompatActivity() {
|
|||
fun addApp(app: String) {
|
||||
valuesRecyclerView.add(app)
|
||||
|
||||
val context = applicationContext() // get app context
|
||||
|
||||
// read apps from the app preference
|
||||
val sharedPref = context.getSharedPreferences(sharedPref, MODE_PRIVATE)
|
||||
sharedPref.edit().putStringSet("apps", valuesRecyclerView.toSet()).apply()
|
||||
sharedPref!!.edit().putStringSet("apps", valuesRecyclerView.toSet()).apply()
|
||||
recyclerView.adapter = Adapter(valuesRecyclerView)
|
||||
|
||||
}
|
||||
|
@ -249,6 +348,21 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
fun isItAnInstalledApp(app: String): String? {
|
||||
var appName = app
|
||||
val registeredAnswered: Map<String, String> = Gson().fromJson(
|
||||
assets.open("appNames.json").bufferedReader()
|
||||
.use { it.readText() },
|
||||
object : TypeToken<Map<String, String>>() {}.type
|
||||
)
|
||||
for ((key, value) in registeredAnswered) appName =
|
||||
if (Regex(appName.lowercase()).containsMatchIn(key)) value else appName.lowercase()
|
||||
for (packageInfo in packageManager.getInstalledPackages(PackageManager.GET_META_DATA)) {
|
||||
if (Regex(appName).containsMatchIn(packageInfo.packageName)) return packageInfo.versionName
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun callScrapper(
|
||||
user: String,
|
||||
password: String,
|
||||
|
@ -470,9 +584,13 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
val title = findViewById<TextView>(R.id.textViewAppName)
|
||||
title.text = appMobilismInfos!!["title"]
|
||||
title.setOnClickListener { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://forum.mobilism.org$link"))) }
|
||||
title.setOnClickListener {
|
||||
startActivity(Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse("https://forum.mobilism.org$link")))
|
||||
}
|
||||
findViewById<TextView>(R.id.textViewAppAuthor).text = appMobilismInfos!!["author"]
|
||||
findViewById<TextView>(R.id.textViewAppDate).text = appMobilismInfos!!["date"]
|
||||
val changelogs = findViewById<TextView>(R.id.textViewAppChangelogs)
|
||||
|
@ -527,9 +645,9 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (prefs!!.getBoolean("firstrun", true)) {
|
||||
if (sharedPref!!.getBoolean("firstrun", true)) {
|
||||
Credentials().generateKey() // Generate RSA keys
|
||||
prefs!!.edit().putBoolean("firstrun", false)
|
||||
sharedPref!!.edit().putBoolean("firstrun", false)
|
||||
.apply() // first run done, now the next ones won't be "first".
|
||||
}
|
||||
}
|
||||
|
|
169
app/src/main/java/com/mylloon/mobidl/Notification.kt
Normal file
169
app/src/main/java/com/mylloon/mobidl/Notification.kt
Normal file
|
@ -0,0 +1,169 @@
|
|||
package com.mylloon.mobidl
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
|
||||
var sharedPrefName = "com.mylloon.MobiDL" // shared pref name
|
||||
|
||||
class BackgroundUpdateCheck(appContext: Context, workerParams: WorkerParameters) :
|
||||
Worker(appContext, workerParams) {
|
||||
private val ctx = appContext
|
||||
|
||||
override fun doWork(): Result {
|
||||
fun getValuesAppNeedToBeUpdated(): MutableMap<String, Boolean> { // list of the apps (from the storage if exists)
|
||||
val context = MainActivity.applicationContext() // get app context
|
||||
|
||||
// read apps from the app preference
|
||||
val sharedPref = context.getSharedPreferences(sharedPrefName,
|
||||
AppCompatActivity.MODE_PRIVATE)
|
||||
val dataApp: Set<String>? = sharedPref.getStringSet("appsUpdate", null)
|
||||
val dataBool: Set<String>? = sharedPref.getStringSet("boolUpdate", null)
|
||||
val dataAppList = dataApp?.toList()
|
||||
val dataBoolList = dataBool?.toList()
|
||||
|
||||
val finalMap = mutableMapOf<String, Boolean>()
|
||||
if ((dataAppList != null) and (dataBoolList != null)) {
|
||||
for (i in dataAppList!!.indices) {
|
||||
if (i in dataBoolList!!.indices) {
|
||||
finalMap[dataAppList[i]] =
|
||||
dataBoolList[i].substring(i.toString().length).toBoolean()
|
||||
}
|
||||
}
|
||||
}
|
||||
return finalMap
|
||||
}
|
||||
val apps = getValuesAppNeedToBeUpdated()
|
||||
for (app in apps.keys) {
|
||||
if (apps[app] == true) {
|
||||
println("Checking update for $app...")
|
||||
Notif().work(ctx, app)
|
||||
}
|
||||
}
|
||||
println("Update check: Done.")
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
|
||||
class Notif {
|
||||
private lateinit var notificationManager: NotificationManager
|
||||
private lateinit var notificationChannel: NotificationChannel
|
||||
private lateinit var builder: Notification.Builder
|
||||
private val channelId = "i.apps.notifications"
|
||||
private val description = R.string.descriptionNotification
|
||||
|
||||
@SuppressLint("UnspecifiedImmutableFlag")
|
||||
fun newNotification(
|
||||
context: Context,
|
||||
notificationID: Int,
|
||||
appName: String,
|
||||
version: String,
|
||||
url: String,
|
||||
) {
|
||||
notificationManager = context.getSystemService(NotificationManager::class.java)
|
||||
val pendingIntent = PendingIntent.getActivity(context,
|
||||
notificationID,
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(url)),
|
||||
(PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // checking if android version is greater than oreo(API 26) or not
|
||||
notificationChannel = NotificationChannel(channelId,
|
||||
context.getString(description),
|
||||
NotificationManager.IMPORTANCE_LOW)
|
||||
notificationChannel.enableLights(true)
|
||||
notificationChannel.lightColor = Color.GREEN
|
||||
notificationChannel.enableVibration(false)
|
||||
notificationManager.createNotificationChannel(notificationChannel)
|
||||
|
||||
builder = Notification.Builder(context, channelId)
|
||||
.setContentTitle("${context.getString(R.string.newUpdateTitleNotification)} $appName")
|
||||
.setContentText("${context.getString(R.string.newUpdateVersionNotification)} $version ${
|
||||
context.getString(R.string.newUpdateAvailableNotification)
|
||||
}")
|
||||
.setSmallIcon(R.drawable.download)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
} else {
|
||||
builder = Notification.Builder(context)
|
||||
.setContentTitle("${context.getString(R.string.newUpdateTitleNotification)} $appName")
|
||||
.setContentText("${context.getString(R.string.newUpdateVersionNotification)} $version ${
|
||||
context.getString(R.string.newUpdateAvailableNotification)
|
||||
}")
|
||||
.setSmallIcon(R.drawable.download)
|
||||
.setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
}
|
||||
notificationManager.notify(notificationID, builder.build())
|
||||
val sharedPref =
|
||||
context.getSharedPreferences(sharedPrefName, AppCompatActivity.MODE_PRIVATE)
|
||||
sharedPref.edit().putInt("notifID", notificationID + 1).apply()
|
||||
}
|
||||
|
||||
fun work(context: Context, app: String) {
|
||||
var appName = app
|
||||
val registeredAnswered: Map<String, String> = Gson().fromJson(
|
||||
context.assets.open("appNames.json").bufferedReader()
|
||||
.use { it.readText() },
|
||||
object : TypeToken<Map<String, String>>() {}.type
|
||||
)
|
||||
for ((key, value) in registeredAnswered) appName =
|
||||
if (Regex(appName.lowercase()).containsMatchIn(key)) value else appName.lowercase()
|
||||
var installedAppVersion: String? = null
|
||||
for (packageInfo in context.packageManager.getInstalledPackages(PackageManager.GET_META_DATA)) {
|
||||
if (Regex(appName).containsMatchIn(packageInfo.packageName)) installedAppVersion =
|
||||
packageInfo.versionName
|
||||
}
|
||||
if (installedAppVersion == null) return
|
||||
else {
|
||||
try {
|
||||
val res =
|
||||
Scraper(Credentials().get(0), Credentials().get(1), app).search() ?: return
|
||||
if (res[0].containsKey("gotResults")) {
|
||||
var latestVersion = res[1]["title"]!!
|
||||
latestVersion = Regex("""(?<=v?)\d+\.\d+(\.\d+)?""").findAll(latestVersion)
|
||||
.map { it.groupValues[0] }.toList()[0]
|
||||
val arrayInstalledVersion: MutableList<String> =
|
||||
installedAppVersion.split(".") as MutableList<String>
|
||||
val arrayLatestVersion: MutableList<String> =
|
||||
latestVersion.split(".") as MutableList<String>
|
||||
while (arrayInstalledVersion.size > arrayLatestVersion.size) arrayLatestVersion.add(
|
||||
"0")
|
||||
while (arrayInstalledVersion.size < arrayLatestVersion.size) arrayInstalledVersion.add(
|
||||
"0")
|
||||
var needUpdate = false
|
||||
for (i in arrayInstalledVersion.indices) {
|
||||
if (arrayLatestVersion[i].toInt() > arrayInstalledVersion[i].toInt()) needUpdate =
|
||||
true
|
||||
}
|
||||
if (needUpdate) {
|
||||
val sharedPref = context.getSharedPreferences(sharedPrefName,
|
||||
AppCompatActivity.MODE_PRIVATE)
|
||||
return newNotification(context,
|
||||
sharedPref.getInt("notifID", 0),
|
||||
app,
|
||||
latestVersion,
|
||||
"https://forum.mobilism.org${res[1]["link"]}")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
app/src/main/res/drawable/download.xml
Normal file
5
app/src/main/res/drawable/download.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#8E44AD"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M5,20h14v-2H5V20zM19,9h-4V3H9v6H5l7,7L19,9z"/>
|
||||
</vector>
|
|
@ -32,4 +32,11 @@
|
|||
<string name="changelogs">Changements</string>
|
||||
<string name="downloads">Téléchargements</string>
|
||||
<string name="noLinkFound">Aucun lien trouvé</string>
|
||||
<string name="descriptionNotification">Mise à jour des applications</string>
|
||||
<string name="newUpdateTitleNotification">Nouvelle version pour</string>
|
||||
<string name="newUpdateVersionNotification">Version</string>
|
||||
<string name="newUpdateAvailableNotification">disponible</string>
|
||||
<string name="forceUpdate">Force recherche de MàJ</string>
|
||||
<string name="rename">Renommer</string>
|
||||
<string name="config">Configurer</string>
|
||||
</resources>
|
|
@ -13,11 +13,20 @@
|
|||
<string name="newAppDialogTitle">New app name</string>
|
||||
<string name="validate">Validate</string>
|
||||
<string name="remove">Remove</string>
|
||||
<string name="config">Config</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="forceUpdate">Force update search</string>
|
||||
<string name="rename">Rename</string>
|
||||
<string name="updateCheck">Update check</string>
|
||||
<string name="notConnected">You are not logged in, redirection to the login page</string>
|
||||
<string name="dropBro">drop bro</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="descriptionNotification">Application Update</string>
|
||||
<string name="newUpdateTitleNotification">New version for</string>
|
||||
<string name="newUpdateVersionNotification">Version</string>
|
||||
<string name="newUpdateAvailableNotification">available</string>
|
||||
|
||||
<!-- Settings page -->
|
||||
<string name="titleSettings">Settings</string>
|
||||
<string name="changeCredentials">Change credentials</string>
|
||||
|
|
Reference in a new issue