Improve installer & related UI
This commit is contained in:
@@ -20,6 +20,8 @@
|
|||||||
package com.aurora.extensions
|
package com.aurora.extensions
|
||||||
|
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import java.io.StringWriter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
fun Long.toDate(): String {
|
fun Long.toDate(): String {
|
||||||
@@ -27,3 +29,11 @@ fun Long.toDate(): String {
|
|||||||
calendar.timeInMillis = this
|
calendar.timeInMillis = this
|
||||||
return DateFormat.format("dd/MM/yy", calendar).toString()
|
return DateFormat.format("dd/MM/yy", calendar).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Throwable.stackTraceToString(): String {
|
||||||
|
val stringWriter = StringWriter(1024)
|
||||||
|
val printWriter = PrintWriter(stringWriter)
|
||||||
|
printStackTrace(printWriter)
|
||||||
|
printWriter.close()
|
||||||
|
return stringWriter.toString()
|
||||||
|
}
|
||||||
@@ -36,6 +36,10 @@ class AuroraApplication : MultiDexApplication() {
|
|||||||
private lateinit var fetch: Fetch
|
private lateinit var fetch: Fetch
|
||||||
private lateinit var packageManagerReceiver: PackageManagerReceiver
|
private lateinit var packageManagerReceiver: PackageManagerReceiver
|
||||||
|
|
||||||
|
companion object{
|
||||||
|
val enqueuedInstalls: MutableSet<String> = mutableSetOf()
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
|
|||||||
@@ -28,4 +28,17 @@ sealed class BusEvent {
|
|||||||
var email: String = String(),
|
var email: String = String(),
|
||||||
var aasToken: String = String()
|
var aasToken: String = String()
|
||||||
) : BusEvent()
|
) : BusEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class SessionEvent {
|
||||||
|
data class Success(
|
||||||
|
var packageName: String? = "",
|
||||||
|
var extra: String? = ""
|
||||||
|
) : SessionEvent()
|
||||||
|
|
||||||
|
data class Failed(
|
||||||
|
var packageName: String? = "",
|
||||||
|
var error: String? = "",
|
||||||
|
var extra: String? = ""
|
||||||
|
) : SessionEvent()
|
||||||
}
|
}
|
||||||
@@ -25,9 +25,7 @@ import com.aurora.store.data.SingletonHolder
|
|||||||
import com.aurora.store.util.Preferences
|
import com.aurora.store.util.Preferences
|
||||||
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
|
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
|
||||||
|
|
||||||
open class AppInstaller private constructor(var context: Context) {
|
open class AppInstaller constructor(var context: Context) {
|
||||||
|
|
||||||
companion object : SingletonHolder<AppInstaller, Context>(::AppInstaller)
|
|
||||||
|
|
||||||
fun getPreferredInstaller(): IInstaller {
|
fun getPreferredInstaller(): IInstaller {
|
||||||
val prefValue = Preferences.getInteger(
|
val prefValue = Preferences.getInteger(
|
||||||
|
|||||||
@@ -24,24 +24,22 @@ import android.content.Intent
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import com.aurora.store.AuroraApplication
|
||||||
import com.aurora.store.BuildConfig
|
import com.aurora.store.BuildConfig
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
private val enqueuedInstalls: MutableSet<String> = mutableSetOf()
|
|
||||||
|
|
||||||
abstract class InstallerBase(protected var context: Context) : IInstaller {
|
abstract class InstallerBase(protected var context: Context) : IInstaller {
|
||||||
|
|
||||||
|
|
||||||
override fun clearQueue() {
|
override fun clearQueue() {
|
||||||
enqueuedInstalls.clear()
|
AuroraApplication.enqueuedInstalls.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isAlreadyQueued(packageName: String): Boolean {
|
override fun isAlreadyQueued(packageName: String): Boolean {
|
||||||
return enqueuedInstalls.contains(packageName)
|
return AuroraApplication.enqueuedInstalls.contains(packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeFromInstallQueue(packageName: String) {
|
override fun removeFromInstallQueue(packageName: String) {
|
||||||
enqueuedInstalls.remove(packageName)
|
AuroraApplication.enqueuedInstalls.remove(packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun uninstall(packageName: String) {
|
override fun uninstall(packageName: String) {
|
||||||
|
|||||||
@@ -25,52 +25,69 @@ import android.content.pm.PackageInstaller
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import org.apache.commons.lang3.StringUtils
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.data.event.SessionEvent
|
||||||
|
import com.aurora.store.util.Log
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
class InstallerService : Service() {
|
class InstallerService : Service() {
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||||
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)
|
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -69)
|
||||||
val packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
|
val packageName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
|
||||||
|
val extra = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
|
||||||
//Send broadcast for the installation status of the package
|
when (status) {
|
||||||
sendStatusBroadcast(status, packageName)
|
PackageInstaller.STATUS_PENDING_USER_ACTION -> promptUser(intent)
|
||||||
|
else -> postStatus(status, packageName, extra)
|
||||||
//Launch user confirmation activity
|
|
||||||
if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
|
|
||||||
val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
|
||||||
confirmationIntent!!.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
|
|
||||||
confirmationIntent.putExtra(
|
|
||||||
Intent.EXTRA_INSTALLER_PACKAGE_NAME,
|
|
||||||
"com.android.vending"
|
|
||||||
)
|
|
||||||
confirmationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
try {
|
|
||||||
startActivity(confirmationIntent)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
sendStatusBroadcast(PackageInstaller.STATUS_FAILURE, packageName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stopSelf()
|
stopSelf()
|
||||||
return START_NOT_STICKY
|
return START_NOT_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
private fun promptUser(intent: Intent) {
|
||||||
private fun sendStatusBroadcast(status: Int, packageName: String?) {
|
val confirmationIntent: Intent? = intent.getParcelableExtra(Intent.EXTRA_INTENT)
|
||||||
if (StringUtils.isNotEmpty(packageName)) {
|
|
||||||
val statusIntent = Intent(ACTION_SESSION_INSTALLER)
|
confirmationIntent?.let {
|
||||||
statusIntent.putExtra(PackageInstaller.EXTRA_STATUS, status)
|
it.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
|
||||||
statusIntent.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName)
|
it.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, "com.android.vending")
|
||||||
sendBroadcast(statusIntent)
|
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
|
||||||
|
try {
|
||||||
|
startActivity(it)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun postStatus(status: Int, packageName: String?, extra: String?) {
|
||||||
|
when (status) {
|
||||||
|
PackageInstaller.STATUS_SUCCESS -> {
|
||||||
|
EventBus.getDefault().post(SessionEvent.Success(packageName, "Success"))
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val errorString = getErrorString(status)
|
||||||
|
Log.e("$packageName : $errorString")
|
||||||
|
EventBus.getDefault().post(SessionEvent.Failed(packageName, errorString, extra))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getErrorString(status: Int): String {
|
||||||
|
return when (status) {
|
||||||
|
PackageInstaller.STATUS_FAILURE_ABORTED -> getString(R.string.installer_status_user_action)
|
||||||
|
PackageInstaller.STATUS_FAILURE_BLOCKED -> getString(R.string.installer_status_failure_blocked)
|
||||||
|
PackageInstaller.STATUS_FAILURE_CONFLICT -> getString(R.string.installer_status_failure_conflict)
|
||||||
|
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> getString(R.string.installer_status_failure_incompatible)
|
||||||
|
PackageInstaller.STATUS_FAILURE_INVALID -> getString(R.string.installer_status_failure_invalid)
|
||||||
|
PackageInstaller.STATUS_FAILURE_STORAGE -> getString(R.string.installer_status_failure_storage)
|
||||||
|
else -> getString(R.string.installer_status_failure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val ACTION_SESSION_INSTALLER = "ACTION_SESSION_INSTALLER"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -20,11 +20,13 @@
|
|||||||
package com.aurora.store.data.installer
|
package com.aurora.store.data.installer
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.aurora.store.R
|
|
||||||
import com.aurora.store.util.Log
|
|
||||||
import com.aurora.extensions.isLAndAbove
|
import com.aurora.extensions.isLAndAbove
|
||||||
import com.aurora.extensions.toast
|
import com.aurora.extensions.toast
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.data.event.SessionEvent
|
||||||
|
import com.aurora.store.util.Log
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
@@ -50,7 +52,12 @@ class RootInstaller(context: Context) : InstallerBase(context) {
|
|||||||
xInstallLegacy(packageName, it)
|
xInstallLegacy(packageName, it)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.toast(context.getString(R.string.installer_root_unavailable))
|
val event = SessionEvent.Failed(
|
||||||
|
packageName,
|
||||||
|
context.getString(R.string.installer_status_failure),
|
||||||
|
context.getString(R.string.installer_root_unavailable)
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(event)
|
||||||
Log.e(" >>>>>>>>>>>>>>>>>>>>>>>>>> NO ROOT ACCESS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
|
Log.e(" >>>>>>>>>>>>>>>>>>>>>>>>>> NO ROOT ACCESS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,12 +87,34 @@ class RootInstaller(context: Context) : InstallerBase(context) {
|
|||||||
.exec()
|
.exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
Shell.su("pm install-commit $sessionId").exec()
|
val shellResult = Shell.su("pm install-commit $sessionId").exec()
|
||||||
|
|
||||||
|
if (!shellResult.isSuccess) {
|
||||||
|
removeFromInstallQueue(packageName)
|
||||||
|
val event = SessionEvent.Failed(
|
||||||
|
packageName,
|
||||||
|
context.getString(R.string.installer_status_failure),
|
||||||
|
parseError(shellResult)
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(event)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
removeFromInstallQueue(packageName)
|
removeFromInstallQueue(packageName)
|
||||||
|
val event = SessionEvent.Failed(
|
||||||
|
packageName,
|
||||||
|
context.getString(R.string.installer_status_failure),
|
||||||
|
context.getString(R.string.installer_root_unavailable)
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(event)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
removeFromInstallQueue(packageName)
|
removeFromInstallQueue(packageName)
|
||||||
|
val event = SessionEvent.Failed(
|
||||||
|
packageName,
|
||||||
|
context.getString(R.string.installer_status_failure),
|
||||||
|
context.getString(R.string.installer_status_failure_session)
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,4 +125,8 @@ class RootInstaller(context: Context) : InstallerBase(context) {
|
|||||||
removeFromInstallQueue(packageName)
|
removeFromInstallQueue(packageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseError(result: Shell.Result): String {
|
||||||
|
return result.err.joinToString(separator = "\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,10 @@ import androidx.core.content.FileProvider
|
|||||||
import com.aurora.services.IPrivilegedCallback
|
import com.aurora.services.IPrivilegedCallback
|
||||||
import com.aurora.services.IPrivilegedService
|
import com.aurora.services.IPrivilegedService
|
||||||
import com.aurora.store.BuildConfig
|
import com.aurora.store.BuildConfig
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.data.event.SessionEvent
|
||||||
import com.aurora.store.util.Log
|
import com.aurora.store.util.Log
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class ServiceInstaller(context: Context) : InstallerBase(context) {
|
class ServiceInstaller(context: Context) : InstallerBase(context) {
|
||||||
@@ -65,27 +68,51 @@ class ServiceInstaller(context: Context) : InstallerBase(context) {
|
|||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
private fun xInstall(packageName: String, uriList: List<Uri>) {
|
private fun xInstall(packageName: String, uriList: List<Uri>) {
|
||||||
|
|
||||||
val serviceConnection = object : ServiceConnection {
|
val serviceConnection = object : ServiceConnection {
|
||||||
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
|
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
|
||||||
val service = IPrivilegedService.Stub.asInterface(binder)
|
val service = IPrivilegedService.Stub.asInterface(binder)
|
||||||
val callback = object : IPrivilegedCallback.Stub() {
|
|
||||||
override fun handleResult(packageName: String, returnCode: Int) {
|
|
||||||
removeFromInstallQueue(packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if (service.hasPrivilegedPermissions()) {
|
||||||
service.installSplitPackage(
|
Log.i(context.getString(R.string.installer_service_available))
|
||||||
packageName,
|
val callback = object : IPrivilegedCallback.Stub() {
|
||||||
uriList,
|
override fun handleResult(packageName: String, returnCode: Int) {
|
||||||
ACTION_INSTALL_REPLACE_EXISTING,
|
Log.e("$packageName : $returnCode")
|
||||||
BuildConfig.APPLICATION_ID,
|
removeFromInstallQueue(packageName)
|
||||||
callback
|
}
|
||||||
)
|
}
|
||||||
} catch (e: RemoteException) {
|
|
||||||
removeFromInstallQueue(packageName)
|
try {
|
||||||
Log.e("Failed to connect Aurora Services")
|
service.installSplitPackage(
|
||||||
|
packageName,
|
||||||
|
uriList,
|
||||||
|
ACTION_INSTALL_REPLACE_EXISTING,
|
||||||
|
BuildConfig.APPLICATION_ID,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
} catch (e: RemoteException) {
|
||||||
|
removeFromInstallQueue(packageName)
|
||||||
|
EventBus
|
||||||
|
.getDefault()
|
||||||
|
.post(
|
||||||
|
SessionEvent.Failed(
|
||||||
|
packageName,
|
||||||
|
e.localizedMessage,
|
||||||
|
e.stackTraceToString()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Log.e("Failed to connect Aurora Services")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e(context.getString(R.string.installer_service_unavailable))
|
||||||
|
EventBus
|
||||||
|
.getDefault()
|
||||||
|
.post(
|
||||||
|
SessionEvent.Failed(
|
||||||
|
packageName,
|
||||||
|
context.getString(R.string.installer_status_failure),
|
||||||
|
context.getString(R.string.installer_service_unavailable)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,12 @@ import android.net.Uri
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import com.aurora.extensions.isNAndAbove
|
||||||
import com.aurora.store.BuildConfig
|
import com.aurora.store.BuildConfig
|
||||||
|
import com.aurora.store.data.event.SessionEvent
|
||||||
import com.aurora.store.util.Log
|
import com.aurora.store.util.Log
|
||||||
import org.apache.commons.io.IOUtils
|
import org.apache.commons.io.IOUtils
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class SessionInstaller(context: Context) : InstallerBase(context) {
|
class SessionInstaller(context: Context) : InstallerBase(context) {
|
||||||
@@ -57,18 +60,22 @@ class SessionInstaller(context: Context) : InstallerBase(context) {
|
|||||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
private fun xInstall(packageName: String, uriList: List<Uri>) {
|
private fun xInstall(packageName: String, uriList: List<Uri>) {
|
||||||
val packageInstaller = context.packageManager.packageInstaller
|
val packageInstaller = context.packageManager.packageInstaller
|
||||||
val sessionParams = SessionParams(SessionParams.MODE_FULL_INSTALL)
|
val sessionParams = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
|
||||||
|
setAppPackageName(packageName)
|
||||||
|
if (isNAndAbove()) {
|
||||||
|
setOriginatingUid(android.os.Process.myUid())
|
||||||
|
}
|
||||||
|
}
|
||||||
val sessionId = packageInstaller.createSession(sessionParams)
|
val sessionId = packageInstaller.createSession(sessionParams)
|
||||||
val session = packageInstaller.openSession(sessionId)
|
val session = packageInstaller.openSession(sessionId)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Log.i("Writing splits to session for $packageName")
|
Log.i("Writing splits to session for $packageName")
|
||||||
var apkId = 1
|
|
||||||
for (uri in uriList) {
|
for (uri in uriList) {
|
||||||
val inputStream = context.contentResolver.openInputStream(uri)
|
val inputStream = context.contentResolver.openInputStream(uri)
|
||||||
val outputStream = session.openWrite(
|
val outputStream = session.openWrite(
|
||||||
"${packageName}_${apkId++}",
|
"${packageName}_${System.currentTimeMillis()}",
|
||||||
0,
|
0,
|
||||||
-1
|
-1
|
||||||
)
|
)
|
||||||
@@ -81,11 +88,11 @@ class SessionInstaller(context: Context) : InstallerBase(context) {
|
|||||||
IOUtils.close(outputStream)
|
IOUtils.close(outputStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
val intent = Intent(context, InstallerService::class.java)
|
val callBackIntent = Intent(context, InstallerService::class.java)
|
||||||
val pendingIntent = PendingIntent.getService(
|
val pendingIntent = PendingIntent.getService(
|
||||||
context,
|
context,
|
||||||
sessionId,
|
sessionId,
|
||||||
intent,
|
callBackIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -95,6 +102,12 @@ class SessionInstaller(context: Context) : InstallerBase(context) {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
session.abandon()
|
session.abandon()
|
||||||
removeFromInstallQueue(packageName)
|
removeFromInstallQueue(packageName)
|
||||||
|
val event = SessionEvent.Failed(
|
||||||
|
packageName,
|
||||||
|
e.localizedMessage,
|
||||||
|
e.stackTraceToString()
|
||||||
|
)
|
||||||
|
EventBus.getDefault().post(event)
|
||||||
Log.e("Failed to install $packageName : %s", e.message)
|
Log.e("Failed to install $packageName : %s", e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ open class PackageManagerReceiver : BroadcastReceiver() {
|
|||||||
.post(InstallEvent(packageName, ""))
|
.post(InstallEvent(packageName, ""))
|
||||||
|
|
||||||
//Clear installation queue
|
//Clear installation queue
|
||||||
AppInstaller.with(context)
|
AppInstaller(context)
|
||||||
.getPreferredInstaller()
|
.getPreferredInstaller()
|
||||||
.removeFromInstallQueue(packageName)
|
.removeFromInstallQueue(packageName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,12 @@ import android.util.ArrayMap
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
|
import com.aurora.extensions.getStyledAttributeColor
|
||||||
import com.aurora.extensions.isLAndAbove
|
import com.aurora.extensions.isLAndAbove
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.downloader.DownloadManager
|
import com.aurora.store.data.downloader.DownloadManager
|
||||||
|
import com.aurora.store.data.event.SessionEvent
|
||||||
import com.aurora.store.data.installer.AppInstaller
|
import com.aurora.store.data.installer.AppInstaller
|
||||||
import com.aurora.store.data.receiver.DownloadCancelReceiver
|
import com.aurora.store.data.receiver.DownloadCancelReceiver
|
||||||
import com.aurora.store.data.receiver.DownloadPauseReceiver
|
import com.aurora.store.data.receiver.DownloadPauseReceiver
|
||||||
@@ -46,6 +48,8 @@ import com.google.gson.Gson
|
|||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.tonyodev.fetch2.*
|
import com.tonyodev.fetch2.*
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
import org.greenrobot.eventbus.Subscribe
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -83,6 +87,8 @@ class NotificationService : Service() {
|
|||||||
|
|
||||||
Log.i("Notification Service Started")
|
Log.i("Notification Service Started")
|
||||||
|
|
||||||
|
EventBus.getDefault().register(this)
|
||||||
|
|
||||||
notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
//Create Notification Channels : General & Alert
|
//Create Notification Channels : General & Alert
|
||||||
@@ -97,9 +103,6 @@ class NotificationService : Service() {
|
|||||||
|
|
||||||
override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
||||||
showNotification(groupId, download, fetchGroup)
|
showNotification(groupId, download, fetchGroup)
|
||||||
if (fetchGroup.groupDownloadProgress == 100) {
|
|
||||||
install(download.tag!!, fetchGroup.downloads)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(
|
override fun onError(
|
||||||
@@ -214,8 +217,8 @@ class NotificationService : Service() {
|
|||||||
}
|
}
|
||||||
val progress = fetchGroup.groupDownloadProgress
|
val progress = fetchGroup.groupDownloadProgress
|
||||||
val progressBigText = NotificationCompat.BigTextStyle()
|
val progressBigText = NotificationCompat.BigTextStyle()
|
||||||
when (status) {
|
|
||||||
|
|
||||||
|
when (status) {
|
||||||
Status.QUEUED -> {
|
Status.QUEUED -> {
|
||||||
builder.setProgress(100, 0, true)
|
builder.setProgress(100, 0, true)
|
||||||
progressBigText.bigText(getString(R.string.download_queued))
|
progressBigText.bigText(getString(R.string.download_queued))
|
||||||
@@ -308,7 +311,6 @@ class NotificationService : Service() {
|
|||||||
app.id,
|
app.id,
|
||||||
builder.build()
|
builder.build()
|
||||||
)
|
)
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPauseIntent(groupId: Int): PendingIntent {
|
private fun getPauseIntent(groupId: Int): PendingIntent {
|
||||||
@@ -356,9 +358,28 @@ class NotificationService : Service() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe()
|
||||||
|
fun onEventMainThread(event: Any) {
|
||||||
|
when (event) {
|
||||||
|
is SessionEvent.Success -> {
|
||||||
|
val app = appMap[event.packageName.hashCode()]
|
||||||
|
if (app != null)
|
||||||
|
notifyInstallationStatus(app, event.extra)
|
||||||
|
}
|
||||||
|
is SessionEvent.Failed -> {
|
||||||
|
val app = appMap[event.packageName.hashCode()]
|
||||||
|
if (app != null)
|
||||||
|
notifyInstallationStatus(app, event.error)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun install(packageName: String, files: List<Download>) {
|
private fun install(packageName: String, files: List<Download>) {
|
||||||
AppInstaller.with(this)
|
AppInstaller(this)
|
||||||
.getPreferredInstaller()
|
.getPreferredInstaller()
|
||||||
.install(
|
.install(
|
||||||
packageName,
|
packageName,
|
||||||
@@ -370,9 +391,25 @@ class NotificationService : Service() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun notifyInstallationStatus(app: App, status: String?) {
|
||||||
|
val builder = NotificationCompat.Builder(this, Constants.NOTIFICATION_CHANNEL_ALERT)
|
||||||
|
builder.color = getStyledAttributeColor(R.attr.colorAccent)
|
||||||
|
builder.setSmallIcon(R.drawable.ic_install)
|
||||||
|
builder.setContentTitle(app.displayName)
|
||||||
|
builder.setContentText(status)
|
||||||
|
builder.setSubText(app.packageName)
|
||||||
|
|
||||||
|
notificationManager.notify(
|
||||||
|
app.packageName,
|
||||||
|
app.id,
|
||||||
|
builder.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Log.i("Notification Service Stopped")
|
Log.i("Notification Service Stopped")
|
||||||
fetch.removeListener(fetchListener)
|
fetch.removeListener(fetchListener)
|
||||||
|
EventBus.getDefault().unregister(this);
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,16 +17,22 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.aurora.store.view.custom
|
package com.aurora.store.view.custom.layouts.button
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
|
||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.aurora.extensions.getString
|
||||||
|
import com.aurora.extensions.isLAndAbove
|
||||||
|
import com.aurora.extensions.runOnUiThread
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.databinding.ViewActionButtonBinding
|
import com.aurora.store.databinding.ViewActionButtonBinding
|
||||||
|
import nl.komponents.kovenant.task
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class ActionButton : RelativeLayout {
|
class ActionButton : RelativeLayout {
|
||||||
|
|
||||||
@@ -64,23 +70,71 @@ class ActionButton : RelativeLayout {
|
|||||||
|
|
||||||
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ActionButton)
|
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ActionButton)
|
||||||
val btnTxt = typedArray.getString(R.styleable.ActionButton_btnActionText)
|
val btnTxt = typedArray.getString(R.styleable.ActionButton_btnActionText)
|
||||||
|
|
||||||
|
val btnTxtColor = typedArray.getResourceId(
|
||||||
|
R.styleable.ActionButton_btnActionTextColor,
|
||||||
|
R.color.colorWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
val stateIcon = typedArray.getResourceId(
|
||||||
|
R.styleable.ActionButton_btnActionIcon,
|
||||||
|
R.drawable.ic_check
|
||||||
|
)
|
||||||
|
|
||||||
|
val stateColor = ContextCompat.getColor(context, btnTxtColor)
|
||||||
|
|
||||||
B.btn.text = btnTxt
|
B.btn.text = btnTxt
|
||||||
|
B.btn.setTextColor(stateColor)
|
||||||
|
B.img.setImageDrawable(ContextCompat.getDrawable(context, stateIcon))
|
||||||
|
if (isLAndAbove()) {
|
||||||
|
B.img.imageTintList = ColorStateList.valueOf(stateColor)
|
||||||
|
}
|
||||||
|
|
||||||
typedArray.recycle()
|
typedArray.recycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setText(text: String) {
|
fun setText(text: String) {
|
||||||
|
B.viewFlipper.displayedChild = 0
|
||||||
B.btn.text = text
|
B.btn.text = text
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateProgress(isVisible: Boolean) {
|
fun setText(text: Int) {
|
||||||
if (isVisible)
|
B.viewFlipper.displayedChild = 0
|
||||||
B.progress.visibility = View.VISIBLE
|
B.btn.text = getString(text)
|
||||||
else
|
}
|
||||||
|
|
||||||
B.progress.visibility = View.INVISIBLE
|
fun updateState(state: State) {
|
||||||
|
val displayChild = when (state) {
|
||||||
|
State.IDLE -> 0
|
||||||
|
State.PROGRESS -> 1
|
||||||
|
State.COMPLETE -> 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if (B.viewFlipper.displayedChild != displayChild) {
|
||||||
|
runOnUiThread {
|
||||||
|
B.viewFlipper.displayedChild = displayChild
|
||||||
|
|
||||||
|
if (displayChild == 2)
|
||||||
|
switchToIdle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun switchToIdle() {
|
||||||
|
task {
|
||||||
|
TimeUnit.SECONDS.sleep(3)
|
||||||
|
} success {
|
||||||
|
updateState(State.IDLE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addOnClickListener(onClickListener: OnClickListener) {
|
fun addOnClickListener(onClickListener: OnClickListener) {
|
||||||
B.btn.setOnClickListener(onClickListener)
|
B.btn.setOnClickListener(onClickListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class State {
|
||||||
|
IDLE,
|
||||||
|
PROGRESS,
|
||||||
|
COMPLETE,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.aurora.store.view.custom
|
package com.aurora.store.view.custom.layouts.button
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@@ -0,0 +1,398 @@
|
|||||||
|
package com.aurora.store.view.custom.progress;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.animation.AnimationUtils;
|
||||||
|
|
||||||
|
import com.aurora.store.R;
|
||||||
|
import com.aurora.store.view.custom.progress.indicators.BallPulseIndicator;
|
||||||
|
import com.aurora.store.view.custom.progress.indicators.Indicator;
|
||||||
|
|
||||||
|
|
||||||
|
public class AuroraProgressView extends View {
|
||||||
|
|
||||||
|
private static final String TAG = "AuroraProgressView";
|
||||||
|
|
||||||
|
private static final BallPulseIndicator DEFAULT_INDICATOR = new BallPulseIndicator();
|
||||||
|
|
||||||
|
private static final int MIN_SHOW_TIME = 500; // ms
|
||||||
|
private static final int MIN_DELAY = 500; // ms
|
||||||
|
|
||||||
|
private long startTime = -1;
|
||||||
|
|
||||||
|
private boolean postedHide = false;
|
||||||
|
private boolean postedShow = false;
|
||||||
|
private boolean dismissed = false;
|
||||||
|
|
||||||
|
private final Runnable mDelayedHide = new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
postedHide = false;
|
||||||
|
startTime = -1;
|
||||||
|
setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final Runnable mDelayedShow = new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
postedShow = false;
|
||||||
|
if (!dismissed) {
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int minWidth;
|
||||||
|
int maxWidth;
|
||||||
|
int minHeight;
|
||||||
|
int maxHeight;
|
||||||
|
|
||||||
|
private Indicator indicator;
|
||||||
|
private int indicatorColor;
|
||||||
|
|
||||||
|
private boolean mShouldStartAnimationDrawable;
|
||||||
|
|
||||||
|
public AuroraProgressView(Context context) {
|
||||||
|
super(context);
|
||||||
|
init(context, null, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuroraProgressView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init(context, attrs, 0, R.style.AuroraProgressView);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuroraProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init(context, attrs, defStyleAttr, R.style.AuroraProgressView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
public AuroraProgressView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
init(context, attrs, defStyleAttr, R.style.AuroraProgressView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
minWidth = 24;
|
||||||
|
maxWidth = 48;
|
||||||
|
minHeight = 24;
|
||||||
|
maxHeight = 48;
|
||||||
|
|
||||||
|
final TypedArray a = context.obtainStyledAttributes(
|
||||||
|
attrs, R.styleable.AuroraProgressView, defStyleAttr, defStyleRes);
|
||||||
|
|
||||||
|
minWidth = a.getDimensionPixelSize(R.styleable.AuroraProgressView_minWidth, minWidth);
|
||||||
|
maxWidth = a.getDimensionPixelSize(R.styleable.AuroraProgressView_maxWidth, maxWidth);
|
||||||
|
minHeight = a.getDimensionPixelSize(R.styleable.AuroraProgressView_minHeight, minHeight);
|
||||||
|
maxHeight = a.getDimensionPixelSize(R.styleable.AuroraProgressView_maxHeight, maxHeight);
|
||||||
|
String indicatorName = a.getString(R.styleable.AuroraProgressView_indicatorName);
|
||||||
|
indicatorColor = a.getColor(R.styleable.AuroraProgressView_indicatorColor, Color.WHITE);
|
||||||
|
setIndicator(indicatorName);
|
||||||
|
|
||||||
|
if (indicator == null) {
|
||||||
|
setIndicator(DEFAULT_INDICATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Indicator getIndicator() {
|
||||||
|
return indicator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndicator(Indicator d) {
|
||||||
|
if (indicator != d) {
|
||||||
|
if (indicator != null) {
|
||||||
|
indicator.setCallback(null);
|
||||||
|
unscheduleDrawable(indicator);
|
||||||
|
}
|
||||||
|
|
||||||
|
indicator = d;
|
||||||
|
//need to set indicator color again if you didn't specified when you update the indicator .
|
||||||
|
setIndicatorColor(indicatorColor);
|
||||||
|
if (d != null) {
|
||||||
|
d.setCallback(this);
|
||||||
|
}
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndicatorColor(int color) {
|
||||||
|
this.indicatorColor = color;
|
||||||
|
indicator.setColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndicator(String indicatorName) {
|
||||||
|
if (TextUtils.isEmpty(indicatorName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StringBuilder drawableClassName = new StringBuilder();
|
||||||
|
if (!indicatorName.contains(".")) {
|
||||||
|
String defaultPackageName = getClass().getPackage().getName();
|
||||||
|
drawableClassName.append(defaultPackageName)
|
||||||
|
.append(".indicators")
|
||||||
|
.append(".");
|
||||||
|
}
|
||||||
|
drawableClassName.append(indicatorName);
|
||||||
|
try {
|
||||||
|
Class<?> drawableClass = Class.forName(drawableClassName.toString());
|
||||||
|
Indicator indicator = (Indicator) drawableClass.newInstance();
|
||||||
|
setIndicator(indicator);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
Log.e(TAG, "Didn't find your class , check the name again !");
|
||||||
|
} catch (InstantiationException | IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void smoothToShow() {
|
||||||
|
startAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_in));
|
||||||
|
setVisibility(VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void smoothToHide() {
|
||||||
|
startAnimation(AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_out));
|
||||||
|
setVisibility(GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void hide() {
|
||||||
|
dismissed = true;
|
||||||
|
removeCallbacks(mDelayedShow);
|
||||||
|
long diff = System.currentTimeMillis() - startTime;
|
||||||
|
if (diff >= MIN_SHOW_TIME || startTime == -1) {
|
||||||
|
// The progress spinner has been shown long enough
|
||||||
|
// OR was not shown yet. If it wasn't shown yet,
|
||||||
|
// it will just never be shown.
|
||||||
|
setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
// The progress spinner is shown, but not long enough,
|
||||||
|
// so put a delayed message in to hide it when its been
|
||||||
|
// shown long enough.
|
||||||
|
if (!postedHide) {
|
||||||
|
postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
|
||||||
|
postedHide = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void show() {
|
||||||
|
// Reset the start time.
|
||||||
|
startTime = -1;
|
||||||
|
dismissed = false;
|
||||||
|
removeCallbacks(mDelayedHide);
|
||||||
|
if (!postedShow) {
|
||||||
|
postDelayed(mDelayedShow, MIN_DELAY);
|
||||||
|
postedShow = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean verifyDrawable(Drawable who) {
|
||||||
|
return who == indicator
|
||||||
|
|| super.verifyDrawable(who);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startAnimation() {
|
||||||
|
if (getVisibility() != VISIBLE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indicator != null) {
|
||||||
|
mShouldStartAnimationDrawable = true;
|
||||||
|
}
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopAnimation() {
|
||||||
|
if (indicator != null) {
|
||||||
|
indicator.stop();
|
||||||
|
mShouldStartAnimationDrawable = false;
|
||||||
|
}
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisibility(int v) {
|
||||||
|
if (getVisibility() != v) {
|
||||||
|
super.setVisibility(v);
|
||||||
|
if (v == GONE || v == INVISIBLE) {
|
||||||
|
stopAnimation();
|
||||||
|
} else {
|
||||||
|
startAnimation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onVisibilityChanged(View changedView, int visibility) {
|
||||||
|
super.onVisibilityChanged(changedView, visibility);
|
||||||
|
if (visibility == GONE || visibility == INVISIBLE) {
|
||||||
|
stopAnimation();
|
||||||
|
} else {
|
||||||
|
startAnimation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateDrawable(Drawable dr) {
|
||||||
|
if (verifyDrawable(dr)) {
|
||||||
|
final Rect dirty = dr.getBounds();
|
||||||
|
final int scrollX = getScrollX() + getPaddingLeft();
|
||||||
|
final int scrollY = getScrollY() + getPaddingTop();
|
||||||
|
|
||||||
|
invalidate(dirty.left + scrollX, dirty.top + scrollY,
|
||||||
|
dirty.right + scrollX, dirty.bottom + scrollY);
|
||||||
|
} else {
|
||||||
|
super.invalidateDrawable(dr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
updateDrawableBounds(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDrawableBounds(int w, int h) {
|
||||||
|
// onDraw will translate the canvas so we draw starting at 0,0.
|
||||||
|
// Subtract out padding for the purposes of the calculations below.
|
||||||
|
w -= getPaddingRight() + getPaddingLeft();
|
||||||
|
h -= getPaddingTop() + getPaddingBottom();
|
||||||
|
|
||||||
|
int right = w;
|
||||||
|
int bottom = h;
|
||||||
|
int top = 0;
|
||||||
|
int left = 0;
|
||||||
|
|
||||||
|
if (indicator != null) {
|
||||||
|
// Maintain aspect ratio. Certain kinds of animated drawables
|
||||||
|
// get very confused otherwise.
|
||||||
|
final int intrinsicWidth = indicator.getIntrinsicWidth();
|
||||||
|
final int intrinsicHeight = indicator.getIntrinsicHeight();
|
||||||
|
final float intrinsicAspect = (float) intrinsicWidth / intrinsicHeight;
|
||||||
|
final float boundAspect = (float) w / h;
|
||||||
|
if (intrinsicAspect != boundAspect) {
|
||||||
|
if (boundAspect > intrinsicAspect) {
|
||||||
|
// New width is larger. Make it smaller to match height.
|
||||||
|
final int width = (int) (h * intrinsicAspect);
|
||||||
|
left = (w - width) / 2;
|
||||||
|
right = left + width;
|
||||||
|
} else {
|
||||||
|
// New height is larger. Make it smaller to match width.
|
||||||
|
final int height = (int) (w * (1 / intrinsicAspect));
|
||||||
|
top = (h - height) / 2;
|
||||||
|
bottom = top + height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
indicator.setBounds(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected synchronized void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
drawTrack(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawTrack(Canvas canvas) {
|
||||||
|
final Drawable d = indicator;
|
||||||
|
if (d != null) {
|
||||||
|
// Translate canvas so a indeterminate circular progress bar with padding
|
||||||
|
// rotates properly in its animation
|
||||||
|
final int saveCount = canvas.save();
|
||||||
|
|
||||||
|
canvas.translate(getPaddingLeft(), getPaddingTop());
|
||||||
|
|
||||||
|
d.draw(canvas);
|
||||||
|
canvas.restoreToCount(saveCount);
|
||||||
|
|
||||||
|
if (mShouldStartAnimationDrawable && d instanceof Animatable) {
|
||||||
|
((Animatable) d).start();
|
||||||
|
mShouldStartAnimationDrawable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
int dw = 0;
|
||||||
|
int dh = 0;
|
||||||
|
|
||||||
|
final Drawable d = indicator;
|
||||||
|
if (d != null) {
|
||||||
|
dw = Math.max(minWidth, Math.min(maxWidth, d.getIntrinsicWidth()));
|
||||||
|
dh = Math.max(minHeight, Math.min(maxHeight, d.getIntrinsicHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDrawableState();
|
||||||
|
|
||||||
|
dw += getPaddingLeft() + getPaddingRight();
|
||||||
|
dh += getPaddingTop() + getPaddingBottom();
|
||||||
|
|
||||||
|
final int measuredWidth = resolveSizeAndState(dw, widthMeasureSpec, 0);
|
||||||
|
final int measuredHeight = resolveSizeAndState(dh, heightMeasureSpec, 0);
|
||||||
|
setMeasuredDimension(measuredWidth, measuredHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawableStateChanged() {
|
||||||
|
super.drawableStateChanged();
|
||||||
|
updateDrawableState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDrawableState() {
|
||||||
|
final int[] state = getDrawableState();
|
||||||
|
if (indicator != null && indicator.isStateful()) {
|
||||||
|
indicator.setState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
@Override
|
||||||
|
public void drawableHotspotChanged(float x, float y) {
|
||||||
|
super.drawableHotspotChanged(x, y);
|
||||||
|
|
||||||
|
if (indicator != null) {
|
||||||
|
indicator.setHotspot(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
startAnimation();
|
||||||
|
removeCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDetachedFromWindow() {
|
||||||
|
stopAnimation();
|
||||||
|
// This should come after stopAnimation(), otherwise an invalidate message remains in the
|
||||||
|
// queue, which can prevent the entire view hierarchy from being GC'ed during a rotation
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
removeCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeCallbacks() {
|
||||||
|
removeCallbacks(mDelayedHide);
|
||||||
|
removeCallbacks(mDelayedShow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.aurora.store.view.custom.progress.indicators
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class BallPulseIndicator : Indicator() {
|
||||||
|
|
||||||
|
private val scaleFloats = floatArrayOf(
|
||||||
|
0.75f,
|
||||||
|
1.0f,
|
||||||
|
0.75f
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun draw(canvas: Canvas, paint: Paint) {
|
||||||
|
val circleSpacing = 4f
|
||||||
|
val radius = (width.coerceAtMost(height) - circleSpacing * 2) / 6
|
||||||
|
val x = width / 2f - (radius * 2 + circleSpacing)
|
||||||
|
val y = height / 2f
|
||||||
|
|
||||||
|
for (i in 0..2) {
|
||||||
|
canvas.save()
|
||||||
|
val translateX = x + radius * 2 * i + circleSpacing * i
|
||||||
|
canvas.translate(translateX, y)
|
||||||
|
canvas.scale(scaleFloats[i], scaleFloats[i])
|
||||||
|
canvas.drawCircle(0f, 0f, radius, paint)
|
||||||
|
canvas.restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateAnimators(): ArrayList<ValueAnimator> {
|
||||||
|
val animators = ArrayList<ValueAnimator>()
|
||||||
|
val delays = intArrayOf(120, 240, 360)
|
||||||
|
|
||||||
|
for (i in 0..2) {
|
||||||
|
val scaleAnim = ValueAnimator.ofFloat(1f, 0.3f, 1f)
|
||||||
|
scaleAnim.duration = 750
|
||||||
|
scaleAnim.repeatCount = -1
|
||||||
|
scaleAnim.startDelay = delays[i].toLong()
|
||||||
|
|
||||||
|
addUpdateListener(scaleAnim) { animation: ValueAnimator ->
|
||||||
|
scaleFloats[i] = animation.animatedValue as Float
|
||||||
|
postInvalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
animators.add(scaleAnim)
|
||||||
|
}
|
||||||
|
return animators
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
package com.aurora.store.view.custom.progress.indicators;
|
||||||
|
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public abstract class Indicator extends Drawable implements Animatable {
|
||||||
|
|
||||||
|
private static final Rect ZERO_BOUNDS_RECT = new Rect();
|
||||||
|
|
||||||
|
private final HashMap<ValueAnimator, ValueAnimator.AnimatorUpdateListener> updateListenerHashMap = new HashMap<>();
|
||||||
|
private final Paint paint = new Paint();
|
||||||
|
|
||||||
|
private int alpha = 255;
|
||||||
|
private ArrayList<ValueAnimator> animators;
|
||||||
|
private boolean hasAnimators;
|
||||||
|
|
||||||
|
protected Rect drawBounds = ZERO_BOUNDS_RECT;
|
||||||
|
|
||||||
|
public Indicator() {
|
||||||
|
paint.setColor(Color.WHITE);
|
||||||
|
paint.setStyle(Paint.Style.FILL);
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColor() {
|
||||||
|
return paint.getColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int color) {
|
||||||
|
paint.setColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int alpha) {
|
||||||
|
this.alpha = alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAlpha() {
|
||||||
|
return alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity() {
|
||||||
|
return PixelFormat.OPAQUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(ColorFilter colorFilter) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Canvas canvas) {
|
||||||
|
draw(canvas, paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void draw(Canvas canvas, Paint paint);
|
||||||
|
|
||||||
|
public abstract ArrayList<ValueAnimator> onCreateAnimators();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
ensureAnimators();
|
||||||
|
|
||||||
|
if (animators == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the animators has not ended, do nothing.
|
||||||
|
if (isStarted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startAnimators();
|
||||||
|
invalidateSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startAnimators() {
|
||||||
|
for (int i = 0; i < animators.size(); i++) {
|
||||||
|
ValueAnimator animator = animators.get(i);
|
||||||
|
|
||||||
|
//when the animator restart , add the updateListener again because they
|
||||||
|
// was removed by animator stop .
|
||||||
|
ValueAnimator.AnimatorUpdateListener updateListener = updateListenerHashMap.get(animator);
|
||||||
|
if (updateListener != null) {
|
||||||
|
animator.addUpdateListener(updateListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
animator.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopAnimators() {
|
||||||
|
if (animators != null) {
|
||||||
|
for (ValueAnimator animator : animators) {
|
||||||
|
if (animator != null && animator.isStarted()) {
|
||||||
|
animator.removeAllUpdateListeners();
|
||||||
|
animator.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ensureAnimators() {
|
||||||
|
if (!hasAnimators) {
|
||||||
|
animators = onCreateAnimators();
|
||||||
|
hasAnimators = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
stopAnimators();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isStarted() {
|
||||||
|
for (ValueAnimator animator : animators) {
|
||||||
|
return animator.isStarted();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
for (ValueAnimator animator : animators) {
|
||||||
|
return animator.isRunning();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addUpdateListener(ValueAnimator animator, ValueAnimator.AnimatorUpdateListener updateListener) {
|
||||||
|
updateListenerHashMap.put(animator, updateListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBoundsChange(Rect bounds) {
|
||||||
|
super.onBoundsChange(bounds);
|
||||||
|
setDrawBounds(bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDrawBounds(Rect drawBounds) {
|
||||||
|
setDrawBounds(drawBounds.left, drawBounds.top, drawBounds.right, drawBounds.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDrawBounds(int left, int top, int right, int bottom) {
|
||||||
|
this.drawBounds = new Rect(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postInvalidate() {
|
||||||
|
invalidateSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rect getDrawBounds() {
|
||||||
|
return drawBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return drawBounds.width();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return drawBounds.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int centerX() {
|
||||||
|
return drawBounds.centerX();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int centerY() {
|
||||||
|
return drawBounds.centerY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float exactCenterX() {
|
||||||
|
return drawBounds.exactCenterX();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float exactCenterY() {
|
||||||
|
return drawBounds.exactCenterY();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -29,9 +29,7 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.extensions.isLAndAbove
|
import com.aurora.extensions.*
|
||||||
import com.aurora.extensions.load
|
|
||||||
import com.aurora.extensions.toast
|
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.AuthData
|
import com.aurora.gplayapi.data.models.AuthData
|
||||||
import com.aurora.gplayapi.data.models.File
|
import com.aurora.gplayapi.data.models.File
|
||||||
@@ -42,13 +40,15 @@ import com.aurora.store.R
|
|||||||
import com.aurora.store.data.downloader.DownloadManager
|
import com.aurora.store.data.downloader.DownloadManager
|
||||||
import com.aurora.store.data.downloader.RequestBuilder
|
import com.aurora.store.data.downloader.RequestBuilder
|
||||||
import com.aurora.store.data.event.BusEvent
|
import com.aurora.store.data.event.BusEvent
|
||||||
|
import com.aurora.store.data.event.SessionEvent
|
||||||
import com.aurora.store.data.installer.AppInstaller
|
import com.aurora.store.data.installer.AppInstaller
|
||||||
import com.aurora.store.data.network.HttpClient
|
import com.aurora.store.data.network.HttpClient
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
import com.aurora.store.data.providers.AuthProvider
|
||||||
import com.aurora.store.databinding.ActivityDetailsBinding
|
import com.aurora.store.databinding.ActivityDetailsBinding
|
||||||
import com.aurora.store.util.*
|
import com.aurora.store.util.*
|
||||||
import com.aurora.extensions.*
|
import com.aurora.store.view.custom.layouts.button.ActionButton
|
||||||
import com.aurora.store.view.ui.downloads.DownloadActivity
|
import com.aurora.store.view.ui.downloads.DownloadActivity
|
||||||
|
import com.aurora.store.view.ui.sheets.InstallErrorDialogSheet
|
||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
||||||
@@ -102,13 +102,29 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onEventMainThread(event: BusEvent) {
|
fun onEventMainThread(event: Any) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is BusEvent.InstallEvent -> {
|
is BusEvent.InstallEvent -> {
|
||||||
attachActions()
|
if (app.packageName == event.packageName) {
|
||||||
|
attachActions()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
is BusEvent.UninstallEvent -> {
|
is BusEvent.UninstallEvent -> {
|
||||||
attachActions()
|
if (app.packageName == event.packageName) {
|
||||||
|
attachActions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is SessionEvent.Failed -> {
|
||||||
|
if (app.packageName == event.packageName) {
|
||||||
|
InstallErrorDialogSheet.newInstance(
|
||||||
|
app,
|
||||||
|
event.packageName,
|
||||||
|
event.error,
|
||||||
|
event.extra
|
||||||
|
).show(supportFragmentManager, "SED")
|
||||||
|
attachActions()
|
||||||
|
updateActionState(ActionButton.State.IDLE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|
||||||
@@ -208,6 +224,10 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
checkAndSetupInstall()
|
checkAndSetupInstall()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateActionState(state: ActionButton.State) {
|
||||||
|
B.layoutDetailsInstall.btnDownload.updateState(state)
|
||||||
|
}
|
||||||
|
|
||||||
private fun openApp() {
|
private fun openApp() {
|
||||||
val intent = PackageUtil.getLaunchIntent(this, app.packageName)
|
val intent = PackageUtil.getLaunchIntent(this, app.packageName)
|
||||||
if (intent != null) {
|
if (intent != null) {
|
||||||
@@ -221,8 +241,10 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun install(files: List<Download>) {
|
private fun install(files: List<Download>) {
|
||||||
|
updateActionState(ActionButton.State.IDLE)
|
||||||
|
|
||||||
task {
|
task {
|
||||||
AppInstaller.with(this)
|
AppInstaller(this)
|
||||||
.getPreferredInstaller()
|
.getPreferredInstaller()
|
||||||
.install(
|
.install(
|
||||||
app.packageName,
|
app.packageName,
|
||||||
@@ -241,7 +263,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun uninstallApp() {
|
private fun uninstallApp() {
|
||||||
AppInstaller.with(this)
|
AppInstaller(this)
|
||||||
.getPreferredInstaller()
|
.getPreferredInstaller()
|
||||||
.uninstall(app.packageName)
|
.uninstall(app.packageName)
|
||||||
}
|
}
|
||||||
@@ -363,6 +385,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun purchase() {
|
private fun purchase() {
|
||||||
|
updateActionState(ActionButton.State.PROGRESS)
|
||||||
task {
|
task {
|
||||||
val authData = AuthProvider
|
val authData = AuthProvider
|
||||||
.with(this)
|
.with(this)
|
||||||
@@ -376,9 +399,11 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
enqueue(it)
|
enqueue(it)
|
||||||
} else {
|
} else {
|
||||||
Log.e("Failed to download : ${app.displayName}")
|
Log.e("Failed to download : ${app.displayName}")
|
||||||
|
updateActionState(ActionButton.State.IDLE)
|
||||||
}
|
}
|
||||||
} failUi {
|
} failUi {
|
||||||
expandBottomSheet(it.message)
|
expandBottomSheet(it.message)
|
||||||
|
updateActionState(ActionButton.State.IDLE)
|
||||||
Log.e("Failed to purchase ${app.displayName} : ${it.message}")
|
Log.e("Failed to purchase ${app.displayName} : ${it.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,6 +427,7 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
Log.i("Downloading Apks : %s", app.displayName)
|
Log.i("Downloading Apks : %s", app.displayName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
updateActionState(ActionButton.State.IDLE)
|
||||||
expandBottomSheet(getString(R.string.purchase_no_file))
|
expandBottomSheet(getString(R.string.purchase_no_file))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -443,8 +469,14 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
bottomSheetBehavior.isHideable = false
|
bottomSheetBehavior.isHideable = false
|
||||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||||
|
|
||||||
B.layoutDetailsInstall.txtPurchaseError.text = message
|
with(B.layoutDetailsInstall) {
|
||||||
B.layoutDetailsInstall.btnDownload.updateProgress(false)
|
txtPurchaseError.text = message
|
||||||
|
btnDownload.updateState(ActionButton.State.IDLE)
|
||||||
|
if (app.isFree)
|
||||||
|
btnDownload.setText(R.string.action_install)
|
||||||
|
else
|
||||||
|
btnDownload.setText(app.price)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkAndSetupInstall() {
|
private fun checkAndSetupInstall() {
|
||||||
@@ -459,39 +491,40 @@ class AppDetailsActivity : BaseDetailsActivity() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (isUpdatable) {
|
if (isUpdatable) {
|
||||||
btn.setText(getString(R.string.action_update))
|
btn.setText(R.string.action_update)
|
||||||
btn.addOnClickListener {
|
btn.addOnClickListener {
|
||||||
btn.updateProgress(true)
|
|
||||||
startDownload()
|
startDownload()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
btn.setText(getString(R.string.action_open))
|
btn.setText(R.string.action_open)
|
||||||
btn.addOnClickListener { openApp() }
|
btn.addOnClickListener { openApp() }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (app.isFree) {
|
if (app.isFree) {
|
||||||
btn.setText(getString(R.string.action_install))
|
btn.setText(R.string.action_install)
|
||||||
} else {
|
} else {
|
||||||
btn.setText(app.price)
|
btn.setText(app.price)
|
||||||
}
|
}
|
||||||
|
|
||||||
btn.addOnClickListener {
|
btn.addOnClickListener {
|
||||||
btn.setText(getString(R.string.download_metadata))
|
btn.setText(R.string.download_metadata)
|
||||||
btn.updateProgress(true)
|
|
||||||
startDownload()
|
startDownload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
btn.updateProgress(false)
|
btn.updateState(ActionButton.State.IDLE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
private fun flip(nextView: Int) {
|
private fun flip(nextView: Int) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
B.layoutDetailsInstall.viewFlipper.displayedChild = nextView
|
val displayChild = B.layoutDetailsInstall.viewFlipper.displayedChild
|
||||||
if (nextView == 0)
|
if (displayChild != nextView) {
|
||||||
checkAndSetupInstall()
|
B.layoutDetailsInstall.viewFlipper.displayedChild = nextView
|
||||||
|
if (nextView == 0)
|
||||||
|
checkAndSetupInstall()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ class AppMenuSheet : BaseBottomSheet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_uninstall -> {
|
R.id.action_uninstall -> {
|
||||||
AppInstaller
|
AppInstaller(requireContext())
|
||||||
.with(requireContext())
|
|
||||||
.getPreferredInstaller().uninstall(app.packageName)
|
.getPreferredInstaller().uninstall(app.packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ abstract class BaseBottomSheet : BottomSheetDialogFragment() {
|
|||||||
|
|
||||||
lateinit var VM: SheetBaseBinding
|
lateinit var VM: SheetBaseBinding
|
||||||
|
|
||||||
var gson: Gson = GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create()
|
var gson: Gson = GsonBuilder()
|
||||||
|
.excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT)
|
||||||
|
.create()
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val bottomSheetDialog = BottomSheetDialog(
|
val bottomSheetDialog = BottomSheetDialog(
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Aurora Store
|
||||||
|
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
*
|
||||||
|
* Aurora Store 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 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Aurora Store 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 Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.view.ui.sheets
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.aurora.Constants
|
||||||
|
import com.aurora.extensions.copyToClipBoard
|
||||||
|
import com.aurora.extensions.load
|
||||||
|
import com.aurora.extensions.px
|
||||||
|
import com.aurora.extensions.toast
|
||||||
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.databinding.SheetInstallErrorBinding
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.CircleCrop
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||||
|
|
||||||
|
class InstallErrorDialogSheet : BaseBottomSheet() {
|
||||||
|
|
||||||
|
private lateinit var B: SheetInstallErrorBinding
|
||||||
|
|
||||||
|
private lateinit var app: App
|
||||||
|
private lateinit var title: String
|
||||||
|
private lateinit var error: String
|
||||||
|
private lateinit var extra: String
|
||||||
|
|
||||||
|
private var rawApp = String()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val DIALOG_TITLE = "DIALOG_TITLE"
|
||||||
|
private const val DIALOG_ERROR = "DIALOG_ERROR"
|
||||||
|
private const val DIALOG_EXTRA = "DIALOG_EXTRA"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun newInstance(
|
||||||
|
app: App,
|
||||||
|
title: String?,
|
||||||
|
error: String?,
|
||||||
|
extra: String?
|
||||||
|
): InstallErrorDialogSheet {
|
||||||
|
return InstallErrorDialogSheet().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putString(Constants.STRING_EXTRA, gson.toJson(app))
|
||||||
|
putString(DIALOG_TITLE, title)
|
||||||
|
putString(DIALOG_ERROR, error)
|
||||||
|
putString(DIALOG_EXTRA, extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateContentView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
B = SheetInstallErrorBinding.inflate(inflater, container, false)
|
||||||
|
|
||||||
|
val bundle = arguments
|
||||||
|
bundle?.let {
|
||||||
|
rawApp = bundle.getString(Constants.STRING_EXTRA, "{}")
|
||||||
|
app = gson.fromJson(rawApp, App::class.java)
|
||||||
|
title = bundle.getString(DIALOG_TITLE, "")
|
||||||
|
error = bundle.getString(DIALOG_ERROR, "")
|
||||||
|
extra = bundle.getString(DIALOG_EXTRA, "")
|
||||||
|
|
||||||
|
if (app.packageName.isNotEmpty()) {
|
||||||
|
inflateData()
|
||||||
|
} else {
|
||||||
|
dismissAllowingStateLoss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attachAction()
|
||||||
|
|
||||||
|
return B.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inflateData() {
|
||||||
|
B.imgIcon.load(app.iconArtwork.url) {
|
||||||
|
transform(CircleCrop())
|
||||||
|
}
|
||||||
|
|
||||||
|
B.txtLine1.text = app.displayName
|
||||||
|
B.txtLine2.text = error
|
||||||
|
B.txtLine3.text = extra
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun attachAction() {
|
||||||
|
B.btnPrimary.setOnClickListener {
|
||||||
|
dismissAllowingStateLoss()
|
||||||
|
}
|
||||||
|
|
||||||
|
B.btnSecondary.setOnClickListener {
|
||||||
|
if (::extra.isInitialized) {
|
||||||
|
requireContext().copyToClipBoard(extra)
|
||||||
|
requireContext().toast(R.string.toast_clipboard_copied)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -93,16 +93,22 @@ class UpdatesFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch.addListener(fetchListener)
|
|
||||||
|
|
||||||
return B.root
|
return B.root
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
if (::fetch.isInitialized && ::fetchListener.isInitialized) {
|
||||||
|
fetch.addListener(fetchListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
if (::fetch.isInitialized && ::fetchListener.isInitialized) {
|
if (::fetch.isInitialized && ::fetchListener.isInitialized) {
|
||||||
fetch.removeListener(fetchListener)
|
fetch.removeListener(fetchListener)
|
||||||
}
|
}
|
||||||
super.onDestroyView()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
@@ -249,7 +255,7 @@ class UpdatesFragment : BaseFragment() {
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
private fun install(packageName: String, files: List<Download>) {
|
private fun install(packageName: String, files: List<Download>) {
|
||||||
task {
|
task {
|
||||||
AppInstaller.with(requireContext())
|
AppInstaller(requireContext())
|
||||||
.getPreferredInstaller()
|
.getPreferredInstaller()
|
||||||
.install(
|
.install(
|
||||||
packageName,
|
packageName,
|
||||||
|
|||||||
28
app/src/main/res/drawable/ic_install.xml
Normal file
28
app/src/main/res/drawable/ic_install.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!--
|
||||||
|
~ Aurora Store
|
||||||
|
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
~
|
||||||
|
~ Aurora Store 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 2 of the License, or
|
||||||
|
~ (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ Aurora Store 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 Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M9,2v2L5,4l-0.001,10h14L19,4h-4L15,2h5a1,1 0,0 1,1 1v18a1,1 0,0 1,-1 1L4,22a1,1 0,0 1,-1 -1L3,3a1,1 0,0 1,1 -1h5zM18.999,16h-14L5,20h14l-0.001,-4zM17,17v2h-2v-2h2zM13,2v5h3l-4,4 -4,-4h3L11,2h2z"/>
|
||||||
|
</vector>
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
android:text="@string/account_login_using"
|
android:text="@string/account_login_using"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_google"
|
android:id="@+id/btn_google"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
app:btnStateIcon="@drawable/ic_google"
|
app:btnStateIcon="@drawable/ic_google"
|
||||||
app:btnStateText="@string/account_google" />
|
app:btnStateText="@string/account_google" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_anonymous"
|
android:id="@+id/btn_anonymous"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
android:text="@string/account_logout"
|
android:text="@string/account_logout"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_logout"
|
android:id="@+id/btn_logout"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -96,7 +96,7 @@
|
|||||||
android:text="@string/account_login_using"
|
android:text="@string/account_login_using"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_google"
|
android:id="@+id/btn_google"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
app:btnStateIcon="@drawable/ic_google"
|
app:btnStateIcon="@drawable/ic_google"
|
||||||
app:btnStateText="@string/account_google" />
|
app:btnStateText="@string/account_google" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_anonymous"
|
android:id="@+id/btn_anonymous"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -36,9 +36,13 @@
|
|||||||
<ViewFlipper
|
<ViewFlipper
|
||||||
android:id="@+id/view_flipper"
|
android:id="@+id/view_flipper"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:animateFirstView="true"
|
||||||
|
android:inAnimation="@anim/fade_in"
|
||||||
|
android:outAnimation="@anim/fade_out"
|
||||||
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
<com.aurora.store.view.custom.ActionButton
|
<com.aurora.store.view.custom.layouts.button.ActionButton
|
||||||
android:id="@+id/btn_download"
|
android:id="@+id/btn_download"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/height_button"
|
android:layout_height="@dimen/height_button"
|
||||||
@@ -114,7 +118,6 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/img_cancel"
|
android:id="@+id/img_cancel"
|
||||||
android:layout_width="@dimen/icon_size_small"
|
android:layout_width="@dimen/icon_size_small"
|
||||||
@@ -126,7 +129,6 @@
|
|||||||
android:padding="@dimen/padding_medium"
|
android:padding="@dimen/padding_medium"
|
||||||
app:srcCompat="@drawable/ic_cancel" />
|
app:srcCompat="@drawable/ic_cancel" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</ViewFlipper>
|
</ViewFlipper>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -131,7 +131,7 @@
|
|||||||
android:text="@string/account_login_using"
|
android:text="@string/account_login_using"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_google"
|
android:id="@+id/btn_google"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
app:btnStateIcon="@drawable/ic_google"
|
app:btnStateIcon="@drawable/ic_google"
|
||||||
app:btnStateText="@string/account_google" />
|
app:btnStateText="@string/account_google" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_anonymous"
|
android:id="@+id/btn_anonymous"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
android:text="@string/account_logout"
|
android:text="@string/account_logout"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_logout"
|
android:id="@+id/btn_logout"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -89,7 +89,7 @@
|
|||||||
android:text="@string/account_login_using"
|
android:text="@string/account_login_using"
|
||||||
android:textAlignment="center" />
|
android:textAlignment="center" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_google"
|
android:id="@+id/btn_google"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
app:btnStateIcon="@drawable/ic_google"
|
app:btnStateIcon="@drawable/ic_google"
|
||||||
app:btnStateText="@string/account_google" />
|
app:btnStateText="@string/account_google" />
|
||||||
|
|
||||||
<com.aurora.store.view.custom.StateButton
|
<com.aurora.store.view.custom.layouts.button.StateButton
|
||||||
android:id="@+id/btn_anonymous"
|
android:id="@+id/btn_anonymous"
|
||||||
android:layout_width="@dimen/width_button"
|
android:layout_width="@dimen/width_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -36,9 +36,13 @@
|
|||||||
<ViewFlipper
|
<ViewFlipper
|
||||||
android:id="@+id/view_flipper"
|
android:id="@+id/view_flipper"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:animateFirstView="true"
|
||||||
|
android:inAnimation="@anim/fade_in"
|
||||||
|
android:outAnimation="@anim/fade_out"
|
||||||
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
<com.aurora.store.view.custom.ActionButton
|
<com.aurora.store.view.custom.layouts.button.ActionButton
|
||||||
android:id="@+id/btn_download"
|
android:id="@+id/btn_download"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/height_button"
|
android:layout_height="@dimen/height_button"
|
||||||
@@ -113,7 +117,6 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:id="@+id/img_cancel"
|
android:id="@+id/img_cancel"
|
||||||
android:layout_width="@dimen/icon_size_small"
|
android:layout_width="@dimen/icon_size_small"
|
||||||
@@ -125,7 +128,6 @@
|
|||||||
android:padding="@dimen/padding_medium"
|
android:padding="@dimen/padding_medium"
|
||||||
app:srcCompat="@drawable/ic_cancel" />
|
app:srcCompat="@drawable/ic_cancel" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</ViewFlipper>
|
</ViewFlipper>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
110
app/src/main/res/layout/sheet_install_error.xml
Normal file
110
app/src/main/res/layout/sheet_install_error.xml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~ Aurora Store
|
||||||
|
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||||
|
~
|
||||||
|
~ Aurora Store 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 2 of the License, or
|
||||||
|
~ (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ Aurora Store 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 Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:divider="@drawable/divider"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/padding_large"
|
||||||
|
android:showDividers="middle">
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/img_icon"
|
||||||
|
android:layout_width="@dimen/icon_size_category"
|
||||||
|
android:layout_height="@dimen/icon_size_category"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
tools:src="@drawable/bg_circle" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txt_header"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginStart="@dimen/margin_normal"
|
||||||
|
android:layout_marginBottom="@dimen/margin_normal"
|
||||||
|
android:layout_toEndOf="@id/img_icon"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="@string/title_installer"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAppearance="@style/TextAppearance.Aurora.SubTitle" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txt_line1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="3"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAppearance="@style/TextAppearance.Aurora.Line1"
|
||||||
|
tools:text="Title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txt_line2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="10"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAppearance="@style/TextAppearance.Aurora.Line2"
|
||||||
|
android:textColor="@color/colorRed"
|
||||||
|
tools:text="Error" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txt_line3"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxLines="10"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
android:textAppearance="@style/TextAppearance.Aurora.Line2"
|
||||||
|
tools:text="Extra" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:weightSum="2">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_secondary"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="@dimen/height_button"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/action_copy" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_primary"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="@dimen/height_button"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/action_ok" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
@@ -21,37 +21,59 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="@dimen/height_button">
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<ViewFlipper
|
||||||
android:id="@+id/btn"
|
android:id="@+id/view_flipper"
|
||||||
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_centerHorizontal="true"
|
android:animateFirstView="true"
|
||||||
android:paddingVertical="@dimen/padding_normal"
|
android:inAnimation="@anim/fade_in"
|
||||||
android:paddingStart="24dp"
|
android:outAnimation="@anim/fade_out"
|
||||||
android:paddingEnd="24dp"
|
tools:ignore="UselessParent">
|
||||||
android:textAllCaps="true"
|
|
||||||
android:textAppearance="@style/TextAppearance.Aurora.SubTitle"
|
|
||||||
android:textColor="@color/colorWhite"
|
|
||||||
app:iconPadding="@dimen/padding_large"
|
|
||||||
app:iconTint="?colorAccent"
|
|
||||||
tools:text="Install" />
|
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
<RelativeLayout
|
||||||
android:id="@+id/progress"
|
android:layout_width="match_parent"
|
||||||
style="?android:attr/progressBarStyle"
|
android:layout_height="match_parent">
|
||||||
android:layout_width="@dimen/icon_size_default"
|
|
||||||
android:layout_height="@dimen/icon_size_default"
|
<com.google.android.material.button.MaterialButton
|
||||||
android:layout_alignEnd="@id/btn"
|
android:id="@+id/btn"
|
||||||
android:layout_centerVertical="true"
|
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog.Flush"
|
||||||
android:layout_marginEnd="@dimen/icon_size_default"
|
android:layout_width="match_parent"
|
||||||
android:elevation="8dp"
|
android:layout_height="match_parent"
|
||||||
android:indeterminateTint="@color/colorWhite"
|
android:layout_centerHorizontal="true"
|
||||||
android:progressTint="@color/colorWhite"
|
android:textAllCaps="true"
|
||||||
android:visibility="invisible"
|
android:textAppearance="@style/TextAppearance.Aurora.SubTitle"
|
||||||
tools:visibility="visible" />
|
app:iconPadding="@dimen/padding_large"
|
||||||
|
app:iconTint="@color/colorWhite"
|
||||||
|
tools:text="Install" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.aurora.store.view.custom.progress.AuroraProgressView
|
||||||
|
android:id="@+id/progress"
|
||||||
|
android:layout_width="@dimen/icon_size_small"
|
||||||
|
android:layout_height="@dimen/icon_size_small"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
tools:indicatorColor="@color/colorAccent" />
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/img"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
app:srcCompat="@drawable/ic_check"
|
||||||
|
app:tint="@color/colorWhite" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</ViewFlipper>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_copy"
|
android:id="@+id/action_copy"
|
||||||
android:title="@string/action_copy" />
|
android:title="@string/action_copy_link" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_pause"
|
android:id="@+id/action_pause"
|
||||||
android:title="@string/action_pause" />
|
android:title="@string/action_pause" />
|
||||||
|
|||||||
@@ -38,6 +38,24 @@
|
|||||||
|
|
||||||
<declare-styleable name="ActionButton">
|
<declare-styleable name="ActionButton">
|
||||||
<attr name="btnActionText" format="string" />
|
<attr name="btnActionText" format="string" />
|
||||||
|
<attr name="btnActionTextColor" format="string" />
|
||||||
|
<attr name="btnActionIcon" format="string" />
|
||||||
|
<attr name="btnActionBackground" format="enum">
|
||||||
|
<enum name="outlined" value="0" />
|
||||||
|
<enum name="roundedOutlined" value="1" />
|
||||||
|
<enum name="flat" value="2" />
|
||||||
|
<enum name="roundedFlat" value="3" />
|
||||||
|
<enum name="none" value="4" />
|
||||||
|
</attr>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="AuroraProgressView">
|
||||||
|
<attr name="minWidth" format="dimension" />
|
||||||
|
<attr name="maxWidth" format="dimension" />
|
||||||
|
<attr name="minHeight" format="dimension" />
|
||||||
|
<attr name="maxHeight" format="dimension" />
|
||||||
|
<attr name="indicatorName" format="string" />
|
||||||
|
<attr name="indicatorColor" format="color" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<declare-styleable name="DevInfoLayout">
|
<declare-styleable name="DevInfoLayout">
|
||||||
|
|||||||
@@ -56,7 +56,9 @@
|
|||||||
<string name="action_clear">"Clear"</string>
|
<string name="action_clear">"Clear"</string>
|
||||||
<string name="action_clear_all">"Clear all"</string>
|
<string name="action_clear_all">"Clear all"</string>
|
||||||
<string name="action_clear_restart">"Clear data"</string>
|
<string name="action_clear_restart">"Clear data"</string>
|
||||||
<string name="action_copy">"Copy Link"</string>
|
<string name="action_close">"Close"</string>
|
||||||
|
<string name="action_copy">"Copy"</string>
|
||||||
|
<string name="action_copy_link">"Copy Link"</string>
|
||||||
<string name="action_disable">"Disable"</string>
|
<string name="action_disable">"Disable"</string>
|
||||||
<string name="action_discard">"Discard"</string>
|
<string name="action_discard">"Discard"</string>
|
||||||
<string name="action_discover">"Discover"</string>
|
<string name="action_discover">"Discover"</string>
|
||||||
@@ -92,6 +94,7 @@
|
|||||||
<string name="action_manual">"Manual download"</string>
|
<string name="action_manual">"Manual download"</string>
|
||||||
<string name="action_more">"More"</string>
|
<string name="action_more">"More"</string>
|
||||||
<string name="action_next">"Next"</string>
|
<string name="action_next">"Next"</string>
|
||||||
|
<string name="action_ok">"Okay"</string>
|
||||||
<string name="action_open">"Open"</string>
|
<string name="action_open">"Open"</string>
|
||||||
<string name="action_pause">"Pause"</string>
|
<string name="action_pause">"Pause"</string>
|
||||||
<string name="action_pending">"Pending"</string>
|
<string name="action_pending">"Pending"</string>
|
||||||
@@ -201,9 +204,12 @@
|
|||||||
|
|
||||||
<string name="installer_root_available">"Root access available"</string>
|
<string name="installer_root_available">"Root access available"</string>
|
||||||
<string name="installer_root_unavailable">"No Root, grant root access or change the installer."</string>
|
<string name="installer_root_unavailable">"No Root, grant root access or change the installer."</string>
|
||||||
|
<string name="installer_service_available">"Aurora services is available and ready to install."</string>
|
||||||
|
<string name="installer_service_unavailable">"Aurora services not configured. Make sure service is enabled & all permissions are granted"</string>
|
||||||
<string name="installer_status_failure">"Installation failed"</string>
|
<string name="installer_status_failure">"Installation failed"</string>
|
||||||
<string name="installer_status_failure_aborted">"Installation cancelled"</string>
|
<string name="installer_status_failure_aborted">"Installation cancelled"</string>
|
||||||
<string name="installer_status_failure_blocked">"Installation was blocked"</string>
|
<string name="installer_status_failure_blocked">"Installation was blocked"</string>
|
||||||
|
<string name="installer_status_failure_session">"Could not create session"</string>
|
||||||
<string name="installer_status_failure_conflict">"Conflicting package exists"</string>
|
<string name="installer_status_failure_conflict">"Conflicting package exists"</string>
|
||||||
<string name="installer_status_failure_incompatible">"Incompatible app"</string>
|
<string name="installer_status_failure_incompatible">"Incompatible app"</string>
|
||||||
<string name="installer_status_failure_invalid">"Invalid or corrupted APK"</string>
|
<string name="installer_status_failure_invalid">"Invalid or corrupted APK"</string>
|
||||||
@@ -290,6 +296,7 @@
|
|||||||
<string name="title_games">"Games"</string>
|
<string name="title_games">"Games"</string>
|
||||||
<string name="title_installation">"Installation"</string>
|
<string name="title_installation">"Installation"</string>
|
||||||
<string name="title_installed">"Installed"</string>
|
<string name="title_installed">"Installed"</string>
|
||||||
|
<string name="title_installer">"App Installer"</string>
|
||||||
<string name="title_language">"Language"</string>
|
<string name="title_language">"Language"</string>
|
||||||
<string name="title_library">"Library"</string>
|
<string name="title_library">"Library"</string>
|
||||||
<string name="title_purchase_history">"Purchase history"</string>
|
<string name="title_purchase_history">"Purchase history"</string>
|
||||||
|
|||||||
@@ -72,4 +72,28 @@
|
|||||||
<style name="Aurora.BottomSheetDialog" parent="Theme.Design.BottomSheetDialog">
|
<style name="Aurora.BottomSheetDialog" parent="Theme.Design.BottomSheetDialog">
|
||||||
<item name="bottomSheetStyle">@style/Aurora.BottomSheetDialogStyle</item>
|
<item name="bottomSheetStyle">@style/Aurora.BottomSheetDialogStyle</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="AuroraProgressView">
|
||||||
|
<item name="minWidth">32dp</item>
|
||||||
|
<item name="maxWidth">46dp</item>
|
||||||
|
<item name="minHeight">36dp</item>
|
||||||
|
<item name="maxHeight">36dp</item>
|
||||||
|
<item name="indicatorName">BallPulseIndicator</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AuroraProgressView.Large">
|
||||||
|
<item name="minWidth">48dp</item>
|
||||||
|
<item name="maxWidth">48dp</item>
|
||||||
|
<item name="minHeight">56dp</item>
|
||||||
|
<item name="maxHeight">56dp</item>
|
||||||
|
<item name="indicatorName">BallPulseIndicator</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="AuroraProgressView.Small">
|
||||||
|
<item name="minWidth">24dp</item>
|
||||||
|
<item name="maxWidth">24dp</item>
|
||||||
|
<item name="minHeight">24dp</item>
|
||||||
|
<item name="maxHeight">24dp</item>
|
||||||
|
<item name="indicatorName">BallPulseIndicator</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user