Initial commit

This commit is contained in:
kitsunyan
2020-06-06 01:28:55 +03:00
commit b046baab59
131 changed files with 12203 additions and 0 deletions
@@ -0,0 +1,241 @@
package nya.kitsunyan.foxydroid.screen
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Parcel
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import nya.kitsunyan.foxydroid.R
import nya.kitsunyan.foxydroid.content.Cache
import nya.kitsunyan.foxydroid.content.Preferences
import nya.kitsunyan.foxydroid.database.CursorOwner
import nya.kitsunyan.foxydroid.utility.KParcelable
import nya.kitsunyan.foxydroid.utility.Utils
import nya.kitsunyan.foxydroid.utility.extension.android.*
import nya.kitsunyan.foxydroid.utility.extension.resources.*
import nya.kitsunyan.foxydroid.utility.extension.text.*
import java.lang.ref.WeakReference
abstract class ScreenActivity: AppCompatActivity() {
companion object {
private const val STATE_FRAGMENT_STACK = "fragmentStack"
}
sealed class SpecialIntent {
object Updates: SpecialIntent()
class Install(val packageName: String?, val cacheFileName: String?): SpecialIntent()
}
private class FragmentStackItem(val className: String, val arguments: Bundle?,
val savedState: Fragment.SavedState?): KParcelable {
override fun writeToParcel(dest: Parcel, flags: Int) {
dest.writeString(className)
dest.writeByte(if (arguments != null) 1 else 0)
arguments?.writeToParcel(dest, flags)
dest.writeByte(if (savedState != null) 1 else 0)
savedState?.writeToParcel(dest, flags)
}
companion object {
@Suppress("unused") @JvmField val CREATOR = KParcelable.creator {
val className = it.readString()!!
val arguments = if (it.readByte().toInt() == 0) null else Bundle.CREATOR.createFromParcel(it)
arguments?.classLoader = ScreenActivity::class.java.classLoader
val savedState = if (it.readByte().toInt() == 0) null else Fragment.SavedState.CREATOR.createFromParcel(it)
FragmentStackItem(className, arguments, savedState)
}
}
}
lateinit var cursorOwner: CursorOwner
private set
private val fragmentStack = mutableListOf<FragmentStackItem>()
private var toolbar: WeakReference<Toolbar>? = null
private val currentFragment: Fragment?
get() {
supportFragmentManager.executePendingTransactions()
return supportFragmentManager.findFragmentById(R.id.main_content)
}
override fun attachBaseContext(base: Context) {
super.attachBaseContext(Utils.configureLocale(base))
}
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(Preferences[Preferences.Key.Theme].resId)
super.onCreate(savedInstanceState)
window.decorView.systemUiVisibility = window.decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
addContentView(FrameLayout(this).apply { id = R.id.main_content },
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))
if (savedInstanceState == null) {
cursorOwner = CursorOwner()
supportFragmentManager.beginTransaction()
.add(cursorOwner, CursorOwner::class.java.name)
.commit()
} 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)
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelableArrayList(STATE_FRAGMENT_STACK, ArrayList(fragmentStack))
}
override fun onBackPressed() {
val menuItem = toolbar?.get()?.menu?.findItem(R.id.toolbar_search)
if (menuItem != null && menuItem.isActionViewExpanded) {
menuItem.collapseActionView()
} else {
hideKeyboard()
if (!popFragment()) {
super.onBackPressed()
}
}
}
private fun replaceFragment(fragment: Fragment, open: Boolean?) {
if (open != null) {
currentFragment?.view?.translationZ = (if (open) Int.MIN_VALUE else Int.MAX_VALUE).toFloat()
}
supportFragmentManager
.beginTransaction()
.apply {
if (open != null) {
setCustomAnimations(if (open) R.animator.slide_in else 0,
if (open) R.animator.slide_in_keep else R.animator.slide_out)
}
}
.replace(R.id.main_content, fragment)
.commit()
}
private fun pushFragment(fragment: Fragment) {
currentFragment?.let { fragmentStack.add(FragmentStackItem(it::class.java.name, it.arguments,
supportFragmentManager.saveFragmentInstanceState(it))) }
replaceFragment(fragment, true)
}
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)
true
}
}
private fun hideKeyboard() {
(getSystemService(INPUT_METHOD_SERVICE) as? InputMethodManager)
?.hideSoftInputFromWindow((currentFocus ?: window.decorView).windowToken, 0)
}
override fun onAttachFragment(fragment: Fragment) {
super.onAttachFragment(fragment)
hideKeyboard()
}
internal fun onFragmentViewCreated(toolbar: Toolbar?) {
this.toolbar = toolbar?.let(::WeakReference)
if (fragmentStack.isNotEmpty() && toolbar != null) {
toolbar.navigationIcon = toolbar.context.getDrawableFromAttr(R.attr.homeAsUpIndicator)
toolbar.setNavigationOnClickListener { onBackPressed() }
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
handleIntent(intent)
}
protected 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()
else -> null
}
}
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()
}
is SpecialIntent.Install -> {
val packageName = specialIntent.packageName
if (!packageName.isNullOrEmpty()) {
val fragment = currentFragment
if (fragment !is ProductFragment || fragment.packageName != packageName) {
pushFragment(ProductFragment(packageName))
}
specialIntent.cacheFileName?.let(::startPackageInstaller)
}
Unit
}
}::class
}
open fun handleIntent(intent: Intent?) {
when (intent?.action) {
Intent.ACTION_VIEW -> {
val packageName = intent.packageName
if (!packageName.isNullOrEmpty()) {
val fragment = currentFragment
if (fragment !is ProductFragment || fragment.packageName != packageName) {
pushFragment(ProductFragment(packageName))
}
}
}
}
}
internal fun startPackageInstaller(cacheFileName: String) {
val (uri, flags) = if (Android.sdk(24)) {
Pair(Cache.getReleaseUri(this, cacheFileName), Intent.FLAG_GRANT_READ_URI_PERMISSION)
} else {
Pair(Uri.fromFile(Cache.getReleaseFile(this, cacheFileName)), 0)
}
// TODO Handle deprecation
@Suppress("DEPRECATION")
startActivity(Intent(Intent.ACTION_INSTALL_PACKAGE)
.setDataAndType(uri, "application/vnd.android.package-archive").setFlags(flags))
}
internal fun navigateProduct(packageName: String) = pushFragment(ProductFragment(packageName))
internal fun navigateRepositories() = pushFragment(RepositoriesFragment())
internal fun navigatePreferences() = pushFragment(PreferencesFragment())
internal fun navigateAddRepository() = pushFragment(EditRepositoryFragment(null))
internal fun navigateRepository(repositoryId: Long) = pushFragment(RepositoryFragment(repositoryId))
internal fun navigateEditRepository(repositoryId: Long) = pushFragment(EditRepositoryFragment(repositoryId))
}