diff --git a/app/build.gradle b/app/build.gradle index b4cb5967a..f0c9cd06e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,8 @@ android { defaultConfig { applicationId 'org.koitharu.kotatsu' minSdkVersion 21 + //TODO: update as soon as sources becomes available + //noinspection OldTargetApi targetSdkVersion 33 versionCode 600 versionName '6.0.0' @@ -79,12 +81,12 @@ afterEvaluate { } dependencies { //noinspection GradleDependency - implementation('com.github.KotatsuApp:kotatsu-parsers:92bfc7e9fa') { + implementation('com.github.KotatsuApp:kotatsu-parsers:07df5a81cf') { exclude group: 'org.json', module: 'json' } implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.22' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.2' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.core:core-ktx:1.10.1' @@ -107,7 +109,7 @@ dependencies { // TODO https://issuetracker.google.com/issues/254846063 implementation 'androidx.work:work-runtime-ktx:2.8.1' //noinspection GradleDependency - implementation('com.google.guava:guava:32.0.0-android') { + implementation('com.google.guava:guava:32.0.1-android') { exclude group: 'com.google.guava', module: 'failureaccess' exclude group: 'org.checkerframework', module: 'checker-qual' exclude group: 'com.google.j2objc', module: 'j2objc-annotations' @@ -139,18 +141,18 @@ dependencies { implementation 'ch.acra:acra-http:5.10.1' implementation 'ch.acra:acra-dialog:5.10.1' - debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.11' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12' testImplementation 'junit:junit:4.13.2' testImplementation 'org.json:json:20230618' - testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1' + testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.2' androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation 'androidx.test:rules:1.5.0' androidTestImplementation 'androidx.test:core-ktx:1.5.0' androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.5' - androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1' + androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.2' androidTestImplementation 'androidx.room:room-testing:2.5.2' androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.15.0' diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/TipView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/TipView.kt new file mode 100644 index 000000000..a481f156b --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/TipView.kt @@ -0,0 +1,135 @@ +package org.koitharu.kotatsu.core.ui.widgets + +import android.content.Context +import android.graphics.Outline +import android.graphics.Rect +import android.graphics.RectF +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewOutlineProvider +import android.widget.LinearLayout +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.core.content.ContextCompat +import androidx.core.content.withStyledAttributes +import androidx.core.view.setPadding +import com.google.android.material.shape.MaterialShapeDrawable +import com.google.android.material.shape.ShapeAppearanceModel +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.util.ext.drawableStart +import org.koitharu.kotatsu.core.util.ext.getDrawableCompat +import org.koitharu.kotatsu.core.util.ext.getThemeColorStateList +import org.koitharu.kotatsu.core.util.ext.setTextAndVisible +import org.koitharu.kotatsu.core.util.ext.textAndVisible +import org.koitharu.kotatsu.databinding.ViewTipBinding +import com.google.android.material.R as materialR + +class TipView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = R.attr.tipViewStyle, +) : LinearLayout(context, attrs, defStyleAttr), View.OnClickListener { + + private val binding = ViewTipBinding.inflate(LayoutInflater.from(context), this) + + var title: CharSequence? + get() = binding.textViewTitle.text + set(value) { + binding.textViewTitle.text = value + } + + var text: CharSequence? + get() = binding.textViewBody.text + set(value) { + binding.textViewBody.text = value + } + + var icon: Drawable? + get() = binding.textViewTitle.drawableStart + set(value) { + binding.textViewTitle.drawableStart = value + } + + var primaryButtonText: CharSequence? + get() = binding.buttonPrimary.textAndVisible + set(value) { + binding.buttonPrimary.textAndVisible = value + } + + var secondaryButtonText: CharSequence? + get() = binding.buttonSecondary.textAndVisible + set(value) { + binding.buttonSecondary.textAndVisible = value + } + + var onButtonClickListener: OnButtonClickListener? = null + + init { + orientation = VERTICAL + setPadding(context.resources.getDimensionPixelOffset(R.dimen.margin_normal)) + context.withStyledAttributes(attrs, R.styleable.TipView, defStyleAttr) { + title = getText(R.styleable.TipView_title) + text = getText(R.styleable.TipView_android_text) + icon = getDrawableCompat(context, R.styleable.TipView_icon) + primaryButtonText = getString(R.styleable.TipView_primaryButtonText) + secondaryButtonText = getString(R.styleable.TipView_secondaryButtonText) + val shapeAppearanceModel = ShapeAppearanceModel.builder(context, attrs, defStyleAttr, 0).build() + background = MaterialShapeDrawable(shapeAppearanceModel).also { + it.fillColor = getColorStateList(R.styleable.TipView_cardBackgroundColor) + ?: context.getThemeColorStateList(materialR.attr.colorBackgroundFloating) + it.strokeWidth = getDimension(R.styleable.TipView_strokeWidth, 0f) + it.strokeColor = getColorStateList(R.styleable.TipView_strokeColor) + it.elevation = getDimension(R.styleable.TipView_elevation, 0f) + } + outlineProvider = OutlineProvider(shapeAppearanceModel) + } + } + + override fun onClick(v: View) { + when (v.id) { + R.id.button_primary -> onButtonClickListener?.onPrimaryButtonClick(this) + R.id.button_secondary -> onButtonClickListener?.onSecondaryButtonClick(this) + } + } + + fun setTitle(@StringRes resId: Int) { + binding.textViewTitle.setText(resId) + } + + fun setText(@StringRes resId: Int) { + binding.textViewBody.setText(resId) + } + + fun setPrimaryButtonText(@StringRes resId: Int) { + binding.buttonPrimary.setTextAndVisible(resId) + } + + fun setSecondaryButtonText(@StringRes resId: Int) { + binding.buttonSecondary.setTextAndVisible(resId) + } + + fun setIcon(@DrawableRes resId: Int) { + icon = ContextCompat.getDrawable(context, resId) + } + + interface OnButtonClickListener { + + fun onPrimaryButtonClick(tipView: TipView) + + fun onSecondaryButtonClick(tipView: TipView) + } + + private class OutlineProvider( + shapeAppearanceModel: ShapeAppearanceModel, + ) : ViewOutlineProvider() { + + private val shapeDrawable = MaterialShapeDrawable(shapeAppearanceModel) + override fun getOutline(view: View, outline: Outline) { + shapeDrawable.setBounds(0, 0, view.width, view.height) + shapeDrawable.getOutline(outline) + } + } + +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt index dae151af2..4ce515d9b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt @@ -1,11 +1,16 @@ package org.koitharu.kotatsu.core.util.ext import android.content.Context +import android.content.res.TypedArray import android.graphics.Color +import android.graphics.drawable.Drawable import androidx.annotation.AttrRes import androidx.annotation.ColorInt import androidx.annotation.FloatRange import androidx.annotation.Px +import androidx.core.content.ContextCompat +import androidx.core.content.res.ResourcesCompat +import androidx.core.content.res.TypedArrayUtils import androidx.core.content.res.use import androidx.core.graphics.ColorUtils @@ -60,3 +65,8 @@ fun Context.getThemeColorStateList( ) = obtainStyledAttributes(intArrayOf(resId)).use { it.getColorStateList(0) } + +fun TypedArray.getDrawableCompat(context: Context, index: Int): Drawable? { + val resId = getResourceId(index, 0) + return if (resId != 0) ContextCompat.getDrawable(context, resId) else null +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 8272c3b2e..b98d8180b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -52,6 +52,7 @@ import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo +import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet import org.koitharu.kotatsu.search.ui.MangaListActivity import org.koitharu.kotatsu.search.ui.SearchActivity import javax.inject.Inject @@ -80,6 +81,9 @@ class DetailsFragment : super.onViewBindingCreated(binding, savedInstanceState) binding.textViewAuthor.setOnClickListener(this) binding.imageViewCover.setOnClickListener(this) + binding.buttonDescriptionMore.setOnClickListener(this) + binding.buttonBookmarksMore.setOnClickListener(this) + binding.buttonScrobblingMore.setOnClickListener(this) binding.infoLayout.textViewSource.setOnClickListener(this) binding.textViewDescription.movementMethod = LinkMovementMethod.getInstance() binding.chipsTags.onChipClickListener = this @@ -217,7 +221,7 @@ class DetailsFragment : private fun onScrobblingInfoChanged(scrobblings: List) { var adapter = requireViewBinding().recyclerViewScrobbling.adapter as? ScrollingInfoAdapter - requireViewBinding().recyclerViewScrobbling.isGone = scrobblings.isEmpty() + requireViewBinding().groupScrobbling.isGone = scrobblings.isEmpty() if (adapter != null) { adapter.items = scrobblings } else { @@ -260,6 +264,10 @@ class DetailsFragment : scaleUpActivityOptionsOf(v), ) } + + R.id.button_scrobbling_more -> { + ScrobblingSelectorSheet.show(parentFragmentManager, manga, null) + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt index 42644501a..1447002d3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/scrobbling/ScrobblingInfoAD.kt @@ -29,8 +29,8 @@ fun scrobblingInfoAD( error(R.drawable.ic_error_placeholder) enqueueWith(coil) } - binding.textViewTitle.text = item.title - binding.textViewTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, item.scrobbler.iconResId, 0) + binding.textViewTitle.setText(item.scrobbler.titleResId) + binding.imageViewIcon.setImageResource(item.scrobbler.iconResId) binding.ratingBar.rating = item.rating * binding.ratingBar.numStars binding.textViewStatus.text = item.status?.let { context.resources.getStringArray(R.array.scrobbling_statuses).getOrNull(it.ordinal) diff --git a/app/src/main/res/layout-w600dp-land/fragment_details.xml b/app/src/main/res/layout-w600dp-land/fragment_details.xml deleted file mode 100644 index 19d7974ea..000000000 --- a/app/src/main/res/layout-w600dp-land/fragment_details.xml +++ /dev/null @@ -1,223 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 4f32dbd71..66e41f044 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -139,25 +139,79 @@ app:layout_constraintTop_toBottomOf="@+id/info_layout" /> + +