diff --git a/app/src/androidTest/java/com/rusili/superstreet/database/FavoriteDaoTest.kt b/app/src/androidTest/java/com/rusili/superstreet/database/FavoriteDaoTest.kt index b2cc88b..4066132 100644 --- a/app/src/androidTest/java/com/rusili/superstreet/database/FavoriteDaoTest.kt +++ b/app/src/androidTest/java/com/rusili/superstreet/database/FavoriteDaoTest.kt @@ -12,18 +12,15 @@ import com.rusili.superstreet.common.models.Type import com.rusili.superstreet.common.models.footer.Author import com.rusili.superstreet.common.models.header.HeaderImage import com.rusili.superstreet.common.models.header.Title -import com.rusili.superstreet.database.favorites.FavoriteEntity -import com.rusili.superstreet.database.favorites.FavoriteModelMapper +import com.rusili.superstreet.database.favorites.model.FavoriteModelMapper import com.rusili.superstreet.previewlist.domain.ArticlePreviewModel import com.rusili.superstreet.previewlist.domain.CardSize -import io.reactivex.functions.Predicate import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import java.util.Date -import java.util.concurrent.TimeUnit @RunWith(AndroidJUnit4::class) class FavoriteDaoTest { diff --git a/app/src/main/java/com/rusili/superstreet/MainActivity.kt b/app/src/main/java/com/rusili/superstreet/MainActivity.kt index ca755e0..e67b8c6 100644 --- a/app/src/main/java/com/rusili/superstreet/MainActivity.kt +++ b/app/src/main/java/com/rusili/superstreet/MainActivity.kt @@ -1,5 +1,6 @@ package com.rusili.superstreet +import android.accounts.NetworkErrorException import android.content.Intent import android.os.Bundle import android.view.View @@ -8,6 +9,7 @@ import androidx.core.view.ViewCompat import androidx.fragment.app.Fragment import com.rusili.superstreet.article.ui.ArticleActivity import com.rusili.superstreet.common.base.BaseActivity +import com.rusili.superstreet.common.extensions.isNetworkConnected import com.rusili.superstreet.common.models.Header import com.squareup.moshi.Moshi import dagger.android.AndroidInjector @@ -18,8 +20,8 @@ import javax.inject.Inject class MainActivity : BaseActivity(), MainNavigator, HasSupportFragmentInjector { @Inject protected lateinit var fragmentInjector: DispatchingAndroidInjector - @Inject protected lateinit var moshi: Moshi + @Inject protected lateinit var moshi: Moshi override fun supportFragmentInjector(): AndroidInjector = fragmentInjector override fun onCreate(savedInstanceState: Bundle?) { @@ -33,6 +35,10 @@ class MainActivity : BaseActivity(), MainNavigator, HasSupportFragmentInjector { view: View, header: Header ) { + if (!isNetworkConnected()) { + showError(NetworkErrorException()) + } + Intent(this, ArticleActivity::class.java).apply { putExtra( ArticleActivity.ARTICLE_HEADER_BUNDLE_KEY, @@ -42,9 +48,13 @@ class MainActivity : BaseActivity(), MainNavigator, HasSupportFragmentInjector { val options = ActivityOptionsCompat.makeSceneTransitionAnimation( this, view, - ViewCompat.getTransitionName(view)!! + ViewCompat.getTransitionName(view).orEmpty() ) startActivity(it, options.toBundle()) } } + + override fun shareLink(link: String) { + // TODO + } } diff --git a/app/src/main/java/com/rusili/superstreet/MainNavigator.kt b/app/src/main/java/com/rusili/superstreet/MainNavigator.kt index ad7bc5d..ebd76ff 100644 --- a/app/src/main/java/com/rusili/superstreet/MainNavigator.kt +++ b/app/src/main/java/com/rusili/superstreet/MainNavigator.kt @@ -1,5 +1,6 @@ package com.rusili.superstreet +import android.content.Intent import android.view.View import com.rusili.superstreet.common.models.Header @@ -9,4 +10,6 @@ interface MainNavigator { view: View, header: Header ) + + fun shareLink(link: String) } diff --git a/app/src/main/java/com/rusili/superstreet/article/domain/ArticleUsecaseImpl.kt b/app/src/main/java/com/rusili/superstreet/article/domain/ArticleUsecaseImpl.kt index 9f08c99..3a293ef 100644 --- a/app/src/main/java/com/rusili/superstreet/article/domain/ArticleUsecaseImpl.kt +++ b/app/src/main/java/com/rusili/superstreet/article/domain/ArticleUsecaseImpl.kt @@ -1,9 +1,12 @@ package com.rusili.superstreet.article.domain import com.rusili.superstreet.article.ui.ArticleUsecase +import io.reactivex.Single import javax.inject.Inject -class ArticleUsecaseImpl @Inject constructor(private val repository: ArticleRepository) : ArticleUsecase { +class ArticleUsecaseImpl @Inject constructor(private val repository: ArticleRepository) : + ArticleUsecase { - override fun getArticle(href: String) = repository.getArticle(href) + override fun getArticle(href: String): Single = + repository.getArticle(href) } diff --git a/app/src/main/java/com/rusili/superstreet/article/ui/ArticleActivity.kt b/app/src/main/java/com/rusili/superstreet/article/ui/ArticleActivity.kt index 8a73cbb..ff47b59 100644 --- a/app/src/main/java/com/rusili/superstreet/article/ui/ArticleActivity.kt +++ b/app/src/main/java/com/rusili/superstreet/article/ui/ArticleActivity.kt @@ -6,6 +6,7 @@ import android.content.IntentSender import android.os.Bundle import android.view.View import androidx.core.app.ActivityOptionsCompat +import androidx.core.content.ContextCompat.startActivity import androidx.core.view.ViewCompat import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders @@ -21,6 +22,7 @@ import com.rusili.superstreet.common.models.Header import com.rusili.superstreet.common.models.body.Image import com.rusili.superstreet.common.models.body.ImageSize import com.rusili.superstreet.common.ui.SimpleRequestListener +import com.rusili.superstreet.common.ui.actions.HasActionsView import com.rusili.superstreet.image.ImageActivity import com.rusili.superstreet.image.ImageActivity.Companion.IMAGE_BUNDLE_KEY import com.rusili.superstreet.image.ImageActivity.Companion.IMAGE_SIZE_BUNDLE_KEY @@ -29,18 +31,15 @@ import com.squareup.moshi.Moshi import kotlinx.android.synthetic.main.activity_article.* import javax.inject.Inject -class ArticleActivity : BaseActivity() { +class ArticleActivity : BaseActivity(), HasActionsView { @Inject protected lateinit var viewModelFactory: ArticleViewModelFactory @Inject protected lateinit var moshi: Moshi private val viewModel: ArticleViewModel by lazy { ViewModelProviders.of(this, viewModelFactory).get(ArticleViewModel::class.java) } - private val adapter: ArticleAdapter by lazy { - ArticleAdapter(::onImageClicked, Glide.with(this)).apply { - setHasStableIds(true) - } + ArticleAdapter(::onImageClicked, Glide.with(this)) } companion object { @@ -55,6 +54,7 @@ class ArticleActivity : BaseActivity() { intent?.getStringExtra(ARTICLE_HEADER_BUNDLE_KEY)?.let { json -> moshi.adapter
(Header::class.java).fromJson(json)?.let { header -> setupViews(header) + setActionsView(header.title.href) articleProgressBar.show() viewModel.getArticle(header.title.href) } @@ -72,6 +72,13 @@ class ArticleActivity : BaseActivity() { }) } + override fun setActionsView(link: String) { + articleActionsView.apply { + setFavoriteAction() + setShareLink(link) + } + } + private fun setupViews(header: Header) { articleHeaderImageView.transitionName = header.headerImage.title articleHeaderTitle.text = header.title.value @@ -113,7 +120,7 @@ class ArticleActivity : BaseActivity() { val options = ActivityOptionsCompat.makeSceneTransitionAnimation( this@ArticleActivity, view, - ViewCompat.getTransitionName(view)!! + ViewCompat.getTransitionName(view) ?: "" ) startActivity(this, options.toBundle()) } diff --git a/app/src/main/java/com/rusili/superstreet/article/ui/ArticleViewModel.kt b/app/src/main/java/com/rusili/superstreet/article/ui/ArticleViewModel.kt index 2fda18c..f82c754 100644 --- a/app/src/main/java/com/rusili/superstreet/article/ui/ArticleViewModel.kt +++ b/app/src/main/java/com/rusili/superstreet/article/ui/ArticleViewModel.kt @@ -19,8 +19,8 @@ class ArticleViewModel(private val usecase: ArticleUsecase) : BaseViewModel() { .subscribeBy( onSuccess = { livedata.postValue(LiveDataWrapper(it)) }, onError = { - livedata.postValue(LiveDataWrapper(null, it)) Timber.e(it, "Error getting preview articles.") + livedata.postValue(LiveDataWrapper(null, it)) } )) } diff --git a/app/src/main/java/com/rusili/superstreet/article/ui/rv/ArticleAdapter.kt b/app/src/main/java/com/rusili/superstreet/article/ui/rv/ArticleAdapter.kt index b43c68b..45c9b33 100644 --- a/app/src/main/java/com/rusili/superstreet/article/ui/rv/ArticleAdapter.kt +++ b/app/src/main/java/com/rusili/superstreet/article/ui/rv/ArticleAdapter.kt @@ -18,6 +18,10 @@ class ArticleAdapter( private val glide: RequestManager ) : ListAdapter(ArticleDiffCallback()) { + init { + setHasStableIds(true) + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = when (viewType) { ArticleViewType.Paragraph.viewType -> ParagraphViewHolder(parent.inflate(R.layout.viewholder_article_paragraph)) diff --git a/app/src/main/java/com/rusili/superstreet/article/ui/rv/ImageGroupViewHolder.kt b/app/src/main/java/com/rusili/superstreet/article/ui/rv/ImageGroupViewHolder.kt index debf940..8b41fc0 100644 --- a/app/src/main/java/com/rusili/superstreet/article/ui/rv/ImageGroupViewHolder.kt +++ b/app/src/main/java/com/rusili/superstreet/article/ui/rv/ImageGroupViewHolder.kt @@ -39,7 +39,6 @@ class ImageGroupViewHolder( ViewCompat.setTransitionName(view, image.id.toString()) glide.load(image.getGroupSizeUrl()) - .override(IMAGE_GROUP_WIDTH, IMAGE_GROUP_HEIGHT) .into(view) view.setOnClickListener { onClick(it, image, ImageSize.GROUP) } diff --git a/app/src/main/java/com/rusili/superstreet/article/ui/rv/ImageViewHolder.kt b/app/src/main/java/com/rusili/superstreet/article/ui/rv/ImageViewHolder.kt index 2282f50..a4bb421 100644 --- a/app/src/main/java/com/rusili/superstreet/article/ui/rv/ImageViewHolder.kt +++ b/app/src/main/java/com/rusili/superstreet/article/ui/rv/ImageViewHolder.kt @@ -31,7 +31,6 @@ class ImageViewHolder( glide.load(model.getDefaultSizeUrl()) .listener(glideListener) - .override(IMAGE_DEFAULT_WIDTH, IMAGE_DEFAULT_HEIGHT) .into(articleImageView) articleImageView.setOnClickListener { onClick(it, model, ImageSize.DEFAULT) } diff --git a/app/src/main/java/com/rusili/superstreet/common/base/BaseActivity.kt b/app/src/main/java/com/rusili/superstreet/common/base/BaseActivity.kt index b63baa3..bf01507 100644 --- a/app/src/main/java/com/rusili/superstreet/common/base/BaseActivity.kt +++ b/app/src/main/java/com/rusili/superstreet/common/base/BaseActivity.kt @@ -1,6 +1,7 @@ package com.rusili.superstreet.common.base import android.accounts.NetworkErrorException +import android.content.Intent import android.content.IntentSender import android.os.Bundle import androidx.appcompat.app.AlertDialog @@ -14,7 +15,7 @@ import java.net.UnknownHostException abstract class BaseActivity : AppCompatActivity() { protected val disposable = CompositeDisposable() - var container = 0 + protected var container = 0 override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) @@ -32,12 +33,12 @@ abstract class BaseActivity : AppCompatActivity() { super.onStop() } - fun inflateFragment(fragment: BaseFragment) = + protected fun inflateFragment(fragment: BaseFragment) = supportFragmentManager.beginTransaction() .add(container, fragment) .commit() - fun showError(error: Throwable?) { + protected fun showError(error: Throwable?) { when (error) { is IntentSender.SendIntentException -> showErrorDialogToFinish() is NetworkErrorException -> showNetworkError() @@ -47,7 +48,7 @@ abstract class BaseActivity : AppCompatActivity() { } } - fun showSnackbar( + protected fun showSnackbar( message: String, length: Int = 0 ) { diff --git a/app/src/main/java/com/rusili/superstreet/common/extensions/ContextExtensions.kt b/app/src/main/java/com/rusili/superstreet/common/extensions/ContextExtensions.kt index 471a8d9..e96c301 100644 --- a/app/src/main/java/com/rusili/superstreet/common/extensions/ContextExtensions.kt +++ b/app/src/main/java/com/rusili/superstreet/common/extensions/ContextExtensions.kt @@ -1,9 +1,31 @@ package com.rusili.superstreet.common.extensions import android.content.Context +import android.content.Intent import android.net.ConnectivityManager +import com.rusili.superstreet.jsoup.api.BASE_HTML + +private const val STRING_SHARE_TYPE = "text/plain" +private const val STRING_SHARE_SUBJECT = "Sharing URL" +private const val STRING_SHARE_VIA = "Share via: " fun Context?.isNetworkConnected(): Boolean = this?.let { (it.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo?.isConnected ?: false } ?: false + +fun Context?.shareLink(link: String) { + this?.startActivity( + Intent.createChooser( + Intent(Intent.ACTION_SEND).apply { + setType(STRING_SHARE_TYPE) + putExtra( + Intent.EXTRA_SUBJECT, + STRING_SHARE_SUBJECT + ) + putExtra(Intent.EXTRA_TEXT, BASE_HTML.plus(link)) + }, + STRING_SHARE_VIA + ) + ) +} diff --git a/app/src/main/java/com/rusili/superstreet/common/extensions/StringExtensions.kt b/app/src/main/java/com/rusili/superstreet/common/extensions/StringExtensions.kt index b1541d6..3791f9e 100644 --- a/app/src/main/java/com/rusili/superstreet/common/extensions/StringExtensions.kt +++ b/app/src/main/java/com/rusili/superstreet/common/extensions/StringExtensions.kt @@ -1,4 +1,6 @@ package com.rusili.superstreet.common.extensions +import com.rusili.superstreet.jsoup.api.BASE_HTML + fun String.remove(toRemove: String) = replace(toRemove, "") diff --git a/app/src/main/java/com/rusili/superstreet/common/extensions/TypedArrayExtensions.kt b/app/src/main/java/com/rusili/superstreet/common/extensions/TypedArrayExtensions.kt new file mode 100644 index 0000000..8429f16 --- /dev/null +++ b/app/src/main/java/com/rusili/superstreet/common/extensions/TypedArrayExtensions.kt @@ -0,0 +1,6 @@ +package com.rusili.superstreet.common.extensions + +import android.content.res.TypedArray + +fun TypedArray.getDimen(id: Int) = + resources.getDimensionPixelSize(id) diff --git a/app/src/main/java/com/rusili/superstreet/common/ui/ShapedRippleImageButton.kt b/app/src/main/java/com/rusili/superstreet/common/ui/ShapedRippleImageButton.kt new file mode 100644 index 0000000..70ec255 --- /dev/null +++ b/app/src/main/java/com/rusili/superstreet/common/ui/ShapedRippleImageButton.kt @@ -0,0 +1,77 @@ +package com.rusili.superstreet.common.ui + +import android.annotation.SuppressLint +import android.content.Context +import android.content.res.ColorStateList +import android.content.res.TypedArray +import android.graphics.drawable.Drawable +import android.graphics.drawable.RippleDrawable +import android.graphics.drawable.ShapeDrawable +import android.graphics.drawable.shapes.OvalShape +import android.graphics.drawable.shapes.Shape +import android.os.Build +import android.util.AttributeSet +import android.widget.ImageButton +import androidx.annotation.ColorInt +import androidx.core.content.res.getColorOrThrow +import androidx.core.content.withStyledAttributes +import com.rusili.superstreet.R + +@SuppressLint("ResourceAsColor") +private const @ColorInt val MASK_COLOR_DEFAULT: Int = R.color.black_10_1000 + +class ShapedRippleImageButton @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ImageButton(context, attrs, defStyleAttr) { + + init { + context.withStyledAttributes( + attrs, + R.styleable.ShapedRippleImageButton, + defStyleAttr, + 0 + ) { + setupViews() + } + } + + private fun TypedArray.setupViews() { + val buttonShape = OvalShape() + + @ColorInt val fillColor = resources.getColor(android.R.color.transparent) + @ColorInt val maskColor = getColor( + R.styleable.ShapedRippleImageButton_ripple_color, + MASK_COLOR_DEFAULT + ) + + background?.let { + setBackgroundRippleShape(it, buttonShape) + } ?: setForegroundRippleShape(buttonShape, fillColor, maskColor) + } + + private fun setBackgroundRippleShape( + background: Drawable, + buttonShape: Shape + ) { + (background as RippleDrawable).apply { + (getDrawable(0) as ShapeDrawable).apply { shape = buttonShape } + (getDrawable(1) as ShapeDrawable).apply { shape = buttonShape } + } + } + + private fun setForegroundRippleShape( + buttonShape: Shape, + fillColor: Int, + maskColor: Int + ) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + foreground = RippleDrawable( + ColorStateList.valueOf(maskColor), + ShapeDrawable(buttonShape).apply { paint.color = fillColor }, + ShapeDrawable(buttonShape).apply { paint.color = maskColor } + ) + } + } +} diff --git a/app/src/main/java/com/rusili/superstreet/common/ui/StyleableFrameLayout.kt b/app/src/main/java/com/rusili/superstreet/common/ui/StyleableFrameLayout.kt new file mode 100644 index 0000000..daf7b44 --- /dev/null +++ b/app/src/main/java/com/rusili/superstreet/common/ui/StyleableFrameLayout.kt @@ -0,0 +1,43 @@ +package com.rusili.superstreet.common.ui + +import android.content.Context +import android.content.res.TypedArray +import android.util.AttributeSet +import android.widget.FrameLayout +import androidx.annotation.LayoutRes +import androidx.annotation.StyleableRes +import androidx.core.content.withStyledAttributes + +abstract class StyleableFrameLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + init { + this.getLayout()?.let { + inflate(context, it, this@StyleableFrameLayout) + } + + context.withStyledAttributes( + attrs, + this.getStyleable(), + defStyleAttr, + 0 + ) { + setupViews() + } + + post(::postSetup) + } + + @LayoutRes + abstract fun getLayout(): Int? + + abstract fun TypedArray.setupViews() + + @StyleableRes + open fun getStyleable(): IntArray = IntArray(0) + + open fun postSetup() = Unit +} diff --git a/app/src/main/java/com/rusili/superstreet/common/ui/actions/ActionsView.kt b/app/src/main/java/com/rusili/superstreet/common/ui/actions/ActionsView.kt new file mode 100644 index 0000000..3d9d676 --- /dev/null +++ b/app/src/main/java/com/rusili/superstreet/common/ui/actions/ActionsView.kt @@ -0,0 +1,107 @@ +package com.rusili.superstreet.common.ui.actions + +import android.content.Context +import android.content.Intent +import android.content.res.TypedArray +import android.util.AttributeSet +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.Toast +import androidx.core.view.setPadding +import com.rusili.superstreet.R +import com.rusili.superstreet.common.extensions.getDimen +import com.rusili.superstreet.common.extensions.shareLink +import com.rusili.superstreet.common.ui.StyleableFrameLayout +import com.rusili.superstreet.jsoup.api.BASE_HTML + +class ActionsView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : StyleableFrameLayout(context, attrs, defStyleAttr) { + private lateinit var actionFavorite: ImageButton + private lateinit var actionShare: ImageButton + private lateinit var allActionViews: List + + override fun getLayout(): Int? = R.layout.view_actions + override fun getStyleable(): IntArray = R.styleable.ActionsView + + override fun TypedArray.setupViews() { + + actionFavorite = findViewById(R.id.actionFavorite) + actionShare = findViewById(R.id.actionShare) + allActionViews = listOf(actionFavorite, actionShare) + + setSize() + } + + fun setShareLink(link: String) { + findViewById(R.id.actionShare).setOnClickListener { + context.shareLink(link) + } + } + + fun setFavoriteAction() { + findViewById(R.id.actionFavorite).setOnClickListener { + Toast.makeText( + context, + "TEST", + Toast.LENGTH_SHORT + ).show() + } + } + + private fun TypedArray.setSize() { + val smallStyle = ViewStyle( + length = getDimen(R.dimen.action_button_height_small), + padding = getDimen(R.dimen.action_button_padding_small) + ) + val mediumStyle = ViewStyle( + length = getDimen(R.dimen.action_button_height_medium), + padding = getDimen(R.dimen.action_button_padding_medium) + ) + val largeStyle = ViewStyle( + length = getDimen(R.dimen.action_button_height_large), + padding = getDimen(R.dimen.action_button_padding_large) + ) + + getInt(R.styleable.ActionsView_size, -1) + .takeIf { it != -1 } + ?.let(ViewSize.Companion::fromAttr) + ?.let { size -> + when (size) { + ViewSize.SMALL -> applySize(smallStyle) + ViewSize.MEDIUM -> applySize(mediumStyle) + ViewSize.LARGE -> applySize(largeStyle) + } + } + } + + private fun applySize(style: ViewStyle) { + val params = LinearLayout.LayoutParams(style.length, style.length) + + allActionViews.forEach { view -> + view.apply { + layoutParams = params + setPadding(style.padding) + } + } + } + + private enum class ViewSize(val attr: Int) { + SMALL(0), + MEDIUM(1), + LARGE(2); + + companion object { + fun fromAttr(input: Int): ViewSize = + values().first { it.attr == input } + } + } + + private data class ViewStyle( + val length: Int, + val padding: Int + ) +} diff --git a/app/src/main/java/com/rusili/superstreet/common/ui/actions/HasActionsView.kt b/app/src/main/java/com/rusili/superstreet/common/ui/actions/HasActionsView.kt new file mode 100644 index 0000000..6364431 --- /dev/null +++ b/app/src/main/java/com/rusili/superstreet/common/ui/actions/HasActionsView.kt @@ -0,0 +1,6 @@ +package com.rusili.superstreet.common.ui.actions + +interface HasActionsView { + + fun setActionsView(link: String) +} diff --git a/app/src/main/java/com/rusili/superstreet/database/AppDatabase.kt b/app/src/main/java/com/rusili/superstreet/database/AppDatabase.kt index 207b7c2..89fc590 100644 --- a/app/src/main/java/com/rusili/superstreet/database/AppDatabase.kt +++ b/app/src/main/java/com/rusili/superstreet/database/AppDatabase.kt @@ -3,7 +3,7 @@ package com.rusili.superstreet.database import androidx.room.Database import androidx.room.RoomDatabase import com.rusili.superstreet.database.favorites.FavoriteDao -import com.rusili.superstreet.database.favorites.FavoriteEntity +import com.rusili.superstreet.database.favorites.model.FavoriteEntity @Database(entities = [FavoriteEntity::class], version = 1) abstract class AppDatabase : RoomDatabase() { diff --git a/app/src/main/java/com/rusili/superstreet/database/di/DatabaseModule.kt b/app/src/main/java/com/rusili/superstreet/database/di/DatabaseModule.kt index fa61993..514ed8a 100644 --- a/app/src/main/java/com/rusili/superstreet/database/di/DatabaseModule.kt +++ b/app/src/main/java/com/rusili/superstreet/database/di/DatabaseModule.kt @@ -4,29 +4,41 @@ import android.content.Context import androidx.room.Room import com.rusili.superstreet.database.AppDatabase import com.rusili.superstreet.database.favorites.FavoriteDao -import com.rusili.superstreet.database.favorites.FavoriteModelMapper +import com.rusili.superstreet.database.favorites.FavoriteManager +import com.rusili.superstreet.database.favorites.FavoriteManagerImpl +import com.rusili.superstreet.database.favorites.model.FavoriteModelMapper import com.rusili.superstreet.di.AppModule +import dagger.Binds import dagger.Module import dagger.Provides private const val DATABASE_NAME = "SuperstreetDb" @Module(includes = [AppModule::class]) -class DatabaseModule { +abstract class DatabaseModule { - @Provides - fun provideFavoriteDao(database: AppDatabase): FavoriteDao = - database.favoriteDao() + @Module + companion object { + @JvmStatic + @Provides + fun provideFavoriteModelMapper(): FavoriteModelMapper = + FavoriteModelMapper() - @Provides - fun provideFavoriteModelMapper(): FavoriteModelMapper = - FavoriteModelMapper() + @JvmStatic + @Provides + protected fun provideFavoriteDao(database: AppDatabase): FavoriteDao = + database.favoriteDao() - @Provides - protected fun provideRoomDatabase(context: Context): AppDatabase = - Room.databaseBuilder( - context, - AppDatabase::class.java, - DATABASE_NAME - ).build() + @JvmStatic + @Provides + protected fun provideRoomDatabase(context: Context): AppDatabase = + Room.databaseBuilder( + context, + AppDatabase::class.java, + DATABASE_NAME + ).build() + } + + @Binds + abstract fun provideFavoriteManager(manager: FavoriteManagerImpl): FavoriteManager } diff --git a/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteDao.kt b/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteDao.kt index cbd2f2c..7f88302 100644 --- a/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteDao.kt +++ b/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteDao.kt @@ -4,6 +4,7 @@ import androidx.room.Dao import androidx.room.Delete import androidx.room.Insert import androidx.room.Query +import com.rusili.superstreet.database.favorites.model.FavoriteEntity import io.reactivex.Completable import io.reactivex.Single diff --git a/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteManager.kt b/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteManager.kt new file mode 100644 index 0000000..a39fd68 --- /dev/null +++ b/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteManager.kt @@ -0,0 +1,15 @@ +package com.rusili.superstreet.database.favorites + +import com.rusili.superstreet.database.favorites.model.FavoriteEntity +import io.reactivex.Completable +import io.reactivex.Single + +interface FavoriteManager { + + fun toggleFavorite( + entity: FavoriteEntity, + enabled: Boolean + ): Completable + + fun getAllFavorites(): Single> +} diff --git a/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteManagerImpl.kt b/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteManagerImpl.kt new file mode 100644 index 0000000..1491827 --- /dev/null +++ b/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteManagerImpl.kt @@ -0,0 +1,21 @@ +package com.rusili.superstreet.database.favorites + +import com.rusili.superstreet.database.favorites.model.FavoriteEntity +import io.reactivex.Completable +import io.reactivex.Single +import javax.inject.Inject + +class FavoriteManagerImpl @Inject constructor(private val dao: FavoriteDao) : FavoriteManager { + + override fun toggleFavorite( + entity: FavoriteEntity, + enabled: Boolean + ): Completable = + when (enabled) { + true -> dao.removeFavorite(entity) + false -> dao.addFavorite(entity) + } + + override fun getAllFavorites(): Single> = + dao.getAllFavorites() +} diff --git a/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteEntity.kt b/app/src/main/java/com/rusili/superstreet/database/favorites/model/FavoriteEntity.kt similarity index 89% rename from app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteEntity.kt rename to app/src/main/java/com/rusili/superstreet/database/favorites/model/FavoriteEntity.kt index dd5a54e..8308abf 100644 --- a/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteEntity.kt +++ b/app/src/main/java/com/rusili/superstreet/database/favorites/model/FavoriteEntity.kt @@ -1,4 +1,4 @@ -package com.rusili.superstreet.database.favorites +package com.rusili.superstreet.database.favorites.model import androidx.room.Entity import androidx.room.PrimaryKey diff --git a/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteModelMapper.kt b/app/src/main/java/com/rusili/superstreet/database/favorites/model/FavoriteModelMapper.kt similarity index 97% rename from app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteModelMapper.kt rename to app/src/main/java/com/rusili/superstreet/database/favorites/model/FavoriteModelMapper.kt index 07c4267..b551109 100644 --- a/app/src/main/java/com/rusili/superstreet/database/favorites/FavoriteModelMapper.kt +++ b/app/src/main/java/com/rusili/superstreet/database/favorites/model/FavoriteModelMapper.kt @@ -1,4 +1,4 @@ -package com.rusili.superstreet.database.favorites +package com.rusili.superstreet.database.favorites.model import com.rusili.superstreet.common.models.Flag import com.rusili.superstreet.common.models.Footer diff --git a/app/src/main/java/com/rusili/superstreet/favoritelist/data/FavoriteListRepositoryImpl.kt b/app/src/main/java/com/rusili/superstreet/favoritelist/data/FavoriteListRepositoryImpl.kt index 7495cd6..85069e2 100644 --- a/app/src/main/java/com/rusili/superstreet/favoritelist/data/FavoriteListRepositoryImpl.kt +++ b/app/src/main/java/com/rusili/superstreet/favoritelist/data/FavoriteListRepositoryImpl.kt @@ -1,18 +1,17 @@ package com.rusili.superstreet.favoritelist.data import com.rusili.superstreet.database.favorites.FavoriteDao -import com.rusili.superstreet.database.favorites.FavoriteEntity +import com.rusili.superstreet.database.favorites.FavoriteManager +import com.rusili.superstreet.database.favorites.model.FavoriteEntity import com.rusili.superstreet.favoritelist.domain.FavoriteListRepository import io.reactivex.Completable import io.reactivex.Single import javax.inject.Inject -class FavoriteListRepositoryImpl @Inject constructor(private val dao: FavoriteDao) : - FavoriteListRepository { - +class FavoriteListRepositoryImpl @Inject constructor(private val manager: FavoriteManager) : FavoriteListRepository { override fun getAllFavorites(): Single> = - dao.getAllFavorites() + manager.getAllFavorites() override fun removeFavorite(entity: FavoriteEntity): Completable = - dao.removeFavorite(entity) + manager.toggleFavorite(entity, false) } diff --git a/app/src/main/java/com/rusili/superstreet/favoritelist/di/FavoriteListModule.kt b/app/src/main/java/com/rusili/superstreet/favoritelist/di/FavoriteListModule.kt index dc1dc10..63b17e7 100644 --- a/app/src/main/java/com/rusili/superstreet/favoritelist/di/FavoriteListModule.kt +++ b/app/src/main/java/com/rusili/superstreet/favoritelist/di/FavoriteListModule.kt @@ -1,5 +1,7 @@ package com.rusili.superstreet.favoritelist.di +import com.rusili.superstreet.database.di.DatabaseModule +import com.rusili.superstreet.di.AppModule import com.rusili.superstreet.favoritelist.data.FavoriteListRepositoryImpl import com.rusili.superstreet.favoritelist.domain.FavoriteListRepository import com.rusili.superstreet.favoritelist.domain.FavoriteListUsecaseImpl @@ -9,7 +11,7 @@ import dagger.Binds import dagger.Module import dagger.Provides -@Module +@Module(includes = [DatabaseModule::class]) abstract class FavoriteListModule() { @Module diff --git a/app/src/main/java/com/rusili/superstreet/favoritelist/domain/FavoriteListRepository.kt b/app/src/main/java/com/rusili/superstreet/favoritelist/domain/FavoriteListRepository.kt index 505d68f..6955b2f 100644 --- a/app/src/main/java/com/rusili/superstreet/favoritelist/domain/FavoriteListRepository.kt +++ b/app/src/main/java/com/rusili/superstreet/favoritelist/domain/FavoriteListRepository.kt @@ -1,6 +1,6 @@ package com.rusili.superstreet.favoritelist.domain -import com.rusili.superstreet.database.favorites.FavoriteEntity +import com.rusili.superstreet.database.favorites.model.FavoriteEntity import io.reactivex.Completable import io.reactivex.Single diff --git a/app/src/main/java/com/rusili/superstreet/favoritelist/domain/FavoriteListUsecaseImpl.kt b/app/src/main/java/com/rusili/superstreet/favoritelist/domain/FavoriteListUsecaseImpl.kt index 752b09f..868ce56 100644 --- a/app/src/main/java/com/rusili/superstreet/favoritelist/domain/FavoriteListUsecaseImpl.kt +++ b/app/src/main/java/com/rusili/superstreet/favoritelist/domain/FavoriteListUsecaseImpl.kt @@ -1,23 +1,23 @@ package com.rusili.superstreet.favoritelist.domain -import com.rusili.superstreet.database.favorites.FavoriteEntity -import com.rusili.superstreet.database.favorites.FavoriteModelMapper +import com.rusili.superstreet.database.favorites.model.FavoriteEntity +import com.rusili.superstreet.database.favorites.model.FavoriteModelMapper import com.rusili.superstreet.favoritelist.ui.FavoriteListUsecase import com.rusili.superstreet.previewlist.domain.ArticlePreviewModel import io.reactivex.Completable import io.reactivex.Single +import io.reactivex.rxkotlin.flatMapSequence import javax.inject.Inject class FavoriteListUsecaseImpl @Inject constructor( private val repository: FavoriteListRepository, private val mapper: FavoriteModelMapper -) : - FavoriteListUsecase { +) : FavoriteListUsecase { override fun getAllFavorites(): Single> = repository.getAllFavorites() .toObservable() - .flatMapIterable { favorites -> favorites } + .flatMapSequence(Iterable::asSequence) .map { favorite -> mapper.to(favorite) } .toList() diff --git a/app/src/main/java/com/rusili/superstreet/favoritelist/ui/FavoriteListFragment.kt b/app/src/main/java/com/rusili/superstreet/favoritelist/ui/FavoriteListFragment.kt index 59932c5..ba91a65 100644 --- a/app/src/main/java/com/rusili/superstreet/favoritelist/ui/FavoriteListFragment.kt +++ b/app/src/main/java/com/rusili/superstreet/favoritelist/ui/FavoriteListFragment.kt @@ -41,7 +41,7 @@ class FavoriteListFragment : BaseFragment() { } private val adapter: PreviewListAdapter by lazy { - PreviewListAdapter(::onTitleClicked, Glide.with(this), dateHelper).apply { + PreviewListAdapter(navigator, Glide.with(this), dateHelper).apply { setHasStableIds(true) } } @@ -82,14 +82,4 @@ class FavoriteListFragment : BaseFragment() { // adapter.submitList(favoriteList) fragmentListLoadingLayout.fadeAndHide() } - - private fun onTitleClicked( - view: View, - header: Header - ) { - if (view.context.isNetworkConnected()) { - Timber.d("Title: %s Href: %s", header.title.value, header.title.href) - navigator.goToArticle(view, header) - } else showError(NetworkErrorException()) - } } diff --git a/app/src/main/java/com/rusili/superstreet/favoritelist/ui/FavoriteListUsecase.kt b/app/src/main/java/com/rusili/superstreet/favoritelist/ui/FavoriteListUsecase.kt index 5d151b3..c03ecef 100644 --- a/app/src/main/java/com/rusili/superstreet/favoritelist/ui/FavoriteListUsecase.kt +++ b/app/src/main/java/com/rusili/superstreet/favoritelist/ui/FavoriteListUsecase.kt @@ -1,6 +1,6 @@ package com.rusili.superstreet.favoritelist.ui -import com.rusili.superstreet.database.favorites.FavoriteEntity +import com.rusili.superstreet.database.favorites.model.FavoriteEntity import com.rusili.superstreet.previewlist.domain.ArticlePreviewModel import io.reactivex.Completable import io.reactivex.Single diff --git a/app/src/main/java/com/rusili/superstreet/image/ImageActivity.kt b/app/src/main/java/com/rusili/superstreet/image/ImageActivity.kt index a60a4b5..6231ccd 100644 --- a/app/src/main/java/com/rusili/superstreet/image/ImageActivity.kt +++ b/app/src/main/java/com/rusili/superstreet/image/ImageActivity.kt @@ -106,6 +106,8 @@ class ImageActivity : BaseActivity() { } private fun setOnClickListeners(image: Image) { + activityImageBack.setOnClickListener { finish() } + activityImageSaveButton.setOnClickListener { saveImage( image = (activityImagePhotoView as PhotoView).drawable as BitmapDrawable, diff --git a/app/src/main/java/com/rusili/superstreet/previewlist/DateHelper.kt b/app/src/main/java/com/rusili/superstreet/previewlist/DateHelper.kt index 193d33a..82f35eb 100644 --- a/app/src/main/java/com/rusili/superstreet/previewlist/DateHelper.kt +++ b/app/src/main/java/com/rusili/superstreet/previewlist/DateHelper.kt @@ -4,6 +4,9 @@ import androidx.annotation.VisibleForTesting import java.util.Date import java.util.concurrent.TimeUnit +private const val CHAR_S = 's' +private const val STRING_AGO = " ago" + private const val STRING_TODAY = "Today" private const val STRING_YESTERDAY = "Yesterday" @@ -24,9 +27,9 @@ class DateHelper { else -> { var value = it.length.toString() + " " + it.period.name if (it.length > 1) { - value += "s" + value += CHAR_S } - value.toLowerCase() + " ago" + value.toLowerCase() + STRING_AGO } } } diff --git a/app/src/main/java/com/rusili/superstreet/previewlist/ui/PreviewListFragment.kt b/app/src/main/java/com/rusili/superstreet/previewlist/ui/PreviewListFragment.kt index 10660d7..2d1dd11 100644 --- a/app/src/main/java/com/rusili/superstreet/previewlist/ui/PreviewListFragment.kt +++ b/app/src/main/java/com/rusili/superstreet/previewlist/ui/PreviewListFragment.kt @@ -41,7 +41,7 @@ class PreviewListFragment : BaseFragment() { } private val adapter: PreviewListAdapter by lazy { - PreviewListAdapter(::onTitleClicked, Glide.with(this), dateHelper) + PreviewListAdapter(navigator, Glide.with(this), dateHelper) } companion object { @@ -86,14 +86,4 @@ class PreviewListFragment : BaseFragment() { adapter.submitList(previewList) fragmentListLoadingLayout.fadeAndHide() } - - private fun onTitleClicked( - view: View, - header: Header - ) { - if (view.context.isNetworkConnected()) { - Timber.d("Title: %s Href: %s", header.title.value, header.title.href) - navigator.goToArticle(view, header) - } else showError(NetworkErrorException()) - } } diff --git a/app/src/main/java/com/rusili/superstreet/previewlist/ui/rv/PreviewListAdapter.kt b/app/src/main/java/com/rusili/superstreet/previewlist/ui/rv/PreviewListAdapter.kt index c456fcd..92af570 100644 --- a/app/src/main/java/com/rusili/superstreet/previewlist/ui/rv/PreviewListAdapter.kt +++ b/app/src/main/java/com/rusili/superstreet/previewlist/ui/rv/PreviewListAdapter.kt @@ -5,6 +5,7 @@ import android.view.View import android.view.ViewGroup import androidx.paging.PagedListAdapter import com.bumptech.glide.RequestManager +import com.rusili.superstreet.MainNavigator import com.rusili.superstreet.R import com.rusili.superstreet.common.models.Header import com.rusili.superstreet.previewlist.DateHelper @@ -12,16 +13,16 @@ import com.rusili.superstreet.previewlist.domain.ArticlePreviewModel import com.rusili.superstreet.previewlist.domain.CardSize class PreviewListAdapter( - private val onClick: (View, Header) -> Unit, + private val navigator: MainNavigator, private val glide: RequestManager, private val dateHelper: DateHelper ) : PagedListAdapter(PreviewDiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PreviewViewHolder = when (viewType) { - CardSize.Large.viewType -> PreviewViewHolder(parent.inflate(R.layout.viewholder_preview_large), onClick, glide, dateHelper) - CardSize.Small.viewType -> PreviewViewHolder(parent.inflate(R.layout.viewholder_preview_small), onClick, glide, dateHelper) - else -> PreviewViewHolder(parent.inflate(R.layout.viewholder_preview_small), onClick, glide, dateHelper) + CardSize.Large.viewType -> PreviewViewHolder(parent.inflate(R.layout.viewholder_preview_large), navigator, glide, dateHelper) + CardSize.Small.viewType -> PreviewViewHolder(parent.inflate(R.layout.viewholder_preview_small), navigator, glide, dateHelper) + else -> PreviewViewHolder(parent.inflate(R.layout.viewholder_preview_small), navigator, glide, dateHelper) } override fun onBindViewHolder(holder: PreviewViewHolder, position: Int) { diff --git a/app/src/main/java/com/rusili/superstreet/previewlist/ui/rv/PreviewViewHolder.kt b/app/src/main/java/com/rusili/superstreet/previewlist/ui/rv/PreviewViewHolder.kt index d60a346..be086f8 100644 --- a/app/src/main/java/com/rusili/superstreet/previewlist/ui/rv/PreviewViewHolder.kt +++ b/app/src/main/java/com/rusili/superstreet/previewlist/ui/rv/PreviewViewHolder.kt @@ -5,11 +5,12 @@ import android.view.animation.AnimationUtils import androidx.core.view.ViewCompat import com.bumptech.glide.RequestManager import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import com.rusili.superstreet.MainNavigator import com.rusili.superstreet.R import com.rusili.superstreet.common.base.BaseViewHolder -import com.rusili.superstreet.common.models.Header import com.rusili.superstreet.common.models.header.HeaderImage.Companion.HEADER_IMAGE_HEIGHT import com.rusili.superstreet.common.models.header.HeaderImage.Companion.HEADER_IMAGE_WIDTH +import com.rusili.superstreet.common.ui.actions.HasActionsView import com.rusili.superstreet.previewlist.DateHelper import com.rusili.superstreet.previewlist.domain.ArticlePreviewModel import kotlinx.android.extensions.LayoutContainer @@ -20,20 +21,33 @@ private val crossFadeTransition = DrawableTransitionOptions.withCrossFade() class PreviewViewHolder( override val containerView: View, - private val onClick: (View, Header) -> Unit, + private val navigator: MainNavigator, private val glide: RequestManager, private val dateHelper: DateHelper -) : BaseViewHolder(containerView), LayoutContainer { +) : BaseViewHolder(containerView), LayoutContainer, HasActionsView { override fun bind(model: ArticlePreviewModel) { if (adapterPosition > 2) { animate() } - setupImage(model) + setImage(model) setText(model) + setActionsView(model.header.title.href) - containerView.setOnClickListener { onClick(previewThumbnail, model.header) } + previewBackground.setOnClickListener { + navigator.goToArticle( + view = previewThumbnail, + header = model.header + ) + } + } + + override fun setActionsView(link: String) { + previewActionsView.apply { + setShareLink(link) + setFavoriteAction() + } } private fun animate() { @@ -45,7 +59,7 @@ class PreviewViewHolder( ) } - private fun setupImage(model: ArticlePreviewModel) { + private fun setImage(model: ArticlePreviewModel) { ViewCompat.setTransitionName(previewThumbnail, model.header.headerImage.title) ViewCompat.setTransitionName(previewLayout, model.header.headerImage.getDefaultSizeUrl()) diff --git a/app/src/main/res/drawable/ic_bookmark.xml b/app/src/main/res/drawable/ic_bookmark.xml new file mode 100644 index 0000000..bcd12cf --- /dev/null +++ b/app/src/main/res/drawable/ic_bookmark.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_download.xml b/app/src/main/res/drawable/ic_download.xml new file mode 100644 index 0000000..fbfe42f --- /dev/null +++ b/app/src/main/res/drawable/ic_download.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_file_download_white_24dp.xml b/app/src/main/res/drawable/ic_file_download_white_24dp.xml deleted file mode 100644 index 9f98049..0000000 --- a/app/src/main/res/drawable/ic_file_download_white_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_left_arrow.xml b/app/src/main/res/drawable/ic_left_arrow.xml new file mode 100644 index 0000000..0178b26 --- /dev/null +++ b/app/src/main/res/drawable/ic_left_arrow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml new file mode 100644 index 0000000..7330501 --- /dev/null +++ b/app/src/main/res/drawable/ic_share.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_share_white_24dp.xml b/app/src/main/res/drawable/ic_share_white_24dp.xml deleted file mode 100644 index 1447d9b..0000000 --- a/app/src/main/res/drawable/ic_share_white_24dp.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/layout-land/activity_image.xml b/app/src/main/res/layout-land/activity_image.xml index 9add86a..858451d 100644 --- a/app/src/main/res/layout-land/activity_image.xml +++ b/app/src/main/res/layout-land/activity_image.xml @@ -29,6 +29,18 @@ tools:src="@tools:sample/backgrounds/scenic" /> + + - diff --git a/app/src/main/res/layout-sw600dp/viewholder_preview_large.xml b/app/src/main/res/layout-sw600dp/viewholder_preview_large.xml index 555d150..7fa983d 100644 --- a/app/src/main/res/layout-sw600dp/viewholder_preview_large.xml +++ b/app/src/main/res/layout-sw600dp/viewholder_preview_large.xml @@ -53,4 +53,13 @@ app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@id/previewTitle" tools:text="1 week ago" /> + + + diff --git a/app/src/main/res/layout-sw600dp/viewholder_preview_small.xml b/app/src/main/res/layout-sw600dp/viewholder_preview_small.xml index 774fd19..e54314c 100644 --- a/app/src/main/res/layout-sw600dp/viewholder_preview_small.xml +++ b/app/src/main/res/layout-sw600dp/viewholder_preview_small.xml @@ -51,4 +51,13 @@ app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@id/previewTitle" tools:text="1 week ago" /> + + + diff --git a/app/src/main/res/layout/activity_article.xml b/app/src/main/res/layout/activity_article.xml index fb4fdb2..c9c65d8 100644 --- a/app/src/main/res/layout/activity_article.xml +++ b/app/src/main/res/layout/activity_article.xml @@ -45,6 +45,14 @@ app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" /> + + + + - diff --git a/app/src/main/res/layout/view_actions.xml b/app/src/main/res/layout/view_actions.xml new file mode 100644 index 0000000..d9ed7c6 --- /dev/null +++ b/app/src/main/res/layout/view_actions.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/layout/viewholder_preview_large.xml b/app/src/main/res/layout/viewholder_preview_large.xml index 42d2bd8..341ca1d 100644 --- a/app/src/main/res/layout/viewholder_preview_large.xml +++ b/app/src/main/res/layout/viewholder_preview_large.xml @@ -7,6 +7,15 @@ android:layout_height="wrap_content" android:paddingBottom="4dp"> + + + + + diff --git a/app/src/main/res/layout/viewholder_preview_small.xml b/app/src/main/res/layout/viewholder_preview_small.xml index 370f342..3d3bb0c 100644 --- a/app/src/main/res/layout/viewholder_preview_small.xml +++ b/app/src/main/res/layout/viewholder_preview_small.xml @@ -7,6 +7,15 @@ android:layout_height="wrap_content" android:paddingBottom="4dp"> + + + + + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..d6481b4 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 0d6af0a..22c631f 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -6,6 +6,12 @@ 64dp 2dp 64dp + 10dp + 14dp + 20dp + 32dp + 48dp + 64dp 4dp diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 0447f32..a130f14 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,5 +1,28 @@ + + + + + + + + +