├── 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 |
--------------------------------------------------------------------------------