DO NOT MERGE: WIP: Migrate AppDetailsActivity to fragment
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -92,43 +92,6 @@
|
|||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:launchMode="singleTask" />
|
android:launchMode="singleTask" />
|
||||||
|
|
||||||
<activity android:name=".view.ui.details.EmptyAppDetailsActivity"
|
|
||||||
android:noHistory="true"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
<data android:scheme="market" android:host="details" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
|
|
||||||
<data android:scheme="http" />
|
|
||||||
<data android:scheme="https" />
|
|
||||||
<data android:host="market.android.com" />
|
|
||||||
<data android:path="/store/apps/details" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.VIEW" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
|
||||||
|
|
||||||
<data android:scheme="http" />
|
|
||||||
<data android:scheme="https" />
|
|
||||||
<data android:host="play.google.com" />
|
|
||||||
<data android:path="/store/apps/details" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity android:name=".view.ui.details.AppDetailsActivity"
|
|
||||||
android:exported="false">
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<activity android:name=".view.ui.details.DevProfileActivity"
|
<activity android:name=".view.ui.details.DevProfileActivity"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -37,13 +37,13 @@ import androidx.core.app.ActivityOptionsCompat
|
|||||||
import androidx.core.app.ShareCompat
|
import androidx.core.app.ShareCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
|
import androidx.navigation.NavDeepLinkBuilder
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.store.MainActivity
|
import com.aurora.store.MainActivity
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.util.Log
|
import com.aurora.store.util.Log
|
||||||
import com.aurora.store.util.Preferences
|
import com.aurora.store.util.Preferences
|
||||||
import com.aurora.store.view.ui.details.AppDetailsActivity
|
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
val Context.inflater: LayoutInflater
|
val Context.inflater: LayoutInflater
|
||||||
@@ -58,12 +58,11 @@ fun Context.browse(url: String, showOpenInAuroraAction: Boolean = false) {
|
|||||||
if (showOpenInAuroraAction) {
|
if (showOpenInAuroraAction) {
|
||||||
val icon =
|
val icon =
|
||||||
ContextCompat.getDrawable(this, R.drawable.ic_open_in_new)?.toBitmap()
|
ContextCompat.getDrawable(this, R.drawable.ic_open_in_new)?.toBitmap()
|
||||||
val pendingIntent = PendingIntent.getActivity(
|
val pendingIntent = NavDeepLinkBuilder(this)
|
||||||
this,
|
.setGraph(R.navigation.mobile_navigation)
|
||||||
0,
|
.setDestination(R.id.appDetailsFragment)
|
||||||
Intent(this, AppDetailsActivity::class.java),
|
.setComponentName(MainActivity::class.java)
|
||||||
PendingIntent.FLAG_MUTABLE
|
.createPendingIntent()
|
||||||
)
|
|
||||||
customTabsIntent.setActionButton(
|
customTabsIntent.setActionButton(
|
||||||
icon!!,
|
icon!!,
|
||||||
this.getString(R.string.open_in_aurora),
|
this.getString(R.string.open_in_aurora),
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import android.util.ArrayMap
|
|||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import androidx.navigation.NavDeepLinkBuilder
|
import androidx.navigation.NavDeepLinkBuilder
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.extensions.getStyledAttributeColor
|
import com.aurora.extensions.getStyledAttributeColor
|
||||||
@@ -46,7 +47,6 @@ import com.aurora.store.data.receiver.DownloadResumeReceiver
|
|||||||
import com.aurora.store.data.receiver.InstallReceiver
|
import com.aurora.store.data.receiver.InstallReceiver
|
||||||
import com.aurora.store.util.CommonUtil
|
import com.aurora.store.util.CommonUtil
|
||||||
import com.aurora.store.util.Log
|
import com.aurora.store.util.Log
|
||||||
import com.aurora.store.view.ui.details.AppDetailsActivity
|
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import com.tonyodev.fetch2.*
|
import com.tonyodev.fetch2.*
|
||||||
@@ -313,17 +313,12 @@ class NotificationService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getContentIntentForDetails(app: App?): PendingIntent {
|
private fun getContentIntentForDetails(app: App?): PendingIntent {
|
||||||
val intent = Intent(this, AppDetailsActivity::class.java)
|
return NavDeepLinkBuilder(this)
|
||||||
intent.putExtra(Constants.STRING_EXTRA, gson.toJson(app))
|
.setGraph(R.navigation.mobile_navigation)
|
||||||
val flags = if (isMAndAbove())
|
.setDestination(R.id.appDetailsFragment)
|
||||||
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
.setComponentName(MainActivity::class.java)
|
||||||
else PendingIntent.FLAG_CANCEL_CURRENT
|
.setArguments(bundleOf("packageName" to app!!.packageName))
|
||||||
return PendingIntent.getActivity(
|
.createPendingIntent()
|
||||||
this,
|
|
||||||
packageName.hashCode(),
|
|
||||||
intent,
|
|
||||||
flags
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getContentIntentForDownloads(): PendingIntent {
|
private fun getContentIntentForDownloads(): PendingIntent {
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import androidx.appcompat.app.AppCompatActivity
|
|||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.store.data.model.Report
|
import com.aurora.store.data.model.Report
|
||||||
import com.aurora.store.view.ui.details.AppDetailsActivity
|
|
||||||
import com.aurora.store.view.ui.details.DetailsExodusActivity
|
import com.aurora.store.view.ui.details.DetailsExodusActivity
|
||||||
import com.aurora.store.view.ui.details.DevAppsActivity
|
import com.aurora.store.view.ui.details.DevAppsActivity
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
@@ -36,17 +35,6 @@ import java.lang.reflect.Modifier
|
|||||||
object NavigationUtil {
|
object NavigationUtil {
|
||||||
val gson: Gson = GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create()
|
val gson: Gson = GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT).create()
|
||||||
|
|
||||||
fun openDetailsActivity(context: Context, app: App) {
|
|
||||||
val intent = Intent(
|
|
||||||
context,
|
|
||||||
AppDetailsActivity::class.java
|
|
||||||
).apply {
|
|
||||||
putExtra(Constants.STRING_EXTRA, gson.toJson(app))
|
|
||||||
}
|
|
||||||
val options = ActivityOptions.makeSceneTransitionAnimation(context as AppCompatActivity)
|
|
||||||
context.startActivity(intent, options.toBundle())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun openDevAppsActivity(context: Context, app: App) {
|
fun openDevAppsActivity(context: Context, app: App) {
|
||||||
val intent = Intent(
|
val intent = Intent(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package com.aurora.store.view.ui.commons
|
|||||||
|
|
||||||
import android.app.ActivityOptions
|
import android.app.ActivityOptions
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
@@ -38,10 +37,7 @@ import com.aurora.store.view.ui.sheets.NetworkDialogSheet
|
|||||||
import com.aurora.store.view.ui.sheets.TOSSheet
|
import com.aurora.store.view.ui.sheets.TOSSheet
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import nl.komponents.kovenant.task
|
|
||||||
import nl.komponents.kovenant.ui.successUi
|
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
|
|
||||||
abstract class BaseActivity : AppCompatActivity(), NetworkProvider.NetworkListener {
|
abstract class BaseActivity : AppCompatActivity(), NetworkProvider.NetworkListener {
|
||||||
@@ -56,7 +52,7 @@ abstract class BaseActivity : AppCompatActivity(), NetworkProvider.NetworkListen
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun openDetailsActivity(app: App) {
|
fun openDetailsActivity(app: App) {
|
||||||
val intent = Intent(this, AppDetailsActivity::class.java)
|
val intent = Intent(this, AppDetailsFragment::class.java)
|
||||||
intent.putExtra(Constants.STRING_EXTRA, gson.toJson(app))
|
intent.putExtra(Constants.STRING_EXTRA, gson.toJson(app))
|
||||||
val options = ActivityOptions.makeSceneTransitionAnimation(this)
|
val options = ActivityOptions.makeSceneTransitionAnimation(this)
|
||||||
startActivity(intent, options.toBundle())
|
startActivity(intent, options.toBundle())
|
||||||
|
|||||||
@@ -23,11 +23,12 @@ import android.app.ActivityOptions
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.extensions.getEmptyActivityBundle
|
import com.aurora.extensions.getEmptyActivityBundle
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.Category
|
import com.aurora.gplayapi.data.models.Category
|
||||||
import com.aurora.store.view.ui.details.AppDetailsActivity
|
import com.aurora.store.MobileNavigationDirections
|
||||||
import com.aurora.store.view.ui.details.DevProfileActivity
|
import com.aurora.store.view.ui.details.DevProfileActivity
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
@@ -44,10 +45,9 @@ open class BaseFragment : Fragment {
|
|||||||
).create()
|
).create()
|
||||||
|
|
||||||
fun openDetailsActivity(app: App) {
|
fun openDetailsActivity(app: App) {
|
||||||
val intent = Intent(context, AppDetailsActivity::class.java)
|
findNavController().navigate(
|
||||||
intent.putExtra(Constants.STRING_EXTRA, Gson().toJson(app))
|
MobileNavigationDirections.actionGlobalAppDetailsFragment(app.packageName)
|
||||||
val options = ActivityOptions.makeSceneTransitionAnimation(requireActivity())
|
)
|
||||||
startActivity(intent, options.toBundle())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openCategoryBrowseActivity(category: Category) {
|
fun openCategoryBrowseActivity(category: Category) {
|
||||||
|
|||||||
@@ -1,846 +0,0 @@
|
|||||||
/*
|
|
||||||
* Aurora Store
|
|
||||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
|
||||||
* Copyright (C) 2022, The Calyx Institute
|
|
||||||
*
|
|
||||||
* 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.details
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.ServiceConnection
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.Environment
|
|
||||||
import android.os.IBinder
|
|
||||||
import android.provider.Settings
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.LinearLayout
|
|
||||||
import androidx.activity.addCallback
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import com.aurora.Constants
|
|
||||||
import com.aurora.extensions.*
|
|
||||||
import com.aurora.gplayapi.data.models.App
|
|
||||||
import com.aurora.gplayapi.data.models.AuthData
|
|
||||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
|
||||||
import com.aurora.store.MainActivity
|
|
||||||
import com.aurora.store.R
|
|
||||||
import com.aurora.store.State
|
|
||||||
import com.aurora.store.data.downloader.DownloadManager
|
|
||||||
import com.aurora.store.data.downloader.getGroupId
|
|
||||||
import com.aurora.store.data.event.BusEvent
|
|
||||||
import com.aurora.store.data.event.InstallerEvent
|
|
||||||
import com.aurora.store.data.installer.AppInstaller
|
|
||||||
import com.aurora.store.data.network.HttpClient
|
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
|
||||||
import com.aurora.store.data.service.AppMetadataStatusListener
|
|
||||||
import com.aurora.store.data.service.UpdateService
|
|
||||||
import com.aurora.store.databinding.ActivityDetailsBinding
|
|
||||||
import com.aurora.store.util.*
|
|
||||||
import com.aurora.store.view.ui.sheets.InstallErrorDialogSheet
|
|
||||||
import com.aurora.store.view.ui.sheets.ManualDownloadSheet
|
|
||||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
|
|
||||||
import com.tonyodev.fetch2.*
|
|
||||||
import com.tonyodev.fetch2core.DownloadBlock
|
|
||||||
import nl.komponents.kovenant.task
|
|
||||||
import nl.komponents.kovenant.ui.failUi
|
|
||||||
import nl.komponents.kovenant.ui.successUi
|
|
||||||
import org.greenrobot.eventbus.EventBus
|
|
||||||
import org.greenrobot.eventbus.Subscribe
|
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class AppDetailsActivity : BaseDetailsActivity() {
|
|
||||||
|
|
||||||
private lateinit var B: ActivityDetailsBinding
|
|
||||||
private lateinit var bottomSheetBehavior: BottomSheetBehavior<LinearLayout>
|
|
||||||
|
|
||||||
private val startForStorageManagerResult =
|
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
||||||
if (isRAndAbove() && Environment.isExternalStorageManager()) {
|
|
||||||
updateApp(app)
|
|
||||||
} else {
|
|
||||||
toast(R.string.permissions_denied)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private val startForPermissions =
|
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
|
||||||
if (it) updateApp(app) else toast(R.string.permissions_denied)
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var authData: AuthData
|
|
||||||
private lateinit var app: App
|
|
||||||
private var fetch: Fetch? = null
|
|
||||||
private var downloadManager: DownloadManager? = null
|
|
||||||
|
|
||||||
private var attachToServiceCalled = false
|
|
||||||
private var updateService: UpdateService? = null
|
|
||||||
private var pendingAddListeners = true
|
|
||||||
private var serviceConnection = object : ServiceConnection {
|
|
||||||
override fun onServiceConnected(name: ComponentName, binder: IBinder) {
|
|
||||||
updateService = (binder as UpdateService.UpdateServiceBinder).getUpdateService()
|
|
||||||
if (::fetchGroupListener.isInitialized && ::appMetadataListener.isInitialized && pendingAddListeners) {
|
|
||||||
updateService!!.registerFetchListener(fetchGroupListener)
|
|
||||||
// appMetadataListener needs to be initialized after the fetchGroupListener
|
|
||||||
updateService!!.registerAppMetadataListener(appMetadataListener)
|
|
||||||
pendingAddListeners = false
|
|
||||||
}
|
|
||||||
if (listOfActionsWhenServiceAttaches.isNotEmpty()) {
|
|
||||||
val iterator = listOfActionsWhenServiceAttaches.iterator()
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
val next = iterator.next()
|
|
||||||
next.run()
|
|
||||||
iterator.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServiceDisconnected(name: ComponentName) {
|
|
||||||
updateService = null
|
|
||||||
attachToServiceCalled = false
|
|
||||||
pendingAddListeners = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private lateinit var fetchGroupListener: FetchGroupListener
|
|
||||||
private lateinit var appMetadataListener: AppMetadataStatusListener
|
|
||||||
private lateinit var completionMarker: java.io.File
|
|
||||||
private lateinit var inProgressMarker: java.io.File
|
|
||||||
|
|
||||||
private var isExternal = false
|
|
||||||
private var isNone = false
|
|
||||||
private var status = Status.NONE
|
|
||||||
private var isInstalled: Boolean = false
|
|
||||||
private var isUpdatable: Boolean = false
|
|
||||||
private var autoDownload: Boolean = false
|
|
||||||
private var downloadOnly: Boolean = false
|
|
||||||
|
|
||||||
override fun onConnected() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDisconnected() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onReconnected() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStart() {
|
|
||||||
super.onStart()
|
|
||||||
EventBus.getDefault().register(this)
|
|
||||||
if (autoDownload) {
|
|
||||||
purchase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStop() {
|
|
||||||
EventBus.getDefault().unregister(this)
|
|
||||||
super.onStop()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
||||||
fun onEventMainThread(event: Any) {
|
|
||||||
when (event) {
|
|
||||||
is BusEvent.InstallEvent -> {
|
|
||||||
if (app.packageName == event.packageName) {
|
|
||||||
attachActions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is BusEvent.UninstallEvent -> {
|
|
||||||
if (app.packageName == event.packageName) {
|
|
||||||
attachActions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is BusEvent.ManualDownload -> {
|
|
||||||
if (app.packageName == event.packageName) {
|
|
||||||
app.versionCode = event.versionCode
|
|
||||||
purchase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is InstallerEvent.Failed -> {
|
|
||||||
if (app.packageName == event.packageName) {
|
|
||||||
InstallErrorDialogSheet.newInstance(
|
|
||||||
app,
|
|
||||||
event.packageName,
|
|
||||||
event.error,
|
|
||||||
event.extra
|
|
||||||
).show(supportFragmentManager, "SED")
|
|
||||||
attachActions()
|
|
||||||
updateActionState(State.IDLE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
B = ActivityDetailsBinding.inflate(layoutInflater)
|
|
||||||
setContentView(B.root)
|
|
||||||
|
|
||||||
onNewIntent(intent)
|
|
||||||
|
|
||||||
onBackPressedDispatcher.addCallback(this) {
|
|
||||||
if (isExternal) {
|
|
||||||
open(MainActivity::class.java, true)
|
|
||||||
} else {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
getUpdateServiceInstance()
|
|
||||||
checkAndSetupInstall()
|
|
||||||
super.onResume()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent) {
|
|
||||||
super.onNewIntent(intent)
|
|
||||||
|
|
||||||
if (intent.scheme != null && (intent.scheme == "market" || intent.scheme == "http" || intent.scheme == "https")) {
|
|
||||||
val packageName = intent.data!!.getQueryParameter("id")
|
|
||||||
val packageVersion = intent.data!!.getQueryParameter("v")
|
|
||||||
if (packageName.isNullOrEmpty()) {
|
|
||||||
finishAfterTransition()
|
|
||||||
} else {
|
|
||||||
isExternal = true
|
|
||||||
app = App(packageName)
|
|
||||||
if (!packageVersion.isNullOrEmpty()) {
|
|
||||||
app.versionCode = packageVersion.toInt()
|
|
||||||
}
|
|
||||||
fetchCompleteApp()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val rawApp: String? = intent.getStringExtra(Constants.STRING_EXTRA)
|
|
||||||
if (rawApp != null) {
|
|
||||||
app = gson.fromJson(rawApp, App::class.java)
|
|
||||||
isInstalled = PackageUtil.isInstalled(this, app.packageName)
|
|
||||||
|
|
||||||
inflatePartialApp()
|
|
||||||
fetchCompleteApp()
|
|
||||||
} else {
|
|
||||||
finishAfterTransition()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var uninstallActionEnabled = false
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
|
||||||
menuInflater.inflate(R.menu.menu_details, menu)
|
|
||||||
if (::app.isInitialized) {
|
|
||||||
val installed = PackageUtil.isInstalled(this, app.packageName)
|
|
||||||
menu?.findItem(R.id.action_uninstall)?.isVisible = installed
|
|
||||||
uninstallActionEnabled = installed
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
when (item.itemId) {
|
|
||||||
android.R.id.home -> {
|
|
||||||
onBackPressedDispatcher.onBackPressed()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
R.id.action_share -> {
|
|
||||||
share(app)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
R.id.action_uninstall -> {
|
|
||||||
uninstallApp()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
R.id.menu_download_manual -> {
|
|
||||||
val sheet = ManualDownloadSheet.newInstance(app)
|
|
||||||
sheet.isCancelable = false
|
|
||||||
sheet.show(supportFragmentManager, ManualDownloadSheet.TAG)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
R.id.action_playstore -> {
|
|
||||||
browse("${Constants.SHARE_URL}${app.packageName}")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun attachToolbar() {
|
|
||||||
setSupportActionBar(B.layoutDetailsToolbar.toolbar)
|
|
||||||
val actionBar = supportActionBar
|
|
||||||
if (actionBar != null) {
|
|
||||||
actionBar.setDisplayShowCustomEnabled(true)
|
|
||||||
actionBar.setDisplayHomeAsUpEnabled(true)
|
|
||||||
actionBar.elevation = 0f
|
|
||||||
actionBar.title = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun attachActions() {
|
|
||||||
flip(0)
|
|
||||||
checkAndSetupInstall()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateActionState(state: State) {
|
|
||||||
B.layoutDetailsInstall.btnDownload.updateState(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openApp() {
|
|
||||||
val intent = PackageUtil.getLaunchIntent(this, app.packageName)
|
|
||||||
if (intent != null) {
|
|
||||||
try {
|
|
||||||
startActivity(intent)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
toast("Unable to open app")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun verifyAndInstall(files: List<Download>) {
|
|
||||||
if (downloadOnly)
|
|
||||||
return
|
|
||||||
|
|
||||||
var filesExist = true
|
|
||||||
|
|
||||||
files.forEach { download ->
|
|
||||||
filesExist = filesExist && File(download.file).exists()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filesExist)
|
|
||||||
install(files)
|
|
||||||
else
|
|
||||||
purchase()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
private fun install(files: List<Download>) {
|
|
||||||
updateActionState(State.IDLE)
|
|
||||||
|
|
||||||
val apkFiles = files.filter { it.file.endsWith(".apk") }
|
|
||||||
val preferredInstaller = Preferences.getInteger(this, Preferences.PREFERENCE_INSTALLER_ID)
|
|
||||||
|
|
||||||
if (apkFiles.size > 1 && preferredInstaller == 1) {
|
|
||||||
showDialog(R.string.title_installer, R.string.dialog_desc_native_split)
|
|
||||||
} else {
|
|
||||||
task {
|
|
||||||
AppInstaller.getInstance(this)
|
|
||||||
.getPreferredInstaller()
|
|
||||||
.install(
|
|
||||||
app.packageName,
|
|
||||||
apkFiles.map { it.file }
|
|
||||||
)
|
|
||||||
} fail {
|
|
||||||
Log.e(it.stackTraceToString())
|
|
||||||
}
|
|
||||||
|
|
||||||
runOnUiThread {
|
|
||||||
B.layoutDetailsInstall.btnDownload.setText(getString(R.string.action_installing))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
private fun uninstallApp() {
|
|
||||||
task {
|
|
||||||
AppInstaller.getInstance(this)
|
|
||||||
.getPreferredInstaller()
|
|
||||||
.uninstall(app.packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun attachWhiteListStatus() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fetchCompleteApp() {
|
|
||||||
task {
|
|
||||||
authData = AuthProvider.with(this).getAuthData()
|
|
||||||
return@task AppDetailsHelper(authData)
|
|
||||||
.using(HttpClient.getPreferredClient())
|
|
||||||
.getAppByPackageName(app.packageName)
|
|
||||||
} successUi {
|
|
||||||
if (isExternal) {
|
|
||||||
app = it
|
|
||||||
inflatePartialApp()
|
|
||||||
}
|
|
||||||
inflateExtraDetails(it)
|
|
||||||
} failUi {
|
|
||||||
toast("Failed to fetch app details")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun inflatePartialApp() {
|
|
||||||
if (::app.isInitialized) {
|
|
||||||
attachWhiteListStatus()
|
|
||||||
attachHeader()
|
|
||||||
attachToolbar()
|
|
||||||
attachBottomSheet()
|
|
||||||
attachFetch()
|
|
||||||
attachActions()
|
|
||||||
|
|
||||||
if (autoDownload) {
|
|
||||||
purchase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun attachHeader() {
|
|
||||||
B.layoutDetailsApp.apply {
|
|
||||||
imgIcon.load(app.iconArtwork.url) {
|
|
||||||
placeholder(R.drawable.bg_placeholder)
|
|
||||||
transform(RoundedCorners(32))
|
|
||||||
}
|
|
||||||
|
|
||||||
txtLine1.text = app.displayName
|
|
||||||
txtLine2.text = app.developerName
|
|
||||||
txtLine2.setOnClickListener {
|
|
||||||
NavigationUtil.openDevAppsActivity(
|
|
||||||
this@AppDetailsActivity,
|
|
||||||
app
|
|
||||||
)
|
|
||||||
}
|
|
||||||
txtLine3.text = ("${app.versionName} (${app.versionCode})")
|
|
||||||
packageName.text = app.packageName
|
|
||||||
|
|
||||||
val tags = mutableListOf<String>()
|
|
||||||
if (app.isFree)
|
|
||||||
tags.add(getString(R.string.details_free))
|
|
||||||
else
|
|
||||||
tags.add(getString(R.string.details_paid))
|
|
||||||
|
|
||||||
if (app.containsAds)
|
|
||||||
tags.add(getString(R.string.details_contains_ads))
|
|
||||||
else
|
|
||||||
tags.add(getString(R.string.details_no_ads))
|
|
||||||
|
|
||||||
txtLine4.text = tags.joinToString(separator = " • ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun inflateExtraDetails(app: App?) {
|
|
||||||
app?.let {
|
|
||||||
B.viewFlipper.displayedChild = 1
|
|
||||||
inflateAppDescription(B.layoutDetailDescription, app)
|
|
||||||
inflateAppRatingAndReviews(B.layoutDetailsReview, app)
|
|
||||||
inflateAppDevInfo(B.layoutDetailsDev, app)
|
|
||||||
inflateAppPrivacy(B.layoutDetailsPrivacy, app)
|
|
||||||
inflateAppPermission(B.layoutDetailsPermissions, app)
|
|
||||||
|
|
||||||
if (!authData.isAnonymous) {
|
|
||||||
app.testingProgram?.let {
|
|
||||||
if (it.isAvailable && it.isSubscribed) {
|
|
||||||
B.layoutDetailsApp.txtLine1.text = it.displayName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inflateBetaSubscription(B.layoutDetailsBeta, app)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Preferences.getBoolean(this, Preferences.PREFERENCE_SIMILAR)) {
|
|
||||||
inflateAppStream(B.epoxyRecyclerStream, app)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
private fun startDownload() {
|
|
||||||
when (status) {
|
|
||||||
Status.PAUSED -> {
|
|
||||||
fetch?.resumeGroup(app.getGroupId(this@AppDetailsActivity))
|
|
||||||
}
|
|
||||||
Status.DOWNLOADING -> {
|
|
||||||
flip(1)
|
|
||||||
toast("Already downloading")
|
|
||||||
}
|
|
||||||
Status.COMPLETED -> {
|
|
||||||
fetch?.getFetchGroup(app.getGroupId(this@AppDetailsActivity)) {
|
|
||||||
verifyAndInstall(it.downloads)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
purchase()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val listOfActionsWhenServiceAttaches = ArrayList<Runnable>()
|
|
||||||
|
|
||||||
private fun purchase() {
|
|
||||||
bottomSheetBehavior.isHideable = false
|
|
||||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
|
||||||
updateActionState(State.PROGRESS)
|
|
||||||
|
|
||||||
if (PathUtil.needsStorageManagerPerm(app.fileList) || this.isExternalStorageEnable()) {
|
|
||||||
if (isRAndAbove()) {
|
|
||||||
if (!Environment.isExternalStorageManager()) {
|
|
||||||
startForStorageManagerResult.launch(
|
|
||||||
Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
updateApp(app)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (ContextCompat.checkSelfPermission(
|
|
||||||
this,
|
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
||||||
) == PackageManager.PERMISSION_GRANTED
|
|
||||||
) {
|
|
||||||
updateApp(app)
|
|
||||||
} else {
|
|
||||||
startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
updateApp(app)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateApp(app: App) {
|
|
||||||
if (updateService == null) {
|
|
||||||
listOfActionsWhenServiceAttaches.add {
|
|
||||||
updateService?.updateApp(app, true)
|
|
||||||
}
|
|
||||||
getUpdateServiceInstance()
|
|
||||||
} else {
|
|
||||||
updateService?.updateApp(app, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateProgress(
|
|
||||||
fetchGroup: FetchGroup,
|
|
||||||
etaInMilliSeconds: Long,
|
|
||||||
downloadedBytesPerSecond: Long
|
|
||||||
) {
|
|
||||||
runOnUiThread {
|
|
||||||
val progress = if (fetchGroup.groupDownloadProgress > 0)
|
|
||||||
fetchGroup.groupDownloadProgress
|
|
||||||
else
|
|
||||||
0
|
|
||||||
|
|
||||||
if (progress == 100) {
|
|
||||||
B.layoutDetailsInstall.btnDownload.setText(getString(R.string.action_installing))
|
|
||||||
return@runOnUiThread
|
|
||||||
}
|
|
||||||
B.layoutDetailsInstall.apply {
|
|
||||||
txtProgressPercent.text = ("${progress}%")
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
progressDownload.setProgress(progress, true)
|
|
||||||
} else {
|
|
||||||
progressDownload.progress = progress
|
|
||||||
}
|
|
||||||
|
|
||||||
txtEta.text = CommonUtil.getETAString(
|
|
||||||
this@AppDetailsActivity,
|
|
||||||
etaInMilliSeconds
|
|
||||||
)
|
|
||||||
txtSpeed.text =
|
|
||||||
CommonUtil.getDownloadSpeedString(
|
|
||||||
this@AppDetailsActivity,
|
|
||||||
downloadedBytesPerSecond
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun expandBottomSheet(message: String?) {
|
|
||||||
bottomSheetBehavior.isHideable = false
|
|
||||||
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
|
|
||||||
|
|
||||||
with(B.layoutDetailsInstall) {
|
|
||||||
txtPurchaseError.text = message
|
|
||||||
btnDownload.updateState(State.IDLE)
|
|
||||||
if (app.isFree)
|
|
||||||
btnDownload.setText(R.string.action_install)
|
|
||||||
else
|
|
||||||
btnDownload.setText(app.price)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkAndSetupInstall() {
|
|
||||||
isInstalled = PackageUtil.isInstalled(this, app.packageName)
|
|
||||||
|
|
||||||
B.layoutDetailsInstall.btnDownload.let { btn ->
|
|
||||||
if (isInstalled) {
|
|
||||||
isUpdatable = PackageUtil.isUpdatable(
|
|
||||||
this,
|
|
||||||
app.packageName,
|
|
||||||
app.versionCode.toLong()
|
|
||||||
)
|
|
||||||
|
|
||||||
val installedVersion = PackageUtil.getInstalledVersion(this, app.packageName)
|
|
||||||
|
|
||||||
if (isUpdatable) {
|
|
||||||
B.layoutDetailsApp.txtLine3.text =
|
|
||||||
("$installedVersion ➔ ${app.versionName} (${app.versionCode})")
|
|
||||||
btn.setText(R.string.action_update)
|
|
||||||
btn.addOnClickListener { startDownload() }
|
|
||||||
} else {
|
|
||||||
B.layoutDetailsApp.txtLine3.text = installedVersion
|
|
||||||
btn.setText(R.string.action_open)
|
|
||||||
btn.addOnClickListener { openApp() }
|
|
||||||
}
|
|
||||||
if (!uninstallActionEnabled) {
|
|
||||||
invalidateOptionsMenu()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (app.isFree) {
|
|
||||||
btn.setText(R.string.action_install)
|
|
||||||
} else {
|
|
||||||
btn.setText(app.price)
|
|
||||||
}
|
|
||||||
|
|
||||||
btn.addOnClickListener {
|
|
||||||
if (authData.isAnonymous && !app.isFree) {
|
|
||||||
toast(R.string.toast_purchase_blocked)
|
|
||||||
} else {
|
|
||||||
btn.setText(R.string.download_metadata)
|
|
||||||
startDownload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (uninstallActionEnabled) {
|
|
||||||
invalidateOptionsMenu()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
private fun flip(nextView: Int) {
|
|
||||||
runOnUiThread {
|
|
||||||
val displayChild = B.layoutDetailsInstall.viewFlipper.displayedChild
|
|
||||||
if (displayChild != nextView) {
|
|
||||||
B.layoutDetailsInstall.viewFlipper.displayedChild = nextView
|
|
||||||
if (nextView == 0)
|
|
||||||
checkAndSetupInstall()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun attachFetch() {
|
|
||||||
if (fetch == null) {
|
|
||||||
downloadManager = DownloadManager.with(this)
|
|
||||||
fetch = downloadManager!!.fetch
|
|
||||||
}
|
|
||||||
fetch?.getFetchGroup(app.getGroupId(this@AppDetailsActivity)) { fetchGroup: FetchGroup ->
|
|
||||||
if (fetchGroup.groupDownloadProgress == 100 && fetchGroup.completedDownloads.isNotEmpty()) {
|
|
||||||
status = Status.COMPLETED
|
|
||||||
} else if (downloadManager?.isDownloading(fetchGroup) == true) {
|
|
||||||
status = Status.DOWNLOADING
|
|
||||||
flip(1)
|
|
||||||
} else if (downloadManager?.isCanceled(fetchGroup) == true) {
|
|
||||||
status = Status.CANCELLED
|
|
||||||
} else if (fetchGroup.pausedDownloads.isNotEmpty()) {
|
|
||||||
status = Status.PAUSED
|
|
||||||
} else {
|
|
||||||
status = Status.NONE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchGroupListener = object : AbstractFetchGroupListener() {
|
|
||||||
|
|
||||||
override fun onAdded(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(this@AppDetailsActivity)) {
|
|
||||||
status = download.status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStarted(
|
|
||||||
groupId: Int,
|
|
||||||
download: Download,
|
|
||||||
downloadBlocks: List<DownloadBlock>,
|
|
||||||
totalBlocks: Int,
|
|
||||||
fetchGroup: FetchGroup
|
|
||||||
) {
|
|
||||||
if (groupId == app.getGroupId(this@AppDetailsActivity)) {
|
|
||||||
status = download.status
|
|
||||||
flip(1)
|
|
||||||
|
|
||||||
val pkgDir = PathUtil.getPackageDirectory(applicationContext, app.packageName)
|
|
||||||
completionMarker =
|
|
||||||
java.io.File("$pkgDir/.${app.versionCode}.download-complete")
|
|
||||||
inProgressMarker =
|
|
||||||
java.io.File("$pkgDir/.${app.versionCode}.download-in-progress")
|
|
||||||
|
|
||||||
if (completionMarker.exists())
|
|
||||||
completionMarker.delete()
|
|
||||||
|
|
||||||
inProgressMarker.createNewFile()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResumed(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(this@AppDetailsActivity)) {
|
|
||||||
status = download.status
|
|
||||||
flip(1)
|
|
||||||
inProgressMarker.parentFile?.mkdirs()
|
|
||||||
inProgressMarker.createNewFile()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPaused(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(this@AppDetailsActivity)) {
|
|
||||||
status = download.status
|
|
||||||
flip(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onProgress(
|
|
||||||
groupId: Int,
|
|
||||||
download: Download,
|
|
||||||
etaInMilliSeconds: Long,
|
|
||||||
downloadedBytesPerSecond: Long,
|
|
||||||
fetchGroup: FetchGroup
|
|
||||||
) {
|
|
||||||
if (groupId == app.getGroupId(this@AppDetailsActivity)) {
|
|
||||||
updateProgress(fetchGroup, etaInMilliSeconds, downloadedBytesPerSecond)
|
|
||||||
Log.i(
|
|
||||||
"${app.displayName} : ${download.file} -> Progress : %d",
|
|
||||||
fetchGroup.groupDownloadProgress
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(this@AppDetailsActivity) && fetchGroup.groupDownloadProgress == 100) {
|
|
||||||
status = download.status
|
|
||||||
flip(0)
|
|
||||||
updateProgress(fetchGroup, -1, -1)
|
|
||||||
try {
|
|
||||||
inProgressMarker.delete()
|
|
||||||
completionMarker.createNewFile()
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCancelled(groupId: Int, download: Download, fetchGroup: FetchGroup) {
|
|
||||||
if (groupId == app.getGroupId(this@AppDetailsActivity)) {
|
|
||||||
status = download.status
|
|
||||||
flip(0)
|
|
||||||
inProgressMarker.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(
|
|
||||||
groupId: Int,
|
|
||||||
download: Download,
|
|
||||||
error: Error,
|
|
||||||
throwable: Throwable?,
|
|
||||||
fetchGroup: FetchGroup
|
|
||||||
) {
|
|
||||||
if (groupId == app.getGroupId(this@AppDetailsActivity)) {
|
|
||||||
status = download.status
|
|
||||||
flip(0)
|
|
||||||
inProgressMarker.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appMetadataListener = object : AppMetadataStatusListener {
|
|
||||||
override fun onAppMetadataStatusError(reason: String, app: App) {
|
|
||||||
if (app.packageName == this@AppDetailsActivity.app.packageName) {
|
|
||||||
updateActionState(State.IDLE)
|
|
||||||
expandBottomSheet(reason)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getUpdateServiceInstance()
|
|
||||||
|
|
||||||
B.layoutDetailsInstall.imgCancel.setOnClickListener {
|
|
||||||
fetch?.cancelGroup(
|
|
||||||
app.getGroupId(this@AppDetailsActivity)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (updateService != null) {
|
|
||||||
pendingAddListeners = false
|
|
||||||
updateService!!.registerFetchListener(fetchGroupListener)
|
|
||||||
// appMetadataListener needs to be initialized after the fetchGroupListener
|
|
||||||
updateService!!.registerAppMetadataListener(appMetadataListener)
|
|
||||||
} else {
|
|
||||||
pendingAddListeners = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getUpdateServiceInstance() {
|
|
||||||
if (updateService == null && !attachToServiceCalled) {
|
|
||||||
attachToServiceCalled = true
|
|
||||||
val intent = Intent(this, UpdateService::class.java)
|
|
||||||
startService(intent)
|
|
||||||
bindService(
|
|
||||||
intent,
|
|
||||||
serviceConnection,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
|
||||||
if (updateService != null) {
|
|
||||||
updateService = null
|
|
||||||
attachToServiceCalled = false
|
|
||||||
pendingAddListeners = true
|
|
||||||
unbindService(serviceConnection)
|
|
||||||
}
|
|
||||||
super.onPause()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
if (updateService != null) {
|
|
||||||
updateService = null
|
|
||||||
attachToServiceCalled = false
|
|
||||||
pendingAddListeners = true
|
|
||||||
unbindService(serviceConnection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun attachBottomSheet() {
|
|
||||||
B.layoutDetailsInstall.apply {
|
|
||||||
viewFlipper.setInAnimation(this@AppDetailsActivity, R.anim.fade_in)
|
|
||||||
viewFlipper.setOutAnimation(this@AppDetailsActivity, R.anim.fade_out)
|
|
||||||
}
|
|
||||||
|
|
||||||
bottomSheetBehavior = BottomSheetBehavior.from(B.layoutDetailsInstall.bottomSheet)
|
|
||||||
bottomSheetBehavior.isDraggable = false
|
|
||||||
|
|
||||||
bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetCallback() {
|
|
||||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
|
||||||
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
|
|
||||||
bottomSheetBehavior.setDraggable(true)
|
|
||||||
} else if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
|
|
||||||
bottomSheetBehavior.isDraggable = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,443 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.details
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.RelativeLayout
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.text.HtmlCompat
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import com.airbnb.epoxy.EpoxyRecyclerView
|
|
||||||
import com.aurora.extensions.hide
|
|
||||||
import com.aurora.extensions.load
|
|
||||||
import com.aurora.extensions.show
|
|
||||||
import com.aurora.extensions.toast
|
|
||||||
import com.aurora.gplayapi.data.models.*
|
|
||||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
|
||||||
import com.aurora.gplayapi.helpers.ReviewsHelper
|
|
||||||
import com.aurora.store.R
|
|
||||||
import com.aurora.store.data.ViewState
|
|
||||||
import com.aurora.store.data.model.ExodusReport
|
|
||||||
import com.aurora.store.data.model.Report
|
|
||||||
import com.aurora.store.data.network.HttpClient
|
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
|
||||||
import com.aurora.store.databinding.*
|
|
||||||
import com.aurora.store.util.CommonUtil
|
|
||||||
import com.aurora.store.util.NavigationUtil
|
|
||||||
import com.aurora.store.view.custom.RatingView
|
|
||||||
import com.aurora.store.view.epoxy.controller.DetailsCarouselController
|
|
||||||
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
|
||||||
import com.aurora.store.view.epoxy.views.details.ReviewViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.details.ScreenshotView
|
|
||||||
import com.aurora.store.view.epoxy.views.details.ScreenshotViewModel_
|
|
||||||
import com.aurora.store.view.ui.commons.BaseActivity
|
|
||||||
import com.aurora.store.view.ui.sheets.PermissionBottomSheet
|
|
||||||
import com.aurora.store.viewmodel.details.DetailsClusterViewModel
|
|
||||||
import nl.komponents.kovenant.task
|
|
||||||
import nl.komponents.kovenant.ui.failUi
|
|
||||||
import nl.komponents.kovenant.ui.successUi
|
|
||||||
import org.json.JSONObject
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
|
|
||||||
abstract class BaseDetailsActivity : BaseActivity() {
|
|
||||||
|
|
||||||
private val exodusBaseUrl = "https://reports.exodus-privacy.eu.org/api/search/"
|
|
||||||
private val exodusApiKey = "Token bbe6ebae4ad45a9cbacb17d69739799b8df2c7ae"
|
|
||||||
|
|
||||||
//Sub Section Inflation
|
|
||||||
fun inflateAppDescription(B: LayoutDetailsDescriptionBinding, app: App) {
|
|
||||||
val installs = CommonUtil.addDiPrefix(app.installs)
|
|
||||||
|
|
||||||
if (installs != "NA") {
|
|
||||||
B.txtInstalls.text = CommonUtil.addDiPrefix(app.installs)
|
|
||||||
} else {
|
|
||||||
B.txtInstalls.hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
B.txtSize.text = CommonUtil.addSiPrefix(app.size)
|
|
||||||
B.txtRating.text = app.labeledRating
|
|
||||||
B.txtSdk.text = ("Target SDK ${app.targetSdk}")
|
|
||||||
B.txtUpdated.text = app.updatedOn
|
|
||||||
B.txtDescription.text = HtmlCompat.fromHtml(
|
|
||||||
app.shortDescription,
|
|
||||||
HtmlCompat.FROM_HTML_OPTION_USE_CSS_COLORS
|
|
||||||
)
|
|
||||||
|
|
||||||
app.changes.apply {
|
|
||||||
if (isEmpty()) {
|
|
||||||
B.txtChangelog.text = getString(R.string.details_changelog_unavailable)
|
|
||||||
} else {
|
|
||||||
B.txtChangelog.text = HtmlCompat.fromHtml(
|
|
||||||
this,
|
|
||||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
B.headerDescription.addClickListener {
|
|
||||||
openDetailsMoreActivity(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
B.epoxyRecycler.withModels {
|
|
||||||
setFilterDuplicates(true)
|
|
||||||
var position = 0
|
|
||||||
app.screenshots
|
|
||||||
//.sortedWith { o1, o2 -> o2.height.compareTo(o1.height) }
|
|
||||||
.forEach { artwork ->
|
|
||||||
add(
|
|
||||||
ScreenshotViewModel_()
|
|
||||||
.id(artwork.url)
|
|
||||||
.artwork(artwork)
|
|
||||||
.position(position++)
|
|
||||||
.callback(object : ScreenshotView.ScreenshotCallback {
|
|
||||||
override fun onClick(position: Int) {
|
|
||||||
openScreenshotActivity(app, position)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inflateAppRatingAndReviews(B: LayoutDetailsReviewBinding, app: App) {
|
|
||||||
B.averageRating.text = app.rating.average.toString()
|
|
||||||
B.txtReviewCount.text = app.rating.abbreviatedLabel
|
|
||||||
|
|
||||||
var totalStars = 0L
|
|
||||||
totalStars += app.rating.oneStar
|
|
||||||
totalStars += app.rating.twoStar
|
|
||||||
totalStars += app.rating.threeStar
|
|
||||||
totalStars += app.rating.fourStar
|
|
||||||
totalStars += app.rating.fiveStar
|
|
||||||
|
|
||||||
B.avgRatingLayout.apply {
|
|
||||||
removeAllViews()
|
|
||||||
addView(addAvgReviews(5, totalStars, app.rating.fiveStar))
|
|
||||||
addView(addAvgReviews(4, totalStars, app.rating.fourStar))
|
|
||||||
addView(addAvgReviews(3, totalStars, app.rating.threeStar))
|
|
||||||
addView(addAvgReviews(2, totalStars, app.rating.twoStar))
|
|
||||||
addView(addAvgReviews(1, totalStars, app.rating.oneStar))
|
|
||||||
}
|
|
||||||
|
|
||||||
B.averageRating.text = String.format(Locale.getDefault(), "%.1f", app.rating.average)
|
|
||||||
B.txtReviewCount.text = app.rating.abbreviatedLabel
|
|
||||||
|
|
||||||
val authData = AuthProvider.with(this).getAuthData()
|
|
||||||
|
|
||||||
B.layoutUserReview.visibility = if (authData.isAnonymous) View.GONE else View.VISIBLE
|
|
||||||
|
|
||||||
B.btnPostReview.setOnClickListener {
|
|
||||||
if (authData.isAnonymous) {
|
|
||||||
toast(R.string.toast_anonymous_restriction)
|
|
||||||
} else {
|
|
||||||
addOrUpdateReview(B, app, Review().apply {
|
|
||||||
title = authData.userProfile!!.name
|
|
||||||
rating = B.userStars.rating.toInt()
|
|
||||||
comment = B.inputReview.text.toString()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
B.headerRatingReviews.addClickListener {
|
|
||||||
openDetailsReviewActivity(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
task {
|
|
||||||
fetchReviewSummary(app)
|
|
||||||
} successUi {
|
|
||||||
B.epoxyRecycler.withModels {
|
|
||||||
it.take(4)
|
|
||||||
.forEach {
|
|
||||||
add(
|
|
||||||
ReviewViewModel_()
|
|
||||||
.id(it.timeStamp)
|
|
||||||
.review(it)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} failUi {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inflateAppPrivacy(B: LayoutDetailsPrivacyBinding, app: App) {
|
|
||||||
|
|
||||||
task {
|
|
||||||
fetchReport(app.packageName)
|
|
||||||
} successUi { report ->
|
|
||||||
if (report.trackers.isNotEmpty()) {
|
|
||||||
B.txtStatus.apply {
|
|
||||||
setTextColor(
|
|
||||||
ContextCompat.getColor(
|
|
||||||
this@BaseDetailsActivity,
|
|
||||||
if (report.trackers.size > 4)
|
|
||||||
R.color.colorRed
|
|
||||||
else
|
|
||||||
R.color.colorOrange
|
|
||||||
)
|
|
||||||
)
|
|
||||||
text =
|
|
||||||
("${report.trackers.size} ${getString(R.string.exodus_substring)} ${report.version}")
|
|
||||||
}
|
|
||||||
|
|
||||||
B.headerPrivacy.addClickListener {
|
|
||||||
NavigationUtil.openExodusActivity(this, app, report)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
B.txtStatus.apply {
|
|
||||||
setTextColor(
|
|
||||||
ContextCompat.getColor(
|
|
||||||
this@BaseDetailsActivity,
|
|
||||||
R.color.colorGreen
|
|
||||||
)
|
|
||||||
)
|
|
||||||
text = getString(R.string.exodus_no_tracker)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} failUi {
|
|
||||||
B.txtStatus.text = it.message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inflateAppDevInfo(B: LayoutDetailsDevBinding, app: App) {
|
|
||||||
if (app.developerAddress.isNotEmpty()) {
|
|
||||||
B.devAddress.apply {
|
|
||||||
setTxtSubtitle(
|
|
||||||
HtmlCompat.fromHtml(
|
|
||||||
app.developerAddress,
|
|
||||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
|
||||||
).toString()
|
|
||||||
)
|
|
||||||
visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (app.developerWebsite.isNotEmpty()) {
|
|
||||||
B.devWeb.apply {
|
|
||||||
setTxtSubtitle(app.developerWebsite)
|
|
||||||
visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (app.developerEmail.isNotEmpty()) {
|
|
||||||
B.devMail.apply {
|
|
||||||
setTxtSubtitle(app.developerEmail)
|
|
||||||
visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inflateBetaSubscription(B: LayoutDetailsBetaBinding, app: App) {
|
|
||||||
app.testingProgram?.let { betaProgram ->
|
|
||||||
if (betaProgram.isAvailable) {
|
|
||||||
B.root.show()
|
|
||||||
|
|
||||||
updateBetaActions(B, betaProgram.isSubscribed)
|
|
||||||
|
|
||||||
if (betaProgram.isSubscribedAndInstalled) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
B.imgBeta.load(betaProgram.artwork.url) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
B.btnBetaAction.setOnClickListener {
|
|
||||||
val authData = AuthProvider.with(this).getAuthData()
|
|
||||||
task {
|
|
||||||
B.btnBetaAction.text = getString(R.string.action_pending)
|
|
||||||
B.btnBetaAction.isEnabled = false
|
|
||||||
AppDetailsHelper(authData).testingProgram(
|
|
||||||
app.packageName,
|
|
||||||
!betaProgram.isSubscribed
|
|
||||||
)
|
|
||||||
} successUi {
|
|
||||||
B.btnBetaAction.isEnabled = true
|
|
||||||
if (it.subscribed) {
|
|
||||||
updateBetaActions(B, true)
|
|
||||||
}
|
|
||||||
if (it.unsubscribed) {
|
|
||||||
updateBetaActions(B, false)
|
|
||||||
}
|
|
||||||
} failUi {
|
|
||||||
updateBetaActions(B, betaProgram.isSubscribed)
|
|
||||||
toast(getString(R.string.details_beta_delay))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
B.root.hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inflateAppStream(epoxyRecyclerView: EpoxyRecyclerView, app: App) {
|
|
||||||
app.detailsStreamUrl?.let {
|
|
||||||
val VM = ViewModelProvider(this)[DetailsClusterViewModel::class.java]
|
|
||||||
|
|
||||||
val carouselController =
|
|
||||||
DetailsCarouselController(object : GenericCarouselController.Callbacks {
|
|
||||||
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
|
||||||
if (streamCluster.clusterBrowseUrl.isNotEmpty())
|
|
||||||
openStreamBrowseActivity(
|
|
||||||
streamCluster.clusterBrowseUrl,
|
|
||||||
streamCluster.clusterTitle
|
|
||||||
)
|
|
||||||
else
|
|
||||||
toast(getString(R.string.toast_page_unavailable))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
|
||||||
VM.observeCluster(streamCluster)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAppClick(app: App) {
|
|
||||||
openDetailsActivity(app)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAppLongClick(app: App) {
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
VM.liveData.observe(this) {
|
|
||||||
when (it) {
|
|
||||||
is ViewState.Empty -> {
|
|
||||||
}
|
|
||||||
is ViewState.Loading -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
is ViewState.Error -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
is ViewState.Status -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
is ViewState.Success<*> -> {
|
|
||||||
carouselController.setData(it.data as StreamBundle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
epoxyRecyclerView.setController(carouselController)
|
|
||||||
|
|
||||||
VM.getStreamBundle(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inflateAppPermission(B: LayoutDetailsPermissionsBinding, app: App) {
|
|
||||||
B.headerPermission.addClickListener {
|
|
||||||
if (app.permissions.size > 0) {
|
|
||||||
PermissionBottomSheet.newInstance(app)
|
|
||||||
.show(supportFragmentManager, PermissionBottomSheet.TAG)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
B.txtPermissionCount.text = ("${app.permissions.size} permissions")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateBetaActions(B: LayoutDetailsBetaBinding, isSubscribed: Boolean) {
|
|
||||||
if (isSubscribed) {
|
|
||||||
B.btnBetaAction.text = getString(R.string.action_leave)
|
|
||||||
B.txtBetaTitle.text = getString(R.string.details_beta_subscribed)
|
|
||||||
} else {
|
|
||||||
B.btnBetaAction.text = getString(R.string.action_join)
|
|
||||||
B.txtBetaTitle.text = getString(R.string.details_beta_available)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* App Review Helpers */
|
|
||||||
|
|
||||||
private fun addAvgReviews(number: Int, max: Long, rating: Long): RelativeLayout {
|
|
||||||
return RatingView(this, number, max.toInt(), rating.toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addOrUpdateReview(
|
|
||||||
B: LayoutDetailsReviewBinding,
|
|
||||||
app: App,
|
|
||||||
review: Review,
|
|
||||||
isBeta: Boolean = false
|
|
||||||
) {
|
|
||||||
task {
|
|
||||||
val authData = AuthProvider.with(this).getAuthData()
|
|
||||||
ReviewsHelper(authData)
|
|
||||||
.using(HttpClient.getPreferredClient())
|
|
||||||
.addOrEditReview(
|
|
||||||
app.packageName,
|
|
||||||
review.title,
|
|
||||||
review.comment,
|
|
||||||
review.rating,
|
|
||||||
isBeta
|
|
||||||
)
|
|
||||||
}.successUi {
|
|
||||||
it?.let {
|
|
||||||
B.userStars.rating = it.rating.toFloat()
|
|
||||||
Toast.makeText(this, getString(R.string.toast_rated_success), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}.failUi {
|
|
||||||
Toast.makeText(this, getString(R.string.toast_rated_failed), Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fetchReviewSummary(app: App): List<Review> {
|
|
||||||
val authData = AuthProvider
|
|
||||||
.with(this)
|
|
||||||
.getAuthData()
|
|
||||||
val reviewsHelper = ReviewsHelper(authData)
|
|
||||||
.using(HttpClient.getPreferredClient())
|
|
||||||
return reviewsHelper.getReviewSummary(app.packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* App Privacy Helpers */
|
|
||||||
|
|
||||||
private fun parseResponse(response: String, packageName: String): List<Report> {
|
|
||||||
try {
|
|
||||||
val jsonObject = JSONObject(response)
|
|
||||||
val exodusObject = jsonObject.getJSONObject(packageName)
|
|
||||||
val exodusReport: ExodusReport = gson.fromJson(
|
|
||||||
exodusObject.toString(),
|
|
||||||
ExodusReport::class.java
|
|
||||||
)
|
|
||||||
return exodusReport.reports
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw Exception("No reports found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fetchReport(packageName: String): Report {
|
|
||||||
val headers: MutableMap<String, String> = mutableMapOf()
|
|
||||||
headers["Content-Type"] = "application/json"
|
|
||||||
headers["Accept"] = "application/json"
|
|
||||||
headers["Authorization"] = exodusApiKey
|
|
||||||
|
|
||||||
val url = exodusBaseUrl + packageName
|
|
||||||
|
|
||||||
val playResponse = HttpClient
|
|
||||||
.getPreferredClient()
|
|
||||||
.get(url, headers)
|
|
||||||
|
|
||||||
if (playResponse.isSuccessful) {
|
|
||||||
return parseResponse(String(playResponse.responseBytes), packageName)[0]
|
|
||||||
} else {
|
|
||||||
throw Exception("Failed to fetch report")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package com.aurora.store.view.ui.details
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import com.aurora.store.R
|
|
||||||
|
|
||||||
class EmptyAppDetailsActivity: AppCompatActivity(R.layout.activity_details) {
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
onNewIntent(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
|
||||||
super.onNewIntent(intent)
|
|
||||||
val validSchemes = listOf("market", "http", "https")
|
|
||||||
|
|
||||||
if (intent != null && validSchemes.any { it == intent.scheme }) {
|
|
||||||
if (intent.data!!.getQueryParameter("id").isNullOrEmpty()) {
|
|
||||||
finishAfterTransition()
|
|
||||||
} else {
|
|
||||||
// Construct a new intent manually to avoid accepting extras from external apps
|
|
||||||
Intent(this, AppDetailsActivity::class.java).also { extIntent ->
|
|
||||||
extIntent.data = intent.data
|
|
||||||
startActivity(extIntent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,7 +23,8 @@
|
|||||||
android:id="@+id/container"
|
android:id="@+id/container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical"
|
||||||
|
tools:context=".view.ui.details.AppDetailsFragment">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -98,4 +98,18 @@
|
|||||||
android:name="com.aurora.store.view.ui.downloads.DownloadFragment"
|
android:name="com.aurora.store.view.ui.downloads.DownloadFragment"
|
||||||
android:label="@string/title_download_manager"
|
android:label="@string/title_download_manager"
|
||||||
tools:layout="@layout/fragment_download" />
|
tools:layout="@layout/fragment_download" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/appDetailsFragment"
|
||||||
|
android:name="com.aurora.store.view.ui.details.AppDetailsFragment"
|
||||||
|
tools:layout="@layout/fragment_details" >
|
||||||
|
<argument
|
||||||
|
android:name="packageName"
|
||||||
|
app:argType="string" />
|
||||||
|
<deepLink
|
||||||
|
app:action="android.intent.action.VIEW"
|
||||||
|
app:uri="play.google.com/store/apps/details?id={packageName}" />
|
||||||
|
</fragment>
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_global_appDetailsFragment"
|
||||||
|
app:destination="@id/appDetailsFragment" />
|
||||||
</navigation>
|
</navigation>
|
||||||
Reference in New Issue
Block a user