├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values-v21 │ │ │ │ └── styles.xml │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── xml │ │ │ │ └── provider_paths.xml │ │ │ ├── selector_transparent.xml │ │ │ ├── drawable-v21 │ │ │ │ ├── selector_transparent.xml │ │ │ │ └── ripple_transparent.xml │ │ │ ├── drawable │ │ │ │ ├── shape_oval_gray.xml │ │ │ │ ├── toolbar_dropshadow.xml │ │ │ │ ├── ic_back_black.xml │ │ │ │ ├── ic_back_white.xml │ │ │ │ └── selector_transparent.xml │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── theme.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ └── layout │ │ │ │ ├── activity_splash.xml │ │ │ │ ├── toolbar_without_elevation.xml │ │ │ │ ├── toolbar.xml │ │ │ │ ├── layout_progress_dialog.xml │ │ │ │ ├── activity_web_view.xml │ │ │ │ ├── content_main.xml │ │ │ │ └── activity_main.xml │ │ ├── java │ │ │ └── co │ │ │ │ └── lateralview │ │ │ │ └── myapp │ │ │ │ ├── ui │ │ │ │ ├── util │ │ │ │ │ ├── BaseGlideModule.java │ │ │ │ │ ├── di │ │ │ │ │ │ └── ActivityScoped.java │ │ │ │ │ ├── StringUtils.java │ │ │ │ │ ├── MathUtils.java │ │ │ │ │ ├── DialogUtils.java │ │ │ │ │ ├── RxSchedulersUtils.java │ │ │ │ │ ├── KeyboardUtils.java │ │ │ │ │ ├── ToolbarUtils.java │ │ │ │ │ ├── SystemUtils.java │ │ │ │ │ ├── SnackBarHelper.java │ │ │ │ │ └── UIUtils.java │ │ │ │ ├── activities │ │ │ │ │ ├── main │ │ │ │ │ │ ├── MainComponent.java │ │ │ │ │ │ ├── MainActivityComponent.java │ │ │ │ │ │ ├── Main.java │ │ │ │ │ │ ├── MainPresenter.java │ │ │ │ │ │ └── MainActivity.java │ │ │ │ │ ├── base │ │ │ │ │ │ ├── BaseComponent.java │ │ │ │ │ │ ├── fragments │ │ │ │ │ │ │ ├── BaseDialogFragment.java │ │ │ │ │ │ │ └── BaseFragment.java │ │ │ │ │ │ ├── Base.java │ │ │ │ │ │ ├── BasePresenter.java │ │ │ │ │ │ └── BaseActivity.java │ │ │ │ │ ├── splash │ │ │ │ │ │ └── SplashActivity.java │ │ │ │ │ └── webview │ │ │ │ │ │ └── WebViewActivity.java │ │ │ │ ├── broadcast │ │ │ │ │ └── InternetReceiver.java │ │ │ │ └── dialog │ │ │ │ │ └── DatePickerDialogFragment.java │ │ │ │ ├── infraestructure │ │ │ │ ├── networking │ │ │ │ │ ├── interfaces │ │ │ │ │ │ └── UserServer.java │ │ │ │ │ ├── gson │ │ │ │ │ │ ├── Exclude.java │ │ │ │ │ │ └── AnnotationExclusionStrategy.java │ │ │ │ │ ├── implementation │ │ │ │ │ │ ├── BaseServerImpl.java │ │ │ │ │ │ └── UserServerImpl.java │ │ │ │ │ ├── MyAppServerError.java │ │ │ │ │ ├── RestConstants.java │ │ │ │ │ ├── NetModule.java │ │ │ │ │ ├── ServerException.java │ │ │ │ │ ├── RxErrorHandlingCallAdapterFactory.java │ │ │ │ │ └── RetrofitManager.java │ │ │ │ ├── manager │ │ │ │ │ ├── interfaces │ │ │ │ │ │ ├── ParserManager.java │ │ │ │ │ │ ├── TaskManager.java │ │ │ │ │ │ ├── FileManager.java │ │ │ │ │ │ ├── ImageManager.java │ │ │ │ │ │ └── SharedPreferencesManager.java │ │ │ │ │ ├── implementation │ │ │ │ │ │ ├── PendingTask.java │ │ │ │ │ │ ├── ParserManagerImpl.java │ │ │ │ │ │ ├── TaskManagerImpl.java │ │ │ │ │ │ ├── FileManagerImpl.java │ │ │ │ │ │ ├── SharedPreferencesManagerImpl.java │ │ │ │ │ │ └── ImageManagerImpl.java │ │ │ │ │ ├── InternetManager.java │ │ │ │ │ ├── MailManager.java │ │ │ │ │ ├── PhotoDecodeTask.java │ │ │ │ │ ├── SocialManager.java │ │ │ │ │ ├── CropManager.java │ │ │ │ │ ├── SystemManager.java │ │ │ │ │ ├── GalleryPhotoManager.java │ │ │ │ │ └── CameraManager.java │ │ │ │ └── pushNotification │ │ │ │ │ ├── processor │ │ │ │ │ ├── base │ │ │ │ │ │ ├── NotificationType.java │ │ │ │ │ │ └── PushNotificationProcessor.java │ │ │ │ │ └── ConcretePushNotification.java │ │ │ │ │ └── PushNotificationIntentReceiver.java │ │ │ │ ├── domain │ │ │ │ ├── repository │ │ │ │ │ ├── interfaces │ │ │ │ │ │ ├── UserRepository.java │ │ │ │ │ │ └── SessionRepository.java │ │ │ │ │ ├── implementation │ │ │ │ │ │ ├── UserRepositoryImpl.java │ │ │ │ │ │ └── SessionRepositoryImpl.java │ │ │ │ │ └── RepositoryModule.java │ │ │ │ ├── model │ │ │ │ │ └── User.java │ │ │ │ └── util │ │ │ │ │ └── SnackBarData.java │ │ │ │ ├── application │ │ │ │ ├── AppComponent.java │ │ │ │ ├── MyApp.java │ │ │ │ └── AppModule.java │ │ │ │ └── services │ │ │ │ └── MyAppService.java │ │ └── AndroidManifest.xml │ └── test │ │ ├── resources │ │ └── robolectric.properties │ │ └── java │ │ └── co │ │ └── lateralview │ │ └── myapp │ │ ├── TestApplication.java │ │ ├── TrampolineSchedulerRule.java │ │ ├── domain │ │ └── repository │ │ │ └── implementation │ │ │ └── UserRepositoryImplTest.java │ │ ├── infraestructure │ │ └── manager │ │ │ └── InternetManagerTest.java │ │ └── ui │ │ └── activities │ │ └── main │ │ └── MainPresenterTest.java ├── checkstyle.gradle ├── google-services.json ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── keystore.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── ci ├── suppressions.xml └── checkstyle.xml ├── gradle.properties ├── gradlew.bat ├── .circleci └── config.yml ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /keystore.properties: -------------------------------------------------------------------------------- 1 | storePassword=storePassword 2 | keyPassword=keyPassword 3 | keyAlias=myapp 4 | storeFile=../myapp.keystore.jks -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LateralView/android-base-project/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LateralView/android-base-project/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LateralView/android-base-project/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LateralView/android-base-project/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LateralView/android-base-project/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LateralView/android-base-project/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/test/resources/robolectric.properties: -------------------------------------------------------------------------------- 1 | constants=co.lateralview.myapp.BuildConfig 2 | manifest=--none 3 | application=co.lateralview.myapp.TestApplication -------------------------------------------------------------------------------- /app/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /app/src/test/java/co/lateralview/myapp/TestApplication.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp; 2 | 3 | import android.app.Application; 4 | 5 | public class TestApplication extends Application 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/res/selector_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/selector_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_oval_gray.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu May 17 11:20:37 ART 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/BaseGlideModule.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | import com.bumptech.glide.annotation.GlideModule; 4 | import com.bumptech.glide.module.AppGlideModule; 5 | 6 | @GlideModule 7 | public final class BaseGlideModule extends AppGlideModule { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/interfaces/UserServer.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking.interfaces; 2 | 3 | import co.lateralview.myapp.domain.model.User; 4 | import io.reactivex.Single; 5 | 6 | public interface UserServer { 7 | Single login(String email, String password); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/domain/repository/interfaces/UserRepository.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.domain.repository.interfaces; 2 | 3 | import co.lateralview.myapp.domain.model.User; 4 | import io.reactivex.Single; 5 | 6 | public interface UserRepository { 7 | Single login(String userEmail, String userPassword); 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/di/ActivityScoped.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util.di; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | import javax.inject.Scope; 7 | 8 | @Scope 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface ActivityScoped { 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toolbar_dropshadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/checkstyle.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'checkstyle' 2 | 3 | task Checkstyle(type: Checkstyle) { 4 | configFile file("${project.rootDir}/ci/checkstyle.xml") 5 | source 'src' 6 | include '**/*.java' 7 | exclude '**/gen/**' 8 | exclude '**/test/**' 9 | exclude '**/R.java' 10 | exclude '**/BuildConfig.java' 11 | 12 | classpath = files() 13 | } -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/ParserManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.interfaces; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | public interface ParserManager { 6 | String toJson(Object object); 7 | 8 | T fromJson(String json, Class type); 9 | 10 | T fromJson(String json, Type type); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/domain/model/User.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.domain.model; 2 | 3 | import java.io.Serializable; 4 | 5 | @SuppressWarnings("checkstyle:hideutilityclassconstructor") 6 | public class User implements Serializable { 7 | public static final String SHARED_PREFERENCE_KEY = "co.lateralview.myapp.domain.model.User"; 8 | 9 | public User() { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back_black.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back_white.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/gson/Exclude.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking.gson; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.FIELD) 10 | public @interface Exclude { 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ripple_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #FFF 8 | #000 9 | 10 | #e47373 11 | #59b394 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/TaskManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.interfaces; 2 | 3 | import co.lateralview.myapp.infraestructure.manager.implementation.PendingTask; 4 | 5 | public interface TaskManager { 6 | void callPendingTasks(); 7 | 8 | void addTask(PendingTask task); 9 | 10 | void removeTasks(String tag); 11 | 12 | void clearQueue(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_transparent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar_without_elevation.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/FileManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.interfaces; 2 | 3 | import android.graphics.Bitmap; 4 | import android.net.Uri; 5 | 6 | import java.io.File; 7 | 8 | public interface FileManager { 9 | String savePhotoToInternalStorage(Bitmap bitmapImage); 10 | 11 | File createPhotoFile(); 12 | 13 | Uri getUri(File file); 14 | 15 | Uri createPhotoUri(); 16 | 17 | void saveBitmapToFile(Bitmap croppedImage, Uri saveUri); 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | /*/build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # OSX files 30 | .DS_Store 31 | 32 | # Android Studio 33 | *.iml 34 | .idea 35 | 36 | # Windows files 37 | Thumbs.db -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/implementation/BaseServerImpl.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking.implementation; 2 | 3 | import co.lateralview.myapp.infraestructure.networking.RetrofitManager; 4 | 5 | public class BaseServerImpl { 6 | protected static final String TAG = BaseServerImpl.class.getSimpleName(); 7 | 8 | protected RetrofitManager mRetrofitManager; 9 | 10 | public BaseServerImpl(RetrofitManager retrofitManager) { 11 | mRetrofitManager = retrofitManager; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/main/MainComponent.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.main; 2 | 3 | 4 | import co.lateralview.myapp.application.AppComponent; 5 | import co.lateralview.myapp.ui.activities.base.Base; 6 | import co.lateralview.myapp.ui.util.di.ActivityScoped; 7 | import dagger.Component; 8 | 9 | @ActivityScoped 10 | @Component(dependencies = AppComponent.class, 11 | modules = {Base.BaseViewModule.class, Main.ViewModule.class}) 12 | public interface MainComponent { 13 | void inject(MainActivity activity); 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/MyAppServerError.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | public class MyAppServerError { 6 | @SerializedName("error_code") 7 | private Integer mErrorCode; 8 | 9 | @SerializedName("message") 10 | private String mErrorMessage; 11 | 12 | public int getErrorCode() { 13 | return mErrorCode; 14 | } 15 | 16 | public String getErrorMessage() { 17 | return mErrorMessage; 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/main/MainActivityComponent.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.main; 2 | 3 | 4 | import co.lateralview.myapp.application.AppComponent; 5 | import co.lateralview.myapp.ui.activities.base.Base; 6 | import co.lateralview.myapp.ui.util.di.ActivityScoped; 7 | import dagger.Component; 8 | 9 | @ActivityScoped 10 | @Component(dependencies = AppComponent.class, 11 | modules = {Base.BaseViewModule.class, Main.ViewModule.class}) 12 | public interface MainActivityComponent { 13 | void inject(MainActivity activity); 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | 4 | public abstract class StringUtils { 5 | public static String upperCase(String string) { 6 | return upperCase(string, string.length()); 7 | } 8 | 9 | public static String upperCase(String string, int upperCaseChars) { 10 | return string.substring(0, upperCaseChars).toUpperCase() + string.substring(upperCaseChars); 11 | } 12 | 13 | public static String removeChars(String s, String c) { 14 | return s.replaceAll(c, ""); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_progress_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/MathUtils.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | 4 | import java.math.BigDecimal; 5 | import java.math.RoundingMode; 6 | 7 | public final class MathUtils { 8 | 9 | private MathUtils() { 10 | } 11 | 12 | public static float round(float value, int places) { 13 | if (places < 0) { 14 | throw new IllegalArgumentException(); 15 | } 16 | 17 | BigDecimal bd = new BigDecimal(value); 18 | bd = bd.setScale(places, RoundingMode.HALF_UP); 19 | return bd.floatValue(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/gson/AnnotationExclusionStrategy.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking.gson; 2 | 3 | import com.google.gson.ExclusionStrategy; 4 | import com.google.gson.FieldAttributes; 5 | 6 | public class AnnotationExclusionStrategy implements ExclusionStrategy { 7 | 8 | @Override 9 | public boolean shouldSkipField(FieldAttributes f) { 10 | return f.getAnnotation(Exclude.class) != null; 11 | } 12 | 13 | @Override 14 | public boolean shouldSkipClass(Class clazz) { 15 | return false; 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/implementation/PendingTask.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.implementation; 2 | 3 | public class PendingTask { 4 | private String mTag; 5 | private ITasksListener mListener; 6 | 7 | public PendingTask(String mTag, ITasksListener mListener) { 8 | this.mTag = mTag; 9 | this.mListener = mListener; 10 | } 11 | 12 | public void callPendingTask() { 13 | mListener.callPendingTask(); 14 | } 15 | 16 | public String getTag() { 17 | return mTag; 18 | } 19 | 20 | public interface ITasksListener { 21 | void callPendingTask(); 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/res/values/theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/base/BaseComponent.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.base; 2 | 3 | 4 | import co.lateralview.myapp.application.AppComponent; 5 | import co.lateralview.myapp.services.MyAppService; 6 | import co.lateralview.myapp.ui.activities.splash.SplashActivity; 7 | import co.lateralview.myapp.ui.util.di.ActivityScoped; 8 | import dagger.Component; 9 | 10 | @ActivityScoped 11 | @Component(dependencies = AppComponent.class, 12 | modules = {Base.BaseViewModule.class}) 13 | public interface BaseComponent { 14 | void inject(BaseActivity activity); 15 | 16 | void inject(SplashActivity activity); 17 | 18 | void inject(MyAppService service); 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/main/Main.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.main; 2 | 3 | import dagger.Module; 4 | import dagger.Provides; 5 | 6 | public interface Main { 7 | interface Presenter { 8 | void login(String email, String password); 9 | 10 | void destroy(); 11 | } 12 | 13 | interface View { 14 | void showError(); 15 | } 16 | 17 | @Module 18 | class ViewModule { 19 | private final View mView; 20 | 21 | public ViewModule(View view) { 22 | mView = view; 23 | } 24 | 25 | @Provides 26 | View provideView() { 27 | return mView; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/base/fragments/BaseDialogFragment.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.base.fragments; 2 | 3 | import android.support.v4.app.DialogFragment; 4 | 5 | import co.lateralview.myapp.application.AppComponent; 6 | import co.lateralview.myapp.ui.activities.base.Base; 7 | import co.lateralview.myapp.ui.activities.base.BaseActivity; 8 | 9 | 10 | public class BaseDialogFragment extends DialogFragment { 11 | protected AppComponent getAppComponent() { 12 | return ((BaseActivity) getActivity()).getAppComponent(); 13 | } 14 | 15 | protected Base.BaseViewModule getBaseActivityModule() { 16 | return ((BaseActivity) getActivity()).getBaseActivityModule(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/base/Base.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.base; 2 | 3 | import dagger.Module; 4 | import dagger.Provides; 5 | 6 | public interface Base { 7 | interface Presenter { 8 | //TODO 9 | } 10 | 11 | interface View { 12 | void showInternetRequiredError(); 13 | 14 | void showUnexpectedErrorHappened(); 15 | 16 | void logout(); 17 | } 18 | 19 | @Module 20 | class BaseViewModule { 21 | private final View mView; 22 | 23 | public BaseViewModule(View view) { 24 | mView = view; 25 | } 26 | 27 | @Provides 28 | View provideView() { 29 | return mView; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/domain/repository/interfaces/SessionRepository.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.domain.repository.interfaces; 2 | 3 | import co.lateralview.myapp.domain.model.User; 4 | import io.reactivex.Completable; 5 | import io.reactivex.Single; 6 | 7 | /* 8 | To save the current user in the device. This repository doesn't make server calls. 9 | */ 10 | public interface SessionRepository { 11 | //TODO Do this reactive (SharedPreferences cause I/O Block) 12 | 13 | Single isUserLoggedIn(); 14 | 15 | Completable logOut(); 16 | 17 | Completable logIn(User user, String accessToken); 18 | 19 | Single getCurrentUser(); 20 | 21 | Single updateUser(User user); 22 | 23 | Single getAccessToken(); 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/DialogUtils.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.support.v7.app.AlertDialog; 6 | 7 | public final class DialogUtils { 8 | 9 | private DialogUtils() { 10 | 11 | } 12 | 13 | public static AlertDialog createDialog(Context context, 14 | DialogInterface.OnClickListener disconnectClickListener) { 15 | return new AlertDialog.Builder(context) 16 | .setTitle("Title") 17 | .setMessage("Description") 18 | .setPositiveButton("Ok", disconnectClickListener) 19 | .setNegativeButton("cancel", null) 20 | .setCancelable(false) 21 | .create(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_web_view.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 10 | 11 | 16 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/ImageManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.interfaces; 2 | 3 | import android.graphics.Bitmap; 4 | import android.widget.ImageView; 5 | 6 | import com.bumptech.glide.request.target.SimpleTarget; 7 | 8 | import java.io.File; 9 | 10 | public interface ImageManager { 11 | void loadCircleImage(String url, ImageView imageView); 12 | 13 | void loadImage(String url, ImageView imageView); 14 | 15 | void loadImage(String url, SimpleTarget simpleTarget); 16 | 17 | void loadGifFromRes(int res, ImageView imageView); 18 | 19 | Bitmap compressImage(Bitmap bitmap, File file); 20 | 21 | Bitmap rotateBitmap(Bitmap bitmap, int orientation); 22 | 23 | Bitmap blur(Bitmap image); 24 | 25 | Bitmap transformToCircle(Bitmap bitmap); 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/pushNotification/processor/base/NotificationType.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.pushNotification.processor.base; 2 | 3 | import java.io.Serializable; 4 | 5 | public enum NotificationType implements Serializable { 6 | NOTIFICATION_TYPE("NOTIFICATION_TYPE"); 7 | 8 | private String mType; 9 | 10 | NotificationType(String type) { 11 | mType = type; 12 | } 13 | 14 | public static NotificationType fromString(String type) { 15 | for (NotificationType notificationType : NotificationType.values()) { 16 | if (notificationType.getType().equals(type)) { 17 | return notificationType; 18 | } 19 | } 20 | return null; 21 | } 22 | 23 | public String getType() { 24 | return mType; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/implementation/ParserManagerImpl.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.implementation; 2 | 3 | import com.google.gson.Gson; 4 | 5 | import java.lang.reflect.Type; 6 | 7 | import co.lateralview.myapp.infraestructure.manager.interfaces.ParserManager; 8 | 9 | public class ParserManagerImpl implements ParserManager { 10 | private Gson mGson; 11 | 12 | public ParserManagerImpl(Gson gson) { 13 | mGson = gson; 14 | } 15 | 16 | public String toJson(Object object) { 17 | return mGson.toJson(object); 18 | } 19 | 20 | public T fromJson(String json, Class type) { 21 | return mGson.fromJson(json, type); 22 | } 23 | 24 | @Override 25 | public T fromJson(String json, Type type) { 26 | return mGson.fromJson(json, type); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/InternetManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo; 6 | 7 | public class InternetManager { 8 | private ConnectivityManager mConnectivityManager; 9 | 10 | public InternetManager(Context context) { 11 | mConnectivityManager = (ConnectivityManager) context.getSystemService( 12 | Context.CONNECTIVITY_SERVICE); 13 | } 14 | 15 | public Boolean isOnline() { 16 | NetworkInfo netInfo = mConnectivityManager.getActiveNetworkInfo(); 17 | return netInfo != null && netInfo.isConnectedOrConnecting(); 18 | } 19 | 20 | public boolean onWifi() { 21 | return !mConnectivityManager.isActiveNetworkMetered(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ci/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MyApp 3 | Settings 4 | 5 | 6 | sec 7 | min 8 | h 9 | days 10 | months 11 | years 12 | 13 | 14 | No internet connection 15 | 16 | 17 | Connection Error 18 | RETRY 19 | 20 | 21 | Coming soon 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/domain/repository/implementation/UserRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.domain.repository.implementation; 2 | 3 | import javax.inject.Inject; 4 | 5 | import co.lateralview.myapp.domain.model.User; 6 | import co.lateralview.myapp.domain.repository.interfaces.UserRepository; 7 | import co.lateralview.myapp.infraestructure.networking.interfaces.UserServer; 8 | import co.lateralview.myapp.ui.util.RxSchedulersUtils; 9 | import io.reactivex.Single; 10 | 11 | public class UserRepositoryImpl implements UserRepository { 12 | @Inject 13 | UserServer mUserServer; 14 | 15 | @Inject 16 | public UserRepositoryImpl() { 17 | } 18 | 19 | @Override 20 | public Single login(final String email, final String password) { 21 | return mUserServer.login(email, password) 22 | .compose(RxSchedulersUtils.applySingleSchedulers()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/domain/repository/RepositoryModule.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.domain.repository; 2 | 3 | import javax.inject.Singleton; 4 | 5 | import co.lateralview.myapp.domain.repository.implementation.SessionRepositoryImpl; 6 | import co.lateralview.myapp.domain.repository.implementation.UserRepositoryImpl; 7 | import co.lateralview.myapp.domain.repository.interfaces.SessionRepository; 8 | import co.lateralview.myapp.domain.repository.interfaces.UserRepository; 9 | import dagger.Module; 10 | import dagger.Provides; 11 | 12 | @Module 13 | public class RepositoryModule { 14 | @Provides 15 | @Singleton 16 | public SessionRepository provideSessionRepository( 17 | SessionRepositoryImpl sessionRepository) { 18 | return sessionRepository; 19 | } 20 | 21 | @Provides 22 | @Singleton 23 | public UserRepository provideUserRepository(UserRepositoryImpl userRepository) { 24 | return userRepository; 25 | } 26 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | #Required for Butterknife with Jack Enabled 21 | org.gradle.jvmargs=-Xmx1536m 22 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/MailManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | 7 | import co.lateralview.myapp.R; 8 | 9 | public class MailManager { 10 | public MailManager() { 11 | 12 | } 13 | 14 | public void sendSupportMail(Activity activity, String to, String subject) { 15 | StringBuffer buffer = new StringBuffer(); 16 | 17 | buffer.append("mailto:") 18 | .append(to) 19 | .append("?subject=") 20 | .append(subject) 21 | .append("&body="); 22 | 23 | String uriString = buffer.toString().replace(" ", "%20"); 24 | 25 | activity.startActivity( 26 | Intent.createChooser(new Intent(Intent.ACTION_SENDTO, Uri.parse(uriString)), 27 | activity.getString(R.string.app_name) + " help mail")); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/RestConstants.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking; 2 | 3 | import co.lateralview.myapp.BuildConfig; 4 | 5 | public final class RestConstants { 6 | public static final String BASE_URL = BuildConfig.BASE_URL; 7 | 8 | //TODO Set Auth Header 9 | public static final String HEADER_AUTH = "auth-header"; 10 | 11 | public enum Subcode { 12 | INVALID_TOKEN(200002); 13 | 14 | private int mSubcode; 15 | 16 | Subcode(int subcode) { 17 | mSubcode = subcode; 18 | } 19 | 20 | public static Subcode fromInt(int code) { 21 | for (Subcode subcode : Subcode.values()) { 22 | if (subcode.getSubcode() == code) { 23 | return subcode; 24 | } 25 | } 26 | return null; 27 | } 28 | 29 | public int getSubcode() { 30 | return mSubcode; 31 | } 32 | } 33 | 34 | private RestConstants() { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/NetModule.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking; 2 | 3 | import android.app.Application; 4 | 5 | import com.google.gson.Gson; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import co.lateralview.myapp.domain.repository.interfaces.SessionRepository; 10 | import co.lateralview.myapp.infraestructure.manager.InternetManager; 11 | import co.lateralview.myapp.infraestructure.networking.implementation.UserServerImpl; 12 | import co.lateralview.myapp.infraestructure.networking.interfaces.UserServer; 13 | import dagger.Module; 14 | import dagger.Provides; 15 | 16 | @Module 17 | public class NetModule { 18 | @Provides 19 | @Singleton 20 | public RetrofitManager provideRetrofitManager(Application application, 21 | Gson gson, 22 | SessionRepository sessionRepository, 23 | InternetManager internetManager) { 24 | return new RetrofitManager(application, gson, sessionRepository, internetManager); 25 | } 26 | 27 | @Provides 28 | @Singleton 29 | public UserServer provideUserServer(UserServerImpl userServer) { 30 | return userServer; 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/pushNotification/processor/ConcretePushNotification.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.pushNotification.processor; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | 6 | import java.io.Serializable; 7 | 8 | import co.lateralview.myapp.infraestructure.pushNotification.processor.base.NotificationType; 9 | import co.lateralview.myapp.infraestructure.pushNotification.processor.base 10 | .PushNotificationProcessor; 11 | 12 | 13 | public class ConcretePushNotification extends PushNotificationProcessor implements Serializable { 14 | public ConcretePushNotification(NotificationType notificationType, String title, 15 | String description, boolean fromActivity) { 16 | super(notificationType, title, description, fromActivity); 17 | } 18 | 19 | @Override 20 | public void process(Context context) { 21 | Intent intent = null; 22 | 23 | if (isAppInForeground()) { 24 | //The app is open 25 | } else { 26 | //The app is closed 27 | } 28 | 29 | sendNotification(context, intent); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/application/AppComponent.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.application; 2 | 3 | 4 | import android.app.Application; 5 | 6 | import javax.inject.Singleton; 7 | 8 | import co.lateralview.myapp.domain.repository.RepositoryModule; 9 | import co.lateralview.myapp.domain.repository.interfaces.SessionRepository; 10 | import co.lateralview.myapp.domain.repository.interfaces.UserRepository; 11 | import co.lateralview.myapp.infraestructure.manager.InternetManager; 12 | import co.lateralview.myapp.infraestructure.manager.interfaces.ImageManager; 13 | import co.lateralview.myapp.infraestructure.manager.interfaces.TaskManager; 14 | import co.lateralview.myapp.infraestructure.networking.NetModule; 15 | import dagger.Component; 16 | 17 | @Singleton 18 | @Component( 19 | modules = { 20 | AppModule.class, 21 | NetModule.class, 22 | RepositoryModule.class 23 | } 24 | ) 25 | public interface AppComponent { 26 | Application application(); 27 | 28 | ImageManager imageManager(); 29 | 30 | InternetManager internetManager(); 31 | 32 | TaskManager taskManager(); 33 | 34 | UserRepository userRepository(); 35 | 36 | SessionRepository sessionRepository(); 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/SharedPreferencesManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.interfaces; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | public interface SharedPreferencesManager { 6 | void save(String key, boolean value); 7 | 8 | void save(String key, String value); 9 | 10 | void save(String key, int value); 11 | 12 | boolean saveBlocking(String key, boolean value); 13 | 14 | boolean saveBlocking(String key, String value); 15 | 16 | boolean saveBlocking(String key, int value); 17 | 18 | boolean getBoolean(String key); 19 | 20 | boolean getBoolean(String key, boolean defaultValue); 21 | 22 | String getString(String key); 23 | 24 | String getString(String key, String defaultValue); 25 | 26 | int getInt(String key); 27 | 28 | int getInt(String key, int defaultValue); 29 | 30 | void save(String key, T model); 31 | 32 | boolean saveBlocking(String key, T model); 33 | 34 | T get(String key, Class type); 35 | 36 | T get(String key, Type type); 37 | 38 | void clear(); 39 | 40 | boolean clearBlocking(); 41 | 42 | void remove(String key); 43 | 44 | boolean removeBlocking(String key); 45 | } 46 | -------------------------------------------------------------------------------- /app/src/test/java/co/lateralview/myapp/TrampolineSchedulerRule.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp; 2 | 3 | import org.junit.rules.TestRule; 4 | import org.junit.runner.Description; 5 | import org.junit.runners.model.Statement; 6 | 7 | import io.reactivex.android.plugins.RxAndroidPlugins; 8 | import io.reactivex.plugins.RxJavaPlugins; 9 | import io.reactivex.schedulers.Schedulers; 10 | 11 | public class TrampolineSchedulerRule implements TestRule { 12 | 13 | @Override 14 | public Statement apply(final Statement base, final Description description) { 15 | return new Statement() { 16 | @Override 17 | public void evaluate() throws Throwable { 18 | RxAndroidPlugins.setInitMainThreadSchedulerHandler(schedulerCallable -> Schedulers.trampoline()); 19 | RxJavaPlugins.setIoSchedulerHandler(schedulerCallable -> Schedulers.trampoline()); 20 | RxJavaPlugins.setComputationSchedulerHandler(schedulerCallable -> Schedulers.trampoline()); 21 | 22 | try { 23 | base.evaluate(); 24 | } finally { 25 | RxAndroidPlugins.reset(); 26 | RxJavaPlugins.reset(); 27 | } 28 | } 29 | }; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/main/MainPresenter.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.main; 2 | 3 | 4 | import javax.inject.Inject; 5 | 6 | import co.lateralview.myapp.domain.repository.interfaces.UserRepository; 7 | import co.lateralview.myapp.ui.activities.base.BasePresenter; 8 | import co.lateralview.myapp.ui.util.di.ActivityScoped; 9 | import io.reactivex.disposables.CompositeDisposable; 10 | 11 | @ActivityScoped 12 | public class MainPresenter extends BasePresenter implements Main.Presenter { 13 | public static final String TAG = "MainPresenter"; 14 | 15 | protected CompositeDisposable mSubscriptions = new CompositeDisposable(); 16 | 17 | @Inject 18 | Main.View mView; 19 | @Inject 20 | UserRepository mUserRepository; 21 | 22 | @Inject 23 | MainPresenter() { 24 | 25 | } 26 | 27 | @Override 28 | public void login(String email, String password) { 29 | mSubscriptions.add(mUserRepository.login(email, password) 30 | .flatMapCompletable(user -> mSessionRepository.logIn(user, "token")) 31 | .subscribe(() -> { /*login done*/ }, 32 | error -> mView.showError())); 33 | } 34 | 35 | @Override 36 | public void destroy() { 37 | mSubscriptions.dispose(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/PhotoDecodeTask.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.BitmapFactory; 5 | import android.os.AsyncTask; 6 | 7 | import java.io.File; 8 | 9 | public class PhotoDecodeTask extends AsyncTask { 10 | private IPhotoDecodeTaskCallback mPhotoDecodeTaskListener; 11 | 12 | public PhotoDecodeTask(IPhotoDecodeTaskCallback photoDecodeTaskListener) { 13 | mPhotoDecodeTaskListener = photoDecodeTaskListener; 14 | } 15 | 16 | protected Integer doInBackground(String... paths) { 17 | String photoPath = paths[0]; 18 | 19 | if (!photoPath.isEmpty()) { 20 | final File photo = new File(photoPath); 21 | 22 | if (photo.exists()) { 23 | final Bitmap bitmap = BitmapFactory.decodeFile(photoPath); 24 | 25 | mPhotoDecodeTaskListener.onPhotoDecodeTaskSuccess(bitmap); 26 | } 27 | } 28 | 29 | return 0; 30 | } 31 | 32 | protected void onProgressUpdate(Integer... progress) { 33 | } 34 | 35 | public interface IPhotoDecodeTaskCallback { 36 | void onPhotoDecodeTaskSuccess(Bitmap photo); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/SocialManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.net.Uri; 7 | import android.support.v4.app.ShareCompat; 8 | 9 | import co.lateralview.myapp.infraestructure.manager.implementation.FileManagerImpl; 10 | 11 | public class SocialManager { 12 | private FileManagerImpl mFileManager; 13 | 14 | public SocialManager(FileManagerImpl fileManager) { 15 | mFileManager = fileManager; 16 | } 17 | 18 | public void shareData(Activity activity, String text) { 19 | Intent shareIntent = ShareCompat.IntentBuilder.from(activity) 20 | .setType("text/plain") 21 | .setText(text) 22 | .getIntent(); 23 | 24 | activity.startActivity(shareIntent); 25 | } 26 | 27 | public void shareData(Activity activity, String text, Bitmap image) { 28 | Intent shareIntent = ShareCompat.IntentBuilder.from(activity) 29 | .setType("image/jpeg") 30 | .setText(text) 31 | .setStream(Uri.parse(mFileManager.savePhotoToInternalStorage(image))) 32 | .getIntent(); 33 | 34 | activity.startActivity(shareIntent); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "934985628718", 4 | "firebase_url": "https://base-project-b3d16.firebaseio.com", 5 | "project_id": "base-project-b3d16", 6 | "storage_bucket": "base-project-b3d16.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:934985628718:android:6a296f4475a1e127", 12 | "android_client_info": { 13 | "package_name": "co.lateralview.myapp" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "934985628718-sk43fjv71nfupnerkni8agh4tqi53u61.apps.googleusercontent.com", 19 | "client_type": 3 20 | }, 21 | { 22 | "client_id": "934985628718-sk43fjv71nfupnerkni8agh4tqi53u61.apps.googleusercontent.com", 23 | "client_type": 3 24 | } 25 | ], 26 | "api_key": [ 27 | { 28 | "current_key": "AIzaSyAWlffPdZ3ClF8LmF0xNM-5akS-sQ7lLLc" 29 | } 30 | ], 31 | "services": { 32 | "analytics_service": { 33 | "status": 1 34 | }, 35 | "appinvite_service": { 36 | "status": 1, 37 | "other_platform_oauth_client": [] 38 | }, 39 | "ads_service": { 40 | "status": 2 41 | } 42 | } 43 | } 44 | ], 45 | "configuration_version": "1" 46 | } -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/pushNotification/PushNotificationIntentReceiver.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.pushNotification; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.v4.content.WakefulBroadcastReceiver; 7 | 8 | import co.lateralview.myapp.application.MyApp; 9 | import co.lateralview.myapp.infraestructure.pushNotification.processor.base.NotificationType; 10 | import co.lateralview.myapp.infraestructure.pushNotification.processor.base 11 | .PushNotificationProcessor; 12 | 13 | public class PushNotificationIntentReceiver extends WakefulBroadcastReceiver { 14 | public static final String TAG = PushNotificationIntentReceiver.class.getSimpleName(); 15 | 16 | @Override 17 | public void onReceive(Context context, Intent intent) { 18 | Bundle extras = intent.getExtras(); 19 | 20 | PushNotificationProcessor concretePushNotification = PushNotificationProcessor.create( 21 | NotificationType.fromString(extras.getString("type").toUpperCase()), 22 | extras.getString("title"), extras.getString("message"), 23 | MyApp.isApplicationRunning()); 24 | 25 | if (concretePushNotification != null) { 26 | concretePushNotification.process(context); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/application/MyApp.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.application; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.stetho.Stetho; 6 | 7 | import co.lateralview.myapp.services.MyAppService; 8 | 9 | public class MyApp extends Application { 10 | public static final String TAG = MyApp.class.getSimpleName(); 11 | 12 | private static String sCurrentScreenTag; 13 | 14 | private static AppComponent mAppComponent; 15 | 16 | public static AppComponent getAppComponent() { 17 | return mAppComponent; 18 | } 19 | 20 | public static void setCurrentScreenTag(String tag) { 21 | sCurrentScreenTag = tag; 22 | } 23 | 24 | public static boolean isApplicationRunning() { 25 | return sCurrentScreenTag != null; 26 | } 27 | 28 | @Override 29 | public void onCreate() { 30 | super.onCreate(); 31 | 32 | initializeServices(); 33 | } 34 | 35 | private void initializeServices() { 36 | mAppComponent = DaggerAppComponent.builder() 37 | // list of modules that are part of this component need to be created here too 38 | .appModule(new AppModule(this)) 39 | .build(); 40 | 41 | //TODO Wrapper 42 | Stetho.initializeWithDefaults(this); 43 | 44 | MyAppService.startService(this); 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/RxSchedulersUtils.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | 4 | import io.reactivex.CompletableTransformer; 5 | import io.reactivex.MaybeTransformer; 6 | import io.reactivex.ObservableTransformer; 7 | import io.reactivex.SingleTransformer; 8 | import io.reactivex.android.schedulers.AndroidSchedulers; 9 | import io.reactivex.schedulers.Schedulers; 10 | 11 | public final class RxSchedulersUtils { 12 | 13 | private RxSchedulersUtils() { 14 | 15 | } 16 | 17 | public static ObservableTransformer applyObservableSchedulers() { 18 | return upstream -> upstream.subscribeOn(Schedulers.io()) 19 | .observeOn(AndroidSchedulers.mainThread()); 20 | } 21 | 22 | public static MaybeTransformer applyMaybeSchedulers() { 23 | return upstream -> upstream.subscribeOn(Schedulers.io()) 24 | .observeOn(AndroidSchedulers.mainThread()); 25 | } 26 | 27 | public static SingleTransformer applySingleSchedulers() { 28 | return upstream -> upstream.subscribeOn(Schedulers.io()) 29 | .observeOn(AndroidSchedulers.mainThread()); 30 | } 31 | 32 | public static CompletableTransformer applyCompletableSchedulers() { 33 | return upstream -> upstream.subscribeOn(Schedulers.io()) 34 | .observeOn(AndroidSchedulers.mainThread()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/test/java/co/lateralview/myapp/domain/repository/implementation/UserRepositoryImplTest.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.domain.repository.implementation; 2 | 3 | import org.junit.ClassRule; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.InjectMocks; 7 | import org.mockito.Mock; 8 | import org.mockito.junit.MockitoJUnitRunner; 9 | 10 | import co.lateralview.myapp.TrampolineSchedulerRule; 11 | import co.lateralview.myapp.domain.model.User; 12 | import co.lateralview.myapp.infraestructure.networking.interfaces.UserServer; 13 | import io.reactivex.Single; 14 | import io.reactivex.observers.TestObserver; 15 | 16 | import static org.mockito.Mockito.when; 17 | 18 | @RunWith(MockitoJUnitRunner.class) 19 | public class UserRepositoryImplTest { 20 | 21 | @ClassRule public static TrampolineSchedulerRule mTrampolineSchedulerRule = new TrampolineSchedulerRule(); 22 | 23 | @Mock UserServer mUserServer; 24 | @InjectMocks UserRepositoryImpl mUserRepository; 25 | 26 | @Test 27 | public void itShouldLoginUser() 28 | { 29 | String email = "email"; 30 | String password = "password"; 31 | User user = new User(); 32 | when(mUserServer.login(email, password)).thenReturn(Single.just(user)); 33 | 34 | TestObserver observer = mUserRepository.login(email, password).test(); 35 | observer.assertValue(user); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/splash/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.splash; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | 6 | import co.lateralview.myapp.R; 7 | import co.lateralview.myapp.ui.activities.base.BaseActivity; 8 | import co.lateralview.myapp.ui.activities.base.DaggerBaseComponent; 9 | import co.lateralview.myapp.ui.activities.main.MainActivity; 10 | import co.lateralview.myapp.ui.util.SystemUtils; 11 | 12 | public class SplashActivity extends BaseActivity { 13 | public static final String TAG = "SplashActivity"; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | 19 | setContentView(R.layout.activity_splash); 20 | 21 | SystemUtils.fullScreenMode(this); 22 | 23 | new Handler().postDelayed(() -> start(), 24 | 2000); 25 | } 26 | 27 | public String getTAG() { 28 | return TAG; 29 | } 30 | 31 | @Override 32 | protected void injectDependencies() { 33 | DaggerBaseComponent.builder() 34 | .appComponent(getAppComponent()) 35 | .baseViewModule(getBaseActivityModule()) 36 | .build().inject(this); 37 | } 38 | 39 | private void start() { 40 | startActivity(MainActivity.newInstance(SplashActivity.this, true)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/implementation/TaskManagerImpl.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.implementation; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import co.lateralview.myapp.infraestructure.manager.interfaces.TaskManager; 7 | 8 | public class TaskManagerImpl implements TaskManager { 9 | private List mPendingTasksQueue = new ArrayList<>(); 10 | 11 | public void callPendingTasks() { 12 | if (!mPendingTasksQueue.isEmpty()) { 13 | for (PendingTask pendingTask : mPendingTasksQueue) { 14 | pendingTask.callPendingTask(); 15 | } 16 | 17 | clearQueue(); 18 | } 19 | } 20 | 21 | public void addTask(PendingTask task) { 22 | if (!mPendingTasksQueue.contains(task)) { 23 | mPendingTasksQueue.add(task); 24 | } 25 | } 26 | 27 | public void removeTasks(String tag) { 28 | List pendingTasks = new ArrayList<>(); 29 | 30 | for (PendingTask pendingTask : mPendingTasksQueue) { 31 | if (!pendingTask.getTag().equals(tag)) { 32 | pendingTasks.add(pendingTask); 33 | } 34 | } 35 | 36 | mPendingTasksQueue.clear(); 37 | mPendingTasksQueue.addAll(pendingTasks); 38 | } 39 | 40 | public void clearQueue() { 41 | mPendingTasksQueue.clear(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/KeyboardUtils.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.view.inputmethod.InputMethodManager; 6 | 7 | public final class KeyboardUtils { 8 | 9 | private KeyboardUtils() { 10 | } 11 | 12 | public static void hideSoftKeyboard(Activity activity) { 13 | if (activity.getCurrentFocus() != null) { 14 | InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService( 15 | Context.INPUT_METHOD_SERVICE); 16 | inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 17 | 0); 18 | } 19 | } 20 | 21 | public static void showKeyboard(Activity activity) { 22 | if (!isKeyboardVisible(activity)) { 23 | InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService( 24 | Context.INPUT_METHOD_SERVICE); 25 | activity.getWindow().getDecorView().requestFocus(); 26 | inputMethodManager.showSoftInput(activity.getWindow().getDecorView(), 0); 27 | } 28 | } 29 | 30 | public static boolean isKeyboardVisible(Activity activity) { 31 | InputMethodManager imm = (InputMethodManager) activity.getSystemService( 32 | Context.INPUT_METHOD_SERVICE); 33 | 34 | return imm.isAcceptingText(); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/broadcast/InternetReceiver.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.broadcast; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | 8 | import co.lateralview.myapp.infraestructure.manager.InternetManager; 9 | 10 | public class InternetReceiver extends BroadcastReceiver { 11 | private InternetReceiverListener mReceiverHandler; 12 | private InternetManager mInternetManager; 13 | 14 | public InternetReceiver(InternetReceiverListener handler, InternetManager internetManager) { 15 | mReceiverHandler = handler; 16 | mInternetManager = internetManager; 17 | } 18 | 19 | @Override 20 | public void onReceive(final Context context, final Intent intent) { 21 | if (mInternetManager.isOnline()) { 22 | mReceiverHandler.onInternetServiceEnabled(); 23 | } else { 24 | mReceiverHandler.onInternetServiceDisabled(); 25 | } 26 | } 27 | 28 | public IntentFilter getIntentFilter() { 29 | IntentFilter locationServicesChangeFilter = new IntentFilter(); 30 | 31 | locationServicesChangeFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); 32 | 33 | return locationServicesChangeFilter; 34 | } 35 | 36 | public interface InternetReceiverListener { 37 | void onInternetServiceEnabled(); 38 | 39 | void onInternetServiceDisabled(); 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 12 | 13 | 17 | 18 | 19 | 20 | 24 | 25 | 28 | 29 | 30 | 31 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/CropManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager; 2 | 3 | import android.app.Activity; 4 | import android.net.Uri; 5 | import android.support.v4.app.Fragment; 6 | 7 | import com.soundcloud.android.crop.Crop; 8 | 9 | public class CropManager { 10 | public static final int MAX_IMAGE_WIDTH = 600; 11 | public static final int MAX_IMAGE_HEIGHT = 600; 12 | 13 | protected Activity mCallerActivity; 14 | protected Fragment mCallerFragment; 15 | 16 | protected int mRequestId; 17 | 18 | public CropManager(Fragment fragment, int requestId) { 19 | this(fragment.getActivity(), requestId); 20 | mCallerFragment = fragment; 21 | } 22 | 23 | public CropManager(Activity activity, int requestId) { 24 | mCallerActivity = activity; 25 | mRequestId = requestId; 26 | } 27 | 28 | public boolean requestCrop(Uri imagePath, Uri outPath) { 29 | //Uri croppedImage = new FileManagerImpl().createPhotoUri(); 30 | 31 | if (outPath != null) { 32 | if (mCallerFragment != null) { 33 | Crop.of(imagePath, outPath) 34 | .withMaxSize(MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT) 35 | .start(mCallerActivity, mCallerFragment, mRequestId); 36 | } else { 37 | Crop.of(imagePath, outPath) 38 | .withMaxSize(MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT) 39 | .start(mCallerActivity, mRequestId); 40 | } 41 | 42 | return true; 43 | } 44 | 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/dialog/DatePickerDialogFragment.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.dialog; 2 | 3 | import android.app.DatePickerDialog; 4 | import android.app.Dialog; 5 | import android.os.Bundle; 6 | import android.support.v4.app.DialogFragment; 7 | import android.widget.DatePicker; 8 | 9 | import java.util.Calendar; 10 | import java.util.Date; 11 | 12 | public class DatePickerDialogFragment extends DialogFragment implements 13 | DatePickerDialog.OnDateSetListener { 14 | public static final String TAG = "DatePickerDialogFragment"; 15 | 16 | private DatePickerInterface mCallback; 17 | 18 | @Override 19 | public Dialog onCreateDialog(Bundle savedInstanceState) { 20 | final Calendar c = Calendar.getInstance(); 21 | int year = c.get(Calendar.YEAR); 22 | int month = c.get(Calendar.MONTH); 23 | int day = c.get(Calendar.DAY_OF_MONTH); 24 | 25 | return new DatePickerDialog(getActivity(), this, year, month, day); 26 | } 27 | 28 | public void onDateSet(DatePicker view, int year, int month, int day) { 29 | Calendar calendar = Calendar.getInstance(); 30 | calendar.clear(); 31 | calendar.set(Calendar.DATE, day); 32 | calendar.set(Calendar.MONTH, month); 33 | calendar.set(Calendar.YEAR, year); 34 | Date expirationDate = calendar.getTime(); 35 | 36 | if (mCallback != null) { 37 | mCallback.onDateSelected(expirationDate); 38 | } 39 | } 40 | 41 | public void setListener(DatePickerInterface datePickerInterface) { 42 | mCallback = datePickerInterface; 43 | } 44 | 45 | public interface DatePickerInterface { 46 | void onDateSelected(Date date); 47 | } 48 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | ################################## 2 | # Keep line numbers 3 | -renamesourcefileattribute SourceFile 4 | -keepattributes SourceFile, LineNumberTable 5 | -keepattributes *Annotation* 6 | -keepattributes Signature 7 | -keepattributes Exceptions 8 | 9 | ################################## 10 | # OkHttp3 11 | -dontwarn okio.** 12 | -dontwarn com.squareup.okhttp3.** 13 | -dontwarn okio.** 14 | -dontwarn org.conscrypt.OpenSSLProvider 15 | -dontwarn org.conscrypt.Conscrypt 16 | 17 | ################################## 18 | # Retrofit 19 | # Platform calls Class.forName on types which do not exist on Android to determine platform. 20 | -dontnote retrofit2.Platform 21 | # Platform used when running on Java 8 VMs. Will not be used at runtime. 22 | -dontwarn retrofit2.Platform$Java8 23 | 24 | ################################## 25 | # Icepick 26 | -dontwarn icepick.** 27 | -keep class icepick.** { *; } 28 | -keep class **$$Icepick { *; } 29 | -keepclasseswithmembernames class * { 30 | @icepick.* ; 31 | } 32 | -keepnames class * { @icepick.State *;} 33 | 34 | ################################## 35 | # Glide 36 | -keep public class * implements com.bumptech.glide.module.GlideModule 37 | -keep public class * extends com.bumptech.glide.module.AppGlideModule 38 | -keep class com.bumptech.glide.integration.okhttp3.OkHttpGlideModule 39 | -keep class com.bumptech.glide.GeneratedAppGlideModuleImpl 40 | -keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { 41 | **[] $VALUES; 42 | public *; 43 | } 44 | 45 | ################################## 46 | #Firebase Crashlytics 47 | -keep class com.crashlytics.** { *; } 48 | -dontwarn com.crashlytics.** 49 | 50 | ################################## 51 | # Lambda 52 | -dontwarn java.lang.invoke** 53 | 54 | -dontwarn javax.annotation.** -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/main/MainActivity.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.main; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import javax.inject.Inject; 8 | 9 | import butterknife.ButterKnife; 10 | import co.lateralview.myapp.R; 11 | import co.lateralview.myapp.ui.activities.base.BaseActivity; 12 | 13 | public class MainActivity extends BaseActivity implements Main.View { 14 | public static final String TAG = "MainActivity"; 15 | 16 | @Inject 17 | MainPresenter mPresenter; 18 | 19 | public static Intent newInstance(Context fromActivity, boolean clearStack) { 20 | return BaseActivity.newActivityInstance(fromActivity, clearStack, MainActivity.class); 21 | } 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | 27 | setContentView(R.layout.activity_main); 28 | 29 | ButterKnife.bind(this); 30 | 31 | initializeToolbar(false); 32 | } 33 | 34 | @Override 35 | protected void onDestroy() { 36 | mPresenter.destroy(); 37 | super.onDestroy(); 38 | } 39 | 40 | @Override 41 | protected void injectDependencies() { 42 | DaggerMainComponent.builder() 43 | .appComponent(getAppComponent()) 44 | .baseViewModule(getBaseActivityModule()) 45 | .viewModule(new Main.ViewModule(this)) 46 | .build().inject(this); 47 | } 48 | 49 | public String getTAG() { 50 | return TAG; 51 | } 52 | 53 | @Override 54 | public void onBackPressed() { 55 | moveTaskToBack(true); 56 | } 57 | 58 | @Override 59 | public void showError() { 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/base/fragments/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.base.fragments; 2 | 3 | import android.app.Fragment; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.view.View; 7 | 8 | import javax.inject.Inject; 9 | 10 | import co.lateralview.myapp.application.AppComponent; 11 | import co.lateralview.myapp.infraestructure.manager.interfaces.ImageManager; 12 | import co.lateralview.myapp.ui.activities.base.Base; 13 | import co.lateralview.myapp.ui.activities.base.BaseActivity; 14 | 15 | public abstract class BaseFragment extends Fragment { 16 | @Inject 17 | protected ImageManager mImageManager; 18 | 19 | @Override 20 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 21 | super.onViewCreated(view, savedInstanceState); 22 | 23 | injectDependencies(); 24 | } 25 | 26 | protected AppComponent getAppComponent() { 27 | return BaseActivity.getAppComponent(); 28 | } 29 | 30 | protected Base.BaseViewModule getBaseActivityModule() { 31 | return ((BaseActivity) getActivity()).getBaseActivityModule(); 32 | } 33 | 34 | protected abstract void injectDependencies(); 35 | 36 | public void onInternetServiceEnabled() { 37 | 38 | } 39 | 40 | public void onInternetServiceDisabled() { 41 | 42 | } 43 | 44 | public void showProgressDialog() { 45 | if (getActivity() != null) { 46 | ((BaseActivity) getActivity()).showProgressDialog(); 47 | } 48 | } 49 | 50 | public void hideProgressDialog() { 51 | if (getActivity() != null) { 52 | ((BaseActivity) getActivity()).hideProgressDialog(); 53 | } 54 | } 55 | 56 | public void showComingSoonMessage() { 57 | if (getActivity() != null) { 58 | ((BaseActivity) getActivity()).showComingSoonMessage(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/implementation/UserServerImpl.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking.implementation; 2 | 3 | import javax.inject.Inject; 4 | 5 | import co.lateralview.myapp.domain.model.User; 6 | import co.lateralview.myapp.infraestructure.networking.RetrofitManager; 7 | import co.lateralview.myapp.infraestructure.networking.interfaces.UserServer; 8 | import io.reactivex.Single; 9 | import retrofit2.http.Field; 10 | import retrofit2.http.FormUrlEncoded; 11 | import retrofit2.http.Headers; 12 | import retrofit2.http.POST; 13 | 14 | public class UserServerImpl extends BaseServerImpl implements UserServer { 15 | protected static final String TAG = UserServerImpl.class.getSimpleName(); 16 | 17 | private IUserServer mIUserServer; 18 | 19 | public enum Subcode { 20 | INVALID_CREDENTIALS(200001); 21 | 22 | private int mSubcode; 23 | 24 | Subcode(int subcode) { 25 | mSubcode = subcode; 26 | } 27 | 28 | public static Subcode fromInt(int code) { 29 | for (Subcode subcode : Subcode.values()) { 30 | if (subcode.getSubcode() == code) { 31 | return subcode; 32 | } 33 | } 34 | return null; 35 | } 36 | 37 | public int getSubcode() { 38 | return mSubcode; 39 | } 40 | } 41 | 42 | @Inject 43 | UserServerImpl(RetrofitManager retrofitManager) { 44 | super(retrofitManager); 45 | mIUserServer = mRetrofitManager.getRetrofit().create(IUserServer.class); 46 | } 47 | 48 | @Override 49 | public Single login(String email, String password) { 50 | return mIUserServer.login(email, password); 51 | } 52 | 53 | interface IUserServer { 54 | @FormUrlEncoded 55 | @Headers({"Accept: */*"}) 56 | @POST("users/authenticate") 57 | Single login(@Field(value = "email") String email, 58 | @Field(value = "password") String password); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/services/MyAppService.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.services; 2 | 3 | import android.app.Service; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.IBinder; 7 | import android.util.Log; 8 | 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import javax.inject.Inject; 12 | 13 | import co.lateralview.myapp.application.MyApp; 14 | import co.lateralview.myapp.domain.repository.interfaces.SessionRepository; 15 | import co.lateralview.myapp.ui.activities.base.DaggerBaseComponent; 16 | import co.lateralview.myapp.ui.util.RxSchedulersUtils; 17 | import co.lateralview.myapp.ui.util.SystemUtils; 18 | import io.reactivex.Observable; 19 | 20 | public class MyAppService extends Service { 21 | public static final String TAG = "MyAppService"; 22 | 23 | @Inject 24 | SessionRepository mSessionRepository; 25 | 26 | public static void startService(Context context) { 27 | if (!SystemUtils.isServiceRunning(context, MyAppService.class)) { 28 | context.startService(new Intent(context, MyAppService.class)); 29 | } 30 | } 31 | 32 | @Override 33 | public void onCreate() { 34 | Log.i(TAG, "Initiate"); 35 | injectDependencies(this); 36 | 37 | Observable.interval( 38 | 2, TimeUnit.MINUTES) 39 | .compose(RxSchedulersUtils.applyObservableSchedulers()) 40 | .subscribe(__ -> { /*do something*/ }, 41 | error -> Log.e(TAG, "Error executing MyAppService", error)); 42 | } 43 | 44 | @Override 45 | public void onDestroy() { 46 | Log.i(TAG, "onDestroy"); 47 | super.onDestroy(); 48 | } 49 | 50 | @Override 51 | public int onStartCommand(Intent intent, int flags, int startId) { 52 | super.onStartCommand(intent, flags, startId); 53 | return START_STICKY; // To keep alive this Service until app is closed or killed 54 | } 55 | 56 | @Override 57 | public IBinder onBind(Intent intent) { 58 | return null; 59 | } 60 | 61 | private void injectDependencies(Context context) { 62 | DaggerBaseComponent.builder() 63 | .appComponent(MyApp.getAppComponent()) 64 | .build() 65 | .inject(this); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/SystemManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager; 2 | 3 | import android.content.Context; 4 | import android.content.pm.PackageInfo; 5 | import android.content.pm.PackageManager; 6 | import android.content.pm.Signature; 7 | import android.util.Base64; 8 | import android.util.DisplayMetrics; 9 | import android.util.Log; 10 | 11 | import java.security.MessageDigest; 12 | import java.security.NoSuchAlgorithmException; 13 | 14 | public class SystemManager { 15 | private Context mContext; 16 | 17 | public SystemManager(Context context) { 18 | mContext = context; 19 | } 20 | 21 | /** 22 | * Shown in console the KeyHash application. 23 | */ 24 | public void showKeyHash() { 25 | try { 26 | PackageInfo info = mContext.getPackageManager().getPackageInfo( 27 | mContext.getPackageName(), 28 | PackageManager.GET_SIGNATURES); 29 | 30 | for (Signature signature : info.signatures) { 31 | MessageDigest md = MessageDigest.getInstance("SHA"); 32 | md.update(signature.toByteArray()); 33 | Log.d("KeyHash:", Base64.encodeToString(md.digest(), Base64.DEFAULT)); 34 | } 35 | } catch (PackageManager.NameNotFoundException e) { 36 | 37 | } catch (NoSuchAlgorithmException e) { 38 | 39 | } 40 | } 41 | 42 | public void showDeviceInfo() { 43 | switch (mContext.getResources().getDisplayMetrics().densityDpi) { 44 | case DisplayMetrics.DENSITY_LOW: 45 | Log.i("DeviceScreenDensity", "ldpi"); 46 | break; 47 | case DisplayMetrics.DENSITY_MEDIUM: 48 | Log.i("DeviceScreenDensity", "mdpi"); 49 | break; 50 | case DisplayMetrics.DENSITY_HIGH: 51 | Log.i("DeviceScreenDensity", "hdpi"); 52 | break; 53 | case DisplayMetrics.DENSITY_XHIGH: 54 | Log.i("DeviceScreenDensity", "xhdpi"); 55 | break; 56 | case DisplayMetrics.DENSITY_XXHIGH: 57 | Log.i("DeviceScreenDensity", "xxhdpi"); 58 | break; 59 | case DisplayMetrics.DENSITY_XXXHIGH: 60 | Log.i("DeviceScreenDensity", "xxxhdpi"); 61 | break; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/ToolbarUtils.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | import android.app.Activity; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.support.v7.widget.ActionMenuView; 7 | import android.support.v7.widget.Toolbar; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ImageView; 11 | 12 | import co.lateralview.myapp.R; 13 | 14 | public final class ToolbarUtils { 15 | 16 | private ToolbarUtils() { 17 | } 18 | 19 | public static void initializeToolbar(AppCompatActivity activity, boolean backEnabled, 20 | @Nullable String title) { 21 | Toolbar toolbar = activity.findViewById(R.id.toolbar); 22 | 23 | if (toolbar != null) { 24 | activity.setSupportActionBar(toolbar); 25 | 26 | setToolbarTitle(activity, title != null && !title.isEmpty() ? title : ""); 27 | 28 | activity.getSupportActionBar().setDisplayHomeAsUpEnabled(backEnabled); 29 | activity.getSupportActionBar().setHomeButtonEnabled(backEnabled); 30 | } 31 | } 32 | 33 | public static void setActionBarVisibility(Activity activity, int visibility) { 34 | Toolbar toolbar = activity.findViewById(R.id.toolbar); 35 | 36 | if (toolbar != null) { 37 | toolbar.setVisibility(visibility); 38 | } 39 | } 40 | 41 | public static void setToolbarTitle(AppCompatActivity activity, String title) { 42 | if (activity.getSupportActionBar() != null) { 43 | activity.getSupportActionBar().setTitle(title); 44 | } 45 | } 46 | 47 | public static ImageView findOverflowMenuButton(Activity activity, ViewGroup viewGroup) { 48 | if (viewGroup == null) { 49 | return null; 50 | } 51 | ImageView overflow = null; 52 | for (int i = 0, count = viewGroup.getChildCount(); i < count; i++) { 53 | View v = viewGroup.getChildAt(i); 54 | if (v instanceof ImageView && (v.getClass().getSimpleName().equals("OverflowMenuButton") 55 | || 56 | v instanceof ActionMenuView.ActionMenuChildView)) { 57 | overflow = (ImageView) v; 58 | } else if (v instanceof ViewGroup) { 59 | overflow = findOverflowMenuButton(activity, (ViewGroup) v); 60 | } 61 | if (overflow != null) { 62 | break; 63 | } 64 | } 65 | return overflow; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/domain/util/SnackBarData.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.domain.util; 2 | 3 | import android.support.design.widget.Snackbar; 4 | 5 | import java.io.Serializable; 6 | 7 | import co.lateralview.myapp.R; 8 | import co.lateralview.myapp.ui.util.SnackBarHelper; 9 | 10 | public class SnackBarData implements Serializable { 11 | private SnackBarType mSnackBarType; 12 | private SnackBarHelper.ISnackBarHandler mSnackBarListener; 13 | 14 | public enum SnackBarType implements Serializable { 15 | DUMMY(-1, -1, -1, Snackbar.LENGTH_INDEFINITE), 16 | CONNECTION_ERROR(R.string.snackBarConnectionError_description, 17 | R.string.snackBarConnectionError_action, R.color.red, Snackbar.LENGTH_INDEFINITE), 18 | NO_INTERNET(R.string.snackBarNoInternet_description, -1, R.color.red, 19 | Snackbar.LENGTH_INDEFINITE); 20 | 21 | private int mDescription; 22 | private int mActionTitle; 23 | private int mBackgroundColor; 24 | private int mDuration; 25 | 26 | SnackBarType(int description, int actionTitle, int backgroundColor, int duration) { 27 | mDescription = description; 28 | mActionTitle = actionTitle; 29 | mBackgroundColor = backgroundColor; 30 | mDuration = duration; 31 | } 32 | 33 | public int getDescription() { 34 | return mDescription; 35 | } 36 | 37 | public int getActionTitle() { 38 | return mActionTitle; 39 | } 40 | 41 | public int getBackgroundColor() { 42 | return mBackgroundColor; 43 | } 44 | 45 | public int getDuration() { 46 | return mDuration; 47 | } 48 | } 49 | 50 | public SnackBarData(SnackBarType mSnackBarType) { 51 | this(mSnackBarType, null); 52 | } 53 | 54 | public SnackBarData(SnackBarType mSnackBarType, 55 | SnackBarHelper.ISnackBarHandler mSnackBarListener) { 56 | this.mSnackBarType = mSnackBarType; 57 | this.mSnackBarListener = mSnackBarListener; 58 | } 59 | 60 | public SnackBarType getSnackBarType() { 61 | return mSnackBarType; 62 | } 63 | 64 | public void setSnackBarType(SnackBarType mSnackBarType) { 65 | this.mSnackBarType = mSnackBarType; 66 | } 67 | 68 | public SnackBarHelper.ISnackBarHandler getSnackBarListener() { 69 | return mSnackBarListener; 70 | } 71 | 72 | public void setSnackBarListener(SnackBarHelper.ISnackBarHandler mSnackBarListener) { 73 | this.mSnackBarListener = mSnackBarListener; 74 | } 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /app/src/test/java/co/lateralview/myapp/infraestructure/manager/InternetManagerTest.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager; 2 | 3 | import android.content.Context; 4 | import android.net.ConnectivityManager; 5 | import android.net.NetworkInfo.State; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.robolectric.RobolectricTestRunner; 11 | import org.robolectric.RuntimeEnvironment; 12 | import org.robolectric.shadows.ShadowConnectivityManager; 13 | import org.robolectric.shadows.ShadowNetworkInfo; 14 | 15 | import static org.junit.Assert.assertFalse; 16 | import static org.junit.Assert.assertTrue; 17 | import static org.robolectric.Shadows.shadowOf; 18 | 19 | @RunWith(RobolectricTestRunner.class) 20 | public class InternetManagerTest 21 | { 22 | private ShadowConnectivityManager mShadowConnectivityManager; 23 | private ShadowNetworkInfo mShadowNetworkInfo; 24 | 25 | private InternetManager mInternetManager; 26 | 27 | @Before 28 | public void setUpTest() { 29 | final Context context = RuntimeEnvironment.application.getApplicationContext(); 30 | mInternetManager = new InternetManager(context); 31 | 32 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 33 | mShadowConnectivityManager = shadowOf(connectivityManager); 34 | mShadowNetworkInfo = shadowOf(mShadowConnectivityManager.getActiveNetworkInfo()); 35 | } 36 | 37 | @Test 38 | public void itShouldReturnIsOnlineIfConnectedToNetwork() { 39 | mShadowNetworkInfo.setConnectionStatus(State.CONNECTED); 40 | assertTrue(mInternetManager.isOnline()); 41 | } 42 | 43 | @Test 44 | public void itShouldReturnIsOnlineIfIsConnectingToNetwork() { 45 | mShadowNetworkInfo.setConnectionStatus(State.CONNECTING); 46 | assertTrue(mInternetManager.isOnline()); 47 | } 48 | 49 | @Test 50 | public void itShouldReturnIsNotOnline() { 51 | mShadowNetworkInfo.setConnectionStatus(State.DISCONNECTED); 52 | assertFalse(mInternetManager.isOnline()); 53 | } 54 | 55 | @Test 56 | public void itShouldReturnOnWifi() { 57 | mShadowConnectivityManager.setActiveNetworkInfo(mShadowConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)); 58 | assertTrue(mInternetManager.onWifi()); 59 | } 60 | 61 | @Test 62 | public void itShouldReturnNotOnWifi() { 63 | mShadowConnectivityManager.setActiveNetworkInfo(mShadowConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)); 64 | assertFalse(mInternetManager.onWifi()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.base; 2 | 3 | 4 | import android.app.Application; 5 | import android.util.Log; 6 | 7 | import javax.inject.Inject; 8 | 9 | import co.lateralview.myapp.domain.repository.interfaces.SessionRepository; 10 | import co.lateralview.myapp.infraestructure.manager.InternetManager; 11 | import co.lateralview.myapp.infraestructure.manager.interfaces.TaskManager; 12 | import co.lateralview.myapp.infraestructure.networking.MyAppServerError; 13 | import co.lateralview.myapp.infraestructure.networking.RestConstants.Subcode; 14 | import co.lateralview.myapp.infraestructure.networking.ServerException; 15 | import co.lateralview.myapp.ui.util.di.ActivityScoped; 16 | 17 | @ActivityScoped 18 | public class BasePresenter implements Base.Presenter { 19 | private static final String TAG = "BasePresenter"; 20 | 21 | @Inject 22 | protected SessionRepository mSessionRepository; 23 | @Inject 24 | InternetManager mInternetManager; 25 | @Inject 26 | Application mApplication; 27 | @Inject 28 | Base.View mBaseView; 29 | @Inject 30 | TaskManager mTaskManager; //Help us to retry failed tasks 31 | 32 | public void cancelPendingTasks(String tag) { 33 | mTaskManager.removeTasks(tag); 34 | } 35 | 36 | protected boolean handlerError(Throwable throwable) { 37 | try { 38 | if (!mInternetManager.isOnline()) { 39 | noInternetError(); 40 | return true; 41 | } 42 | 43 | if (throwable instanceof ServerException) { 44 | ServerException serverException = (ServerException) throwable; 45 | 46 | if (serverException.getKind() == ServerException.Kind.HTTP) { 47 | MyAppServerError serverError = serverException.getServerError(); 48 | Integer errorCode = serverError.getErrorCode(); 49 | 50 | if (Subcode.INVALID_TOKEN.equals(Subcode.fromInt(errorCode))) { 51 | mBaseView.logout(); 52 | return true; 53 | } 54 | } else { 55 | unexpectedErrorHappened(); 56 | return true; 57 | } 58 | } 59 | } catch (Exception e) { 60 | unexpectedErrorHappened(); 61 | return true; 62 | } 63 | 64 | return false; 65 | } 66 | 67 | protected void noInternetError() { 68 | mBaseView.showInternetRequiredError(); 69 | } 70 | 71 | protected void unexpectedErrorHappened() { 72 | Log.e(TAG, "Unexpected Error Happened"); 73 | mBaseView.showUnexpectedErrorHappened(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | check-build: 4 | docker: 5 | - image: circleci/android:api-27-alpha #SDK version 6 | working_directory: ~/code 7 | steps: 8 | - checkout 9 | - run: echo "Starting..." 10 | - restore_cache: 11 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} 12 | - run: 13 | name: Download Dependencies 14 | command: ./gradlew androidDependencies 15 | - save_cache: 16 | paths: 17 | - ~/.gradle 18 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} 19 | - run: 20 | name: Run Tests 21 | command: ./gradlew lint test 22 | - store_artifacts: 23 | path: app/build/reports 24 | destination: reports 25 | - store_test_results: 26 | path: app/build/test-results 27 | 28 | dev-build: 29 | docker: 30 | - image: circleci/android:api-27-alpha #SDK version 31 | working_directory: ~/code 32 | steps: 33 | - checkout 34 | - run: echo "Starting..." 35 | - restore_cache: 36 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} 37 | - run: 38 | name: Download Dependencies 39 | command: ./gradlew androidDependencies 40 | - save_cache: 41 | paths: 42 | - ~/.gradle 43 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} 44 | - run: 45 | name: Run Tests 46 | command: ./gradlew lint test 47 | - store_artifacts: 48 | path: app/build/reports 49 | destination: reports 50 | - store_test_results: 51 | path: app/build/test-results 52 | - run: 53 | name: Initial build 54 | command: ./gradlew clean assembleDebug --no-daemon --stacktrace 55 | - store_artifacts: 56 | path: app/build/outputs/apk/ 57 | destination: apks/ 58 | - run: 59 | name: Upload to Slack 60 | command: | 61 | export GIT_COMMIT_DESC=$(git log --format=oneline -n 1 | sed -E 's/^[^ ]+ (.*)$/\1/g') 62 | curl -F file=@app/build/outputs/apk/debug/app-debug.apk -F channels=$SLACK_CHANNEL -F token=$SLACK_API_TOKEN -F title="${CIRCLE_PROJECT_REPONAME} | branch -> ${CIRCLE_BRANCH} | commit -> ${GIT_COMMIT_DESC}" https://slack.com/api/files.upload 63 | 64 | 65 | workflows: 66 | version: 2 67 | deploy: 68 | jobs: 69 | - check-build: 70 | filters: 71 | branches: 72 | ignore: 73 | - develop+MVP+RX 74 | - /^dev-build-.*/ 75 | - dev-build: 76 | filters: 77 | branches: 78 | only: 79 | - develop+MVP+RX 80 | - /^dev-build-.*/ 81 | -------------------------------------------------------------------------------- /app/src/test/java/co/lateralview/myapp/ui/activities/main/MainPresenterTest.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.main; 2 | 3 | import org.junit.ClassRule; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.InjectMocks; 7 | import org.mockito.Mock; 8 | import org.mockito.junit.MockitoJUnitRunner; 9 | 10 | import co.lateralview.myapp.TrampolineSchedulerRule; 11 | import co.lateralview.myapp.domain.model.User; 12 | import co.lateralview.myapp.domain.repository.interfaces.SessionRepository; 13 | import co.lateralview.myapp.domain.repository.interfaces.UserRepository; 14 | import io.reactivex.Completable; 15 | import io.reactivex.Single; 16 | 17 | import static org.mockito.ArgumentMatchers.eq; 18 | import static org.mockito.ArgumentMatchers.isA; 19 | import static org.mockito.Mockito.never; 20 | import static org.mockito.Mockito.verify; 21 | import static org.mockito.Mockito.verifyZeroInteractions; 22 | import static org.mockito.Mockito.when; 23 | 24 | @RunWith(MockitoJUnitRunner.class) 25 | public class MainPresenterTest { 26 | 27 | @ClassRule public static TrampolineSchedulerRule mTrampolineSchedulerRule = new TrampolineSchedulerRule(); 28 | 29 | @Mock Main.View mView; 30 | @Mock UserRepository mUserRepository; 31 | @Mock SessionRepository mSessionRepository; 32 | @InjectMocks MainPresenter mMainPresenter; 33 | 34 | private static final String EMAIL = "email"; 35 | private static final String PASSWORD = "password"; 36 | private static final String TOKEN = "token"; 37 | private User user = new User(); 38 | 39 | @Test 40 | public void itShouldLoginSuccessfully() { 41 | when(mUserRepository.login(EMAIL, PASSWORD)).thenReturn(Single.just(user)); 42 | when(mSessionRepository.logIn(user, "token")).thenReturn(Completable.complete()); 43 | 44 | mMainPresenter.login(EMAIL, PASSWORD); 45 | verify(mUserRepository).login(EMAIL, PASSWORD); 46 | verify(mSessionRepository).logIn(user, TOKEN); 47 | verifyZeroInteractions(mView); 48 | } 49 | 50 | @Test 51 | public void itShouldShowErrorIfLoginFails() { 52 | when(mUserRepository.login(EMAIL, PASSWORD)).thenReturn(Single.error(new Exception())); 53 | 54 | mMainPresenter.login(EMAIL, PASSWORD); 55 | verify(mUserRepository).login(EMAIL, PASSWORD); 56 | verify(mSessionRepository, never()).logIn(isA(User.class), eq(TOKEN)); 57 | verify(mView).showError(); 58 | } 59 | 60 | @Test 61 | public void itShouldShowErrorIfSessionFails() { 62 | when(mUserRepository.login(EMAIL, PASSWORD)).thenReturn(Single.just(user)); 63 | when(mSessionRepository.logIn(user, "token")).thenReturn(Completable.error(new Exception())); 64 | 65 | mMainPresenter.login(EMAIL, PASSWORD); 66 | verify(mUserRepository).login(EMAIL, PASSWORD); 67 | verify(mSessionRepository).logIn(isA(User.class), eq(TOKEN)); 68 | verify(mView).showError(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/pushNotification/processor/base/PushNotificationProcessor.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.pushNotification.processor.base; 2 | 3 | import android.app.NotificationManager; 4 | import android.app.PendingIntent; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.media.RingtoneManager; 8 | import android.support.v4.app.NotificationCompat; 9 | 10 | import java.io.Serializable; 11 | 12 | import co.lateralview.myapp.R; 13 | import co.lateralview.myapp.infraestructure.pushNotification.processor.ConcretePushNotification; 14 | 15 | public abstract class PushNotificationProcessor implements Serializable { 16 | protected NotificationType mNotificationType; 17 | protected String mTitle; 18 | protected String mDescription; 19 | private boolean mAppRunning; //Some Activity on the stack 20 | 21 | public PushNotificationProcessor(NotificationType notificationType, String title, 22 | String description, boolean appRunning) { 23 | mNotificationType = notificationType; 24 | mTitle = title; 25 | mDescription = description; 26 | mAppRunning = appRunning; 27 | } 28 | 29 | public static PushNotificationProcessor create(NotificationType notificationType, String title, 30 | String message, boolean appRunning) { 31 | switch (notificationType) { 32 | default: 33 | return new ConcretePushNotification(notificationType, title, message, appRunning); 34 | } 35 | } 36 | 37 | protected void sendNotification(Context context, Intent intent) { 38 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 39 | PendingIntent pendingIntent = PendingIntent.getActivity(context, 0 /* Request code */, 40 | intent, PendingIntent.FLAG_ONE_SHOT); 41 | 42 | NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context) 43 | .setSmallIcon(R.mipmap.ic_launcher) 44 | .setContentTitle(mTitle != null && !mTitle.isEmpty() ? mTitle 45 | : context.getString(R.string.app_name)) 46 | .setContentText(mDescription) 47 | .setAutoCancel(true) 48 | .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) 49 | .setContentIntent(pendingIntent); 50 | 51 | NotificationManager notificationManager = (NotificationManager) context.getSystemService( 52 | Context.NOTIFICATION_SERVICE); 53 | 54 | notificationManager.notify(mNotificationType.ordinal(), notificationBuilder.build()); 55 | } 56 | 57 | public abstract void process(Context context); 58 | 59 | public NotificationType getNotificationType() { 60 | return mNotificationType; 61 | } 62 | 63 | public boolean isAppInForeground() { 64 | return mAppRunning; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/application/AppModule.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.application; 2 | 3 | import android.app.Application; 4 | 5 | import com.google.gson.Gson; 6 | import com.google.gson.GsonBuilder; 7 | 8 | import javax.inject.Singleton; 9 | 10 | import co.lateralview.myapp.infraestructure.manager.InternetManager; 11 | import co.lateralview.myapp.infraestructure.manager.MailManager; 12 | import co.lateralview.myapp.infraestructure.manager.SystemManager; 13 | import co.lateralview.myapp.infraestructure.manager.implementation.ImageManagerImpl; 14 | import co.lateralview.myapp.infraestructure.manager.implementation.ParserManagerImpl; 15 | import co.lateralview.myapp.infraestructure.manager.implementation.SharedPreferencesManagerImpl; 16 | import co.lateralview.myapp.infraestructure.manager.implementation.TaskManagerImpl; 17 | import co.lateralview.myapp.infraestructure.manager.interfaces.ImageManager; 18 | import co.lateralview.myapp.infraestructure.manager.interfaces.ParserManager; 19 | import co.lateralview.myapp.infraestructure.manager.interfaces.SharedPreferencesManager; 20 | import co.lateralview.myapp.infraestructure.manager.interfaces.TaskManager; 21 | import co.lateralview.myapp.infraestructure.networking.gson.AnnotationExclusionStrategy; 22 | import co.lateralview.myapp.ui.util.DateUtils; 23 | import dagger.Module; 24 | import dagger.Provides; 25 | 26 | @Module 27 | public class AppModule { 28 | protected Application mApplication; 29 | 30 | public AppModule(Application application) { 31 | mApplication = application; 32 | } 33 | 34 | @Provides 35 | @Singleton 36 | public Application providesApplication() { 37 | return mApplication; 38 | } 39 | 40 | @Provides 41 | public ImageManager providesImageManager(Application application) { 42 | return new ImageManagerImpl(application); 43 | } 44 | 45 | @Provides 46 | public Gson providesGson() { 47 | //TODO Check ISO FORMAT 48 | return new GsonBuilder() 49 | .setDateFormat(DateUtils.ISO_8601_PATTERN) 50 | .setExclusionStrategies(new AnnotationExclusionStrategy()) 51 | .create(); 52 | } 53 | 54 | @Provides 55 | public ParserManager providesParserManager(Gson gson) { 56 | return new ParserManagerImpl(gson); 57 | } 58 | 59 | @Provides 60 | public SharedPreferencesManager providesSharedPreferencesManager(Application application, 61 | ParserManager parserManager) { 62 | return new SharedPreferencesManagerImpl(application, parserManager); 63 | } 64 | 65 | @Provides 66 | public SystemManager providesSystemManager(Application application) { 67 | return new SystemManager(application); 68 | } 69 | 70 | @Provides 71 | public InternetManager providesInternetManager(Application application) { 72 | return new InternetManager(application); 73 | } 74 | 75 | @Provides 76 | @Singleton 77 | public TaskManager providesTaskManager() { 78 | return new TaskManagerImpl(); 79 | } 80 | 81 | @Provides 82 | public MailManager providesMailManager() { 83 | return new MailManager(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/SystemUtils.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | import android.app.Activity; 4 | import android.app.ActivityManager; 5 | import android.content.Context; 6 | import android.content.pm.ActivityInfo; 7 | import android.content.pm.PackageInfo; 8 | import android.content.pm.PackageManager; 9 | import android.os.Looper; 10 | import android.os.Vibrator; 11 | import android.view.View; 12 | import android.view.WindowManager; 13 | 14 | public final class SystemUtils { 15 | 16 | private SystemUtils() { 17 | } 18 | 19 | public static void setDeviceOrientation(Activity activity) { 20 | activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 21 | } 22 | 23 | public static void vibrate(Activity activity, int millis) { 24 | ((Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE)).vibrate(millis); 25 | } 26 | 27 | public static void keepScreenOn(Activity activity) { 28 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 29 | } 30 | 31 | public static String getVersionName(Context context) { 32 | final PackageInfo packageInfo; 33 | try { 34 | packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); 35 | return packageInfo.versionName; 36 | } catch (PackageManager.NameNotFoundException e) { 37 | return null; 38 | } 39 | } 40 | 41 | public static int getVersionCode(Context context) { 42 | final PackageInfo packageInfo; 43 | try { 44 | packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); 45 | return packageInfo.versionCode; 46 | } catch (PackageManager.NameNotFoundException e) { 47 | return -1; 48 | } 49 | } 50 | 51 | public static void fullScreenMode(Activity activity) { 52 | // Hide both the navigation bar and the status bar. 53 | // SYSTEM_UI_FLAG_FULLSCREEN is only available on Android 4.1 and higher, but as 54 | // a general rule, you should design your app to hide the status bar whenever you 55 | // hide the navigation bar. 56 | int uiOptions = View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_IMMERSIVE 57 | | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; 58 | 59 | activity.getWindow().getDecorView().setSystemUiVisibility(uiOptions); 60 | } 61 | 62 | public static boolean isRunningOnMainThread() { 63 | return Looper.getMainLooper().getThread() == Thread.currentThread(); 64 | } 65 | 66 | public static boolean isServiceRunning(Context context, Class serviceClass) { 67 | ActivityManager manager = (ActivityManager) context.getSystemService( 68 | Context.ACTIVITY_SERVICE); 69 | for (ActivityManager.RunningServiceInfo service : manager.getRunningServices( 70 | Integer.MAX_VALUE)) { 71 | if (serviceClass.getName().equals(service.service.getClassName())) { 72 | return true; 73 | } 74 | } 75 | 76 | return false; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/SnackBarHelper.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | 4 | import android.app.Activity; 5 | import android.graphics.Color; 6 | import android.support.design.widget.Snackbar; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | 10 | import co.lateralview.myapp.domain.util.SnackBarData; 11 | 12 | public final class SnackBarHelper { 13 | 14 | private SnackBarHelper() { 15 | } 16 | 17 | public static Snackbar createSnackBar(Activity activity, SnackBarData snackBar) { 18 | return createSnackBar(getActivityViewContainer(activity), snackBar); 19 | } 20 | 21 | public static Snackbar createSnackBar(View view, SnackBarData snackBar) { 22 | SnackBarData.SnackBarType snackBarType = snackBar.getSnackBarType(); 23 | 24 | String description = null; 25 | String action = null; 26 | 27 | if (snackBarType != null) { 28 | if (snackBarType.getDescription() != -1) { 29 | description = view.getContext().getString(snackBarType.getDescription()); 30 | } 31 | 32 | if (snackBarType.getActionTitle() != -1) { 33 | action = view.getContext().getString(snackBarType.getActionTitle()); 34 | } 35 | } 36 | 37 | return createSnackBar(view, description, action, snackBarType.getDuration(), 38 | snackBarType.getBackgroundColor(), snackBar.getSnackBarListener()); 39 | } 40 | 41 | public static Snackbar createSnackBar(Activity activity, String description, String action, 42 | int duration, int backgroundColor, final ISnackBarHandler snackBarHandler) { 43 | return createSnackBar(getActivityViewContainer(activity), description, action, duration, 44 | backgroundColor, snackBarHandler); 45 | } 46 | 47 | public static Snackbar createSnackBar(View view, String description, String action, 48 | int duration, int backgroundColor, final ISnackBarHandler snackBarHandler) { 49 | final Snackbar snackbar = Snackbar.make(view, description, duration); 50 | 51 | if (action != null && snackBarHandler != null) { 52 | snackbar.setAction(action, view1 -> { 53 | snackBarHandler.onActionClick(); 54 | snackbar.dismiss(); 55 | }); 56 | } 57 | 58 | snackbar.setActionTextColor(Color.WHITE); 59 | 60 | View snackBarView = snackbar.getView(); 61 | 62 | if (backgroundColor != -1) { 63 | snackBarView.setBackgroundColor( 64 | UIUtils.getColorFromRes(view.getContext(), backgroundColor)); 65 | } 66 | 67 | TextView textView = (TextView) snackBarView.findViewById( 68 | android.support.design.R.id.snackbar_text); 69 | 70 | textView.setOnClickListener(v -> snackbar.dismiss()); 71 | 72 | textView.setTextColor(Color.WHITE); 73 | 74 | return snackbar; 75 | } 76 | 77 | private static View getActivityViewContainer(Activity activity) { 78 | return activity.getWindow().getDecorView().findViewById(android.R.id.content); 79 | } 80 | 81 | public interface ISnackBarHandler { 82 | void onActionClick(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 30 | 31 | 36 | 37 | 43 | 44 | 48 | 49 | 54 | 55 | 59 | 60 | 65 | 66 | 70 | 71 | 75 | 76 | 80 | 81 | 85 | 86 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/webview/WebViewActivity.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.webview; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.webkit.WebView; 8 | import android.webkit.WebViewClient; 9 | import android.widget.ProgressBar; 10 | 11 | import butterknife.BindView; 12 | import butterknife.ButterKnife; 13 | import co.lateralview.myapp.R; 14 | import co.lateralview.myapp.ui.activities.base.BaseActivity; 15 | 16 | public class WebViewActivity extends BaseActivity { 17 | public static final String TAG = "WebViewActivity"; 18 | 19 | private static final String EXTRA_URL = "EXTRA_URL"; 20 | private static final String EXTRA_ACTION_BAR_TITLE = "EXTRA_ACTION_BAR_TITLE"; 21 | private static final String EXTRA_BACK_ENABLED = "EXTRA_BACK_ENABLED"; 22 | 23 | @BindView(R.id.webViewActivity_webview) 24 | protected WebView mWebView; 25 | @BindView(R.id.webViewActivity_progressBar) 26 | protected ProgressBar mProgressBar; 27 | private String mUrl; 28 | private String mActionBarTitle; 29 | private boolean mBackEnabled = false; 30 | 31 | public static Intent newInstance(Context fromActivity, boolean clearStack, String url) { 32 | Intent intent = BaseActivity.newActivityInstance(fromActivity, clearStack, 33 | WebViewActivity.class); 34 | intent.putExtra(EXTRA_URL, url); 35 | 36 | return intent; 37 | } 38 | 39 | public static Intent newInstance(Context fromActivity, boolean clearStack, String url, 40 | String actionBarTitle, boolean backEnabled) { 41 | Intent intent = newInstance(fromActivity, clearStack, url); 42 | intent.putExtra(EXTRA_ACTION_BAR_TITLE, actionBarTitle); 43 | intent.putExtra(EXTRA_BACK_ENABLED, backEnabled); 44 | 45 | return intent; 46 | } 47 | 48 | @Override 49 | protected void onCreate(Bundle savedInstanceState) { 50 | super.onCreate(savedInstanceState); 51 | 52 | setContentView(R.layout.activity_web_view); 53 | 54 | ButterKnife.bind(this); 55 | 56 | getExtras(); 57 | 58 | if (mActionBarTitle != null && !mActionBarTitle.isEmpty()) { 59 | initializeToolbar(mBackEnabled, mActionBarTitle); 60 | } else { 61 | setActionBarVisibility(View.GONE); 62 | } 63 | 64 | initializeWebView(); 65 | 66 | startWebView(); 67 | } 68 | 69 | @Override 70 | public String getTAG() { 71 | return TAG; 72 | } 73 | 74 | private void getExtras() { 75 | Bundle extras = getIntent().getExtras(); 76 | if (extras != null) { 77 | mUrl = extras.getString(EXTRA_URL); 78 | mActionBarTitle = extras.getString(EXTRA_ACTION_BAR_TITLE); 79 | mBackEnabled = extras.getBoolean(EXTRA_BACK_ENABLED, false); 80 | } 81 | } 82 | 83 | private void initializeWebView() { 84 | mWebView.setWebViewClient(new WebViewClient()); 85 | mWebView.getSettings().setJavaScriptEnabled(true); 86 | mWebView.getSettings().setDomStorageEnabled(true); 87 | 88 | mWebView.setWebViewClient(new WebViewClient() { 89 | public void onPageFinished(WebView view, String url) { 90 | mProgressBar.setVisibility(View.GONE); 91 | } 92 | }); 93 | } 94 | 95 | private void startWebView() { 96 | if (mUrl != null && !mUrl.isEmpty()) { 97 | mWebView.loadUrl(mUrl); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/domain/repository/implementation/SessionRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.domain.repository.implementation; 2 | 3 | import javax.inject.Inject; 4 | 5 | import co.lateralview.myapp.domain.model.User; 6 | import co.lateralview.myapp.domain.repository.interfaces.SessionRepository; 7 | import co.lateralview.myapp.infraestructure.manager.interfaces.SharedPreferencesManager; 8 | import co.lateralview.myapp.ui.util.RxSchedulersUtils; 9 | import io.reactivex.Completable; 10 | import io.reactivex.Single; 11 | 12 | 13 | public class SessionRepositoryImpl implements SessionRepository { 14 | 15 | public static final String SHARED_PREFERENCES_ACCESS_TOKEN_KEY = 16 | "SHARED_PREFERENCES_ACCESS_TOKEN_KEY"; 17 | 18 | @Inject 19 | SharedPreferencesManager mSharedPreferencesManager; 20 | 21 | private User mCurrentUser; 22 | private String mAccessToken; 23 | 24 | @Inject 25 | public SessionRepositoryImpl() { 26 | } 27 | 28 | @Override 29 | public Single isUserLoggedIn() { 30 | return Single.create(e -> { 31 | if (mCurrentUser == null) { 32 | mCurrentUser = mSharedPreferencesManager.get(User.SHARED_PREFERENCE_KEY, 33 | User.class); 34 | } 35 | 36 | e.onSuccess(mCurrentUser != null); 37 | }).compose(RxSchedulersUtils.applySingleSchedulers()); 38 | } 39 | 40 | @Override 41 | public Completable logOut() { 42 | return Completable.create(e -> { 43 | mCurrentUser = null; 44 | mAccessToken = null; 45 | mSharedPreferencesManager.clearBlocking(); 46 | e.onComplete(); 47 | }).compose(RxSchedulersUtils.applyCompletableSchedulers()); 48 | } 49 | 50 | @Override 51 | public Completable logIn(User user, String accessToken) { 52 | return save(user).toCompletable() 53 | .andThen(setAccessToken(accessToken).toCompletable()) 54 | .compose(RxSchedulersUtils.applyCompletableSchedulers()); 55 | } 56 | 57 | @Override 58 | public Single getCurrentUser() { 59 | return Single.create(e -> { 60 | if (mCurrentUser == null) { 61 | mCurrentUser = mSharedPreferencesManager.get(User.SHARED_PREFERENCE_KEY, 62 | User.class); 63 | } 64 | 65 | e.onSuccess(mCurrentUser); 66 | }).compose(RxSchedulersUtils.applySingleSchedulers()); 67 | } 68 | 69 | @Override 70 | public Single updateUser(User user) { 71 | return save(user).compose(RxSchedulersUtils.applySingleSchedulers()); 72 | } 73 | 74 | @Override 75 | public Single getAccessToken() { 76 | return Single.create(e -> { 77 | if (mAccessToken == null) { 78 | mAccessToken = mSharedPreferencesManager.get(SHARED_PREFERENCES_ACCESS_TOKEN_KEY, 79 | String.class); 80 | } 81 | 82 | e.onSuccess(mAccessToken); 83 | }).compose(RxSchedulersUtils.applySingleSchedulers()); 84 | } 85 | 86 | private Single setAccessToken(String accessToken) { 87 | return Single.create(e -> { 88 | mSharedPreferencesManager.saveBlocking(SHARED_PREFERENCES_ACCESS_TOKEN_KEY, 89 | accessToken); 90 | mAccessToken = accessToken; 91 | 92 | e.onSuccess(mAccessToken); 93 | }); 94 | } 95 | 96 | private Single save(User newUser) { 97 | return Single.create(e -> { 98 | mSharedPreferencesManager.saveBlocking(User.SHARED_PREFERENCE_KEY, newUser); 99 | 100 | mCurrentUser = newUser; 101 | 102 | e.onSuccess(mCurrentUser); 103 | }); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/ServerException.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking; 2 | 3 | import java.io.IOException; 4 | import java.lang.annotation.Annotation; 5 | 6 | import okhttp3.ResponseBody; 7 | import retrofit2.Converter; 8 | import retrofit2.Response; 9 | import retrofit2.Retrofit; 10 | 11 | public class ServerException extends RuntimeException { 12 | private final String url; 13 | private final Response response; 14 | private final Kind kind; 15 | private final Retrofit retrofit; 16 | private MyAppServerError mServerError = null; 17 | 18 | /** 19 | * Identifies the event kind which triggered a {@link ServerException}. 20 | */ 21 | public enum Kind { 22 | /** 23 | * An {@link IOException} occurred while communicating to the server. 24 | */ 25 | NETWORK, 26 | /** 27 | * A non-200 HTTP status code was received from the server. 28 | */ 29 | HTTP, 30 | /** 31 | * An internal error occurred while attempting to execute a request. It is best practice to 32 | * re-throw this exception so your application crashes. 33 | */ 34 | UNEXPECTED 35 | } 36 | 37 | ServerException(String message, String url, Response response, Kind kind, Throwable exception, 38 | Retrofit retrofit) { 39 | super(message, exception); 40 | this.url = url; 41 | this.response = response; 42 | this.kind = kind; 43 | this.retrofit = retrofit; 44 | 45 | if (response != null) { 46 | try { 47 | mServerError = getErrorBodyAs(MyAppServerError.class); 48 | } catch (IOException ignored) { 49 | } 50 | } 51 | } 52 | 53 | public static ServerException httpError(String url, Response response, Retrofit retrofit) { 54 | String message = response.code() + " " + response.message(); 55 | return new ServerException(message, url, response, Kind.HTTP, null, retrofit); 56 | } 57 | 58 | public static ServerException networkError(IOException exception) { 59 | return new ServerException(exception.getMessage(), null, null, Kind.NETWORK, exception, 60 | null); 61 | } 62 | 63 | public static ServerException unexpectedError(Throwable exception) { 64 | return new ServerException(exception.getMessage(), null, null, Kind.UNEXPECTED, exception, 65 | null); 66 | } 67 | 68 | /** 69 | * The request URL which produced the error. 70 | */ 71 | public String getUrl() { 72 | return url; 73 | } 74 | 75 | /** 76 | * Response object containing status code, headers, body, etc. 77 | */ 78 | public Response getResponse() { 79 | return response; 80 | } 81 | 82 | /** 83 | * The event kind which triggered this error. 84 | */ 85 | public Kind getKind() { 86 | return kind; 87 | } 88 | 89 | /** 90 | * The Retrofit this request was executed on. 91 | */ 92 | public Retrofit getRetrofit() { 93 | return retrofit; 94 | } 95 | 96 | public MyAppServerError getServerError() { 97 | return mServerError; 98 | } 99 | 100 | /** 101 | * HTTP response body converted to specified {@code type}. {@code null} if there is no 102 | * response. 103 | * 104 | * @throws IOException if unable to convert the body to the specified {@code type}. 105 | */ 106 | public T getErrorBodyAs(Class type) throws IOException { 107 | if (response == null || response.errorBody() == null) { 108 | return null; 109 | } 110 | Converter converter = retrofit.responseBodyConverter(type, 111 | new Annotation[0]); 112 | return converter.convert(response.errorBody()); 113 | } 114 | } -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/implementation/FileManagerImpl.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.implementation; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.net.Uri; 6 | import android.os.Environment; 7 | import android.support.annotation.Nullable; 8 | import android.support.v4.content.FileProvider; 9 | 10 | import java.io.ByteArrayOutputStream; 11 | import java.io.Closeable; 12 | import java.io.File; 13 | import java.io.FileNotFoundException; 14 | import java.io.FileOutputStream; 15 | import java.io.IOException; 16 | import java.text.SimpleDateFormat; 17 | import java.util.Date; 18 | 19 | import co.lateralview.myapp.infraestructure.manager.interfaces.FileManager; 20 | 21 | public class FileManagerImpl implements FileManager { 22 | private static final String FILE_TEMP_PREFIX = "LV_MYAPP"; 23 | 24 | private Context mContext; 25 | 26 | public FileManagerImpl(Context mContext) { 27 | this.mContext = mContext; 28 | } 29 | 30 | public String savePhotoToInternalStorage(Bitmap bitmapImage) { 31 | ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 32 | bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes); 33 | 34 | File photoFile = createPhotoFile(); 35 | 36 | writeFile(photoFile, bytes.toByteArray()); 37 | 38 | return getUri(photoFile).toString(); 39 | } 40 | 41 | public File createPhotoFile() { 42 | String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); 43 | String imageFileName = FILE_TEMP_PREFIX + "_" + timeStamp + "_"; 44 | File storageDir = Environment.getExternalStoragePublicDirectory( 45 | Environment.DIRECTORY_PICTURES); 46 | storageDir.mkdirs(); 47 | 48 | try { 49 | return File.createTempFile(imageFileName, ".jpg", storageDir); 50 | } catch (IOException e) { 51 | return null; 52 | } 53 | } 54 | 55 | public Uri getUri(File file) { 56 | return null != file ? FileProvider.getUriForFile(mContext, 57 | mContext.getApplicationContext().getPackageName() + ".provider", file) : null; 58 | } 59 | 60 | public Uri createPhotoUri() { 61 | File file = createPhotoFile(); 62 | 63 | return null != file ? getUri(file) : null; 64 | } 65 | 66 | public void saveBitmapToFile(Bitmap croppedImage, Uri saveUri) { 67 | if (saveUri != null) { 68 | FileOutputStream outputStream = null; 69 | try { 70 | outputStream = new FileOutputStream(saveUri.getPath()); 71 | 72 | if (outputStream != null) { 73 | croppedImage.compress(Bitmap.CompressFormat.JPEG, 90, outputStream); 74 | } 75 | 76 | } catch (FileNotFoundException e) { 77 | e.printStackTrace(); 78 | } finally { 79 | closeSilently(outputStream); 80 | croppedImage.recycle(); 81 | } 82 | } 83 | } 84 | 85 | private boolean writeFile(File f, byte[] bytes) { 86 | return writeFile(f.getAbsolutePath(), bytes); 87 | } 88 | 89 | private boolean writeFile(String path, byte[] bytes) { 90 | try { 91 | FileOutputStream stream = new FileOutputStream(path); 92 | 93 | try { 94 | stream.write(bytes); 95 | } finally { 96 | closeSilently(stream); 97 | } 98 | 99 | return true; 100 | } catch (Exception e) { 101 | return false; 102 | } 103 | } 104 | 105 | 106 | private void closeSilently(@Nullable Closeable c) { 107 | if (c == null) { 108 | return; 109 | } 110 | 111 | try { 112 | c.close(); 113 | } catch (Throwable t) { 114 | // Do nothing 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/RxErrorHandlingCallAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking; 2 | 3 | import java.io.IOException; 4 | import java.lang.annotation.Annotation; 5 | import java.lang.reflect.Type; 6 | 7 | import io.reactivex.Completable; 8 | import io.reactivex.Maybe; 9 | import io.reactivex.MaybeSource; 10 | import io.reactivex.Observable; 11 | import io.reactivex.ObservableSource; 12 | import io.reactivex.Single; 13 | import retrofit2.Call; 14 | import retrofit2.CallAdapter; 15 | import retrofit2.HttpException; 16 | import retrofit2.Response; 17 | import retrofit2.Retrofit; 18 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 19 | 20 | public final class RxErrorHandlingCallAdapterFactory extends CallAdapter.Factory { 21 | private final RxJava2CallAdapterFactory original; 22 | 23 | private RxErrorHandlingCallAdapterFactory() { 24 | original = RxJava2CallAdapterFactory.create(); 25 | } 26 | 27 | public static CallAdapter.Factory create() { 28 | return new RxErrorHandlingCallAdapterFactory(); 29 | } 30 | 31 | @SuppressWarnings("NullableProblems") 32 | @Override 33 | public CallAdapter get(Type returnType, Annotation[] annotations, Retrofit retrofit) { 34 | return new RxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit)); 35 | } 36 | 37 | private static class RxCallAdapterWrapper implements CallAdapter { 38 | private final Retrofit retrofit; 39 | private final CallAdapter wrapped; 40 | 41 | RxCallAdapterWrapper(Retrofit retrofit, CallAdapter wrapped) { 42 | this.retrofit = retrofit; 43 | this.wrapped = wrapped; 44 | } 45 | 46 | @Override 47 | public Type responseType() { 48 | return wrapped.responseType(); 49 | } 50 | 51 | @SuppressWarnings({"unchecked", "NullableProblems"}) 52 | @Override 53 | public Object adapt(Call call) { 54 | Object adaptedCall = wrapped.adapt(call); 55 | 56 | if (adaptedCall instanceof Completable) { 57 | return ((Completable) adaptedCall).onErrorResumeNext( 58 | throwable -> Completable.error(asRetrofitException(throwable))); 59 | } 60 | 61 | if (adaptedCall instanceof Single) { 62 | return ((Single) adaptedCall).onErrorResumeNext( 63 | throwable -> Single.error(asRetrofitException((Throwable) throwable))); 64 | } 65 | 66 | if (adaptedCall instanceof Observable) { 67 | return ((Observable) adaptedCall).onErrorResumeNext( 68 | (ObservableSource) throwable -> Observable.error( 69 | asRetrofitException((Throwable) throwable))); 70 | } 71 | 72 | if (adaptedCall instanceof Maybe) { 73 | return ((Maybe) adaptedCall).onErrorResumeNext( 74 | (MaybeSource) throwable -> Maybe.error( 75 | asRetrofitException((Throwable) throwable))); 76 | } 77 | 78 | throw new RuntimeException("Observable Type not supported"); 79 | } 80 | 81 | private ServerException asRetrofitException(Throwable throwable) { 82 | // We had non-200 http error 83 | if (throwable instanceof HttpException) { 84 | HttpException httpException = (HttpException) throwable; 85 | Response response = httpException.response(); 86 | return ServerException.httpError(response.raw().request().url().toString(), 87 | response, retrofit); 88 | } 89 | 90 | // A network error happened 91 | if (throwable instanceof IOException) { 92 | return ServerException.networkError((IOException) throwable); 93 | } 94 | 95 | // We don't know what happened. We need to simply convert to an unknown error 96 | return ServerException.unexpectedError(throwable); 97 | } 98 | } 99 | } -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/implementation/SharedPreferencesManagerImpl.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.implementation; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | import java.lang.reflect.Type; 7 | 8 | import co.lateralview.myapp.infraestructure.manager.interfaces.ParserManager; 9 | import co.lateralview.myapp.infraestructure.manager.interfaces.SharedPreferencesManager; 10 | 11 | public class SharedPreferencesManagerImpl implements SharedPreferencesManager { 12 | public static final String DEFAULT_FILE_NAME = "co.lateralview.myapp.sharedPreferences"; 13 | private SharedPreferences mSharedPreferences; 14 | private ParserManager mParserManager; 15 | 16 | public SharedPreferencesManagerImpl(Context context, ParserManager parserManager, 17 | String fileName) { 18 | mSharedPreferences = context.getSharedPreferences(fileName, Context.MODE_PRIVATE); 19 | mParserManager = parserManager; 20 | } 21 | 22 | public SharedPreferencesManagerImpl(Context context, ParserManager parserManager) { 23 | this(context, parserManager, DEFAULT_FILE_NAME); 24 | } 25 | 26 | public void save(String key, boolean value) { 27 | mSharedPreferences.edit().putBoolean(key, value).apply(); 28 | } 29 | 30 | public void save(String key, String value) { 31 | mSharedPreferences.edit().putString(key, value).apply(); 32 | } 33 | 34 | public void save(String key, int value) { 35 | mSharedPreferences.edit().putInt(key, value).apply(); 36 | } 37 | 38 | @Override 39 | public boolean saveBlocking(String key, boolean value) { 40 | return mSharedPreferences.edit().putBoolean(key, value).commit(); 41 | 42 | } 43 | 44 | @Override 45 | public boolean saveBlocking(String key, String value) { 46 | return mSharedPreferences.edit().putString(key, value).commit(); 47 | } 48 | 49 | @Override 50 | public boolean saveBlocking(String key, int value) { 51 | return mSharedPreferences.edit().putInt(key, value).commit(); 52 | 53 | } 54 | 55 | public boolean getBoolean(String key) { 56 | return mSharedPreferences.getBoolean(key, false); 57 | } 58 | 59 | public boolean getBoolean(String key, boolean defaultValue) { 60 | return mSharedPreferences.getBoolean(key, defaultValue); 61 | } 62 | 63 | public String getString(String key) { 64 | return getString(key, ""); 65 | } 66 | 67 | public String getString(String key, String defaultValue) { 68 | return mSharedPreferences.getString(key, defaultValue); 69 | } 70 | 71 | public int getInt(String key) { 72 | return getInt(key, -1); 73 | } 74 | 75 | public int getInt(String key, int defaultValue) { 76 | return mSharedPreferences.getInt(key, defaultValue); 77 | } 78 | 79 | public void save(String key, T model) { 80 | mSharedPreferences.edit().putString(key, mParserManager.toJson(model)).apply(); 81 | } 82 | 83 | @Override 84 | public boolean saveBlocking(String key, T model) { 85 | return mSharedPreferences.edit().putString(key, mParserManager.toJson(model)).commit(); 86 | } 87 | 88 | public T get(String key, Class type) { 89 | String json = getString(key); 90 | return json != "" ? mParserManager.fromJson(json, type) : null; 91 | } 92 | 93 | @Override 94 | public T get(String key, Type type) { 95 | String json = getString(key); 96 | return json != "" ? (T) mParserManager.fromJson(json, type) : null; 97 | } 98 | 99 | @Override 100 | public void clear() { 101 | mSharedPreferences.edit().clear().apply(); 102 | } 103 | 104 | @Override 105 | public boolean clearBlocking() { 106 | return mSharedPreferences.edit().clear().commit(); 107 | } 108 | 109 | @Override 110 | public void remove(String key) { 111 | mSharedPreferences.edit().remove(key).apply(); 112 | } 113 | 114 | @Override 115 | public boolean removeBlocking(String key) { 116 | return mSharedPreferences.edit().remove(key).commit(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/GalleryPhotoManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.graphics.Bitmap; 6 | import android.net.Uri; 7 | import android.support.v4.app.Fragment; 8 | 9 | import java.io.File; 10 | 11 | import co.lateralview.myapp.infraestructure.manager.implementation.FileManagerImpl; 12 | import co.lateralview.myapp.infraestructure.manager.interfaces.FileManager; 13 | 14 | public class GalleryPhotoManager { 15 | protected Fragment mCallerFragment; 16 | protected Activity mCallerActivity; 17 | protected IGalleryPhotoCallback mCallback; 18 | protected Uri mCroppedImage; 19 | private int mRequestCodePhotoFromGallery; 20 | private int mRequestCodePhotoFromGalleryCrop; 21 | private int mRequestCodeCropImage; 22 | private FileManager mFileManager; 23 | 24 | public GalleryPhotoManager(Fragment fragment, IGalleryPhotoCallback callback, int requestCode) { 25 | this(fragment.getActivity(), callback, requestCode); 26 | mCallerFragment = fragment; 27 | } 28 | 29 | public GalleryPhotoManager(Activity activity, IGalleryPhotoCallback callback, int requestCode) { 30 | mRequestCodePhotoFromGallery = requestCode; 31 | mCallerActivity = activity; 32 | mCallback = callback; 33 | mFileManager = new FileManagerImpl(mCallerActivity); 34 | } 35 | 36 | public void startService() { 37 | start(false); 38 | } 39 | 40 | public void startServiceWithCrop(int cropImageRequestCode1, int cropImageRequestCode2) { 41 | mRequestCodePhotoFromGalleryCrop = cropImageRequestCode1; 42 | mRequestCodeCropImage = cropImageRequestCode2; 43 | 44 | start(true); 45 | } 46 | 47 | private void start(boolean cropIt) { 48 | Intent mediaChooser = new Intent(Intent.ACTION_OPEN_DOCUMENT); 49 | mediaChooser.setType("image/*"); 50 | 51 | if (mCallerFragment != null) { 52 | mCallerFragment.startActivityForResult(mediaChooser, 53 | cropIt ? mRequestCodePhotoFromGalleryCrop : mRequestCodePhotoFromGallery); 54 | } else { 55 | mCallerActivity.startActivityForResult(mediaChooser, 56 | cropIt ? mRequestCodePhotoFromGalleryCrop : mRequestCodePhotoFromGallery); 57 | } 58 | } 59 | 60 | public void processResult(int requestCode, int resultCode, Intent data) { 61 | if (resultCode == Activity.RESULT_OK) { 62 | if (requestCode == mRequestCodePhotoFromGallery) { 63 | onPhotoFromGallerySuccess(data); 64 | return; 65 | } 66 | 67 | if (requestCode == mRequestCodeCropImage) { 68 | onPhotoFromGalleryCroppedSuccess(mCroppedImage); 69 | return; 70 | } 71 | 72 | if (requestCode == mRequestCodePhotoFromGalleryCrop) { 73 | mCroppedImage = mFileManager.createPhotoUri(); 74 | 75 | if (mCallerFragment != null) { 76 | new CropManager(mCallerFragment, mRequestCodeCropImage).requestCrop( 77 | data.getData(), mCroppedImage); 78 | } else { 79 | new CropManager(mCallerActivity, mRequestCodeCropImage).requestCrop( 80 | data.getData(), mCroppedImage); 81 | } 82 | 83 | return; 84 | } 85 | } 86 | } 87 | 88 | protected void onPhotoFromGallerySuccess(Intent data) { 89 | if (data != null) { 90 | final String path = 91 | null != data.getData() ? data.getData().getPath() : data.getAction().replace( 92 | "file://", ""); 93 | 94 | getPhotoFromPath(path); 95 | } 96 | } 97 | 98 | protected void onPhotoFromGalleryCroppedSuccess(final Uri imageUri) { 99 | if (imageUri != null) { 100 | getPhotoFromPath(imageUri.getPath()); 101 | } 102 | } 103 | 104 | protected void getPhotoFromPath(final String path) { 105 | new PhotoDecodeTask(photo -> mCallerActivity.runOnUiThread( 106 | () -> mCallback.onPhotoObtained(photo, new File(path)))).execute(path); 107 | } 108 | 109 | public interface IGalleryPhotoCallback { 110 | void onPhotoObtained(Bitmap picture, File file); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Looking for our latest Base Project in Kotlin ? 2 | https://github.com/LateralView/android-base-project-kotlin 3 | 4 | # Android Base Project 5 | 6 | Base project built using [Dagger2](https://google.github.io/dagger/) for Dependency Injection and MVC architecture with Repository Pattern. 7 | It has several managers classes that encapsulate features that are usually used in a common project, most using inversion control to decouple the implementation. 8 | 9 | This project has the most common setttings applied. The main idea is to start any new project cloning this one. 10 | 11 | Developed by the [LateralView](https://lateralview.co) team. 12 | 13 | Check our [Wiki](https://github.com/LateralView/android-base-project/wiki) to learn the Best Practice in Android Development and more. 14 | 15 | ### Included Libraries 16 | 17 | - [Dagger2](https://google.github.io/dagger/) 18 | Compile-time dependency injection framework. 19 | 20 | - [Retrofit](http://square.github.io/retrofit/) 21 | Networking library that allows developers to easily make request to a server through Annotations. 22 | 23 | - [Glide](https://github.com/bumptech/glide) 24 | Fast and efficient open source media management and image loading framework. 25 | 26 | More... 27 | 28 | ### Useful Managers 29 | 30 | - [Camera Manager](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/infraestructure/manager/CameraManager.java) 31 | 32 | - [Internet Manager](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/infraestructure/manager/InternetManager.java) 33 | 34 | - [System Manager](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/infraestructure/manager/SystemManager.java) 35 | 36 | - [Social Manager](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/infraestructure/manager/SocialManager.java) 37 | 38 | - [File Manager](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/FileManager.java) 39 | 40 | - [Image Manager](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/ImageManager.java) 41 | 42 | - [Parser Manager](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/ParserManager.java) 43 | 44 | - [Shared Preferences Manager](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/SharedPreferencesManager.java) 45 | 46 | - [Task Manager](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/infraestructure/manager/interfaces/TaskManager.java) 47 | 48 | Also there is a repository for user management ([UserRepository](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/domain/repository/interfaces/UserRepository.java)) and another repository for sessions ([SessionRepository](https://github.com/LateralView/android-base-project/blob/master/app/src/main/java/co/lateralview/myapp/domain/repository/interfaces/SessionRepository.java)), which are stored in the shared preferences. 49 | 50 | ### Package Structure 51 | 52 | ``` 53 | android-base-project/app/src/main/java/co/lateralview/myapp/ 54 | └───application 55 | │ │ ... 56 | └───domain 57 | │ └─── model 58 | │ │ │ ... 59 | │ └─── repository 60 | │ └─── implementation 61 | │ └─── interfaces 62 | │ │ ... 63 | └───infraestructure 64 | │ └─── manager 65 | │ │ └─── implementation 66 | │ │ └─── interfaces 67 | │ │ │ ... 68 | │ └─── networking 69 | │ │ └─── implementation 70 | │ │ └─── interfaces 71 | │ │ │ ... 72 | │ └─── pushNotification 73 | │ │ ... 74 | └───ui 75 | └─── activity 76 | │ │ ... 77 | └─── broadcast 78 | │ │ ... 79 | └─── common 80 | │ │ ... 81 | └─── fragment 82 | │ │ ... 83 | └─── util 84 | │ ... 85 | ``` 86 | 87 | If you are going to develop a big application, we recommend to create one package for each functionality (screen). 88 | 89 | ### Base Project Architectures 90 | 91 | There is a branch with the following architectures: 92 | 93 | - MVP + Dagger2: Check develop+MVP branch [more](https://github.com/LateralView/android-base-project/wiki/%5BBRANCH%5D-Develop-MVP) 94 | 95 | - [Latest] MVP + Dagger2 + RX: Check develop+MVP+RX branch [more](https://github.com/LateralView/android-base-project/wiki/%5BBRANCH%5D-Develop---MVP---RX) 96 | 97 | For more information about that check our [Wiki](https://github.com/LateralView/android-base-project/wiki) 98 | 99 | ### Extra 100 | 101 | [Here](https://github.com/LateralView/android-lateral-skeleton/wiki) you will find all the documentation related to Android Lateral naming standards, good practices, tips, useful libraries and more! 102 | 103 | For any suggestions or if you want to join our team please contact us via [android@lateralview.net](mailto:android@lateralview.net) 104 | 105 | Happy codding! 106 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven { 4 | url 'https://maven.fabric.io/public' 5 | } 6 | } 7 | dependencies { 8 | classpath 'io.fabric.tools:gradle:1.25.4' 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.application' 13 | apply plugin: 'com.google.firebase.firebase-perf' 14 | apply plugin: 'io.fabric' 15 | 16 | // If checkstyle fails we can't generate a build 17 | apply from: 'checkstyle.gradle' 18 | preBuild.dependsOn('Checkstyle') 19 | 20 | android { 21 | compileSdkVersion 27 22 | 23 | defaultConfig { 24 | applicationId "co.lateralview.myapp" 25 | minSdkVersion 21 26 | targetSdkVersion 27 27 | versionCode 1 28 | versionName "1.0" 29 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 30 | vectorDrawables.useSupportLibrary = true 31 | 32 | renderscriptTargetApi 19 33 | renderscriptSupportModeEnabled true 34 | 35 | javaCompileOptions { 36 | annotationProcessorOptions { 37 | includeCompileClasspath = true 38 | } 39 | } 40 | } 41 | signingConfigs { 42 | // Load your keystore.properties file into the keystoreProperties object. 43 | def keystoreProperties = new Properties() 44 | keystoreProperties.load(new FileInputStream(rootProject.file("keystore.properties"))) 45 | 46 | release { 47 | keyAlias keystoreProperties['keyAlias'] 48 | keyPassword keystoreProperties['keyPassword'] 49 | storeFile file(keystoreProperties['storeFile']) 50 | storePassword keystoreProperties['storePassword'] 51 | } 52 | } 53 | buildTypes { 54 | debug { 55 | debuggable true 56 | jniDebuggable true 57 | buildConfigField "String", "BASE_URL", "\"https://base-url-for-debug\"" 58 | } 59 | release { 60 | debuggable false 61 | minifyEnabled true 62 | shrinkResources true 63 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 64 | signingConfig signingConfigs.release 65 | buildConfigField "String", "BASE_URL", "\"https://base-url-for-release\"" 66 | } 67 | } 68 | compileOptions { 69 | sourceCompatibility JavaVersion.VERSION_1_8 70 | targetCompatibility JavaVersion.VERSION_1_8 71 | } 72 | testOptions { 73 | unitTests { 74 | includeAndroidResources = true 75 | } 76 | } 77 | } 78 | 79 | ext { 80 | version_support = '27.1.1' 81 | version_butterknife = "8.8.1" 82 | version_rxjava = '2.1.14' 83 | version_rxandroid = '2.0.2' 84 | version_dagger = "2.16" 85 | version_retrofit = "2.4.0" 86 | version_leakcanary = "1.5.4" 87 | version_permission = '3.2.0' 88 | version_firebase = '16.0.0' 89 | version_glide = '4.7.1' 90 | } 91 | 92 | dependencies { 93 | implementation fileTree(dir: 'libs', include: ['*.jar']) 94 | implementation "com.android.support:recyclerview-v7:$version_support" 95 | implementation "com.android.support:design:$version_support" 96 | implementation "com.android.support:support-v13:$version_support" 97 | implementation "com.android.support:appcompat-v7:$version_support" 98 | 99 | // Dagger 2 - Dependency Injection 100 | implementation "com.google.dagger:dagger:$version_dagger" 101 | annotationProcessor "com.google.dagger:dagger-compiler:$version_dagger" 102 | 103 | // Butterknife - View binding 104 | implementation "com.jakewharton:butterknife:$version_butterknife" 105 | annotationProcessor "com.jakewharton:butterknife-compiler:$version_butterknife" 106 | 107 | //Image Loader 108 | implementation "com.github.bumptech.glide:glide:$version_glide" 109 | implementation "com.github.bumptech.glide:okhttp3-integration:$version_glide@aar" 110 | annotationProcessor "com.github.bumptech.glide:compiler:$version_glide" 111 | 112 | //Instance State Handler 113 | implementation 'frankiesardo:icepick:3.2.0' 114 | compileOnly 'frankiesardo:icepick-processor:3.2.0' 115 | 116 | //Image Crop 117 | implementation 'com.soundcloud.android:android-crop:1.0.1@aar' 118 | 119 | //Reactive Programming 120 | implementation "io.reactivex.rxjava2:rxjava:$version_rxjava" 121 | implementation "io.reactivex.rxjava2:rxandroid:$version_rxandroid" 122 | 123 | // Leak Canary - memory leak detection 124 | // LeakCanary for debug builds only: notifications, analysis, etc 125 | debugImplementation "com.squareup.leakcanary:leakcanary-android:$version_leakcanary" 126 | // No-Op version of LeakCanary for release builds: no notifications, no analysis, nothing 127 | releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$version_leakcanary" 128 | testImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$version_leakcanary" 129 | 130 | // REST Client 131 | implementation "com.squareup.retrofit2:retrofit:$version_retrofit" 132 | implementation "com.squareup.retrofit2:converter-gson:$version_retrofit" 133 | implementation "com.squareup.retrofit2:adapter-rxjava2:$version_retrofit" 134 | implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0' 135 | 136 | // Stetho 137 | implementation 'com.facebook.stetho:stetho:1.5.0' 138 | implementation 'com.facebook.stetho:stetho-okhttp3:1.5.0' 139 | 140 | //Firebase 141 | implementation "com.google.firebase:firebase-core:$version_firebase" 142 | implementation "com.google.firebase:firebase-perf:$version_firebase" 143 | releaseImplementation('com.crashlytics.sdk.android:crashlytics:2.9.3@aar') { 144 | transitive = true 145 | } 146 | 147 | //PermissionsDispatcher 148 | implementation("com.github.hotchemi:permissionsdispatcher:$version_permission") 149 | annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$version_permission" 150 | 151 | testImplementation 'junit:junit:4.12' 152 | testImplementation 'org.mockito:mockito-core:2.17.0' 153 | testImplementation "org.robolectric:robolectric:3.8" 154 | 155 | // Friendly Http Inspector through separate App 156 | debugImplementation 'com.readystatesoftware.chuck:library:1.1.0' 157 | releaseImplementation 'com.readystatesoftware.chuck:library-no-op:1.1.0' 158 | 159 | // Proguard Annotations 160 | implementation 'com.infstory:proguard-annotations:1.0.2' 161 | } 162 | 163 | apply plugin: 'com.google.gms.google-services' 164 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/implementation/ImageManagerImpl.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager.implementation; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapShader; 6 | import android.graphics.Canvas; 7 | import android.graphics.Matrix; 8 | import android.graphics.Paint; 9 | import android.graphics.Shader; 10 | import android.media.ExifInterface; 11 | import android.renderscript.Allocation; 12 | import android.renderscript.Element; 13 | import android.renderscript.RenderScript; 14 | import android.renderscript.ScriptIntrinsicBlur; 15 | import android.widget.ImageView; 16 | 17 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 18 | import com.bumptech.glide.request.target.SimpleTarget; 19 | 20 | import java.io.File; 21 | import java.io.FileNotFoundException; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.io.OutputStream; 25 | 26 | import co.lateralview.myapp.infraestructure.manager.interfaces.ImageManager; 27 | import co.lateralview.myapp.ui.util.GlideApp; 28 | 29 | public class ImageManagerImpl implements ImageManager { 30 | private static final float BITMAP_SCALE = 0.4f; 31 | private static final float BLUR_RADIUS = 7.5f; 32 | private Context mContext; 33 | 34 | public ImageManagerImpl(Context context) { 35 | mContext = context; 36 | } 37 | 38 | @Override 39 | public void loadCircleImage(String url, ImageView imageView) { 40 | GlideApp.with(mContext) 41 | .load(url) 42 | .centerCrop() 43 | .circleCrop() 44 | .into(imageView); 45 | } 46 | 47 | @Override 48 | public void loadImage(String url, ImageView imageView) { 49 | GlideApp.with(mContext) 50 | .load(url) 51 | .into(imageView); 52 | } 53 | 54 | @Override 55 | public void loadImage(String url, SimpleTarget simpleTarget) { 56 | GlideApp.with(mContext) 57 | .asBitmap() 58 | .load(url) 59 | .into(simpleTarget); 60 | } 61 | 62 | @Override 63 | public void loadGifFromRes(int res, ImageView imageView) { 64 | GlideApp.with(mContext) 65 | .asGif() 66 | .load(res) 67 | .fitCenter() 68 | .diskCacheStrategy(DiskCacheStrategy.DATA) 69 | .into(imageView); 70 | } 71 | 72 | @Override 73 | public Bitmap compressImage(Bitmap bitmap, File file) { 74 | OutputStream outputStream = null; 75 | 76 | try { 77 | outputStream = new FileOutputStream(file); 78 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream); 79 | } catch (FileNotFoundException e) { 80 | e.printStackTrace(); 81 | } finally { 82 | try { 83 | if (outputStream != null) { 84 | outputStream.close(); 85 | } 86 | } catch (IOException e) { 87 | e.printStackTrace(); 88 | } 89 | } 90 | 91 | return bitmap; 92 | } 93 | 94 | @Override 95 | public Bitmap rotateBitmap(Bitmap bitmap, int orientation) { 96 | Matrix matrix = new Matrix(); 97 | switch (orientation) { 98 | case ExifInterface.ORIENTATION_NORMAL: 99 | return bitmap; 100 | case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: 101 | matrix.setScale(-1, 1); 102 | break; 103 | case ExifInterface.ORIENTATION_ROTATE_180: 104 | matrix.setRotate(180); 105 | break; 106 | case ExifInterface.ORIENTATION_FLIP_VERTICAL: 107 | matrix.setRotate(180); 108 | matrix.postScale(-1, 1); 109 | break; 110 | case ExifInterface.ORIENTATION_TRANSPOSE: 111 | matrix.setRotate(90); 112 | matrix.postScale(-1, 1); 113 | break; 114 | case ExifInterface.ORIENTATION_ROTATE_90: 115 | matrix.setRotate(90); 116 | break; 117 | case ExifInterface.ORIENTATION_TRANSVERSE: 118 | matrix.setRotate(-90); 119 | matrix.postScale(-1, 1); 120 | break; 121 | case ExifInterface.ORIENTATION_ROTATE_270: 122 | matrix.setRotate(-90); 123 | break; 124 | default: 125 | return bitmap; 126 | } 127 | 128 | try { 129 | Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), 130 | bitmap.getHeight(), matrix, true); 131 | bitmap.recycle(); 132 | 133 | return bmRotated; 134 | } catch (OutOfMemoryError e) { 135 | e.printStackTrace(); 136 | return null; 137 | } 138 | } 139 | 140 | @Override 141 | public Bitmap blur(Bitmap image) { 142 | Bitmap u84Bitmap; 143 | 144 | if (image.getConfig() == Bitmap.Config.ARGB_8888) { 145 | u84Bitmap = image; 146 | } else { 147 | u84Bitmap = image.copy(Bitmap.Config.ARGB_8888, true); 148 | } 149 | 150 | int width = Math.round(u84Bitmap.getWidth() * BITMAP_SCALE); 151 | int height = Math.round(u84Bitmap.getHeight() * BITMAP_SCALE); 152 | 153 | Bitmap inputBitmap = Bitmap.createScaledBitmap(u84Bitmap, width, height, false); 154 | Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap); 155 | 156 | RenderScript rs = RenderScript.create(mContext); 157 | ScriptIntrinsicBlur theIntrinsic = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); 158 | Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap); 159 | Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap); 160 | theIntrinsic.setRadius(BLUR_RADIUS); 161 | theIntrinsic.setInput(tmpIn); 162 | theIntrinsic.forEach(tmpOut); 163 | tmpOut.copyTo(outputBitmap); 164 | 165 | return outputBitmap; 166 | } 167 | 168 | @Override 169 | public Bitmap transformToCircle(Bitmap bitmap) { 170 | Bitmap circleBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), 171 | Bitmap.Config.ARGB_8888); 172 | 173 | BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, 174 | Shader.TileMode.CLAMP); 175 | Paint paint = new Paint(); 176 | paint.setShader(shader); 177 | paint.setAntiAlias(true); 178 | Canvas c = new Canvas(circleBitmap); 179 | c.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint); 180 | 181 | return circleBitmap; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/manager/CameraManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.manager; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.media.ExifInterface; 9 | import android.net.Uri; 10 | import android.provider.MediaStore; 11 | import android.support.v4.app.Fragment; 12 | 13 | import java.io.File; 14 | import java.io.IOException; 15 | 16 | import co.lateralview.myapp.infraestructure.manager.implementation.FileManagerImpl; 17 | import co.lateralview.myapp.infraestructure.manager.implementation.ImageManagerImpl; 18 | import co.lateralview.myapp.infraestructure.manager.interfaces.FileManager; 19 | import co.lateralview.myapp.infraestructure.manager.interfaces.ImageManager; 20 | 21 | public class CameraManager { 22 | protected Activity mCallerActivity; 23 | protected Fragment mCallerFragment; 24 | protected Uri mCroppedImage; 25 | private int mRequestCodeTakePhoto; 26 | private int mRequestCodeTakePhotoCrop; 27 | private int mRequestCodeCropImage; 28 | private ICameraServiceCallback mCameraServiceListener; 29 | private File mPhotoFile; 30 | private FileManager mFileManager; 31 | private ImageManager mImageManager; 32 | 33 | public CameraManager(Fragment fragment, ICameraServiceCallback cameraServiceListener, 34 | int takePhotoRequestCode) { 35 | this(fragment.getActivity(), cameraServiceListener, takePhotoRequestCode); 36 | mCallerFragment = fragment; 37 | } 38 | 39 | public CameraManager(Activity callerActivity, ICameraServiceCallback cameraServiceListener, 40 | int takePhotoRequestCode) { 41 | mCallerActivity = callerActivity; 42 | mCameraServiceListener = cameraServiceListener; 43 | mFileManager = new FileManagerImpl(callerActivity); 44 | mImageManager = new ImageManagerImpl(callerActivity); 45 | mRequestCodeTakePhoto = takePhotoRequestCode; 46 | } 47 | 48 | public void startService() { 49 | mPhotoFile = dispatchTakePictureIntent(false); 50 | } 51 | 52 | public void startServiceWithCrop(int cropImageRequestCode1, int cropImageRequestCode2) { 53 | mRequestCodeTakePhotoCrop = cropImageRequestCode1; 54 | mRequestCodeCropImage = cropImageRequestCode2; 55 | 56 | mPhotoFile = dispatchTakePictureIntent(true); 57 | } 58 | 59 | private File dispatchTakePictureIntent(boolean cropIt) { 60 | Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 61 | 62 | File photoFile = null; 63 | 64 | // Ensure that there's a camera activity to handle the intent 65 | if (takePictureIntent.resolveActivity(mCallerActivity.getPackageManager()) != null) { 66 | // Create the File where the photo should go 67 | photoFile = mFileManager.createPhotoFile(); 68 | 69 | // Continue only if the File was successfully created 70 | if (photoFile != null) { 71 | takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mFileManager.getUri(photoFile)); 72 | 73 | if (mCallerFragment != null) { 74 | mCallerFragment.startActivityForResult(takePictureIntent, 75 | cropIt ? mRequestCodeTakePhotoCrop : mRequestCodeTakePhoto); 76 | } else { 77 | mCallerActivity.startActivityForResult(takePictureIntent, 78 | cropIt ? mRequestCodeTakePhotoCrop : mRequestCodeTakePhoto); 79 | } 80 | } 81 | } 82 | 83 | return photoFile; 84 | } 85 | 86 | public void processResult(int requestCode, int resultCode, Intent data) { 87 | if (resultCode == Activity.RESULT_OK) { 88 | if (requestCode == mRequestCodeCropImage) { 89 | onRequestTakePhotoCropSuccess(mCroppedImage); 90 | return; 91 | } 92 | 93 | if (requestCode == mRequestCodeTakePhoto) { 94 | onRequestTakePhotoSuccess(); 95 | return; 96 | } 97 | 98 | if (requestCode == mRequestCodeTakePhotoCrop) { 99 | normalizeImageForUri(mCallerActivity, mFileManager.getUri(mPhotoFile)); 100 | 101 | mCroppedImage = mFileManager.createPhotoUri(); 102 | 103 | if (mCallerFragment != null) { 104 | new CropManager(mCallerFragment, 105 | mRequestCodeCropImage).requestCrop( 106 | mFileManager.getUri(mPhotoFile), mCroppedImage); 107 | } else { 108 | new CropManager(mCallerActivity, 109 | mRequestCodeCropImage).requestCrop( 110 | mFileManager.getUri(mPhotoFile), mCroppedImage); 111 | } 112 | 113 | return; 114 | } 115 | } 116 | } 117 | 118 | private void onRequestTakePhotoSuccess() { 119 | Bitmap imageBitmap = BitmapFactory.decodeFile(mPhotoFile.getAbsolutePath()); 120 | 121 | if (mCameraServiceListener != null) { 122 | mCameraServiceListener.onPictureTaken(imageBitmap, mPhotoFile); 123 | } 124 | } 125 | 126 | private void onRequestTakePhotoCropSuccess(Uri imageCropperUri) { 127 | if (imageCropperUri != null) { 128 | final String path = imageCropperUri.getPath(); 129 | 130 | new PhotoDecodeTask( 131 | photo -> mCallerActivity.runOnUiThread( 132 | () -> mCameraServiceListener.onPictureTaken(photo, 133 | new File(path)))).execute(path); 134 | } 135 | } 136 | 137 | /** 138 | * Allows to fix issue for some phones when image processed with android-crop 139 | * is not rotated properly. 140 | * Based on https://github.com/jdamcd/android-crop/issues/140#issuecomment-125109892 141 | * 142 | * @param context - context to use while saving file 143 | * @param uri - origin file uri 144 | */ 145 | private void normalizeImageForUri(Context context, Uri uri) { 146 | try { 147 | ExifInterface exif = new ExifInterface(uri.getPath()); 148 | int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 149 | ExifInterface.ORIENTATION_UNDEFINED); 150 | Bitmap bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri); 151 | Bitmap rotatedBitmap = mImageManager.rotateBitmap(bitmap, orientation); 152 | 153 | if (!bitmap.equals(rotatedBitmap)) { 154 | mFileManager.saveBitmapToFile(rotatedBitmap, uri); 155 | } 156 | } catch (IOException e) { 157 | e.printStackTrace(); 158 | } 159 | } 160 | 161 | public interface ICameraServiceCallback { 162 | void onPictureTaken(Bitmap picture, File file); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /ci/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/activities/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.activities.base; 2 | 3 | import android.app.Fragment; 4 | import android.app.ProgressDialog; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.graphics.drawable.ColorDrawable; 9 | import android.os.Bundle; 10 | import android.support.annotation.Nullable; 11 | import android.support.design.widget.Snackbar; 12 | import android.support.v7.app.AppCompatActivity; 13 | import android.view.MenuItem; 14 | import android.widget.Toast; 15 | 16 | import java.lang.ref.WeakReference; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | import javax.inject.Inject; 21 | 22 | import co.lateralview.myapp.R; 23 | import co.lateralview.myapp.application.AppComponent; 24 | import co.lateralview.myapp.application.MyApp; 25 | import co.lateralview.myapp.domain.util.SnackBarData; 26 | import co.lateralview.myapp.infraestructure.manager.InternetManager; 27 | import co.lateralview.myapp.ui.activities.base.fragments.BaseFragment; 28 | import co.lateralview.myapp.ui.broadcast.InternetReceiver; 29 | import co.lateralview.myapp.ui.util.SnackBarHelper; 30 | import co.lateralview.myapp.ui.util.ToolbarUtils; 31 | 32 | 33 | public abstract class BaseActivity 34 | extends AppCompatActivity 35 | implements InternetReceiver.InternetReceiverListener, Base.View { 36 | 37 | public static final String TAG = "BaseActivity"; 38 | 39 | private static boolean isAppVisible = false; 40 | 41 | @Inject 42 | protected InternetManager mInternetManager; 43 | 44 | private ProgressDialog mProgressDialog; 45 | private InternetReceiver mInternetReceiver; 46 | private IntentFilter mInternetStatusChangedFilter; 47 | private boolean mProcessInternetChangeReceiver = false; 48 | 49 | private Snackbar mNoInternetSnackbar; 50 | private Snackbar mConnectionErrorSnackbar; 51 | 52 | private List> mFragments = new ArrayList<>(); 53 | 54 | protected static Intent newActivityInstance(Context fromActivity, boolean clearStack, 55 | Class toActivity) { 56 | Intent intent = new Intent(fromActivity, toActivity); 57 | 58 | if (clearStack) { 59 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); 60 | } 61 | 62 | return intent; 63 | } 64 | 65 | public static AppComponent getAppComponent() { 66 | return MyApp.getAppComponent(); 67 | } 68 | 69 | public static boolean isAppVisible() { 70 | return isAppVisible; 71 | } 72 | 73 | @Override 74 | protected void onCreate(@Nullable Bundle savedInstanceState) { 75 | super.onCreate(savedInstanceState); 76 | 77 | MyApp.setCurrentScreenTag(getTAG()); 78 | 79 | enterTransition(); 80 | 81 | injectDependencies(); 82 | 83 | initInternetBroadcastReceiver(); 84 | } 85 | 86 | @Override 87 | protected void onResume() { 88 | super.onResume(); 89 | isAppVisible = true; 90 | } 91 | 92 | @Override 93 | protected void onPause() { 94 | isAppVisible = false; 95 | super.onPause(); 96 | } 97 | 98 | @Override 99 | protected void onStop() { 100 | unregisterReceiver(mInternetReceiver); 101 | 102 | super.onStop(); 103 | } 104 | 105 | @Override 106 | public boolean onOptionsItemSelected(MenuItem item) { 107 | switch (item.getItemId()) { 108 | case android.R.id.home: 109 | onBackPressed(); 110 | break; 111 | } 112 | 113 | return super.onOptionsItemSelected(item); 114 | } 115 | 116 | @Override 117 | protected void onStart() { 118 | super.onStart(); 119 | 120 | registerReceiver(mInternetReceiver, mInternetStatusChangedFilter); 121 | } 122 | 123 | @Override 124 | public void onAttachFragment(Fragment fragment) { 125 | mFragments.add(new WeakReference(fragment)); 126 | } 127 | 128 | @Override 129 | public void onInternetServiceEnabled() { 130 | if (mProcessInternetChangeReceiver) { 131 | if (mNoInternetSnackbar != null) { 132 | mNoInternetSnackbar.dismiss(); 133 | } 134 | 135 | } else { 136 | mProcessInternetChangeReceiver = true; 137 | } 138 | 139 | notifyFragmentsConnectivityChange(true); 140 | } 141 | 142 | @Override 143 | public void onInternetServiceDisabled() { 144 | if (!mProcessInternetChangeReceiver) { 145 | mProcessInternetChangeReceiver = true; 146 | } 147 | 148 | if (canShowInternetConnectionProblemsSnackbar()) { 149 | showInternetConnectionProblemsSnackbar(); 150 | } 151 | 152 | notifyFragmentsConnectivityChange(false); 153 | } 154 | 155 | @Override 156 | public void onBackPressed() { 157 | super.onBackPressed(); 158 | exitTransition(); 159 | } 160 | 161 | @Override 162 | public void showInternetRequiredError() { 163 | //TODO 164 | } 165 | 166 | @Override 167 | public void showUnexpectedErrorHappened() { 168 | //TODO 169 | } 170 | 171 | @Override 172 | public void logout() { 173 | //TODO 174 | } 175 | 176 | public void initializeToolbar(boolean backEnabled) { 177 | initializeToolbar(backEnabled, null); 178 | } 179 | 180 | public void initializeToolbar(boolean backEnabled, @Nullable int title) { 181 | initializeToolbar(backEnabled, getString(title)); 182 | } 183 | 184 | public void initializeToolbar(boolean backEnabled, @Nullable String title) { 185 | ToolbarUtils.initializeToolbar(this, backEnabled, title); 186 | } 187 | 188 | public void setActionBarVisibility(int visibility) { 189 | ToolbarUtils.setActionBarVisibility(this, visibility); 190 | } 191 | 192 | public void setToolbarTitle(String title) { 193 | ToolbarUtils.setToolbarTitle(this, title); 194 | } 195 | 196 | protected void injectDependencies() { 197 | DaggerBaseComponent.builder() 198 | .appComponent(getAppComponent()) 199 | .baseViewModule(getBaseActivityModule()) 200 | .build() 201 | .inject(this); 202 | } 203 | 204 | public Base.BaseViewModule getBaseActivityModule() { 205 | return new Base.BaseViewModule(this); 206 | } 207 | 208 | private void initInternetBroadcastReceiver() { 209 | mInternetReceiver = new InternetReceiver(this, mInternetManager); 210 | mInternetStatusChangedFilter = mInternetReceiver.getIntentFilter(); 211 | } 212 | 213 | private void notifyFragmentsConnectivityChange(boolean enabled) { 214 | for (WeakReference fragmentWeakReference : mFragments) { 215 | Fragment fragment = fragmentWeakReference.get(); 216 | 217 | if (fragment != null && fragment instanceof BaseFragment) { 218 | BaseFragment baseFragment = (BaseFragment) fragment; 219 | 220 | if (enabled) { 221 | baseFragment.onInternetServiceEnabled(); 222 | } else { 223 | baseFragment.onInternetServiceDisabled(); 224 | } 225 | } 226 | } 227 | } 228 | 229 | public void showProgressDialog(String message) { 230 | if (mProgressDialog == null) { 231 | mProgressDialog = new ProgressDialog(this); 232 | mProgressDialog.setCancelable(false); 233 | } 234 | 235 | if (!mProgressDialog.isShowing()) { 236 | mProgressDialog.show(); 237 | mProgressDialog.getWindow().setBackgroundDrawable( 238 | new ColorDrawable(android.graphics.Color.TRANSPARENT)); 239 | mProgressDialog.setContentView(R.layout.layout_progress_dialog); 240 | 241 | if (message != null) { 242 | mProgressDialog.setMessage(message); 243 | } 244 | } 245 | } 246 | 247 | public void showProgressDialog() { 248 | showProgressDialog(null); 249 | } 250 | 251 | public void hideProgressDialog() { 252 | if (mProgressDialog != null && mProgressDialog.isShowing()) { 253 | mProgressDialog.dismiss(); 254 | } 255 | } 256 | 257 | public abstract String getTAG(); 258 | 259 | protected void showInternetConnectionProblemsSnackbar() { 260 | if (mNoInternetSnackbar == null) { 261 | mNoInternetSnackbar = SnackBarHelper.createSnackBar(this, 262 | new SnackBarData(SnackBarData.SnackBarType.NO_INTERNET, null)); 263 | } 264 | 265 | mNoInternetSnackbar.show(); 266 | } 267 | 268 | protected boolean canShowInternetConnectionProblemsSnackbar() { 269 | return true; 270 | } 271 | 272 | public void showComingSoonMessage() { 273 | Toast.makeText(this, getString(R.string.coming_soon), Toast.LENGTH_SHORT).show(); 274 | } 275 | 276 | protected void enterTransition() { 277 | /*override method on child's*/ 278 | } 279 | 280 | protected void exitTransition() { 281 | /*override method on child's*/ 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/infraestructure/networking/RetrofitManager.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.infraestructure.networking; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.facebook.stetho.Stetho; 7 | import com.facebook.stetho.okhttp3.StethoInterceptor; 8 | import com.google.gson.Gson; 9 | import com.readystatesoftware.chuck.ChuckInterceptor; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | import co.lateralview.myapp.domain.repository.interfaces.SessionRepository; 16 | import co.lateralview.myapp.infraestructure.manager.InternetManager; 17 | import okhttp3.Cache; 18 | import okhttp3.CacheControl; 19 | import okhttp3.Interceptor; 20 | import okhttp3.OkHttpClient; 21 | import okhttp3.Request; 22 | import okhttp3.Response; 23 | import okhttp3.logging.HttpLoggingInterceptor; 24 | import okhttp3.logging.HttpLoggingInterceptor.Level; 25 | import retrofit2.Retrofit; 26 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; 27 | import retrofit2.converter.gson.GsonConverterFactory; 28 | 29 | public class RetrofitManager { 30 | public static final String TAG = "RetrofitManager"; 31 | 32 | public static final String HEADER_CACHE_CONTROL = "Cache-Control"; 33 | public static final String HEADER_PRAGMA = "Pragma"; 34 | 35 | private Application mApplication; 36 | private Gson mGson; 37 | private Retrofit mCustomRetrofit; 38 | private Retrofit mCustomCachedRetrofit; 39 | private Retrofit mRetrofit; 40 | private OkHttpClient mCustomOkHttpClient; 41 | private OkHttpClient mCustomCachedOkHttpClient; 42 | private OkHttpClient mDefaultOkHttpClient; 43 | private Cache mCache; 44 | private SessionRepository mSessionRepository; 45 | private InternetManager mInternetManager; 46 | 47 | public RetrofitManager(Application application, Gson gson, SessionRepository sessionRepository, 48 | InternetManager internetManager) { 49 | mApplication = application; 50 | mGson = gson; 51 | mSessionRepository = sessionRepository; 52 | mInternetManager = internetManager; 53 | 54 | Stetho.initializeWithDefaults(mApplication); 55 | } 56 | 57 | /** 58 | * Returns a Custom Retrofit instance. 59 | */ 60 | public Retrofit getCustomRetrofit() { 61 | if (mCustomRetrofit == null) { 62 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); 63 | // set your desired log level 64 | logging.setLevel(Level.BODY); 65 | 66 | OkHttpClient.Builder httpClient = new OkHttpClient.Builder() 67 | .addInterceptor(logging) 68 | .addInterceptor(provideHeaderInterceptor()) 69 | .addInterceptor(provideOfflineCacheInterceptor()) 70 | .addInterceptor(new ChuckInterceptor(mApplication)) 71 | .addNetworkInterceptor(new StethoInterceptor()) 72 | .addNetworkInterceptor(provideCacheInterceptor()) 73 | .cache(provideCache()) 74 | .connectTimeout(1, TimeUnit.MINUTES) 75 | .readTimeout(1, TimeUnit.MINUTES); 76 | 77 | mCustomOkHttpClient = httpClient.build(); 78 | 79 | mCustomRetrofit = new Retrofit.Builder() 80 | .baseUrl(RestConstants.BASE_URL) 81 | .addConverterFactory(GsonConverterFactory.create(mGson)) 82 | .addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create()) 83 | .client(mCustomOkHttpClient) 84 | .build(); 85 | } 86 | 87 | return mCustomRetrofit; 88 | } 89 | 90 | /** 91 | * Returns a Custom Retrofit instance which only checks on Cache. 92 | */ 93 | public Retrofit getCustomCachedRetrofit() { 94 | if (mCustomCachedRetrofit == null) { 95 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); 96 | // set your desired log level 97 | logging.setLevel(Level.BODY); 98 | 99 | OkHttpClient.Builder httpClient = new OkHttpClient.Builder() 100 | .addInterceptor(logging) 101 | .addInterceptor(provideForcedOfflineCacheInterceptor()) 102 | .addNetworkInterceptor(new StethoInterceptor()) 103 | .cache(provideCache()) 104 | .connectTimeout(1, TimeUnit.MINUTES) 105 | .readTimeout(1, TimeUnit.MINUTES); 106 | 107 | mCustomCachedOkHttpClient = httpClient.build(); 108 | 109 | mCustomCachedRetrofit = new Retrofit.Builder() 110 | .baseUrl(RestConstants.BASE_URL) 111 | .addConverterFactory(GsonConverterFactory.create(mGson)) 112 | .addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create()) 113 | .client(mCustomCachedOkHttpClient) 114 | .build(); 115 | } 116 | 117 | return mCustomCachedRetrofit; 118 | } 119 | 120 | 121 | /** 122 | * Returns a Clean Retrofit instance. 123 | */ 124 | public Retrofit getRetrofit() { 125 | if (mRetrofit == null) { 126 | HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); 127 | // set your desired log level 128 | logging.setLevel(Level.BODY); 129 | 130 | OkHttpClient.Builder httpClient = new OkHttpClient.Builder() 131 | .addInterceptor(logging) 132 | .addInterceptor(provideOfflineCacheInterceptor()) 133 | .addNetworkInterceptor(new StethoInterceptor()) 134 | .addNetworkInterceptor(provideCacheInterceptor()) 135 | .cache(provideCache()); 136 | 137 | mDefaultOkHttpClient = httpClient.build(); 138 | 139 | mRetrofit = new Retrofit.Builder() 140 | .baseUrl(RestConstants.BASE_URL) 141 | .addConverterFactory(GsonConverterFactory.create(mGson)) 142 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 143 | .client(mDefaultOkHttpClient) 144 | .build(); 145 | } 146 | 147 | return mRetrofit; 148 | } 149 | 150 | private Cache provideCache() { 151 | if (mCache == null) { 152 | try { 153 | mCache = new Cache(new File(mApplication.getCacheDir(), "http-cache"), 154 | 10 * 1024 * 1024); // 10 MB 155 | } catch (Exception e) { 156 | Log.e(TAG, "Could not create Cache!"); 157 | } 158 | } 159 | 160 | return mCache; 161 | } 162 | 163 | private Interceptor provideCacheInterceptor() { 164 | return chain -> { 165 | Response response = chain.proceed(chain.request()); 166 | 167 | CacheControl cacheControl; 168 | 169 | if (mInternetManager.isOnline()) { 170 | cacheControl = new CacheControl.Builder() 171 | .maxAge(0, TimeUnit.SECONDS) 172 | .build(); 173 | } else { 174 | cacheControl = new CacheControl.Builder() 175 | .maxStale(7, TimeUnit.DAYS) 176 | .build(); 177 | } 178 | 179 | return response.newBuilder() 180 | .removeHeader(HEADER_PRAGMA) 181 | .removeHeader(HEADER_CACHE_CONTROL) 182 | .header(HEADER_CACHE_CONTROL, cacheControl.toString()) 183 | .build(); 184 | 185 | }; 186 | } 187 | 188 | private Interceptor provideOfflineCacheInterceptor() { 189 | return chain -> { 190 | Request request = chain.request(); 191 | 192 | if (!mInternetManager.isOnline()) { 193 | CacheControl cacheControl = new CacheControl.Builder() 194 | .maxStale(7, TimeUnit.DAYS) 195 | .build(); 196 | 197 | request = request.newBuilder() 198 | .removeHeader(HEADER_PRAGMA) 199 | .removeHeader(HEADER_CACHE_CONTROL) 200 | .cacheControl(cacheControl) 201 | .build(); 202 | } 203 | 204 | return chain.proceed(request); 205 | }; 206 | } 207 | 208 | private Interceptor provideForcedOfflineCacheInterceptor() { 209 | return chain -> { 210 | Request request = chain.request(); 211 | 212 | CacheControl cacheControl = new CacheControl.Builder() 213 | .maxStale(7, TimeUnit.DAYS) 214 | .build(); 215 | 216 | request = request.newBuilder() 217 | .removeHeader(HEADER_PRAGMA) 218 | .removeHeader(HEADER_CACHE_CONTROL) 219 | .cacheControl(cacheControl) 220 | .build(); 221 | 222 | return chain.proceed(request); 223 | }; 224 | } 225 | 226 | public void clean() { 227 | 228 | if (mCustomOkHttpClient != null) { 229 | // Cancel Pending Request 230 | mCustomOkHttpClient.dispatcher().cancelAll(); 231 | } 232 | 233 | if (mDefaultOkHttpClient != null) { 234 | // Cancel Pending Request 235 | mDefaultOkHttpClient.dispatcher().cancelAll(); 236 | } 237 | 238 | mCustomRetrofit = null; 239 | mRetrofit = null; 240 | 241 | if (mCache != null) { 242 | try { 243 | mCache.evictAll(); 244 | } catch (IOException e) { 245 | Log.e(TAG, "Error cleaning http cache"); 246 | } 247 | } 248 | 249 | mCache = null; 250 | } 251 | 252 | private Interceptor provideHeaderInterceptor() { 253 | return chain -> { 254 | Request request = chain.request(); 255 | 256 | String accessToken = mSessionRepository.getAccessToken().blockingGet(); 257 | 258 | Request.Builder requestBuilder = request.newBuilder() 259 | .header(RestConstants.HEADER_AUTH, 260 | accessToken != null ? accessToken : ""); 261 | 262 | request = requestBuilder.build(); 263 | 264 | return chain.proceed(request); 265 | }; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /app/src/main/java/co/lateralview/myapp/ui/util/UIUtils.java: -------------------------------------------------------------------------------- 1 | package co.lateralview.myapp.ui.util; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.res.Resources; 7 | import android.graphics.Bitmap; 8 | import android.graphics.BitmapFactory; 9 | import android.graphics.Color; 10 | import android.graphics.drawable.ColorDrawable; 11 | import android.graphics.drawable.Drawable; 12 | import android.graphics.drawable.GradientDrawable; 13 | import android.os.Build; 14 | import android.support.v4.content.ContextCompat; 15 | import android.text.Spannable; 16 | import android.text.SpannableString; 17 | import android.text.style.TextAppearanceSpan; 18 | import android.util.DisplayMetrics; 19 | import android.util.TypedValue; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | import android.view.Window; 23 | import android.view.WindowManager; 24 | import android.widget.CheckBox; 25 | import android.widget.NumberPicker; 26 | 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.lang.reflect.Field; 30 | import java.net.HttpURLConnection; 31 | import java.net.URL; 32 | 33 | public abstract class UIUtils { 34 | public static void setStatusBarColor(Activity activity, int color) { 35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 36 | Window window = activity.getWindow(); 37 | 38 | //clear FLAG_TRANSLUCENT_STATUS flag: 39 | window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 40 | 41 | // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window 42 | window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); 43 | 44 | // finally change the color 45 | window.setStatusBarColor(color); 46 | } 47 | } 48 | 49 | public static void setGroupViewTreeEnable(ViewGroup view, boolean enable) { 50 | view.setEnabled(enable); 51 | for (int i = 0; i < view.getChildCount(); i++) { 52 | View child = view.getChildAt(i); 53 | child.setEnabled(enable); 54 | if (child instanceof ViewGroup && ((ViewGroup) child).getChildCount() > 0) { 55 | setGroupViewTreeEnable((ViewGroup) child, enable); 56 | } else { 57 | child.setEnabled(enable); 58 | } 59 | } 60 | } 61 | 62 | public static void setGroupViewTreeActived(ViewGroup view, boolean actived) { 63 | view.setActivated(actived); 64 | for (int i = 0; i < view.getChildCount(); i++) { 65 | View child = view.getChildAt(i); 66 | child.setActivated(actived); 67 | if (child instanceof ViewGroup && ((ViewGroup) child).getChildCount() > 0) { 68 | setGroupViewTreeActived((ViewGroup) child, actived); 69 | } else { 70 | child.setActivated(actived); 71 | } 72 | } 73 | } 74 | 75 | public static void setGroupViewTreeSelected(ViewGroup view, boolean selected) { 76 | view.setSelected(selected); 77 | for (int i = 0; i < view.getChildCount(); i++) { 78 | View child = view.getChildAt(i); 79 | child.setSelected(selected); 80 | if (child instanceof ViewGroup && ((ViewGroup) child).getChildCount() > 0) { 81 | setGroupViewTreeSelected((ViewGroup) child, selected); 82 | } else { 83 | child.setSelected(selected); 84 | } 85 | } 86 | } 87 | 88 | public static void setGroupViewTreeChecked(ViewGroup view, boolean checked) { 89 | for (int i = 0; i < view.getChildCount(); i++) { 90 | View child = view.getChildAt(i); 91 | child.setSelected(checked); 92 | if (child instanceof ViewGroup && ((ViewGroup) child).getChildCount() > 0) { 93 | setGroupViewTreeChecked((ViewGroup) child, checked); 94 | } else { 95 | if (child instanceof CheckBox) { 96 | ((CheckBox) child).setChecked(checked); 97 | } 98 | } 99 | } 100 | } 101 | 102 | public static Drawable getDrawable(Context context, int id) { 103 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 104 | return context.getResources().getDrawable(id, context.getTheme()); 105 | } else { 106 | return context.getResources().getDrawable(id); 107 | } 108 | } 109 | 110 | public static int convertDipToPixels(Context context, int dip) { 111 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, 112 | context.getResources().getDisplayMetrics()); 113 | } 114 | 115 | public static void setBackground(View view, Context context, int id) { 116 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 117 | view.setBackground(ContextCompat.getDrawable(context, id)); 118 | } else { 119 | view.setBackgroundDrawable(ContextCompat.getDrawable(context, id)); 120 | } 121 | } 122 | 123 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 124 | public static void setGradientBackground(View view, String color1, String color2) { 125 | setGradientBackground(view, Color.parseColor(color1), Color.parseColor(color2)); 126 | } 127 | 128 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 129 | public static void setGradientBackground(View view, Integer color1, Integer color2) { 130 | GradientDrawable gd = new GradientDrawable( 131 | GradientDrawable.Orientation.LEFT_RIGHT, 132 | new int[]{color1, color2}); 133 | 134 | gd.setCornerRadius(0f); 135 | 136 | gd.setAlpha(204); 137 | 138 | view.setBackground(gd); 139 | } 140 | 141 | public static int getColorWithAlpha(int color, float ratio) { 142 | int newColor; 143 | int alpha = Math.round(Color.alpha(color) * ratio); 144 | int r = Color.red(color); 145 | int g = Color.green(color); 146 | int b = Color.blue(color); 147 | newColor = Color.argb(alpha, r, g, b); 148 | return newColor; 149 | } 150 | 151 | public static SpannableString setTextWithMultipleStyles(Context context, int[] styles, 152 | String[] texts) { 153 | if (styles == null || texts == null || styles.length != texts.length) { 154 | return null; 155 | } 156 | 157 | String allText = null; 158 | 159 | for (String text : texts) { 160 | allText = allText == null ? text : allText + " " + text; 161 | } 162 | 163 | SpannableString spannableString = new SpannableString(allText); 164 | 165 | int startIndex = 0; 166 | 167 | for (int i = 0; i < texts.length; i++) { 168 | spannableString.setSpan(new TextAppearanceSpan(context, styles[i]), startIndex, 169 | startIndex + texts[i].length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 170 | startIndex = startIndex + texts[i].length() + 1; // +1 for the with space 171 | } 172 | 173 | return spannableString; 174 | } 175 | 176 | public static int getColorFromRes(Context context, int resColor) { 177 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 178 | return context.getColor(resColor); 179 | } else { 180 | return context.getResources().getColor(resColor); 181 | } 182 | } 183 | 184 | public static Drawable getDrawableFromRes(Context context, int resDrawable) { 185 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 186 | return context.getDrawable(resDrawable); 187 | } else { 188 | return context.getResources().getDrawable(resDrawable); 189 | } 190 | } 191 | 192 | public static Bitmap getBitmapFromURL(String src) { 193 | try { 194 | URL url = new URL(src); 195 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 196 | connection.setDoInput(true); 197 | connection.connect(); 198 | InputStream input = connection.getInputStream(); 199 | Bitmap myBitmap = BitmapFactory.decodeStream(input); 200 | return myBitmap; 201 | } catch (IOException e) { 202 | // Log exception 203 | return null; 204 | } 205 | } 206 | 207 | public static void setNumberPickerDividerColor(NumberPicker picker, int color) { 208 | Field[] pickerFields = NumberPicker.class.getDeclaredFields(); 209 | for (Field pf : pickerFields) { 210 | if (pf.getName().equals("mSelectionDivider")) { 211 | pf.setAccessible(true); 212 | try { 213 | ColorDrawable colorDrawable = new ColorDrawable(color); 214 | pf.set(picker, colorDrawable); 215 | } catch (IllegalArgumentException e) { 216 | e.printStackTrace(); 217 | } catch (Resources.NotFoundException e) { 218 | e.printStackTrace(); 219 | } catch (IllegalAccessException e) { 220 | e.printStackTrace(); 221 | } 222 | break; 223 | } 224 | } 225 | } 226 | 227 | public static String getDensityString(Context context) { 228 | int densityDpi = context.getResources().getDisplayMetrics().densityDpi; 229 | 230 | if (densityDpi >= DisplayMetrics.DENSITY_LOW 231 | && densityDpi < DisplayMetrics.DENSITY_HIGH) { 232 | return "mdpi"; 233 | } 234 | 235 | if (densityDpi >= DisplayMetrics.DENSITY_HIGH 236 | && densityDpi < DisplayMetrics.DENSITY_XHIGH) { 237 | return "hdpi"; 238 | } 239 | 240 | if (densityDpi >= DisplayMetrics.DENSITY_XHIGH 241 | && densityDpi < DisplayMetrics.DENSITY_XXHIGH) { 242 | return "xhdpi"; 243 | } 244 | 245 | if (densityDpi >= DisplayMetrics.DENSITY_XXHIGH 246 | && densityDpi < DisplayMetrics.DENSITY_XXXHIGH) { 247 | return "xxhdpi"; 248 | } 249 | 250 | return "xxxhdpi"; 251 | } 252 | } 253 | --------------------------------------------------------------------------------