├── README.md └── devcamp2018 ├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── psato │ │ └── devcamp │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── psato │ │ │ └── devcamp │ │ │ ├── data │ │ │ ├── entity │ │ │ │ ├── Show.java │ │ │ │ ├── ShowIds.java │ │ │ │ ├── ShowInfo.java │ │ │ │ ├── ShowRating.java │ │ │ │ └── ShowResponse.java │ │ │ ├── remote │ │ │ │ ├── APIConstants.java │ │ │ │ └── TraktAPI.java │ │ │ └── repository │ │ │ │ ├── resource │ │ │ │ ├── ResourceRepository.java │ │ │ │ └── ResourceRepositoryImpl.java │ │ │ │ └── show │ │ │ │ ├── ShowRepository.java │ │ │ │ └── ShowRepositoryImpl.java │ │ │ ├── di │ │ │ ├── FragmentScoped.java │ │ │ ├── component │ │ │ │ ├── ApplicationComponent.java │ │ │ │ ├── BaseFragmentComponent.java │ │ │ │ └── ViewModelSubComponent.java │ │ │ └── module │ │ │ │ ├── ApplicationModule.java │ │ │ │ ├── NetworkModule.java │ │ │ │ ├── ResourceRepositoryModule.java │ │ │ │ └── ShowRepositoryModule.java │ │ │ ├── infrastructure │ │ │ ├── Constants.java │ │ │ ├── DevCampApplication.java │ │ │ └── ProjectViewModelFactory.java │ │ │ ├── interactor │ │ │ ├── rx │ │ │ │ └── RxSchedulers.java │ │ │ └── usecase │ │ │ │ ├── UseCase.java │ │ │ │ └── show │ │ │ │ └── SearchShows.kt │ │ │ └── presentation │ │ │ ├── MVVM │ │ │ ├── QueryActivity.java │ │ │ ├── QueryFragment.java │ │ │ └── QueryViewModelArc.java │ │ │ ├── base │ │ │ └── BaseFragment.java │ │ │ ├── bindadapter │ │ │ ├── EditTextBindingAdapter.java │ │ │ └── ViewBinderAdapter.java │ │ │ └── home │ │ │ ├── HomeActivity.java │ │ │ ├── HomeFragment.java │ │ │ └── HomeFragmentViewModel.java │ └── res │ │ ├── drawable │ │ └── ic_search_grey_24dp.xml │ │ ├── layout │ │ ├── activity_list.xml │ │ ├── fragment_home.xml │ │ ├── fragment_query_mvp.xml │ │ └── fragment_query_mvvm.xml │ │ ├── mipmap-hdpi │ │ └── logo.png │ │ ├── mipmap-mdpi │ │ └── logo.png │ │ ├── mipmap-xhdpi │ │ └── logo.png │ │ ├── mipmap-xxhdpi │ │ └── logo.png │ │ ├── mipmap-xxxhdpi │ │ └── logo.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── psato │ └── devcamp │ └── presentation │ └── showlist │ └── ShowListViewModelTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | Repositório com o código apresentado do devcamp 2018. 2 | -------------------------------------------------------------------------------- /devcamp2018/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | /.idea/sonarlint/ 11 | /.idea/vcs.xml 12 | /.idea/caches/* -------------------------------------------------------------------------------- /devcamp2018/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /devcamp2018/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /devcamp2018/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /devcamp2018/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /devcamp2018/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /devcamp2018/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /devcamp2018/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion 27 6 | defaultConfig { 7 | vectorDrawables.useSupportLibrary = true 8 | applicationId "com.example.psato.devcamp2018" 9 | minSdkVersion 15 10 | targetSdkVersion 27 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | dataBinding { 22 | enabled = true 23 | } 24 | compileOptions { 25 | targetCompatibility 1.8 26 | sourceCompatibility 1.8 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation 'com.android.support:appcompat-v7:27.1.1' 33 | implementation 'com.android.support:recyclerview-v7:27.1.1' 34 | implementation 'com.android.support:design:27.1.1' 35 | testImplementation 'junit:junit:4.12' 36 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 37 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 38 | implementation 'io.reactivex.rxjava2:rxandroid:2.0.2' 39 | implementation 'io.reactivex.rxjava2:rxjava:2.1.16' 40 | annotationProcessor "com.google.dagger:dagger-compiler:2.11" 41 | implementation "com.google.dagger:dagger:2.11" 42 | compileOnly "javax.annotation:jsr250-api:1.0" 43 | implementation 'com.squareup.retrofit2:retrofit:2.4.0' 44 | implementation 'com.squareup.retrofit2:converter-gson:2.0.2' 45 | implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' 46 | implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' 47 | implementation "android.arch.lifecycle:extensions:1.1.1" 48 | annotationProcessor "android.arch.lifecycle:compiler:1.1.1" 49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 50 | 51 | } 52 | repositories { 53 | mavenCentral() 54 | } 55 | -------------------------------------------------------------------------------- /devcamp2018/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /devcamp2018/app/src/androidTest/java/com/psato/devcamp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.psato.devcamp", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/entity/Show.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.entity; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * Created by psato on 29/10/16. 7 | */ 8 | 9 | public class Show { 10 | 11 | @SerializedName("title") 12 | private String mTitle; 13 | @SerializedName("year") 14 | private Integer mYear; 15 | @SerializedName("ids") 16 | private ShowIds mIds; 17 | 18 | 19 | public String getTitle() { 20 | return mTitle; 21 | } 22 | 23 | public Integer getYear() { 24 | return mYear; 25 | } 26 | 27 | public ShowIds getIds() { 28 | return mIds; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/entity/ShowIds.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.entity; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * Created by psato on 29/10/16. 7 | */ 8 | 9 | public class ShowIds { 10 | @SerializedName("trakt") 11 | private String trakt; 12 | @SerializedName("slug") 13 | private String mSlug; 14 | @SerializedName("tvdb") 15 | private String mTvdb; 16 | @SerializedName("imdb") 17 | private String mImdb; 18 | @SerializedName("tmdb") 19 | private String mTmdb; 20 | @SerializedName("tvrage") 21 | private String mTvrage; 22 | 23 | public String getTrakt() { 24 | return trakt; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/entity/ShowInfo.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.entity; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * Created by psato on 29/10/16. 7 | */ 8 | 9 | public class ShowInfo { 10 | 11 | @SerializedName("type") 12 | private String mType; 13 | @SerializedName("score") 14 | private String mScore; 15 | @SerializedName("show") 16 | private Show mShow; 17 | 18 | public String getType() { 19 | return mType; 20 | } 21 | 22 | public String getScore() { 23 | return mScore; 24 | } 25 | 26 | public Show getShow() { 27 | return mShow; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/entity/ShowRating.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.entity; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class ShowRating { 6 | @SerializedName("rating") 7 | private double rating; 8 | 9 | public double getRating() { 10 | return rating; 11 | } 12 | 13 | public void setRating(double rating) { 14 | this.rating = rating; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/entity/ShowResponse.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.entity; 2 | 3 | public class ShowResponse { 4 | private String name; 5 | private double rating; 6 | 7 | public ShowResponse(String name, double rating) { 8 | this.name = name; 9 | this.rating = rating; 10 | } 11 | 12 | public String getName() { 13 | return name; 14 | } 15 | 16 | public double getRating() { 17 | return rating; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/remote/APIConstants.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.remote; 2 | 3 | /** 4 | * Created by psato on 29/10/16. 5 | */ 6 | 7 | public class APIConstants { 8 | public static final String CLIENT_ID = "9bd25dc2491eb7c065d3913257913b8e249067f61fa6d0d09eb09b904925362f"; 9 | public static final String BASE_URL = "https://api-v2launch.trakt.tv/"; 10 | public static final String HEADER_CLIENT_ID = "trakt-api-key"; 11 | public static final String HEADER_API_VERSION = "trakt-api-version"; 12 | } 13 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/remote/TraktAPI.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.remote; 2 | 3 | import com.psato.devcamp.data.entity.ShowInfo; 4 | import com.psato.devcamp.data.entity.ShowRating; 5 | 6 | import java.util.List; 7 | 8 | import io.reactivex.Single; 9 | import retrofit2.http.GET; 10 | import retrofit2.http.Headers; 11 | import retrofit2.http.Path; 12 | import retrofit2.http.Query; 13 | 14 | /** 15 | * Created by psato on 29/10/16. 16 | */ 17 | 18 | public interface TraktAPI { 19 | @Headers({APIConstants.HEADER_API_VERSION + ": 2", 20 | APIConstants.HEADER_CLIENT_ID + ": " + APIConstants.CLIENT_ID}) 21 | @GET("search/show") 22 | Single> searchForShows(@Query("query") String query, @Query("limit") int limit); 23 | 24 | 25 | @Headers({APIConstants.HEADER_API_VERSION + ": 2", 26 | APIConstants.HEADER_CLIENT_ID + ": " + APIConstants.CLIENT_ID}) 27 | @GET("shows/{id}/ratings") 28 | Single getShowRating(@Path("id") String id); 29 | } 30 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/repository/resource/ResourceRepository.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.repository.resource; 2 | 3 | /** 4 | * Created by psato on 29/06/16. 5 | */ 6 | 7 | public interface ResourceRepository { 8 | String getNotFoundShow(); 9 | } 10 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/repository/resource/ResourceRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.repository.resource; 2 | 3 | import com.psato.devcamp.infrastructure.DevCampApplication; 4 | import com.psato.devcamp.R; 5 | 6 | /** 7 | * Created by psato on 29/06/16. 8 | */ 9 | 10 | public class ResourceRepositoryImpl implements ResourceRepository { 11 | DevCampApplication devCampApplication; 12 | 13 | public ResourceRepositoryImpl(DevCampApplication application) { 14 | devCampApplication = application; 15 | } 16 | 17 | 18 | @Override 19 | public String getNotFoundShow() { 20 | return devCampApplication.getResources().getString(R.string.error); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/repository/show/ShowRepository.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.repository.show; 2 | 3 | import com.psato.devcamp.data.entity.ShowInfo; 4 | import com.psato.devcamp.data.entity.ShowRating; 5 | 6 | import java.util.List; 7 | 8 | import io.reactivex.Single; 9 | 10 | /** 11 | * Created by psato on 29/06/16. 12 | */ 13 | 14 | public interface ShowRepository { 15 | Single> searchShow(String query); 16 | 17 | Single showRating(String id); 18 | } 19 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/data/repository/show/ShowRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.data.repository.show; 2 | 3 | import com.psato.devcamp.data.entity.ShowInfo; 4 | import com.psato.devcamp.data.entity.ShowRating; 5 | import com.psato.devcamp.data.remote.TraktAPI; 6 | 7 | import java.util.List; 8 | 9 | import io.reactivex.Single; 10 | 11 | /** 12 | * Created by psato on 29/06/16. 13 | */ 14 | 15 | public class ShowRepositoryImpl implements ShowRepository { 16 | private TraktAPI traktAPI; 17 | 18 | public ShowRepositoryImpl(TraktAPI traktAPI) { 19 | this.traktAPI = traktAPI; 20 | } 21 | 22 | @Override 23 | public Single> searchShow(final String query) { 24 | return traktAPI.searchForShows(query, 200); 25 | } 26 | 27 | @Override 28 | public Single showRating(String id) { 29 | return traktAPI.getShowRating(id); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/di/FragmentScoped.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.di; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | import javax.inject.Scope; 8 | 9 | /** 10 | * Created by athila on 31/05/16. 11 | */ 12 | 13 | @Scope 14 | @Documented 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface FragmentScoped { 17 | } 18 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/di/component/ApplicationComponent.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.di.component; 2 | 3 | import com.psato.devcamp.data.repository.resource.ResourceRepository; 4 | import com.psato.devcamp.data.repository.show.ShowRepository; 5 | import com.psato.devcamp.di.module.ApplicationModule; 6 | import com.psato.devcamp.di.module.NetworkModule; 7 | import com.psato.devcamp.di.module.ResourceRepositoryModule; 8 | import com.psato.devcamp.di.module.ShowRepositoryModule; 9 | import com.psato.devcamp.infrastructure.DevCampApplication; 10 | import com.psato.devcamp.infrastructure.ProjectViewModelFactory; 11 | 12 | import javax.inject.Singleton; 13 | 14 | import dagger.Component; 15 | 16 | /** 17 | * Created by psato on 29/10/16. 18 | */ 19 | 20 | @Singleton 21 | @Component(modules = { 22 | ApplicationModule.class, 23 | NetworkModule.class, 24 | ShowRepositoryModule.class, 25 | ResourceRepositoryModule.class 26 | }) 27 | public interface ApplicationComponent { 28 | // expose to sub graphs 29 | 30 | DevCampApplication application(); 31 | 32 | ShowRepository showRepository(); 33 | 34 | ResourceRepository resourceRepository(); 35 | 36 | ProjectViewModelFactory viewModelFactory(); 37 | } 38 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/di/component/BaseFragmentComponent.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.di.component; 2 | 3 | import com.psato.devcamp.di.FragmentScoped; 4 | import com.psato.devcamp.presentation.base.BaseFragment; 5 | 6 | import dagger.Component; 7 | 8 | 9 | @FragmentScoped 10 | @Component(dependencies = {ApplicationComponent.class}) 11 | public interface BaseFragmentComponent { 12 | void inject(BaseFragment baseFragment); 13 | } 14 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/di/component/ViewModelSubComponent.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.di.component; 2 | 3 | import com.psato.devcamp.presentation.MVVM.QueryViewModelArc; 4 | import com.psato.devcamp.presentation.home.HomeFragmentViewModel; 5 | 6 | import dagger.Subcomponent; 7 | 8 | @Subcomponent 9 | public interface ViewModelSubComponent { 10 | @Subcomponent.Builder 11 | interface Builder { 12 | ViewModelSubComponent build(); 13 | } 14 | 15 | HomeFragmentViewModel homeFragmentViewModel(); 16 | QueryViewModelArc queryViewModelArc(); 17 | } 18 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/di/module/ApplicationModule.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.psato.devcamp.di.module; 17 | 18 | import com.psato.devcamp.data.remote.APIConstants; 19 | import com.psato.devcamp.di.component.ViewModelSubComponent; 20 | import com.psato.devcamp.infrastructure.DevCampApplication; 21 | import com.psato.devcamp.infrastructure.ProjectViewModelFactory; 22 | 23 | import javax.inject.Singleton; 24 | 25 | import dagger.Module; 26 | import dagger.Provides; 27 | import okhttp3.OkHttpClient; 28 | import okhttp3.logging.HttpLoggingInterceptor; 29 | import retrofit2.Retrofit; 30 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 31 | import retrofit2.converter.gson.GsonConverterFactory; 32 | 33 | /** 34 | * Dagger module that provides objects which will live during the application lifecycle. 35 | */ 36 | @Module(subcomponents = {ViewModelSubComponent.class}) 37 | public class ApplicationModule { 38 | private final DevCampApplication application; 39 | 40 | public ApplicationModule(DevCampApplication application) { 41 | this.application = application; 42 | } 43 | 44 | @Provides 45 | @Singleton 46 | DevCampApplication provideApplication() { 47 | return application; 48 | } 49 | 50 | @Provides 51 | @Singleton 52 | Retrofit provideRetrofit() { 53 | HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); 54 | interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 55 | OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build(); 56 | 57 | return new Retrofit.Builder() 58 | .client(client) 59 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 60 | .addConverterFactory(GsonConverterFactory.create()) 61 | .baseUrl(APIConstants.BASE_URL) 62 | .build(); 63 | } 64 | 65 | @Singleton 66 | @Provides 67 | ProjectViewModelFactory provideViewModelFactory( 68 | ViewModelSubComponent.Builder viewModelSubComponent) { 69 | 70 | return new ProjectViewModelFactory(viewModelSubComponent.build()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/di/module/NetworkModule.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.di.module; 2 | 3 | import com.psato.devcamp.data.remote.TraktAPI; 4 | import com.psato.devcamp.data.remote.TraktAPI; 5 | 6 | import javax.inject.Singleton; 7 | 8 | import dagger.Module; 9 | import dagger.Provides; 10 | import retrofit2.Retrofit; 11 | 12 | /** 13 | * Created by athila on 31/05/16. 14 | */ 15 | 16 | @Module 17 | public class NetworkModule { 18 | 19 | @Provides 20 | @Singleton 21 | TraktAPI provideUserApi(Retrofit retrofit) { 22 | return retrofit.create(TraktAPI.class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/di/module/ResourceRepositoryModule.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.di.module; 2 | 3 | import com.psato.devcamp.data.repository.resource.ResourceRepository; 4 | import com.psato.devcamp.data.repository.resource.ResourceRepositoryImpl; 5 | import com.psato.devcamp.infrastructure.DevCampApplication; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | 10 | @Module 11 | public class ResourceRepositoryModule { 12 | 13 | @Provides 14 | ResourceRepository provideResourceRepository(DevCampApplication application) { 15 | return new ResourceRepositoryImpl(application); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/di/module/ShowRepositoryModule.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | *

8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | *

10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.psato.devcamp.di.module; 17 | 18 | import com.psato.devcamp.data.remote.TraktAPI; 19 | import com.psato.devcamp.data.repository.show.ShowRepository; 20 | import com.psato.devcamp.data.repository.show.ShowRepositoryImpl; 21 | 22 | import dagger.Module; 23 | import dagger.Provides; 24 | 25 | @Module 26 | public class ShowRepositoryModule { 27 | 28 | @Provides 29 | ShowRepository provideUserRepository(TraktAPI traktAPI) { 30 | return new ShowRepositoryImpl(traktAPI); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/infrastructure/Constants.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.infrastructure; 2 | 3 | /** 4 | * Created by psato on 29/10/16. 5 | */ 6 | 7 | public class Constants { 8 | 9 | public static final class LoaderID{ 10 | public static final int SHOW_LIST_ID = 1; 11 | public static final int HOME_ID = 2; 12 | public static final int MVP_ID = 3; 13 | public static final int MVVM_ID = 4; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/infrastructure/DevCampApplication.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.infrastructure; 2 | 3 | import android.app.Application; 4 | 5 | import com.psato.devcamp.di.component.ApplicationComponent; 6 | import com.psato.devcamp.di.module.ApplicationModule; 7 | import com.psato.devcamp.di.component.DaggerApplicationComponent; 8 | 9 | /** 10 | * Created by psato on 29/10/16. 11 | */ 12 | 13 | public class DevCampApplication extends Application { 14 | private ApplicationComponent applicationComponent; 15 | 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | initializeInjector(); 20 | } 21 | 22 | private void initializeInjector() { 23 | applicationComponent = DaggerApplicationComponent.builder() 24 | .applicationModule(new ApplicationModule(this)) 25 | .build(); 26 | } 27 | 28 | public ApplicationComponent getApplicationComponent() { 29 | return applicationComponent; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/infrastructure/ProjectViewModelFactory.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.infrastructure; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | import android.arch.lifecycle.ViewModelProvider; 5 | import android.support.v4.util.ArrayMap; 6 | 7 | import com.psato.devcamp.di.component.ViewModelSubComponent; 8 | import com.psato.devcamp.presentation.MVVM.QueryViewModelArc; 9 | import com.psato.devcamp.presentation.home.HomeFragmentViewModel; 10 | 11 | import java.util.Map; 12 | import java.util.concurrent.Callable; 13 | 14 | import javax.inject.Inject; 15 | import javax.inject.Singleton; 16 | 17 | public class ProjectViewModelFactory implements ViewModelProvider.Factory { 18 | private final ArrayMap> creators; 19 | 20 | public ProjectViewModelFactory(ViewModelSubComponent viewModelSubComponent) { 21 | creators = new ArrayMap<>(); 22 | creators.put(HomeFragmentViewModel.class, () -> viewModelSubComponent.homeFragmentViewModel()); 23 | creators.put(QueryViewModelArc.class, () -> viewModelSubComponent.queryViewModelArc()); 24 | } 25 | 26 | @Override 27 | public T create(Class modelClass) { 28 | Callable creator = creators.get(modelClass); 29 | if (creator == null) { 30 | for (Map.Entry> entry : creators.entrySet()) { 31 | if (modelClass.isAssignableFrom(entry.getKey())) { 32 | creator = entry.getValue(); 33 | break; 34 | } 35 | } 36 | } 37 | if (creator == null) { 38 | throw new IllegalArgumentException("Unknown model class " + modelClass); 39 | } 40 | try { 41 | return (T) creator.call(); 42 | } catch (Exception e) { 43 | throw new RuntimeException(e); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/interactor/rx/RxSchedulers.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.interactor.rx; 2 | 3 | import io.reactivex.SingleTransformer; 4 | import io.reactivex.android.schedulers.AndroidSchedulers; 5 | import io.reactivex.schedulers.Schedulers; 6 | 7 | 8 | /** 9 | * Created by athila on 15/03/16. 10 | */ 11 | public class RxSchedulers { 12 | /** 13 | * Execute the operation on a new thread (from thread pool) and listen on the main thread. 14 | * It can be used for I/O operations 15 | * 16 | * @return the transformer properly configured 17 | */ 18 | public static SingleTransformer applyDefaultSchedulers() { 19 | return upstream -> upstream.subscribeOn(Schedulers.io()) 20 | .observeOn(AndroidSchedulers.mainThread()); 21 | } 22 | 23 | /** 24 | * Execute and listen the operation on the current thread. It can be used for scenarios where the 25 | * parallelism is already provided (operations executed by IntentService, for example) 26 | * 27 | * @return the transformer properly configured 28 | */ 29 | public static SingleTransformer applyImmediateSchedulers() { 30 | return upstream -> upstream.subscribeOn(Schedulers.trampoline()) 31 | .observeOn(Schedulers.trampoline()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/interactor/usecase/UseCase.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.interactor.usecase; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.psato.devcamp.interactor.rx.RxSchedulers; 6 | 7 | import io.reactivex.Single; 8 | import io.reactivex.SingleTransformer; 9 | import io.reactivex.disposables.Disposable; 10 | import io.reactivex.disposables.Disposables; 11 | import io.reactivex.functions.Consumer; 12 | 13 | public abstract class UseCase { 14 | 15 | private Disposable subscription = Disposables.empty(); 16 | 17 | 18 | protected abstract Single buildUseCaseObservable(); 19 | 20 | 21 | @SuppressWarnings("unchecked") 22 | public void execute(@NonNull Consumer onSuccess, @NonNull Consumer onError) { 23 | execute(onSuccess, onError, RxSchedulers.applyDefaultSchedulers()); 24 | } 25 | 26 | 27 | @SuppressWarnings("unchecked") 28 | public void execute(@NonNull Consumer onSuccess, @NonNull Consumer onError, SingleTransformer transformer) { 29 | // Need to use the calling chain. It does not work if we break the chain like: 30 | if (transformer != null) { 31 | subscription = buildUseCaseObservable() 32 | .compose(transformer) 33 | .subscribe(onSuccess, onError); 34 | } else { 35 | subscription = buildUseCaseObservable() 36 | .subscribe(onSuccess, onError); 37 | } 38 | } 39 | 40 | /** 41 | * Unsubscribes from current {@link rx.Subscription}. 42 | */ 43 | public void unsubscribe() { 44 | if (subscription != null && !subscription.isDisposed()) { 45 | subscription.dispose(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/interactor/usecase/show/SearchShows.kt: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.interactor.usecase.show 2 | 3 | import com.psato.devcamp.data.entity.ShowInfo 4 | import com.psato.devcamp.data.entity.ShowResponse 5 | import com.psato.devcamp.data.repository.show.ShowRepository 6 | import com.psato.devcamp.interactor.usecase.UseCase 7 | import io.reactivex.Flowable 8 | import io.reactivex.Single 9 | import io.reactivex.schedulers.Schedulers 10 | import javax.inject.Inject 11 | 12 | /** 13 | * Created by psato on 29/10/16. 14 | */ 15 | 16 | class SearchShows @Inject 17 | constructor(private val showRepository: ShowRepository) : UseCase() { 18 | var query: String? = null 19 | 20 | override fun buildUseCaseObservable(): Single> { 21 | return showRepository.searchShow(query).flatMapPublisher { Flowable.fromIterable(it) } 22 | .flatMapSingle({ showInfo: ShowInfo -> 23 | showRepository.showRating(showInfo.show.ids.trakt) 24 | .subscribeOn(Schedulers.io()) 25 | .map { rating -> 26 | ShowResponse(showInfo.show.title, rating 27 | .rating) 28 | } 29 | }, false, 4).toList() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/presentation/MVVM/QueryActivity.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.presentation.MVVM; 2 | 3 | import android.databinding.DataBindingUtil; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | import com.psato.devcamp.R; 8 | 9 | public class QueryActivity extends AppCompatActivity { 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | DataBindingUtil.setContentView(this, R.layout.activity_list); 14 | if (savedInstanceState == null) { 15 | getSupportFragmentManager() 16 | .beginTransaction() 17 | .add(R.id.fragment_content, new com.psato.devcamp.presentation.MVVM.QueryFragment()) 18 | .commit(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/presentation/MVVM/QueryFragment.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.presentation.MVVM; 2 | 3 | import android.arch.lifecycle.ViewModelProviders; 4 | import android.databinding.DataBindingUtil; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.psato.devcamp.R; 13 | import com.psato.devcamp.databinding.FragmentQueryMvvmBinding; 14 | import com.psato.devcamp.presentation.base.BaseFragment; 15 | 16 | public class QueryFragment extends BaseFragment { 17 | private FragmentQueryMvvmBinding binding; 18 | 19 | @Nullable 20 | @Override 21 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 22 | View view = inflater.inflate(R.layout.fragment_query_mvvm, container, false); 23 | binding = DataBindingUtil.bind(view); 24 | return view; 25 | } 26 | 27 | @Override 28 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 29 | super.onViewCreated(view, savedInstanceState); 30 | QueryViewModelArc queryViewModelArc = 31 | ViewModelProviders.of(this, getViewModelFactory()).get(QueryViewModelArc.class); 32 | binding.setViewModel(queryViewModelArc); 33 | binding.setLifecycleOwner(this); 34 | binding.executePendingBindings(); 35 | } 36 | 37 | @Override 38 | public void onDestroyView() { 39 | binding = null; 40 | super.onDestroyView(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/presentation/MVVM/QueryViewModelArc.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.presentation.MVVM; 2 | 3 | import android.arch.lifecycle.MutableLiveData; 4 | import android.arch.lifecycle.Observer; 5 | import android.arch.lifecycle.ViewModel; 6 | import android.text.TextUtils; 7 | import android.util.Log; 8 | import android.view.View; 9 | 10 | import com.psato.devcamp.data.entity.ShowResponse; 11 | import com.psato.devcamp.interactor.usecase.show.SearchShows; 12 | 13 | import java.util.Date; 14 | import java.util.List; 15 | 16 | import javax.inject.Inject; 17 | 18 | import io.reactivex.functions.Consumer; 19 | 20 | public class QueryViewModelArc extends ViewModel { 21 | 22 | private MutableLiveData result = new MutableLiveData<>(); 23 | 24 | private MutableLiveData query = new MutableLiveData<>(); 25 | 26 | private MutableLiveData showLoading = new MutableLiveData<>(); 27 | 28 | private MutableLiveData searchEnabled = new MutableLiveData<>(); 29 | 30 | private Observer queryObserver; 31 | 32 | private SearchShows searchShows; 33 | 34 | @Inject 35 | public QueryViewModelArc(SearchShows searchShows) { 36 | this.searchShows = searchShows; 37 | showLoading.setValue(false); 38 | searchEnabled.setValue(false); 39 | result.setValue(""); 40 | query.setValue(""); 41 | queryObserver = query -> searchEnabled.setValue(!TextUtils.isEmpty(query)); 42 | query.observeForever(queryObserver); 43 | } 44 | 45 | @Override 46 | protected void onCleared() { 47 | super.onCleared(); 48 | query.removeObserver(queryObserver); 49 | } 50 | 51 | public void onQueryClick(View view) { 52 | searchShow(query.getValue()); 53 | } 54 | 55 | private void searchShow(String value) { 56 | if (searchShows != null) { 57 | showLoading.setValue(true); 58 | searchShows.unsubscribe(); 59 | searchShows.setQuery(value); 60 | Date start = new Date(); 61 | searchShows.execute((Consumer>) title -> { 62 | Date end = new Date(); 63 | Log.e("SATO", "SATO - Time: " + (end.getTime() - start.getTime())/1000 + "s"); 64 | showLoading.setValue(false); 65 | result.setValue(title.get(0).getName()); 66 | }, throwable -> showLoading.setValue(false)); 67 | } 68 | } 69 | 70 | public MutableLiveData getSearchEnabled() { 71 | return searchEnabled; 72 | } 73 | 74 | public MutableLiveData getShowLoading() { 75 | return showLoading; 76 | } 77 | 78 | public MutableLiveData getQuery() { 79 | return query; 80 | } 81 | 82 | public MutableLiveData getResult() { 83 | return result; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/presentation/base/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.presentation.base; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.NonNull; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.Fragment; 7 | import android.view.View; 8 | 9 | import com.psato.devcamp.di.component.DaggerBaseFragmentComponent; 10 | import com.psato.devcamp.infrastructure.DevCampApplication; 11 | import com.psato.devcamp.infrastructure.ProjectViewModelFactory; 12 | 13 | import javax.inject.Inject; 14 | 15 | /** 16 | * Created by psato on 29/10/16. 17 | */ 18 | 19 | public abstract class BaseFragment extends Fragment { 20 | 21 | @Inject 22 | ProjectViewModelFactory viewModelFactory; 23 | 24 | @Override 25 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 26 | super.onViewCreated(view, savedInstanceState); 27 | DevCampApplication app = (DevCampApplication) getActivity().getApplication(); 28 | DaggerBaseFragmentComponent.builder().applicationComponent(app.getApplicationComponent()) 29 | .build().inject(this); 30 | } 31 | 32 | protected ProjectViewModelFactory getViewModelFactory(){ 33 | return viewModelFactory; 34 | } 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/presentation/bindadapter/EditTextBindingAdapter.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.presentation.bindadapter; 2 | 3 | import android.databinding.BindingAdapter; 4 | import android.view.inputmethod.EditorInfo; 5 | import android.widget.EditText; 6 | 7 | /** 8 | * Created by psato on 31/10/16. 9 | */ 10 | 11 | public class EditTextBindingAdapter { 12 | 13 | @BindingAdapter("extractUI") 14 | public static void setExtractUi(EditText editText, boolean extractUi){ 15 | if(!extractUi){ 16 | int options = editText.getImeOptions(); 17 | editText.setImeOptions(options| EditorInfo.IME_FLAG_NO_EXTRACT_UI); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/presentation/bindadapter/ViewBinderAdapter.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.presentation.bindadapter; 2 | 3 | import android.databinding.BindingAdapter; 4 | import android.view.View; 5 | 6 | public class ViewBinderAdapter { 7 | 8 | @BindingAdapter("android:visibility") 9 | public static void setVisibility(View view, Boolean visibility){ 10 | if(visibility){ 11 | view.setVisibility(View.VISIBLE); 12 | }else{ 13 | view.setVisibility(View.GONE); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/presentation/home/HomeActivity.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.presentation.home; 2 | 3 | import android.databinding.DataBindingUtil; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | 7 | import com.psato.devcamp.R; 8 | 9 | public class HomeActivity extends AppCompatActivity { 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | DataBindingUtil.setContentView(this, R.layout.activity_list); 14 | if (savedInstanceState == null) { 15 | getSupportFragmentManager() 16 | .beginTransaction() 17 | .add(R.id.fragment_content, new HomeFragment()) 18 | .commit(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/presentation/home/HomeFragment.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.presentation.home; 2 | 3 | import android.arch.lifecycle.ViewModelProviders; 4 | import android.databinding.DataBindingUtil; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | 12 | import com.psato.devcamp.R; 13 | import com.psato.devcamp.databinding.FragmentHomeBinding; 14 | import com.psato.devcamp.presentation.base.BaseFragment; 15 | 16 | public class HomeFragment extends BaseFragment { 17 | private FragmentHomeBinding binding; 18 | 19 | @Nullable 20 | @Override 21 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 22 | View view = inflater.inflate(R.layout.fragment_home, container, false); 23 | binding = DataBindingUtil.bind(view); 24 | return view; 25 | } 26 | 27 | @Override 28 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 29 | super.onViewCreated(view, savedInstanceState); 30 | HomeFragmentViewModel homeFragmentViewModel = 31 | ViewModelProviders.of(this,getViewModelFactory()).get(HomeFragmentViewModel.class); 32 | binding.setViewModel(homeFragmentViewModel); 33 | binding.setLifecycleOwner(this); 34 | binding.executePendingBindings(); 35 | } 36 | 37 | @Override 38 | public void onDestroyView() { 39 | binding = null; 40 | super.onDestroyView(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/java/com/psato/devcamp/presentation/home/HomeFragmentViewModel.java: -------------------------------------------------------------------------------- 1 | package com.psato.devcamp.presentation.home; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | import android.content.Intent; 5 | import android.view.View; 6 | 7 | import javax.inject.Inject; 8 | 9 | public class HomeFragmentViewModel extends ViewModel { 10 | 11 | @Inject 12 | public HomeFragmentViewModel() { 13 | } 14 | 15 | public void onMVVMClicked(View view) { 16 | view.getContext().startActivity(new Intent(view.getContext(), com.psato.devcamp.presentation.MVVM.QueryActivity.class)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/res/drawable/ic_search_grey_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/res/layout/activity_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /devcamp2018/app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 18 | 19 | 28 | 29 |