diff --git a/app/build.gradle b/app/build.gradle index fb623ee..0b86815 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,11 +5,13 @@ apply plugin: 'com.android.databinding' def cfg = rootProject.ext.configuration def libs = rootProject.ext.libraries; +def test = rootProject.ext.testingLibraries; android { compileSdkVersion cfg.compileVersion buildToolsVersion cfg.buildToolsVersion + //noinspection GroovyAssignabilityCheck defaultConfig { applicationId cfg.package minSdkVersion cfg.compileVersion @@ -19,34 +21,33 @@ android { buildConfigField "String", "MARVEL_PUBLIC_KEY", "\"${marvel_public_key}\"" buildConfigField "String", "MARVEL_PRIVATE_KEY", "\"${marvel_private_key}\"" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } + //noinspection GroovyAssignabilityCheck compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + buildToolsVersion '23.0.2' } +//noinspection GroovyAssignabilityCheck dependencies { - // Square - compile "com.squareup.retrofit:retrofit:${libs.retrofit}" - compile "com.squareup.retrofit:converter-gson:${libs.retrofit}" - compile "com.squareup.retrofit:adapter-rxjava:${libs.retrofit}" - - // ReactiveX - compile "io.reactivex:rxjava:${libs.rxjava}" - compile "io.reactivex:rxandroid:${libs.rxandroid}" + compile project(':domain') - // Others compile "com.github.bumptech.glide:glide:${libs.glide}" compile "jp.wasabeef:recyclerview-animators:${libs.recycler_animators}" compile "com.jakewharton:butterknife:${libs.butterknife}" compile "de.hdodenhof:circleimageview:${libs.circleimageview}" + compile "io.reactivex:rxandroid:${libs.rxandroid}" + + compile "com.squareup.retrofit:retrofit:${libs.retrofit}" + compile "com.squareup.retrofit:converter-gson:${libs.retrofit}" + compile "com.squareup.retrofit:adapter-rxjava:${libs.retrofit}" - // Google - compile "com.google.dagger:dagger:${libs.dagger}" apt "com.google.dagger:dagger-compiler:${libs.dagger}" - compile "org.glassfish:javax.annotation:${libs.javax_annotation}" // Android compile "com.android.support:design:${libs.supportVersion}" @@ -54,4 +55,30 @@ dependencies { compile "com.android.support:cardview-v7:${libs.supportVersion}" compile "com.android.support:recyclerview-v7:${libs.supportVersion}" compile "com.android.support:palette-v7:${libs.supportVersion}" + + // Test + testCompile "org.mockito:mockito-core:$test.mockito" + testCompile "junit:junit:$test.junit" + + androidTestCompile "com.squareup.okhttp:mockwebserver:$libs.okhttp" + androidTestCompile "com.android.support.test:runner:$test.espressoRunner" + androidTestCompile "com.android.support.test:rules:$test.espressoRules" + androidTestCompile("com.android.support.test.espresso:espresso-core:$test.espresso") { + exclude group: 'javax.inject' + } + + androidTestCompile "com.android.support.test.espresso:espresso-intents:$test.espresso" + androidTestCompile ("com.android.support.test.espresso:espresso-contrib:$test.espresso") { + exclude group: 'com.android.support', module: 'appcompat' + exclude group: 'com.android.support', module: 'support-v4' + exclude module: 'recyclerview-v7' + } + + androidTestApt "com.google.dagger:dagger-compiler:${libs.dagger}" + } + +configurations.all { + resolutionStrategy.force "com.android.support:support-annotations:$libs.supportVersion" + resolutionStrategy.force "com.squareup.okhttp:okhttp:$libs.okhttp" +} \ No newline at end of file diff --git a/app/src/androidTest/java/saulmm/avengers/ApplicationTest.java b/app/src/androidTest/java/saulmm/avengers/ApplicationTest.java deleted file mode 100644 index f1e8759..0000000 --- a/app/src/androidTest/java/saulmm/avengers/ApplicationTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package saulmm.avengers; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - - super(Application.class); - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/saulmm/avengers/TestData.java b/app/src/androidTest/java/saulmm/avengers/TestData.java new file mode 100644 index 0000000..1afdb3e --- /dev/null +++ b/app/src/androidTest/java/saulmm/avengers/TestData.java @@ -0,0 +1,12 @@ +package saulmm.avengers; + +/** + * Created by saulmm on 04/01/16. + */ +public final class TestData { + + public static final String SINGLE_CHARACTER_JSON = "{\"code\":200,\"status\":\"Ok\",\"copyright\":\"© 2016 MARVEL\",\"attributionText\":\"Data provided by Marvel. © 2016 MARVEL\",\"attributionHTML\":\"Data provided by Marvel. © 2016 MARVEL\",\"etag\":\"352196c55578b344079a81ffea21343cc8843d38\",\"data\":{\"offset\":0,\"limit\":20,\"total\":1,\"count\":1,\"results\":[{\"id\":1011170,\"name\":\"Alain\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1011170\",\"comics\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011170/comics\",\"items\":[],\"returned\":0},\"series\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011170/series\",\"items\":[],\"returned\":0},\"stories\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011170/stories\",\"items\":[],\"returned\":0},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011170/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/116/alain?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Alain?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1011170/alain?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]}]}}"; + + + public static final String TWENTY_CHARACTERS_JSON = "{\"code\":200,\"status\":\"Ok\",\"copyright\":\"© 2016 MARVEL\",\"attributionText\":\"Data provided by Marvel. © 2016 MARVEL\",\"attributionHTML\":\"Data provided by Marvel. © 2016 MARVEL\",\"etag\":\"46495bc6b3f0e9f7348c3be2715f9cf27d3daeb1\",\"data\":{\"offset\":60,\"limit\":20,\"total\":1485,\"count\":20,\"results\":[{\"id\":1010686,\"name\":\"Arcana\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1010686\",\"comics\":{\"available\":2,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010686/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/3908\",\"name\":\"Squadron Supreme (2006) #1\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/5425\",\"name\":\"Squadron Supreme Vol. 1: The Pre-War Years Premiere (Hardcover)\"}],\"returned\":2},\"series\":{\"available\":2,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010686/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/944\",\"name\":\"Squadron Supreme (2006)\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/1791\",\"name\":\"Squadron Supreme Vol. 1: The Pre-War Years Premiere (2006)\"}],\"returned\":2},\"stories\":{\"available\":1,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010686/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/5249\",\"name\":\"1 of 6 - The Pre-War Years\",\"type\":\"cover\"}],\"returned\":1},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010686/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/179/arcana?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1010686/arcana?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1009159,\"name\":\"Archangel\",\"description\":\"\",\"modified\":\"2013-10-18T12:48:24-0400\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/8/03/526165ed93180\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1009159\",\"comics\":{\"available\":531,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009159/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/17701\",\"name\":\"Age of Apocalypse: The Chosen (1995) #1\"}],\"returned\":1},\"series\":{\"available\":137,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009159/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/3614\",\"name\":\"Age of Apocalypse: The Chosen (1995)\"}],\"returned\":1},\"stories\":{\"available\":576,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009159/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/3261\",\"name\":\"2 of 2 - Save the Life of My Child\",\"type\":\"cover\"}],\"returned\":1},\"events\":{\"available\":17,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009159/events\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/116\",\"name\":\"Acts of Vengeance!\"}],\"returned\":1},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/1/angel?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Angel_(Warren_Worthington_III)?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1009159/archangel?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1009160,\"name\":\"Arclight\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/5/f0/4c0042067fd8b\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1009160\",\"comics\":{\"available\":13,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009160/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/26024\",\"name\":\"New Avengers Annual (2009) #3\"}],\"returned\":1},\"series\":{\"available\":5,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009160/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/8140\",\"name\":\"New Avengers Annual (2009)\"}],\"returned\":1},\"stories\":{\"available\":12,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009160/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/22086\",\"name\":\"Fallen Angel!\",\"type\":\"interiorStory\"}],\"returned\":1},\"events\":{\"available\":3,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009160/events\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/252\",\"name\":\"Inferno\"}],\"returned\":1},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/182/arclight?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Arclight?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1009160/arclight?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1010784,\"name\":\"Ares\",\"description\":\"\",\"modified\":\"2014-04-29T14:50:59-0400\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/c/10/535ff3daea603\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1010784\",\"comics\":{\"available\":41,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010784/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/24348\",\"name\":\"Adam: Legend of the Blue Marvel (Trade Paperback)\"}],\"returned\":1},\"series\":{\"available\":17,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010784/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/7524\",\"name\":\"Adam: Legend of the Blue Marvel (2008)\"}],\"returned\":1},\"stories\":{\"available\":43,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010784/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/5532\",\"name\":\"1 of 5 xLS\",\"type\":\"cover\"}],\"returned\":1},\"events\":{\"available\":4,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010784/events\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/296\",\"name\":\"Chaos War\"}],\"returned\":1},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/183/ares?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Ares?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1010784/ares?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1011275,\"name\":\"Argent\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1011275\",\"comics\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011275/comics\",\"items\":[],\"returned\":0},\"series\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011275/series\",\"items\":[],\"returned\":0},\"stories\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011275/stories\",\"items\":[],\"returned\":0},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011275/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/184/argent?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Argent?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1011275/argent?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1011012,\"name\":\"Armadillo\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/2/40/4c0032754da02\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1011012\",\"comics\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011012/comics\",\"items\":[],\"returned\":0},\"series\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011012/series\",\"items\":[],\"returned\":0},\"stories\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011012/stories\",\"items\":[],\"returned\":0},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011012/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/189/armadillo?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Armadillo?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1011012/armadillo?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1011298,\"name\":\"Armor (Hiscanao Ichiki)\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/9/20/4c002e6cbf990\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1011298\",\"comics\":{\"available\":4,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011298/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/21511\",\"name\":\"Astonishing X-Men (2004) #25\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/38320\",\"name\":\"Astonishing X-Men (2004) #37\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/38318\",\"name\":\"Astonishing X-Men (2004) #38\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/41548\",\"name\":\"Wolverine & the X-Men: Alpha & Omega (2011) #3\"}],\"returned\":4},\"series\":{\"available\":2,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011298/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/744\",\"name\":\"Astonishing X-Men (2004 - 2013)\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/15486\",\"name\":\"Wolverine & the X-Men: Alpha & Omega (2011 - 2012)\"}],\"returned\":2},\"stories\":{\"available\":4,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011298/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/47427\",\"name\":\"1 of 6 Ghost Box\",\"type\":\"cover\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/84191\",\"name\":\"Cover #84191\",\"type\":\"cover\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/89900\",\"name\":\"Astonishing X-Men (2004) #38\",\"type\":\"interiorStory\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/93985\",\"name\":\"Cover #93985\",\"type\":\"cover\"}],\"returned\":4},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011298/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/191/armor?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Armor_(Hisako_Ichiki)?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1011298/armor_hisako_ichiki?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1010827,\"name\":\"Armory\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1010827\",\"comics\":{\"available\":2,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010827/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/17293\",\"name\":\"Avengers: The Initiative Annual (2007) #1\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/21085\",\"name\":\"Secret Invasion: The Infiltration (Trade Paperback)\"}],\"returned\":2},\"series\":{\"available\":2,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010827/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/3087\",\"name\":\"Avengers: The Initiative Annual (2007)\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/4619\",\"name\":\"Secret Invasion: The Infiltration (2008)\"}],\"returned\":2},\"stories\":{\"available\":1,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010827/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/36242\",\"name\":\"1 of 1\",\"type\":\"cover\"}],\"returned\":1},\"events\":{\"available\":1,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010827/events\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/269\",\"name\":\"Secret Invasion\"}],\"returned\":1},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/193/armory?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1010827/armory?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1009740,\"name\":\"Arnim Zola\",\"description\":\"The frail, dwarfish Arnim Zola was born in 1930s Switzerland where he became the world's leading biochemist and genetic engineer.\",\"modified\":\"2012-03-20T12:33:28-0400\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/8/b0/4c00393a4cb7c\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1009740\",\"comics\":{\"available\":6,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009740/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/5402\",\"name\":\"Captain America (2004) #24\"}],\"returned\":1},\"series\":{\"available\":5,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009740/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/1996\",\"name\":\"Captain America (1968 - 1996)\"}],\"returned\":1},\"stories\":{\"available\":5,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009740/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/4276\",\"name\":\"3 of 3 - Civil War\",\"type\":\"interiorStory\"}],\"returned\":1},\"events\":{\"available\":1,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009740/events\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/238\",\"name\":\"Civil War\"}],\"returned\":1},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/2790/arnim_zola?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Zola,_Arnim?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1009740/arnim_zola?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1010748,\"name\":\"Arsenic\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/8/c0/4c00359a2be7b\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1010748\",\"comics\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010748/comics\",\"items\":[],\"returned\":0},\"series\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010748/series\",\"items\":[],\"returned\":0},\"stories\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010748/stories\",\"items\":[],\"returned\":0},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010748/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/197/arsenic?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Arsenic_(and_Old_Lace)?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1010748/arsenic?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1009161,\"name\":\"Artiee\",\"description\":\"\",\"modified\":\"2011-10-27T09:59:16-0400\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1009161\",\"comics\":{\"available\":1,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009161/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/35509\",\"name\":\"Amazing Spider-Man (1999) #673\"}],\"returned\":1},\"series\":{\"available\":1,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009161/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/454\",\"name\":\"Amazing Spider-Man (1999 - 2013)\"}],\"returned\":1},\"stories\":{\"available\":1,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009161/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/95451\",\"name\":\"Amazing Spider-Man (1999) #673\",\"type\":\"cover\"}],\"returned\":1},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009161/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/198/artiee?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1009161/artiee?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1010718,\"name\":\"Asgardian\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1010718\",\"comics\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010718/comics\",\"items\":[],\"returned\":0},\"series\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010718/series\",\"items\":[],\"returned\":0},\"stories\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010718/stories\",\"items\":[],\"returned\":0},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010718/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/201/asgardian?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Lorelei_%28Asgardian%29?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1010718/asgardian?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1009162,\"name\":\"Askew-Tronics\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1009162\",\"comics\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009162/comics\",\"items\":[],\"returned\":0},\"series\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009162/series\",\"items\":[],\"returned\":0},\"stories\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009162/stories\",\"items\":[],\"returned\":0},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009162/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/204/askew-tronics?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1009162/askew-tronics?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1010835,\"name\":\"Asylum\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1010835\",\"comics\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010835/comics\",\"items\":[],\"returned\":0},\"series\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010835/series\",\"items\":[],\"returned\":0},\"stories\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010835/stories\",\"items\":[],\"returned\":0},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010835/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/211/asylum?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Asylum_(Henrique_Gallante)?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1010835/asylum?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1010336,\"name\":\"Atlas (Team)\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1010336\",\"comics\":{\"available\":12,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010336/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/17493\",\"name\":\"Avengers (1998) #12\"}],\"returned\":1},\"series\":{\"available\":8,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010336/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/354\",\"name\":\"Avengers (1998 - 2004)\"}],\"returned\":1},\"stories\":{\"available\":8,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010336/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/3878\",\"name\":\"6 of 6 - One Step Forward\",\"type\":\"cover\"}],\"returned\":1},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1010336/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/214/atlas?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Atlas_(Team)?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1010336/atlas_team?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1009163,\"name\":\"Aurora\",\"description\":\"\",\"modified\":\"2011-05-10T15:56:51-0400\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/f/10/4c004203f1072\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1009163\",\"comics\":{\"available\":53,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009163/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/17701\",\"name\":\"Age of Apocalypse: The Chosen (1995) #1\"}],\"returned\":1},\"series\":{\"available\":14,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009163/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/3614\",\"name\":\"Age of Apocalypse: The Chosen (1995)\"}],\"returned\":1},\"stories\":{\"available\":59,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009163/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/15427\",\"name\":\"Shoot-Out at the Stampede!\",\"type\":\"interiorStory\"}],\"returned\":20},\"events\":{\"available\":4,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009163/events\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/227\",\"name\":\"Age of Apocalypse\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/296\",\"name\":\"Chaos War\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/302\",\"name\":\"Fear Itself\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/29\",\"name\":\"Infinity War\"}],\"returned\":4},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/221/aurora?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Aurora?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1009163/aurora?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1009164,\"name\":\"Avalanche\",\"description\":\"\",\"modified\":\"1969-12-31T19:00:00-0500\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/f/10/4c0042010d383\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1009164\",\"comics\":{\"available\":30,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009164/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/6929\",\"name\":\"Avengers Annual (1967) #15\"}],\"returned\":1},\"series\":{\"available\":9,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009164/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/1988\",\"name\":\"Avengers Annual (1967 - 1994)\"}],\"returned\":1},\"stories\":{\"available\":37,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009164/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/15472\",\"name\":\"Days of Future Past\",\"type\":\"interiorStory\"}],\"returned\":1},\"events\":{\"available\":5,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009164/events\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/246\",\"name\":\"Evolutionary War\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/248\",\"name\":\"Fall of the Mutants\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/32\",\"name\":\"Kings of Pain\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/263\",\"name\":\"Mutant Massacre\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/270\",\"name\":\"Secret Wars\"}],\"returned\":5},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/222/avalanche?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Avalanche?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1009164/avalanche?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1009165,\"name\":\"Avengers\",\"description\":\"Earth's Mightiest Heroes joined forces to take on threats that were too big for any one hero to tackle. With a roster that has included Captain America, Iron Man, Ant-Man, Hulk, Thor, Wasp and dozens more over the years, the Avengers have come to be regarded as Earth's No. 1 team.\",\"modified\":\"2014-05-27T20:28:26-0400\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/9/20/5102c774ebae7\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1009165\",\"comics\":{\"available\":962,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009165/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/35501\",\"name\":\"Amazing Spider-Man (1999) #663\"}],\"returned\":1},\"series\":{\"available\":191,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009165/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/15331\",\"name\":\"Age of Apocalypse (2011 - 2013)\"}],\"returned\":1},\"stories\":{\"available\":1373,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009165/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/490\",\"name\":\"Interior #490\",\"type\":\"interiorStory\"}],\"returned\":1},\"events\":{\"available\":21,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1009165/events\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/events/116\",\"name\":\"Acts of Vengeance!\"}],\"returned\":1},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/comics/characters/1009165/avengers?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Avengers?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1009165/avengers?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1015239,\"name\":\"Avengers (Ultimate)\",\"description\":\"\",\"modified\":\"2012-07-10T19:18:28-0400\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1015239\",\"comics\":{\"available\":23,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1015239/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/15718\",\"name\":\"Ultimate X-Men (2000) #27\"}],\"returned\":1},\"series\":{\"available\":9,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1015239/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/9867\",\"name\":\"Ultimate Comics Avengers 3 (2010 - 2011)\"}],\"returned\":1},\"stories\":{\"available\":16,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1015239/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/32194\",\"name\":\"Free Preview of AVENGERS 65\",\"type\":\"interiorStory\"}],\"returned\":1},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1015239/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/68/avengers?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1015239/avengers_ultimate?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]},{\"id\":1011766,\"name\":\"Azazel (Mutant)\",\"description\":\"A mutant from biblical times, Azazel is the ruler of the Neyaphem and claims that the Earth and everything on it belongs to him.\",\"modified\":\"2011-06-09T11:04:52-0400\",\"thumbnail\":{\"path\":\"http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available\",\"extension\":\"jpg\"},\"resourceURI\":\"http://gateway.marvel.com/v1/public/characters/1011766\",\"comics\":{\"available\":2,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011766/comics\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/13969\",\"name\":\"Uncanny X-Men (1963) #428\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/comics/41821\",\"name\":\"X-MEN FIRST CLASS: THE HIGH HAND (2011) #1\"}],\"returned\":2},\"series\":{\"available\":2,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011766/series\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/2258\",\"name\":\"Uncanny X-Men (1963 - 2011)\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/series/15598\",\"name\":\"X-MEN FIRST CLASS: THE HIGH HAND (2011)\"}],\"returned\":2},\"stories\":{\"available\":2,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011766/stories\",\"items\":[{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/28405\",\"name\":\"How Did I Get Here?\",\"type\":\"interiorStory\"},{\"resourceURI\":\"http://gateway.marvel.com/v1/public/stories/94626\",\"name\":\"X-MEN FIRST CLASS: THE HIGH HAND (2011) #1\",\"type\":\"interiorStory\"}],\"returned\":2},\"events\":{\"available\":0,\"collectionURI\":\"http://gateway.marvel.com/v1/public/characters/1011766/events\",\"items\":[],\"returned\":0},\"urls\":[{\"type\":\"detail\",\"url\":\"http://marvel.com/characters/227/azazel?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"wiki\",\"url\":\"http://marvel.com/universe/Azazel_(mutant)?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"},{\"type\":\"comiclink\",\"url\":\"http://marvel.com/comics/characters/1011766/azazel_mutant?utm_campaign=apiRef&utm_source=74129ef99c9fd5f7692608f17abb88f9\"}]}]}}"; +} diff --git a/app/src/androidTest/java/saulmm/avengers/tests/CharacterListActivityInstrumentationTest.java b/app/src/androidTest/java/saulmm/avengers/tests/CharacterListActivityInstrumentationTest.java new file mode 100644 index 0000000..89be678 --- /dev/null +++ b/app/src/androidTest/java/saulmm/avengers/tests/CharacterListActivityInstrumentationTest.java @@ -0,0 +1,75 @@ +package saulmm.avengers.tests; + +import android.content.Intent; +import android.support.test.espresso.Espresso; +import android.support.test.espresso.contrib.RecyclerViewActions; +import android.support.test.espresso.intent.Intents; +import android.support.test.espresso.intent.rule.IntentsTestRule; +import android.support.test.espresso.matcher.ViewMatchers; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.LargeTest; +import android.util.Log; +import android.widget.TextView; + +import com.squareup.okhttp.mockwebserver.Dispatcher; +import com.squareup.okhttp.mockwebserver.MockResponse; +import com.squareup.okhttp.mockwebserver.MockWebServer; +import com.squareup.okhttp.mockwebserver.RecordedRequest; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.inject.Inject; + +import saulmm.avengers.R; +import saulmm.avengers.TestData; +import saulmm.avengers.injector.components.DaggerAvengersComponent; +import saulmm.avengers.injector.modules.ActivityModule; +import saulmm.avengers.repository.CharacterRepository; +import saulmm.avengers.rest.RestDataSource; +import saulmm.avengers.views.activities.CharacterDetailActivity; +import saulmm.avengers.views.activities.CharacterListActivity; + +import static android.support.test.espresso.Espresso.*; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.intent.Intents.intended; +import static android.support.test.espresso.intent.Intents.times; +import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static android.support.test.espresso.matcher.ViewMatchers.*; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.is; + +@LargeTest +@RunWith(AndroidJUnit4.class) +public class CharacterListActivityInstrumentationTest { + private MockWebServer mMockWebServer; + + @Rule public ActivityTestRule mCharacterListIntentRule = + new ActivityTestRule<>(CharacterListActivity.class, true, false); + + @Before public void setUp() throws Exception { + mMockWebServer = new MockWebServer(); + mMockWebServer.start(); + + RestDataSource.END_POINT = mMockWebServer.getUrl("/").toString(); + } + + @Test public void showCharacters() { + mMockWebServer.enqueue(new MockResponse().setBody(TestData.TWENTY_CHARACTERS_JSON)); + + mCharacterListIntentRule.launchActivity(null); + + onView(withId(R.id.activity_avengers_recycler)).check(matches(isDisplayed())); + } + + @After public void tearDown() throws Exception { + mMockWebServer.shutdown(); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 008cb34..63af7a0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,7 +15,7 @@ > @@ -27,7 +27,7 @@ diff --git a/app/src/main/java/saulmm/avengers/AvengersApplication.java b/app/src/main/java/saulmm/avengers/AvengersApplication.java index f312a06..feaa0f3 100644 --- a/app/src/main/java/saulmm/avengers/AvengersApplication.java +++ b/app/src/main/java/saulmm/avengers/AvengersApplication.java @@ -7,30 +7,26 @@ import android.app.Application; -import saulmm.avengers.injector.AppModule; +import saulmm.avengers.injector.modules.AppModule; import saulmm.avengers.injector.components.AppComponent; import saulmm.avengers.injector.components.DaggerAppComponent; public class AvengersApplication extends Application { - private AppComponent mAppComponent; @Override public void onCreate() { - super.onCreate(); initializeInjector(); } private void initializeInjector() { - mAppComponent = DaggerAppComponent.builder() .appModule(new AppModule(this)) .build(); } public AppComponent getAppComponent() { - return mAppComponent; } } diff --git a/app/src/main/java/saulmm/avengers/domain/GetCharacterInformationUsecase.java b/app/src/main/java/saulmm/avengers/domain/GetCharacterInformationUsecase.java deleted file mode 100644 index 9ba2ff0..0000000 --- a/app/src/main/java/saulmm/avengers/domain/GetCharacterInformationUsecase.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package saulmm.avengers.domain; - -import javax.inject.Inject; - -import rx.Observable; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; -import saulmm.avengers.model.entities.Character; -import saulmm.avengers.model.repository.Repository; - -public class GetCharacterInformationUsecase implements Usecase { - - private final Repository mRepository; - private int mCharacterId; - - @Inject public GetCharacterInformationUsecase(int characterId, Repository repository) { - - mCharacterId = characterId; - mRepository = repository; - } - - @Override - public Observable execute() { - return mRepository.getCharacter(mCharacterId) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()); - } -} diff --git a/app/src/main/java/saulmm/avengers/domain/GetCharactersUsecase.java b/app/src/main/java/saulmm/avengers/domain/GetCharactersUsecase.java deleted file mode 100644 index a3b0f19..0000000 --- a/app/src/main/java/saulmm/avengers/domain/GetCharactersUsecase.java +++ /dev/null @@ -1,40 +0,0 @@ -package saulmm.avengers.domain; - -import java.util.List; - -import javax.inject.Inject; - -import rx.Observable; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; -import saulmm.avengers.model.entities.Character; -import saulmm.avengers.model.repository.Repository; - -public class GetCharactersUsecase implements Usecase> { - public final static int CHARACTERS_LIMIT = 20; - - private final Repository mRepository; - private int currentOffset; - - @Inject public GetCharactersUsecase(Repository repository) { - mRepository = repository; - } - - @Override - public Observable> execute() { - return mRepository.getCharacters(currentOffset) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()); - } - - public Observable> executeIncreasingOffset() { - currentOffset += CHARACTERS_LIMIT; - - return mRepository.getCharacters(currentOffset) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .doOnError(throwable -> { - currentOffset -= CHARACTERS_LIMIT; - }); - } -} diff --git a/app/src/main/java/saulmm/avengers/domain/GetCollectionUsecase.java b/app/src/main/java/saulmm/avengers/domain/GetCollectionUsecase.java deleted file mode 100644 index ab9e9e5..0000000 --- a/app/src/main/java/saulmm/avengers/domain/GetCollectionUsecase.java +++ /dev/null @@ -1,47 +0,0 @@ -package saulmm.avengers.domain; - -import java.util.List; -import javax.inject.Inject; -import rx.Observable; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; -import saulmm.avengers.model.entities.CollectionItem; -import saulmm.avengers.model.repository.Repository; - -import static saulmm.avengers.model.entities.CollectionItem.COMIC; -import static saulmm.avengers.model.entities.CollectionItem.EVENT; -import static saulmm.avengers.model.entities.CollectionItem.SERIES; -import static saulmm.avengers.model.entities.CollectionItem.STORY; - -public class GetCollectionUsecase implements Usecase> { - private final Repository mRepository; - private final int mCharacterId; - - @Inject public GetCollectionUsecase(int characterId, Repository repository) { - mRepository = repository; - mCharacterId = characterId; - } - - @Override - public Observable> execute() { - return null; - } - - public Observable> execute(String type) { - if (!type.equals(COMIC) && !type.equals(EVENT) && !type.equals(SERIES) && !type.equals(STORY)) - throw new IllegalArgumentException("Collection type must be events|series|comics|stories"); - - return mRepository.getCharacterCollection(mCharacterId, type) - .subscribeOn(Schedulers.newThread()) - .flatMap(collectionItems -> { - - for (CollectionItem coolectionItem: collectionItems) { - System.out.println("[DEBUG]" + " GetCollectionUsecase execute - " + - ""+coolectionItem); - } - - return Observable.just(collectionItems); - }) - .observeOn(AndroidSchedulers.mainThread()); - } -} diff --git a/app/src/main/java/saulmm/avengers/domain/Usecase.java b/app/src/main/java/saulmm/avengers/domain/Usecase.java deleted file mode 100644 index 3e4eeca..0000000 --- a/app/src/main/java/saulmm/avengers/domain/Usecase.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package saulmm.avengers.domain; - -import rx.Observable; - -public interface Usecase { - - Observable execute(); -} diff --git a/app/src/main/java/saulmm/avengers/injector/AppModule.java b/app/src/main/java/saulmm/avengers/injector/AppModule.java deleted file mode 100644 index 15ffcd8..0000000 --- a/app/src/main/java/saulmm/avengers/injector/AppModule.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package saulmm.avengers.injector; - -import dagger.Module; -import dagger.Provides; -import javax.inject.Singleton; -import saulmm.avengers.AvengersApplication; -import saulmm.avengers.model.repository.Repository; -import saulmm.avengers.model.rest.RestDataSource; - -@Module -public class AppModule { - - private final AvengersApplication mAvengersApplication; - - public AppModule(AvengersApplication avengersApplication) { - - this.mAvengersApplication = avengersApplication; - } - - @Provides @Singleton AvengersApplication provideAvengersApplicationContext () { return mAvengersApplication; } - - @Provides @Singleton Repository provideDataRepository (RestDataSource restDataSource) { return restDataSource; } -} diff --git a/app/src/main/java/saulmm/avengers/injector/components/ActivityComponent.java b/app/src/main/java/saulmm/avengers/injector/components/ActivityComponent.java index 79ae3f5..9494a42 100644 --- a/app/src/main/java/saulmm/avengers/injector/components/ActivityComponent.java +++ b/app/src/main/java/saulmm/avengers/injector/components/ActivityComponent.java @@ -7,7 +7,6 @@ import android.content.Context; - import dagger.Component; import saulmm.avengers.injector.Activity; import saulmm.avengers.injector.modules.ActivityModule; diff --git a/app/src/main/java/saulmm/avengers/injector/components/AppComponent.java b/app/src/main/java/saulmm/avengers/injector/components/AppComponent.java index 6c01143..38d1c98 100644 --- a/app/src/main/java/saulmm/avengers/injector/components/AppComponent.java +++ b/app/src/main/java/saulmm/avengers/injector/components/AppComponent.java @@ -6,13 +6,24 @@ package saulmm.avengers.injector.components; import dagger.Component; + +import javax.inject.Named; import javax.inject.Singleton; + +import rx.Scheduler; import saulmm.avengers.AvengersApplication; -import saulmm.avengers.injector.AppModule; -import saulmm.avengers.model.repository.Repository; +import saulmm.avengers.injector.modules.AppModule; +import saulmm.avengers.repository.CharacterRepository; +import saulmm.avengers.rest.Endpoint; +import saulmm.avengers.rest.MarvelAuthorizer; @Singleton @Component(modules = AppModule.class) public interface AppComponent { AvengersApplication app(); - Repository dataRepository(); + CharacterRepository dataRepository(); + Endpoint restEndpoint(); + MarvelAuthorizer marvelAuthorizer(); + + @Named("ui_thread") Scheduler uiThread(); + @Named("executor_thread") Scheduler executorThread(); } diff --git a/app/src/main/java/saulmm/avengers/injector/components/AvengerInformationComponent.java b/app/src/main/java/saulmm/avengers/injector/components/AvengerInformationComponent.java index f891fa4..7d780ee 100644 --- a/app/src/main/java/saulmm/avengers/injector/components/AvengerInformationComponent.java +++ b/app/src/main/java/saulmm/avengers/injector/components/AvengerInformationComponent.java @@ -6,8 +6,8 @@ package saulmm.avengers.injector.components; import dagger.Component; -import saulmm.avengers.domain.GetCharacterInformationUsecase; -import saulmm.avengers.domain.GetCollectionUsecase; +import saulmm.avengers.CharacterDetailsUsecase; +import saulmm.avengers.GetCollectionUsecase; import saulmm.avengers.injector.Activity; import saulmm.avengers.injector.modules.ActivityModule; import saulmm.avengers.injector.modules.AvengerInformationModule; @@ -22,6 +22,6 @@ public interface AvengerInformationComponent extends ActivityComponent { void inject (CollectionActivity detailActivity); - GetCharacterInformationUsecase getCharacerInformationUsecase(); + CharacterDetailsUsecase getCharacerInformationUsecase(); GetCollectionUsecase getCollectionUsecase(); } diff --git a/app/src/main/java/saulmm/avengers/injector/components/AvengersComponent.java b/app/src/main/java/saulmm/avengers/injector/components/AvengersComponent.java index 85910e8..54e4ce9 100644 --- a/app/src/main/java/saulmm/avengers/injector/components/AvengersComponent.java +++ b/app/src/main/java/saulmm/avengers/injector/components/AvengersComponent.java @@ -10,12 +10,12 @@ import dagger.Component; import saulmm.avengers.injector.Activity; import saulmm.avengers.injector.modules.ActivityModule; -import saulmm.avengers.views.activities.CharacterListListActivity; +import saulmm.avengers.views.activities.CharacterListActivity; @Activity @Component(dependencies = AppComponent.class, modules = {ActivityModule.class}) public interface AvengersComponent extends ActivityComponent { - void inject (CharacterListListActivity activity); + void inject (CharacterListActivity activity); Context activityContext(); } diff --git a/app/src/main/java/saulmm/avengers/injector/modules/ActivityModule.java b/app/src/main/java/saulmm/avengers/injector/modules/ActivityModule.java index b14fb34..31ea2b5 100644 --- a/app/src/main/java/saulmm/avengers/injector/modules/ActivityModule.java +++ b/app/src/main/java/saulmm/avengers/injector/modules/ActivityModule.java @@ -10,15 +10,17 @@ import dagger.Module; import dagger.Provides; +import javax.inject.Named; +import rx.Scheduler; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; import saulmm.avengers.injector.Activity; @Module public class ActivityModule { - private final Context mContext; public ActivityModule(Context mContext) { - this.mContext = mContext; } diff --git a/app/src/main/java/saulmm/avengers/injector/modules/AppModule.java b/app/src/main/java/saulmm/avengers/injector/modules/AppModule.java new file mode 100644 index 0000000..41a728c --- /dev/null +++ b/app/src/main/java/saulmm/avengers/injector/modules/AppModule.java @@ -0,0 +1,59 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package saulmm.avengers.injector.modules; + +import dagger.Module; +import dagger.Provides; + +import javax.inject.Named; +import javax.inject.Singleton; + +import rx.Scheduler; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; +import saulmm.avengers.AvengersApplication; +import saulmm.avengers.BuildConfig; +import saulmm.avengers.repository.CharacterRepository; +import saulmm.avengers.rest.Endpoint; +import saulmm.avengers.rest.MarvelAuthorizer; +import saulmm.avengers.rest.RestDataSource; + +@Module +public class AppModule { + private final AvengersApplication mAvengersApplication; + + public AppModule(AvengersApplication avengersApplication) { + this.mAvengersApplication = avengersApplication; + } + + @Provides @Singleton + AvengersApplication provideAvengersApplicationContext() { + return mAvengersApplication; } + + @Provides + MarvelAuthorizer provideMarvelAuthorizer() { + return new MarvelAuthorizer(BuildConfig.MARVEL_PUBLIC_KEY, BuildConfig.MARVEL_PRIVATE_KEY); + } + + @Provides + Endpoint provideRestEndpoint() { + return new Endpoint("http://gateway.marvel.com/"); + } + + @Provides @Singleton + CharacterRepository provideDataRepository(RestDataSource restDataSource) { + return restDataSource; } + + @Provides @Named("executor_thread") + Scheduler provideExecutorThread() { + return Schedulers.newThread(); + } + + @Provides @Named("ui_thread") + Scheduler provideUiThread() { + return AndroidSchedulers.mainThread(); + } +} diff --git a/app/src/main/java/saulmm/avengers/injector/modules/AvengerInformationModule.java b/app/src/main/java/saulmm/avengers/injector/modules/AvengerInformationModule.java index b212f42..10652fe 100644 --- a/app/src/main/java/saulmm/avengers/injector/modules/AvengerInformationModule.java +++ b/app/src/main/java/saulmm/avengers/injector/modules/AvengerInformationModule.java @@ -5,12 +5,16 @@ */ package saulmm.avengers.injector.modules; +import javax.inject.Named; + import dagger.Module; import dagger.Provides; -import saulmm.avengers.domain.GetCharacterInformationUsecase; -import saulmm.avengers.domain.GetCollectionUsecase; +import rx.Scheduler; +import saulmm.avengers.CharacterDetailsUsecase; +import saulmm.avengers.GetCollectionUsecase; import saulmm.avengers.injector.Activity; -import saulmm.avengers.model.repository.Repository; +import saulmm.avengers.repository.CharacterRepository; +import saulmm.avengers.rest.MarvelAuthorizer; @Module public class AvengerInformationModule { @@ -20,11 +24,18 @@ public AvengerInformationModule(int characterId) { mCharacterId = characterId; } - @Provides @Activity GetCharacterInformationUsecase provideGetCharacterInformationUsecase (Repository repository) { - return new GetCharacterInformationUsecase(mCharacterId, repository); + @Provides @Activity CharacterDetailsUsecase provideGetCharacterInformationUsecase ( + CharacterRepository repository, + @Named("ui_thread") Scheduler uiThread, + @Named("executor_thread") Scheduler executorThread) { + + return new CharacterDetailsUsecase(mCharacterId, repository, uiThread, executorThread); } - @Provides @Activity GetCollectionUsecase provideGetCharacterComicsUsecase (Repository repository) { - return new GetCollectionUsecase(mCharacterId, repository); + @Provides @Activity GetCollectionUsecase provideGetCharacterComicsUsecase (CharacterRepository repository, + @Named("ui_thread") Scheduler uiThread, + @Named("executor_thread") Scheduler executorThread) { + + return new GetCollectionUsecase(mCharacterId, repository, uiThread, executorThread); } } diff --git a/app/src/main/java/saulmm/avengers/model/rest/RestDataSource.java b/app/src/main/java/saulmm/avengers/model/rest/RestDataSource.java deleted file mode 100644 index 4976b07..0000000 --- a/app/src/main/java/saulmm/avengers/model/rest/RestDataSource.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ -package saulmm.avengers.model.rest; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import com.squareup.okhttp.OkHttpClient; -import java.util.List; -import javax.inject.Inject; -import retrofit.GsonConverterFactory; -import retrofit.Retrofit; -import retrofit.RxJavaCallAdapterFactory; -import rx.Observable; -import saulmm.avengers.BuildConfig; -import saulmm.avengers.model.entities.Character; -import saulmm.avengers.model.entities.CollectionItem; -import saulmm.avengers.model.repository.Repository; -import saulmm.avengers.model.rest.exceptions.ServerErrorException; -import saulmm.avengers.model.rest.exceptions.UknownErrorException; -import saulmm.avengers.model.rest.utils.deserializers.MarvelResultsDeserializer; -import saulmm.avengers.model.rest.utils.interceptors.HttpLoggingInterceptor; -import saulmm.avengers.model.rest.utils.interceptors.MarvelSigningIterceptor; - -import static saulmm.avengers.model.entities.CollectionItem.COMIC; -import static saulmm.avengers.model.entities.CollectionItem.EVENT; -import static saulmm.avengers.model.entities.CollectionItem.SERIES; -import static saulmm.avengers.model.entities.CollectionItem.STORY; - -public class RestDataSource implements Repository { - - private final MarvelApi mMarvelApi; - public final static int MAX_ATTEMPS = 3; - - @Inject - public RestDataSource() { - OkHttpClient client = new OkHttpClient(); - - HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); - loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BASIC); - - MarvelSigningIterceptor signingIterceptor = new MarvelSigningIterceptor( - BuildConfig.MARVEL_PUBLIC_KEY, BuildConfig.MARVEL_PRIVATE_KEY); - - client.interceptors().add(signingIterceptor); - client.interceptors().add(loggingInterceptor); - - Gson customGsonInstance = new GsonBuilder() - .registerTypeAdapter(new TypeToken>() {}.getType(), - new MarvelResultsDeserializer()) - - .registerTypeAdapter(new TypeToken>() {}.getType(), - new MarvelResultsDeserializer()) - - .create(); - - Retrofit marvelApiAdapter = new Retrofit.Builder() - .baseUrl(MarvelApi.END_POINT) - .addConverterFactory(GsonConverterFactory.create(customGsonInstance)) - .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) - .client(client) - .build(); - - mMarvelApi = marvelApiAdapter.create(MarvelApi.class); - } - - @Override - public Observable getCharacter(int characterId) { - return mMarvelApi.getCharacterById(characterId).flatMap( - characters -> Observable.just(characters.get(0))); - } - - @Override - public Observable> getCharacters(int currentOffset) { - return mMarvelApi.getCharacters(currentOffset) - .onErrorResumeNext(throwable -> { - boolean serverError = throwable.getMessage().equals(HttpErrors.SERVER_ERROR); - return Observable.error((serverError) ? new ServerErrorException() : new UknownErrorException()); - }); - } - - @Override - public Observable> getCharacterCollection(int characterId, String type) { - if (!type.equals(COMIC) && !type.equals(EVENT) && !type.equals(SERIES) && !type.equals(STORY)) - throw new IllegalArgumentException("Collection type must be: events|series|comics|stories"); - - return mMarvelApi.getCharacterCollection(characterId, type); - } -} diff --git a/app/src/main/java/saulmm/avengers/model/rest/utils/interceptors/HttpLoggingInterceptor.java b/app/src/main/java/saulmm/avengers/model/rest/utils/interceptors/HttpLoggingInterceptor.java deleted file mode 100644 index 36723c0..0000000 --- a/app/src/main/java/saulmm/avengers/model/rest/utils/interceptors/HttpLoggingInterceptor.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (C) 2015 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package saulmm.avengers.model.rest.utils.interceptors; - -import android.util.Log; -import com.squareup.okhttp.Connection; -import com.squareup.okhttp.Headers; -import com.squareup.okhttp.HttpUrl; -import com.squareup.okhttp.Interceptor; -import com.squareup.okhttp.MediaType; -import com.squareup.okhttp.OkHttpClient; -import com.squareup.okhttp.Protocol; -import com.squareup.okhttp.Request; -import com.squareup.okhttp.RequestBody; -import com.squareup.okhttp.Response; -import com.squareup.okhttp.ResponseBody; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.concurrent.TimeUnit; -import okio.Buffer; -import okio.BufferedSource; - -/** - * An OkHttp interceptor which logs request and response information. Can be applied as an - * {@linkplain OkHttpClient#interceptors() application interceptor} or as a - * {@linkplain OkHttpClient#networkInterceptors() network interceptor}. - *

- * The format of the logs created by this class should not be considered stable and may change - * slightly between releases. If you need a stable logging format, use your own interceptor. - */ -public final class HttpLoggingInterceptor implements Interceptor { - private static final Charset UTF8 = Charset.forName("UTF-8"); - private static final String TAG = "HttpLoggingInterceptor"; - - public enum Level { - /** No logs. */ - NONE, - /** - * Logs request and response lines. - *

- * Example: - *

{@code
-     * --> POST /greeting HTTP/1.1 (3-byte body)
-     *
-     * <-- HTTP/1.1 200 OK (22ms, 6-byte body)
-     * }
- */ - BASIC, - /** - * Logs request and response lines and their respective headers. - *

- * Example: - *

{@code
-     * --> POST /greeting HTTP/1.1
-     * Host: example.com
-     * Content-Type: plain/text
-     * Content-Length: 3
-     * --> END POST
-     *
-     * <-- HTTP/1.1 200 OK (22ms)
-     * Content-Type: plain/text
-     * Content-Length: 6
-     * <-- END HTTP
-     * }
- */ - HEADERS, - /** - * Logs request and response lines and their respective headers and bodies (if present). - *

- * Example: - *

{@code
-     * --> POST /greeting HTTP/1.1
-     * Host: example.com
-     * Content-Type: plain/text
-     * Content-Length: 3
-     *
-     * Hi?
-     * --> END GET
-     *
-     * <-- HTTP/1.1 200 OK (22ms)
-     * Content-Type: plain/text
-     * Content-Length: 6
-     *
-     * Hello!
-     * <-- END HTTP
-     * }
- */ - BODY - } - - public interface Logger { - void log(String message); - - /** A {@link Logger} defaults output appropriate for the current platform. */ - Logger DEFAULT = new Logger() { - @Override public void log(String message) { - Log.d(TAG, message); - } - }; - } - - public HttpLoggingInterceptor() { - this(Logger.DEFAULT); - } - - public HttpLoggingInterceptor(Logger logger) { - this.logger = logger; - } - - private final Logger logger; - - private volatile Level level = Level.NONE; - - /** Change the level at which this interceptor logs. */ - public void setLevel(Level level) { - this.level = level; - } - - @Override public Response intercept(Chain chain) throws IOException { - Level level = this.level; - - Request request = chain.request(); - if (level == Level.NONE) { - return chain.proceed(request); - } - - boolean logBody = level == Level.BODY; - boolean logHeaders = logBody || level == Level.HEADERS; - - RequestBody requestBody = request.body(); - boolean hasRequestBody = requestBody != null; - - Connection connection = chain.connection(); - Protocol protocol = connection != null ? connection.getProtocol() : Protocol.HTTP_1_1; - String requestStartMessage = - "--> " + request.method() + ' ' + requestPath(request.httpUrl()) + ' ' + protocol(protocol); - if (!logHeaders && hasRequestBody) { - requestStartMessage += " (" + requestBody.contentLength() + "-byte body)"; - } - logger.log(requestStartMessage); - - if (logHeaders) { - Headers headers = request.headers(); - for (int i = 0, count = headers.size(); i < count; i++) { - logger.log(headers.name(i) + ": " + headers.value(i)); - } - - String endMessage = "--> END " + request.method(); - if (logBody && hasRequestBody) { - Buffer buffer = new Buffer(); - requestBody.writeTo(buffer); - - Charset charset = UTF8; - MediaType contentType = requestBody.contentType(); - if (contentType != null) { - contentType.charset(UTF8); - } - - logger.log(""); - logger.log(buffer.readString(charset)); - - endMessage += " (" + requestBody.contentLength() + "-byte body)"; - } - logger.log(endMessage); - } - - long startNs = System.nanoTime(); - Response response = chain.proceed(request); - long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs); - - ResponseBody responseBody = response.body(); - logger.log("<-- " + protocol(response.protocol()) + ' ' + response.code() + ' ' - + response.message() + " (" + tookMs + "ms" - + (!logHeaders ? ", " + responseBody.contentLength() + "-byte body" : "") + ')'); - - if (logHeaders) { - Headers headers = response.headers(); - for (int i = 0, count = headers.size(); i < count; i++) { - logger.log(headers.name(i) + ": " + headers.value(i)); - } - - String endMessage = "<-- END HTTP"; - if (logBody) { - BufferedSource source = responseBody.source(); - source.request(Long.MAX_VALUE); // Buffer the entire body. - Buffer buffer = source.buffer(); - - Charset charset = UTF8; - MediaType contentType = responseBody.contentType(); - if (contentType != null) { - charset = contentType.charset(UTF8); - } - - if (responseBody.contentLength() != 0) { - logger.log(""); - logger.log(buffer.clone().readString(charset)); - } - - endMessage += " (" + buffer.size() + "-byte body)"; - } - logger.log(endMessage); - } - - return response; - } - - private static String protocol(Protocol protocol) { - return protocol == Protocol.HTTP_1_0 ? "HTTP/1.0" : "HTTP/1.1"; - } - - private static String requestPath(HttpUrl url) { - String path = url.encodedPath(); - String query = url.encodedQuery(); - return query != null ? (path + '?' + query) : path; - } -} \ No newline at end of file diff --git a/app/src/main/java/saulmm/avengers/mvp/presenters/CharacterDetailPresenter.java b/app/src/main/java/saulmm/avengers/mvp/presenters/CharacterDetailPresenter.java index a092daa..1efe14d 100644 --- a/app/src/main/java/saulmm/avengers/mvp/presenters/CharacterDetailPresenter.java +++ b/app/src/main/java/saulmm/avengers/mvp/presenters/CharacterDetailPresenter.java @@ -5,41 +5,37 @@ */ package saulmm.avengers.mvp.presenters; -import android.content.Context; -import android.graphics.Bitmap; import javax.inject.Inject; import rx.Subscription; -import saulmm.avengers.domain.GetCharacterInformationUsecase; -import saulmm.avengers.model.entities.Character; -import saulmm.avengers.model.entities.CollectionItem; +import saulmm.avengers.CharacterDetailsUsecase; +import saulmm.avengers.entities.MarvelCharacter; import saulmm.avengers.mvp.views.CharacterDetailView; import saulmm.avengers.mvp.views.View; -import saulmm.avengers.views.activities.CollectionActivity; public class CharacterDetailPresenter implements Presenter { - - private final Context mActivityContext; private CharacterDetailView mCharacterDetailView; - private final GetCharacterInformationUsecase mGetCharacterInformationUsecase; + private final CharacterDetailsUsecase mGetCharacterInformationUsecase; private Subscription mCharacterSubscription; private int mCharacterId; private String mCharacterName; @Inject - public CharacterDetailPresenter(GetCharacterInformationUsecase getCharacterInformationUsecase, - Context activityContext) { - + public CharacterDetailPresenter(CharacterDetailsUsecase getCharacterInformationUsecase) { mGetCharacterInformationUsecase = getCharacterInformationUsecase; - mActivityContext = activityContext; } @Override public void onCreate() { if (mCharacterId == -1 || mCharacterName == null) - throw new IllegalStateException("initializePresenter was not well initialised"); + throw new IllegalStateException(); + mCharacterDetailView.disableScroll(); + askForCharacterDetails(); + } + + public void askForCharacterDetails() { mCharacterSubscription = mGetCharacterInformationUsecase.execute() .subscribe(this::onCharacterReceived, this::manageCharacterError); } @@ -65,42 +61,42 @@ public void attachView(View v) { mCharacterDetailView = (CharacterDetailView) v; } - @SuppressWarnings("Convert2MethodRef") public void initializePresenter(int characterId, String characterName) { mCharacterId = characterId; mCharacterName = characterName; } private void manageCharacterError(Throwable error) { - // TODO } - private void onCharacterReceived(Character character) { + private void onCharacterReceived(MarvelCharacter character) { mCharacterDetailView.bindCharacter(character); + + if (character.getDescription() != null && !character.getDescription().equals("")) + mCharacterDetailView.enableScroll(); } public void onComicsIndicatorPressed() { - CollectionActivity.start(mActivityContext, mCharacterId, CollectionItem.COMIC); + mCharacterDetailView.goToCharacterComicsView(mCharacterId); } public void onSeriesIndicatorPressed() { - CollectionActivity.start(mActivityContext, mCharacterId, CollectionItem.SERIES); + mCharacterDetailView.goToCharacterSeriesView(mCharacterId); } public void onStoriesIndicatorPressed() { - CollectionActivity.start(mActivityContext, mCharacterId, CollectionItem.STORY); + mCharacterDetailView.goToCharacterStoriesView(mCharacterId); } public void onEventIndicatorPressed() { - CollectionActivity.start(mActivityContext, mCharacterId, CollectionItem.EVENT); + mCharacterDetailView.goToCharacterEventsView(mCharacterId); } public void setCharacterId(int characterId) { mCharacterId = characterId; } - public void onImageReceived(Bitmap resource) { + public void onImageReceived() { mCharacterDetailView.hideRevealViewByAlpha(); - mCharacterDetailView.initActivityColors(resource); } } \ No newline at end of file diff --git a/app/src/main/java/saulmm/avengers/mvp/presenters/CharacterListPresenter.java b/app/src/main/java/saulmm/avengers/mvp/presenters/CharacterListPresenter.java index 36dc588..6eee80c 100644 --- a/app/src/main/java/saulmm/avengers/mvp/presenters/CharacterListPresenter.java +++ b/app/src/main/java/saulmm/avengers/mvp/presenters/CharacterListPresenter.java @@ -9,10 +9,8 @@ import java.util.List; import javax.inject.Inject; import rx.Subscription; -import saulmm.avengers.domain.GetCharactersUsecase; -import saulmm.avengers.model.entities.Character; -import saulmm.avengers.model.rest.exceptions.NetworkUknownHostException; -import saulmm.avengers.model.rest.exceptions.ServerErrorException; +import saulmm.avengers.GetCharactersUsecase; +import saulmm.avengers.entities.MarvelCharacter; import saulmm.avengers.mvp.views.CharacterListView; import saulmm.avengers.mvp.views.View; @@ -21,13 +19,12 @@ public class CharacterListPresenter implements Presenter { private boolean mIsTheCharacterRequestRunning; private Subscription mCharactersSubscription; - private List mCharacters; + private List mCharacters = new ArrayList<>(); private CharacterListView mAvengersView; @Inject public CharacterListPresenter(GetCharactersUsecase charactersUsecase) { mCharactersUsecase = charactersUsecase; - mCharacters = new ArrayList<>(); } @Override @@ -62,66 +59,52 @@ public void onListEndReached() { if (!mIsTheCharacterRequestRunning) askForNewCharacters(); } - @SuppressWarnings("Convert2MethodRef") - private void askForCharacters() { + public void askForCharacters() { mIsTheCharacterRequestRunning = true; mAvengersView.hideErrorView(); mAvengersView.showLoadingView(); mCharactersSubscription = mCharactersUsecase.execute() - .subscribe(characters -> { - mCharacters.addAll(characters); - mAvengersView.bindCharacterList(mCharacters); - mAvengersView.showCharacterList(); - mAvengersView.hideEmptyIndicator(); - mIsTheCharacterRequestRunning = false; - - }, error -> { - showErrorView(error); - }); - + .subscribe(this::onCharactersReceived, this::showErrorView); } - private void askForNewCharacters() { + public void onCharactersReceived(List characters) { + mCharacters.addAll(characters); + mAvengersView.bindCharacterList(mCharacters); + mAvengersView.showCharacterList(); + mAvengersView.hideEmptyIndicator(); + mIsTheCharacterRequestRunning = false; + } + + public void askForNewCharacters() { mAvengersView.showLoadingMoreCharactersIndicator(); mIsTheCharacterRequestRunning = true; - mCharactersSubscription = mCharactersUsecase.executeIncreasingOffset() - .subscribe( - - newCharacters -> { - mCharacters.addAll(newCharacters); - mAvengersView.updateCharacterList ( - GetCharactersUsecase.CHARACTERS_LIMIT); - - mAvengersView.hideLoadingIndicator(); - mIsTheCharacterRequestRunning = false; - }, - - error -> { - showGenericError(); - mIsTheCharacterRequestRunning = false; - } - ); + mCharactersSubscription = mCharactersUsecase.execute() + .subscribe(this::onNewCharactersReceived, this::onNewCharactersError); } - private void showErrorView(Throwable error) { - if (error instanceof NetworkUknownHostException) { - mAvengersView.showConnectionErrorMessage(); + private void onNewCharactersError(Throwable error) { + showGenericError(); + mIsTheCharacterRequestRunning = false; + } - } else if (error instanceof ServerErrorException) { - mAvengersView.showServerErrorMessage(); + private void onNewCharactersReceived(List newCharacters) { + mCharacters.addAll(newCharacters); + mAvengersView.updateCharacterList(GetCharactersUsecase.DEFAULT_CHARACTERS_LIMIT); - } else { - mAvengersView.showUknownErrorMessage(); - } + mAvengersView.hideLoadingIndicator(); + mIsTheCharacterRequestRunning = false; + } + public void showErrorView(Throwable error) { + mAvengersView.showUknownErrorMessage(); mAvengersView.hideLoadingMoreCharactersIndicator(); mAvengersView.hideEmptyIndicator(); mAvengersView.hideCharactersList(); } - private void showGenericError() { + public void showGenericError() { mAvengersView.hideLoadingIndicator(); mAvengersView.showLightError(); } diff --git a/app/src/main/java/saulmm/avengers/mvp/presenters/CollectionPresenter.java b/app/src/main/java/saulmm/avengers/mvp/presenters/CollectionPresenter.java index 9879196..374b306 100644 --- a/app/src/main/java/saulmm/avengers/mvp/presenters/CollectionPresenter.java +++ b/app/src/main/java/saulmm/avengers/mvp/presenters/CollectionPresenter.java @@ -3,8 +3,10 @@ import android.content.Context; import java.util.List; import javax.inject.Inject; -import saulmm.avengers.domain.GetCollectionUsecase; -import saulmm.avengers.model.entities.CollectionItem; +import rx.android.schedulers.AndroidSchedulers; +import rx.schedulers.Schedulers; +import saulmm.avengers.GetCollectionUsecase; +import saulmm.avengers.entities.CollectionItem; import saulmm.avengers.mvp.views.CollectionView; import saulmm.avengers.mvp.views.View; @@ -35,8 +37,9 @@ public void attachView(View v) { @Override public void onCreate() { - mGetCollectionUsecase.execute(mCollectionType).subscribe( - this::onCollectionItemsReceived); + mGetCollectionUsecase.setType(mCollectionType); + mGetCollectionUsecase.execute() + .subscribe(this::onCollectionItemsReceived); } public void initialisePresenters(String collectionType, int characterId) { diff --git a/app/src/main/java/saulmm/avengers/mvp/views/CharacterDetailView.java b/app/src/main/java/saulmm/avengers/mvp/views/CharacterDetailView.java index feeb27c..08d1e06 100644 --- a/app/src/main/java/saulmm/avengers/mvp/views/CharacterDetailView.java +++ b/app/src/main/java/saulmm/avengers/mvp/views/CharacterDetailView.java @@ -6,14 +6,25 @@ package saulmm.avengers.mvp.views; import android.graphics.Bitmap; -import saulmm.avengers.model.entities.Character; +import saulmm.avengers.entities.MarvelCharacter; public interface CharacterDetailView extends View { + void disableScroll(); + void hideRevealViewByAlpha(); - void showError(String s); + void bindCharacter(MarvelCharacter character); + + void enableScroll(); + + void goToCharacterComicsView(int characterId); - void bindCharacter(Character character); + void goToCharacterSeriesView(int characterId); + + void goToCharacterEventsView(int characterId); + + void goToCharacterStoriesView(int characterId); + + void showError(String s); - void initActivityColors(Bitmap resource); } diff --git a/app/src/main/java/saulmm/avengers/mvp/views/CharacterListView.java b/app/src/main/java/saulmm/avengers/mvp/views/CharacterListView.java index 4cfc0c7..a3f0eb6 100644 --- a/app/src/main/java/saulmm/avengers/mvp/views/CharacterListView.java +++ b/app/src/main/java/saulmm/avengers/mvp/views/CharacterListView.java @@ -7,12 +7,12 @@ import android.app.ActivityOptions; import java.util.List; -import saulmm.avengers.model.entities.Character; +import saulmm.avengers.entities.MarvelCharacter; @SuppressWarnings("unused") public interface CharacterListView extends View { - void bindCharacterList(List avengers); + void bindCharacterList(List avengers); void showCharacterList(); @@ -38,8 +38,6 @@ public interface CharacterListView extends View { void updateCharacterList(int charactersLimit); - ActivityOptions getActivityOptions(int position, android.view.View clickedView); - void showConnectionErrorMessage(); void showServerErrorMessage(); diff --git a/app/src/main/java/saulmm/avengers/mvp/views/CollectionView.java b/app/src/main/java/saulmm/avengers/mvp/views/CollectionView.java index ab0bc1f..013b043 100644 --- a/app/src/main/java/saulmm/avengers/mvp/views/CollectionView.java +++ b/app/src/main/java/saulmm/avengers/mvp/views/CollectionView.java @@ -1,7 +1,7 @@ package saulmm.avengers.mvp.views; import java.util.List; -import saulmm.avengers.model.entities.CollectionItem; +import saulmm.avengers.entities.CollectionItem; public interface CollectionView extends View { diff --git a/app/src/main/java/saulmm/avengers/utils/OnCharacterImageCallback.java b/app/src/main/java/saulmm/avengers/utils/OnCharacterImageCallback.java new file mode 100644 index 0000000..d40b548 --- /dev/null +++ b/app/src/main/java/saulmm/avengers/utils/OnCharacterImageCallback.java @@ -0,0 +1,7 @@ +package saulmm.avengers.utils; + +import android.graphics.Bitmap; + +public interface OnCharacterImageCallback { + void onReceive(Bitmap bitmap); +} \ No newline at end of file diff --git a/app/src/main/java/saulmm/avengers/views/activities/CharacterDetailActivity.java b/app/src/main/java/saulmm/avengers/views/activities/CharacterDetailActivity.java index 7087722..a292808 100644 --- a/app/src/main/java/saulmm/avengers/views/activities/CharacterDetailActivity.java +++ b/app/src/main/java/saulmm/avengers/views/activities/CharacterDetailActivity.java @@ -12,6 +12,8 @@ import android.databinding.DataBindingUtil; import android.graphics.Bitmap; import android.os.Bundle; +import android.support.design.widget.AppBarLayout; +import android.support.design.widget.CollapsingToolbarLayout; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.graphics.Palette; @@ -20,6 +22,8 @@ import android.view.View; import android.view.ViewTreeObserver; import android.widget.ImageView; + +import butterknife.Bind; import butterknife.BindColor; import butterknife.BindInt; import butterknife.ButterKnife; @@ -30,11 +34,14 @@ import saulmm.avengers.AvengersApplication; import saulmm.avengers.R; import saulmm.avengers.databinding.ActivityAvengerDetailBinding; +import saulmm.avengers.entities.CollectionItem; +import saulmm.avengers.entities.MarvelCharacter; import saulmm.avengers.injector.components.DaggerAvengerInformationComponent; import saulmm.avengers.injector.modules.ActivityModule; import saulmm.avengers.injector.modules.AvengerInformationModule; import saulmm.avengers.mvp.presenters.CharacterDetailPresenter; import saulmm.avengers.mvp.views.CharacterDetailView; +import saulmm.avengers.utils.OnCharacterImageCallback; import saulmm.avengers.utils.TransitionUtils; import saulmm.avengers.views.utils.AnimUtils; @@ -42,6 +49,7 @@ public class CharacterDetailActivity extends AppCompatActivity implements Charac private static final String EXTRA_CHARACTER_NAME = "character.name"; public static final String EXTRA_CHARACTER_ID = "character.id"; + @Bind(R.id.character_collapsing) CollapsingToolbarLayout mCollapsing; @BindInt(R.integer.duration_medium) int mAnimMediumDuration; @BindInt(R.integer.duration_huge) int mAnimHugeDuration; @BindColor(R.color.brand_primary_dark) int mColorPrimaryDark; @@ -49,20 +57,40 @@ public class CharacterDetailActivity extends AppCompatActivity implements Charac @Inject CharacterDetailPresenter mCharacterDetailPresenter; private ActivityAvengerDetailBinding mBinding; + private OnCharacterImageCallback onCharacterImageCallback = new OnCharacterImageCallback() { + + @Override + public void onReceive(Bitmap bitmap) { + initActivityColors(bitmap); + } + }; + + private int mScrolleableAppbarLayoutFlags; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + initializeDependencyInjector(); initializeBinding(); initButterknife(); - initializeDependencyInjector(); initializePresenter(); initToolbar(); initTransitions(); } + @Override + public void disableScroll() { + mScrolleableAppbarLayoutFlags = ((AppBarLayout.LayoutParams) mCollapsing.getLayoutParams()).getScrollFlags(); + AppBarLayout.LayoutParams layoutParams = ((AppBarLayout.LayoutParams) mCollapsing.getLayoutParams()); + layoutParams.setScrollFlags(0); + mCollapsing.setLayoutParams(layoutParams); + } + private void initializeBinding() { mBinding = DataBindingUtil.setContentView( this, R.layout.activity_avenger_detail); + + mBinding.setImageCallback(onCharacterImageCallback); } @@ -135,10 +163,10 @@ private void initializeDependencyInjector() { private void initTransitions() { final String sharedViewName = getIntent().getStringExtra( - CharacterListListActivity.EXTRA_IMAGE_TRANSITION_NAME); + CharacterListActivity.EXTRA_IMAGE_TRANSITION_NAME); String characterTitle = getIntent().getStringExtra( - CharacterListListActivity.EXTRA_CHARACTER_NAME); + CharacterListActivity.EXTRA_CHARACTER_NAME); Transition enterTransition = TransitionUtils.buildSlideTransition(Gravity.BOTTOM); enterTransition.setDuration(mAnimMediumDuration); @@ -167,9 +195,6 @@ public void hideRevealViewByAlpha() { } private void initToolbar() { - mBinding.characterCollapsing.setExpandedTitleTextAppearance( - R.style.Text_CollapsedExpanded); - mBinding.characterToolbar.setNavigationOnClickListener(v -> onBackPressed()); } @@ -183,18 +208,49 @@ public void showError(String errorMessage) { } @Override - public void bindCharacter(saulmm.avengers.model.entities.Character character) { + public void bindCharacter(MarvelCharacter character) { mBinding.setCharacter(character); } - @BindingAdapter({"source", "presenter"}) - public static void setImageSource(ImageView v, String url, CharacterDetailPresenter detailPresenter) { + @Override + public void enableScroll() { + AppBarLayout.LayoutParams layoutParams = (AppBarLayout.LayoutParams) mCollapsing.getLayoutParams(); + layoutParams.setScrollFlags(mScrolleableAppbarLayoutFlags); + mCollapsing.setLayoutParams(layoutParams); + } + + @Override + public void goToCharacterComicsView(int characterId) { + CollectionActivity.start(this, characterId, CollectionItem.COMICS); + } + + @Override + public void goToCharacterSeriesView(int characterId) { + CollectionActivity.start(this, characterId, CollectionItem.SERIES); + } + + @Override + public void goToCharacterEventsView(int characterId) { + CollectionActivity.start(this, characterId, CollectionItem.EVENTS); + } + + @Override + public void goToCharacterStoriesView(int characterId) { + CollectionActivity.start(this, characterId, CollectionItem.STORIES); + } + + @BindingAdapter({"source", "presenter", "callback"}) + public static void setImageSource(ImageView v, String url, + CharacterDetailPresenter detailPresenter, OnCharacterImageCallback imageCallback) { + Glide.with(v.getContext()).load(url).asBitmap().into(new BitmapImageViewTarget(v) { @Override public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { super.onResourceReady(resource, glideAnimation); v.setImageBitmap(resource); - detailPresenter.onImageReceived(resource); + imageCallback.onReceive(resource); + detailPresenter.onImageReceived(); + } }); } diff --git a/app/src/main/java/saulmm/avengers/views/activities/CharacterListListActivity.java b/app/src/main/java/saulmm/avengers/views/activities/CharacterListActivity.java similarity index 91% rename from app/src/main/java/saulmm/avengers/views/activities/CharacterListListActivity.java rename to app/src/main/java/saulmm/avengers/views/activities/CharacterListActivity.java index 610c2d6..3575cbb 100644 --- a/app/src/main/java/saulmm/avengers/views/activities/CharacterListListActivity.java +++ b/app/src/main/java/saulmm/avengers/views/activities/CharacterListActivity.java @@ -24,9 +24,10 @@ import javax.inject.Inject; import saulmm.avengers.AvengersApplication; import saulmm.avengers.R; +import saulmm.avengers.entities.MarvelCharacter; import saulmm.avengers.injector.components.DaggerAvengersComponent; import saulmm.avengers.injector.modules.ActivityModule; -import saulmm.avengers.model.entities.Character; + import saulmm.avengers.mvp.presenters.CharacterListPresenter; import saulmm.avengers.mvp.views.CharacterListView; import saulmm.avengers.utils.Utils; @@ -34,13 +35,11 @@ import saulmm.avengers.views.views.RecyclerInsetsDecoration; -public class CharacterListListActivity extends AppCompatActivity +public class CharacterListActivity extends AppCompatActivity implements CharacterListView { - public final static String EXTRA_CHARACTER_ID = "character_id"; public final static String EXTRA_CHARACTER_NAME = "character_name"; public final static String EXTRA_IMAGE_TRANSITION_NAME = "transition_name"; - public final static int KEY_SHARED_BITMAP = 41; @Bind(R.id.activity_avengers_recycler) RecyclerView mAvengersRecycler; @Bind(R.id.activity_avengers_toolbar) Toolbar mAvengersToolbar; @@ -111,7 +110,7 @@ private void initializeRecyclerView() { } @Override - public void bindCharacterList(List avengers) { + public void bindCharacterList(List avengers) { mCharacterListAdapter = new AvengersListAdapter(avengers, this, (position, sharedView, characterImageView) -> { mAvengersListPresenter.onElementClick(position); @@ -131,7 +130,7 @@ public void showCharacterList() { @Override public void updateCharacterList(int charactersAdded) { mCharacterListAdapter.notifyItemRangeInserted( - mCharacterListAdapter.getItemCount() + charactersAdded, charactersAdded); + mCharacterListAdapter.getItemCount() + charactersAdded, charactersAdded); } @Override @@ -209,13 +208,6 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { } }; - @Override - public ActivityOptions getActivityOptions(int position, View clickedView) { - String sharedViewName = Utils.getListTransitionName(position); - return ActivityOptions.makeSceneTransitionAnimation( - this, clickedView, sharedViewName); - } - @Override public void showConnectionErrorMessage() { TextView errorTextView = ButterKnife.findById(mErrorView, R.id.view_error_message); diff --git a/app/src/main/java/saulmm/avengers/views/activities/CollectionActivity.java b/app/src/main/java/saulmm/avengers/views/activities/CollectionActivity.java index 8bc3764..a254704 100644 --- a/app/src/main/java/saulmm/avengers/views/activities/CollectionActivity.java +++ b/app/src/main/java/saulmm/avengers/views/activities/CollectionActivity.java @@ -18,10 +18,10 @@ import javax.inject.Inject; import saulmm.avengers.AvengersApplication; import saulmm.avengers.R; +import saulmm.avengers.entities.CollectionItem; import saulmm.avengers.injector.components.DaggerAvengerInformationComponent; import saulmm.avengers.injector.modules.ActivityModule; import saulmm.avengers.injector.modules.AvengerInformationModule; -import saulmm.avengers.model.entities.CollectionItem; import saulmm.avengers.mvp.presenters.CollectionPresenter; import saulmm.avengers.mvp.views.CollectionView; diff --git a/app/src/main/java/saulmm/avengers/views/adapter/AvengersListAdapter.java b/app/src/main/java/saulmm/avengers/views/adapter/AvengersListAdapter.java index 06e59ff..d84c122 100644 --- a/app/src/main/java/saulmm/avengers/views/adapter/AvengersListAdapter.java +++ b/app/src/main/java/saulmm/avengers/views/adapter/AvengersListAdapter.java @@ -19,18 +19,20 @@ import com.bumptech.glide.Glide; import java.util.List; import saulmm.avengers.R; +import saulmm.avengers.entities.MarvelCharacter; import saulmm.avengers.utils.Utils; -import saulmm.avengers.model.entities.Character; import saulmm.avengers.views.RecyclerClickListener; public class AvengersListAdapter extends RecyclerView.Adapter { private final String NOT_AVAILABLE_URL = "http://i.annihil.us/u/prod/marvel/i/mg/b/40/image_not_available.jpg"; private final RecyclerClickListener mRecyclerListener; - private final List mCharacters; + private final List mCharacters; private Context mContext; - public AvengersListAdapter(List avengers, Context context, RecyclerClickListener recyclerClickListener) { + public AvengersListAdapter(List avengers, Context context, + RecyclerClickListener recyclerClickListener) { + mCharacters = avengers; mContext = context; mRecyclerListener = recyclerClickListener; @@ -66,7 +68,7 @@ public CharacterViewHolder(View itemView, final RecyclerClickListener recyclerCl bindListener(itemView, recyclerClickListener); } - public void bindAvenger(Character character) { + public void bindAvenger(MarvelCharacter character) { avengerTitleTextView.setText(character.getName()); avengerTitleTextView.setTransitionName(Utils.getListTransitionName(getPosition())); diff --git a/app/src/main/res/drawable/gradient_dark_up.xml b/app/src/main/res/drawable/gradient_dark_up.xml index 218f078..a6f2ac5 100644 --- a/app/src/main/res/drawable/gradient_dark_up.xml +++ b/app/src/main/res/drawable/gradient_dark_up.xml @@ -3,8 +3,8 @@ \ No newline at end of file diff --git a/app/src/main/res/layout/activity_avenger_detail.xml b/app/src/main/res/layout/activity_avenger_detail.xml index d693b21..ddf285f 100644 --- a/app/src/main/res/layout/activity_avenger_detail.xml +++ b/app/src/main/res/layout/activity_avenger_detail.xml @@ -8,10 +8,11 @@ - + - + + @@ -29,6 +30,7 @@ android:layout_height="wrap_content" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:fitsSystemWindows="true" + app:elevation="0dp" > - + - - - + + + + + + + - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index df4635f..c2c2216 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -42,11 +42,10 @@ 0dp -