v0.6.5
This commit is contained in:
@@ -1,276 +0,0 @@
|
||||
package com.looker.droidify
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.hilt.work.HiltWorkerFactory
|
||||
import androidx.work.Configuration
|
||||
import androidx.work.NetworkType
|
||||
import coil.ImageLoader
|
||||
import coil.ImageLoaderFactory
|
||||
import coil.disk.DiskCache
|
||||
import coil.memory.MemoryCache
|
||||
import com.looker.core.common.Constants
|
||||
import com.looker.core.common.cache.Cache
|
||||
import com.looker.core.common.extension.getInstalledPackagesCompat
|
||||
import com.looker.core.common.extension.jobScheduler
|
||||
import com.looker.core.common.log
|
||||
import com.looker.core.datastore.SettingsRepository
|
||||
import com.looker.core.datastore.get
|
||||
import com.looker.core.datastore.model.AutoSync
|
||||
import com.looker.core.datastore.model.InstallerType
|
||||
import com.looker.core.datastore.model.ProxyPreference
|
||||
import com.looker.core.datastore.model.ProxyType
|
||||
import com.looker.droidify.content.ProductPreferences
|
||||
import com.looker.droidify.database.Database
|
||||
import com.looker.droidify.index.RepositoryUpdater
|
||||
import com.looker.droidify.receivers.InstalledAppReceiver
|
||||
import com.looker.droidify.service.Connection
|
||||
import com.looker.droidify.service.SyncService
|
||||
import com.looker.droidify.sync.SyncPreference
|
||||
import com.looker.droidify.sync.toJobNetworkType
|
||||
import com.looker.droidify.utility.extension.toInstalledItem
|
||||
import com.looker.droidify.work.CleanUpWorker
|
||||
import com.looker.installer.InstallManager
|
||||
import com.looker.installer.installers.root.RootPermissionHandler
|
||||
import com.looker.installer.installers.shizuku.ShizukuPermissionHandler
|
||||
import com.looker.network.Downloader
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.collectIndexed
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.Proxy
|
||||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.INFINITE
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import com.looker.core.common.R as CommonR
|
||||
|
||||
@HiltAndroidApp
|
||||
class MainApplication : Application(), ImageLoaderFactory, Configuration.Provider {
|
||||
|
||||
private val parentJob = SupervisorJob()
|
||||
private val appScope = CoroutineScope(Dispatchers.Default + parentJob)
|
||||
|
||||
@Inject
|
||||
lateinit var settingsRepository: SettingsRepository
|
||||
|
||||
@Inject
|
||||
lateinit var installer: InstallManager
|
||||
|
||||
@Inject
|
||||
lateinit var downloader: Downloader
|
||||
|
||||
@Inject
|
||||
lateinit var shizukuPermissionHandler: ShizukuPermissionHandler
|
||||
|
||||
@Inject
|
||||
lateinit var rootPermissionHandler: RootPermissionHandler
|
||||
|
||||
@Inject
|
||||
lateinit var workerFactory: HiltWorkerFactory
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
val databaseUpdated = Database.init(this)
|
||||
ProductPreferences.init(this, appScope)
|
||||
RepositoryUpdater.init(appScope, downloader)
|
||||
listenApplications()
|
||||
checkLanguage()
|
||||
updatePreference()
|
||||
setupInstaller()
|
||||
|
||||
if (databaseUpdated) forceSyncAll()
|
||||
}
|
||||
|
||||
override fun onTerminate() {
|
||||
super.onTerminate()
|
||||
appScope.cancel("Application Terminated")
|
||||
installer.close()
|
||||
}
|
||||
|
||||
private fun setupInstaller() {
|
||||
appScope.launch {
|
||||
launch {
|
||||
settingsRepository.get { installerType }.collect {
|
||||
if (it == InstallerType.SHIZUKU) handleShizukuInstaller()
|
||||
if (it == InstallerType.ROOT) {
|
||||
if (!rootPermissionHandler.isGranted) {
|
||||
settingsRepository.setInstallerType(InstallerType.Default)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
installer()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.handleShizukuInstaller() = launch {
|
||||
shizukuPermissionHandler.state.collect { (isGranted, isAlive, _) ->
|
||||
if (isAlive && isGranted) {
|
||||
settingsRepository.setInstallerType(InstallerType.SHIZUKU)
|
||||
return@collect
|
||||
}
|
||||
if (isAlive) {
|
||||
settingsRepository.setInstallerType(InstallerType.Default)
|
||||
shizukuPermissionHandler.requestPermission()
|
||||
return@collect
|
||||
}
|
||||
settingsRepository.setInstallerType(InstallerType.Default)
|
||||
}
|
||||
}
|
||||
|
||||
private fun listenApplications() {
|
||||
registerReceiver(
|
||||
InstalledAppReceiver(packageManager),
|
||||
IntentFilter().apply {
|
||||
addAction(Intent.ACTION_PACKAGE_ADDED)
|
||||
addAction(Intent.ACTION_PACKAGE_REMOVED)
|
||||
addDataScheme("package")
|
||||
}
|
||||
)
|
||||
val installedItems =
|
||||
packageManager.getInstalledPackagesCompat()
|
||||
?.map { it.toInstalledItem() }
|
||||
?: return
|
||||
Database.InstalledAdapter.putAll(installedItems)
|
||||
}
|
||||
|
||||
private fun checkLanguage() {
|
||||
appScope.launch {
|
||||
val lastSetLanguage = settingsRepository.getInitial().language
|
||||
val systemSetLanguage = AppCompatDelegate.getApplicationLocales().toLanguageTags()
|
||||
if (systemSetLanguage != lastSetLanguage && lastSetLanguage != "system") {
|
||||
settingsRepository.setLanguage(systemSetLanguage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePreference() {
|
||||
appScope.launch {
|
||||
launch {
|
||||
settingsRepository.get { unstableUpdate }.drop(1).collect {
|
||||
forceSyncAll()
|
||||
}
|
||||
}
|
||||
launch {
|
||||
settingsRepository.get { autoSync }.collectIndexed { index, syncMode ->
|
||||
// Don't update sync job on initial collect
|
||||
updateSyncJob(index > 0, syncMode)
|
||||
}
|
||||
}
|
||||
launch {
|
||||
settingsRepository.get { cleanUpInterval }.drop(1).collect {
|
||||
if (it == INFINITE) {
|
||||
CleanUpWorker.removeAllSchedules(applicationContext)
|
||||
} else {
|
||||
CleanUpWorker.scheduleCleanup(applicationContext, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
settingsRepository.get { proxy }.collect(::updateProxy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateProxy(proxyPreference: ProxyPreference) {
|
||||
val type = proxyPreference.type
|
||||
val host = proxyPreference.host
|
||||
val port = proxyPreference.port
|
||||
val socketAddress = when (type) {
|
||||
ProxyType.DIRECT -> null
|
||||
ProxyType.HTTP, ProxyType.SOCKS -> {
|
||||
try {
|
||||
InetSocketAddress.createUnresolved(host, port)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
log(e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
val androidProxyType = when (type) {
|
||||
ProxyType.DIRECT -> Proxy.Type.DIRECT
|
||||
ProxyType.HTTP -> Proxy.Type.HTTP
|
||||
ProxyType.SOCKS -> Proxy.Type.SOCKS
|
||||
}
|
||||
val determinedProxy = socketAddress?.let { Proxy(androidProxyType, it) } ?: Proxy.NO_PROXY
|
||||
downloader.setProxy(determinedProxy)
|
||||
}
|
||||
|
||||
private fun updateSyncJob(force: Boolean, autoSync: AutoSync) {
|
||||
if (autoSync == AutoSync.NEVER) {
|
||||
jobScheduler?.cancel(Constants.JOB_ID_SYNC)
|
||||
return
|
||||
}
|
||||
val jobScheduler = jobScheduler
|
||||
val syncConditions = when (autoSync) {
|
||||
AutoSync.ALWAYS -> SyncPreference(NetworkType.CONNECTED)
|
||||
AutoSync.WIFI_ONLY -> SyncPreference(NetworkType.UNMETERED)
|
||||
AutoSync.WIFI_PLUGGED_IN -> SyncPreference(NetworkType.UNMETERED, pluggedIn = true)
|
||||
else -> null
|
||||
}
|
||||
val isCompleted = jobScheduler?.allPendingJobs
|
||||
?.any { it.id == Constants.JOB_ID_SYNC } == false
|
||||
if ((force || isCompleted) && syncConditions != null) {
|
||||
val period = 12.hours.inWholeMilliseconds
|
||||
val job = SyncService.Job.create(
|
||||
context = this,
|
||||
periodMillis = period,
|
||||
networkType = syncConditions.toJobNetworkType(),
|
||||
isCharging = syncConditions.pluggedIn,
|
||||
isBatteryLow = syncConditions.batteryNotLow
|
||||
)
|
||||
jobScheduler?.schedule(job)
|
||||
}
|
||||
}
|
||||
|
||||
private fun forceSyncAll() {
|
||||
Database.RepositoryAdapter.getAll().forEach {
|
||||
if (it.lastModified.isNotEmpty() || it.entityTag.isNotEmpty()) {
|
||||
Database.RepositoryAdapter.put(it.copy(lastModified = "", entityTag = ""))
|
||||
}
|
||||
}
|
||||
Connection(SyncService::class.java, onBind = { connection, binder ->
|
||||
binder.sync(SyncService.SyncRequest.FORCE)
|
||||
connection.unbind(this)
|
||||
}).bind(this)
|
||||
}
|
||||
|
||||
class BootReceiver : BroadcastReceiver() {
|
||||
@SuppressLint("UnsafeProtectedBroadcastReceiver")
|
||||
override fun onReceive(context: Context, intent: Intent) = Unit
|
||||
}
|
||||
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
val memoryCache = MemoryCache.Builder(this)
|
||||
.maxSizePercent(0.25)
|
||||
.build()
|
||||
|
||||
val diskCache = DiskCache.Builder()
|
||||
.directory(Cache.getImagesDir(this))
|
||||
.maxSizePercent(0.05)
|
||||
.build()
|
||||
|
||||
return ImageLoader.Builder(this)
|
||||
.memoryCache(memoryCache)
|
||||
.diskCache(diskCache)
|
||||
.error(CommonR.drawable.ic_cannot_load)
|
||||
.crossfade(350)
|
||||
.build()
|
||||
}
|
||||
|
||||
override val workManagerConfiguration: Configuration
|
||||
get() = Configuration.Builder()
|
||||
.setWorkerFactory(workerFactory)
|
||||
.build()
|
||||
}
|
||||
@@ -1,308 +0,0 @@
|
||||
package com.looker.droidify
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.looker.core.common.DeeplinkType
|
||||
import com.looker.core.common.SdkCheck
|
||||
import com.looker.core.common.deeplinkType
|
||||
import com.looker.core.common.extension.homeAsUp
|
||||
import com.looker.core.common.extension.inputManager
|
||||
import com.looker.core.common.requestNotificationPermission
|
||||
import com.looker.core.datastore.SettingsRepository
|
||||
import com.looker.core.datastore.extension.getThemeRes
|
||||
import com.looker.core.datastore.get
|
||||
import com.looker.droidify.database.CursorOwner
|
||||
import com.looker.droidify.ui.appDetail.AppDetailFragment
|
||||
import com.looker.droidify.ui.favourites.FavouritesFragment
|
||||
import com.looker.droidify.ui.repository.EditRepositoryFragment
|
||||
import com.looker.droidify.ui.repository.RepositoriesFragment
|
||||
import com.looker.droidify.ui.repository.RepositoryFragment
|
||||
import com.looker.droidify.ui.settings.SettingsFragment
|
||||
import com.looker.droidify.ui.tabsFragment.TabsFragment
|
||||
import com.looker.installer.InstallManager
|
||||
import com.looker.installer.model.installFrom
|
||||
import dagger.hilt.EntryPoint
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
abstract class ScreenActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private const val STATE_FRAGMENT_STACK = "fragmentStack"
|
||||
}
|
||||
|
||||
sealed interface SpecialIntent {
|
||||
data object Updates : SpecialIntent
|
||||
class Install(val packageName: String?, val cacheFileName: String?) : SpecialIntent
|
||||
}
|
||||
|
||||
private val notificationPermission =
|
||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { }
|
||||
|
||||
@Inject
|
||||
lateinit var installer: InstallManager
|
||||
|
||||
@Parcelize
|
||||
private class FragmentStackItem(
|
||||
val className: String, val arguments: Bundle?, val savedState: Fragment.SavedState?
|
||||
) : Parcelable
|
||||
|
||||
lateinit var cursorOwner: CursorOwner
|
||||
private set
|
||||
|
||||
private var onBackPressedCallback: OnBackPressedCallback? = null
|
||||
|
||||
private val fragmentStack = mutableListOf<FragmentStackItem>()
|
||||
|
||||
private val currentFragment: Fragment?
|
||||
get() {
|
||||
supportFragmentManager.executePendingTransactions()
|
||||
return supportFragmentManager.findFragmentById(R.id.main_content)
|
||||
}
|
||||
|
||||
@EntryPoint
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface CustomUserRepositoryInjector {
|
||||
fun settingsRepository(): SettingsRepository
|
||||
}
|
||||
|
||||
private fun collectChange() {
|
||||
val hiltEntryPoint = EntryPointAccessors.fromApplication(
|
||||
this, CustomUserRepositoryInjector::class.java
|
||||
)
|
||||
val newSettings = hiltEntryPoint.settingsRepository().get { theme to dynamicTheme }
|
||||
runBlocking {
|
||||
val theme = newSettings.first()
|
||||
setTheme(
|
||||
resources.configuration.getThemeRes(
|
||||
theme = theme.first, dynamicTheme = theme.second
|
||||
)
|
||||
)
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
newSettings.drop(1).collect { themeAndDynamic ->
|
||||
setTheme(
|
||||
resources.configuration.getThemeRes(
|
||||
theme = themeAndDynamic.first, dynamicTheme = themeAndDynamic.second
|
||||
)
|
||||
)
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
collectChange()
|
||||
super.onCreate(savedInstanceState)
|
||||
val rootView = FrameLayout(this).apply { id = R.id.main_content }
|
||||
addContentView(
|
||||
rootView, ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
)
|
||||
|
||||
requestNotificationPermission(request = notificationPermission::launch)
|
||||
|
||||
supportFragmentManager.addFragmentOnAttachListener { _, _ ->
|
||||
hideKeyboard()
|
||||
}
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
cursorOwner = CursorOwner()
|
||||
supportFragmentManager.commit {
|
||||
add(cursorOwner, CursorOwner::class.java.name)
|
||||
}
|
||||
} else {
|
||||
cursorOwner =
|
||||
supportFragmentManager.findFragmentByTag(CursorOwner::class.java.name) as CursorOwner
|
||||
}
|
||||
|
||||
savedInstanceState?.getParcelableArrayList<FragmentStackItem>(STATE_FRAGMENT_STACK)
|
||||
?.let { fragmentStack += it }
|
||||
if (savedInstanceState == null) {
|
||||
replaceFragment(TabsFragment(), null)
|
||||
if ((intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
|
||||
handleIntent(intent)
|
||||
}
|
||||
}
|
||||
if (SdkCheck.isR) {
|
||||
window.statusBarColor = resources.getColor(android.R.color.transparent, theme)
|
||||
window.navigationBarColor = resources.getColor(android.R.color.transparent, theme)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
}
|
||||
backHandler()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putParcelableArrayList(STATE_FRAGMENT_STACK, ArrayList(fragmentStack))
|
||||
}
|
||||
|
||||
private fun backHandler() {
|
||||
if (onBackPressedCallback == null) {
|
||||
onBackPressedCallback = object : OnBackPressedCallback(enabled = false) {
|
||||
override fun handleOnBackPressed() {
|
||||
hideKeyboard()
|
||||
popFragment()
|
||||
}
|
||||
}
|
||||
onBackPressedDispatcher.addCallback(
|
||||
this,
|
||||
onBackPressedCallback!!,
|
||||
)
|
||||
}
|
||||
onBackPressedCallback?.isEnabled = fragmentStack.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun replaceFragment(fragment: Fragment, open: Boolean?) {
|
||||
if (open != null) {
|
||||
currentFragment?.view?.translationZ =
|
||||
(if (open) Int.MIN_VALUE else Int.MAX_VALUE).toFloat()
|
||||
}
|
||||
supportFragmentManager.commit {
|
||||
if (open != null) {
|
||||
setCustomAnimations(
|
||||
if (open) R.animator.slide_in else 0,
|
||||
if (open) R.animator.slide_in_keep else R.animator.slide_out
|
||||
)
|
||||
}
|
||||
setReorderingAllowed(true)
|
||||
replace(R.id.main_content, fragment)
|
||||
}
|
||||
}
|
||||
|
||||
private fun pushFragment(fragment: Fragment) {
|
||||
currentFragment?.let {
|
||||
fragmentStack.add(
|
||||
FragmentStackItem(
|
||||
it::class.java.name,
|
||||
it.arguments,
|
||||
supportFragmentManager.saveFragmentInstanceState(it)
|
||||
)
|
||||
)
|
||||
}
|
||||
replaceFragment(fragment, true)
|
||||
backHandler()
|
||||
}
|
||||
|
||||
private fun popFragment(): Boolean {
|
||||
return fragmentStack.isNotEmpty() && run {
|
||||
val stackItem = fragmentStack.removeAt(fragmentStack.size - 1)
|
||||
val fragment = Class.forName(stackItem.className).newInstance() as Fragment
|
||||
stackItem.arguments?.let(fragment::setArguments)
|
||||
stackItem.savedState?.let(fragment::setInitialSavedState)
|
||||
replaceFragment(fragment, false)
|
||||
backHandler()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun hideKeyboard() {
|
||||
inputManager?.hideSoftInputFromWindow((currentFocus ?: window.decorView).windowToken, 0)
|
||||
}
|
||||
|
||||
internal fun onToolbarCreated(toolbar: Toolbar) {
|
||||
if (fragmentStack.isNotEmpty()) {
|
||||
toolbar.navigationIcon = toolbar.context.homeAsUp
|
||||
toolbar.setNavigationOnClickListener { onBackPressedDispatcher.onBackPressed() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
handleIntent(intent)
|
||||
}
|
||||
|
||||
protected fun handleSpecialIntent(specialIntent: SpecialIntent) {
|
||||
when (specialIntent) {
|
||||
is SpecialIntent.Updates -> {
|
||||
if (currentFragment !is TabsFragment) {
|
||||
fragmentStack.clear()
|
||||
replaceFragment(TabsFragment(), true)
|
||||
}
|
||||
val tabsFragment = currentFragment as TabsFragment
|
||||
tabsFragment.selectUpdates()
|
||||
backHandler()
|
||||
}
|
||||
|
||||
is SpecialIntent.Install -> {
|
||||
val packageName = specialIntent.packageName
|
||||
if (!packageName.isNullOrEmpty()) {
|
||||
navigateProduct(packageName)
|
||||
specialIntent.cacheFileName?.also { cacheFile ->
|
||||
val installItem = packageName installFrom cacheFile
|
||||
lifecycleScope.launch { installer install installItem }
|
||||
}
|
||||
}
|
||||
Unit
|
||||
}
|
||||
}::class
|
||||
}
|
||||
|
||||
open fun handleIntent(intent: Intent?) {
|
||||
when (intent?.action) {
|
||||
Intent.ACTION_VIEW -> {
|
||||
when (val deeplink = intent.deeplinkType) {
|
||||
is DeeplinkType.AppDetail -> {
|
||||
val fragment = currentFragment
|
||||
if (fragment !is AppDetailFragment) {
|
||||
navigateProduct(deeplink.packageName, deeplink.repoAddress)
|
||||
}
|
||||
}
|
||||
|
||||
is DeeplinkType.AddRepository -> {
|
||||
navigateAddRepository(repoAddress = deeplink.address)
|
||||
}
|
||||
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
Intent.ACTION_SHOW_APP_INFO -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val packageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)
|
||||
|
||||
if (packageName != null && currentFragment !is AppDetailFragment) {
|
||||
navigateProduct(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun navigateFavourites() = pushFragment(FavouritesFragment())
|
||||
internal fun navigateProduct(packageName: String, repoAddress: String? = null) =
|
||||
pushFragment(AppDetailFragment(packageName, repoAddress))
|
||||
|
||||
internal fun navigateRepositories() = pushFragment(RepositoriesFragment())
|
||||
internal fun navigatePreferences() = pushFragment(SettingsFragment.newInstance())
|
||||
internal fun navigateAddRepository(repoAddress: String? = null) =
|
||||
pushFragment(EditRepositoryFragment(null, repoAddress))
|
||||
|
||||
internal fun navigateRepository(repositoryId: Long) =
|
||||
pushFragment(RepositoryFragment(repositoryId))
|
||||
|
||||
internal fun navigateEditRepository(repositoryId: Long) =
|
||||
pushFragment(EditRepositoryFragment(repositoryId, null))
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package com.looker.droidify.utility.extension
|
||||
|
||||
import android.view.View
|
||||
import com.looker.core.common.Singleton
|
||||
import com.looker.core.common.extension.dpi
|
||||
import com.looker.droidify.model.Product
|
||||
import com.looker.droidify.model.ProductItem
|
||||
import com.looker.droidify.model.Repository
|
||||
|
||||
object ImageUtils {
|
||||
private val SUPPORTED_DPI = listOf(120, 160, 240, 320, 480, 640)
|
||||
private var DeviceDpi = Singleton<String>()
|
||||
|
||||
fun Product.Screenshot.url(
|
||||
repository: Repository,
|
||||
packageName: String
|
||||
): String {
|
||||
val phoneType = when (type) {
|
||||
Product.Screenshot.Type.PHONE -> "phoneScreenshots"
|
||||
Product.Screenshot.Type.SMALL_TABLET -> "sevenInchScreenshots"
|
||||
Product.Screenshot.Type.LARGE_TABLET -> "tenInchScreenshots"
|
||||
}
|
||||
return "${repository.address}/$packageName/$locale/$phoneType/$path"
|
||||
}
|
||||
|
||||
fun ProductItem.icon(
|
||||
view: View,
|
||||
repository: Repository
|
||||
): String? {
|
||||
if (packageName.isBlank()) return null
|
||||
if (icon.isBlank() && metadataIcon.isBlank()) return null
|
||||
if (repository.version < 11 && icon.isNotBlank()) {
|
||||
return "${repository.address}/icons/$icon"
|
||||
}
|
||||
if (icon.isNotBlank()) {
|
||||
val deviceDpi = DeviceDpi.getOrUpdate {
|
||||
(SUPPORTED_DPI.find { it >= view.dpi } ?: SUPPORTED_DPI.last()).toString()
|
||||
}
|
||||
return "${repository.address}/icons-$deviceDpi/$icon"
|
||||
}
|
||||
if (metadataIcon.isNotBlank()) {
|
||||
return "${repository.address}/$packageName/$metadataIcon"
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/exploreTab"
|
||||
android:icon="@drawable/ic_public"
|
||||
android:title="@string/explore" />
|
||||
<item
|
||||
android:id="@+id/latestTab"
|
||||
android:icon="@drawable/ic_new_releases"
|
||||
android:title="@string/latest" />
|
||||
<item
|
||||
android:id="@+id/installedTab"
|
||||
android:icon="@drawable/ic_launch"
|
||||
android:title="@string/installed" />
|
||||
</menu>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB |
Reference in New Issue
Block a user