diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..81b4fd15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,88 @@ +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# Android Profiling +*.hprof \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 00000000..d51775f7 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +StarWarsWiki \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..61a9130c --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 00000000..a5f05cd8 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..d5d35ec4 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 00000000..a0351b31 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,69 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' + id 'kotlin-android-extensions' + id 'kotlin-kapt' +} + +android { + compileSdkVersion 29 + buildToolsVersion "30.0.2" + + defaultConfig { + applicationId "br.com.challenge.android.starwarswiki" + minSdkVersion 21 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.3.2' + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + + // to support retrofit and viewModel implementations + implementation 'com.google.code.gson:gson:2.8.6' + implementation 'io.reactivex.rxjava2:rxkotlin:2.2.0' + implementation 'io.reactivex.rxjava2:rxjava:2.2.18' + implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' + + // Room and Lifecycle dependencies + kapt 'androidx.room:room-compiler:2.2.5' + implementation 'androidx.room:room-runtime:2.2.5' + implementation 'androidx.room:room-ktx:2.2.5' + implementation 'androidx.room:room-rxjava2:2.2.5' + implementation 'android.arch.persistence.room:rxjava2:2.2.5' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0' + + // retrofit libraries + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' + implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0' + + testImplementation 'junit:junit:4.13.1' + + androidTestImplementation 'androidx.test.ext:junit:1.1.2' +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/br/com/challenge/android/starwarswiki/ExampleInstrumentedTest.kt b/app/src/androidTest/java/br/com/challenge/android/starwarswiki/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..a7093e85 --- /dev/null +++ b/app/src/androidTest/java/br/com/challenge/android/starwarswiki/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package br.com.challenge.android.starwarswiki + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("br.com.challenge.android.starwarswiki", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a69a49ab --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/MainActivity.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/MainActivity.kt new file mode 100644 index 00000000..ac94cbe5 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/MainActivity.kt @@ -0,0 +1,36 @@ +package br.com.challenge.android.starwarswiki + +import android.os.Bundle +import android.widget.FrameLayout +import androidx.appcompat.app.AppCompatActivity +import br.com.challenge.android.starwarswiki.model.domain.Person +import br.com.challenge.android.starwarswiki.view.PeopleListFragment +import br.com.challenge.android.starwarswiki.view.PersonItemClickListener + +class MainActivity : AppCompatActivity(), PersonItemClickListener { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + if(findViewById(R.id.fragments_placeholder) != null) { + if (savedInstanceState != null) { + return + } + + supportFragmentManager.beginTransaction() + .add(R.id.fragments_placeholder, PeopleListFragment(this)) + .commit() + } + } + + override fun onClick(person: Person) { + if(findViewById(R.id.fragments_placeholder) != null) { + + /*supportFragmentManager.beginTransaction().replace(R.id.fragments_placeholder, MovieDetailsFragment(movie)) + .addToBackStack("Details") + .commit()*/ + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/api/ApiRetrofitService.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/api/ApiRetrofitService.kt new file mode 100644 index 00000000..edb71df0 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/api/ApiRetrofitService.kt @@ -0,0 +1,50 @@ +package br.com.challenge.android.starwarswiki.model.api + +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import retrofit2.converter.gson.GsonConverterFactory + +class ApiRetrofitService { + companion object { + private const val SWAPI_BASE_URL = "https://swapi.dev/api/" + @Volatile + private var INSTANCE: Retrofit? = null + + fun getInstance(): Retrofit { + + synchronized(this) { + var instance = INSTANCE + + if (instance == null) { + instance = Retrofit.Builder() + .baseUrl(SWAPI_BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .client(getOkHttpClientBuilder().build()) + .build() + + INSTANCE = instance + } + + return instance!! + } + + } + + private fun getOkHttpClientBuilder(): OkHttpClient.Builder { + val okHttpBuilder = OkHttpClient.Builder() + + okHttpBuilder.addInterceptor {chain -> + val request = chain.request().newBuilder() + val originalHttpUrl = chain.request().url() + + request.url(originalHttpUrl) + + return@addInterceptor chain.proceed(request.build()) + } + + return okHttpBuilder + } + } +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/api/PeopleReturned.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/api/PeopleReturned.kt new file mode 100644 index 00000000..b275bec7 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/api/PeopleReturned.kt @@ -0,0 +1,9 @@ +package br.com.challenge.android.starwarswiki.model.api + +import br.com.challenge.android.starwarswiki.model.data.dto.ApiPerson +import com.google.gson.annotations.SerializedName + +data class PeopleReturned( + val countPages: Int, + val results: ArrayList +) \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/api/SwapiService.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/api/SwapiService.kt new file mode 100644 index 00000000..a17fc534 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/api/SwapiService.kt @@ -0,0 +1,18 @@ +package br.com.challenge.android.starwarswiki.model.api + +import io.reactivex.Observable +import retrofit2.http.GET +import retrofit2.http.Query + +interface SwapiService { + + @GET("people/") + fun getAllPeople( + @Query("page") + pageNumber: Int): Observable + + @GET("people/") + fun getPersonByName( + @Query("search") + name: String): Observable +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/dto/ApiPerson.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/dto/ApiPerson.kt new file mode 100644 index 00000000..ecb32138 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/dto/ApiPerson.kt @@ -0,0 +1,26 @@ +package br.com.challenge.android.starwarswiki.model.data.dto + +import com.google.gson.annotations.SerializedName + +data class ApiPerson( + @SerializedName("name") + val name: String, + @SerializedName("birth_year") + val birthYear: String, + @SerializedName("eye_color") + val eyeColor: String, + @SerializedName("gender") + val gender: String, + @SerializedName("hair_color") + val hairColor: String, + @SerializedName("height") + val height: String, + @SerializedName("mass") + val mass: String, + @SerializedName("skin_color") + val skinColor: String, + @SerializedName("homeworld") + val homeWorld: String/*, + @SerializedName("species") + val species: ArrayList*/ +) \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/dto/ApiPlanet.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/dto/ApiPlanet.kt new file mode 100644 index 00000000..195b1b67 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/dto/ApiPlanet.kt @@ -0,0 +1,8 @@ +package br.com.challenge.android.starwarswiki.model.data.dto + +import com.google.gson.annotations.SerializedName + +data class ApiPlanet( + @SerializedName("name") + val name: String +) \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/dto/ApiSpecie.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/dto/ApiSpecie.kt new file mode 100644 index 00000000..dac9b2f8 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/dto/ApiSpecie.kt @@ -0,0 +1,8 @@ +package br.com.challenge.android.starwarswiki.model.data.dto + +import com.google.gson.annotations.SerializedName + +data class ApiSpecie( + @SerializedName("name") + val name: String +) \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/ApiPersonDataMapper.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/ApiPersonDataMapper.kt new file mode 100644 index 00000000..8a8ed94d --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/ApiPersonDataMapper.kt @@ -0,0 +1,23 @@ +package br.com.challenge.android.starwarswiki.model.data.mapper + +import br.com.challenge.android.starwarswiki.model.data.dto.ApiPerson +import br.com.challenge.android.starwarswiki.model.domain.Person + +class ApiPersonDataMapper: Mapper { + + override fun map(input: ApiPerson): Person { + return Person( + name = input.name, + birthYear = input.birthYear, + eyeColor = input.eyeColor, + gender = input.gender, + hairColor = input.hairColor, + height = input.height, + mass = input.mass, + skinColor = input.skinColor, + homeWorld = input.homeWorld/*, + species = input.species*/ + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/DaoPersonDataMapper.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/DaoPersonDataMapper.kt new file mode 100644 index 00000000..b32928e9 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/DaoPersonDataMapper.kt @@ -0,0 +1,23 @@ +package br.com.challenge.android.starwarswiki.model.data.mapper + +import br.com.challenge.android.starwarswiki.model.database.PersonEntity +import br.com.challenge.android.starwarswiki.model.domain.Person + +class DaoPersonDataMapper: Mapper { + + override fun map(input: PersonEntity): Person { + return Person( + name = input.name, + birthYear = input.birthYear, + eyeColor = input.eyeColor, + gender = input.gender, + hairColor = input.hairColor, + height = input.height, + mass = input.mass, + skinColor = input.skinColor, + homeWorld = input.homeWorld/*, + species = input.species*/ + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/ListMapper.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/ListMapper.kt new file mode 100644 index 00000000..c353c42a --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/ListMapper.kt @@ -0,0 +1,3 @@ +package br.com.challenge.android.starwarswiki.model.data.mapper + +interface ListMapper: Mapper, List> \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/ListMapperImpl.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/ListMapperImpl.kt new file mode 100644 index 00000000..e1406b7f --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/ListMapperImpl.kt @@ -0,0 +1,11 @@ +package br.com.challenge.android.starwarswiki.model.data.mapper + +class ListMapperImpl(private val mapper: Mapper): ListMapper { + + override fun map(input: List): List { + return input.map { + mapper.map(it) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/Mapper.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/Mapper.kt new file mode 100644 index 00000000..fce015bf --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/data/mapper/Mapper.kt @@ -0,0 +1,5 @@ +package br.com.challenge.android.starwarswiki.model.data.mapper + +interface Mapper { + fun map(input: I): O +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/database/PersonDatabase.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/database/PersonDatabase.kt new file mode 100644 index 00000000..924f54e5 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/database/PersonDatabase.kt @@ -0,0 +1,42 @@ +package br.com.challenge.android.starwarswiki.model.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase + +@Database( + entities = [PersonEntity::class], + version = 1, + exportSchema = false) +abstract class PersonDatabase: RoomDatabase() { + /** + * Connects the database to the DAO. + */ + abstract val personDatabaseDao: PersonDatabaseDao + + companion object { + @Volatile + private var INSTANCE: PersonDatabase? = null + + fun getInstance(context: Context): PersonDatabase { + synchronized(this) { + var instance = INSTANCE + + if (instance == null) { + instance = Room.databaseBuilder( + context.applicationContext, + PersonDatabase::class.java, + "person_history_database" + ).fallbackToDestructiveMigration().build() + + INSTANCE = instance + } + + return instance + } + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/database/PersonDatabaseDao.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/database/PersonDatabaseDao.kt new file mode 100644 index 00000000..6ffcee11 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/database/PersonDatabaseDao.kt @@ -0,0 +1,43 @@ +package br.com.challenge.android.starwarswiki.model.database + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query +import androidx.room.Update +import io.reactivex.Observable + +@Dao +interface PersonDatabaseDao { + + @Insert + fun insert(person: PersonEntity) + + /** + * When updating a row with a value already set in a column, + * replaces the old value with the new one. + * + * @param person new value to write + */ + @Update + fun update(person: PersonEntity) + + @Query("SELECT * from person WHERE name = :keyName") + fun get(keyName: String): Observable> + + /** + * Deletes all values from the table. + * + * This does not delete the table, only its contents. + */ + @Query("DELETE FROM person") + fun clear() + + /** + * Selects and returns all rows in the table, + * + * Sorted by name in descending order. + */ + @Query("SELECT * FROM person ORDER BY name DESC") + fun getPeople(): Observable> + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/database/PersonEntity.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/database/PersonEntity.kt new file mode 100644 index 00000000..2402d135 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/database/PersonEntity.kt @@ -0,0 +1,40 @@ +package br.com.challenge.android.starwarswiki.model.database + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "person") +data class PersonEntity( + + @PrimaryKey + @ColumnInfo(name = "name") + var name: String, + + @ColumnInfo(name = "birth_year") + var birthYear: String, + + @ColumnInfo(name = "eye_color") + var eyeColor: String, + + @ColumnInfo(name = "gender") + var gender: String, + + @ColumnInfo(name = "hair_color") + var hairColor: String, + + @ColumnInfo(name = "height") + var height: String, + + @ColumnInfo(name = "mass") + var mass: String, + + @ColumnInfo(name = "skin_color") + var skinColor: String, + + @ColumnInfo(name = "home_world") + var homeWorld: String/*, + + @ColumnInfo(name = "species") + var species: Species*/ +) \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/domain/Person.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/domain/Person.kt new file mode 100644 index 00000000..fc422def --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/domain/Person.kt @@ -0,0 +1,14 @@ +package br.com.challenge.android.starwarswiki.model.domain + +data class Person( + val name: String, + val birthYear: String, + val eyeColor: String, + val gender: String, + val hairColor: String, + val height: String, + val mass: String, + val skinColor: String, + val homeWorld: String/*, + val species: ArrayList*/ +) \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/domain/Planet.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/domain/Planet.kt new file mode 100644 index 00000000..485345cc --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/domain/Planet.kt @@ -0,0 +1,5 @@ +package br.com.challenge.android.starwarswiki.model.domain + +data class Planet( + val name: String +) \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/domain/Species.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/domain/Species.kt new file mode 100644 index 00000000..ea0717fd --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/domain/Species.kt @@ -0,0 +1,5 @@ +package br.com.challenge.android.starwarswiki.model.domain + +data class Species( + val names: List +) \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/repository/PersonRepository.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/repository/PersonRepository.kt new file mode 100644 index 00000000..7e552101 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/repository/PersonRepository.kt @@ -0,0 +1,12 @@ +package br.com.challenge.android.starwarswiki.model.repository + +import br.com.challenge.android.starwarswiki.model.domain.Person +import io.reactivex.Observable + +interface PersonRepository { + + fun getPersonByName(name: String): Observable> + + fun getPeople(page: Int): Observable> + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/model/repository/PersonRepositoryImpl.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/model/repository/PersonRepositoryImpl.kt new file mode 100644 index 00000000..a98381b2 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/model/repository/PersonRepositoryImpl.kt @@ -0,0 +1,47 @@ +package br.com.challenge.android.starwarswiki.model.repository + +import android.content.Context +import br.com.challenge.android.starwarswiki.model.api.ApiRetrofitService +import br.com.challenge.android.starwarswiki.model.api.SwapiService +import br.com.challenge.android.starwarswiki.model.data.dto.ApiPerson +import br.com.challenge.android.starwarswiki.model.data.mapper.ListMapper +import br.com.challenge.android.starwarswiki.model.database.PersonDatabase +import br.com.challenge.android.starwarswiki.model.database.PersonDatabaseDao +import br.com.challenge.android.starwarswiki.model.domain.Person +import br.com.challenge.android.starwarswiki.utils.CheckNetwork +import io.reactivex.Observable + +class PersonRepositoryImpl( + private val listApiMapper: ListMapper, + context: Context +): PersonRepository { + private var personDatabaseDao: PersonDatabaseDao + + init { + CheckNetwork().checkIfDeviceIsReadyToConnectInternet(context) + personDatabaseDao = PersonDatabase.getInstance(context).personDatabaseDao + } + + override fun getPersonByName(name: String): Observable> { + val personByName = getRetrofitService().getPersonByName(name) + + return personByName.map{ + listApiMapper.map(it.results) + } + + } + + override fun getPeople(page: Int): Observable> { + val allPeople = getRetrofitService().getAllPeople(page) + + return allPeople.map{ + listApiMapper.map(it.results) + } + + } + + private fun getRetrofitService(): SwapiService { + return ApiRetrofitService.getInstance().create(SwapiService::class.java) + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/utils/CheckNetwork.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/utils/CheckNetwork.kt new file mode 100644 index 00000000..0dbf13eb --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/utils/CheckNetwork.kt @@ -0,0 +1,104 @@ +package br.com.challenge.android.starwarswiki.utils + +import android.content.Context +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkRequest +import java.io.IOException +import java.net.HttpURLConnection +import java.net.URL + +// Reference: +// https://gist.github.com/PasanBhanu/730a32a9eeb180ec2950c172d54bb06a + +class CheckNetwork { + + companion object { + var isNetworkConnected = false + const val ERROR_INTERNET_NOT_AVAILABLE = "Error: Internet connection not available" + } + + fun checkIfDeviceIsReadyToConnectInternet(context: Context) { + // at first checking using the old approach + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) { + verifyInternetEnabledAndConnecting(context) + } else { + registerNetworkCallback(context) + } + +// if(!isNetworkConnected) { +// Toast +// .makeText( +// context, +// "Please make sure the device has an internet connection.", Toast.LENGTH_LONG) +// .show() +// } + } + + private fun verifyInternetEnabledAndConnecting(context: Context) { + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + if (connectivityManager.activeNetworkInfo != null) { + try { + val urlc = URL("http://clients3.google.com/generate_204") + .openConnection() as HttpURLConnection + + urlc.setRequestProperty("User-Agent", "Android") + urlc.setRequestProperty("Connection", "close") + + urlc.connectTimeout = 1500 + + urlc.connect() + + isNetworkConnected = urlc.responseCode == 204 && urlc.contentLength == 0 + } catch (e: IOException) { + isNetworkConnected = false + + e.printStackTrace() + } + + return + } + + isNetworkConnected = false + } + + private fun registerNetworkCallback(context: Context) { + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + val connectivityManagerNetworkCallback = object : ConnectivityManager.NetworkCallback() { + val activeNetworks: MutableList = mutableListOf() + + override fun onAvailable(network: Network) { + super.onAvailable(network) + + // Add to list of active networks if not already in list + if (activeNetworks + .none {activeNetwork -> + activeNetwork.networkHandle == network.networkHandle + }) { + activeNetworks.add(network) + } + + isNetworkConnected = activeNetworks.isNotEmpty() + } + + override fun onLost(network: Network) { + super.onLost(network) + + // Remove network from active network list + activeNetworks.removeAll {activeNetwork -> + activeNetwork.networkHandle == network.networkHandle + } + + isNetworkConnected = activeNetworks.isNotEmpty() + } + } + + connectivityManager.registerNetworkCallback(NetworkRequest.Builder().build(), connectivityManagerNetworkCallback) + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/view/PeopleListFragment.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/view/PeopleListFragment.kt new file mode 100644 index 00000000..bfeafca8 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/view/PeopleListFragment.kt @@ -0,0 +1,45 @@ +package br.com.challenge.android.starwarswiki.view + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.viewpager2.widget.ViewPager2 +import br.com.challenge.android.starwarswiki.R +import br.com.challenge.android.starwarswiki.view.adapter.TypeOfListFragmentTabAdapter +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator + +class PeopleListFragment(private val callback: PersonItemClickListener): Fragment() { + private lateinit var typeOfListFragmentTabAdapter: TypeOfListFragmentTabAdapter + private lateinit var viewPager: ViewPager2 + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_people, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + typeOfListFragmentTabAdapter = TypeOfListFragmentTabAdapter(this, callback) + viewPager = view.findViewById(R.id.pager) + viewPager.adapter = typeOfListFragmentTabAdapter + + val tabLayout = view.findViewById(R.id.tab_layout) as TabLayout + + TabLayoutMediator(tabLayout, viewPager) { tab, position -> + when(position) { + 0 -> { + tab.text = "People" + } + else -> { + tab.text = "Favorites" + } + } + + }.attach() + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/view/PersonFragment.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/view/PersonFragment.kt new file mode 100644 index 00000000..9f5a3072 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/view/PersonFragment.kt @@ -0,0 +1,59 @@ +package br.com.challenge.android.starwarswiki.view + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.GridView +import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider +import br.com.challenge.android.starwarswiki.R +import br.com.challenge.android.starwarswiki.view.adapter.PeopleGridViewAdapter +import br.com.challenge.android.starwarswiki.viewmodel.PeopleViewModel +import br.com.challenge.android.starwarswiki.viewmodel.Status +import br.com.challenge.android.starwarswiki.viewmodel.ViewModelFactory + +class PersonFragment(private val callback: PersonItemClickListener) : Fragment() { + private lateinit var gridViewAdapter: PeopleGridViewAdapter + private lateinit var personViewModel: PeopleViewModel + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_person, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val moviesGridView = view.findViewById(R.id.peopleGridView) + val inflater = LayoutInflater.from(view.context) + gridViewAdapter = PeopleGridViewAdapter(inflater, ArrayList(), callback) + + setupViewModel(view.context) + setupObserver() + + moviesGridView.emptyView = view.findViewById(R.id.emptyGridTextView) + moviesGridView.adapter = gridViewAdapter + } + + private fun setupViewModel(viewContext: Context){ + personViewModel = ViewModelProvider( + this, + ViewModelFactory(viewContext) + ).get(PeopleViewModel::class.java) + } + + private fun setupObserver(){ + personViewModel.peopleLiveData.observe(this, { + when(it.status) { + Status.LOADING -> {} + Status.SUCCESS -> { + gridViewAdapter.refreshData(it.data!!) + } + Status.ERROR -> {} + } + }) + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/view/PersonItemClickListener.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/view/PersonItemClickListener.kt new file mode 100644 index 00000000..15ddd8c8 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/view/PersonItemClickListener.kt @@ -0,0 +1,7 @@ +package br.com.challenge.android.starwarswiki.view + +import br.com.challenge.android.starwarswiki.model.domain.Person + +interface PersonItemClickListener { + fun onClick(person: Person) +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/view/adapter/PeopleGridViewAdapter.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/view/adapter/PeopleGridViewAdapter.kt new file mode 100644 index 00000000..849b748d --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/view/adapter/PeopleGridViewAdapter.kt @@ -0,0 +1,65 @@ +package br.com.challenge.android.starwarswiki.view.adapter + +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import br.com.challenge.android.starwarswiki.R +import br.com.challenge.android.starwarswiki.model.domain.Person +import br.com.challenge.android.starwarswiki.view.PersonItemClickListener + +class PeopleGridViewAdapter( + private val inflaterFromAppContext: LayoutInflater, + private var people: ArrayList, + private val callback: PersonItemClickListener +): BaseAdapter() { + + fun refreshData(people: ArrayList) { + this.people = people + notifyDataSetChanged() + } + + override fun getCount(): Int { + return people.size + } + + override fun getItem(p0: Int): Person { + return people[p0] + } + + override fun getItemId(p0: Int): Long { + return getItem(p0).name.hashCode().toLong() + } + + override fun getView(p0: Int, p1: View?, p2: ViewGroup?): View { + val viewHolderItem: PersonInGridViewHolderItem + var view = p1 + + if(p1 == null) { + view = inflaterFromAppContext.inflate(R.layout.grid_item_person, null) + viewHolderItem = PersonInGridViewHolderItem(view) + + } else { + viewHolderItem = p1.tag as PersonInGridViewHolderItem + } + + val item = getItem(p0) + + viewHolderItem.personNameInItem.text = item.name + viewHolderItem.personHeightInItem.text = item.height + viewHolderItem.personGenderInItem.text = item.gender + viewHolderItem.personMassInItem.text = item.mass + + Log.d("DebugPerson","Inside PeopleGridViewAdapter, viewHolderItem name text: ${viewHolderItem.personNameInItem.text}") + + view!!.setOnClickListener{ + callback.onClick(item) + } + + view.tag = viewHolderItem + + return view + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/view/adapter/PersonInGridViewHolderItem.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/view/adapter/PersonInGridViewHolderItem.kt new file mode 100644 index 00000000..0899de6e --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/view/adapter/PersonInGridViewHolderItem.kt @@ -0,0 +1,13 @@ +package br.com.challenge.android.starwarswiki.view.adapter + +import android.view.View +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import br.com.challenge.android.starwarswiki.R + +class PersonInGridViewHolderItem(view: View): RecyclerView.ViewHolder(view) { + var personNameInItem: TextView = view.findViewById(R.id.personNameTextView) + var personHeightInItem: TextView = view.findViewById(R.id.personHeightTextView) + var personGenderInItem: TextView = view.findViewById(R.id.personGenderTextView) + var personMassInItem: TextView = view.findViewById(R.id.personMassTextView) +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/view/adapter/TypeOfListFragmentTabAdapter.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/view/adapter/TypeOfListFragmentTabAdapter.kt new file mode 100644 index 00000000..1b71147a --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/view/adapter/TypeOfListFragmentTabAdapter.kt @@ -0,0 +1,27 @@ +package br.com.challenge.android.starwarswiki.view.adapter + +import androidx.fragment.app.Fragment +import androidx.viewpager2.adapter.FragmentStateAdapter +import br.com.challenge.android.starwarswiki.view.PersonFragment +import br.com.challenge.android.starwarswiki.view.PersonItemClickListener + +class TypeOfListFragmentTabAdapter( + fragmentRelatedToAdapter: Fragment, + private val callback: PersonItemClickListener): FragmentStateAdapter(fragmentRelatedToAdapter) { + + override fun getItemCount(): Int { + return 2 + } + + override fun createFragment(position: Int): Fragment { + return when(position) { + 0 -> { + return PersonFragment(callback) + } + else -> { + return PersonFragment(callback) // TODO favorites + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/PeopleViewModel.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/PeopleViewModel.kt new file mode 100644 index 00000000..59cef4fe --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/PeopleViewModel.kt @@ -0,0 +1,74 @@ +package br.com.challenge.android.starwarswiki.viewmodel + +import android.content.Context +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import br.com.challenge.android.starwarswiki.model.data.dto.ApiPerson +import br.com.challenge.android.starwarswiki.model.data.mapper.ApiPersonDataMapper +import br.com.challenge.android.starwarswiki.model.data.mapper.DaoPersonDataMapper +import br.com.challenge.android.starwarswiki.model.data.mapper.ListMapper +import br.com.challenge.android.starwarswiki.model.data.mapper.ListMapperImpl +import br.com.challenge.android.starwarswiki.model.database.PersonEntity +import br.com.challenge.android.starwarswiki.model.domain.Person +import br.com.challenge.android.starwarswiki.model.repository.PersonRepositoryImpl +import br.com.challenge.android.starwarswiki.utils.CheckNetwork +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers + +class PeopleViewModel(private val appContext: Context): ViewModel() { + // region Declaring properties + + private val listApiMapperImpl: ListMapper + private val listDaoMapperImpl: ListMapper + private val repositoryImpl: PersonRepositoryImpl + + private val _peopleMutableLiveData = MutableLiveData>>() + val peopleLiveData: LiveData>> = _peopleMutableLiveData + + private val compositeDisposable = CompositeDisposable() + + // endregion + + init { + listApiMapperImpl = ListMapperImpl(ApiPersonDataMapper()) + listDaoMapperImpl = ListMapperImpl(DaoPersonDataMapper()) + repositoryImpl = PersonRepositoryImpl(listApiMapperImpl, appContext) + + synchronized(this) { + fetchPeople() + } + } + + private fun fetchPeople() { + val disposablePeople = repositoryImpl + .getPeople(1) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _peopleMutableLiveData.postValue(Resource.success(it as ArrayList)) + }, + { + CheckNetwork().checkIfDeviceIsReadyToConnectInternet(appContext) + + if (CheckNetwork.isNetworkConnected) { + _peopleMutableLiveData.postValue(Resource.error(it.message?: "", null)) + } else { + _peopleMutableLiveData.postValue( + Resource.error( + CheckNetwork.ERROR_INTERNET_NOT_AVAILABLE, + null)) + } + } + ) + + compositeDisposable.add(disposablePeople) + } + + override fun onCleared() { + super.onCleared() + compositeDisposable.dispose() + } +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/Resource.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/Resource.kt new file mode 100644 index 00000000..1aa7c168 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/Resource.kt @@ -0,0 +1,21 @@ +package br.com.challenge.android.starwarswiki.viewmodel + +data class Resource(val status: Status, val data: T?, val message: String?) { + + companion object { + + fun success(data: T): Resource { + return Resource(Status.SUCCESS, data, null) + } + + fun error(msg: String, data: T?): Resource { + return Resource(Status.ERROR, data, msg) + } + + fun loading(data: T?): Resource { + return Resource(Status.LOADING, data, null) + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/Status.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/Status.kt new file mode 100644 index 00000000..a0b7a428 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/Status.kt @@ -0,0 +1,7 @@ +package br.com.challenge.android.starwarswiki.viewmodel + +enum class Status { + SUCCESS, + ERROR, + LOADING +} \ No newline at end of file diff --git a/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/ViewModelFactory.kt b/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/ViewModelFactory.kt new file mode 100644 index 00000000..f799f434 --- /dev/null +++ b/app/src/main/java/br/com/challenge/android/starwarswiki/viewmodel/ViewModelFactory.kt @@ -0,0 +1,17 @@ +package br.com.challenge.android.starwarswiki.viewmodel + +import android.content.Context +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider + +class ViewModelFactory(private val appContext: Context): ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + if(modelClass.isAssignableFrom(PeopleViewModel::class.java)) { + return PeopleViewModel(appContext) as T + } + + throw IllegalArgumentException("Unknown class name!") + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..2b068d11 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..07d5da9c --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..720e34f5 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_people.xml b/app/src/main/res/layout/fragment_people.xml new file mode 100644 index 00000000..301223e3 --- /dev/null +++ b/app/src/main/res/layout/fragment_people.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_person.xml b/app/src/main/res/layout/fragment_person.xml new file mode 100644 index 00000000..1347cc27 --- /dev/null +++ b/app/src/main/res/layout/fragment_person.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/grid_item_person.xml b/app/src/main/res/layout/grid_item_person.xml new file mode 100644 index 00000000..77a1e1ab --- /dev/null +++ b/app/src/main/res/layout/grid_item_person.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..eca70cfe --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..a571e600 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000..61da551c Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..c41dd285 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000..db5080a7 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..6dba46da Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000..da31a871 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..15ac6817 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..b216f2d3 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000..f25a4197 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000..e96783cc Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..943706a0 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..0136589c --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,13 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + #FFE81F + #0c2c43 + #fb7a24 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..32714c92 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + StarWarsWiki + Hello blank fragment + The list of people is empty! + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..7dfc92bc --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/test/java/br/com/challenge/android/starwarswiki/ExampleUnitTest.kt b/app/src/test/java/br/com/challenge/android/starwarswiki/ExampleUnitTest.kt new file mode 100644 index 00000000..f55e7ace --- /dev/null +++ b/app/src/test/java/br/com/challenge/android/starwarswiki/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package br.com.challenge.android.starwarswiki + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..24d13823 --- /dev/null +++ b/build.gradle @@ -0,0 +1,26 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext.kotlin_version = "1.4.10" + repositories { + google() + jcenter() + } + dependencies { + classpath "com.android.tools.build:gradle:4.1.0" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..98bed167 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f6b961fd Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..b911ae6e --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Oct 31 09:46:18 BRT 2020 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip diff --git a/gradlew b/gradlew new file mode 100644 index 00000000..cccdd3d5 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..f9553162 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..f1527c43 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include ':app' +rootProject.name = "StarWarsWiki" \ No newline at end of file