mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-04-23 11:22:12 +00:00
Add: The main activity [new UI]
This commit is contained in:
parent
f5422ba511
commit
ea80e658cf
@ -45,7 +45,7 @@ class CursorOwner : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
|
|||||||
fun onCursorData(request: Request, cursor: Cursor?)
|
fun onCursorData(request: Request, cursor: Cursor?)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class ActiveRequest(
|
data class ActiveRequest(
|
||||||
val request: Request,
|
val request: Request,
|
||||||
val callback: Callback?,
|
val callback: Callback?,
|
||||||
val cursor: Cursor?,
|
val cursor: Cursor?,
|
||||||
|
@ -0,0 +1,308 @@
|
|||||||
|
package com.looker.droidify.ui.activities
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.*
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.loader.app.LoaderManager
|
||||||
|
import androidx.loader.content.Loader
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.navigation.fragment.NavHostFragment
|
||||||
|
import androidx.navigation.ui.AppBarConfiguration
|
||||||
|
import androidx.navigation.ui.navigateUp
|
||||||
|
import androidx.navigation.ui.setupActionBarWithNavController
|
||||||
|
import androidx.navigation.ui.setupWithNavController
|
||||||
|
import com.google.android.material.appbar.MaterialToolbar
|
||||||
|
import com.looker.droidify.BuildConfig
|
||||||
|
import com.looker.droidify.ContextWrapperX
|
||||||
|
import com.looker.droidify.R
|
||||||
|
import com.looker.droidify.content.Preferences
|
||||||
|
import com.looker.droidify.database.CursorOwner
|
||||||
|
import com.looker.droidify.database.Database
|
||||||
|
import com.looker.droidify.database.QueryLoader
|
||||||
|
import com.looker.droidify.databinding.ActivityMainXBinding
|
||||||
|
import com.looker.droidify.installer.AppInstaller
|
||||||
|
import com.looker.droidify.screen.*
|
||||||
|
import com.looker.droidify.service.Connection
|
||||||
|
import com.looker.droidify.service.SyncService
|
||||||
|
import com.looker.droidify.ui.fragments.MainNavFragmentX
|
||||||
|
import com.looker.droidify.ui.fragments.Source
|
||||||
|
import com.looker.droidify.ui.viewmodels.MainActivityViewModelX
|
||||||
|
import com.looker.droidify.utility.extension.android.Android
|
||||||
|
import com.looker.droidify.utility.extension.text.nullIfEmpty
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class MainActivityX : AppCompatActivity(), LoaderManager.LoaderCallbacks<Cursor> {
|
||||||
|
companion object {
|
||||||
|
const val ACTION_UPDATES = "${BuildConfig.APPLICATION_ID}.intent.action.UPDATES"
|
||||||
|
const val ACTION_INSTALL = "${BuildConfig.APPLICATION_ID}.intent.action.INSTALL"
|
||||||
|
const val EXTRA_CACHE_FILE_NAME =
|
||||||
|
"${BuildConfig.APPLICATION_ID}.intent.extra.CACHE_FILE_NAME"
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class SpecialIntent {
|
||||||
|
object Updates : SpecialIntent()
|
||||||
|
class Install(val packageName: String?, val cacheFileName: String?) : SpecialIntent()
|
||||||
|
}
|
||||||
|
|
||||||
|
lateinit var binding: ActivityMainXBinding
|
||||||
|
lateinit var toolbar: MaterialToolbar
|
||||||
|
lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
|
private lateinit var navController: NavController
|
||||||
|
private val viewModel: MainActivityViewModelX by viewModels()
|
||||||
|
|
||||||
|
private val syncConnection = Connection(SyncService::class.java, onBind = { _, _ ->
|
||||||
|
navController.currentDestination?.let {
|
||||||
|
val source = Source.values()[when (it.id) {
|
||||||
|
R.id.latestTab -> 1
|
||||||
|
R.id.installedTab -> 2
|
||||||
|
else -> 0 // R.id.exploreTab
|
||||||
|
}]
|
||||||
|
updateUpdateNotificationBlocker(source)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
lateinit var cursorOwner: CursorOwner
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
setTheme(Preferences[Preferences.Key.Theme].getResId(resources.configuration))
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
binding = ActivityMainXBinding.inflate(layoutInflater)
|
||||||
|
binding.lifecycleOwner = this
|
||||||
|
toolbar = binding.toolbar
|
||||||
|
|
||||||
|
if (savedInstanceState == null && (intent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
|
||||||
|
handleIntent(intent)
|
||||||
|
}
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
cursorOwner = CursorOwner()
|
||||||
|
supportFragmentManager.beginTransaction()
|
||||||
|
.add(cursorOwner, CursorOwner::class.java.name)
|
||||||
|
.commit()
|
||||||
|
} else {
|
||||||
|
cursorOwner = supportFragmentManager
|
||||||
|
.findFragmentByTag(CursorOwner::class.java.name) as CursorOwner
|
||||||
|
}
|
||||||
|
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
|
if (Android.sdk(28) && !Android.Device.isHuaweiEmui) {
|
||||||
|
toolbar.menu.setGroupDividerEnabled(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
toolbar.isFocusableInTouchMode = true
|
||||||
|
binding.collapsingToolbar.title = getString(R.string.application_name)
|
||||||
|
|
||||||
|
val navHostFragment =
|
||||||
|
supportFragmentManager.findFragmentById(R.id.fragment_content) as NavHostFragment
|
||||||
|
navController = navHostFragment.navController
|
||||||
|
binding.bottomNavigation.setupWithNavController(navController)
|
||||||
|
|
||||||
|
appBarConfiguration = AppBarConfiguration(
|
||||||
|
setOf(R.id.exploreTab, R.id.latestTab, R.id.installedTab)
|
||||||
|
)
|
||||||
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
|
|
||||||
|
supportFragmentManager.addFragmentOnAttachListener { _, _ ->
|
||||||
|
hideKeyboard()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
syncConnection.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
|
return navController.navigateUp(appBarConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.menu_main, menu)
|
||||||
|
return super.onCreateOptionsMenu(menu)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideKeyboard() {
|
||||||
|
(getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager)
|
||||||
|
?.hideSoftInputFromWindow((currentFocus ?: window.decorView).windowToken, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun syncManual(item: MenuItem) {
|
||||||
|
syncConnection.binder?.sync(SyncService.SyncRequest.MANUAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun navigateSettings(item: MenuItem) {
|
||||||
|
navigateSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent?) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
handleIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val Intent.packageName: String?
|
||||||
|
get() {
|
||||||
|
val uri = data
|
||||||
|
return when {
|
||||||
|
uri?.scheme == "package" || uri?.scheme == "fdroid.app" -> {
|
||||||
|
uri.schemeSpecificPart?.nullIfEmpty()
|
||||||
|
}
|
||||||
|
uri?.scheme == "market" && uri.host == "details" -> {
|
||||||
|
uri.getQueryParameter("id")?.nullIfEmpty()
|
||||||
|
}
|
||||||
|
uri != null && uri.scheme in setOf("http", "https") -> {
|
||||||
|
val host = uri.host.orEmpty()
|
||||||
|
if (host == "f-droid.org" || host.endsWith(".f-droid.org")) {
|
||||||
|
uri.lastPathSegment?.nullIfEmpty()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSpecialIntent(specialIntent: SpecialIntent) {
|
||||||
|
when (specialIntent) {
|
||||||
|
is SpecialIntent.Updates -> navController.navigate(R.id.installedTab)
|
||||||
|
is SpecialIntent.Install -> {
|
||||||
|
val packageName = specialIntent.packageName
|
||||||
|
if (!packageName.isNullOrEmpty()) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
specialIntent.cacheFileName?.let {
|
||||||
|
AppInstaller.getInstance(this@MainActivityX)
|
||||||
|
?.defaultInstaller?.install(packageName, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
}::class
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleIntent(intent: Intent?) {
|
||||||
|
when (intent?.action) {
|
||||||
|
Intent.ACTION_VIEW -> {
|
||||||
|
val packageName = intent.packageName
|
||||||
|
if (!packageName.isNullOrEmpty()) {
|
||||||
|
navigateProduct(packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ACTION_UPDATES -> handleSpecialIntent(SpecialIntent.Updates)
|
||||||
|
ACTION_INSTALL -> handleSpecialIntent(
|
||||||
|
SpecialIntent.Install(
|
||||||
|
intent.packageName,
|
||||||
|
intent.getStringExtra(EXTRA_CACHE_FILE_NAME)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun attachBaseContext(newBase: Context) {
|
||||||
|
super.attachBaseContext(ContextWrapperX.wrap(newBase))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun navigateProduct(packageName: String) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun navigateSettings() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateUpdateNotificationBlocker(activeSource: Source) {
|
||||||
|
val blockerFragment = if (activeSource == Source.UPDATES) {
|
||||||
|
supportFragmentManager.fragments.asSequence().mapNotNull { it as? MainNavFragmentX }
|
||||||
|
.find { it.source == activeSource }
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
syncConnection.binder?.setUpdateNotificationBlocker(blockerFragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun attachCursorOwner(callback: CursorOwner.Callback, request: CursorOwner.Request) {
|
||||||
|
val oldActiveRequest = viewModel.activeRequests[request.id]
|
||||||
|
if (oldActiveRequest?.callback != null &&
|
||||||
|
oldActiveRequest.callback != callback && oldActiveRequest.cursor != null
|
||||||
|
) {
|
||||||
|
oldActiveRequest.callback.onCursorData(oldActiveRequest.request, null)
|
||||||
|
}
|
||||||
|
val cursor = if (oldActiveRequest?.request == request && oldActiveRequest.cursor != null) {
|
||||||
|
callback.onCursorData(request, oldActiveRequest.cursor)
|
||||||
|
oldActiveRequest.cursor
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
viewModel.activeRequests[request.id] = CursorOwner.ActiveRequest(request, callback, cursor)
|
||||||
|
if (cursor == null) {
|
||||||
|
LoaderManager.getInstance(this).restartLoader(request.id, null, this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun detachCursorOwner(callback: CursorOwner.Callback) {
|
||||||
|
for (id in viewModel.activeRequests.keys) {
|
||||||
|
val activeRequest = viewModel.activeRequests[id]!!
|
||||||
|
if (activeRequest.callback == callback) {
|
||||||
|
viewModel.activeRequests[id] = activeRequest.copy(callback = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
|
||||||
|
val request = viewModel.activeRequests[id]!!.request
|
||||||
|
return QueryLoader(this) {
|
||||||
|
when (request) {
|
||||||
|
is CursorOwner.Request.ProductsAvailable -> Database.ProductAdapter
|
||||||
|
.query(
|
||||||
|
installed = false,
|
||||||
|
updates = false,
|
||||||
|
searchQuery = request.searchQuery,
|
||||||
|
section = request.section,
|
||||||
|
order = request.order,
|
||||||
|
signal = it
|
||||||
|
)
|
||||||
|
is CursorOwner.Request.ProductsInstalled -> Database.ProductAdapter
|
||||||
|
.query(
|
||||||
|
installed = true,
|
||||||
|
updates = false,
|
||||||
|
searchQuery = request.searchQuery,
|
||||||
|
section = request.section,
|
||||||
|
order = request.order,
|
||||||
|
signal = it
|
||||||
|
)
|
||||||
|
is CursorOwner.Request.ProductsUpdates -> Database.ProductAdapter
|
||||||
|
.query(
|
||||||
|
installed = true,
|
||||||
|
updates = true,
|
||||||
|
searchQuery = request.searchQuery,
|
||||||
|
section = request.section,
|
||||||
|
order = request.order,
|
||||||
|
signal = it
|
||||||
|
)
|
||||||
|
is CursorOwner.Request.Repositories -> Database.RepositoryAdapter.query(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) {
|
||||||
|
val activeRequest = viewModel.activeRequests[loader.id]
|
||||||
|
if (activeRequest != null) {
|
||||||
|
viewModel.activeRequests[loader.id] = activeRequest.copy(cursor = data)
|
||||||
|
activeRequest.callback?.onCursorData(activeRequest.request, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLoaderReset(loader: Loader<Cursor>) = onLoadFinished(loader, null)
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.looker.droidify.ui.viewmodels
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.looker.droidify.database.CursorOwner
|
||||||
|
import com.looker.droidify.ui.activities.MainActivityX
|
||||||
|
|
||||||
|
class MainActivityViewModelX() : ViewModel() {
|
||||||
|
|
||||||
|
val activeRequests = mutableMapOf<Int, CursorOwner.ActiveRequest>()
|
||||||
|
}
|
@ -37,17 +37,21 @@
|
|||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
style="?attr/toolbarStyle"
|
style="?attr/toolbarStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?actionBarSize" />
|
android:layout_height="?actionBarSize"
|
||||||
|
app:menu="@menu/menu_main" />
|
||||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
<androidx.fragment.app.FragmentContainerView
|
||||||
android:id="@+id/fragment_content"
|
android:id="@+id/fragment_content"
|
||||||
|
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/background_recycler_bottom"
|
android:background="@drawable/background_recycler_bottom"
|
||||||
android:backgroundTint="?android:colorBackground"
|
android:backgroundTint="?android:colorBackground"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:defaultNavHost="true"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
app:navGraph="@navigation/navigation_graph_main" />
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
Loading…
x
Reference in New Issue
Block a user