diff --git a/apollo-compiler/src/main/java/com/github/damontecres/apollo/compiler/StashApolloCompilerPlugin.kt b/apollo-compiler/src/main/java/com/github/damontecres/apollo/compiler/StashApolloCompilerPlugin.kt index 0675ee85..38c21109 100644 --- a/apollo-compiler/src/main/java/com/github/damontecres/apollo/compiler/StashApolloCompilerPlugin.kt +++ b/apollo-compiler/src/main/java/com/github/damontecres/apollo/compiler/StashApolloCompilerPlugin.kt @@ -8,28 +8,35 @@ import com.squareup.kotlinpoet.DelicateKotlinPoetApi import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.ParameterizedTypeName -import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeSpec import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable +/** + * An [ApolloCompilerPlugin] to add some extra annotations and interfaces to the classes generated from the graphql schema + */ class StashApolloCompilerPlugin : ApolloCompilerPlugin { override fun kotlinOutputTransform(): Transform { return object : Transform { override fun transform(input: KotlinOutput): KotlinOutput { - val sampleFileSpec = input.fileSpecs.first() val packageName = "com.github.damontecres.stashapp.api" + + // Create an interface for "data" types (Performer, Scene, etc) + // It has an id property and is sealed which allows for Serializable val stashDataInterface = ClassName("$packageName.fragment", "StashData") val stashDataFileSpec = FileSpec.builder(stashDataInterface) .addType( TypeSpec.interfaceBuilder(stashDataInterface) - .addModifiers(KModifier.SEALED) .addProperty("id", String::class) + .addModifiers(KModifier.SEALED) + .addAnnotation(Serializable::class) .build(), ) .build() + // Create an interface for "filter" types (PerformerFilterType, etc) + // It is sealed which allows for Serializable val stashFilterInterface = ClassName("$packageName.type", "StashDataFilter") val stashFilterFileSpec = FileSpec.builder(stashFilterInterface) @@ -44,8 +51,11 @@ class StashApolloCompilerPlugin : ApolloCompilerPlugin { val newFileSpecs = input.fileSpecs.map { file -> if (file.name.endsWith("FilterType") || file.name.endsWith("CriterionInput")) { + // Modify filter or filter input types handleFilterInput(file, stashFilterInterface) } else if (file.name.endsWith("Data")) { + // Modify data types + // Note that fragments for data types by convention are suffixed with "Data" handleData(file, stashDataInterface) } else { file @@ -67,6 +77,7 @@ class StashApolloCompilerPlugin : ApolloCompilerPlugin { val builder = file.toBuilder() builder.members.replaceAll { member -> if (member is TypeSpec) { + // Mark as Serializable val typeBuilder = member.toBuilder() .addAnnotation(Serializable::class.java) @@ -74,6 +85,8 @@ class StashApolloCompilerPlugin : ApolloCompilerPlugin { if (prop.type is ParameterizedTypeName && (prop.type as ParameterizedTypeName).rawType.canonicalName == "com.apollographql.apollo.api.Optional" ) { + // If the property is an Optional (basically all of them), then add a Contextual annotation + // This allows for runtime serialization, because the app defines a serializer for this class prop.toBuilder() .addAnnotation(Contextual::class) .build() @@ -82,12 +95,12 @@ class StashApolloCompilerPlugin : ApolloCompilerPlugin { } } + // If the type is a filter, add the interface if (member.name!!.endsWith("FilterType")) { typeBuilder.addSuperinterface(stashFilterInterface) } typeBuilder.build() - } else if (member is PropertySpec) { } else { member } @@ -95,7 +108,6 @@ class StashApolloCompilerPlugin : ApolloCompilerPlugin { return builder.build() } - @OptIn(DelicateKotlinPoetApi::class) private fun handleData( file: FileSpec, stashDataInterface: ClassName, @@ -103,34 +115,20 @@ class StashApolloCompilerPlugin : ApolloCompilerPlugin { val builder = file.toBuilder() builder.members.replaceAll { member -> if (member is TypeSpec && member.propertySpecs.find { it.name == "id" } != null) { + // Mark the type with the data interface val memberBuilder = member.toBuilder() .addSuperinterface(stashDataInterface) - // TODO: adding Serializable i -// .addAnnotation(Serializable::class) memberBuilder.propertySpecs.replaceAll { if (it.name == "id") { + // If the property is named id, need to add override due to the super interface it.toBuilder() .addModifiers(KModifier.OVERRIDE) .build() - } else if (it.name == "updated_at" || it.name == "created_at") { - it.toBuilder() -// .addAnnotation(Contextual::class) - .build() } else { it } } - memberBuilder.typeSpecs.replaceAll { innerType -> - if (innerType.modifiers.contains(KModifier.DATA)) { - // Inner data class - innerType.toBuilder() -// .addAnnotation(Serializable::class) - .build() - } else { - innerType - } - } memberBuilder.build() } else { member diff --git a/app/src/main/graphql/FindImages.graphql b/app/src/main/graphql/FindImages.graphql index b8b1fd59..aa958f14 100644 --- a/app/src/main/graphql/FindImages.graphql +++ b/app/src/main/graphql/FindImages.graphql @@ -1,5 +1,5 @@ -query FindImages($filter: FindFilterType, $image_filter: ImageFilterType) { - findImages(filter: $filter, image_filter: $image_filter) { +query FindImages($filter: FindFilterType, $image_filter: ImageFilterType, $ids: [ID!]) { + findImages(filter: $filter, image_filter: $image_filter, ids: $ids) { images { ...ImageData } diff --git a/app/src/main/java/com/github/damontecres/stashapp/DebugActivity.kt b/app/src/main/java/com/github/damontecres/stashapp/DebugActivity.kt index e428ab99..400d7f51 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/DebugActivity.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/DebugActivity.kt @@ -26,6 +26,11 @@ import com.github.damontecres.stashapp.util.StashServer import com.github.damontecres.stashapp.util.plugin.CompanionPlugin import kotlinx.coroutines.launch +/** + * Activity to show various debugging information + * + * Not really intended for the average user to use + */ class DebugActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/github/damontecres/stashapp/FilterListActivity.kt b/app/src/main/java/com/github/damontecres/stashapp/FilterListActivity.kt index 4db84df3..3368955f 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/FilterListActivity.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/FilterListActivity.kt @@ -16,8 +16,6 @@ import androidx.appcompat.widget.ListPopupWindow import androidx.fragment.app.FragmentActivity import androidx.fragment.app.commit import androidx.lifecycle.lifecycleScope -import androidx.preference.PreferenceManager -import com.chrynan.parcelable.core.getParcelableExtra import com.github.damontecres.stashapp.data.DataType import com.github.damontecres.stashapp.filter.CreateFilterActivity import com.github.damontecres.stashapp.filter.FilterOptions @@ -27,8 +25,8 @@ import com.github.damontecres.stashapp.util.FilterParser import com.github.damontecres.stashapp.util.QueryEngine import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler import com.github.damontecres.stashapp.util.StashServer +import com.github.damontecres.stashapp.util.getFilterArgs import com.github.damontecres.stashapp.util.getMaxMeasuredWidth -import com.github.damontecres.stashapp.util.parcelable import com.github.damontecres.stashapp.util.putDataType import com.github.damontecres.stashapp.util.putFilterArgs import com.github.damontecres.stashapp.views.PlayAllOnClickListener @@ -52,8 +50,6 @@ class FilterListActivity : FragmentActivity(R.layout.filter_list) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val preferences = PreferenceManager.getDefaultSharedPreferences(this) - filterButton = findViewById(R.id.filter_button) filterButton.setOnClickListener { Toast.makeText(this, "Filters not loaded yet!", Toast.LENGTH_SHORT).show() @@ -85,8 +81,7 @@ class FilterListActivity : FragmentActivity(R.layout.filter_list) { } } - val startingFilter = - intent.getParcelableExtra(INTENT_FILTER_ARGS, FilterArgs::class, 0, parcelable)!! + val startingFilter = intent.getFilterArgs(INTENT_FILTER_ARGS)!! if (savedInstanceState == null) { setup(startingFilter, first = true) } diff --git a/app/src/main/java/com/github/damontecres/stashapp/GalleryFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/GalleryFragment.kt index 7f841a7f..6fb6bd2f 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/GalleryFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/GalleryFragment.kt @@ -34,6 +34,7 @@ import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler import com.github.damontecres.stashapp.util.StashFragmentPagerAdapter import com.github.damontecres.stashapp.util.StashGlide import com.github.damontecres.stashapp.util.StashServer +import com.github.damontecres.stashapp.util.getParcelable import com.github.damontecres.stashapp.util.isNotNullOrBlank import com.github.damontecres.stashapp.util.putFilterArgs import com.github.damontecres.stashapp.util.showSetRatingToast @@ -45,7 +46,7 @@ class GalleryFragment : TabbedFragment() { private lateinit var gallery: Gallery override fun onCreate(savedInstanceState: Bundle?) { - gallery = requireActivity().intent.getParcelableExtra(INTENT_GALLERY_OBJ)!! + gallery = requireActivity().intent.getParcelable(INTENT_GALLERY_OBJ, Gallery::class)!! super.onCreate(savedInstanceState) viewModel.title.value = gallery.name } @@ -60,7 +61,7 @@ class GalleryFragment : TabbedFragment() { ) : StashFragmentPagerAdapter( listOf( - PagerEntry("Details", null), + PagerEntry("Details"), PagerEntry(DataType.IMAGE), PagerEntry(DataType.SCENE), PagerEntry(DataType.PERFORMER), @@ -148,7 +149,7 @@ class GalleryFragment : TabbedFragment() { super.onViewCreated(view, savedInstanceState) if (savedInstanceState != null) { - val gallery = savedInstanceState.getParcelable("gallery") + val gallery = savedInstanceState.getParcelable("gallery", Gallery::class) if (gallery != null) { this.gallery = gallery } else { diff --git a/app/src/main/java/com/github/damontecres/stashapp/ImageActivity.kt b/app/src/main/java/com/github/damontecres/stashapp/ImageActivity.kt index e19cba32..b28a6121 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/ImageActivity.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/ImageActivity.kt @@ -13,7 +13,6 @@ import androidx.fragment.app.commitNow import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceManager import com.apollographql.apollo.api.Optional -import com.chrynan.parcelable.core.getParcelableExtra import com.github.damontecres.stashapp.api.CountImagesQuery import com.github.damontecres.stashapp.api.FindImagesQuery import com.github.damontecres.stashapp.api.fragment.ImageData @@ -26,15 +25,14 @@ import com.github.damontecres.stashapp.image.ImageDetailsFragment import com.github.damontecres.stashapp.image.ImageFragment import com.github.damontecres.stashapp.image.ImageViewModel import com.github.damontecres.stashapp.suppliers.DataSupplierFactory -import com.github.damontecres.stashapp.suppliers.FilterArgs import com.github.damontecres.stashapp.suppliers.ImageDataSupplier import com.github.damontecres.stashapp.suppliers.StashPagingSource import com.github.damontecres.stashapp.suppliers.StashSparseFilterFetcher import com.github.damontecres.stashapp.util.QueryEngine import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler import com.github.damontecres.stashapp.util.StashServer +import com.github.damontecres.stashapp.util.getFilterArgs import com.github.damontecres.stashapp.util.isImageClip -import com.github.damontecres.stashapp.util.parcelable import kotlinx.coroutines.launch class ImageActivity : FragmentActivity(R.layout.activity_image) { @@ -106,8 +104,7 @@ class ImageActivity : FragmentActivity(R.layout.activity_image) { } private fun createDataSupplier(): ImageDataSupplier? { - val filterArgs: FilterArgs? = - intent.getParcelableExtra(INTENT_FILTER_ARGS, FilterArgs::class, 0, parcelable) + val filterArgs = intent.getFilterArgs(INTENT_FILTER_ARGS) val galleryId = intent.getStringExtra(INTENT_GALLERY_ID) if (filterArgs != null) { return DataSupplierFactory( diff --git a/app/src/main/java/com/github/damontecres/stashapp/LicenseActivity.kt b/app/src/main/java/com/github/damontecres/stashapp/LicenseActivity.kt index 5fef7c4b..0a494ca2 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/LicenseActivity.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/LicenseActivity.kt @@ -6,6 +6,9 @@ import android.widget.TextView import com.github.damontecres.stashapp.util.concatIfNotBlank import java.io.BufferedReader +/** + * Show various licenses for third party libraries included in the app + */ class LicenseActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/github/damontecres/stashapp/MainActivity.kt b/app/src/main/java/com/github/damontecres/stashapp/MainActivity.kt index 4c79c540..816fde1d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/MainActivity.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/MainActivity.kt @@ -36,6 +36,7 @@ class MainActivity : FragmentActivity() { } } + @Deprecated("Deprecated in Java") override fun onBackPressed() { if (!fragment.onBackPressed()) { super.onBackPressed() diff --git a/app/src/main/java/com/github/damontecres/stashapp/MainFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/MainFragment.kt index b8cbb998..991e4c3d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/MainFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/MainFragment.kt @@ -260,9 +260,17 @@ class MainFragment : BrowseSupportFragment() { StashServer.requireCurrentServer().serverPreferences .updatePreferences(config) - val ui = config.configuration.ui + val ui = config.configuration.ui as Map<*, *> val frontPageContent = - (ui as Map).getCaseInsensitive("frontPageContent") as List> + ui.getCaseInsensitive("frontPageContent") as List>? + if (frontPageContent == null) { + Toast.makeText( + requireContext(), + "Unable to find front page content! Check the Web UI.", + Toast.LENGTH_LONG, + ).show() + return@launch + } val pageSize = PreferenceManager.getDefaultSharedPreferences(requireContext()) .getInt(getString(R.string.pref_key_page_size), 25) @@ -271,21 +279,20 @@ class MainFragment : BrowseSupportFragment() { val jobs = frontPageParser.parse(frontPageContent) jobs.forEachIndexed { index, job -> job.await().let { row -> - if (row.successful) { - val rowData = row.data!! - filterList.add(rowData.filter) + if (row is FrontPageParser.FrontPageRow.Success) { + filterList.add(row.filter) val adapter = ArrayObjectAdapter(StashPresenter.SELECTOR) - adapter.addAll(0, rowData.data) - adapter.add(rowData.filter) + adapter.addAll(0, row.data) + adapter.add(row.filter) adapters.add(adapter) withContext(Dispatchers.Main) { rowsAdapter.set( index, - ListRow(HeaderItem(rowData.name), adapter), + ListRow(HeaderItem(row.name), adapter), ) } - } else if (row.result == FrontPageParser.FrontPageRowResult.ERROR) { + } else if (row is FrontPageParser.FrontPageRow.Error) { withContext(Dispatchers.Main) { Toast.makeText( requireContext(), diff --git a/app/src/main/java/com/github/damontecres/stashapp/MarkerActivity.kt b/app/src/main/java/com/github/damontecres/stashapp/MarkerActivity.kt index 89c7b66d..f18d3dd9 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/MarkerActivity.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/MarkerActivity.kt @@ -13,7 +13,6 @@ import androidx.activity.result.ActivityResult import androidx.activity.result.ActivityResultCallback import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts -import androidx.activity.viewModels import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentActivity import androidx.fragment.app.activityViewModels @@ -56,6 +55,7 @@ import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler import com.github.damontecres.stashapp.util.StashGlide import com.github.damontecres.stashapp.util.StashServer import com.github.damontecres.stashapp.util.convertDpToPixel +import com.github.damontecres.stashapp.util.getParcelable import com.github.damontecres.stashapp.util.isNotNullOrBlank import com.github.damontecres.stashapp.views.MarkerPickerFragment import com.github.damontecres.stashapp.views.StashItemViewClickListener @@ -65,9 +65,10 @@ import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.Job import kotlinx.coroutines.launch +/** + * Shows details and editable actions for a scene marker + */ class MarkerActivity : FragmentActivity() { - private val viewModel by viewModels() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_details) @@ -172,7 +173,7 @@ class MarkerActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val marker = requireActivity().intent.getParcelableExtra("marker")!! + val marker = requireActivity().intent.getParcelable("marker", Marker::class)!! viewModel.setMarker(marker) primaryTagRowManager.name = getString(R.string.stashapp_primary_tag) diff --git a/app/src/main/java/com/github/damontecres/stashapp/MovieDetailsFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/MovieDetailsFragment.kt index 98dbfe21..41cf44df 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/MovieDetailsFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/MovieDetailsFragment.kt @@ -16,6 +16,7 @@ import com.github.damontecres.stashapp.util.QueryEngine import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler import com.github.damontecres.stashapp.util.StashGlide import com.github.damontecres.stashapp.util.StashServer +import com.github.damontecres.stashapp.util.getParcelable import com.github.damontecres.stashapp.views.parseTimeToString import kotlinx.coroutines.launch import kotlin.time.DurationUnit @@ -39,7 +40,7 @@ class MovieDetailsFragment : Fragment(R.layout.movie_view) { table = view.findViewById(R.id.movie_table) - val movie = requireActivity().intent.getParcelableExtra("movie")!! + val movie = requireActivity().intent.getParcelable("movie", Movie::class)!! if (movie.frontImagePath != null) { StashGlide.with(requireActivity(), movie.frontImagePath) .error(StashPresenter.glideError(requireContext())) diff --git a/app/src/main/java/com/github/damontecres/stashapp/MovieFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/MovieFragment.kt index 9c30a405..f4b97be4 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/MovieFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/MovieFragment.kt @@ -13,19 +13,20 @@ import com.github.damontecres.stashapp.data.Movie import com.github.damontecres.stashapp.data.SortAndDirection import com.github.damontecres.stashapp.data.StashFindFilter import com.github.damontecres.stashapp.util.StashFragmentPagerAdapter +import com.github.damontecres.stashapp.util.getParcelable class MovieFragment : TabbedFragment() { private lateinit var movie: Movie override fun onCreate(savedInstanceState: Bundle?) { - movie = requireActivity().intent.getParcelableExtra("movie")!! + movie = requireActivity().intent.getParcelable("movie", Movie::class)!! super.onCreate(savedInstanceState) } override fun getPagerAdapter(fm: FragmentManager): StashFragmentPagerAdapter { val pages = listOf( - StashFragmentPagerAdapter.PagerEntry(getString(R.string.stashapp_details), null), + StashFragmentPagerAdapter.PagerEntry(getString(R.string.stashapp_details)), StashFragmentPagerAdapter.PagerEntry(DataType.SCENE), ) return object : StashFragmentPagerAdapter(pages, fm) { diff --git a/app/src/main/java/com/github/damontecres/stashapp/PerformerDetailsFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/PerformerDetailsFragment.kt index 4d48b12a..b3ec959b 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/PerformerDetailsFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/PerformerDetailsFragment.kt @@ -23,6 +23,7 @@ import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler import com.github.damontecres.stashapp.util.StashGlide import com.github.damontecres.stashapp.util.StashServer import com.github.damontecres.stashapp.util.ageInYears +import com.github.damontecres.stashapp.util.getParcelable import com.github.damontecres.stashapp.util.onlyScrollIfNeeded import com.github.damontecres.stashapp.views.StashOnFocusChangeListener import com.github.damontecres.stashapp.views.parseTimeToString @@ -30,6 +31,9 @@ import kotlinx.coroutines.launch import kotlin.math.floor import kotlin.math.roundToInt +/** + * Details for a performer + */ class PerformerDetailsFragment() : Fragment(R.layout.performer_view) { constructor(performer: Performer) : this() { this.performer = performer @@ -51,7 +55,7 @@ class PerformerDetailsFragment() : Fragment(R.layout.performer_view) { super.onViewCreated(view, savedInstanceState) if (savedInstanceState != null) { - performer = savedInstanceState.getParcelable("performer")!! + performer = savedInstanceState.getParcelable("performer", Performer::class)!! } mPerformerImage = view.findViewById(R.id.performer_image) @@ -59,7 +63,7 @@ class PerformerDetailsFragment() : Fragment(R.layout.performer_view) { favoriteButton = view.findViewById(R.id.favorite_button) favoriteButton.onFocusChangeListener = StashOnFocusChangeListener(requireContext()) - val performer = requireActivity().intent.getParcelableExtra("performer") + val performer = requireActivity().intent.getParcelable("performer", Performer::class) if (performer != null) { val server = StashServer.requireCurrentServer() queryEngine = QueryEngine(server) diff --git a/app/src/main/java/com/github/damontecres/stashapp/PerformerFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/PerformerFragment.kt index 30aa6ff9..741eae39 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/PerformerFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/PerformerFragment.kt @@ -29,14 +29,18 @@ import com.github.damontecres.stashapp.presenters.TagPresenter import com.github.damontecres.stashapp.suppliers.DataSupplierOverride import com.github.damontecres.stashapp.suppliers.FilterArgs import com.github.damontecres.stashapp.util.StashFragmentPagerAdapter +import com.github.damontecres.stashapp.util.getParcelable import com.github.damontecres.stashapp.util.putFilterArgs import com.github.damontecres.stashapp.views.StashItemViewClickListener +/** + * Main [TabbedFragment] for a performers which includes [PerformerDetailsFragment] and other tabs + */ class PerformerFragment : TabbedFragment() { private lateinit var performer: Performer override fun onCreate(savedInstanceState: Bundle?) { - performer = requireActivity().intent.getParcelableExtra("performer")!! + performer = requireActivity().intent.getParcelable("performer", Performer::class)!! super.onCreate(savedInstanceState) viewModel.title.value = SpannableString("${performer.name} ${performer.disambiguation}").apply { @@ -57,13 +61,13 @@ class PerformerFragment : TabbedFragment() { ) : StashFragmentPagerAdapter( listOf( - PagerEntry("Details", null), + PagerEntry("Details"), PagerEntry(DataType.SCENE), PagerEntry(DataType.GALLERY), PagerEntry(DataType.IMAGE), PagerEntry(DataType.MOVIE), PagerEntry(DataType.TAG), - PagerEntry("Appears With", DataType.PERFORMER), + PagerEntry("Appears With"), ), fm, ) { diff --git a/app/src/main/java/com/github/damontecres/stashapp/PinActivity.kt b/app/src/main/java/com/github/damontecres/stashapp/PinActivity.kt index 9c0c4dd9..a13d74d7 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/PinActivity.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/PinActivity.kt @@ -16,6 +16,11 @@ import com.github.damontecres.stashapp.setup.SetupActivity import com.github.damontecres.stashapp.util.StashServer import com.github.damontecres.stashapp.util.UpdateChecker +/** + * Require a PIN code to view the app + * + * If a PIN is not set, just delegates directly to [MainActivity] + */ class PinActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/github/damontecres/stashapp/SceneDetailsFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/SceneDetailsFragment.kt index 044abc70..f42c19e4 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/SceneDetailsFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/SceneDetailsFragment.kt @@ -58,13 +58,11 @@ import com.github.damontecres.stashapp.presenters.ScenePresenter import com.github.damontecres.stashapp.presenters.StashPresenter import com.github.damontecres.stashapp.presenters.StudioPresenter import com.github.damontecres.stashapp.presenters.TagPresenter -import com.github.damontecres.stashapp.util.GalleryDiffCallback import com.github.damontecres.stashapp.util.ListRowManager -import com.github.damontecres.stashapp.util.MarkerDiffCallback -import com.github.damontecres.stashapp.util.MovieDiffCallback import com.github.damontecres.stashapp.util.MutationEngine import com.github.damontecres.stashapp.util.QueryEngine import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler +import com.github.damontecres.stashapp.util.StashDiffCallback import com.github.damontecres.stashapp.util.StashGlide import com.github.damontecres.stashapp.util.StashServer import com.github.damontecres.stashapp.util.asVideoSceneData @@ -307,7 +305,7 @@ class SceneDetailsFragment : DetailsSupportFragment() { } markersAdapter.setItems( sceneData!!.scene_markers.map(::convertMarker), - MarkerDiffCallback, + StashDiffCallback, ) } else { mAdapter.clear(MARKER_POS) @@ -333,7 +331,7 @@ class SceneDetailsFragment : DetailsSupportFragment() { ) } val movies = sceneData!!.movies.map { it.movie.movieData } - moviesAdapter.setItems(movies, MovieDiffCallback) + moviesAdapter.setItems(movies, StashDiffCallback) } else { mAdapter.clear(MOVIE_POS) } @@ -351,7 +349,7 @@ class SceneDetailsFragment : DetailsSupportFragment() { } val galleries = queryEngine.getGalleries(sceneData!!.galleries.map { it.id }) - galleriesAdapter.setItems(galleries, GalleryDiffCallback) + galleriesAdapter.setItems(galleries, StashDiffCallback) } } else { mAdapter.clear(GALLERY_POS) diff --git a/app/src/main/java/com/github/damontecres/stashapp/SearchForFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/SearchForFragment.kt index a10e6120..782ead9d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/SearchForFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/SearchForFragment.kt @@ -49,6 +49,11 @@ import kotlinx.coroutines.withContext import kotlin.coroutines.CoroutineContext import kotlin.properties.Delegates +/** + * Search for an item of a specific [DataType] + * + * Will load recently used items via [com.github.damontecres.stashapp.data.room.RecentSearchItemsDao] + */ class SearchForFragment : SearchSupportFragment(), SearchSupportFragment.SearchResultProvider { private var taskJob: Job? = null private var query: String? = null diff --git a/app/src/main/java/com/github/damontecres/stashapp/StashApplication.kt b/app/src/main/java/com/github/damontecres/stashapp/StashApplication.kt index c8d00397..50903375 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/StashApplication.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/StashApplication.kt @@ -86,16 +86,23 @@ class StashApplication : Application() { val prefs = PreferenceManager.getDefaultSharedPreferences(this) val currentVersion = prefs.getString(VERSION_NAME_CURRENT_KEY, null) val currentVersionCode = prefs.getLong(VERSION_CODE_CURRENT_KEY, -1) - if (pkgInfo.versionName != currentVersion || pkgInfo.versionCode.toLong() != currentVersionCode) { + + val newVersionCode = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + pkgInfo.longVersionCode + } else { + pkgInfo.versionCode.toLong() + } + if (pkgInfo.versionName != currentVersion || newVersionCode != currentVersionCode) { Log.i( TAG, - "App installed: $currentVersion=>${pkgInfo.versionName} ($currentVersionCode=>${pkgInfo.versionCode})", + "App installed: $currentVersion=>${pkgInfo.versionName} ($currentVersionCode=>$newVersionCode", ) prefs.edit(true) { putString(VERSION_NAME_PREVIOUS_KEY, currentVersion) putLong(VERSION_CODE_PREVIOUS_KEY, currentVersionCode) putString(VERSION_NAME_CURRENT_KEY, pkgInfo.versionName) - putLong(VERSION_CODE_CURRENT_KEY, pkgInfo.versionCode.toLong()) + putLong(VERSION_CODE_CURRENT_KEY, newVersionCode) } if (currentVersion != null) { CoroutineScope(Dispatchers.IO + StashCoroutineExceptionHandler()).launch { diff --git a/app/src/main/java/com/github/damontecres/stashapp/StashExoPlayer.kt b/app/src/main/java/com/github/damontecres/stashapp/StashExoPlayer.kt index eb27b4ef..16881d7c 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/StashExoPlayer.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/StashExoPlayer.kt @@ -12,6 +12,9 @@ import com.github.damontecres.stashapp.util.StashClient import com.github.damontecres.stashapp.util.StashServer import okhttp3.CacheControl +/** + * Manages a static [ExoPlayer] which might be reused between views + */ class StashExoPlayer private constructor() { companion object { private val listeners: MutableList = mutableListOf() diff --git a/app/src/main/java/com/github/damontecres/stashapp/StashGridFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/StashGridFragment.kt index b34f7c60..a61ddafc 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/StashGridFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/StashGridFragment.kt @@ -27,7 +27,6 @@ import androidx.paging.PagingConfig import androidx.paging.cachedIn import androidx.preference.PreferenceManager import com.apollographql.apollo.api.Query -import com.chrynan.parcelable.core.getParcelable import com.chrynan.parcelable.core.putParcelable import com.github.damontecres.stashapp.api.fragment.StashData import com.github.damontecres.stashapp.api.type.StashDataFilter @@ -43,11 +42,12 @@ import com.github.damontecres.stashapp.suppliers.StashPagingSource import com.github.damontecres.stashapp.util.QueryEngine import com.github.damontecres.stashapp.util.StashComparator import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler +import com.github.damontecres.stashapp.util.StashParcelable import com.github.damontecres.stashapp.util.StashServer import com.github.damontecres.stashapp.util.animateToInvisible import com.github.damontecres.stashapp.util.animateToVisible +import com.github.damontecres.stashapp.util.getFilterArgs import com.github.damontecres.stashapp.util.getInt -import com.github.damontecres.stashapp.util.parcelable import com.github.damontecres.stashapp.views.ImageGridClickedListener import com.github.damontecres.stashapp.views.PlayAllOnClickListener import com.github.damontecres.stashapp.views.SortButtonManager @@ -55,6 +55,7 @@ import com.github.damontecres.stashapp.views.StashItemViewClickListener import com.github.damontecres.stashapp.views.TitleTransitionHelper import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch +import kotlinx.serialization.ExperimentalSerializationApi /** * A [Fragment] that shows a grid of items of the same [DataType]. @@ -241,7 +242,7 @@ class StashGridFragment() : Fragment() { name = savedInstanceState.getString("name") sortButtonEnabled = savedInstanceState.getBoolean("sortButtonEnabled") _filterArgs = - savedInstanceState.getParcelable("_filterArgs", FilterArgs::class, 0, parcelable)!! + savedInstanceState.getFilterArgs("_filterArgs")!! Log.v(TAG, "sortAndDirection=${_filterArgs.sortAndDirection}") } @@ -367,11 +368,16 @@ class StashGridFragment() : Fragment() { } } + @OptIn(ExperimentalSerializationApi::class) override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putInt("mSelectedPosition", mSelectedPosition) columns?.let { outState.putInt("columns", it) } - outState.putParcelable("_filterArgs", _filterArgs.with(currentSortAndDirection), parcelable) + outState.putParcelable( + "_filterArgs", + _filterArgs.with(currentSortAndDirection), + StashParcelable, + ) outState.putString("name", name) outState.putBoolean("sortButtonEnabled", sortButtonEnabled) } diff --git a/app/src/main/java/com/github/damontecres/stashapp/StashSearchFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/StashSearchFragment.kt index fbc72099..f5020b71 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/StashSearchFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/StashSearchFragment.kt @@ -26,6 +26,9 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +/** + * Search for items regardless of data type + */ class StashSearchFragment : SearchSupportFragment(), SearchSupportFragment.SearchResultProvider { private var taskJob: Job? = null diff --git a/app/src/main/java/com/github/damontecres/stashapp/StudioFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/StudioFragment.kt index 8bba46f0..bea75d4b 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/StudioFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/StudioFragment.kt @@ -28,10 +28,7 @@ class StudioFragment : TabbedFragment() { StashFragmentPagerAdapter.PagerEntry(DataType.IMAGE), StashFragmentPagerAdapter.PagerEntry(DataType.PERFORMER), StashFragmentPagerAdapter.PagerEntry(DataType.MOVIE), - StashFragmentPagerAdapter.PagerEntry( - getString(R.string.stashapp_subsidiary_studios), - DataType.STUDIO, - ), + StashFragmentPagerAdapter.PagerEntry(getString(R.string.stashapp_subsidiary_studios)), ) return StudioPagerAdapter(tabTitles, studioId.toString(), fm) } diff --git a/app/src/main/java/com/github/damontecres/stashapp/TagFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/TagFragment.kt index 6d652278..fb2b6cb6 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/TagFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/TagFragment.kt @@ -29,7 +29,7 @@ class TagFragment : TabbedFragment() { PagerEntry(DataType.IMAGE), PagerEntry(DataType.MARKER), PagerEntry(DataType.PERFORMER), - PagerEntry(getString(R.string.stashapp_sub_tags), DataType.TAG), + PagerEntry(getString(R.string.stashapp_sub_tags)), ) return TabPageAdapter(tabs, tagId, includeSubTags, fm) diff --git a/app/src/main/java/com/github/damontecres/stashapp/actions/StashAction.kt b/app/src/main/java/com/github/damontecres/stashapp/actions/StashAction.kt index 8fc237a6..cb640f0c 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/actions/StashAction.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/actions/StashAction.kt @@ -1,5 +1,10 @@ package com.github.damontecres.stashapp.actions +/** + * Represents an action a user can take + * + * This is sort of a catch-all, but [com.github.damontecres.stashapp.presenters.ActionPresenter] can render them + */ enum class StashAction(val id: Long, val actionName: String) { ADD_TAG(1L, "Add Tag"), ADD_PERFORMER(2L, "Add Performer"), @@ -13,6 +18,7 @@ enum class StashAction(val id: Long, val actionName: String) { ; companion object { + // Actions that require searching for something val SEARCH_FOR_ACTIONS = setOf(ADD_TAG, ADD_PERFORMER, CREATE_MARKER, SET_STUDIO, ADD_GALLERY) } diff --git a/app/src/main/java/com/github/damontecres/stashapp/actions/StashActionClickedListener.kt b/app/src/main/java/com/github/damontecres/stashapp/actions/StashActionClickedListener.kt index ffa81cc0..03d12677 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/actions/StashActionClickedListener.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/actions/StashActionClickedListener.kt @@ -2,6 +2,9 @@ package com.github.damontecres.stashapp.actions import com.github.damontecres.stashapp.data.OCounter +/** + * Respond to clicks on a card for a [StashAction] + */ interface StashActionClickedListener { fun onClicked(action: StashAction) diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/DataType.kt b/app/src/main/java/com/github/damontecres/stashapp/data/DataType.kt index c4fbafb0..238c46bf 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/DataType.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/DataType.kt @@ -23,12 +23,33 @@ import com.github.damontecres.stashapp.presenters.StudioPresenter import com.github.damontecres.stashapp.presenters.TagPresenter import kotlin.reflect.KClass +/** + * One of the eight core types of data that the server uses + */ enum class DataType( + /** + * The equivalent [FilterMode] for this data type + */ val filterMode: FilterMode, + /** + * The string resource for the singular form of the data type + */ @StringRes val stringId: Int, + /** + * The string resource for the plural form of the data type + */ @StringRes val pluralStringId: Int, + /** + * The string resource for the icon form of the data type + */ @StringRes val iconStringId: Int, + /** + * The default sort for items of this data type + */ val defaultSort: SortAndDirection, + /** + * The available options for sorting this data type + */ val sortOptions: List, ) { SCENE( diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/Scene.kt b/app/src/main/java/com/github/damontecres/stashapp/data/Scene.kt index c4313f49..2c155a3f 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/Scene.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/Scene.kt @@ -13,7 +13,6 @@ data class Scene( val id: String, val title: String?, val date: String?, - val details: String?, val streamUrl: String?, val screenshotUrl: String?, val streams: Map, @@ -44,7 +43,6 @@ data class Scene( id = data.id, title = data.titleOrFilename, date = data.date, - details = data.details, streamUrl = data.paths.stream, screenshotUrl = data.paths.screenshot, streams = streams, @@ -72,7 +70,6 @@ data class Scene( id = data.id, title = data.titleOrFilename, date = data.date, - details = data.details, streamUrl = data.paths.stream, screenshotUrl = data.paths.screenshot, streams = streams, @@ -100,7 +97,6 @@ data class Scene( id = data.id, title = data.titleOrFilename, date = data.date, - details = null, streamUrl = data.paths.stream, screenshotUrl = data.paths.screenshot, streams = streams, diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/SortAndDirection.kt b/app/src/main/java/com/github/damontecres/stashapp/data/SortAndDirection.kt index 4418339e..efbf4ade 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/SortAndDirection.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/SortAndDirection.kt @@ -6,16 +6,25 @@ import com.github.damontecres.stashapp.api.type.SortDirectionEnum import com.github.damontecres.stashapp.util.getRandomSort import kotlinx.serialization.Serializable +/** + * Represents a way to sort something + */ @Serializable data class SortAndDirection(val sort: String, val direction: SortDirectionEnum) { val asFindFilterType get() = FindFilterType(sort = Optional.present(sort), direction = Optional.present(direction)) + /** + * Is this sorting by random? + */ val isRandom get() = sort.startsWith("random") companion object { val NAME_ASC = SortAndDirection("name", SortDirectionEnum.ASC) val PATH_ASC = SortAndDirection("path", SortDirectionEnum.ASC) + /** + * Create a random sort + */ fun random() = SortAndDirection(getRandomSort(), SortDirectionEnum.ASC) fun create( diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/SortOptions.kt b/app/src/main/java/com/github/damontecres/stashapp/data/SortOptions.kt index f4ced8dc..11058172 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/SortOptions.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/SortOptions.kt @@ -1,11 +1,26 @@ package com.github.damontecres.stashapp.data +import androidx.annotation.StringRes import com.github.damontecres.stashapp.R import com.github.damontecres.stashapp.util.Version +/** + * A way to sort something + */ data class SortOption( + /** + * The key as understood by the server + */ val key: String, - val nameStringId: Int, + /** + * The string resource for the readable name of the sort + */ + @StringRes val nameStringId: Int, + /** + * The minimum server version required to sort by this + * + * This allows for adding future compatible sorting to older app versions + */ val requiresVersion: Version = Version.MINIMUM_STASH_VERSION, ) diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/StashFindFilter.kt b/app/src/main/java/com/github/damontecres/stashapp/data/StashFindFilter.kt index a04b1d6c..f53392cc 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/StashFindFilter.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/StashFindFilter.kt @@ -5,6 +5,9 @@ import com.github.damontecres.stashapp.api.type.FindFilterType import com.github.damontecres.stashapp.api.type.SortDirectionEnum import kotlinx.serialization.Serializable +/** + * Represents a "find" filter which is basically a query string and a sort by + */ @Serializable data class StashFindFilter( val q: String? = null, @@ -24,12 +27,18 @@ data class StashFindFilter( per_page = Optional.presentIfNotNull(perPage), ) + /** + * This, but with a different sort key + */ fun withSort(sort: String): StashFindFilter { val newSortAndDirection = sortAndDirection?.copy(sort = sort) ?: SortAndDirection(sort, SortDirectionEnum.ASC) return this.copy(sortAndDirection = newSortAndDirection) } + /** + * This, but with a different sort direction + */ fun withDirection( direction: SortDirectionEnum, dataType: DataType, diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/room/PlaybackEffect.kt b/app/src/main/java/com/github/damontecres/stashapp/data/room/PlaybackEffect.kt index d84cf368..0955c059 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/room/PlaybackEffect.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/room/PlaybackEffect.kt @@ -12,6 +12,9 @@ import androidx.media3.effect.ScaleAndRotateTransformation import androidx.room.Embedded import androidx.room.Entity +/** + * Store a [VideoFilter] for a specific scene (by server & scene ID) + */ @Entity(tableName = "playback_effects", primaryKeys = ["serverUrl", "id"]) data class PlaybackEffect( val serverUrl: String, @@ -19,6 +22,9 @@ data class PlaybackEffect( @Embedded val videoFilter: VideoFilter, ) +/** + * Modifications to a video playback + */ data class VideoFilter( val rotation: Int = 0, val brightness: Int = COLOR_DEFAULT, @@ -59,6 +65,9 @@ data class VideoFilter( return blur > 0 } + /** + * Create the list of effects to apply + */ @OptIn(UnstableApi::class) fun createEffectList(): List { return buildList { diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/room/PlaybackEffectsDao.kt b/app/src/main/java/com/github/damontecres/stashapp/data/room/PlaybackEffectsDao.kt index 64445f96..1fbe0682 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/room/PlaybackEffectsDao.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/room/PlaybackEffectsDao.kt @@ -5,6 +5,9 @@ import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +/** + * Store [PlaybackEffect]/][VideoFilter]s + */ @Dao interface PlaybackEffectsDao { @Query("SELECT * FROM playback_effects WHERE serverUrl = :serverUrl AND id = :sceneId") diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/room/RecentSearchItem.kt b/app/src/main/java/com/github/damontecres/stashapp/data/room/RecentSearchItem.kt index 2f7ba4be..82576f3a 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/room/RecentSearchItem.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/room/RecentSearchItem.kt @@ -4,5 +4,10 @@ import androidx.room.Entity import com.github.damontecres.stashapp.data.DataType import java.util.Date +/** + * Represents an item that the user recently used in a search-for task + * + * Must be identified by server, data type, & ID + */ @Entity(tableName = "recent_search_items", primaryKeys = ["serverUrl", "id", "dataType"]) data class RecentSearchItem(val serverUrl: String, val id: String, val dataType: DataType, val timestamp: Date = Date()) diff --git a/app/src/main/java/com/github/damontecres/stashapp/data/room/RecentSearchItemsDao.kt b/app/src/main/java/com/github/damontecres/stashapp/data/room/RecentSearchItemsDao.kt index 0aa26fa6..1ca8bdb0 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/data/room/RecentSearchItemsDao.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/data/room/RecentSearchItemsDao.kt @@ -6,6 +6,9 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import com.github.damontecres.stashapp.data.DataType +/** + * Retrieve and store [RecentSearchItem]s + */ @Dao interface RecentSearchItemsDao { @Query("SELECT * FROM recent_search_items WHERE serverUrl = :serverUrl AND dataType = :dataType ORDER BY timestamp DESC LIMIT :count") diff --git a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackActivity.kt b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackActivity.kt index 58ca3e2b..dfd0e62c 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackActivity.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackActivity.kt @@ -20,6 +20,7 @@ import com.github.damontecres.stashapp.data.Scene import com.github.damontecres.stashapp.util.QueryEngine import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler import com.github.damontecres.stashapp.util.StashServer +import com.github.damontecres.stashapp.util.getParcelable import com.github.damontecres.stashapp.util.toMilliseconds import kotlinx.coroutines.launch @@ -39,7 +40,7 @@ class PlaybackActivity : FragmentActivity() { setResultAndFinish() } - val scene = intent.getParcelableExtra(SceneDetailsActivity.MOVIE) as Scene? + val scene = intent.getParcelable(SceneDetailsActivity.MOVIE, Scene::class) if (scene != null) { this.scene = scene if (savedInstanceState == null) { diff --git a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFragment.kt index 5d598542..d964916f 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFragment.kt @@ -109,7 +109,7 @@ abstract class PlaybackFragment( // Track whether the video is playing before calling the resultLauncher protected var wasPlayingBeforeResultLauncher: Boolean? = null private lateinit var resultLauncher: ActivityResultLauncher - private val videoFilterFragment = PlaybackFilterFragment() + private val videoFilterFragment = PlaybackVideoFiltersFragment() protected var playbackPosition = -1L val currentVideoPosition get() = player?.currentPosition ?: playbackPosition diff --git a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFilterFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackVideoFiltersFragment.kt similarity index 98% rename from app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFilterFragment.kt rename to app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackVideoFiltersFragment.kt index 2abeb78e..1d68b5af 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackFilterFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaybackVideoFiltersFragment.kt @@ -12,7 +12,10 @@ import androidx.preference.PreferenceManager import com.github.damontecres.stashapp.R import com.github.damontecres.stashapp.data.room.VideoFilter -class PlaybackFilterFragment : Fragment(R.layout.apply_video_filters) { +/** + * Display the [VideoFilter] options to manipulate + */ +class PlaybackVideoFiltersFragment : Fragment(R.layout.apply_video_filters) { private val viewModel: VideoFilterViewModel by activityViewModels() override fun onViewCreated( diff --git a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaylistActivity.kt b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaylistActivity.kt index 5242e677..8dc7d925 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/playback/PlaylistActivity.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/playback/PlaylistActivity.kt @@ -9,10 +9,8 @@ import androidx.activity.viewModels import androidx.annotation.OptIn import androidx.fragment.app.FragmentActivity import androidx.media3.common.util.UnstableApi -import com.chrynan.parcelable.core.getParcelableExtra import com.github.damontecres.stashapp.data.DataType -import com.github.damontecres.stashapp.suppliers.FilterArgs -import com.github.damontecres.stashapp.util.parcelable +import com.github.damontecres.stashapp.util.getFilterArgs class PlaylistActivity : FragmentActivity() { private val viewModel: PlaylistViewModel by viewModels() @@ -22,8 +20,7 @@ class PlaylistActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (savedInstanceState == null) { - val filter = - intent.getParcelableExtra(INTENT_FILTER, FilterArgs::class, 0, parcelable)!! + val filter = intent.getFilterArgs(INTENT_FILTER)!! Log.v(TAG, "filter=${filter.sortAndDirection}") viewModel.filterArgs.value = filter fragment = diff --git a/app/src/main/java/com/github/damontecres/stashapp/playback/StashPlayerView.kt b/app/src/main/java/com/github/damontecres/stashapp/playback/StashPlayerView.kt index b76be84a..c72c8035 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/playback/StashPlayerView.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/playback/StashPlayerView.kt @@ -12,6 +12,9 @@ import androidx.media3.common.util.Util import androidx.media3.ui.PlayerView import androidx.preference.PreferenceManager +/** + * A [PlayerView] which overrides button presses + */ class StashPlayerView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : PlayerView(context, attrs, defStyleAttr) { constructor(context: Context, attrs: AttributeSet? = null) : this(context, attrs, 0) diff --git a/app/src/main/java/com/github/damontecres/stashapp/presenters/DetailsDescriptionPresenter.kt b/app/src/main/java/com/github/damontecres/stashapp/presenters/DetailsDescriptionPresenter.kt index 9f6f5539..79a724ba 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/presenters/DetailsDescriptionPresenter.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/presenters/DetailsDescriptionPresenter.kt @@ -14,6 +14,9 @@ import com.github.damontecres.stashapp.views.StashRatingBar import com.github.damontecres.stashapp.views.durationToString import com.github.damontecres.stashapp.views.parseTimeToString +/** + * [AbstractDetailsDescriptionPresenter] for [com.github.damontecres.stashapp.SceneDetailsFragment] + */ class DetailsDescriptionPresenter(val ratingCallback: StashRatingBar.RatingCallback) : AbstractDetailsDescriptionPresenter() { override fun onBindDescription( diff --git a/app/src/main/java/com/github/damontecres/stashapp/presenters/PerformerInScenePresenter.kt b/app/src/main/java/com/github/damontecres/stashapp/presenters/PerformerInScenePresenter.kt index 5081598d..d30c21cc 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/presenters/PerformerInScenePresenter.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/presenters/PerformerInScenePresenter.kt @@ -3,16 +3,15 @@ package com.github.damontecres.stashapp.presenters import android.os.Build import com.github.damontecres.stashapp.R import com.github.damontecres.stashapp.api.fragment.PerformerData -import com.github.damontecres.stashapp.api.fragment.SlimSceneData import java.time.LocalDate import java.time.Period import java.time.format.DateTimeFormatter /** - * A [PerformerPresenter] which will use the age of a [PerformerData] at the time of a [SlimSceneData] for the content text + * A [PerformerPresenter] which will use the age of a [PerformerData] at specified date for the content text */ class PerformerInScenePresenter( - private val sceneDate: String?, + private val date: String?, callback: LongClickCallBack? = null, ) : PerformerPresenter( callback, @@ -23,10 +22,10 @@ class PerformerInScenePresenter( ): CharSequence? { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val ageInScene = - if (item.birthdate != null && sceneDate != null) { + if (item.birthdate != null && date != null) { Period.between( LocalDate.parse(item.birthdate, DateTimeFormatter.ISO_LOCAL_DATE), - LocalDate.parse(sceneDate, DateTimeFormatter.ISO_LOCAL_DATE), + LocalDate.parse(date, DateTimeFormatter.ISO_LOCAL_DATE), ).years } else { null diff --git a/app/src/main/java/com/github/damontecres/stashapp/suppliers/ImageDataSupplier.kt b/app/src/main/java/com/github/damontecres/stashapp/suppliers/ImageDataSupplier.kt index 24910fcc..e22cfd97 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/suppliers/ImageDataSupplier.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/suppliers/ImageDataSupplier.kt @@ -24,6 +24,7 @@ class ImageDataSupplier( return FindImagesQuery( filter = filter, image_filter = imageFilter, + ids = null, ) } diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/Comparators.kt b/app/src/main/java/com/github/damontecres/stashapp/util/Comparators.kt index bc4c2850..204d9aa3 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/Comparators.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/Comparators.kt @@ -2,15 +2,7 @@ package com.github.damontecres.stashapp.util import androidx.leanback.widget.DiffCallback import androidx.recyclerview.widget.DiffUtil -import com.github.damontecres.stashapp.api.fragment.GalleryData -import com.github.damontecres.stashapp.api.fragment.ImageData -import com.github.damontecres.stashapp.api.fragment.MarkerData -import com.github.damontecres.stashapp.api.fragment.MovieData -import com.github.damontecres.stashapp.api.fragment.PerformerData -import com.github.damontecres.stashapp.api.fragment.SlimSceneData import com.github.damontecres.stashapp.api.fragment.StashData -import com.github.damontecres.stashapp.api.fragment.StudioData -import com.github.damontecres.stashapp.api.fragment.TagData import com.github.damontecres.stashapp.data.PlaylistItem object StashComparator : DiffUtil.ItemCallback() { @@ -29,262 +21,6 @@ object StashComparator : DiffUtil.ItemCallback() { } } -object SceneComparator : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: SlimSceneData, - newItem: SlimSceneData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: SlimSceneData, - newItem: SlimSceneData, - ): Boolean { - return oldItem == newItem - } -} - -object PerformerComparator : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: PerformerData, - newItem: PerformerData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: PerformerData, - newItem: PerformerData, - ): Boolean { - return oldItem == newItem - } -} - -object StudioComparator : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: StudioData, - newItem: StudioData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: StudioData, - newItem: StudioData, - ): Boolean { - return oldItem == newItem - } -} - -object TagComparator : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: TagData, - newItem: TagData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: TagData, - newItem: TagData, - ): Boolean { - return oldItem == newItem - } -} - -object MovieComparator : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: MovieData, - newItem: MovieData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: MovieData, - newItem: MovieData, - ): Boolean { - return oldItem == newItem - } -} - -object MarkerComparator : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: MarkerData, - newItem: MarkerData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: MarkerData, - newItem: MarkerData, - ): Boolean { - return oldItem == newItem - } -} - -object ImageComparator : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: ImageData, - newItem: ImageData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: ImageData, - newItem: ImageData, - ): Boolean { - return oldItem == newItem - } -} - -object GalleryComparator : DiffUtil.ItemCallback() { - override fun areItemsTheSame( - oldItem: GalleryData, - newItem: GalleryData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: GalleryData, - newItem: GalleryData, - ): Boolean { - return oldItem == newItem - } -} - -object TagDiffCallback : DiffCallback() { - override fun areItemsTheSame( - oldItem: TagData, - newItem: TagData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: TagData, - newItem: TagData, - ): Boolean { - return oldItem == newItem - } -} - -object MarkerDiffCallback : DiffCallback() { - override fun areItemsTheSame( - oldItem: MarkerData, - newItem: MarkerData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: MarkerData, - newItem: MarkerData, - ): Boolean { - return oldItem == newItem - } -} - -object PerformerDiffCallback : DiffCallback() { - override fun areItemsTheSame( - oldItem: PerformerData, - newItem: PerformerData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: PerformerData, - newItem: PerformerData, - ): Boolean { - return oldItem == newItem - } -} - -object MovieDiffCallback : DiffCallback() { - override fun areItemsTheSame( - oldItem: MovieData, - newItem: MovieData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: MovieData, - newItem: MovieData, - ): Boolean { - return oldItem == newItem - } -} - -object StudioDiffCallback : DiffCallback() { - override fun areItemsTheSame( - oldItem: StudioData, - newItem: StudioData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: StudioData, - newItem: StudioData, - ): Boolean { - return oldItem == newItem - } -} - -object GalleryDiffCallback : DiffCallback() { - override fun areItemsTheSame( - oldItem: GalleryData, - newItem: GalleryData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: GalleryData, - newItem: GalleryData, - ): Boolean { - return oldItem == newItem - } -} - -object SceneDiffCallback : DiffCallback() { - override fun areItemsTheSame( - oldItem: SlimSceneData, - newItem: SlimSceneData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: SlimSceneData, - newItem: SlimSceneData, - ): Boolean { - return oldItem == newItem - } -} - -object ImageDiffCallback : DiffCallback() { - override fun areItemsTheSame( - oldItem: ImageData, - newItem: ImageData, - ): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame( - oldItem: ImageData, - newItem: ImageData, - ): Boolean { - return oldItem == newItem - } -} - object StashDiffCallback : DiffCallback() { override fun areItemsTheSame( oldItem: StashData, diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/Constants.kt b/app/src/main/java/com/github/damontecres/stashapp/util/Constants.kt index 58549178..c30d7ea5 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/Constants.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/Constants.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.content.SharedPreferences import android.os.Build +import android.os.Bundle import android.text.TextUtils import android.util.DisplayMetrics import android.util.Log @@ -21,11 +22,11 @@ import androidx.leanback.widget.ArrayObjectAdapter import androidx.leanback.widget.Visibility import androidx.preference.PreferenceManager import com.apollographql.apollo.ApolloClient -import com.apollographql.apollo.api.Optional import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloHttpException import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.model.LazyHeaders +import com.chrynan.parcelable.core.getParcelable import com.chrynan.parcelable.core.getParcelableExtra import com.chrynan.parcelable.core.putExtra import com.github.damontecres.stashapp.ImageActivity @@ -36,13 +37,11 @@ import com.github.damontecres.stashapp.api.fragment.FullSceneData import com.github.damontecres.stashapp.api.fragment.GalleryData import com.github.damontecres.stashapp.api.fragment.ImageData import com.github.damontecres.stashapp.api.fragment.PerformerData -import com.github.damontecres.stashapp.api.fragment.SavedFilterData import com.github.damontecres.stashapp.api.fragment.SlimSceneData import com.github.damontecres.stashapp.api.fragment.SlimTagData import com.github.damontecres.stashapp.api.fragment.TagData import com.github.damontecres.stashapp.api.fragment.VideoFileData import com.github.damontecres.stashapp.api.fragment.VideoSceneData -import com.github.damontecres.stashapp.api.type.FindFilterType import com.github.damontecres.stashapp.data.DataType import com.github.damontecres.stashapp.suppliers.FilterArgs import com.github.damontecres.stashapp.util.Constants.STASH_API_HEADER @@ -71,13 +70,18 @@ import kotlin.time.Duration import kotlin.time.DurationUnit import kotlin.time.toDuration +/** + * Originally this file was for constant values (such as API Key header name). + * + * Now it's mostly a dumping ground for various extension functions + */ object Constants { /** * The name of the header for authenticating to Stash */ const val STASH_API_HEADER = "ApiKey" const val TAG = "Constants" - const val OK_HTTP_CACHE_DIR = "okhttpcache" + private const val OK_HTTP_CACHE_DIR = "okhttpcache" fun getNetworkCache(context: Context): Cache { val cacheSize = @@ -87,7 +91,7 @@ object Constants { } } -fun join( +fun joinValueNotNull( prefix: String, value: String?, ): String? { @@ -143,6 +147,11 @@ data class TestResult(val status: TestResultStatus, val serverInfo: ServerInfoQu constructor(status: TestResultStatus) : this(status, null) } +/** + * Test the connection to the server using the specified [ApolloClient] + * + * The client should have been configured with the server URL and API key if needed + */ suspend fun testStashConnection( context: Context, showToast: Boolean, @@ -210,16 +219,6 @@ suspend fun testStashConnection( ).show() } return TestResult(TestResultStatus.AUTH_REQUIRED) - } else if (ex.statusCode == 500) { - // In server <0.26.0, the server may return a 500 for incorrect API keys - if (showToast) { - Toast.makeText( - context, - "Connected to server, but the API key may be incorrect.", - Toast.LENGTH_LONG, - ).show() - } - return TestResult(TestResultStatus.AUTH_REQUIRED) } else { if (showToast) { Toast.makeText( @@ -290,40 +289,10 @@ suspend fun testStashConnection( return TestResult(TestResultStatus.ERROR) } -fun SavedFilterData.Find_filter.toFindFilterType(resolveRandom: Boolean = false): FindFilterType { - val newSort = - if (sort != null && resolveRandom && sort.startsWith("random")) { - getRandomSort() - } else { - sort - } - return FindFilterType( - q = Optional.presentIfNotNull(this.q), - page = Optional.presentIfNotNull(this.page), - per_page = Optional.presentIfNotNull(this.per_page), - sort = Optional.presentIfNotNull(newSort), - direction = Optional.presentIfNotNull(this.direction), - ) -} - -@Suppress("ktlint:standard:function-naming") -fun FindFilterType.toFind_filter(): SavedFilterData.Find_filter { - return SavedFilterData.Find_filter( - q = q.getOrNull(), - page = page.getOrNull(), - per_page = per_page.getOrNull(), - sort = sort.getOrNull(), - direction = direction.getOrNull(), - __typename = "FindFilterType", - ) -} - -val supportedFilterModes = DataType.entries.map { it.filterMode }.toSet() - /** * Gets the value for the key trying first the key as provided and next the key lower cased */ -fun Map.getCaseInsensitive(k: String?): V? { +fun Map<*, V>.getCaseInsensitive(k: String?): V? { if (k == null) { return null } @@ -476,9 +445,6 @@ val FullSceneData.asSlimeSceneData: SlimSceneData }, ) -val FullSceneData.File.asVideoSceneDataFile: VideoSceneData.File - get() = VideoSceneData.File("", videoFileData) - val FullSceneData.asVideoSceneData: VideoSceneData get() = VideoSceneData( @@ -671,6 +637,9 @@ val SlimSceneData.resume_position get() = resume_time?.times(1000L)?.toLong() val Long.toMilliseconds get() = this / 1000.0 +/** + * Show a [Toast] on [Dispatchers.Main] + */ suspend fun showToastOnMain( context: Context, message: CharSequence, @@ -726,13 +695,6 @@ val ImageData.isImageClip: Boolean visual_files.firstOrNull()?.onVideoFile != null && visual_files.firstOrNull()?.onVideoFile!!.format != "gif" -val ImageData.isGif: Boolean - get() { - val file = visual_files.firstOrNull() - return (file?.onVideoFile != null && file.onVideoFile.format == "gif") || - file?.onBaseFile?.path?.endsWith(".gif", true) == true - } - /** * Launch in the [Dispatchers.IO] context with an optional [CoroutineExceptionHandler] defaulting to [StashCoroutineExceptionHandler] */ @@ -760,11 +722,17 @@ fun Intent.putFilterArgs( name: String, filterArgs: FilterArgs, ): Intent { - return putExtra(name, filterArgs, parcelable) + return putExtra(name, filterArgs, StashParcelable) } +@OptIn(ExperimentalSerializationApi::class) fun Intent.getFilterArgs(name: String): FilterArgs? { - return getParcelableExtra(name, FilterArgs::class, 0, parcelable) + return getParcelableExtra(name, FilterArgs::class, 0, StashParcelable) +} + +@OptIn(ExperimentalSerializationApi::class) +fun Bundle.getFilterArgs(name: String): FilterArgs? { + return getParcelable(name, FilterArgs::class, 0, StashParcelable) } fun experimentalFeaturesEnabled(): Boolean { diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/FilterParser.kt b/app/src/main/java/com/github/damontecres/stashapp/util/FilterParser.kt index b924ca0c..06bd4ea1 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/FilterParser.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/FilterParser.kt @@ -29,6 +29,9 @@ import com.github.damontecres.stashapp.api.type.StudioFilterType import com.github.damontecres.stashapp.api.type.TagFilterType import com.github.damontecres.stashapp.api.type.TimestampCriterionInput +/** + * Parse a server-side filter from JSON (Map) + */ class FilterParser(private val serverVersion: Version) { companion object { const val TAG = "FilterParser" diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/FrontPageParser.kt b/app/src/main/java/com/github/damontecres/stashapp/util/FrontPageParser.kt index 7a767c59..56c2cbae 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/FrontPageParser.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/FrontPageParser.kt @@ -29,29 +29,16 @@ class FrontPageParser( const val TAG = "FrontPageParser" } - enum class FrontPageRowResult { - SUCCESS, - ERROR, - DATA_TYPE_NOT_SUPPORTED, - } - - data class FrontPageRowData(val name: String, val filter: FilterArgs, val data: List<*>) - - data class FrontPageRow(val result: FrontPageRowResult, val data: FrontPageRowData?) { - constructor( - name: String, - filter: FilterArgs, - data: List<*>, - ) : this(FrontPageRowResult.SUCCESS, FrontPageRowData(name, filter, data)) - - constructor(result: FrontPageRowResult) : this(result, null) + sealed class FrontPageRow { + data class Success( + val name: String, + val filter: FilterArgs, + val data: List<*>, + ) : FrontPageRow() - val successful get() = result == FrontPageRowResult.SUCCESS && data!!.data.isNotEmpty() + data object Error : FrontPageRow() - companion object { - val ERROR = FrontPageRow(FrontPageRowResult.ERROR) - val NOT_SUPPORTED = FrontPageRow(FrontPageRowResult.DATA_TYPE_NOT_SUPPORTED) - } + data object NotSupported : FrontPageRow() } suspend fun parse(frontPageContent: List>): List> { @@ -81,7 +68,7 @@ class FrontPageParser( TAG, "Unknown frontPageFilter typename: $filterType", ) - CompletableDeferred(FrontPageRow.NOT_SUPPORTED) + CompletableDeferred(FrontPageRow.NotSupported) } } } @@ -121,15 +108,14 @@ class FrontPageParser( else -> null } val mode = FilterMode.safeValueOf(frontPageFilter["mode"] as String) - if (mode !in supportedFilterModes) { - Log.w(TAG, "CustomFilter mode is $mode which is not supported yet") - return@withContext CompletableDeferred(FrontPageRow(FrontPageRowResult.DATA_TYPE_NOT_SUPPORTED)) - } + val dataType = + DataType.fromFilterMode(mode) + ?: return@withContext CompletableDeferred(FrontPageRow.NotSupported) val job = async { try { val direction = frontPageFilter["direction"] as String? - val dataType = DataType.fromFilterMode(mode)!! + val customFilter = FilterArgs( dataType = dataType, @@ -149,16 +135,16 @@ class FrontPageParser( customFilter.findFilter!!.toFindFilterType(1, pageSize), useRandom = false, ) - FrontPageRow(description, customFilter, data) + FrontPageRow.Success(description, customFilter, data) } catch (ex: Exception) { Log.e(TAG, "Exception in addCustomFilterRow", ex) - FrontPageRow.ERROR + FrontPageRow.Error } } return@withContext job } catch (ex: Exception) { Log.e(TAG, "Exception during addCustomFilterRow", ex) - CompletableDeferred(FrontPageRow.ERROR) + CompletableDeferred(FrontPageRow.Error) } } @@ -261,14 +247,14 @@ class FrontPageParser( ) } } - FrontPageRow(result.name, filter, data) + FrontPageRow.Success(result.name, filter, data) } else { Log.w(TAG, "SavedFilter does not exist") - FrontPageRow.ERROR + FrontPageRow.Error } } catch (ex: Exception) { Log.e(TAG, "Exception in addSavedFilterRow filterId=$filterId", ex) - FrontPageRow.ERROR + FrontPageRow.Error } } } diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/IntentUtils.kt b/app/src/main/java/com/github/damontecres/stashapp/util/IntentUtils.kt new file mode 100644 index 00000000..4e4bc3a3 --- /dev/null +++ b/app/src/main/java/com/github/damontecres/stashapp/util/IntentUtils.kt @@ -0,0 +1,29 @@ +package com.github.damontecres.stashapp.util + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.os.Parcelable +import kotlin.reflect.KClass + +fun Intent.getParcelable( + name: String, + klass: KClass, +): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getParcelableExtra(name, klass.java) + } else { + getParcelableExtra(name) + } +} + +fun Bundle.getParcelable( + name: String, + klass: KClass, +): T? { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + getParcelable(name, klass.java) + } else { + getParcelable(name) + } +} diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/OptionalSerializer.kt b/app/src/main/java/com/github/damontecres/stashapp/util/OptionalSerializer.kt index eecd600d..7757d151 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/OptionalSerializer.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/OptionalSerializer.kt @@ -9,17 +9,25 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.modules.SerializersModule -val optionalSerializerModule = +val OptionalSerializersModule = SerializersModule { contextual(Optional::class) { args -> OptionalSerializer(args[0]) } } +/** + * Contextual [Parcelable] with [OptionalSerializer] + */ @OptIn(ExperimentalSerializationApi::class) -val parcelable = +val StashParcelable = Parcelable { - serializersModule = optionalSerializerModule + serializersModule = OptionalSerializersModule } +/** + * Serializes [Optional]s + * + * Basically just writes a boolean for whether the [Optional] is present or absent before the value + */ class OptionalSerializer(private val dataSerializer: KSerializer) : KSerializer> { override val descriptor: SerialDescriptor get() = dataSerializer.descriptor diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/QueryEngine.kt b/app/src/main/java/com/github/damontecres/stashapp/util/QueryEngine.kt index a4165e4f..0ac472e9 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/QueryEngine.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/QueryEngine.kt @@ -239,13 +239,15 @@ class QueryEngine( suspend fun findImages( findFilter: FindFilterType? = null, imageFilter: ImageFilterType? = null, + ids: List? = null, useRandom: Boolean = true, ): List { val query = client.query( FindImagesQuery( - updateFilter(findFilter, useRandom), - imageFilter, + filter = updateFilter(findFilter, useRandom), + image_filter = imageFilter, + ids = ids, ), ) return executeQuery(query).data?.findImages?.images?.map { it.imageData }.orEmpty() @@ -324,9 +326,9 @@ class QueryEngine( DataType.TAG -> getTags(ids) DataType.STUDIO -> findStudios(studioIds = ids) DataType.MOVIE -> findMovies(movieIds = ids) - DataType.MARKER -> TODO() - DataType.IMAGE -> TODO() - DataType.GALLERY -> TODO() + DataType.IMAGE -> findImages(ids = ids) + DataType.GALLERY -> findGalleries(galleryIds = ids) + DataType.MARKER -> throw UnsupportedOperationException("Cannot query markers by ID") // TODO: Unsupported by the server } } diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/SingleItemObjectAdapter.kt b/app/src/main/java/com/github/damontecres/stashapp/util/SingleItemObjectAdapter.kt index e757b288..1a8d768d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/SingleItemObjectAdapter.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/SingleItemObjectAdapter.kt @@ -15,7 +15,7 @@ class SingleItemObjectAdapter(presenter: StashPresenter) : ObjectAda } override fun size(): Int { - return 1 + return if (item == null) 0 else 1 } override fun get(position: Int): Any? { diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/StashClient.kt b/app/src/main/java/com/github/damontecres/stashapp/util/StashClient.kt index 83971a7f..8bbbcbd3 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/StashClient.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/StashClient.kt @@ -26,6 +26,9 @@ import javax.net.ssl.SSLContext import javax.net.ssl.X509TrustManager import kotlin.time.DurationUnit +/** + * Provides static functions to get clients to interact with the server + */ class StashClient private constructor() { companion object { private const val TAG = "StashClient" @@ -186,13 +189,18 @@ class StashClient private constructor() { return builder.build() } + /** + * Create the user agent used in HTTP calls + * + * Form of: StashAppAndroidTV/ (release/; sdk/) (; ; ) + */ fun createUserAgent(context: Context): String { val appName = context.getString(R.string.app_name) val versionStr = context.packageManager.getPackageInfo(context.packageName, 0).versionName val comments = listOf( - join("os", Build.VERSION.BASE_OS), - join("release", Build.VERSION.RELEASE), + joinValueNotNull("os", Build.VERSION.BASE_OS), + joinValueNotNull("release", Build.VERSION.RELEASE), "sdk/${Build.VERSION.SDK_INT}", ).joinNotNullOrBlank("; ") val device = @@ -242,6 +250,11 @@ class StashClient private constructor() { .build() } + /** + * Cleans up the URL that a user enters + * + * Tries to add protocol (http) and endpoint (/graphql) + */ fun cleanServerUrl(stashUrl: String): String { var cleanedStashUrl = stashUrl.trim() if (!cleanedStashUrl.startsWith("http://") && !cleanedStashUrl.startsWith("https://")) { @@ -262,6 +275,8 @@ class StashClient private constructor() { /** * Get the server URL excluding the (unlikely) `/graphql` last path segment + * + * Basically, if a user is using a reverse proxy routes the path ending with `/graphql`, this will remove that */ fun getServerRoot(stashUrl: String): String { var cleanedStashUrl = stashUrl.trim() @@ -283,6 +298,8 @@ class StashClient private constructor() { /** * Build an [ApolloClient] suitable for testing connectivity for the specified server. + * + * @see [com.github.damontecres.stashapp.util.testStashConnection] */ fun createTestApolloClient( context: Context, diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/StashCoroutineExceptionHandler.kt b/app/src/main/java/com/github/damontecres/stashapp/util/StashCoroutineExceptionHandler.kt index 47a520f5..ff7af067 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/StashCoroutineExceptionHandler.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/StashCoroutineExceptionHandler.kt @@ -6,6 +6,15 @@ import com.github.damontecres.stashapp.StashApplication import kotlinx.coroutines.CoroutineExceptionHandler import kotlin.coroutines.CoroutineContext +/** + * A general [CoroutineExceptionHandler] which can optionally show [Toast]s when an exception is thrown + * + * Note: a toast will be shown for each parameter given, up to three! + * + * @param autoToast automatically show a toast with the exception's message + * @param toast the toast to show when an exception occurs + * @param makeToast make a toast to show when an exception occurs + */ class StashCoroutineExceptionHandler( private val autoToast: Boolean = false, private val toast: Toast? = null, diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/StashEngine.kt b/app/src/main/java/com/github/damontecres/stashapp/util/StashEngine.kt index fe126d21..c61c4d1e 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/StashEngine.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/StashEngine.kt @@ -6,6 +6,9 @@ import com.apollographql.apollo.exception.ApolloException import com.apollographql.apollo.exception.ApolloHttpException import com.apollographql.apollo.exception.ApolloNetworkException +/** + * Super class for "engines" that interact with the server + */ abstract class StashEngine( protected val server: StashServer, protected val client: ApolloClient, diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/StashFragmentPagerAdapter.kt b/app/src/main/java/com/github/damontecres/stashapp/util/StashFragmentPagerAdapter.kt index 9c01d5bb..ab618d5a 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/StashFragmentPagerAdapter.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/StashFragmentPagerAdapter.kt @@ -7,6 +7,9 @@ import androidx.fragment.app.FragmentStatePagerAdapter import com.github.damontecres.stashapp.StashApplication import com.github.damontecres.stashapp.data.DataType +/** + * A [FragmentStatePagerAdapter] to show various tabs for data types + */ abstract class StashFragmentPagerAdapter( private val items: List, fm: FragmentManager, @@ -30,8 +33,13 @@ abstract class StashFragmentPagerAdapter( abstract fun getFragment(position: Int): Fragment - data class PagerEntry(val title: String, val dataType: DataType?) { - constructor(dataType: DataType) : this(StashApplication.getApplication().getString(dataType.pluralStringId), dataType) + /** + * Represents a tab with an title + */ + data class PagerEntry(val title: String) { + constructor(dataType: DataType) : this( + StashApplication.getApplication().getString(dataType.pluralStringId), + ) } companion object { diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/StashGlide.kt b/app/src/main/java/com/github/damontecres/stashapp/util/StashGlide.kt index a272746a..c9434b56 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/StashGlide.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/StashGlide.kt @@ -10,6 +10,9 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.signature.ObjectKey +/** + * Replacement for [Glide] static functions to incorporate the caching schemes used in the app + */ class StashGlide private constructor() { companion object { // Images larger than 5mb need disk cache diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/StashPreviewLoader.kt b/app/src/main/java/com/github/damontecres/stashapp/util/StashPreviewLoader.kt index 5028b078..d57b0523 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/StashPreviewLoader.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/StashPreviewLoader.kt @@ -12,6 +12,9 @@ import com.github.rubensousa.previewseekbar.PreviewLoader import java.nio.ByteBuffer import java.security.MessageDigest +/** + * Loads and presents scrubber previews + */ class StashPreviewLoader( private val context: Context, private val imageView: ImageView, @@ -38,6 +41,9 @@ class StashPreviewLoader( private const val TAG = "StashPreviewLoader" } + /** + * Extracts the thumbnail scrubber image for a given position of the video (in milliseconds) + */ class GlideThumbnailTransformation(duration: Long, position: Long) : BitmapTransformation() { private val x: Int private val y: Int @@ -80,7 +86,7 @@ class StashPreviewLoader( companion object { private val KEY = - "com.github.damontecres.stashapp.util.StashPreviewLoader.GlideThumbnailTransformation".encodeToByteArray() + GlideThumbnailTransformation::class.qualifiedName!!.encodeToByteArray() const val MAX_LINES = 9 const val MAX_COLUMNS = 9 } diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/StashServer.kt b/app/src/main/java/com/github/damontecres/stashapp/util/StashServer.kt index 75e68f13..cdb9bc08 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/StashServer.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/StashServer.kt @@ -8,14 +8,30 @@ import com.github.damontecres.stashapp.SettingsFragment import com.github.damontecres.stashapp.StashApplication import com.github.damontecres.stashapp.StashExoPlayer +/** + * Represents a server + */ data class StashServer(val url: String, val apiKey: String?) { + /** + * The server side preferences + * + * Note: needs to populated via [updateServerPrefs]! + */ val serverPreferences = ServerPreferences(this) + /** + * The server's version + * + * Depends on [serverPreferences] which depends on [updateServerPrefs]! + */ val version: Version get() { return serverPreferences.serverVersion } + /** + * Query the server for preferences + */ suspend fun updateServerPrefs(): ServerPreferences { val queryEngine = QueryEngine(this) val result = queryEngine.getServerConfiguration() diff --git a/app/src/main/java/com/github/damontecres/stashapp/util/Version.kt b/app/src/main/java/com/github/damontecres/stashapp/util/Version.kt index 645d17c6..f47fbb66 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/util/Version.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/util/Version.kt @@ -7,6 +7,11 @@ data class Version( val numCommits: Int? = null, val hash: String? = null, ) { + /** + * Is this version at least the given version + * + * Checks major, minor, & patch only! + */ fun isAtLeast(version: Version): Boolean { if (this.major > version.major) { return true @@ -26,6 +31,11 @@ data class Version( return false } + /** + * Is this greater than the given version (and not equal to!) + * + * Checks major, minor, & patch only! + */ fun isGreaterThan(version: Version): Boolean { if (this.major > version.major) { return true @@ -45,6 +55,9 @@ data class Version( return false } + /** + * Is this equal to or before the specified version + */ fun isEqualOrBefore(version: Version): Boolean { return !isGreaterThan(version) } @@ -69,15 +82,21 @@ data class Version( val MINIMUM_STASH_VERSION = V0_26_0 + /** + * Parse a version string throwing if it is invalid + */ fun fromString(version: String): Version { val v = tryFromString(version) if (v == null) { - throw IllegalArgumentException() + throw IllegalArgumentException(version) } else { return v } } + /** + * Attempt to parse a version string or else return null + */ fun tryFromString(version: String?): Version? { if (version == null) { return null @@ -96,6 +115,9 @@ data class Version( } } + /** + * Returns if the version is at least [MINIMUM_STASH_VERSION] + */ fun isStashVersionSupported(version: Version?): Boolean { if (version == null) { return false @@ -103,11 +125,15 @@ data class Version( return version.isAtLeast(MINIMUM_STASH_VERSION) } + /** + * Checks if the given server and app versions are compatible + * + * Versions are offset by minor version 22: https://github.com/damontecres/StashAppAndroidTV/discussions/308 + */ fun isServerSupportedByAppVersion( serverVersion: Version, appVersion: Version, ): Boolean { - // Versions are offset by 22: https://github.com/damontecres/StashAppAndroidTV/discussions/308 val minServerVer = appVersion.copy(minor = appVersion.minor + 22, patch = 0) return serverVersion.isAtLeast(minServerVer) } diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/HomeImageButton.kt b/app/src/main/java/com/github/damontecres/stashapp/views/HomeImageButton.kt index ead6dae1..43646cc2 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/HomeImageButton.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/HomeImageButton.kt @@ -8,6 +8,9 @@ import androidx.appcompat.widget.AppCompatImageButton import com.github.damontecres.stashapp.MainActivity import com.github.damontecres.stashapp.R +/** + * An [AppCompatImageButton] that is the stash server icon and clicking returns to [MainActivity] + */ class HomeImageButton(context: Context, attrs: AttributeSet?) : AppCompatImageButton(context, attrs) { init { setOnClickListener { diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/ImageGridClickedListener.kt b/app/src/main/java/com/github/damontecres/stashapp/views/ImageGridClickedListener.kt index 82893fd8..5f938dfc 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/ImageGridClickedListener.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/ImageGridClickedListener.kt @@ -9,6 +9,9 @@ import com.github.damontecres.stashapp.util.maxFileSize import com.github.damontecres.stashapp.util.putFilterArgs import com.github.damontecres.stashapp.views.ClassOnItemViewClickedListener.SimpleOnItemViewClickedListener +/** + * A [SimpleOnItemViewClickedListener] for images in a [StashGridFragment] to allow for scrolling through the grid's other images + */ class ImageGridClickedListener(val fragment: StashGridFragment) : SimpleOnItemViewClickedListener { override fun onItemClicked(item: ImageData) { diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/MainTitleView.kt b/app/src/main/java/com/github/damontecres/stashapp/views/MainTitleView.kt index 48028741..1dcbf74a 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/MainTitleView.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/MainTitleView.kt @@ -32,6 +32,11 @@ import com.github.damontecres.stashapp.util.StashServer import com.github.damontecres.stashapp.util.putFilterArgs import kotlinx.coroutines.launch +/** + * The top title bar which has buttons for each [DataType] + * + * Clicking them goes to the default filter for that [DataType] + */ class MainTitleView(context: Context, attrs: AttributeSet) : RelativeLayout(context, attrs), TitleViewAdapter.Provider { diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/MarkerPickerFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/views/MarkerPickerFragment.kt index 865198ca..bdba7e13 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/MarkerPickerFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/MarkerPickerFragment.kt @@ -18,8 +18,12 @@ import com.github.damontecres.stashapp.data.Marker import com.github.damontecres.stashapp.util.MutationEngine import com.github.damontecres.stashapp.util.StashCoroutineExceptionHandler import com.github.damontecres.stashapp.util.StashServer +import com.github.damontecres.stashapp.util.getParcelable import kotlinx.coroutines.launch +/** + * Select a value to shift a [Marker]'s seconds by + */ class MarkerPickerFragment : Fragment(R.layout.marker_picker) { private val viewModel by activityViewModels() @@ -29,7 +33,7 @@ class MarkerPickerFragment : Fragment(R.layout.marker_picker) { ) { super.onViewCreated(view, savedInstanceState) - val marker = requireActivity().intent.getParcelableExtra("marker")!! + val marker = requireActivity().intent.getParcelable("marker", Marker::class)!! val picker = view.findViewById(R.id.picker) val sceneTitle = view.findViewById(R.id.scene_title) diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/OnImageFilterClickedListener.kt b/app/src/main/java/com/github/damontecres/stashapp/views/OnImageFilterClickedListener.kt index 46ecab26..5c767078 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/OnImageFilterClickedListener.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/OnImageFilterClickedListener.kt @@ -12,6 +12,9 @@ import com.github.damontecres.stashapp.suppliers.FilterArgs import com.github.damontecres.stashapp.util.addToIntent import com.github.damontecres.stashapp.util.putFilterArgs +/** + * Similar to [ImageGridClickedListener] but for the main page + */ class OnImageFilterClickedListener( private val context: Context, private val callback: (ImageData) -> FilterPosition?, diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/PlayAllOnClickListener.kt b/app/src/main/java/com/github/damontecres/stashapp/views/PlayAllOnClickListener.kt index cf175313..07005f87 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/PlayAllOnClickListener.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/PlayAllOnClickListener.kt @@ -11,6 +11,9 @@ import com.github.damontecres.stashapp.playback.PlaylistMarkersFragment import com.github.damontecres.stashapp.suppliers.FilterArgs import com.github.damontecres.stashapp.util.putFilterArgs +/** + * The "Play All" button clicker which starts playback for multiple scenes or markers + */ class PlayAllOnClickListener( private val context: Context, private val dataType: DataType, diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/StarRatingBar.kt b/app/src/main/java/com/github/damontecres/stashapp/views/StarRatingBar.kt index 166fc534..2cc5c97d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/StarRatingBar.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/StarRatingBar.kt @@ -5,6 +5,9 @@ import android.util.AttributeSet import android.view.KeyEvent import androidx.appcompat.widget.AppCompatRatingBar +/** + * Overrides [AppCompatRatingBar] to allow for wrapping around left or right + */ class StarRatingBar(context: Context, attrs: AttributeSet?) : AppCompatRatingBar(context, attrs) { override fun onKeyDown( keyCode: Int, diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/StashOnFocusChangeListener.kt b/app/src/main/java/com/github/damontecres/stashapp/views/StashOnFocusChangeListener.kt index 34822f95..ce77aa74 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/StashOnFocusChangeListener.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/StashOnFocusChangeListener.kt @@ -3,6 +3,9 @@ package com.github.damontecres.stashapp.views import android.content.Context import android.view.View +/** + * A [View.OnFocusChangeListener] which slightly zooms the view + */ open class StashOnFocusChangeListener(val context: Context) : View.OnFocusChangeListener { private val mFocusedZoom = context.resources.getFraction( diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/StashRatingBar.kt b/app/src/main/java/com/github/damontecres/stashapp/views/StashRatingBar.kt index 3c8ded66..b20e2f6d 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/StashRatingBar.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/StashRatingBar.kt @@ -15,6 +15,9 @@ import com.github.damontecres.stashapp.R import com.github.damontecres.stashapp.util.ServerPreferences import com.github.damontecres.stashapp.util.StashServer +/** + * A [View] for rating which handles displaying either a star or decimal rating based on the server preferences + */ @SuppressLint("SetTextI18n") class StashRatingBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) { constructor(context: Context) : this(context, null) diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/WrapAroundSeekBar.kt b/app/src/main/java/com/github/damontecres/stashapp/views/WrapAroundSeekBar.kt index da3c29d1..439dd236 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/WrapAroundSeekBar.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/WrapAroundSeekBar.kt @@ -6,6 +6,9 @@ import android.util.AttributeSet import android.view.KeyEvent import androidx.appcompat.widget.AppCompatSeekBar +/** + * Overrides [AppCompatSeekBar] to allow for wrapping around left or right + */ class WrapAroundSeekBar(context: Context, attrs: AttributeSet?) : AppCompatSeekBar(context, attrs) { override fun onKeyDown( keyCode: Int, diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/dialog/ConfirmationDialogFragment.kt b/app/src/main/java/com/github/damontecres/stashapp/views/dialog/ConfirmationDialogFragment.kt index 01a7db5f..3fe11584 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/dialog/ConfirmationDialogFragment.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/dialog/ConfirmationDialogFragment.kt @@ -7,6 +7,9 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import com.github.damontecres.stashapp.R +/** + * A simple dialog to confirm or cancel an action + */ class ConfirmationDialogFragment( private val message: String, private val onClickListener: DialogInterface.OnClickListener, diff --git a/app/src/main/java/com/github/damontecres/stashapp/views/models/ServerViewModel.kt b/app/src/main/java/com/github/damontecres/stashapp/views/models/ServerViewModel.kt index bcfaf287..04f8ed30 100644 --- a/app/src/main/java/com/github/damontecres/stashapp/views/models/ServerViewModel.kt +++ b/app/src/main/java/com/github/damontecres/stashapp/views/models/ServerViewModel.kt @@ -9,6 +9,9 @@ import com.github.damontecres.stashapp.util.StashServer import com.github.damontecres.stashapp.util.getInt import java.util.Objects +/** + * Tracks the current server + */ class ServerViewModel : ViewModel() { private val _currentServer = EqualityMutableLiveData(StashServer.getCurrentStashServer(StashApplication.getApplication())) diff --git a/app/src/test/java/com/github/damontecres/stashapp/FrontPageFilterTests.kt b/app/src/test/java/com/github/damontecres/stashapp/FrontPageFilterTests.kt index eaab04e5..faa56350 100644 --- a/app/src/test/java/com/github/damontecres/stashapp/FrontPageFilterTests.kt +++ b/app/src/test/java/com/github/damontecres/stashapp/FrontPageFilterTests.kt @@ -106,11 +106,12 @@ class FrontPageFilterTests { runBlocking { frontPageParser.parse(parseFileToFrontPageContent("front_page_basic.json")) } val rows = result.map { runBlocking { it.await() } } Assert.assertEquals(4, rows.size) - rows.forEach { Assert.assertEquals(FrontPageParser.FrontPageRowResult.SUCCESS, it.result) } - Assert.assertEquals(DataType.SCENE, rows[0].data!!.filter.dataType) - Assert.assertEquals(DataType.SCENE, rows[1].data!!.filter.dataType) - Assert.assertEquals(DataType.PERFORMER, rows[2].data!!.filter.dataType) - Assert.assertEquals(DataType.PERFORMER, rows[3].data!!.filter.dataType) + rows.forEach { Assert.assertTrue(it is FrontPageParser.FrontPageRow.Success) } + rows as List + Assert.assertEquals(DataType.SCENE, rows[0].filter.dataType) + Assert.assertEquals(DataType.SCENE, rows[1].filter.dataType) + Assert.assertEquals(DataType.PERFORMER, rows[2].filter.dataType) + Assert.assertEquals(DataType.PERFORMER, rows[3].filter.dataType) } @Test @@ -125,7 +126,7 @@ class FrontPageFilterTests { val result = runBlocking { frontPageParser.parse(parseFileToFrontPageContent("front_page_unsupported.json")) } val rows = result.map { runBlocking { it.await() } } Assert.assertEquals(2, rows.size) - Assert.assertEquals(FrontPageParser.FrontPageRowResult.DATA_TYPE_NOT_SUPPORTED, rows[0].result) - Assert.assertEquals(FrontPageParser.FrontPageRowResult.DATA_TYPE_NOT_SUPPORTED, rows[1].result) + Assert.assertTrue(rows[0] is FrontPageParser.FrontPageRow.NotSupported) + Assert.assertTrue(rows[1] is FrontPageParser.FrontPageRow.NotSupported) } } diff --git a/app/src/test/java/com/github/damontecres/stashapp/ObjectFilterParsingTests.kt b/app/src/test/java/com/github/damontecres/stashapp/ObjectFilterParsingTests.kt index ba97fac1..37f07d42 100644 --- a/app/src/test/java/com/github/damontecres/stashapp/ObjectFilterParsingTests.kt +++ b/app/src/test/java/com/github/damontecres/stashapp/ObjectFilterParsingTests.kt @@ -11,8 +11,8 @@ import com.github.damontecres.stashapp.api.type.GenderEnum import com.github.damontecres.stashapp.api.type.IntCriterionInput import com.github.damontecres.stashapp.api.type.SceneFilterType import com.github.damontecres.stashapp.util.FilterParser +import com.github.damontecres.stashapp.util.OptionalSerializersModule import com.github.damontecres.stashapp.util.Version -import com.github.damontecres.stashapp.util.optionalSerializerModule import kotlinx.serialization.json.Json import okio.FileSystem import okio.Path.Companion.toPath @@ -49,13 +49,13 @@ class ObjectFilterParsingTests { file_count = Optional.present( IntCriterionInput( - 100, - Optional.absent(), - CriterionModifier.INCLUDES_ALL, + value = 100, + value2 = Optional.absent(), + modifier = CriterionModifier.INCLUDES_ALL, ), ), ) - val format = Json { serializersModule = optionalSerializerModule } + val format = Json { serializersModule = OptionalSerializersModule } val json = format.encodeToString(SceneFilterType.serializer(), filter) val result = format.decodeFromString(json) Assert.assertEquals(filter, result)