diff --git a/README.md b/README.md index 80966847..8b137891 100644 --- a/README.md +++ b/README.md @@ -1,57 +1 @@ -# Desafio de programação mobile Android -A ideia deste desafio é nos permitir avaliar melhor as habilidades de candidatos a vagas de programador, de vários níveis. - -Este desafio deve ser feito por você em sua casa. Gaste o tempo que você quiser, porém normalmente você não deve precisar de mais do que algumas horas. - -## Instruções de entrega do desafio - -1. Primeiro, faça um fork deste projeto para sua conta no GitHub (crie uma se você não possuir). -1. Em seguida, implemente o projeto tal qual descrito abaixo, em seu próprio fork. -1. Por fim, empurre todas as suas alterações para o seu fork no GitHub e envie um pull request para este repositório original. Se você já entrou em contato com alguém da AppProva sobre uma vaga, avise também essa pessoa por email, incluindo no email o seu usuário no GitHub. - -## Descrição do projeto - -Você deve criar um aplicativo que irá listar os repositórios públicos mais populares relacionados a Java no GitHub, usando a [API do GitHub](https://developer.github.com/v3/) para buscar os dados necessários. - -O aplicativo deve exibir inicialmente uma lista paginada dos repositórios, ordenados por popularidade decrescente (exemplo de chamada da API: `https://api.github.com/search/repositories?q=language:Java&sort=stars&page=1`). - -Cada repositório deve exibir Nome do repositório, Descrição do Repositório, Nome / Foto do autor, Número de Stars, Número de Forks. - -Ao tocar em um item, deve levar a lista de Pull Requests do repositório. Cada item da lista deve exibir Nome / Foto do autor do PR, Título do PR, Data do PR e Body do PR. - -Ao tocar em um item, deve abrir no browser a página do Pull Request em questão. - -Você pode se basear neste mockup para criar as telas: - -![mockup](https://raw.githubusercontent.com/myfreecomm/desafio-mobile-android/master/mockup-android.png) - -Sua aplicação deve: - -- usar um sistema de build. Ex: Gradle -- fazer mapeamento json -> Objeto . Ex: GSON / Jackson / Moshi / etc -- usar um arquivo .gitignore no seu repositório -- usar Material Design -- usar o padrão MVP ou MVVM -- possuir boa cobertura de testes unitários no projeto. - -Você ganha mais pontos se: - -- criar testes funcionais -- fazer cache de imagens -- utilizar Kotlin -- utilizar RxJava ou RxKotlin -- utilizar os Android Architecture Components -- suportar mudanças de orientação das telas sem perder estado (Sem utilizar o configChanges) - -As sugestões de bibliotecas fornecidas são só um guideline, sinta-se a vontade para usar soluções diferentes e nos surpreender. O importante de fato é que os objetivos macros sejam atingidos. - -## Avaliação - -Seu projeto será avaliado de acordo com os seguintes critérios. - -1. Sua aplicação preenche os requerimentos básicos? -1. Você documentou a maneira de configurar o ambiente e rodar sua aplicação? -1. Você seguiu as instruções de envio do desafio? - -Adicionalmente, tentaremos verificar a sua familiarização com as bibliotecas padrões (standard libs), bem como sua experiência com programação orientada a objetos a partir da estrutura de seu projeto. diff --git a/desafio-mobile-android/.gitignore b/desafio-mobile-android/.gitignore new file mode 100644 index 00000000..39fb081a --- /dev/null +++ b/desafio-mobile-android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/desafio-mobile-android/app/.gitignore b/desafio-mobile-android/app/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/desafio-mobile-android/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/desafio-mobile-android/app/build.gradle b/desafio-mobile-android/app/build.gradle new file mode 100644 index 00000000..c228f433 --- /dev/null +++ b/desafio-mobile-android/app/build.gradle @@ -0,0 +1,66 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + buildToolsVersion "26.0.0" + defaultConfig { + applicationId "com.app.desafio_mobile_android" + minSdkVersion 19 + targetSdkVersion 26 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + vectorDrawables.useSupportLibrary = true + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile "com.android.support:appcompat-v7:$rootProject.ext.supportLibraryVersion" + compile "com.android.support:design:$rootProject.ext.supportLibraryVersion" + compile "com.android.support:recyclerview-v7:$rootProject.ext.supportLibraryVersion" + compile "com.android.support:support-v4:$rootProject.supportLibraryVersion" + compile "com.android.support:support-v4:$rootProject.supportLibraryVersion" + + compile "com.android.support.constraint:constraint-layout:$rootProject.constraint" + + compile "com.squareup.retrofit2:retrofit:$rootProject.retrofit" + compile "com.squareup.okhttp3:logging-interceptor:$rootProject.okhttp" + compile "com.squareup.okhttp3:okhttp:$rootProject.okhttp" + compile "com.squareup.retrofit2:converter-gson:$rootProject.gson" + + compile "com.jakewharton:butterknife:$rootProject.butterknife" + annotationProcessor "com.jakewharton:butterknife-compiler:$rootProject.butterknife" + + compile "com.squareup.picasso:picasso:$rootProject.picasso" + + compile "com.lusfold.spinnerloading:library:$rootProject.spinnerloading" + + androidTestCompile "junit:junit:$rootProject.ext.junitVersion" + androidTestCompile("com.android.support.test.espresso:espresso-core:$rootProject.ext.espressoVersion", { + exclude group: 'com.android.support', module: 'support-annotations' + }) + androidTestCompile "org.powermock:powermock-module-junit4:$rootProject.ext.powermockVersion" + androidTestCompile "org.powermock:powermock-module-junit4-rule:$rootProject.ext.powermockVersion" + androidTestCompile ("com.android.support.test:runner:0.5", { + exclude group: 'com.android.support', module: 'support-annotations' + }) + androidTestCompile "com.android.support:support-annotations:$rootProject.supportLibraryVersion" + + + testCompile "junit:junit:$rootProject.ext.junitVersion" + testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion" + testCompile "org.powermock:powermock-module-junit4:$rootProject.ext.powermockVersion" + testCompile "org.powermock:powermock-api-mockito:$rootProject.ext.powermockVersion" + testCompile "org.hamcrest:hamcrest-all:$rootProject.ext.hamcrestAllVersion" + + androidTestCompile "org.mockito:mockito-core:$rootProject.ext.mockitoVersion" + + +} diff --git a/desafio-mobile-android/app/proguard-rules.pro b/desafio-mobile-android/app/proguard-rules.pro new file mode 100644 index 00000000..347b42ca --- /dev/null +++ b/desafio-mobile-android/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/selem/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 diff --git a/desafio-mobile-android/app/src/androidTest/java/com/app/desafio_mobile_android/ExampleInstrumentedTest.java b/desafio-mobile-android/app/src/androidTest/java/com/app/desafio_mobile_android/ExampleInstrumentedTest.java new file mode 100644 index 00000000..45d97235 --- /dev/null +++ b/desafio-mobile-android/app/src/androidTest/java/com/app/desafio_mobile_android/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.app.desafio_mobile_android; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.app.desafio_mobile_android", appContext.getPackageName()); + } +} diff --git a/desafio-mobile-android/app/src/androidTest/java/com/app/desafio_mobile_android/MainActivityTest.java b/desafio-mobile-android/app/src/androidTest/java/com/app/desafio_mobile_android/MainActivityTest.java new file mode 100644 index 00000000..1653356d --- /dev/null +++ b/desafio-mobile-android/app/src/androidTest/java/com/app/desafio_mobile_android/MainActivityTest.java @@ -0,0 +1,29 @@ +package com.app.desafio_mobile_android; + + +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; + +import com.app.desafio_mobile_android.presentation.home.MainActivity; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.espresso.Espresso.onView; +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; + +@RunWith(AndroidJUnit4.class) +public class MainActivityTest { + + @Rule + public ActivityTestRule mMainActivityActivityTestRule = new + ActivityTestRule<>(MainActivity.class, false, true); + + @Test + public void whenActivityIsLaunched_shouldDisplayInitialState() { + onView(withId(R.id.recyclerview_repositorie_list)).check(matches(isDisplayed())); + } +} diff --git a/desafio-mobile-android/app/src/main/AndroidManifest.xml b/desafio-mobile-android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..5d0794cf --- /dev/null +++ b/desafio-mobile-android/app/src/main/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/business/PullBusiness.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/business/PullBusiness.java new file mode 100644 index 00000000..fb189f8b --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/business/PullBusiness.java @@ -0,0 +1,57 @@ +package com.app.desafio_mobile_android.business; + +import android.content.Context; + +import com.app.desafio_mobile_android.intrastructure.error.OperationError; +import com.app.desafio_mobile_android.intrastructure.util.NetworkUtil; +import com.app.desafio_mobile_android.intrastructure.util.ServiceListener; +import com.app.desafio_mobile_android.presentation.pull.PullAdapter; +import com.app.desafio_mobile_android.presentation.pull.PullPresenter; +import com.app.desafio_mobile_android.presentation.pull.PullViewModel; +import com.app.desafio_mobile_android.service.Api; +import com.app.desafio_mobile_android.service.ApiInstance; +import com.app.desafio_mobile_android.service.model.pull.Pull; +import com.app.desafio_mobile_android.service.repository.PullRepository; + +import java.util.List; + +public class PullBusiness { + private PullRepository mPullRepository; + private ServiceListener mCallback; + + public PullBusiness(Context context) { + mPullRepository = new PullRepository(ApiInstance.getAPI().create(Api.class), + new NetworkUtil(context)); + } + + public void callPullList(String owner, String repo, ServiceListener callback) { + mCallback = callback; + mPullRepository.requestRepositoryList(owner, repo, new RequestPullCallback()); + } + + private void processResult(List pullList) { + int opened = 0; + int closed = 0; + for (Pull pull : pullList) { + if (pull.isClosed()) { + closed++; + } else { + opened++; + } + } + mCallback.onServiceSuccess(new PullViewModel(pullList, opened, closed)); + } + + private class RequestPullCallback extends ServiceListener> { + + @Override + public void onServiceSuccess(List pullList) { + processResult(pullList); + } + + @Override + public void onServiceError(OperationError error) { + mCallback.onServiceError(error); + } + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/business/RepositorieBusiness.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/business/RepositorieBusiness.java new file mode 100644 index 00000000..71fbe81f --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/business/RepositorieBusiness.java @@ -0,0 +1,24 @@ +package com.app.desafio_mobile_android.business; + +import android.content.Context; + +import com.app.desafio_mobile_android.intrastructure.util.NetworkUtil; +import com.app.desafio_mobile_android.intrastructure.util.ServiceListener; +import com.app.desafio_mobile_android.service.Api; +import com.app.desafio_mobile_android.service.ApiInstance; +import com.app.desafio_mobile_android.service.model.repositorie.Repositorie; +import com.app.desafio_mobile_android.service.repository.RepositorieRepository; + +public class RepositorieBusiness { + + private RepositorieRepository mRepositorieRepository; + + public RepositorieBusiness(Context context) { + mRepositorieRepository = new RepositorieRepository(ApiInstance.getAPI().create(Api.class), + new NetworkUtil(context)); + } + + public void callServiceRepositorie(int pageNumber, ServiceListener callback) { + mRepositorieRepository.requestRepositoryList(pageNumber, callback); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/Constants.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/Constants.java new file mode 100644 index 00000000..5e64acec --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/Constants.java @@ -0,0 +1,15 @@ +package com.app.desafio_mobile_android.intrastructure; + +public class Constants { + + public static final String ABOUT_URL = "https://www.linkedin.com/in/selem-afonso-a0b75a8"; + public static final String BASE_API = "https://api.github.com/"; + public enum ErrorType { + NETWORK_ERROR, SERVICE_ERROR, GENERIC_ERROR, CUSTOM_ERROR + } + + public static class DatePattern { + public static final String DATE_ENGLISH = "yyyy-MM-dd'T'HH:mm:ss"; + public static final String DEFAULT_FORMAT = "MM/dd/yyyy hh:mm:ss aa"; + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/error/OperationError.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/error/OperationError.java new file mode 100644 index 00000000..fc948cd6 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/error/OperationError.java @@ -0,0 +1,30 @@ +package com.app.desafio_mobile_android.intrastructure.error; + + +import com.app.desafio_mobile_android.intrastructure.Constants; + +public class OperationError { + + private Constants.ErrorType errorType; + private String errorMsg; + + public Constants.ErrorType getErrorType() { + return errorType; + } + + public void setErrorType(Constants.ErrorType errorType) { + this.errorType = errorType; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public boolean isCustomError() { + return errorType == Constants.ErrorType.CUSTOM_ERROR; + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/ActivityUtils.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/ActivityUtils.java new file mode 100644 index 00000000..95fe193f --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/ActivityUtils.java @@ -0,0 +1,33 @@ + +package com.app.desafio_mobile_android.intrastructure.util; + +import android.support.annotation.NonNull; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; + +import static android.support.v4.util.Preconditions.checkNotNull; + + +/** + * This provides methods to help Activities load their UI. + */ +public class ActivityUtils { + + /** + * The {@code fragment} is added to the container view with id {@code frameId}. The operation is + * performed by the {@code fragmentManager}. + * + */ + public static void addFragmentToActivity (@NonNull FragmentManager fragmentManager, + @NonNull Fragment fragment, int frameId) { + checkNotNull(fragmentManager); + checkNotNull(fragment); + FragmentTransaction transaction = fragmentManager.beginTransaction(); + transaction.add(frameId, fragment); + transaction.commit(); + } + + + +} \ No newline at end of file diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/CircleTransform.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/CircleTransform.java new file mode 100644 index 00000000..7fa7dab1 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/CircleTransform.java @@ -0,0 +1,43 @@ +package com.app.desafio_mobile_android.intrastructure.util; + +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.squareup.picasso.Transformation; + +public class CircleTransform implements Transformation { + @Override + public Bitmap transform(Bitmap source) { + int size = Math.min(source.getWidth(), source.getHeight()); + + int x = (source.getWidth() - size) / 2; + int y = (source.getHeight() - size) / 2; + + Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size); + if (squaredBitmap != source) { + source.recycle(); + } + + Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig()); + + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(); + BitmapShader shader = new BitmapShader(squaredBitmap, + BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP); + paint.setShader(shader); + paint.setAntiAlias(true); + + float r = size / 2f; + canvas.drawCircle(r, r, r, paint); + + squaredBitmap.recycle(); + return bitmap; + } + + @Override + public String key() { + return "circle"; + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/NetworkUtil.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/NetworkUtil.java new file mode 100644 index 00000000..3d47ef5a --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/NetworkUtil.java @@ -0,0 +1,21 @@ +package com.app.desafio_mobile_android.intrastructure.util; + + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +public class NetworkUtil { + private Context context; + + public NetworkUtil(Context context) { + this.context = context; + } + + public boolean isNetworkAvailable() { + ConnectivityManager connectivityManager + = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo(); + return activeNetworkInfo != null && activeNetworkInfo.isConnected(); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/ServiceListener.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/ServiceListener.java new file mode 100644 index 00000000..02dda741 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/intrastructure/util/ServiceListener.java @@ -0,0 +1,10 @@ +package com.app.desafio_mobile_android.intrastructure.util; + +import com.app.desafio_mobile_android.intrastructure.error.OperationError; + +public abstract class ServiceListener { + + public abstract void onServiceSuccess(final TResult result); + + public abstract void onServiceError(final OperationError error); +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BaseActivity.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BaseActivity.java new file mode 100644 index 00000000..1525fd85 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BaseActivity.java @@ -0,0 +1,64 @@ +package com.app.desafio_mobile_android.presentation.base; + +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; + +import com.app.desafio_mobile_android.R; +import com.app.desafio_mobile_android.intrastructure.error.OperationError; +import com.app.desafio_mobile_android.presentation.dialog.ErrorDialogFragment; +import com.app.desafio_mobile_android.presentation.dialog.ProgressDialogFragment; + +public class BaseActivity extends AppCompatActivity { + private static final String FRAGMENT_ALERT_ID = "FRAGMENT_ALERT_ID"; + private ProgressDialogFragment mProgressDialog; + private ErrorDialogFragment errorDialog; + + public void showError(OperationError error, ErrorDialogFragment.MyOnErrorClick myOnErrorClick) { + errorDialog = ErrorDialogFragment.newInstance(getErrorMsg(error)); + errorDialog.setListener(myOnErrorClick); + errorDialog.show(getSupportFragmentManager(), FRAGMENT_ALERT_ID); + } + + private String getErrorMsg(OperationError error) { + + switch (error.getErrorType()) { + case NETWORK_ERROR: + return getString(R.string.default_network_error); + case SERVICE_ERROR: + return getString(R.string.service_error); + case GENERIC_ERROR: + default: + return getString(R.string.generic_error); + } + } + + public void showProgress() { + mProgressDialog = new ProgressDialogFragment(); + mProgressDialog.show(getSupportFragmentManager(), FRAGMENT_ALERT_ID); + } + + + public void hideProgress() { + mProgressDialog.dismiss(); + } + + protected void initToolbar(Toolbar toolbar, String title, boolean hasHomeAsUpIndicator) { + setTitle(title); + setSupportActionBar(toolbar); + + if (hasHomeAsUpIndicator) { + configureToolbarHomeAsUpIndicator(); + } + } + + protected void configureToolbarHomeAsUpIndicator() { + ActionBar toolbar = getSupportActionBar(); + + if (toolbar != null) { + toolbar.setHomeButtonEnabled(true); + toolbar.setDisplayHomeAsUpEnabled(true); + toolbar.setDisplayShowHomeEnabled(true); + } + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BaseFragment.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BaseFragment.java new file mode 100644 index 00000000..3c088db1 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BaseFragment.java @@ -0,0 +1,10 @@ +package com.app.desafio_mobile_android.presentation.base; + +import android.support.v4.app.Fragment; + +import com.app.desafio_mobile_android.intrastructure.error.OperationError; + + +public class BaseFragment extends Fragment { + +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BasePresenter.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BasePresenter.java new file mode 100644 index 00000000..0307d03f --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BasePresenter.java @@ -0,0 +1,6 @@ +package com.app.desafio_mobile_android.presentation.base; + +public interface BasePresenter { + + void start(); +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BaseView.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BaseView.java new file mode 100644 index 00000000..5e59e611 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/base/BaseView.java @@ -0,0 +1,17 @@ +package com.app.desafio_mobile_android.presentation.base; + +import android.content.Context; +import android.support.annotation.NonNull; + +import com.app.desafio_mobile_android.intrastructure.error.OperationError; + +public interface BaseView { + + Context getContext(); + + void setPresenter(@NonNull T presenter); + + void showError(OperationError error); + + void setLoading(boolean isLoading); +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/dialog/ErrorDialogFragment.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/dialog/ErrorDialogFragment.java new file mode 100644 index 00000000..f47b62d5 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/dialog/ErrorDialogFragment.java @@ -0,0 +1,91 @@ +package com.app.desafio_mobile_android.presentation.dialog; + +import android.app.Dialog; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.app.desafio_mobile_android.R; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import butterknife.Unbinder; + +public class ErrorDialogFragment extends DialogFragment { + private final static String KEY_ERROR_MSG = "KEY_ERROR_MSG"; + + @BindView(R.id.textview_dialog_error_description) + TextView mTextviewErrorDescription; + + private Unbinder unbinder; + private MyOnErrorClick listener; + + public static ErrorDialogFragment newInstance(String errorMsg) { + + Bundle args = new Bundle(); + + ErrorDialogFragment fragment = new ErrorDialogFragment(); + args.putString(KEY_ERROR_MSG, errorMsg); + fragment.setArguments(args); + return fragment; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_dialog_erro, null); + unbinder = ButterKnife.bind(this, view); + + return view; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + configMsgError(getArguments().getString(KEY_ERROR_MSG)); + } + + private void configMsgError(String errorMsg) { + mTextviewErrorDescription.setText(errorMsg); + } + + @Override + public void onStart() { + super.onStart(); + + Dialog dialog = getDialog(); + if (dialog != null) { + dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + } + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + + @OnClick(R.id.error_button_try_again) + public void clickTryAgain() { + if (listener != null) { + listener.clickTryAgain(); + } + } + + public void setListener(MyOnErrorClick listener) { + this.listener = listener; + } + + public interface MyOnErrorClick { + void clickTryAgain(); + } + + +} \ No newline at end of file diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/dialog/ProgressDialogFragment.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/dialog/ProgressDialogFragment.java new file mode 100644 index 00000000..7e49b975 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/dialog/ProgressDialogFragment.java @@ -0,0 +1,38 @@ +package com.app.desafio_mobile_android.presentation.dialog; + +import android.app.Dialog; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.app.desafio_mobile_android.R; + + +public class ProgressDialogFragment extends DialogFragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_dialog_loading, null); + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + } + + @Override + public void onStart() { + super.onStart(); + + Dialog dialog = getDialog(); + if (dialog != null) { + dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + } + } +} \ No newline at end of file diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/MainActivity.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/MainActivity.java new file mode 100644 index 00000000..fd8e785a --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/MainActivity.java @@ -0,0 +1,92 @@ +package com.app.desafio_mobile_android.presentation.home; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.design.widget.NavigationView; +import android.support.v4.app.FragmentManager; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; + +import com.app.desafio_mobile_android.R; +import com.app.desafio_mobile_android.intrastructure.Constants; +import com.app.desafio_mobile_android.intrastructure.util.ActivityUtils; +import com.app.desafio_mobile_android.presentation.base.BaseActivity; +import com.app.desafio_mobile_android.presentation.home.repositorielist.RepositorieListFragment; +import com.app.desafio_mobile_android.presentation.home.repositorielist.RepositorieListPresenter; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class MainActivity extends BaseActivity + implements NavigationView.OnNavigationItemSelectedListener { + + @BindView(R.id.toolbar) + Toolbar mToolbar; + @BindView(R.id.nav_view) + NavigationView mNavView; + @BindView(R.id.drawer_layout) + DrawerLayout mDrawerLayout; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + configDrawer(); + showHome(); + } + + private void configDrawer() { + setSupportActionBar(mToolbar); + + ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( + this, mDrawerLayout, mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); + mDrawerLayout.setDrawerListener(toggle); + toggle.syncState(); + + mNavView.setNavigationItemSelectedListener(this); + } + + @Override + public void onBackPressed() { + if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { + mDrawerLayout.closeDrawer(GravityCompat.START); + } else { + super.onBackPressed(); + } + } + + @SuppressWarnings("StatementWithEmptyBody") + @Override + public boolean onNavigationItemSelected(MenuItem item) { + int id = item.getItemId(); + + if (id == R.id.nav_home) { + showHome(); + } else if (id == R.id.nav_about) { + showAbout(); + } + + mDrawerLayout.closeDrawer(GravityCompat.START); + return true; + } + + private void showHome() { + FragmentManager fragmentManager = getSupportFragmentManager(); + RepositorieListFragment fragment = (RepositorieListFragment) fragmentManager.findFragmentById(R.id.framelayout_main); + if(fragment == null) { + fragment = RepositorieListFragment.newInstance(); + ActivityUtils.addFragmentToActivity(fragmentManager, fragment, R.id.framelayout_main); + } + + fragment.setPresenter(new RepositorieListPresenter(this, fragment)); + } + + private void showAbout() { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.ABOUT_URL))); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListAdapter.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListAdapter.java new file mode 100644 index 00000000..819a283a --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListAdapter.java @@ -0,0 +1,111 @@ +package com.app.desafio_mobile_android.presentation.home.repositorielist; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.app.desafio_mobile_android.R; +import com.app.desafio_mobile_android.intrastructure.util.CircleTransform; +import com.app.desafio_mobile_android.service.model.repositorie.RepositorieItem; +import com.squareup.picasso.Picasso; + +import java.text.NumberFormat; +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +public class RepositorieListAdapter extends RecyclerView.Adapter { + + private List mRepositorieItems; + private Context mContext; + private RepositorieListPresenter.RepositorieListListener mListListener; + + public RepositorieListAdapter(Context mContext, List repositorieItems, RepositorieListPresenter.RepositorieListListener listListener) { + this.mContext = mContext; + this.mRepositorieItems = repositorieItems; + this.mListListener = listListener; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + View view = inflater.inflate(R.layout.item_repositorie_list, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + configHolder(mRepositorieItems.get(position), (ViewHolder) holder); + } + + @Override + public int getItemCount() { + return mRepositorieItems.size(); + } + + public void addAll(List repositorieItemList) { + for (RepositorieItem repositorieItem : repositorieItemList) { + add(repositorieItem); + } + } + + public void add(RepositorieItem repositorieItem) { + mRepositorieItems.add(repositorieItem); + notifyItemInserted(mRepositorieItems.size() - 1); + } + + private void configHolder(RepositorieItem item, ViewHolder holder) { + setAvatar(item, holder); + holder.mTextViewRepoName.setText(item.getName()); + holder.mTextViewDescription.setText(item.getDescription()); + NumberFormat numberFormat = NumberFormat.getInstance(); + holder.mTextViewForks.setText(numberFormat.format(item.getForks())); + holder.mTextViewStars.setText(numberFormat.format(item.getStargazersCount())); + holder.mTextViewUsername.setText(item.getOwner().getLogin()); + holder.mTextViewFullName.setText(item.getFullName()); + } + + private void setAvatar(RepositorieItem item, ViewHolder holder) { + Picasso.with(mContext).load(item.getOwner().getAvatarUrl()) + .transform(new CircleTransform()) + .placeholder(R.drawable.ic_user) + .into(holder.mImageViewAvatar); + } + + + class ViewHolder extends RecyclerView.ViewHolder { + @BindView(R.id.textview_item_repositorie_list_repo_name) + TextView mTextViewRepoName; + @BindView(R.id.textview_item_respositorie_list_description) + TextView mTextViewDescription; + @BindView(R.id.textview_item_respositorie_list_forks) + TextView mTextViewForks; + @BindView(R.id.textview_item_respositorie_list_stars) + TextView mTextViewStars; + @BindView(R.id.imageview_item_respositorie_list_avatar) + ImageView mImageViewAvatar; + @BindView(R.id.textview_item_respositorie_list_username) + TextView mTextViewUsername; + @BindView(R.id.textview_item_respositorie_list_fullname) + TextView mTextViewFullName; + @BindView(R.id.relativelayout_item_repositorie_main_content) + RelativeLayout mRelativeLayoutMainContent; + + ViewHolder(View view) { + super(view); + ButterKnife.bind(this, view); + } + + @OnClick(R.id.relativelayout_item_repositorie_main_content) + public void onViewClicked() { + mListListener.onItemSelected(getAdapterPosition()); + } + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListContract.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListContract.java new file mode 100644 index 00000000..fe02f76d --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListContract.java @@ -0,0 +1,20 @@ +package com.app.desafio_mobile_android.presentation.home.repositorielist; + +import com.app.desafio_mobile_android.presentation.base.BasePresenter; +import com.app.desafio_mobile_android.presentation.base.BaseView; + +public class RepositorieListContract { + + interface View extends BaseView { + void updateList(RepositorieListAdapter adapter); + + void updatePageSize(int pageSize); + + void showPullRequest(String owner, String repo); + } + + interface Presenter extends BasePresenter { + void callService(); + void refreshList(); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListFragment.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListFragment.java new file mode 100644 index 00000000..fefb172c --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListFragment.java @@ -0,0 +1,151 @@ +package com.app.desafio_mobile_android.presentation.home.repositorielist; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.app.desafio_mobile_android.R; +import com.app.desafio_mobile_android.intrastructure.error.OperationError; +import com.app.desafio_mobile_android.presentation.base.BaseFragment; +import com.app.desafio_mobile_android.presentation.dialog.ErrorDialogFragment; +import com.app.desafio_mobile_android.presentation.home.MainActivity; +import com.app.desafio_mobile_android.presentation.pull.PullActivity; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; + +public class RepositorieListFragment extends BaseFragment implements RepositorieListContract.View { + + @BindView(R.id.recyclerview_repositorie_list) + RecyclerView mRecyclerview; + Unbinder unbinder; + private MainActivity mMainActivity; + private RepositorieListContract.Presenter mPresenter; + private int mPageSize; + private boolean mIsLoading; + private ErrorListener mErrorListener; + + public static RepositorieListFragment newInstance() { + + Bundle args = new Bundle(); + + RepositorieListFragment fragment = new RepositorieListFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + mMainActivity = (MainActivity) context; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_repositorie_list, container, false); + unbinder = ButterKnife.bind(this, view); + mErrorListener = new ErrorListener(); + initRecyclerView(); + return view; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mPresenter.start(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + + @Override + public void setPresenter(@NonNull RepositorieListContract.Presenter presenter) { + mPresenter = presenter; + } + + @Override + public void showError(OperationError error) { + mMainActivity.showError(error, mErrorListener); + } + + @Override + public void updateList(RepositorieListAdapter adapter) { + mRecyclerview.setAdapter(adapter); + } + + @Override + public void setLoading(boolean isLoading) { + mIsLoading = isLoading; + if (isLoading) { + mMainActivity.showProgress(); + } else { + mMainActivity.hideProgress(); + } + } + + @Override + public void updatePageSize(int pageSize) { + mPageSize = pageSize; + } + + @Override + public void showPullRequest(String owner, String repo) { + startActivity(PullActivity.getLaunchIntent(mMainActivity, owner, repo)); + + } + + private void initRecyclerView() { + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mMainActivity); + mRecyclerview.setLayoutManager(linearLayoutManager); + mRecyclerview.addOnScrollListener(new RecyclerViewListener(linearLayoutManager)); + } + + public class RecyclerViewListener extends RecyclerView.OnScrollListener { + + LinearLayoutManager mLayoutManager; + + public RecyclerViewListener(LinearLayoutManager linearLayoutManager) { + this.mLayoutManager = linearLayoutManager; + } + + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + int visibleItemCount = mLayoutManager.getChildCount(); + int totalItemCount = mLayoutManager.getItemCount(); + int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition(); + + if (!mIsLoading) { + if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount + && firstVisibleItemPosition >= 0 + && totalItemCount >= mPageSize) { + mPresenter.refreshList(); + } + } + } + } + + private class ErrorListener implements ErrorDialogFragment.MyOnErrorClick { + + @Override + public void clickTryAgain() { + mPresenter.callService(); + } + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListPresenter.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListPresenter.java new file mode 100644 index 00000000..cb32882f --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/home/repositorielist/RepositorieListPresenter.java @@ -0,0 +1,91 @@ +package com.app.desafio_mobile_android.presentation.home.repositorielist; + +import android.content.Context; + +import com.app.desafio_mobile_android.business.RepositorieBusiness; +import com.app.desafio_mobile_android.intrastructure.error.OperationError; +import com.app.desafio_mobile_android.intrastructure.util.ServiceListener; +import com.app.desafio_mobile_android.service.model.repositorie.Repositorie; +import com.app.desafio_mobile_android.service.model.repositorie.RepositorieItem; + +import java.util.List; + +public class RepositorieListPresenter implements RepositorieListContract.Presenter { + private final static int FIRST_PAGE_REPOSITORIE = 1; + + private RepositorieListContract.View mView; + private RepositorieBusiness mRepositorieBusiness; + private int mPageRequest; + private RepositorieListAdapter mAdapter; + private List mRepositorieItemList; + private Context mContext; + + public RepositorieListPresenter(Context context, RepositorieListContract.View view) { + this.mView = view; + mContext = context; + mRepositorieBusiness = new RepositorieBusiness(mContext); + mPageRequest = FIRST_PAGE_REPOSITORIE; + } + + @Override + public void start() { + callService(); + } + + @Override + public void callService() { + mView.setLoading(true); + mRepositorieBusiness.callServiceRepositorie(mPageRequest, new RequestRepositoriesCallback()); + } + + @Override + public void refreshList() { + mPageRequest += 1; + callService(); + } + + public class RequestRepositoriesCallback extends ServiceListener { + + @Override + public void onServiceSuccess(Repositorie repositorie) { + mView.setLoading(false); + mView.updatePageSize(repositorie.getItems().size()); + if (mAdapter == null) { + configAdapter(repositorie.getItems()); + } else { + notifyMoreItens(repositorie.getItems()); + } + } + + @Override + public void onServiceError(OperationError error) { + mView.setLoading(false); + mView.showError(error); + } + + private void configAdapter(List items) { + mRepositorieItemList = items; + mAdapter = new RepositorieListAdapter(mContext, mRepositorieItemList, new RepositorieCallback()); + mView.updateList(mAdapter); + } + + private void notifyMoreItens(List items) { + mAdapter.addAll(items); + } + } + + + public class RepositorieCallback implements RepositorieListListener { + + @Override + public void onItemSelected(int position) { + RepositorieItem repositorieItem = mRepositorieItemList.get(position); + mView.showPullRequest(repositorieItem.getOwner().getLogin(), + repositorieItem.getName()); + } + } + + interface RepositorieListListener { + void onItemSelected(int position); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullActivity.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullActivity.java new file mode 100644 index 00000000..1d4d09ce --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullActivity.java @@ -0,0 +1,65 @@ +package com.app.desafio_mobile_android.presentation.pull; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.FragmentManager; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; + +import com.app.desafio_mobile_android.R; +import com.app.desafio_mobile_android.intrastructure.util.ActivityUtils; +import com.app.desafio_mobile_android.presentation.base.BaseActivity; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class PullActivity extends BaseActivity { + private final static String KEY_OWNER = "KEY_OWNER"; + private final static String KEY_REPO = "KEY_REPO"; + + @BindView(R.id.toolbar) + Toolbar mToolbar; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_pull); + ButterKnife.bind(this); + + inflateFragment(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + super.onBackPressed(); + return true; + } + + return super.onOptionsItemSelected(item); + } + + private void inflateFragment() { + FragmentManager fragmentManager = getSupportFragmentManager(); + PullFragment fragment = (PullFragment) fragmentManager.findFragmentById(R.id.framelayout_pull); + if (fragment == null) { + fragment = PullFragment.newInstance(); + ActivityUtils.addFragmentToActivity(fragmentManager, fragment, R.id.framelayout_pull); + } + + String owner = getIntent().getExtras().getString(KEY_OWNER); + String repo = getIntent().getExtras().getString(KEY_REPO); + + initToolbar(mToolbar, repo, true); + fragment.setPresenter(new PullPresenter(this, owner, repo, fragment)); + } + + public static Intent getLaunchIntent(Context context, String owner, String repo) { + Intent intent = new Intent(context, PullActivity.class); + intent.putExtra(KEY_OWNER, owner); + intent.putExtra(KEY_REPO, repo); + return intent; + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullAdapter.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullAdapter.java new file mode 100644 index 00000000..fd07702d --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullAdapter.java @@ -0,0 +1,88 @@ +package com.app.desafio_mobile_android.presentation.pull; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.app.desafio_mobile_android.R; +import com.app.desafio_mobile_android.intrastructure.util.CircleTransform; +import com.app.desafio_mobile_android.service.model.pull.Pull; +import com.squareup.picasso.Picasso; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +public class PullAdapter extends RecyclerView.Adapter { + + private List mPullList; + private Context mContext; + private PullPresenter.PullListListener mPullListListener; + + public PullAdapter(Context mContext, List pullList, PullPresenter.PullListListener pullListListener) { + this.mContext = mContext; + this.mPullList = pullList; + this.mPullListListener = pullListListener; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + View view = inflater.inflate(R.layout.item_pull, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + configHolder(mPullList.get(position), (ViewHolder) holder); + } + + @Override + public int getItemCount() { + return mPullList.size(); + } + + private void configHolder(Pull item, ViewHolder holder) { + setAvatar(item, holder); + holder.mTextViewName.setText(item.getTitle()); + holder.mTextViewDescription.setText(item.getBody()); + holder.mTextViewUsername.setText(item.getUser().getLogin()); + holder.mTextViewDate.setText(item.dateCreate()); + } + + private void setAvatar(Pull item, ViewHolder holder) { + Picasso.with(mContext).load(item.getUser().getAvatarUrl()) + .transform(new CircleTransform()) + .placeholder(R.drawable.ic_user) + .into(holder.mImageViewAvatar); + } + + class ViewHolder extends RecyclerView.ViewHolder { + @BindView(R.id.textview_item_pull_name) + TextView mTextViewName; + @BindView(R.id.textview_item_pull_description) + TextView mTextViewDescription; + @BindView(R.id.imageview_item_pull_user_avatar) + ImageView mImageViewAvatar; + @BindView(R.id.textview_item_pull_username) + TextView mTextViewUsername; + @BindView(R.id.textview_item_pull_date) + TextView mTextViewDate; + + ViewHolder(View view) { + super(view); + ButterKnife.bind(this, view); + } + + @OnClick(R.id.relativelayout_item_pull_main_content) + public void onViewClicked() { + mPullListListener.onItemClick(getAdapterPosition()); + } + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullContract.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullContract.java new file mode 100644 index 00000000..8f95fbe4 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullContract.java @@ -0,0 +1,19 @@ +package com.app.desafio_mobile_android.presentation.pull; + + +import com.app.desafio_mobile_android.presentation.base.BasePresenter; +import com.app.desafio_mobile_android.presentation.base.BaseView; + +public class PullContract { + + interface View extends BaseView { + void updateList(PullAdapter adapter); + void showPage(String url); + void showHeader(int closed, int opened); + } + + interface Presenter extends BasePresenter { + void callPullListService(); + } + +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullFragment.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullFragment.java new file mode 100644 index 00000000..71ba013a --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullFragment.java @@ -0,0 +1,128 @@ +package com.app.desafio_mobile_android.presentation.pull; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.app.desafio_mobile_android.R; +import com.app.desafio_mobile_android.intrastructure.error.OperationError; +import com.app.desafio_mobile_android.presentation.base.BaseFragment; +import com.app.desafio_mobile_android.presentation.dialog.ErrorDialogFragment; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; + +public class PullFragment extends BaseFragment implements PullContract.View { + @BindView(R.id.recyclerview_pull) + RecyclerView mRecyclerview; + @BindView(R.id.textview_pull_header) + TextView mTextviewHeader; + + private Unbinder unbinder; + private PullContract.Presenter mPresenter; + private PullActivity mActivity; + + + public static PullFragment newInstance() { + + Bundle args = new Bundle(); + + PullFragment fragment = new PullFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + mActivity = (PullActivity) context; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_pull, container, false); + unbinder = ButterKnife.bind(this, view); + initRecyclerView(); + return view; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mPresenter.start(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } + + @Override + public void setPresenter(@NonNull PullContract.Presenter presenter) { + mPresenter = presenter; + } + + @Override + public void showError(OperationError error) { + mActivity.showError(error, new ErroClickListener()); + } + + @Override + public void updateList(PullAdapter adapter) { + mRecyclerview.setAdapter(adapter); + } + + @Override + public void setLoading(boolean isLoading) { + if (isLoading) { + mActivity.showProgress(); + } else { + mActivity.hideProgress(); + } + } + + @Override + public void showPage(String url) { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + } + + @Override + public void showHeader(int closed, int opened) { + String header = String.format(getString(R.string.pull_header), opened, closed); + Spannable wordtoSpan = new SpannableString(header); + wordtoSpan.setSpan(new ForegroundColorSpan(ContextCompat.getColor(mActivity, R.color.yellow_info)), + 0, header.indexOf("/"), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + mTextviewHeader.setText(wordtoSpan); + } + + private void initRecyclerView() { + LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mActivity); + mRecyclerview.setLayoutManager(linearLayoutManager); + } + + private class ErroClickListener implements ErrorDialogFragment.MyOnErrorClick { + + @Override + public void clickTryAgain() { + mActivity.hideProgress(); + mPresenter.callPullListService(); + } + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullPresenter.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullPresenter.java new file mode 100644 index 00000000..0f962a19 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullPresenter.java @@ -0,0 +1,69 @@ +package com.app.desafio_mobile_android.presentation.pull; + +import android.content.Context; + +import com.app.desafio_mobile_android.business.PullBusiness; +import com.app.desafio_mobile_android.intrastructure.error.OperationError; +import com.app.desafio_mobile_android.intrastructure.util.ServiceListener; +import com.app.desafio_mobile_android.service.model.pull.Pull; + +import java.util.List; + +public class PullPresenter implements PullContract.Presenter { + private String mOwnerName; + private String mRepoName; + private Context mContext; + private PullContract.View mView; + private PullBusiness mPullBusiness; + private RequestPullCallback mRequestPullCallback; + private List mPullList; + + public PullPresenter(Context context, String ownerName, String repoName, PullContract.View view) { + this.mOwnerName = ownerName; + this.mRepoName = repoName; + this.mContext = context; + this.mView = view; + mPullBusiness = new PullBusiness(context); + mRequestPullCallback = new RequestPullCallback(); + } + + @Override + public void start() { + callPullListService(); + } + + @Override + public void callPullListService() { + mView.setLoading(true); + mPullBusiness.callPullList(mOwnerName, mRepoName, mRequestPullCallback); + } + + private class RequestPullCallback extends ServiceListener { + + @Override + public void onServiceSuccess(PullViewModel pullViewModel) { + mPullList = pullViewModel.getPullList(); + mView.showHeader(pullViewModel.getClosed(), pullViewModel.getOpened()); + mView.setLoading(false); + mView.updateList(new PullAdapter(mView.getContext(), mPullList, new OnPullListCallback())); + } + + @Override + public void onServiceError(OperationError error) { + mView.setLoading(false); + mView.showError(error); + } + } + + private class OnPullListCallback implements PullListListener { + + @Override + public void onItemClick(int position) { + mView.showPage(mPullList.get(position).getUrl()); + } + } + + interface PullListListener { + void onItemClick(int position); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullViewModel.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullViewModel.java new file mode 100644 index 00000000..29160429 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/presentation/pull/PullViewModel.java @@ -0,0 +1,41 @@ +package com.app.desafio_mobile_android.presentation.pull; + +import com.app.desafio_mobile_android.service.model.pull.Pull; + +import java.util.List; + +public class PullViewModel { + private List pullList; + private int opened; + private int closed; + + public PullViewModel(List pullList, int opened, int closed) { + this.pullList = pullList; + this.opened = opened; + this.closed = closed; + } + + public List getPullList() { + return pullList; + } + + public void setPullList(List pullList) { + this.pullList = pullList; + } + + public int getOpened() { + return opened; + } + + public void setOpened(int opened) { + this.opened = opened; + } + + public int getClosed() { + return closed; + } + + public void setClosed(int closed) { + this.closed = closed; + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/Api.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/Api.java new file mode 100644 index 00000000..a538b940 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/Api.java @@ -0,0 +1,23 @@ +package com.app.desafio_mobile_android.service; + +import com.app.desafio_mobile_android.service.model.pull.Pull; +import com.app.desafio_mobile_android.service.model.repositorie.Repositorie; + +import java.util.List; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Path; +import retrofit2.http.Query; + +public interface Api { + + @GET("search/repositories?q=language:Java") + Call getRepositorieList(@Query("sort") String sortType, + @Query("page") int pageNumber); + + @GET("/repos/{owner}/{repo}/pulls") + Call> getPullList(@Path("owner") String owner, + @Path("repo") String repo, + @Query("state") String state); +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/ApiInstance.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/ApiInstance.java new file mode 100644 index 00000000..d594ba31 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/ApiInstance.java @@ -0,0 +1,31 @@ +package com.app.desafio_mobile_android.service; + +import com.app.desafio_mobile_android.intrastructure.Constants; + +import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; +import retrofit2.Retrofit; +import retrofit2.converter.gson.GsonConverterFactory; + +public class ApiInstance { + + private static String BASE_URL = Constants.BASE_API; + + public static Retrofit getAPI() { + HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); + interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); + OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build(); + + Retrofit retrofit = new Retrofit + .Builder() + .client(client) + .baseUrl(BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .build(); + + + + return retrofit; + } + +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/Owner.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/Owner.java new file mode 100644 index 00000000..11cb5a7f --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/Owner.java @@ -0,0 +1,173 @@ +package com.app.desafio_mobile_android.service.model; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +/** + * Classe de mapeamento do objeto Owner - Reposta de lista de repositórios + */ + +public class Owner implements Parcelable{ + @SerializedName("login") + private String login; + @SerializedName("id") + private int id; + @SerializedName("avatar_url") + private String avatarUrl; + @SerializedName("gravatar_id") + private String gravatarId; + @SerializedName("url") + private String url; + @SerializedName("html_url") + private String htmlUrl; + @SerializedName("followers_url") + private String followersUrl; + @SerializedName("following_url") + private String followingUrl; + @SerializedName("gists_url") + private String gistsUrl; + @SerializedName("starred_url") + private String starredUrl; + @SerializedName("subscriptions_url") + private String subscriptionsUrl; + @SerializedName("organizations_url") + private String organizationsUrl; + @SerializedName("repos_url") + private String reposUrl; + @SerializedName("events_url") + private String eventsUrl; + @SerializedName("received_events_url") + private String receivedEventsUrl; + @SerializedName("type") + private String type; + @SerializedName("site_admin") + private boolean siteAdmin; + + protected Owner(Parcel in) { + login = in.readString(); + id = in.readInt(); + avatarUrl = in.readString(); + gravatarId = in.readString(); + url = in.readString(); + htmlUrl = in.readString(); + followersUrl = in.readString(); + followingUrl = in.readString(); + gistsUrl = in.readString(); + starredUrl = in.readString(); + subscriptionsUrl = in.readString(); + organizationsUrl = in.readString(); + reposUrl = in.readString(); + eventsUrl = in.readString(); + receivedEventsUrl = in.readString(); + type = in.readString(); + siteAdmin = in.readByte() != 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public Owner createFromParcel(Parcel in) { + return new Owner(in); + } + + @Override + public Owner[] newArray(int size) { + return new Owner[size]; + } + }; + + public String getLogin() { + return login; + } + + public int getId() { + return id; + } + + public String getAvatarUrl() { + return avatarUrl; + } + + public String getGravatarId() { + return gravatarId; + } + + public String getUrl() { + return url; + } + + public String getHtmlUrl() { + return htmlUrl; + } + + public String getFollowersUrl() { + return followersUrl; + } + + public String getFollowingUrl() { + return followingUrl; + } + + public String getGistsUrl() { + return gistsUrl; + } + + public String getStarredUrl() { + return starredUrl; + } + + public String getSubscriptionsUrl() { + return subscriptionsUrl; + } + + public String getOrganizationsUrl() { + return organizationsUrl; + } + + public String getReposUrl() { + return reposUrl; + } + + public String getEventsUrl() { + return eventsUrl; + } + + public String getReceivedEventsUrl() { + return receivedEventsUrl; + } + + public String getType() { + return type; + } + + public boolean isSiteAdmin() { + return siteAdmin; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeString(login); + parcel.writeInt(id); + parcel.writeString(avatarUrl); + parcel.writeString(gravatarId); + parcel.writeString(url); + parcel.writeString(htmlUrl); + parcel.writeString(followersUrl); + parcel.writeString(followingUrl); + parcel.writeString(gistsUrl); + parcel.writeString(starredUrl); + parcel.writeString(subscriptionsUrl); + parcel.writeString(organizationsUrl); + parcel.writeString(reposUrl); + parcel.writeString(eventsUrl); + parcel.writeString(receivedEventsUrl); + parcel.writeString(type); + parcel.writeByte((byte) (siteAdmin ? 1 : 0)); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/Pull.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/Pull.java new file mode 100644 index 00000000..89ed00fc --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/Pull.java @@ -0,0 +1,280 @@ +package com.app.desafio_mobile_android.service.model.pull; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.app.desafio_mobile_android.intrastructure.Constants; +import com.app.desafio_mobile_android.service.model.Owner; +import com.google.gson.annotations.SerializedName; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +public class Pull implements Parcelable { + private final static String STATE_CLOSED = "closed"; + + @SerializedName("url") + private String url; + @SerializedName("id") + private Integer id; + @SerializedName("html_url") + private String htmlUrl; + @SerializedName("diff_url") + private String diffUrl; + @SerializedName("patch_url") + private String patchUrl; + @SerializedName("issue_url") + private String issueUrl; + @SerializedName("number") + private Integer number; + @SerializedName("state") + private String state; + @SerializedName("locked") + private Boolean locked; + @SerializedName("title") + private String title; + @SerializedName("user") + private Owner user; + @SerializedName("body") + private String body; + @SerializedName("created_at") + private String createdAt; + @SerializedName("updated_at") + private String updatedAt; + @SerializedName("closed_at") + private Object closedAt; + @SerializedName("merged_at") + private Object mergedAt; + @SerializedName("merge_commit_sha") + private String mergeCommitSha; + @SerializedName("assignee") + private Object assignee; + @SerializedName("assignees") + private List assignees = null; + @SerializedName("requested_reviewers") + private List requestedReviewers = null; + @SerializedName("milestone") + private Object milestone; + @SerializedName("commits_url") + private String commitsUrl; + @SerializedName("review_comments_url") + private String reviewCommentsUrl; + @SerializedName("review_comment_url") + private String reviewCommentUrl; + @SerializedName("comments_url") + private String commentsUrl; + @SerializedName("statuses_url") + private String statusesUrl; + @SerializedName("head") + private PullHead head; + @SerializedName("base") + private PullBase base; + @SerializedName("_links") + private PullLink links; + @SerializedName("author_association") + private String authorAssociation; + + protected Pull(Parcel in) { + url = in.readString(); + htmlUrl = in.readString(); + diffUrl = in.readString(); + patchUrl = in.readString(); + issueUrl = in.readString(); + state = in.readString(); + title = in.readString(); + user = in.readParcelable(Owner.class.getClassLoader()); + body = in.readString(); + createdAt = in.readString(); + updatedAt = in.readString(); + mergeCommitSha = in.readString(); + commitsUrl = in.readString(); + reviewCommentsUrl = in.readString(); + reviewCommentUrl = in.readString(); + commentsUrl = in.readString(); + statusesUrl = in.readString(); + head = in.readParcelable(PullHead.class.getClassLoader()); + base = in.readParcelable(PullBase.class.getClassLoader()); + links = in.readParcelable(PullLink.class.getClassLoader()); + authorAssociation = in.readString(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Pull createFromParcel(Parcel in) { + return new Pull(in); + } + + @Override + public Pull[] newArray(int size) { + return new Pull[size]; + } + }; + + public String dateCreate() { + SimpleDateFormat dateFormat = new SimpleDateFormat(Constants.DatePattern.DATE_ENGLISH); + SimpleDateFormat defaultFormat = new SimpleDateFormat(Constants.DatePattern.DEFAULT_FORMAT); + Date convertedDate; + try { + convertedDate = dateFormat.parse(getCreatedAt()); + return defaultFormat.format(convertedDate); + } catch (ParseException e) { + return ""; + } + } + + public String getUrl() { + return url; + } + + public Integer getId() { + return id; + } + + public String getHtmlUrl() { + return htmlUrl; + } + + public String getDiffUrl() { + return diffUrl; + } + + public String getPatchUrl() { + return patchUrl; + } + + public String getIssueUrl() { + return issueUrl; + } + + public Integer getNumber() { + return number; + } + + public String getState() { + return state; + } + + public Boolean getLocked() { + return locked; + } + + public String getTitle() { + return title; + } + + public Owner getUser() { + return user; + } + + public String getBody() { + return body; + } + + public String getCreatedAt() { + return createdAt; + } + + public String getUpdatedAt() { + return updatedAt; + } + + public Object getClosedAt() { + return closedAt; + } + + public Object getMergedAt() { + return mergedAt; + } + + public String getMergeCommitSha() { + return mergeCommitSha; + } + + public Object getAssignee() { + return assignee; + } + + public List getAssignees() { + return assignees; + } + + public List getRequestedReviewers() { + return requestedReviewers; + } + + public Object getMilestone() { + return milestone; + } + + public String getCommitsUrl() { + return commitsUrl; + } + + public String getReviewCommentsUrl() { + return reviewCommentsUrl; + } + + public String getReviewCommentUrl() { + return reviewCommentUrl; + } + + public String getCommentsUrl() { + return commentsUrl; + } + + public String getStatusesUrl() { + return statusesUrl; + } + + public PullHead getHead() { + return head; + } + + public PullBase getBase() { + return base; + } + + public PullLink getLinks() { + return links; + } + + public String getAuthorAssociation() { + return authorAssociation; + } + + public boolean isClosed() { + return getState().equalsIgnoreCase(STATE_CLOSED); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeString(url); + parcel.writeString(htmlUrl); + parcel.writeString(diffUrl); + parcel.writeString(patchUrl); + parcel.writeString(issueUrl); + parcel.writeString(state); + parcel.writeString(title); + parcel.writeParcelable(user, i); + parcel.writeString(body); + parcel.writeString(createdAt); + parcel.writeString(updatedAt); + parcel.writeString(mergeCommitSha); + parcel.writeString(commitsUrl); + parcel.writeString(reviewCommentsUrl); + parcel.writeString(reviewCommentUrl); + parcel.writeString(commentsUrl); + parcel.writeString(statusesUrl); + parcel.writeParcelable(head, i); + parcel.writeParcelable(base, i); + parcel.writeParcelable(links, i); + parcel.writeString(authorAssociation); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullBase.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullBase.java new file mode 100644 index 00000000..130e8702 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullBase.java @@ -0,0 +1,75 @@ +package com.app.desafio_mobile_android.service.model.pull; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.app.desafio_mobile_android.service.model.Owner; +import com.app.desafio_mobile_android.service.model.repositorie.RepositorieItem; +import com.google.gson.annotations.SerializedName; + +public class PullBase implements Parcelable { + @SerializedName("label") + private String label; + @SerializedName("ref") + private String ref; + @SerializedName("sha") + private String sha; + @SerializedName("user") + private Owner user; + @SerializedName("repo") + private RepositorieItem repo; + + protected PullBase(Parcel in) { + label = in.readString(); + ref = in.readString(); + sha = in.readString(); + user = in.readParcelable(Owner.class.getClassLoader()); + repo = in.readParcelable(RepositorieItem.class.getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PullBase createFromParcel(Parcel in) { + return new PullBase(in); + } + + @Override + public PullBase[] newArray(int size) { + return new PullBase[size]; + } + }; + + public String getLabel() { + return label; + } + + public String getRef() { + return ref; + } + + public String getSha() { + return sha; + } + + public Owner getUser() { + return user; + } + + public RepositorieItem getRepo() { + return repo; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeString(label); + parcel.writeString(ref); + parcel.writeString(sha); + parcel.writeParcelable(user, i); + parcel.writeParcelable(repo, i); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullHead.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullHead.java new file mode 100644 index 00000000..df88915b --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullHead.java @@ -0,0 +1,75 @@ +package com.app.desafio_mobile_android.service.model.pull; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.app.desafio_mobile_android.service.model.Owner; +import com.app.desafio_mobile_android.service.model.repositorie.RepositorieItem; +import com.google.gson.annotations.SerializedName; + +public class PullHead implements Parcelable { + @SerializedName("label") + private String label; + @SerializedName("ref") + private String ref; + @SerializedName("sha") + private String sha; + @SerializedName("user") + private Owner user; + @SerializedName("repo") + private RepositorieItem repo; + + protected PullHead(Parcel in) { + label = in.readString(); + ref = in.readString(); + sha = in.readString(); + user = in.readParcelable(Owner.class.getClassLoader()); + repo = in.readParcelable(RepositorieItem.class.getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PullHead createFromParcel(Parcel in) { + return new PullHead(in); + } + + @Override + public PullHead[] newArray(int size) { + return new PullHead[size]; + } + }; + + public String getLabel() { + return label; + } + + public String getRef() { + return ref; + } + + public String getSha() { + return sha; + } + + public Owner getUser() { + return user; + } + + public RepositorieItem getRepo() { + return repo; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeString(label); + parcel.writeString(ref); + parcel.writeString(sha); + parcel.writeParcelable(user, i); + parcel.writeParcelable(repo, i); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullLink.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullLink.java new file mode 100644 index 00000000..8f7cd003 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullLink.java @@ -0,0 +1,97 @@ +package com.app.desafio_mobile_android.service.model.pull; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +public class PullLink implements Parcelable { + @SerializedName("self") + private PullLinkItem self; + @SerializedName("html") + private PullLinkItem html; + @SerializedName("issue") + private PullLinkItem issue; + @SerializedName("comments") + private PullLinkItem comments; + @SerializedName("review_comments") + private PullLinkItem reviewComments; + @SerializedName("review_comment") + private PullLinkItem reviewComment; + @SerializedName("commits") + private PullLinkItem commits; + @SerializedName("statuses") + private PullLinkItem statuses; + + protected PullLink(Parcel in) { + self = in.readParcelable(PullLinkItem.class.getClassLoader()); + html = in.readParcelable(PullLinkItem.class.getClassLoader()); + issue = in.readParcelable(PullLinkItem.class.getClassLoader()); + comments = in.readParcelable(PullLinkItem.class.getClassLoader()); + reviewComments = in.readParcelable(PullLinkItem.class.getClassLoader()); + reviewComment = in.readParcelable(PullLinkItem.class.getClassLoader()); + commits = in.readParcelable(PullLinkItem.class.getClassLoader()); + statuses = in.readParcelable(PullLinkItem.class.getClassLoader()); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PullLink createFromParcel(Parcel in) { + return new PullLink(in); + } + + @Override + public PullLink[] newArray(int size) { + return new PullLink[size]; + } + }; + + public PullLinkItem getSelf() { + return self; + } + + public PullLinkItem getHtml() { + return html; + } + + public PullLinkItem getIssue() { + return issue; + } + + public PullLinkItem getComments() { + return comments; + } + + public PullLinkItem getReviewComments() { + return reviewComments; + } + + public PullLinkItem getReviewComment() { + return reviewComment; + } + + public PullLinkItem getCommits() { + return commits; + } + + public PullLinkItem getStatuses() { + return statuses; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeParcelable(self, i); + parcel.writeParcelable(html, i); + parcel.writeParcelable(issue, i); + parcel.writeParcelable(comments, i); + parcel.writeParcelable(reviewComments, i); + parcel.writeParcelable(reviewComment, i); + parcel.writeParcelable(commits, i); + parcel.writeParcelable(statuses, i); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullLinkItem.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullLinkItem.java new file mode 100644 index 00000000..33f9da7e --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/pull/PullLinkItem.java @@ -0,0 +1,41 @@ +package com.app.desafio_mobile_android.service.model.pull; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +public class PullLinkItem implements Parcelable { + @SerializedName("href") + private String href; + + protected PullLinkItem(Parcel in) { + href = in.readString(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public PullLinkItem createFromParcel(Parcel in) { + return new PullLinkItem(in); + } + + @Override + public PullLinkItem[] newArray(int size) { + return new PullLinkItem[size]; + } + }; + + public String getHref() { + return href; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeString(href); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/repositorie/Repositorie.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/repositorie/Repositorie.java new file mode 100644 index 00000000..96d90945 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/repositorie/Repositorie.java @@ -0,0 +1,63 @@ +package com.app.desafio_mobile_android.service.model.repositorie; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +/** + * Classe de mapeamento com o retorno da lista de repositórios + */ + +public class Repositorie implements Parcelable { + @SerializedName("total_count") + private int totalCount; + @SerializedName("incomplete_results") + private boolean incompleteResults; + @SerializedName("items") + private List items = null; + + protected Repositorie(Parcel in) { + totalCount = in.readInt(); + incompleteResults = in.readByte() != 0; + items = in.createTypedArrayList(RepositorieItem.CREATOR); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Repositorie createFromParcel(Parcel in) { + return new Repositorie(in); + } + + @Override + public Repositorie[] newArray(int size) { + return new Repositorie[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeInt(totalCount); + parcel.writeByte((byte) (incompleteResults ? 1 : 0)); + parcel.writeTypedList(items); + } + + public int getTotalCount() { + return totalCount; + } + + public boolean isIncompleteResults() { + return incompleteResults; + } + + public List getItems() { + return items; + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/repositorie/RepositorieItem.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/repositorie/RepositorieItem.java new file mode 100644 index 00000000..7f147582 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/model/repositorie/RepositorieItem.java @@ -0,0 +1,605 @@ +package com.app.desafio_mobile_android.service.model.repositorie; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.app.desafio_mobile_android.service.model.Owner; +import com.google.gson.annotations.SerializedName; + +/** + * Clase de mapeamento do objeto Repositorie - Item da lista de respositórios retornado pela API + */ + +public class RepositorieItem implements Parcelable{ + + @SerializedName("id") + private int id; + @SerializedName("name") + private String name; + @SerializedName("full_name") + private String fullName; + @SerializedName("owner") + private Owner owner; + @SerializedName("private") + private boolean _private; + @SerializedName("html_url") + private String htmlUrl; + @SerializedName("description") + private String description; + @SerializedName("fork") + private boolean fork; + @SerializedName("url") + private String url; + @SerializedName("forks_url") + private String forksUrl; + @SerializedName("keys_url") + private String keysUrl; + @SerializedName("collaborators_url") + private String collaboratorsUrl; + @SerializedName("teams_url") + private String teamsUrl; + @SerializedName("hooks_url") + private String hooksUrl; + @SerializedName("issue_events_url") + private String issueEventsUrl; + @SerializedName("events_url") + private String eventsUrl; + @SerializedName("assignees_url") + private String assigneesUrl; + @SerializedName("branches_url") + private String branchesUrl; + @SerializedName("tags_url") + private String tagsUrl; + @SerializedName("blobs_url") + private String blobsUrl; + @SerializedName("git_tags_url") + private String gitTagsUrl; + @SerializedName("git_refs_url") + private String gitRefsUrl; + @SerializedName("trees_url") + private String treesUrl; + @SerializedName("statuses_url") + private String statusesUrl; + @SerializedName("languages_url") + private String languagesUrl; + @SerializedName("stargazers_url") + private String stargazersUrl; + @SerializedName("contributors_url") + private String contributorsUrl; + @SerializedName("subscribers_url") + private String subscribersUrl; + @SerializedName("subscription_url") + private String subscriptionUrl; + @SerializedName("commits_url") + private String commitsUrl; + @SerializedName("git_commits_url") + private String gitCommitsUrl; + @SerializedName("comments_url") + private String commentsUrl; + @SerializedName("issue_comment_url") + private String issueCommentUrl; + @SerializedName("contents_url") + private String contentsUrl; + @SerializedName("compare_url") + private String compareUrl; + @SerializedName("merges_url") + private String mergesUrl; + @SerializedName("archive_url") + private String archiveUrl; + @SerializedName("downloads_url") + private String downloadsUrl; + @SerializedName("issues_url") + private String issuesUrl; + @SerializedName("pulls_url") + private String pullsUrl; + @SerializedName("milestones_url") + private String milestonesUrl; + @SerializedName("notifications_url") + private String notificationsUrl; + @SerializedName("labels_url") + private String labelsUrl; + @SerializedName("releases_url") + private String releasesUrl; + @SerializedName("deployments_url") + private String deploymentsUrl; + @SerializedName("created_at") + private String createdAt; + @SerializedName("updated_at") + private String updatedAt; + @SerializedName("pushed_at") + private String pushedAt; + @SerializedName("git_url") + private String gitUrl; + @SerializedName("ssh_url") + private String sshUrl; + @SerializedName("clone_url") + private String cloneUrl; + @SerializedName("svn_url") + private String svnUrl; + @SerializedName("homepage") + private String homepage; + @SerializedName("size") + private int size; + @SerializedName("stargazers_count") + private int stargazersCount; + @SerializedName("watchers_count") + private int watchersCount; + @SerializedName("language") + private String language; + @SerializedName("has_issues") + private boolean hasIssues; + @SerializedName("has_projects") + private boolean hasProjects; + @SerializedName("has_downloads") + private boolean hasDownloads; + @SerializedName("has_wiki") + private boolean hasWiki; + @SerializedName("has_pages") + private boolean hasPages; + @SerializedName("forks_count") + private int forksCount; + @SerializedName("mirror_url") + private Object mirrorUrl; + @SerializedName("archived") + private boolean archived; + @SerializedName("open_issues_count") + private int openIssuesCount; + @SerializedName("forks") + private int forks; + @SerializedName("open_issues") + private int openIssues; + @SerializedName("watchers") + private int watchers; + @SerializedName("default_branch") + private String defaultBranch; + @SerializedName("score") + private int score; + + protected RepositorieItem(Parcel in) { + id = in.readInt(); + name = in.readString(); + fullName = in.readString(); + owner = in.readParcelable(Owner.class.getClassLoader()); + _private = in.readByte() != 0; + htmlUrl = in.readString(); + description = in.readString(); + fork = in.readByte() != 0; + url = in.readString(); + forksUrl = in.readString(); + keysUrl = in.readString(); + collaboratorsUrl = in.readString(); + teamsUrl = in.readString(); + hooksUrl = in.readString(); + issueEventsUrl = in.readString(); + eventsUrl = in.readString(); + assigneesUrl = in.readString(); + branchesUrl = in.readString(); + tagsUrl = in.readString(); + blobsUrl = in.readString(); + gitTagsUrl = in.readString(); + gitRefsUrl = in.readString(); + treesUrl = in.readString(); + statusesUrl = in.readString(); + languagesUrl = in.readString(); + stargazersUrl = in.readString(); + contributorsUrl = in.readString(); + subscribersUrl = in.readString(); + subscriptionUrl = in.readString(); + commitsUrl = in.readString(); + gitCommitsUrl = in.readString(); + commentsUrl = in.readString(); + issueCommentUrl = in.readString(); + contentsUrl = in.readString(); + compareUrl = in.readString(); + mergesUrl = in.readString(); + archiveUrl = in.readString(); + downloadsUrl = in.readString(); + issuesUrl = in.readString(); + pullsUrl = in.readString(); + milestonesUrl = in.readString(); + notificationsUrl = in.readString(); + labelsUrl = in.readString(); + releasesUrl = in.readString(); + deploymentsUrl = in.readString(); + createdAt = in.readString(); + updatedAt = in.readString(); + pushedAt = in.readString(); + gitUrl = in.readString(); + sshUrl = in.readString(); + cloneUrl = in.readString(); + svnUrl = in.readString(); + homepage = in.readString(); + size = in.readInt(); + stargazersCount = in.readInt(); + watchersCount = in.readInt(); + language = in.readString(); + hasIssues = in.readByte() != 0; + hasProjects = in.readByte() != 0; + hasDownloads = in.readByte() != 0; + hasWiki = in.readByte() != 0; + hasPages = in.readByte() != 0; + forksCount = in.readInt(); + archived = in.readByte() != 0; + openIssuesCount = in.readInt(); + forks = in.readInt(); + openIssues = in.readInt(); + watchers = in.readInt(); + defaultBranch = in.readString(); + score = in.readInt(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public RepositorieItem createFromParcel(Parcel in) { + return new RepositorieItem(in); + } + + @Override + public RepositorieItem[] newArray(int size) { + return new RepositorieItem[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeInt(id); + parcel.writeString(name); + parcel.writeString(fullName); + parcel.writeParcelable(owner, i); + parcel.writeByte((byte) (_private ? 1 : 0)); + parcel.writeString(htmlUrl); + parcel.writeString(description); + parcel.writeByte((byte) (fork ? 1 : 0)); + parcel.writeString(url); + parcel.writeString(forksUrl); + parcel.writeString(keysUrl); + parcel.writeString(collaboratorsUrl); + parcel.writeString(teamsUrl); + parcel.writeString(hooksUrl); + parcel.writeString(issueEventsUrl); + parcel.writeString(eventsUrl); + parcel.writeString(assigneesUrl); + parcel.writeString(branchesUrl); + parcel.writeString(tagsUrl); + parcel.writeString(blobsUrl); + parcel.writeString(gitTagsUrl); + parcel.writeString(gitRefsUrl); + parcel.writeString(treesUrl); + parcel.writeString(statusesUrl); + parcel.writeString(languagesUrl); + parcel.writeString(stargazersUrl); + parcel.writeString(contributorsUrl); + parcel.writeString(subscribersUrl); + parcel.writeString(subscriptionUrl); + parcel.writeString(commitsUrl); + parcel.writeString(gitCommitsUrl); + parcel.writeString(commentsUrl); + parcel.writeString(issueCommentUrl); + parcel.writeString(contentsUrl); + parcel.writeString(compareUrl); + parcel.writeString(mergesUrl); + parcel.writeString(archiveUrl); + parcel.writeString(downloadsUrl); + parcel.writeString(issuesUrl); + parcel.writeString(pullsUrl); + parcel.writeString(milestonesUrl); + parcel.writeString(notificationsUrl); + parcel.writeString(labelsUrl); + parcel.writeString(releasesUrl); + parcel.writeString(deploymentsUrl); + parcel.writeString(createdAt); + parcel.writeString(updatedAt); + parcel.writeString(pushedAt); + parcel.writeString(gitUrl); + parcel.writeString(sshUrl); + parcel.writeString(cloneUrl); + parcel.writeString(svnUrl); + parcel.writeString(homepage); + parcel.writeInt(size); + parcel.writeInt(stargazersCount); + parcel.writeInt(watchersCount); + parcel.writeString(language); + parcel.writeByte((byte) (hasIssues ? 1 : 0)); + parcel.writeByte((byte) (hasProjects ? 1 : 0)); + parcel.writeByte((byte) (hasDownloads ? 1 : 0)); + parcel.writeByte((byte) (hasWiki ? 1 : 0)); + parcel.writeByte((byte) (hasPages ? 1 : 0)); + parcel.writeInt(forksCount); + parcel.writeByte((byte) (archived ? 1 : 0)); + parcel.writeInt(openIssuesCount); + parcel.writeInt(forks); + parcel.writeInt(openIssues); + parcel.writeInt(watchers); + parcel.writeString(defaultBranch); + parcel.writeInt(score); + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public String getFullName() { + return fullName; + } + + public Owner getOwner() { + return owner; + } + + public boolean is_private() { + return _private; + } + + public String getHtmlUrl() { + return htmlUrl; + } + + public String getDescription() { + return description; + } + + public boolean isFork() { + return fork; + } + + public String getUrl() { + return url; + } + + public String getForksUrl() { + return forksUrl; + } + + public String getKeysUrl() { + return keysUrl; + } + + public String getCollaboratorsUrl() { + return collaboratorsUrl; + } + + public String getTeamsUrl() { + return teamsUrl; + } + + public String getHooksUrl() { + return hooksUrl; + } + + public String getIssueEventsUrl() { + return issueEventsUrl; + } + + public String getEventsUrl() { + return eventsUrl; + } + + public String getAssigneesUrl() { + return assigneesUrl; + } + + public String getBranchesUrl() { + return branchesUrl; + } + + public String getTagsUrl() { + return tagsUrl; + } + + public String getBlobsUrl() { + return blobsUrl; + } + + public String getGitTagsUrl() { + return gitTagsUrl; + } + + public String getGitRefsUrl() { + return gitRefsUrl; + } + + public String getTreesUrl() { + return treesUrl; + } + + public String getStatusesUrl() { + return statusesUrl; + } + + public String getLanguagesUrl() { + return languagesUrl; + } + + public String getStargazersUrl() { + return stargazersUrl; + } + + public String getContributorsUrl() { + return contributorsUrl; + } + + public String getSubscribersUrl() { + return subscribersUrl; + } + + public String getSubscriptionUrl() { + return subscriptionUrl; + } + + public String getCommitsUrl() { + return commitsUrl; + } + + public String getGitCommitsUrl() { + return gitCommitsUrl; + } + + public String getCommentsUrl() { + return commentsUrl; + } + + public String getIssueCommentUrl() { + return issueCommentUrl; + } + + public String getContentsUrl() { + return contentsUrl; + } + + public String getCompareUrl() { + return compareUrl; + } + + public String getMergesUrl() { + return mergesUrl; + } + + public String getArchiveUrl() { + return archiveUrl; + } + + public String getDownloadsUrl() { + return downloadsUrl; + } + + public String getIssuesUrl() { + return issuesUrl; + } + + public String getPullsUrl() { + return pullsUrl; + } + + public String getMilestonesUrl() { + return milestonesUrl; + } + + public String getNotificationsUrl() { + return notificationsUrl; + } + + public String getLabelsUrl() { + return labelsUrl; + } + + public String getReleasesUrl() { + return releasesUrl; + } + + public String getDeploymentsUrl() { + return deploymentsUrl; + } + + public String getCreatedAt() { + return createdAt; + } + + public String getUpdatedAt() { + return updatedAt; + } + + public String getPushedAt() { + return pushedAt; + } + + public String getGitUrl() { + return gitUrl; + } + + public String getSshUrl() { + return sshUrl; + } + + public String getCloneUrl() { + return cloneUrl; + } + + public String getSvnUrl() { + return svnUrl; + } + + public String getHomepage() { + return homepage; + } + + public int getSize() { + return size; + } + + public int getStargazersCount() { + return stargazersCount; + } + + public int getWatchersCount() { + return watchersCount; + } + + public String getLanguage() { + return language; + } + + public boolean isHasIssues() { + return hasIssues; + } + + public boolean isHasProjects() { + return hasProjects; + } + + public boolean isHasDownloads() { + return hasDownloads; + } + + public boolean isHasWiki() { + return hasWiki; + } + + public boolean isHasPages() { + return hasPages; + } + + public int getForksCount() { + return forksCount; + } + + public Object getMirrorUrl() { + return mirrorUrl; + } + + public boolean isArchived() { + return archived; + } + + public int getOpenIssuesCount() { + return openIssuesCount; + } + + public int getForks() { + return forks; + } + + public int getOpenIssues() { + return openIssues; + } + + public int getWatchers() { + return watchers; + } + + public String getDefaultBranch() { + return defaultBranch; + } + + public int getScore() { + return score; + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/repository/BaseRepository.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/repository/BaseRepository.java new file mode 100644 index 00000000..a0b38482 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/repository/BaseRepository.java @@ -0,0 +1,17 @@ +package com.app.desafio_mobile_android.service.repository; + +import com.app.desafio_mobile_android.intrastructure.Constants; +import com.app.desafio_mobile_android.intrastructure.error.OperationError; +import com.app.desafio_mobile_android.intrastructure.util.NetworkUtil; + +public class BaseRepository { + + public OperationError checkNetwork(NetworkUtil networkUtil) { + final OperationError operationError = null; + + if (!networkUtil.isNetworkAvailable()) { + operationError.setErrorType(Constants.ErrorType.NETWORK_ERROR); + } + return operationError; + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/repository/PullRepository.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/repository/PullRepository.java new file mode 100644 index 00000000..ea1097a8 --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/repository/PullRepository.java @@ -0,0 +1,50 @@ +package com.app.desafio_mobile_android.service.repository; + +import com.app.desafio_mobile_android.intrastructure.Constants; +import com.app.desafio_mobile_android.intrastructure.error.OperationError; +import com.app.desafio_mobile_android.intrastructure.util.NetworkUtil; +import com.app.desafio_mobile_android.intrastructure.util.ServiceListener; +import com.app.desafio_mobile_android.service.Api; +import com.app.desafio_mobile_android.service.model.pull.Pull; +import com.app.desafio_mobile_android.service.model.repositorie.Repositorie; + +import java.util.List; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class PullRepository extends BaseRepository { + private final static String STATE_SELECTED = "all"; + + private Api mApiInstance; + private NetworkUtil mNetworkUtil; + + public PullRepository(Api apiInstance, NetworkUtil networkUtil) { + this.mApiInstance = apiInstance; + this.mNetworkUtil = networkUtil; + } + + public void requestRepositoryList(String owner, String repo, final ServiceListener> callback) { + final OperationError operationError = checkNetwork(mNetworkUtil); + + if (operationError != null) { + callback.onServiceError(operationError); + return; + } + + Call> call = mApiInstance.getPullList(owner, repo, STATE_SELECTED); + call.enqueue(new Callback>() { + @Override + public void onResponse(Call> call, Response> response) { + callback.onServiceSuccess(response.body()); + } + + @Override + public void onFailure(Call> call, Throwable t) { + operationError.setErrorType(Constants.ErrorType.SERVICE_ERROR); + callback.onServiceError(operationError); + } + }); + } +} diff --git a/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/repository/RepositorieRepository.java b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/repository/RepositorieRepository.java new file mode 100644 index 00000000..9e1108ec --- /dev/null +++ b/desafio-mobile-android/app/src/main/java/com/app/desafio_mobile_android/service/repository/RepositorieRepository.java @@ -0,0 +1,47 @@ +package com.app.desafio_mobile_android.service.repository; + +import com.app.desafio_mobile_android.intrastructure.Constants; +import com.app.desafio_mobile_android.intrastructure.error.OperationError; +import com.app.desafio_mobile_android.intrastructure.util.NetworkUtil; +import com.app.desafio_mobile_android.intrastructure.util.ServiceListener; +import com.app.desafio_mobile_android.service.Api; +import com.app.desafio_mobile_android.service.model.repositorie.Repositorie; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +public class RepositorieRepository extends BaseRepository { + private final static String SORT_TYPE = "stars"; + + private Api mApiInstance; + private NetworkUtil mNetworkUtil; + + public RepositorieRepository(Api apiInstance, NetworkUtil networkUtil) { + this.mApiInstance = apiInstance; + this.mNetworkUtil = networkUtil; + } + + public void requestRepositoryList(int page, final ServiceListener callback) { + final OperationError operationError = checkNetwork(mNetworkUtil); + + if (operationError != null) { + callback.onServiceError(operationError); + return; + } + + Call call = mApiInstance.getRepositorieList(SORT_TYPE, page); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + callback.onServiceSuccess(response.body()); + } + + @Override + public void onFailure(Call call, Throwable t) { + operationError.setErrorType(Constants.ErrorType.SERVICE_ERROR); + callback.onServiceError(operationError); + } + }); + } +} diff --git a/desafio-mobile-android/app/src/main/res/drawable/background_button_default.xml b/desafio-mobile-android/app/src/main/res/drawable/background_button_default.xml new file mode 100644 index 00000000..80bbe280 --- /dev/null +++ b/desafio-mobile-android/app/src/main/res/drawable/background_button_default.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/desafio-mobile-android/app/src/main/res/drawable/ic_source_branch.xml b/desafio-mobile-android/app/src/main/res/drawable/ic_source_branch.xml new file mode 100644 index 00000000..23b2f0a8 --- /dev/null +++ b/desafio-mobile-android/app/src/main/res/drawable/ic_source_branch.xml @@ -0,0 +1,9 @@ + + + diff --git a/desafio-mobile-android/app/src/main/res/drawable/ic_star.xml b/desafio-mobile-android/app/src/main/res/drawable/ic_star.xml new file mode 100644 index 00000000..0a4f1c78 --- /dev/null +++ b/desafio-mobile-android/app/src/main/res/drawable/ic_star.xml @@ -0,0 +1,9 @@ + + + diff --git a/desafio-mobile-android/app/src/main/res/drawable/ic_user.xml b/desafio-mobile-android/app/src/main/res/drawable/ic_user.xml new file mode 100644 index 00000000..65d7949e --- /dev/null +++ b/desafio-mobile-android/app/src/main/res/drawable/ic_user.xml @@ -0,0 +1,9 @@ + + + diff --git a/desafio-mobile-android/app/src/main/res/drawable/text_button_color.xml b/desafio-mobile-android/app/src/main/res/drawable/text_button_color.xml new file mode 100644 index 00000000..a302abd9 --- /dev/null +++ b/desafio-mobile-android/app/src/main/res/drawable/text_button_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/desafio-mobile-android/app/src/main/res/layout/activity_main.xml b/desafio-mobile-android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..a61d8a63 --- /dev/null +++ b/desafio-mobile-android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/desafio-mobile-android/app/src/main/res/layout/activity_pull.xml b/desafio-mobile-android/app/src/main/res/layout/activity_pull.xml new file mode 100644 index 00000000..b5074f4b --- /dev/null +++ b/desafio-mobile-android/app/src/main/res/layout/activity_pull.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/desafio-mobile-android/app/src/main/res/layout/app_bar_main.xml b/desafio-mobile-android/app/src/main/res/layout/app_bar_main.xml new file mode 100644 index 00000000..663c15a5 --- /dev/null +++ b/desafio-mobile-android/app/src/main/res/layout/app_bar_main.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/desafio-mobile-android/app/src/main/res/layout/fragment_dialog_erro.xml b/desafio-mobile-android/app/src/main/res/layout/fragment_dialog_erro.xml new file mode 100644 index 00000000..d86f4adf --- /dev/null +++ b/desafio-mobile-android/app/src/main/res/layout/fragment_dialog_erro.xml @@ -0,0 +1,36 @@ + + + + + + + +