Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add artist gallery feature module #8

Merged
merged 3 commits into from
Nov 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ android {
'proguard-rules-retrofit.pro'
}
}
dynamicFeatures = [':painting']

dynamicFeatures = [':artist', ':painting']
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@ import okhttp3.Interceptor
import okhttp3.Response
import retrofit2.http.GET
import retrofit2.http.Path
import retrofit2.http.Query

interface HarvardArtMuseumApi {

@GET("object?$PAINTINGS_&$WITH_IMAGES_&$WITH_ARTIST_&$INC_FIELDS")
fun gallery(): Deferred<ApiPaintingsResponse>

@GET("object?$PAINTINGS_&$WITH_IMAGES_&$INC_FIELDS")
fun artistGallery(@Query("person") artistId: String): Deferred<ApiPaintingsResponse>

@GET("object/{object_id}?$INC_FIELDS")
fun painting(@Path("object_id") id: String): Deferred<ApiRecord>

Expand Down Expand Up @@ -49,7 +53,7 @@ data class ApiInfo(
@Json(name = "totalrecords") val totalRecords: Int,
@Json(name = "pages") val pages: Int,
@Json(name = "page") val page: Int,
@Json(name = "next") val next: String
@Json(name = "next") val next: String?
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for single page, this is null

)

@JsonClass(generateAdapter = true)
Expand Down
39 changes: 26 additions & 13 deletions app/src/main/java/com/ataulm/artcollector/Navigation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,33 @@ package com.ataulm.artcollector
import android.content.Intent
import android.net.Uri

enum class Navigation(private val semiQualifiedActivityName: String) {
private const val SCHEME = "https"
private const val AUTHORITY = "art-collector.ataulm.com"
private const val ARTIST_GALLERY = "${BuildConfig.APPLICATION_ID}.artist.ui.ArtistActivity"
private const val PAINTING = "${BuildConfig.APPLICATION_ID}.painting.ui.PaintingActivity"

GALLERY("gallery.ui.GalleryActivity"),
PAINTING("painting.ui.PaintingActivity");
fun artistGalleryIntent(artistId: String): Intent {
val uri = Uri.Builder()
.scheme(SCHEME)
.authority(AUTHORITY)
.path(artistId)
.build()

fun viewIntent(artistId: String, paintingId: String): Intent {
val uri = Uri.Builder()
.scheme("https://")
.authority("art-collector.ataulm.com")
.path("$artistId/$paintingId")
.build()
return intent(uri, ARTIST_GALLERY)
}

fun paintingIntent(artistId: String, paintingId: String): Intent {
val uri = Uri.Builder()
.scheme(SCHEME)
.authority(AUTHORITY)
.path("$artistId/$paintingId")
.build()

return intent(uri, PAINTING)
}

return Intent(Intent.ACTION_VIEW)
.setClassName(BuildConfig.APPLICATION_ID, "${BuildConfig.APPLICATION_ID}.$semiQualifiedActivityName")
.setData(uri)
}
private fun intent(uri: Uri, componentName: String): Intent {
return Intent(Intent.ACTION_VIEW)
.setClassName(BuildConfig.APPLICATION_ID, componentName)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should just be able to ditch this line when we add the correct intent filters in the manifest 🤔

.setData(uri)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.GridLayoutManager
import com.ataulm.artcollector.DataObserver
import com.ataulm.artcollector.EventObserver
import com.ataulm.artcollector.Navigation
import com.ataulm.artcollector.R
import com.ataulm.artcollector.artistGalleryIntent
import com.ataulm.artcollector.gallery.domain.Gallery
import com.ataulm.artcollector.gallery.injectDependencies
import com.ataulm.artcollector.paintingIntent
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_gallery.*
import javax.inject.Inject
Expand All @@ -26,19 +27,25 @@ class GalleryActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_gallery)

val galleryAdapter = GalleryAdapter(picasso) { viewModel.onClick(it) }
recyclerView.apply {
adapter = galleryAdapter
layoutManager = GridLayoutManager(this@GalleryActivity, 2)
}
val adapter = GalleryAdapter(
picasso,
{ viewModel.onClick(it) },
{ viewModel.onClickArtist(it) }
)

recyclerView.adapter = adapter
recyclerView.layoutManager = GridLayoutManager(this@GalleryActivity, 2)

viewModel.gallery.observe(this, DataObserver<Gallery> { gallery ->
galleryAdapter.submitList(gallery)
adapter.submitList(gallery)
})

viewModel.events.observe(this, EventObserver {
val painting = it.painting
startActivity(Navigation.PAINTING.viewIntent(painting.artist.id, painting.id))
val intent = when (it) {
is NavigateToArtistGallery -> artistGalleryIntent(it.artist.id)
is NavigateToPainting -> paintingIntent(it.painting.artist.id, it.painting.id)
}
startActivity(intent)
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.ataulm.artcollector.R
import com.ataulm.artcollector.gallery.domain.Artist
import com.ataulm.artcollector.gallery.domain.Painting
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.itemview_painting.view.*

internal class GalleryAdapter constructor(
private val picasso: Picasso,
private val onClick: (Painting) -> Unit
private val onClick: (Painting) -> Unit,
private val onClickArtist: (Artist) -> Unit
) : ListAdapter<Painting, GalleryAdapter.PaintingViewHolder>(PaintingDiffer) {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PaintingViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.itemview_painting, parent, false)
return PaintingViewHolder(picasso, onClick, view)
return PaintingViewHolder(picasso, onClick, onClickArtist, view)
}

override fun onBindViewHolder(viewHolder: PaintingViewHolder, position: Int) = viewHolder.bind(getItem(position))
Expand All @@ -31,12 +33,14 @@ internal class GalleryAdapter constructor(
internal class PaintingViewHolder(
private val picasso: Picasso,
private val onClick: (Painting) -> Unit,
private val onClickArtist: (Artist) -> Unit,
view: View
) : RecyclerView.ViewHolder(view) {

fun bind(item: Painting) {
itemView.setOnClickListener { onClick(item) }
itemView.artistTextView.text = item.artist.name
itemView.artistTextView.setOnClickListener { onClickArtist(item.artist) }
picasso.load(item.imageUrl).into(itemView.imageView)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel
import com.ataulm.artcollector.Event
import com.ataulm.artcollector.gallery.domain.Artist
import com.ataulm.artcollector.gallery.domain.Gallery
import com.ataulm.artcollector.gallery.domain.GetGalleryUseCase
import com.ataulm.artcollector.gallery.domain.Painting
Expand All @@ -21,8 +22,8 @@ internal class PaintingsViewModel @Inject constructor(
private val _gallery = MutableLiveData<Gallery>()
val gallery: LiveData<Gallery> = _gallery

private val _events = MutableLiveData<Event<NavigateToPainting>>()
val events: LiveData<Event<NavigateToPainting>>
private val _events = MutableLiveData<Event<NavigateCommand>>()
val events: LiveData<Event<NavigateCommand>>
get() = _events

private val parentJob = Job()
Expand All @@ -39,10 +40,16 @@ internal class PaintingsViewModel @Inject constructor(
_events.value = Event(NavigateToPainting(painting))
}

fun onClickArtist(artist: Artist) {
_events.value = Event(NavigateToArtistGallery(artist))
}

override fun onCleared() {
super.onCleared()
parentJob.cancel()
}
}

internal data class NavigateToPainting(val painting: Painting)
internal sealed class NavigateCommand
internal data class NavigateToPainting(val painting: Painting) : NavigateCommand()
internal data class NavigateToArtistGallery(val artist: Artist) : NavigateCommand()
4 changes: 3 additions & 1 deletion app/src/main/res/layout/itemview_painting.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@android:color/white"
android:gravity="center_vertical" />
android:gravity="center_vertical"
android:minHeight="48dp"
android:padding="8dp" />

</FrameLayout>
14 changes: 14 additions & 0 deletions artist/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apply plugin: 'com.android.dynamic-feature'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion versions.androidSdk.compile
}

dependencies {
implementation project(':app')

kapt libraries.daggerCompiler
}
19 changes: 19 additions & 0 deletions artist/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
package="com.ataulm.artcollector.artist">

<dist:module
dist:onDemand="false"
dist:title="@string/title_artist">
<dist:fusing dist:include="true" />
</dist:module>

<application>
<activity android:name=".ui.ArtistActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.ataulm.artcollector.artist

import com.ataulm.artcollector.ApplicationComponent
import com.ataulm.artcollector.ArtCollectorApplication
import com.ataulm.artcollector.artist.domain.ArtistId
import com.ataulm.artcollector.artist.ui.ArtistActivity
import dagger.BindsInstance
import dagger.Component

@Component(modules = [ArtistModule::class], dependencies = [ApplicationComponent::class])
internal interface ArtistComponent {

fun inject(activity: ArtistActivity)

@Component.Builder
interface Builder {

@BindsInstance
fun activity(activity: ArtistActivity): Builder

fun withParent(component: ApplicationComponent): Builder

@BindsInstance
fun with(artistId: ArtistId): Builder

fun build(): ArtistComponent
}
}

internal fun ArtistActivity.injectDependencies(artistId: ArtistId) = DaggerArtistComponent.builder()
.withParent(ArtCollectorApplication.component(this))
.activity(this)
.with(artistId)
.build()
.inject(this)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.ataulm.artcollector.artist

import android.arch.lifecycle.ViewModelProviders
import com.ataulm.artcollector.artist.data.AndroidArtistRepository
import com.ataulm.artcollector.artist.domain.ArtistRepository
import com.ataulm.artcollector.artist.ui.ArtistActivity
import com.ataulm.artcollector.artist.ui.ArtistViewModel
import com.ataulm.artcollector.artist.ui.ArtistViewModelFactory
import dagger.Module
import dagger.Provides

@Module
internal object ArtistModule {

@JvmStatic
@Provides
fun artistRepository(artistRepository: AndroidArtistRepository): ArtistRepository {
return artistRepository
}

@JvmStatic
@Provides
fun viewModel(activity: ArtistActivity, viewModelFactory: ArtistViewModelFactory) =
ViewModelProviders.of(activity, viewModelFactory).get(ArtistViewModel::class.java)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.ataulm.artcollector.artist.data

import com.ataulm.artcollector.ApiRecord
import com.ataulm.artcollector.HarvardArtMuseumApi
import com.ataulm.artcollector.artist.domain.Artist
import com.ataulm.artcollector.artist.domain.ArtistId
import com.ataulm.artcollector.artist.domain.ArtistRepository
import com.ataulm.artcollector.artist.domain.Gallery
import com.ataulm.artcollector.artist.domain.Painting
import javax.inject.Inject

internal class AndroidArtistRepository @Inject constructor(
private val harvardArtMuseumApi: HarvardArtMuseumApi,
private val artistId: ArtistId
) : ArtistRepository {

override suspend fun artistGallery(): Gallery {
val paintings = harvardArtMuseumApi.artistGallery(artistId.value).await().records
.map { it.toPainting() }
return Gallery(paintings)
}

private fun ApiRecord.toPainting(): Painting {
val apiPerson = people.first()
return Painting(
id.toString(),
title,
description,
primaryImageUrl,
Artist(apiPerson.personId.toString(), apiPerson.name)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we probably don't need the Artist in this module - it'll be the same for each one I think

)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ataulm.artcollector.artist.domain

internal interface ArtistRepository {

suspend fun artistGallery(): Gallery
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ataulm.artcollector.artist.domain

import javax.inject.Inject

internal class GetArtistGalleryUseCase @Inject constructor(
private val repository: ArtistRepository
) {

suspend operator fun invoke() = repository.artistGallery()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ataulm.artcollector.artist.domain

internal class Gallery(collection: Collection<Painting>) : ArrayList<Painting>(collection)

internal data class Painting(
val id: String,
val title: String,
val description: String?,
val imageUrl: String?, // nullable because https://github.com/harvardartmuseums/api-docs/issues/6
val artist: Artist
)

internal data class Artist(val id: String, val name: String)

internal data class ArtistId(val value: String)
Loading