Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Android Heartstone Assessment Solution #9

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild
7 changes: 7 additions & 0 deletions .idea/dictionaries/Maestro.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions .idea/runConfigurations.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
## Explanatory note

This is the Android App for viewing Heartstone Cards. It displays the cards in a grid and provides additional details for each one of them. You can also add and manage favorite cards, also it is possible to filter to show only `Legendary` cards with the `Deathrattle Mechanic`. Some cards have a golden version, including animated picture, so you can see it by clicking `Gold card` button.

The data is dynamically fetched from [Github server](https://raw.githubusercontent.com/maestromaster/HeartstoneAssessment/master/cards.json) and getting stored in the App's database, so the App requires internet connection at the first launch.

###### Tools
For creating app the next tools were used: Dagger 2, RxJava 2, Retrofit 2, Android Architecture Components, Mockito.

###### Architecture
The App is built using MVVM architecture since it is officially recommended by Android Developers on Google #io17. And it also provides possibility for quick UI modifications without involving changes in the logical part.

One of the achievements was to provide clean, maintainable production code, with no Android Studio warnings and following [SOLID principles](https://en.wikipedia.org/wiki/SOLID_(object-oriented_design)). App correctly handles errors, so user can understand what goes wrong by provided message.

###### User interface
UI is built using fragments, keeping in mind that the App might be adapted for tablets by Master-Detail pattern, or fragment can be re-used as a Popup. Big images are loaded using Glide to prevent OOM exceptions. The App is built keeping in mind the view life-cycles, so the data survives configuration changes and won't request server again. It was achieved by using LiveData.

###### External libraries
The app uses Glide to load images from internet. The reason of chosing this library was the support of animated gif, which are used for golden cards.


## Introduction

Hsiao here at Splendo is a very enthusiastic casual Hearthstone player. He is also a user of the KLM houses apps ([iOS](https://itunes.apple.com/nl/app/klm-houses/id371664245?l=en&mt=8) / [Android](https://play.google.com/store/apps/details?id=com.klm.mobile.houses&hl=en))
Expand Down
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
101 changes: 101 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 27
defaultConfig {
applicationId "com.lakhmotkin.heartstonecards"
minSdkVersion 21
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
targetCompatibility 1.8
sourceCompatibility 1.8
}
dataBinding {
enabled = true
}
}

project.ext {
archVersion = "1.1.0"
roomVersion = "1.0.0"
retrofitVersion = "2.3.0"
daggerVersion = "2.11"
supportVersion = "27.0.2"
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.12'

// TESTING
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test:rules:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.2.1') {
exclude group: 'com.android.support', module: 'appcompat'
exclude group: 'com.android.support', module: 'support-v4'
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'recyclerview-v7'
}
testCompile("android.arch.core:core-testing:$project.archVersion", {
exclude group: 'com.android.support', module: 'support-compat'
exclude group: 'com.android.support', module: 'support-annotations'
exclude group: 'com.android.support', module: 'support-core-utils'
})
testCompile 'org.mockito:mockito-core:2.7.22'
testImplementation "android.arch.core:core-testing:$project.archVersion"
testImplementation "android.arch.persistence.room:testing:$project.roomVersion"

// UI
compile "com.android.support:appcompat-v7:$project.supportVersion"
compile "com.android.support:cardview-v7:$project.supportVersion"
compile "com.android.support:recyclerview-v7:$project.supportVersion"
compile "com.android.support:design:$project.supportVersion"
compile "com.android.support:support-v4:$project.supportVersion"
compile "com.android.support.constraint:constraint-layout:1.0.2"
compile 'de.hdodenhof:circleimageview:2.1.0'

// Dagger
compile "com.google.dagger:dagger:$project.daggerVersion"
compile "com.google.dagger:dagger-android:$project.daggerVersion"
compile "com.google.dagger:dagger-android-support:$project.daggerVersion"
annotationProcessor "com.google.dagger:dagger-android-processor:$project.daggerVersion"
annotationProcessor "com.google.dagger:dagger-compiler:$project.daggerVersion"

// API
compile "com.squareup.retrofit2:retrofit:$project.retrofitVersion"
compile "com.squareup.retrofit2:converter-gson:$project.retrofitVersion"
implementation "com.squareup.retrofit2:adapter-rxjava2:$project.retrofitVersion"
implementation "com.squareup.okhttp3:logging-interceptor:3.9.1"
compile 'com.github.bumptech.glide:glide:4.1.1'

// Lifecycle
compile "android.arch.lifecycle:runtime:$project.archVersion"
compile "android.arch.lifecycle:extensions:$project.archVersion"
annotationProcessor "android.arch.lifecycle:compiler:$project.archVersion"
implementation "android.arch.lifecycle:reactivestreams:$project.archVersion"


// DB
compile "android.arch.persistence.room:runtime:$project.roomVersion"
annotationProcessor "android.arch.persistence.room:compiler:$project.roomVersion"
implementation "android.arch.persistence.room:rxjava2:$project.roomVersion"

// ReactiveX
implementation "io.reactivex.rxjava2:rxjava:2.1.8"
implementation "io.reactivex.rxjava2:rxandroid:2.0.1"

// LOGS
implementation 'com.jakewharton.timber:timber:4.6.0'
}

21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.lakhmotkin.heartstonecard;

import android.os.SystemClock;
import android.support.test.espresso.contrib.RecyclerViewActions;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import com.lakhmotkin.heartstonecards.R;
import com.lakhmotkin.heartstonecards.view.ui.CardsGridActivity;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
import static org.hamcrest.CoreMatchers.allOf;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;

/**
* Created by Igor Lakhmotkin on 25.02.2018, for HeartstoneAssessment.
*/
@RunWith(AndroidJUnit4.class)
public class CardsGridUITest {

@Rule
public ActivityTestRule<CardsGridActivity> mCardsGridActivityTestRule =
new ActivityTestRule<>(CardsGridActivity.class);

@Before
public void init(){
mCardsGridActivityTestRule.getActivity()
.getSupportFragmentManager().beginTransaction();
}

@Test
public void clickProductInList_opensDetailedUi() throws Exception {
SystemClock.sleep(2000);
onView(withId(R.id.cards_recycler_grid))
.perform(RecyclerViewActions.actionOnItemAtPosition(1, click()));
SystemClock.sleep(2000);
onView(allOf(withId(R.id.card_name), isDisplayed())).check(matches(isCompletelyDisplayed()));
}
}
24 changes: 24 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lakhmotkin.heartstonecards">

<uses-permission android:name="android.permission.INTERNET"/>

<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".view.ui.CardsGridActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
38 changes: 38 additions & 0 deletions app/src/main/java/com/lakhmotkin/heartstonecards/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.lakhmotkin.heartstonecards;

import android.app.Activity;
import android.app.Application;

import com.lakhmotkin.heartstonecards.di.AppModule;
import com.lakhmotkin.heartstonecards.di.DaggerAppComponent;

import javax.inject.Inject;

import dagger.android.DispatchingAndroidInjector;
import dagger.android.HasActivityInjector;
import timber.log.Timber;

/**
* Created by igorlakhmotkin on 23/02/2018.
*/

public class App extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

@Override
public void onCreate() {
super.onCreate();
DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build().inject(this);
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
}

@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
}
10 changes: 10 additions & 0 deletions app/src/main/java/com/lakhmotkin/heartstonecards/C.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.lakhmotkin.heartstonecards;

/**
* Created by igorlakhmotkin on 23/02/2018.
*/

public abstract class C {
public static final String BASE_URL = "https://raw.githubusercontent.com";
public static final String DB_NAME = "cards.db";
}
Loading