diff --git a/src/main/kotlin/com/looker/droidify/database/DAOs.kt b/src/main/kotlin/com/looker/droidify/database/DAOs.kt index 281bef49..839666ff 100644 --- a/src/main/kotlin/com/looker/droidify/database/DAOs.kt +++ b/src/main/kotlin/com/looker/droidify/database/DAOs.kt @@ -51,6 +51,9 @@ interface RepositoryDao : BaseDao { @Query("SELECT * FROM repository WHERE _id = :id and deleted == 0") fun get(id: Long): Repository? + @Query("SELECT * FROM repository WHERE _id = :id and deleted == 0") + fun getLive(id: Long): LiveData + @get:Query("SELECT * FROM repository WHERE deleted == 0 ORDER BY _id ASC") val allCursor: Cursor @@ -81,6 +84,9 @@ interface ProductDao : BaseDao { @Query("SELECT COUNT(*) FROM product WHERE repository_id = :id") fun countForRepository(id: Long): Long + @Query("SELECT COUNT(*) FROM product WHERE repository_id = :id") + fun countForRepositoryLive(id: Long): LiveData + @Query("SELECT * FROM product WHERE package_name = :packageName") fun get(packageName: String): List diff --git a/src/main/kotlin/com/looker/droidify/entity/Repository.kt b/src/main/kotlin/com/looker/droidify/entity/Repository.kt index 1f67d7e0..8053d2e0 100644 --- a/src/main/kotlin/com/looker/droidify/entity/Repository.kt +++ b/src/main/kotlin/com/looker/droidify/entity/Repository.kt @@ -9,7 +9,7 @@ import java.net.URL data class Repository( var id: Long, val address: String, val mirrors: List, - val name: String, val description: String, val version: Int, val enabled: Boolean, + val name: String, val description: String, val version: Int, var enabled: Boolean, val fingerprint: String, val lastModified: String, val entityTag: String, val updated: Long, val timestamp: Long, val authentication: String, ) { diff --git a/src/main/kotlin/com/looker/droidify/ui/fragments/PrefsRepositoriesFragment.kt b/src/main/kotlin/com/looker/droidify/ui/fragments/PrefsRepositoriesFragment.kt index a0f5ab95..3c6dce35 100644 --- a/src/main/kotlin/com/looker/droidify/ui/fragments/PrefsRepositoriesFragment.kt +++ b/src/main/kotlin/com/looker/droidify/ui/fragments/PrefsRepositoriesFragment.kt @@ -60,6 +60,16 @@ class PrefsRepositoriesFragment : BaseNavFragment() { } false } + reposFastAdapter?.onLongClickListener = + { _: View?, _: IAdapter?, item: RepoItem?, _: Int? -> + item?.item?.let { + RepositorySheetX(it.id).showNow( + parentFragmentManager, + "Repository ${it.id}" + ) + } + false + } binding.recyclerView.apply { layoutManager = LinearLayoutManager(context) diff --git a/src/main/kotlin/com/looker/droidify/ui/fragments/RepositorySheetX.kt b/src/main/kotlin/com/looker/droidify/ui/fragments/RepositorySheetX.kt new file mode 100644 index 00000000..e768934f --- /dev/null +++ b/src/main/kotlin/com/looker/droidify/ui/fragments/RepositorySheetX.kt @@ -0,0 +1,135 @@ +package com.looker.droidify.ui.fragments + +import android.app.Dialog +import android.os.Bundle +import android.text.SpannableStringBuilder +import android.text.format.DateUtils +import android.text.style.ForegroundColorSpan +import android.text.style.TypefaceSpan +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.looker.droidify.R +import com.looker.droidify.databinding.SheetRepositoryBinding +import com.looker.droidify.service.Connection +import com.looker.droidify.service.SyncService +import com.looker.droidify.ui.activities.PrefsActivityX +import com.looker.droidify.ui.viewmodels.RepositoryViewModelX +import com.looker.droidify.utility.extension.resources.getColorFromAttr +import java.util.* + +class RepositorySheetX() : BottomSheetDialogFragment() { + private lateinit var binding: SheetRepositoryBinding + val viewModel: RepositoryViewModelX by viewModels { + RepositoryViewModelX.Factory((requireActivity() as PrefsActivityX).db, repositoryId) + } + + companion object { + private const val EXTRA_REPOSITORY_ID = "repositoryId" + } + + constructor(repositoryId: Long = 0) : this() { + arguments = Bundle().apply { + putLong(EXTRA_REPOSITORY_ID, repositoryId) + } + } + + private val repositoryId: Long + get() = requireArguments().getLong(EXTRA_REPOSITORY_ID) + + private val syncConnection = Connection(SyncService::class.java) + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val sheet = super.onCreateDialog(savedInstanceState) as BottomSheetDialog + sheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED + return sheet + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = SheetRepositoryBinding.inflate(layoutInflater) + syncConnection.bind(requireContext()) + viewModel.repo.observe(viewLifecycleOwner) { updateRepositoryView() } + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + syncConnection.unbind(requireContext()) + } + + private fun updateRepositoryView() { + val repository = viewModel.repo.value?.trueData + + if (repository == null) { + binding.address.text = getString(R.string.unknown) + binding.nameBlock.visibility = View.GONE + binding.descriptionBlock.visibility = View.GONE + binding.updatedBlock.visibility = View.GONE + binding.appsBlock.visibility = View.GONE + } else { + binding.address.text = repository.address + binding.descriptionBlock.visibility = View.VISIBLE + if (repository.updated > 0L) { + binding.nameBlock.visibility = View.VISIBLE + binding.updatedBlock.visibility = View.VISIBLE + binding.name.text = repository.name + binding.description.text = repository.description.replace('\n', ' ') + binding.updated.text = run { + val lastUpdated = repository.updated + if (lastUpdated > 0L) { + val date = Date(repository.updated) + val format = + if (DateUtils.isToday(date.time)) DateUtils.FORMAT_SHOW_TIME else + DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE + DateUtils.formatDateTime(binding.root.context, date.time, format) + } else { + getString(R.string.unknown) + } + } + if (repository.enabled && (repository.lastModified.isNotEmpty() || repository.entityTag.isNotEmpty())) { + binding.appsBlock.visibility = View.VISIBLE + binding.apps.text = viewModel.appsCount.value.toString() + } else { + binding.appsBlock.visibility = View.GONE + } + } else { + binding.description.text = getString(R.string.repository_not_used_DESC) + binding.nameBlock.visibility = View.GONE + binding.updatedBlock.visibility = View.GONE + binding.appsBlock.visibility = View.GONE + } + + if (repository.fingerprint.isEmpty()) { + if (repository.updated > 0L) { + val builder = + SpannableStringBuilder(getString(R.string.repository_unsigned_DESC)) + builder.setSpan( + ForegroundColorSpan(binding.root.context.getColorFromAttr(R.attr.colorError).defaultColor), + 0, builder.length, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE + ) + binding.fingerprint.text = builder + binding.fingerprintBlock.visibility = View.VISIBLE + } else + binding.fingerprintBlock.visibility = View.GONE + } else { + val fingerprint = + SpannableStringBuilder(repository.fingerprint.windowed(2, 2, false) + .take(32).joinToString(separator = " ") { it.uppercase(Locale.US) }) + fingerprint.setSpan( + TypefaceSpan("monospace"), 0, fingerprint.length, + SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE + ) + binding.fingerprint.text = fingerprint + binding.fingerprintBlock.visibility = View.VISIBLE + } + } + } +} diff --git a/src/main/kotlin/com/looker/droidify/ui/viewmodels/RepositoryViewModelX.kt b/src/main/kotlin/com/looker/droidify/ui/viewmodels/RepositoryViewModelX.kt new file mode 100644 index 00000000..d35cace0 --- /dev/null +++ b/src/main/kotlin/com/looker/droidify/ui/viewmodels/RepositoryViewModelX.kt @@ -0,0 +1,28 @@ +package com.looker.droidify.ui.viewmodels + +import androidx.lifecycle.MediatorLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.looker.droidify.database.DatabaseX +import com.looker.droidify.database.entity.Repository + +class RepositoryViewModelX(val db: DatabaseX, val repositoryId: Long) : ViewModel() { + + val repo = MediatorLiveData() + val appsCount = MediatorLiveData() + + init { + repo.addSource(db.repositoryDao.getLive(repositoryId), repo::setValue) + appsCount.addSource(db.productDao.countForRepositoryLive(repositoryId), appsCount::setValue) + } + + class Factory(val db: DatabaseX, val repositoryId: Long) : ViewModelProvider.Factory { + @Suppress("unchecked_cast") + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(RepositoryViewModelX::class.java)) { + return RepositoryViewModelX(db, repositoryId) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } + } +} diff --git a/src/main/res/layout/sheet_repository.xml b/src/main/res/layout/sheet_repository.xml new file mode 100644 index 00000000..0b4edfac --- /dev/null +++ b/src/main/res/layout/sheet_repository.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file