diff --git a/build.gradle b/build.gradle index bb801dcb..a3174c84 100755 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ buildscript { classpath 'me.tatarka:gradle-retrolambda:3.2.3' classpath 'io.fabric.tools:gradle:1.25.4' classpath 'com.google.gms:google-services:4.0.1' + } } @@ -37,9 +38,9 @@ allprojects { jcenter() } ext { - androidVersionCode = 4 - androidVersionName = "0.1.0" - androidBuildNumber = 2 + androidVersionCode = 14 + androidVersionName = "0.4.0" + androidBuildNumber = 1 testInstrumentationRunner = "android.support.test.runner.AndroidJUnitRunner" testApplicationId = 'io.forus.me.test' } diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index 8206a392..13e7471e 100755 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -8,7 +8,7 @@ ext { //Android androidBuildToolsVersion = "28.0.3" androidMinSdkVersion = 18 - androidTargetSdkVersion = 26 + androidTargetSdkVersion = 28 androidCompileSdkVersion = 28 //Libraries @@ -25,6 +25,8 @@ ext { circleIndicatorVersion = '1.2.2@aar' materialDialogs = '0.9.6.0' gooleMapsVersion = '16.0.0' + playCoreVersion = '1.7.2' + nav_version = '1.0.0' //Testing robolectricVersion = '3.1.1' @@ -53,6 +55,7 @@ ext { firebaseCoreVersion='16.0.1' firebaseCloudMessageVersion='17.3.3' + //Development leakCanaryVersion = '1.3.1' qrReaderVersion='2.0.3@aar' @@ -97,7 +100,10 @@ ext { fabric: "com.crashlytics.sdk.android:crashlytics:${rootProject.fabric}", firebaseCore: "com.google.firebase:firebase-core:${rootProject.firebaseCoreVersion}", firebaseMessaging: "com.google.firebase:firebase-messaging:${rootProject.firebaseCloudMessageVersion}", - googleMaps: "com.google.android.gms:play-services-maps:${rootProject.gooleMapsVersion}" + googleMaps: "com.google.android.gms:play-services-maps:${rootProject.gooleMapsVersion}", + playCore: "com.google.android.play:core:${rootProject.playCoreVersion}", + navigationFragment: "android.arch.navigation:navigation-fragment-ktx:${nav_version}", + navigationUI: "android.arch.navigation:navigation-ui-ktx:${nav_version}" ] diff --git a/data/src/main/java/io/forus/me/android/data/entity/records/request/ValidateRecord.java b/data/src/main/java/io/forus/me/android/data/entity/records/request/ValidateRecord.java new file mode 100644 index 00000000..e2d0b3fe --- /dev/null +++ b/data/src/main/java/io/forus/me/android/data/entity/records/request/ValidateRecord.java @@ -0,0 +1,21 @@ +package io.forus.me.android.data.entity.records.request; + +import com.google.gson.annotations.SerializedName; + +public class ValidateRecord { + + @SerializedName("organization_id") + private Long organization_id; + + public ValidateRecord(Long organization_id) { + this.organization_id = organization_id; + } + + public Long getOrganization_id() { + return organization_id; + } + + public void setOrganization_id(Long organization_id) { + this.organization_id = organization_id; + } +} diff --git a/data/src/main/java/io/forus/me/android/data/entity/records/response/Validation.java b/data/src/main/java/io/forus/me/android/data/entity/records/response/Validation.java index b6aac17f..88c26b59 100644 --- a/data/src/main/java/io/forus/me/android/data/entity/records/response/Validation.java +++ b/data/src/main/java/io/forus/me/android/data/entity/records/response/Validation.java @@ -3,6 +3,7 @@ import com.google.gson.annotations.SerializedName; import java.util.Date; +import java.util.List; import io.forus.me.android.data.entity.validators.response.Organization; @@ -39,9 +40,13 @@ public enum State { @SerializedName("organization") private Organization organization; + @SerializedName("organizations_available") + private List organizationsAvailable; + public Validation() { } - public Validation(State state, String identityAddress, Date createdAt, Date updatedAt, String uuid, String value, String key, String name, Organization organization) { + public Validation(State state, String identityAddress, Date createdAt, Date updatedAt, String uuid, String value, String key, String name, Organization organization/*, + ValidatorOrganization organizationsAvailable*/) { this.state = state; this.identityAddress = identityAddress; this.createdAt = createdAt; @@ -51,6 +56,7 @@ public Validation(State state, String identityAddress, Date createdAt, Date upda this.key = key; this.name = name; this.organization = organization; + // this.organizationsAvailable = organizationsAvailable; } public State getState() { @@ -124,4 +130,9 @@ public Organization getOrganization() { public void setOrganization(Organization organization) { this.organization = organization; } + + + public List getValidators() { + return organizationsAvailable; + } } diff --git a/data/src/main/java/io/forus/me/android/data/entity/records/response/ValidatorOrganization.java b/data/src/main/java/io/forus/me/android/data/entity/records/response/ValidatorOrganization.java new file mode 100644 index 00000000..7c47bc0d --- /dev/null +++ b/data/src/main/java/io/forus/me/android/data/entity/records/response/ValidatorOrganization.java @@ -0,0 +1,37 @@ +package io.forus.me.android.data.entity.records.response; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by maestrovs on 29.04.2020. + */ +public class ValidatorOrganization { + + @SerializedName("id") + private Long id; + + @SerializedName("name") + private String name; + + + public ValidatorOrganization(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/data/src/main/java/io/forus/me/android/data/entity/vouchers/response/Voucher.java b/data/src/main/java/io/forus/me/android/data/entity/vouchers/response/Voucher.java index 088a4459..4a4fbe82 100644 --- a/data/src/main/java/io/forus/me/android/data/entity/vouchers/response/Voucher.java +++ b/data/src/main/java/io/forus/me/android/data/entity/vouchers/response/Voucher.java @@ -1,5 +1,7 @@ package io.forus.me.android.data.entity.vouchers.response; +import android.util.Log; + import com.google.gson.annotations.SerializedName; import java.math.BigDecimal; @@ -27,6 +29,9 @@ public enum Type { @SerializedName("created_at_locale") private String createdAtLocale; + @SerializedName("expired") + private boolean expired; + @SerializedName("expire_at_locale") private String expireAtLocale; @@ -65,12 +70,16 @@ public enum Type { public Voucher() { } - public Voucher(Long fundId, String identityAddress, String address, Date createdAt, String createdAtLocale, String expireAtLocale, Long timestamp, Type type, Product product, BigDecimal amount, Fund fund, List transactions, List allowedOrganizations, List allowedProductCategories, List allowedProducts, List childVouchers, List offices) { + public Voucher(Long fundId, String identityAddress, String address, Date createdAt, String createdAtLocale, + boolean expired, String expireAtLocale, Long timestamp, Type type, Product product, BigDecimal amount, + Fund fund, List transactions, List allowedOrganizations, List allowedProductCategories, + List allowedProducts, List childVouchers, List offices) { this.fundId = fundId; this.identityAddress = identityAddress; this.address = address; this.createdAt = createdAt; this.createdAtLocale = createdAtLocale; + this.expired = expired; this.expireAtLocale = expireAtLocale; this.timestamp = timestamp; this.type = type; @@ -126,6 +135,8 @@ public void setCreatedAtLocale(String createdAtLocale) { } public String getExpireAtLocale() { + + Log.d("forus","expire_at_locate="+expireAtLocale); return expireAtLocale; } @@ -220,4 +231,12 @@ public List getOffices() { public void setOffices(List offices) { this.offices = offices; } + + public boolean isExpired() { + return expired; + } + + public void setExpired(boolean expired) { + this.expired = expired; + } } diff --git a/data/src/main/java/io/forus/me/android/data/net/MeHttpLoggingInterceptor.java b/data/src/main/java/io/forus/me/android/data/net/MeHttpLoggingInterceptor.java new file mode 100644 index 00000000..71686c04 --- /dev/null +++ b/data/src/main/java/io/forus/me/android/data/net/MeHttpLoggingInterceptor.java @@ -0,0 +1,233 @@ +package io.forus.me.android.data.net; + +import android.util.Log; + +import java.io.EOFException; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.concurrent.TimeUnit; + +import okhttp3.Connection; +import okhttp3.Headers; +import okhttp3.Interceptor; +import okhttp3.MediaType; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okhttp3.internal.http.HttpHeaders; +import okhttp3.logging.HttpLoggingInterceptor; +import okio.Buffer; +import okio.BufferedSource; + +public class MeHttpLoggingInterceptor implements Interceptor { + + private final HttpLoggingInterceptor.Logger logger; + + private volatile HttpLoggingInterceptor.Level level = HttpLoggingInterceptor.Level.BODY; + + private static final Charset UTF8 = Charset.forName("UTF-8"); + + + public MeHttpLoggingInterceptor(HttpLoggingInterceptor.Logger logger) { + this.logger = logger; + } + + public MeHttpLoggingInterceptor setLevel(HttpLoggingInterceptor.Level level) { + if (level == null) throw new NullPointerException("level == null. Use Level.NONE instead."); + this.level = level; + return this; + } + + public HttpLoggingInterceptor.Level getLevel() { + return level; + } + + private boolean logAccessToken = true; + + public boolean isLogAccessToken() { + return logAccessToken; + } + + public void setLogAccessToken(boolean logAccessToken) { + this.logAccessToken = logAccessToken; + } + + @Override + public Response intercept(Chain chain) throws IOException { + + HttpLoggingInterceptor.Level level = this.level; + Request request = chain.request(); + /*if (level == HttpLoggingInterceptor.Level.BODY) { + Log.d("forus","INTERCEPT_003"); + return chain.proceed(request); + }*/ + + + boolean logBody = level == HttpLoggingInterceptor.Level.BODY; + boolean logHeaders = logBody || level == HttpLoggingInterceptor.Level.HEADERS; + + RequestBody requestBody = request.body(); + boolean hasRequestBody = requestBody != null; + + Connection connection = chain.connection(); + Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1; + String requestStartMessage = "--> " + request.method() + ' ' + request.url() + ' ' + protocol; + + if (!logHeaders && hasRequestBody) { + requestStartMessage += " (" + requestBody.contentLength() + "-byte body)"; + } + logger.log(requestStartMessage); + + if (logHeaders) { + if (hasRequestBody) { + // Request body headers are only present when installed as a network interceptor. Force + // them to be included (when available) so there values are known. + if (requestBody.contentType() != null) { + logger.log("Content-Type: " + requestBody.contentType()); + } + if (requestBody.contentLength() != -1) { + logger.log("Content-Length: " + requestBody.contentLength()); + } + } + + Headers headers = request.headers(); + for (int i = 0, count = headers.size(); i < count; i++) { + String name = headers.name(i); + // Skip headers from the request body as they are explicitly logged above. + if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name) ) { + if(canLog(name) && canLog(headers.value(i))) { + logger.log(name + ": " + headers.value(i)); + } + } + } + + if (!logBody || !hasRequestBody) { + logger.log("--> END " + request.method()); + } else if (bodyEncoded(request.headers())) { + logger.log("--> END " + request.method() + " (encoded body omitted)"); + } else { + Buffer buffer = new Buffer(); + requestBody.writeTo(buffer); + + Charset charset = UTF8; + MediaType contentType = requestBody.contentType(); + if (contentType != null) { + charset = contentType.charset(UTF8); + } + + logger.log(""); + if (isPlaintext(buffer)) { + String str = buffer.readString(charset); + if(canLog(str)) { + logger.log(str); + } + logger.log("--> END " + request.method() + + " (" + requestBody.contentLength() + "-byte body)"); + } else { + logger.log("--> END " + request.method() + " (binary " + + requestBody.contentLength() + "-byte body omitted)"); + } + } + } + + long startNs = System.nanoTime(); + Response response; + try { + response = chain.proceed(request); + } catch (Exception e) { + logger.log("<-- HTTP FAILED: " + e); + throw e; + } + long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs); + + ResponseBody responseBody = response.body(); + long contentLength = responseBody.contentLength(); + String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length"; + if(canLog(response.message())) { + logger.log("<-- " + response.code() + ' ' + response.message() + ' ' + + response.request().url() + " (" + tookMs + "ms" + (!logHeaders ? ", " + + bodySize + " body" : "") + ')'); + } + + if (logHeaders) { + Headers headers = response.headers(); + for (int i = 0, count = headers.size(); i < count; i++) { + if(canLog(headers.name(i))&&canLog(headers.value(i))) + logger.log(headers.name(i) + ": " + headers.value(i)); + } + + if (!logBody || !HttpHeaders.hasBody(response)) { + logger.log("<-- END HTTP"); + } else if (bodyEncoded(response.headers())) { + logger.log("<-- END HTTP (encoded body omitted)"); + } else { + BufferedSource source = responseBody.source(); + source.request(Long.MAX_VALUE); // Buffer the entire body. + Buffer buffer = source.buffer(); + + Charset charset = UTF8; + MediaType contentType = responseBody.contentType(); + if (contentType != null) { + charset = contentType.charset(UTF8); + } + + if (!isPlaintext(buffer)) { + logger.log(""); + logger.log("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)"); + return response; + } + + if (contentLength != 0) { + logger.log(""); + String str = buffer.clone().readString(charset); + if(canLog(str)) { + logger.log(buffer.clone().readString(charset)); + } + } + + logger.log("<-- END HTTP (" + buffer.size() + "-byte body)"); + } + } + + return response; + } + + private boolean canLog(String str){ + if(str.contains("Access-Token") || str.contains("access_token")){ + return logAccessToken; + }else{ + return true; + } + } + + /** + * Returns true if the body in question probably contains human readable text. Uses a small sample + * of code points to detect unicode control characters commonly used in binary file signatures. + */ + private static boolean isPlaintext(Buffer buffer) { + try { + Buffer prefix = new Buffer(); + long byteCount = buffer.size() < 64 ? buffer.size() : 64; + buffer.copyTo(prefix, 0, byteCount); + for (int i = 0; i < 16; i++) { + if (prefix.exhausted()) { + break; + } + int codePoint = prefix.readUtf8CodePoint(); + if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) { + return false; + } + } + return true; + } catch (EOFException e) { + return false; // Truncated UTF-8 sequence. + } + } + + private boolean bodyEncoded(Headers headers) { + String contentEncoding = headers.get("Content-Encoding"); + return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity"); + } +} diff --git a/data/src/main/java/io/forus/me/android/data/net/MeServiceFactory.java b/data/src/main/java/io/forus/me/android/data/net/MeServiceFactory.java index 750946f7..d7710712 100755 --- a/data/src/main/java/io/forus/me/android/data/net/MeServiceFactory.java +++ b/data/src/main/java/io/forus/me/android/data/net/MeServiceFactory.java @@ -46,16 +46,17 @@ public static void init(Context context, AccountLocalDataSource accountLocalData } - public T createRetrofitService(final Class clazz, final String endPoint){ + public T createRetrofitService(final Class clazz, final String endPoint) throws Exception { return createRetrofitService(clazz, endPoint, null); } - public T createRetrofitService(final Class clazz, final String endPoint, final String customAccessToken) { + public T createRetrofitService(final Class clazz, final String endPoint, final String customAccessToken) throws Exception{ okhttp3.OkHttpClient.Builder httpClient = new okhttp3.OkHttpClient.Builder(); - HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); + MeHttpLoggingInterceptor logging = new MeHttpLoggingInterceptor(HttpLoggingInterceptor.Logger.DEFAULT); logging.setLevel(HttpLoggingInterceptor.Level.BODY); + logging.setLogAccessToken(false); httpClient.addInterceptor(logging); httpClient.addInterceptor(chain -> { diff --git a/data/src/main/java/io/forus/me/android/data/net/common/CommonService.kt b/data/src/main/java/io/forus/me/android/data/net/common/CommonService.kt new file mode 100644 index 00000000..0b22658f --- /dev/null +++ b/data/src/main/java/io/forus/me/android/data/net/common/CommonService.kt @@ -0,0 +1,22 @@ +package io.forus.me.android.data.net.common + +import io.forus.me.android.data.entity.common.Success +import io.forus.me.android.data.entity.records.request.* +import io.forus.me.android.data.entity.records.response.* +import io.reactivex.Observable +import okhttp3.ResponseBody +import retrofit2.http.* + +/** + * Created by maestrovs on 03.05.2020 + */ +interface CommonService { + + + + @GET("api/v1/status") + fun status() : Observable + + + +} \ No newline at end of file diff --git a/data/src/main/java/io/forus/me/android/data/net/records/RecordsService.kt b/data/src/main/java/io/forus/me/android/data/net/records/RecordsService.kt index a285a47f..67d0535e 100644 --- a/data/src/main/java/io/forus/me/android/data/net/records/RecordsService.kt +++ b/data/src/main/java/io/forus/me/android/data/net/records/RecordsService.kt @@ -43,6 +43,10 @@ interface RecordsService { @GET("api/v1/identity/records") fun listAllRecords(@Query("type") type: String?, @Query("record_category_id") categoryId: Long?) : Observable> + @GET("api/v1/identity/records?deleted=1") + fun listArchivedRecords() : Observable> + + @POST("api/v1/identity/records") fun createRecord(@Query("type") type: String, @Body createRecord: CreateRecord) : Observable @@ -68,7 +72,7 @@ interface RecordsService { fun readValidation(@Path("uuid") uuid: String) : Observable @PATCH("api/v1/identity/record-validations/{uuid}/approve") - fun approveValidation(@Path("uuid") uuid: String) : Observable + fun approveValidation(@Path("uuid") uuid: String,@Body validateRecord: ValidateRecord) : Observable @PATCH("api/v1/identity/record-validations/{uuid}/decline") fun declineValidation(@Path("uuid") uuid: String) : Observable diff --git a/data/src/main/java/io/forus/me/android/data/repository/common/CommonRepository.kt b/data/src/main/java/io/forus/me/android/data/repository/common/CommonRepository.kt new file mode 100644 index 00000000..d8fb42f5 --- /dev/null +++ b/data/src/main/java/io/forus/me/android/data/repository/common/CommonRepository.kt @@ -0,0 +1,17 @@ +package io.forus.me.android.data.repository.common + +import io.forus.me.android.data.repository.common.datasource.CommonDataSource +import io.forus.me.android.domain.repository.common.CommonRepository +import io.reactivex.Observable + + +class CommonRepository(private val commonDataSource: CommonDataSource) : CommonRepository { + + + override fun status(): Observable { + return commonDataSource.status() + .map { + it + } + } +} \ No newline at end of file diff --git a/data/src/main/java/io/forus/me/android/data/repository/common/datasource/CommonDataSource.kt b/data/src/main/java/io/forus/me/android/data/repository/common/datasource/CommonDataSource.kt new file mode 100644 index 00000000..8f18c5c9 --- /dev/null +++ b/data/src/main/java/io/forus/me/android/data/repository/common/datasource/CommonDataSource.kt @@ -0,0 +1,9 @@ +package io.forus.me.android.data.repository.common.datasource + +import com.gigawatt.android.data.net.sign.models.request.SignUp +import io.reactivex.Observable + +interface CommonDataSource { + + fun status(): Observable +} \ No newline at end of file diff --git a/data/src/main/java/io/forus/me/android/data/repository/common/datasource/CommonRemoteDataSource.kt b/data/src/main/java/io/forus/me/android/data/repository/common/datasource/CommonRemoteDataSource.kt new file mode 100644 index 00000000..a10dd40f --- /dev/null +++ b/data/src/main/java/io/forus/me/android/data/repository/common/datasource/CommonRemoteDataSource.kt @@ -0,0 +1,17 @@ +package io.forus.me.android.data.repository.common.datasource + +import io.forus.me.android.data.net.common.CommonService +import io.forus.me.android.data.repository.datasource.RemoteDataSource +import io.reactivex.Observable + +class CommonRemoteDataSource(f: () -> CommonService) : CommonDataSource, RemoteDataSource(f) { + override fun status(): Observable { + return service.status() + .map { + val result = it.string(); + result == "1"||result.isEmpty() + + } + } +} + diff --git a/data/src/main/java/io/forus/me/android/data/repository/records/RecordsRepository.kt b/data/src/main/java/io/forus/me/android/data/repository/records/RecordsRepository.kt index f877c602..e807ceda 100644 --- a/data/src/main/java/io/forus/me/android/data/repository/records/RecordsRepository.kt +++ b/data/src/main/java/io/forus/me/android/data/repository/records/RecordsRepository.kt @@ -1,5 +1,8 @@ package io.forus.me.android.data.repository.records +import io.forus.me.android.data.entity.common.Success +import io.forus.me.android.data.entity.records.request.UpdateRecord +import io.forus.me.android.data.entity.records.request.ValidateRecord import io.forus.me.android.data.repository.records.datasource.RecordsDataSource import io.forus.me.android.domain.models.records.* import io.forus.me.android.domain.models.vouchers.Organization @@ -84,7 +87,52 @@ class RecordsRepository(private val recordsRemoteDataSource: RecordsDataSource) if(it.organization != null) organization = Organization(it.organization.id,it.organization.name,null,null,null, null,it.organization.phone,it.organization.email) - Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, it.uuid, it.value, it.key, it.name,organization ) + + val validatorsList: MutableList = mutableListOf() + if(it.validators != null && it.validators.size > 0){ + for (validator in it.validators){ + val validationOrg = ValidatorOrganization(validator.id, validator.name) + validatorsList.add(validationOrg) + } + } + + Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, + it.uuid, it.value, it.key, it.name,organization,validatorsList ) + }) + } + } + ).toObservable() + } + + override fun getRecordsArchived(): Observable> { + return Single.zip( + Single.fromObservable(getCategories()), + Single.fromObservable(getRecordTypes()), + Single.fromObservable(recordsRemoteDataSource.getRecordsArchived()), + Function3 { categories: List, types: List, records: List -> + records.map { + val type = types.find { type -> type.key.equals(it.key) } + var category: RecordCategory? = null + + if(it.recordCategoryId != null) category = categories.find { cat -> cat.id == it.recordCategoryId} + Record(it.id, it.value, it.order, type!!, category, it.valid ?: false, + it.validations.map { + + var organization: io.forus.me.android.domain.models.vouchers.Organization? = null + if(it.organization != null) + organization = Organization(it.organization.id,it.organization.name,null,null,null, + null,it.organization.phone,it.organization.email) + + val validatorsList: MutableList = mutableListOf() + if(it.validators != null && it.validators.size > 0){ + for (validator in it.validators){ + val validationOrg = ValidatorOrganization(validator.id, validator.name) + validatorsList.add(validationOrg) + } + } + + Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, + it.uuid, it.value, it.key, it.name,organization,validatorsList ) }) } } @@ -106,7 +154,18 @@ class RecordsRepository(private val recordsRemoteDataSource: RecordsDataSource) if(it.organization != null) organization = Organization(it.organization.id,it.organization.name,null,null,null, null,it.organization.phone,it.organization.email) - Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, it.uuid, it.value, it.key, it.name, organization) }) + + val validatorsList: MutableList = mutableListOf() + if(it.validators != null && it.validators.size > 0){ + for (validator in it.validators){ + val validationOrg = ValidatorOrganization(validator.id, validator.name) + validatorsList.add(validationOrg) + } + } + + + Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, + it.uuid, it.value, it.key, it.name, organization, validatorsList) }) } } } @@ -115,6 +174,47 @@ class RecordsRepository(private val recordsRemoteDataSource: RecordsDataSource) } } + override fun deleteRecord(id: Long) : Observable{ + return recordsRemoteDataSource.deleteRecord(id).map { + it.success + } + } + + /*override fun updateRecord(id: Long, updateRecord: UpdateRecord): Observable { + return recordsRemoteDataSource.updateRecord(id, updateRecord) + .map{ item -> + + val type = types.find { type -> type.key.equals(it.key) } + Record(it.id, it.value, it.order, type!!, category, it.valid ?: false, + it.validations.map { + var organization: io.forus.me.android.domain.models.vouchers.Organization? = null + if(it.organization != null) + organization = Organization(it.organization.id,it.organization.name,null,null,null, + null,it.organization.phone,it.organization.email) + + val validatorsList: MutableList = mutableListOf() + if(it.validators != null && it.validators.size > 0){ + for (validator in it.validators){ + val validationOrg = ValidatorOrganization(validator.id, validator.name) + validatorsList.add(validationOrg) + } + } + + + Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, + it.uuid, it.value, it.key, it.name, organization, validatorsList) }) + + } + + }*/ + + + + + + + + override fun newRecord(model: NewRecordRequest): Observable { val createRecord = io.forus.me.android.data.entity.records.request.CreateRecord(model.recordType?.key, model.category?.id, model.value, model.order) return recordsRemoteDataSource.createRecord(createRecord) @@ -136,7 +236,16 @@ class RecordsRepository(private val recordsRemoteDataSource: RecordsDataSource) if(it.organization != null) organization = Organization(it.organization.id,it.organization.name,null,null,null, null,it.organization.phone,it.organization.email) - Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, it.uuid, it.value, it.key, it.name, organization) }) + + val validatorsList: MutableList = mutableListOf() + if(it.validators != null && it.validators.size > 0){ + for (validator in it.validators){ + val validationOrg = ValidatorOrganization(validator.id, validator.name) + validatorsList.add(validationOrg) + } + } + + Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, it.uuid, it.value, it.key, it.name, organization, validatorsList) }) } } ).flatMapObservable { @@ -155,11 +264,21 @@ class RecordsRepository(private val recordsRemoteDataSource: RecordsDataSource) if(it.organization != null) organization = Organization(it.organization.id,it.organization.name,null,null,null, null,it.organization.phone,it.organization.email) - Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, it.uuid, it.value, it.key, it.name, organization) } + + val validatorsList: MutableList = mutableListOf() + if(it.validators != null && it.validators.size > 0){ + for (validator in it.validators){ + val validationOrg = ValidatorOrganization(validator.id, validator.name) + validatorsList.add(validationOrg) + } + } + + Validation(Validation.State.valueOf(it.state.toString()), it.identityAddress, it.createdAt, it.updatedAt, it.uuid, it.value, it.key, it.name, organization, validatorsList) } } - override fun approveValidation(uuid: String): Observable { - return recordsRemoteDataSource.approveValidation(uuid).map { true } + override fun approveValidation(uuid: String, organization_id: Long): Observable { + val validateRecord = ValidateRecord(organization_id) + return recordsRemoteDataSource.approveValidation(uuid,validateRecord).map { true } } override fun declineValidation(uuid: String): Observable { diff --git a/data/src/main/java/io/forus/me/android/data/repository/records/datasource/RecordsDataSource.kt b/data/src/main/java/io/forus/me/android/data/repository/records/datasource/RecordsDataSource.kt index a3f3c30a..e4e52de4 100644 --- a/data/src/main/java/io/forus/me/android/data/repository/records/datasource/RecordsDataSource.kt +++ b/data/src/main/java/io/forus/me/android/data/repository/records/datasource/RecordsDataSource.kt @@ -37,6 +37,9 @@ interface RecordsDataSource { fun getRecords(): Observable> + fun getRecordsArchived(): Observable> + + fun getRecords(type: String): Observable> @@ -49,6 +52,8 @@ interface RecordsDataSource { fun updateRecord(id: Long, updateRecord: UpdateRecord) : Observable + + fun deleteRecord(id: Long) : Observable @@ -61,7 +66,7 @@ interface RecordsDataSource { fun readValidation(uuid: String): Observable - fun approveValidation(uuid: String): Observable + fun approveValidation(uuid: String, validateRecord: ValidateRecord): Observable fun declineValidation(uuid: String): Observable diff --git a/data/src/main/java/io/forus/me/android/data/repository/records/datasource/mock/RecordsMockDataSource.kt b/data/src/main/java/io/forus/me/android/data/repository/records/datasource/mock/RecordsMockDataSource.kt index 48c58b27..8e285b1f 100644 --- a/data/src/main/java/io/forus/me/android/data/repository/records/datasource/mock/RecordsMockDataSource.kt +++ b/data/src/main/java/io/forus/me/android/data/repository/records/datasource/mock/RecordsMockDataSource.kt @@ -9,6 +9,7 @@ import io.reactivex.Single class RecordsMockDataSource : RecordsDataSource{ + val types: MutableList = mutableListOf() val categories : MutableList = mutableListOf() val records : MutableList = mutableListOf() @@ -86,6 +87,10 @@ class RecordsMockDataSource : RecordsDataSource{ throw UnsupportedOperationException("Not implemented") } + override fun getRecordsArchived(): Observable> { + throw UnsupportedOperationException("Not implemented") + } + override fun getRecords(categoryId: Long): Observable> { throw UnsupportedOperationException("Not implemented") } @@ -126,7 +131,7 @@ class RecordsMockDataSource : RecordsDataSource{ throw UnsupportedOperationException("Not implemented") } - override fun approveValidation(uuid: String): Observable { + override fun approveValidation(uuid: String,validateRecord: ValidateRecord): Observable { throw UnsupportedOperationException("Not implemented") } diff --git a/data/src/main/java/io/forus/me/android/data/repository/records/datasource/remote/RecordsRemoteDataSource.kt b/data/src/main/java/io/forus/me/android/data/repository/records/datasource/remote/RecordsRemoteDataSource.kt index 9ce4f0b2..5c60041f 100644 --- a/data/src/main/java/io/forus/me/android/data/repository/records/datasource/remote/RecordsRemoteDataSource.kt +++ b/data/src/main/java/io/forus/me/android/data/repository/records/datasource/remote/RecordsRemoteDataSource.kt @@ -27,6 +27,8 @@ class RecordsRemoteDataSource(f: () -> RecordsService): RecordsDataSource, Remot override fun getRecords(): Observable> = service.listAllRecords(null, null) + override fun getRecordsArchived(): Observable> = service.listArchivedRecords() + override fun getRecords(categoryId: Long): Observable> = service.listAllRecords(null, categoryId) override fun getRecords(type: String): Observable> = service.listAllRecords(type, null) @@ -45,7 +47,7 @@ class RecordsRemoteDataSource(f: () -> RecordsService): RecordsDataSource, Remot override fun readValidation(uuid: String): Observable = service.readValidation(uuid) - override fun approveValidation(uuid: String): Observable = service.approveValidation(uuid) + override fun approveValidation(uuid: String, validateRecord: ValidateRecord): Observable = service.approveValidation(uuid,validateRecord) override fun declineValidation(uuid: String): Observable = service.declineValidation(uuid) } \ No newline at end of file diff --git a/data/src/main/java/io/forus/me/android/data/repository/vouchers/VouchersRepository.kt b/data/src/main/java/io/forus/me/android/data/repository/vouchers/VouchersRepository.kt index 2eb3f22a..d5d3e226 100644 --- a/data/src/main/java/io/forus/me/android/data/repository/vouchers/VouchersRepository.kt +++ b/data/src/main/java/io/forus/me/android/data/repository/vouchers/VouchersRepository.kt @@ -15,7 +15,6 @@ import java.util.* class VouchersRepository(private val vouchersDataSource: VouchersDataSource) : io.forus.me.android.domain.repository.vouchers.VouchersRepository { - val dateLocaleFormat = SimpleDateFormat("MMM dd, yyyy HH:mm", Locale.US) private fun mapToSimple(voucher: io.forus.me.android.data.entity.vouchers.response.Voucher): Voucher { @@ -100,7 +99,7 @@ class VouchersRepository(private val vouchersDataSource: VouchersDataSource) : i return Voucher(isProduct, isUsed, voucher.address ?: "", name, organizationName, voucher.fund?.name ?: "", voucher.fund?.webShopUrl ?: "", description, createdAt!!, euro, amount, - productLogoUrl, transactions, productMapped, voucher.expireAtLocale ?: "") + productLogoUrl, transactions, productMapped, voucher.isExpired, voucher.expireAtLocale ?: "") } @@ -116,12 +115,40 @@ class VouchersRepository(private val vouchersDataSource: VouchersDataSource) : i return VoucherProvider(item, organizations, categories) } + + private fun getFakeVoucherProvider(): VoucherProvider { + val date = Calendar.getInstance().getTime() + val currency = Currency("EUR", "") + + val organization = Organization(0, "Test bedrijf", "", 0.0, 0.0, + "", "", "") + val organizationsList = mutableListOf() + organizationsList.add(organization) + val transaction = Transaction("0", organization, currency, 0f.toBigDecimal(), date) + val transactionList = mutableListOf() + transactionList.add(transaction) + + val productCategory = ProductCategory(0, "", "") + val productCategoryList = mutableListOf() + productCategoryList.add(productCategory) + val product = Product(0, 0, 0, "", "", + 0f.toBigDecimal(), 0f.toBigDecimal(), + 0, 0, productCategory, organization) + + val voucher = Voucher(false, false, "", "Test bedrijf", + "", "", "", + "", date, currency, 1000.toBigDecimal(), "", + transactionList, product, false,"") + + return VoucherProvider(voucher, organizationsList, productCategoryList) + } + + override fun getVouchers(): Observable> { return vouchersDataSource.listAllVouchers().map { it.map { mapToSimple(it) } } } - override fun getVoucher(address: String): Observable { return vouchersDataSource.retrieveVoucher(address).map { mapToSimple(it) } } @@ -130,6 +157,11 @@ class VouchersRepository(private val vouchersDataSource: VouchersDataSource) : i return vouchersDataSource.retrieveVoucherAsProvider(address).map { mapToProvider(it) } } + override fun getTestVoucherAsProvider(): Observable { + + return Observable.just(getFakeVoucherProvider()) + } + override fun getProductVouchersAsProvider(address: String): Observable> { return vouchersDataSource.retrieveProductVouchersAsProvider(address).map { it.map { mapToSimple(it) } } } @@ -144,8 +176,8 @@ class VouchersRepository(private val vouchersDataSource: VouchersDataSource) : i } override fun makeDemoTransaction(testToken: String): Observable { - return vouchersDataSource.makeDemoTransaction(testToken, MakeDemoTransaction("accepted")).map{ - it.data.state == "accepted" + return vouchersDataSource.makeDemoTransaction(testToken, MakeDemoTransaction("accepted")).map { + it.data.state == "accepted" } } } \ No newline at end of file diff --git a/domain/src/main/java/io/forus/me/android/domain/models/common/DetailsApiError.java b/domain/src/main/java/io/forus/me/android/domain/models/common/DetailsApiError.java index 6ba9d9bb..af193501 100644 --- a/domain/src/main/java/io/forus/me/android/domain/models/common/DetailsApiError.java +++ b/domain/src/main/java/io/forus/me/android/domain/models/common/DetailsApiError.java @@ -42,6 +42,8 @@ public HashMap getErrors() { return errors; } + + @Override public String toString() { return "DetailsApiError{" + @@ -60,4 +62,26 @@ public String getErrorString() { } return msg.toString(); } + + public String getErrorStringWithoutKey() { + StringBuilder msg = new StringBuilder(); + for (String key : errors.keySet()) { + String value = errors.get(key); + String err = value + "\n"; + msg.append(err); + + } + return msg.toString(); + } + + public String getErrorsString(){ + StringBuilder msg = new StringBuilder(); + for (String key : errors.keySet()) { + String value = errors.get(key); + String err = value + "\n"; + msg.append(err); + + } + return msg.toString(); + } } diff --git a/domain/src/main/java/io/forus/me/android/domain/models/records/RecordCategory.kt b/domain/src/main/java/io/forus/me/android/domain/models/records/RecordCategory.kt index 590b685c..cee28a07 100644 --- a/domain/src/main/java/io/forus/me/android/domain/models/records/RecordCategory.kt +++ b/domain/src/main/java/io/forus/me/android/domain/models/records/RecordCategory.kt @@ -17,6 +17,8 @@ class RecordCategory{ var size: Long + var selected = false + constructor(id: Long, name: String, order: Long, size: Long = 0) { this.id = id this.name = name diff --git a/domain/src/main/java/io/forus/me/android/domain/models/records/RecordType.kt b/domain/src/main/java/io/forus/me/android/domain/models/records/RecordType.kt index 1d19d0e9..50245743 100644 --- a/domain/src/main/java/io/forus/me/android/domain/models/records/RecordType.kt +++ b/domain/src/main/java/io/forus/me/android/domain/models/records/RecordType.kt @@ -1,3 +1,6 @@ package io.forus.me.android.domain.models.records -class RecordType(var key: String, var type: String, var name: String) \ No newline at end of file +class RecordType(var key: String, var type: String, var name: String){ + + var selected = false +} \ No newline at end of file diff --git a/domain/src/main/java/io/forus/me/android/domain/models/records/Validation.kt b/domain/src/main/java/io/forus/me/android/domain/models/records/Validation.kt index 3812478d..f0e38481 100644 --- a/domain/src/main/java/io/forus/me/android/domain/models/records/Validation.kt +++ b/domain/src/main/java/io/forus/me/android/domain/models/records/Validation.kt @@ -37,7 +37,11 @@ class Validation { @SerializedName("organization") var organization: Organization? = null - constructor(state: State, identityAddress: String?, createdAt: Date?, updatedAt: Date?, uuid: String?, value: String?, key: String?, name: String?, organization: Organization?) { + @SerializedName("organizations_available") + var organizationsAvailable: List? = null + + constructor(state: State, identityAddress: String?, createdAt: Date?, updatedAt: Date?, uuid: String?, value: String?, key: String?, name: String?, organization: Organization?, + organizationsAvailable: List?) { this.state = state this.identityAddress = identityAddress this.createdAt = createdAt @@ -47,6 +51,7 @@ class Validation { this.key = key this.name = name this.organization = organization + this.organizationsAvailable = organizationsAvailable } companion object { diff --git a/domain/src/main/java/io/forus/me/android/domain/models/records/ValidatorOrganization.java b/domain/src/main/java/io/forus/me/android/domain/models/records/ValidatorOrganization.java new file mode 100644 index 00000000..b3aee623 --- /dev/null +++ b/domain/src/main/java/io/forus/me/android/domain/models/records/ValidatorOrganization.java @@ -0,0 +1,37 @@ +package io.forus.me.android.domain.models.records; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by maestrovs on 29.04.2020. + */ +public class ValidatorOrganization { + + @SerializedName("id") + private Long id; + + @SerializedName("name") + private String name; + + + public ValidatorOrganization(Long id, String name) { + this.id = id; + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/domain/src/main/java/io/forus/me/android/domain/models/vouchers/Voucher.kt b/domain/src/main/java/io/forus/me/android/domain/models/vouchers/Voucher.kt index ec25d21c..baea7c61 100644 --- a/domain/src/main/java/io/forus/me/android/domain/models/vouchers/Voucher.kt +++ b/domain/src/main/java/io/forus/me/android/domain/models/vouchers/Voucher.kt @@ -4,7 +4,7 @@ import io.forus.me.android.domain.models.currency.Currency import java.math.BigDecimal import java.util.* -class Voucher(var isProduct: Boolean?, var isUsed: Boolean?, var address: String?, var name: String?, var organizationName: String?, var fundName: String?, var fundWebShopUrl: String?, var description: String?, var createdAt: Date?, var currency: Currency?, var amount: BigDecimal?, var logo: String, var transactions: List, var product: Product? = null, val expireDate: String?) { +class Voucher(var isProduct: Boolean?, var isUsed: Boolean?, var address: String?, var name: String?, var organizationName: String?, var fundName: String?, var fundWebShopUrl: String?, var description: String?, var createdAt: Date?, var currency: Currency?, var amount: BigDecimal?, var logo: String, var transactions: List, var product: Product? = null, val expired: Boolean?, val expireDate: String?) { // fun getValidString() : String { diff --git a/domain/src/main/java/io/forus/me/android/domain/repository/common/CommonRepository.kt b/domain/src/main/java/io/forus/me/android/domain/repository/common/CommonRepository.kt new file mode 100644 index 00000000..ff5f2862 --- /dev/null +++ b/domain/src/main/java/io/forus/me/android/domain/repository/common/CommonRepository.kt @@ -0,0 +1,8 @@ +package io.forus.me.android.domain.repository.common + +import io.reactivex.Observable + +interface CommonRepository { + + fun status(): Observable +} \ No newline at end of file diff --git a/domain/src/main/java/io/forus/me/android/domain/repository/records/RecordsRepository.kt b/domain/src/main/java/io/forus/me/android/domain/repository/records/RecordsRepository.kt index 8267a1d7..ec65643c 100644 --- a/domain/src/main/java/io/forus/me/android/domain/repository/records/RecordsRepository.kt +++ b/domain/src/main/java/io/forus/me/android/domain/repository/records/RecordsRepository.kt @@ -1,5 +1,6 @@ package io.forus.me.android.domain.repository.records +import com.sun.net.httpserver.Authenticator import io.forus.me.android.domain.models.records.* import io.reactivex.Observable @@ -26,10 +27,17 @@ interface RecordsRepository { fun getRecords(): Observable> + fun getRecordsArchived(): Observable> + + fun getRecords(recordCategoryId: Long): Observable> + fun deleteRecord(id: Long) : Observable + + // fun updateRecord(id: Long, updateRecord: UpdateRecord) : Observable + - fun newRecord(model: NewRecordRequest) : Observable + fun newRecord(model: NewRecordRequest): Observable fun getRecord(recordId: Long): Observable @@ -41,7 +49,7 @@ interface RecordsRepository { fun readValidation(uuid: String): Observable - fun approveValidation(uuid: String): Observable + fun approveValidation(uuid: String, organization_id: Long): Observable fun declineValidation(uuid: String): Observable diff --git a/domain/src/main/java/io/forus/me/android/domain/repository/vouchers/VouchersRepository.kt b/domain/src/main/java/io/forus/me/android/domain/repository/vouchers/VouchersRepository.kt index 84ae57dd..b9dd067e 100644 --- a/domain/src/main/java/io/forus/me/android/domain/repository/vouchers/VouchersRepository.kt +++ b/domain/src/main/java/io/forus/me/android/domain/repository/vouchers/VouchersRepository.kt @@ -13,6 +13,8 @@ interface VouchersRepository { fun getVoucherAsProvider(address: String): Observable + fun getTestVoucherAsProvider(): Observable + fun getProductVouchersAsProvider(address: String): Observable> fun makeTransaction(address: String, amount: BigDecimal, note: String, organizationId: Long): Observable diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b2bc2c57..e594a672 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Feb 27 00:40:42 MSK 2019 +#Fri Jun 05 18:31:30 EEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip diff --git a/presentation/build.gradle b/presentation/build.gradle index d1639fca..7a91a3a3 100755 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -7,6 +7,7 @@ apply plugin: 'org.greenrobot.greendao' apply plugin: 'io.fabric' apply plugin: 'com.google.gms.google-services' + android { dataBinding { @@ -96,6 +97,7 @@ android { prod { dimension "version" versionNameSuffix "-prod" + // buildConfigField "String", SERVER_URL, "\"https://test.api.forus.rminds.dev/\"" buildConfigField "String", SERVER_URL, "\"https://api.forus.io/\"" buildConfigField "String", PREFIX_URL, "\"\"" applicationId 'io.forus.me' @@ -117,7 +119,9 @@ android { dev { dimension "version" versionNameSuffix "-dev" - buildConfigField "String", SERVER_URL, "\"https://dev.api.forus.io/\"" + //buildConfigField "String", SERVER_URL, "\"https://staging.api.forus.io/\"" //TODO + //buildConfigField "String", SERVER_URL, "\"https://test.api.forus.rminds.dev/\"" //TODO + buildConfigField "String", SERVER_URL, "\"https://dev.api.forus.io/\"" //TODO buildConfigField "String", PREFIX_URL, "\"dev.\"" applicationId 'io.forus.me.dev' } @@ -182,6 +186,9 @@ dependencies { implementation presentationDependencies.firebaseCore implementation presentationDependencies.firebaseMessaging implementation presentationDependencies.googleMaps + implementation presentationDependencies.playCore + implementation presentationDependencies.navigationFragment + implementation presentationDependencies.navigationUI implementation(presentationDependencies.fabric) { transitive = true diff --git a/presentation/src/main/AndroidManifest.xml b/presentation/src/main/AndroidManifest.xml index be64cdf1..6cf1b04a 100755 --- a/presentation/src/main/AndroidManifest.xml +++ b/presentation/src/main/AndroidManifest.xml @@ -7,8 +7,12 @@ - - + + + + + - - - @@ -36,10 +50,6 @@ android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="@string/default_notification_channel_id" /> - - - - @@ -49,151 +59,131 @@ - + android:noHistory="true" + android:screenOrientation="unspecified" + android:theme="@android:style/Theme.NoDisplay"> - - - - - - - + + + + + + + + + - - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTask" /> - + android:launchMode="singleTask" /> - + android:launchMode="singleTask" /> - + android:launchMode="singleTask" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> + - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:launchMode="singleTop" /> - + android:windowSoftInputMode="adjustResize" /> - - + android:windowSoftInputMode="adjustResize" /> - + android:launchMode="singleTop" /> - - - + - \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/AndroidApplication.kt b/presentation/src/main/java/io/forus/me/android/presentation/AndroidApplication.kt index 31564a86..b5849758 100755 --- a/presentation/src/main/java/io/forus/me/android/presentation/AndroidApplication.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/AndroidApplication.kt @@ -13,6 +13,7 @@ import com.google.android.gms.tasks.OnCompleteListener import com.google.firebase.iid.FirebaseInstanceId import io.fabric.sdk.android.Fabric import io.forus.me.android.data.net.MeServiceFactory +import io.forus.me.android.presentation.api_config.ApiConfig import io.forus.me.android.presentation.internal.Injection @@ -27,6 +28,9 @@ class AndroidApplication : Application() { override fun onCreate() { super.onCreate() + + ApiConfig.SERVER_URL = BuildConfig.SERVER_URL + this.initializeInjector() this.initializeLeakDetection() this.initRetrofit() diff --git a/presentation/src/main/java/io/forus/me/android/presentation/api_config/ApiConfig.java b/presentation/src/main/java/io/forus/me/android/presentation/api_config/ApiConfig.java new file mode 100644 index 00000000..180e4ea6 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/api_config/ApiConfig.java @@ -0,0 +1,55 @@ +package io.forus.me.android.presentation.api_config; + +import io.forus.me.android.presentation.BuildConfig; +import io.forus.me.android.presentation.helpers.SharedPref; + +public class ApiConfig { + + + + public static String SERVER_URL = BuildConfig.SERVER_URL; + + private static String[] apiVariants = {"https://api.forus.io/", "https://demo.api.forus.io/", + "https://staging.api.forus.io/", "https://dev.api.forus.io/" + }; + + + + public static ApiType getCurrentApiType(){ + if(SERVER_URL.equals(apiVariants[0])) return ApiType.PROD; + else if(SERVER_URL.equals(apiVariants[1])) return ApiType.DEMO; + else if(SERVER_URL.equals(apiVariants[2])) return ApiType.STAGING; + else if(SERVER_URL.equals(apiVariants[3])) return ApiType.DEV; + else return ApiType.OTHER; + } + + public static void changeApi(ApiType apiType){ + + if(apiType == ApiType.PROD) SERVER_URL = apiVariants[0]; + else if(apiType == ApiType.DEMO) SERVER_URL = apiVariants[1]; + else if(apiType == ApiType.STAGING) SERVER_URL = apiVariants[2]; + else if(apiType == ApiType.DEV) SERVER_URL = apiVariants[3]; + else SERVER_URL = apiVariants[0]; + + } + + public static void changeToCustomApi(String customUrl){ + + SERVER_URL = customUrl; + + } + + + + public static ApiType stringToApiType(String str){ + if(str.equalsIgnoreCase("PROD")) return ApiType.PROD; + if(str.equalsIgnoreCase("DEMO")) return ApiType.DEMO; + if(str.equalsIgnoreCase("STAGING")) return ApiType.STAGING; + if(str.equalsIgnoreCase("DEV")) return ApiType.DEV; + else return ApiType.OTHER; + } + +} + + + diff --git a/presentation/src/main/java/io/forus/me/android/presentation/api_config/ApiType.java b/presentation/src/main/java/io/forus/me/android/presentation/api_config/ApiType.java new file mode 100644 index 00000000..f57e316d --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/api_config/ApiType.java @@ -0,0 +1,5 @@ +package io.forus.me.android.presentation.api_config; + +public enum ApiType { + PROD, DEMO, STAGING, DEV, OTHER +} diff --git a/presentation/src/main/java/io/forus/me/android/presentation/api_config/Utils.kt b/presentation/src/main/java/io/forus/me/android/presentation/api_config/Utils.kt new file mode 100644 index 00000000..9b63ed9c --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/api_config/Utils.kt @@ -0,0 +1,32 @@ +package io.forus.me.android.presentation.api_config + +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import io.forus.me.android.presentation.view.screens.main.MainActivity + +class Utils private constructor() { + + init { + println("This ($this) is a singleton") + } + + private object Holder { + val INSTANCE = Utils() + } + + companion object { + val instance: Utils by lazy { Holder.INSTANCE } + } + + + fun restartApp(context: Context) { + val mStartActivity = Intent(context, MainActivity::class.java) + val mPendingIntentId = 123456 + val mPendingIntent = PendingIntent.getActivity(context, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT) + val mgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent) + System.exit(0) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/api_config/check_api_status/CheckApiPresenter.kt b/presentation/src/main/java/io/forus/me/android/presentation/api_config/check_api_status/CheckApiPresenter.kt new file mode 100644 index 00000000..acd75423 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/api_config/check_api_status/CheckApiPresenter.kt @@ -0,0 +1,41 @@ +package io.forus.me.android.presentation.api_config.check_api_status + +import android.content.Context +import io.forus.me.android.data.net.MeServiceFactory +import io.forus.me.android.data.net.common.CommonService +import io.forus.me.android.data.repository.common.CommonRepository +import io.forus.me.android.data.repository.common.datasource.CommonRemoteDataSource +import io.forus.me.android.presentation.api_config.ApiConfig +import io.forus.me.android.presentation.internal.Injection +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject + +class CheckApiPresenter(val context: Context) { + + + fun checkApi(apiString: String, success: (Boolean) -> Unit, error: (Throwable) -> Unit) { + + try { + + val commonRemoteDataSource = CommonRemoteDataSource { MeServiceFactory.getInstance().createRetrofitService(CommonService::class.java, apiString) } + + val commonRepository = CommonRepository(commonRemoteDataSource) + + commonRepository.status() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map { + success(it) + it + } + .onErrorReturn { + error(it) + false + }.subscribe() + + } catch (e: Exception) { + error(e) + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/ChooseApiDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/ChooseApiDialog.kt new file mode 100644 index 00000000..ba5a6bec --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/ChooseApiDialog.kt @@ -0,0 +1,22 @@ +package io.forus.me.android.presentation.api_config.dialogs + +import android.content.Context +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.presentation.R + + +class ChooseApiDialog(private val context: Context, itemCallback: MaterialDialog.ListCallback, + private val cancelListener: () -> Unit){ + + private val dialog: MaterialDialog = MaterialDialog.Builder(context) + .title("Choose api") + .items(R.array.api_items) + .negativeText("Cancel") + .itemsCallback(itemCallback) + .cancelListener { cancelListener.invoke() } + .build() + + fun show(){ + dialog.show() + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/CustomApiDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/CustomApiDialog.kt new file mode 100644 index 00000000..2fd563d0 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/CustomApiDialog.kt @@ -0,0 +1,30 @@ +package io.forus.me.android.presentation.api_config.dialogs + +import android.content.Context +import android.support.v4.text.HtmlCompat +import android.text.Html +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.presentation.R + + +class CustomApiDialog(private val context: Context, customApiStr: String, inputCallback: MaterialDialog.InputCallback, + private val saveListener: (MaterialDialog) -> Unit, + private val cancelListener: () -> Unit){ + + private val dialog: MaterialDialog = MaterialDialog.Builder(context) + .title("Custom API server") + .content(HtmlCompat.fromHtml("API format:
https://{address}/ api/v1/...", + HtmlCompat.FROM_HTML_MODE_LEGACY)) + .positiveText("Save") + .input("",customApiStr,inputCallback) + .negativeText("Cancel") + .onPositive { dialog, which -> saveListener.invoke(dialog) } + .cancelListener { cancelListener.invoke() } + .build() + + fun show(){ + dialog.show() + } + + +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/SaveApiAndRestartDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/SaveApiAndRestartDialog.kt new file mode 100644 index 00000000..06d90737 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/SaveApiAndRestartDialog.kt @@ -0,0 +1,34 @@ +package io.forus.me.android.presentation.api_config.dialogs + +import android.content.Context +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.presentation.R +import java.math.BigDecimal +import android.app.AlarmManager +import android.app.PendingIntent +import android.content.Intent +import android.util.Log +import io.forus.me.android.presentation.helpers.SharedPref +import io.forus.me.android.presentation.view.screens.main.MainActivity + + +class SaveApiAndRestartDialog(private val context: Context, + private val positiveCallback: () -> Unit) { + + private val dialog: MaterialDialog = MaterialDialog.Builder(context) + .title("Confirm") + .content("You must restart the application for the changes to take effect") + .positiveText("Save and restart") + .negativeText(context.resources.getString(R.string.me_cancel)) + .onPositive { dialog, which -> + positiveCallback.invoke() + + } + .build() + + fun show() { + dialog.show() + } + + +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/TestApiErrorDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/TestApiErrorDialog.kt new file mode 100644 index 00000000..2996f93c --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/TestApiErrorDialog.kt @@ -0,0 +1,21 @@ +package io.forus.me.android.presentation.api_config.dialogs + +import android.content.Context +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.presentation.R + + +class TestApiErrorDialog(private val context: Context, private val message: String, + private val cancelListener: () -> Unit){ + + private val dialog: MaterialDialog = MaterialDialog.Builder(context) + .title("Error test API server") + .content(message) + .negativeText("Cancel") + .cancelListener { cancelListener.invoke() } + .build() + + fun show(){ + dialog.show() + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/TestApiSuccessDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/TestApiSuccessDialog.kt new file mode 100644 index 00000000..29e79fba --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/api_config/dialogs/TestApiSuccessDialog.kt @@ -0,0 +1,21 @@ +package io.forus.me.android.presentation.api_config.dialogs + +import android.content.Context +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.presentation.R + + +class TestApiSuccessDialog(private val context: Context, private val message: String, + private val positiveCallback: () -> Unit){ + + private val dialog: MaterialDialog = MaterialDialog.Builder(context) + .title("Success test API server") + .content(message) + .positiveText("OK") + .onPositive { dialog, which -> positiveCallback.invoke() } + .build() + + fun show(){ + dialog.show() + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/deeplinks/UriToIntentMapper.kt b/presentation/src/main/java/io/forus/me/android/presentation/deeplinks/UriToIntentMapper.kt index 467460cc..19e010a0 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/deeplinks/UriToIntentMapper.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/deeplinks/UriToIntentMapper.kt @@ -33,13 +33,12 @@ class UriToIntentMapper(private val mContext: Context, private val navigator: Na "identity-restore" -> { val bQuery = uri.getQueryParameter("token") navigator.navigateToAccountRestoreByEmailExchangeToken(mContext, bQuery) - // navigator.navigateToResoreAccountSuccess(mContext, bQuery) + + } "identity-confirmation" -> { val bQuery = uri.getQueryParameter("token") - //navigator.navigateToAccountRestoreByEmailExchangeToken(mContext, bQuery) - // navigator.navigateToConfirmRegistration(mContext, bQuery) - navigator.navigateToResoreAccountSuccess(mContext, bQuery) + navigator.navigateToResoreAccountSuccess(mContext, bQuery, true) } } return null diff --git a/presentation/src/main/java/io/forus/me/android/presentation/helpers/SharedPref.kt b/presentation/src/main/java/io/forus/me/android/presentation/helpers/SharedPref.kt index c55cad92..f980eff6 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/helpers/SharedPref.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/helpers/SharedPref.kt @@ -7,9 +7,12 @@ import android.app.Activity object SharedPref { private var mSharedPref: SharedPreferences? = null - val RESTORE_EMAIL = "RESTORE_EMAIL" - val OPTION_SEND_CRASH_REPORT = "SEND_CRASH_REPORT" + public val RESTORE_EMAIL = "RESTORE_EMAIL" + public val OPTION_SEND_CRASH_REPORT = "SEND_CRASH_REPORT" + public val OPTION_NEED_APP_UPDATE = "OPTION_NEED_APP_UPDATE" + public val OPTION_API_TYPE = "OPTION_API_TYPE" + public val OPTION_CUSTOM_API_URL = "OPTION_CUSTOM_API_URL" fun init(context: Context) { if (mSharedPref == null) diff --git a/presentation/src/main/java/io/forus/me/android/presentation/internal/Injection.kt b/presentation/src/main/java/io/forus/me/android/presentation/internal/Injection.kt index 148bcd05..503f0d3a 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/internal/Injection.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/internal/Injection.kt @@ -1,16 +1,21 @@ package io.forus.me.android.presentation.internal import android.content.Context +import android.util.Log import io.forus.me.android.data.net.records.RecordsService import io.forus.me.android.data.net.sign.SignService import io.forus.me.android.data.entity.database.DaoSession import io.forus.me.android.data.exception.RetrofitExceptionMapper import io.forus.me.android.data.net.MeServiceFactory +import io.forus.me.android.data.net.common.CommonService import io.forus.me.android.data.net.validators.ValidatorsService import io.forus.me.android.data.net.vouchers.VouchersService import io.forus.me.android.data.repository.account.datasource.local.AccountLocalDataSource import io.forus.me.android.data.repository.account.datasource.remote.AccountRemoteDataSource import io.forus.me.android.data.repository.account.datasource.remote.CheckActivationDataSource +import io.forus.me.android.data.repository.common.CommonRepository +import io.forus.me.android.data.repository.common.datasource.CommonDataSource +import io.forus.me.android.data.repository.common.datasource.CommonRemoteDataSource import io.forus.me.android.data.repository.records.RecordsRepository import io.forus.me.android.data.repository.records.datasource.mock.RecordsMockDataSource import io.forus.me.android.data.repository.records.datasource.remote.RecordsRemoteDataSource @@ -26,6 +31,7 @@ import io.forus.me.android.domain.repository.account.AccountRepository import io.forus.me.android.domain.repository.assets.AssetsRepository import io.forus.me.android.domain.repository.vouchers.VouchersRepository import io.forus.me.android.domain.repository.wallets.WalletsRepository +import io.forus.me.android.presentation.api_config.ApiConfig import io.forus.me.android.presentation.BuildConfig import io.forus.me.android.presentation.DatabaseHelper import io.forus.me.android.presentation.helpers.AppSettings @@ -66,6 +72,19 @@ class Injection private constructor() { } } + + + + val commonRepository: CommonRepository by lazy { + return@lazy io.forus.me.android.data.repository.common.CommonRepository(commonRemoteDataSource) + } + + + private val commonRemoteDataSource: CommonRemoteDataSource by lazy { + return@lazy CommonRemoteDataSource{MeServiceFactory.getInstance().createRetrofitService(CommonService::class.java, ApiConfig.SERVER_URL) } + } + + val databaseHelper: DatabaseHelper by lazy { return@lazy DatabaseHelper(applicationContext!!) } @@ -79,7 +98,7 @@ class Injection private constructor() { } private val accountRemoteDataSource: AccountRemoteDataSource by lazy { - return@lazy AccountRemoteDataSource { MeServiceFactory.getInstance().createRetrofitService(SignService::class.java, BuildConfig.SERVER_URL) } + return@lazy AccountRemoteDataSource { MeServiceFactory.getInstance().createRetrofitService(SignService::class.java, ApiConfig.SERVER_URL) } } val accountLocalDataSource: AccountLocalDataSource by lazy { @@ -87,7 +106,7 @@ class Injection private constructor() { } private val checkActivationDataSource: CheckActivationDataSource by lazy { - return@lazy CheckActivationDataSource(MeServiceFactory.getInstance().createRetrofitService(SignService::class.java, BuildConfig.SERVER_URL)) + return@lazy CheckActivationDataSource(MeServiceFactory.getInstance().createRetrofitService(SignService::class.java, ApiConfig.SERVER_URL)) } private val web3LocalDataSource: Web3DataSource by lazy { @@ -103,7 +122,7 @@ class Injection private constructor() { } val vouchersDataSource: VouchersDataSource by lazy { - return@lazy VouchersRemoteDataSource { MeServiceFactory.getInstance().createRetrofitService(VouchersService::class.java, BuildConfig.SERVER_URL) } + return@lazy VouchersRemoteDataSource { MeServiceFactory.getInstance().createRetrofitService(VouchersService::class.java, ApiConfig.SERVER_URL) } } val vouchersRepository: VouchersRepository by lazy { @@ -120,7 +139,7 @@ class Injection private constructor() { } private val recordRemoteDataSource: RecordsRemoteDataSource by lazy { - return@lazy RecordsRemoteDataSource { MeServiceFactory.getInstance().createRetrofitService(RecordsService::class.java, BuildConfig.SERVER_URL) } + return@lazy RecordsRemoteDataSource { MeServiceFactory.getInstance().createRetrofitService(RecordsService::class.java, ApiConfig.SERVER_URL) } } val retrofitExceptionMapper: RetrofitExceptionMapper by lazy { @@ -128,7 +147,7 @@ class Injection private constructor() { } private val validatorsRemoteDataSource: ValidatorsRemoteDataSource by lazy { - return@lazy ValidatorsRemoteDataSource { MeServiceFactory.getInstance().createRetrofitService(ValidatorsService::class.java, BuildConfig.SERVER_URL) } + return@lazy ValidatorsRemoteDataSource { MeServiceFactory.getInstance().createRetrofitService(ValidatorsService::class.java, ApiConfig.SERVER_URL) } } val validatorsRepository: ValidatorsRepository by lazy { @@ -136,7 +155,7 @@ class Injection private constructor() { } val accessTokenChecker: AccessTokenChecker by lazy { - return@lazy AccessTokenChecker(BuildConfig.SERVER_URL) + return@lazy AccessTokenChecker(ApiConfig.SERVER_URL) } val qrDecoder: QrDecoder by lazy { diff --git a/presentation/src/main/java/io/forus/me/android/presentation/mappers/VoucherDataMapper.kt b/presentation/src/main/java/io/forus/me/android/presentation/mappers/VoucherDataMapper.kt index 7f6e146d..af6e9988 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/mappers/VoucherDataMapper.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/mappers/VoucherDataMapper.kt @@ -29,6 +29,7 @@ class VoucherDataMapper(private val currencyDataMapper: CurrencyDataMapper, } else { null }, + expired ?: false, expireDate ?: "") } } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/models/vouchers/Voucher.kt b/presentation/src/main/java/io/forus/me/android/presentation/models/vouchers/Voucher.kt index 3b9c4cff..d12bd066 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/models/vouchers/Voucher.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/models/vouchers/Voucher.kt @@ -20,6 +20,7 @@ class Voucher(var isProduct: Boolean, var logo: String?, var transactions: List, val product: Product? = null, + val expired: Boolean = false, val expireDate: String?) : Parcelable { constructor(parcel: Parcel) : this( parcel.readByte() != 0.toByte(), @@ -36,6 +37,7 @@ class Voucher(var isProduct: Boolean, parcel.readString() ?: "", parcel.createTypedArrayList(Transaction), parcel.readParcelable(Product::class.java.classLoader), + parcel.readByte() != 0.toByte(), parcel.readString() ?: "") override fun writeToParcel(parcel: Parcel, flags: Int) { @@ -51,6 +53,7 @@ class Voucher(var isProduct: Boolean, parcel.writeString(logo) parcel.writeTypedList(transactions) parcel.writeParcelable(product, flags) + parcel.writeByte(if (expired) 1 else 0) parcel.writeString(expireDate) } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/navigation/Navigator.kt b/presentation/src/main/java/io/forus/me/android/presentation/navigation/Navigator.kt index fef7095e..94f700fb 100755 --- a/presentation/src/main/java/io/forus/me/android/presentation/navigation/Navigator.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/navigation/Navigator.kt @@ -1,9 +1,11 @@ package io.forus.me.android.presentation.navigation +import android.app.Activity import android.content.Context import android.content.Intent import android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK import android.support.v4.app.Fragment +import android.support.v7.app.AppCompatActivity import io.forus.me.android.domain.models.records.Record import io.forus.me.android.domain.models.records.RecordCategory import io.forus.me.android.domain.models.wallets.Wallet @@ -163,9 +165,9 @@ constructor()//empty } } - fun navigateToResoreAccountSuccess(context: Context?, token: String) { + fun navigateToResoreAccountSuccess(context: Context?, token: String, isExchangeToken: Boolean) { if (context != null) { - val intentToLaunch = RestoreAccountSuccessActivity.getCallingIntent(context, token) + val intentToLaunch = RestoreAccountSuccessActivity.getCallingIntent(context, token, isExchangeToken) intentToLaunch.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) context.startActivity(intentToLaunch) } @@ -222,18 +224,18 @@ constructor()//empty } } - fun navigateToRecordDetails(context: Context?, record: Record) { + fun navigateToRecordDetailsForResult(context: AppCompatActivity, record: Record, REQUEST_CODE: Int) { if (context != null) { val intentToLaunch = RecordDetailsActivity.getCallingIntent(context, record) - context.startActivity(intentToLaunch) + context.startActivityForResult(intentToLaunch,REQUEST_CODE) } } - fun navigateToVoucherProvider(context: Context?, voucherAddress: String) { + fun navigateToVoucherProvider(context: Context?, voucherAddress: String, isDemoVoucher: Boolean? = false) { if (context != null) { - val intentToLaunch = ProviderActivity.getCallingIntent(context, voucherAddress) + val intentToLaunch = ProviderActivity.getCallingIntent(context, voucherAddress,isDemoVoucher) context.startActivity(intentToLaunch) } } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/Button.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/Button.kt index 8127427b..2090db97 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/Button.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/Button.kt @@ -37,8 +37,6 @@ class Button : android.support.v7.widget.AppCompatButton { } private fun initNonStyle(context: Context, attrs: AttributeSet?) { - - init(context, attrs) } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/ButtonNext.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/ButtonNext.kt new file mode 100644 index 00000000..05b61fbf --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/ButtonNext.kt @@ -0,0 +1,83 @@ +package io.forus.me.android.presentation.view.component.buttons + +import android.content.Context +import android.graphics.Color +import android.os.Build +import android.util.AttributeSet +import io.forus.me.android.presentation.R +import android.support.v4.content.ContextCompat +import android.util.TypedValue +import io.forus.me.android.presentation.helpers.Converter +import io.forus.me.android.presentation.helpers.FontCache +import io.forus.me.android.presentation.view.component.FontType + + +class ButtonNext : android.support.v7.widget.AppCompatButton { + + private var reverse : Boolean = false + private var customTextSize: Float = 16f + + var active : Boolean = true + set(value) { + field = value + initFont() + initBackground() + } + + constructor(context: Context) : super(context) { + initNonStyle(context, null) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + initNonStyle(context, attrs) + } + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + init(context, attrs) + } + + private fun initNonStyle(context: Context, attrs: AttributeSet?) { + + init(context, attrs) + } + + + + private fun init(context: Context, attrs: AttributeSet?) { + this.minimumHeight = Converter.convertDpToPixel(40f, context) + + if (attrs != null) { + + val ta = context.obtainStyledAttributes(attrs, R.styleable.CustomButtonAttrs, 0, 0) + + if (ta.hasValue(R.styleable.CustomButtonAttrs_reverse)) { + reverse = ta.getBoolean(R.styleable.CustomButtonAttrs_reverse, false) + + } + if (ta.hasValue(R.styleable.CustomButtonAttrs_textSize)) { + customTextSize = ta.getFloat(R.styleable.CustomButtonAttrs_textSize, 16f) + } + ta.recycle() + } + + initFont() + initBackground() + } + + private fun initFont(){ + setTextColor(if (!active) ContextCompat.getColor(context, R.color.body_1_38) else (if (reverse) ContextCompat.getColor(context, R.color.colorAccent) else ContextCompat.getColor(context, R.color.colorAccent))) + setTextSize(TypedValue.COMPLEX_UNIT_DIP, customTextSize) + val fontType = FontType.Regular + + + typeface = FontCache.getTypeface(fontType.getFontPath(), context) + } + + private fun initBackground(){ + setBackgroundResource(if (!reverse && active ) R.drawable.button_main_round_blue else R.drawable.button_main_raund_reverse) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + this.stateListAnimator = null + } + + } +} diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/ButtonWhite.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/ButtonWhite.kt index 523f6c7b..9a121164 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/ButtonWhite.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/component/buttons/ButtonWhite.kt @@ -67,7 +67,7 @@ class ButtonWhite : android.support.v7.widget.AppCompatButton { } private fun initFont(){ - setTextColor(if (!active) ContextCompat.getColor(context, R.color.body_1_38) else (if (reverse) ContextCompat.getColor(context, R.color.colorAccent) else R.color.colorAccent)) + setTextColor(if (!active) ContextCompat.getColor(context, R.color.body_1_38) else (if (reverse) ContextCompat.getColor(context, R.color.colorAccent) else ContextCompat.getColor(context,R.color.colorAccent))) setTextSize(TypedValue.COMPLEX_UNIT_DIP, customTextSize) val fontType = FontType.Bold diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/component/dividers/FDividerItemDecoration.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/component/dividers/FDividerItemDecoration.kt new file mode 100644 index 00000000..2749979b --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/component/dividers/FDividerItemDecoration.kt @@ -0,0 +1,52 @@ +package io.forus.me.android.presentation.view.component.dividers + +import android.content.Context +import android.support.v7.widget.RecyclerView +import android.content.res.TypedArray +import android.graphics.Canvas +import android.graphics.drawable.Drawable +import android.support.v4.content.ContextCompat +import io.forus.me.android.presentation.helpers.Converter + + +class FDividerItemDecoration : RecyclerView.ItemDecoration { + + private val ATTRS = intArrayOf(android.R.attr.listDivider) + + private var divider: Drawable? = null + + private lateinit var context: Context + + + constructor (context: Context) { + val styledAttributes = context.obtainStyledAttributes(ATTRS) + this.context = context + divider = styledAttributes.getDrawable(0) + styledAttributes.recycle() + } + + + + constructor(context: Context, resId: Int) { + this.context = context + divider = ContextCompat.getDrawable(context, resId) + } + + override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + val left = Converter.convertDpToPixel(20.0f, this.context) + val right = parent.width - parent.paddingRight + + val childCount = parent.childCount + for (i in 0 until childCount) { + val child = parent.getChildAt(i) + + val params = child.layoutParams as RecyclerView.LayoutParams + + val top = child.bottom + params.bottomMargin + val bottom = top + divider!!.intrinsicHeight + + divider!!.setBounds(left, top, right, bottom) + divider!!.draw(c) + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/component/extension/ViewExtensions.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/component/extension/ViewExtensions.kt new file mode 100644 index 00000000..219bbd65 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/component/extension/ViewExtensions.kt @@ -0,0 +1,29 @@ +package com.example.customsnackbar.extension + +import android.support.design.widget.CoordinatorLayout +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout + +internal fun View?.findSuitableParent(): ViewGroup? { + var view = this + var fallback: ViewGroup? = null + do { + if (view is CoordinatorLayout) { + return view + } else if (view is FrameLayout) { + if (view.id == android.R.id.content) { + return view + } else { + fallback = view + } + } + + if (view != null) { + val parent = view.parent + view = if (parent is View) parent else null + } + } while (view != null) + + return fallback +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/component/images/FQRCode.java b/presentation/src/main/java/io/forus/me/android/presentation/view/component/images/FQRCode.java new file mode 100644 index 00000000..61fb2bd5 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/component/images/FQRCode.java @@ -0,0 +1,111 @@ +package io.forus.me.android.presentation.view.component.images; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.graphics.drawable.VectorDrawableCompat; +import android.support.v4.content.ContextCompat; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.util.Log; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.EncodeHintType; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; + +import java.util.HashMap; +import java.util.Map; + +public class FQRCode { + public static float logoPercentage = 0.23f; + + + public static Bitmap generateFromVector(Context context, String content, int drawableId, int qrSize) { + + Map hints = new HashMap<>(); + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); + + QRCodeWriter writer = new QRCodeWriter(); + BitMatrix bitMatrix = null; + + try { + + bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, qrSize, qrSize, hints); + + + int height = bitMatrix.getHeight(); + int width = bitMatrix.getWidth(); + Bitmap qrImage = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + qrImage.setPixel(x, y, bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE); + } + } + + + if (logoPercentage > 0.5 || logoPercentage <= 0) logoPercentage = 0.17f; + + float logoSize = qrImage.getWidth() * logoPercentage; + + Bitmap combined = Bitmap.createBitmap(qrImage.getWidth(), + qrImage.getHeight(), qrImage.getConfig()); + Canvas canvas = new Canvas(combined); + + canvas.drawBitmap(qrImage, 0, 0, null); + + Bitmap iconBitmap = getBitmapFromVectorDrawable(context, drawableId, (int) logoSize); + if (iconBitmap != null) { + + + canvas.drawBitmap(iconBitmap, (int) ((width - logoSize) / 2), (int) ((height - logoSize) / 2), + null); + + } + return combined; + + } catch (WriterException e) { + e.printStackTrace(); + } + return null; + } + + + private static Bitmap getBitmapFromVectorDrawable(Context context, int drawableId, int size) { + Drawable drawable = ContextCompat.getDrawable(context, drawableId); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + drawable = (DrawableCompat.wrap(drawable)).mutate(); + } + + Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), + drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, size, size); + drawable.draw(canvas); + + return bitmap; + } + + + private static Bitmap getResizedBitmap(Bitmap bm, int newWidth, int newHeight) { + int width = bm.getWidth(); + int height = bm.getHeight(); + float scaleWidth = ((float) newWidth) / width; + float scaleHeight = ((float) newHeight) / height; + Matrix matrix = new Matrix(); + matrix.postScale(scaleWidth, scaleHeight); + Bitmap resizedBitmap = Bitmap.createBitmap( + bm, 0, 0, width, height, matrix, false); + bm.recycle(); + return resizedBitmap; + } + + +} diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/component/images/QRCodeImageView.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/component/images/QRCodeImageView.kt index 5801597d..1a6fa37e 100755 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/component/images/QRCodeImageView.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/component/images/QRCodeImageView.kt @@ -43,11 +43,14 @@ class QRCodeImageView : AutoLoadImageView { - var bitmap = QRCode.from(text) + /* var bitmap = QRCode.from(text) //.withHint(EncodeHintType.MARGIN, 0) .withColor(onColor, offColor) //.withErrorCorrection(ErrorCorrectionLevel.H) - .withSize(size, size).bitmap() + .withSize(size, size).bitmap()*/ + + var bitmap = FQRCode.generateFromVector( context,text, + R.drawable.ic_ic_forus_logo_backgr_w, size ) var margin = 4 if (size > 200) { diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/component/snackbar/UpdateAppSnackbar.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/component/snackbar/UpdateAppSnackbar.kt new file mode 100644 index 00000000..ad02fd63 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/component/snackbar/UpdateAppSnackbar.kt @@ -0,0 +1,54 @@ +package com.example.snackbarexample.customsnackbar.chef + +import android.support.design.widget.BaseTransientBottomBar +import android.support.design.widget.Snackbar +import android.support.v4.content.ContextCompat +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + + +import com.example.customsnackbar.extension.findSuitableParent +import io.forus.me.android.presentation.R + + +class UpdateAppSnackbar( + parent: ViewGroup, + content: UpdateAppSnackbarView +) : BaseTransientBottomBar(parent, content, content) { + + init { + getView().setBackgroundColor(ContextCompat.getColor(view.context, android.R.color.transparent)) + getView().setPadding(0, 0, 0, 0) + } + + companion object { + + fun make(view: View, updateClickListener: View.OnClickListener?): UpdateAppSnackbar { + + val parent = view.findSuitableParent() ?: throw IllegalArgumentException( + "No suitable parent found from the given view. Please provide a valid view." + ) + + val customView = LayoutInflater.from(view.context).inflate( + R.layout.layout_snackbar_update_app, + parent, + false + ) as UpdateAppSnackbarView + + if (updateClickListener != null) { + customView.setUpdateClickListener(updateClickListener) + } + + val updateAppSnackbar = UpdateAppSnackbar( + parent, + customView + ) + updateAppSnackbar.setDuration(Snackbar.LENGTH_INDEFINITE) + + return updateAppSnackbar + } + + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/component/snackbar/UpdateAppSnackbarView.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/component/snackbar/UpdateAppSnackbarView.kt new file mode 100644 index 00000000..40ae27e0 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/component/snackbar/UpdateAppSnackbarView.kt @@ -0,0 +1,47 @@ +package com.example.snackbarexample.customsnackbar.chef + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.content.Context +import android.support.constraint.ConstraintLayout +import android.support.design.snackbar.ContentViewCallback +import android.util.AttributeSet +import android.view.View +import android.view.animation.OvershootInterpolator +import android.widget.ImageView +import io.forus.me.android.presentation.R +import kotlinx.android.synthetic.main.view_snackbar_update_app.view.* + + +class UpdateAppSnackbarView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr), ContentViewCallback { + + private val chefImage: ImageView + + init { + View.inflate(context, R.layout.view_snackbar_update_app, this) + clipToPadding = false + this.chefImage = findViewById(R.id.image) + } + + override fun animateContentIn(delay: Int, duration: Int) { + val scaleX = ObjectAnimator.ofFloat(chefImage, View.SCALE_X, 0f, 1f) + val scaleY = ObjectAnimator.ofFloat(chefImage, View.SCALE_Y, 0f, 1f) + val animatorSet = AnimatorSet().apply { + interpolator = OvershootInterpolator() + setDuration(500) + playTogether(scaleX, scaleY) + } + animatorSet.start() + } + + fun setUpdateClickListener(listener: View.OnClickListener) { + update.setOnClickListener(listener) + } + + override fun animateContentOut(delay: Int, duration: Int) { + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/fragment/QrFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/fragment/QrFragment.kt index 6d91d7cd..febec928 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/fragment/QrFragment.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/fragment/QrFragment.kt @@ -19,17 +19,21 @@ class QrFragment : BaseFragment() { } } } + + var qrHead: String = "" var qrSubtitle : String = "" var qrDescription : String = "" companion object { private val QR_TEXT = "QR_TEXT" + private val QR_HEAD = "QR_HEAD" private val QR_SUBTITLE = "QR_SUBTITLE" private val QR_DESCRIPTION = "QR_DESCRIPTION" - fun newIntent(qrText: String, qrSubtitle: String?, qrDescription: String?): QrFragment = QrFragment().also { + fun newIntent(qrText: String, qrHead: String?, qrSubtitle: String?, qrDescription: String?): QrFragment = QrFragment().also { val bundle = Bundle() bundle.putString(QR_TEXT, qrText) + if(qrHead != null) bundle.putString(QR_HEAD, qrHead) if(qrSubtitle != null) bundle.putString(QR_SUBTITLE, qrSubtitle) if(qrDescription != null) bundle.putString(QR_DESCRIPTION, qrDescription) it.arguments = bundle @@ -52,6 +56,7 @@ class QrFragment : BaseFragment() { val bundle = this.arguments if (bundle != null) { qrText = bundle.getString(QR_TEXT, "") + qrHead = bundle.getString(QR_HEAD, "") qrSubtitle = bundle.getString(QR_SUBTITLE, "") qrDescription = bundle.getString(QR_DESCRIPTION, "") } @@ -59,6 +64,7 @@ class QrFragment : BaseFragment() { } override fun initUI() { + if(qrHead.isNotBlank()) head.text = qrHead if(qrSubtitle.isNotBlank()) subtitle.text = qrSubtitle if(qrDescription.isNotBlank()) description.text = qrDescription } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/fragment/ToolbarLRFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/fragment/ToolbarLRFragment.kt index 7232c56d..8a5543a4 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/fragment/ToolbarLRFragment.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/fragment/ToolbarLRFragment.kt @@ -83,6 +83,7 @@ abstract class ToolbarLRFragment, P : MviBasePresenter(), LogInSignUpView { - companion object { private val TOKEN_EXTRA = "TOKEN_EXTRA" @@ -39,18 +43,23 @@ class LogInSignUpFragment : ToolbarLRFragment() override fun register() = registerAction @@ -87,47 +95,67 @@ class LogInSignUpFragment : ToolbarLRFragment SharedPref.init(it1) - SharedPref.write(SharedPref.RESTORE_EMAIL, email.getText()); + context?.let { it1 -> + SharedPref.init(it1) + SharedPref.write(SharedPref.RESTORE_EMAIL, email!!.getText()); }; - registerAction.onNext(email.getText()) + registerAction.onNext(email!!.getText()) } } - pair_device.setOnClickListener { + pair_device!!.setOnClickListener { navigator.navigateToPairDevice(context!!) } @@ -148,22 +176,22 @@ class LogInSignUpFragment : ToolbarLRFragment + + val newApiType = ApiConfig.stringToApiType(text.toString()) + devOptionsBt!!.text = newApiType.name + + if (newApiType == ApiType.OTHER) { + + CustomApiDialog(context!!, storedOtherApiStr, MaterialDialog.InputCallback { _, input -> + + val customApiStr = input.toString() + storedOtherApiStr = customApiStr + + CheckApiPresenter(context!!).checkApi(customApiStr, + { result -> + run { + if (result) { + TestApiSuccessDialog(context!!, customApiStr) { + SaveApiAndRestartDialog(context!!) { + SharedPref.write(SharedPref.OPTION_CUSTOM_API_URL, customApiStr) + SharedPref.write(SharedPref.OPTION_API_TYPE, newApiType.name) + ApiConfig.changeToCustomApi(customApiStr) + Utils.instance.restartApp(context!!) + }.show() + }.show() + } else { + TestApiErrorDialog(context!!, "", {}).show() + } + } + }, + { throwable -> + run { + TestApiErrorDialog(context!!, throwable.localizedMessage, {}).show() + } + } + ) + + }, {}, {}).show() + } else { + SaveApiAndRestartDialog(context!!) { + SharedPref.write(SharedPref.OPTION_API_TYPE, newApiType.name) + ApiConfig.changeApi(newApiType) + Utils.instance.restartApp(context!!) + }.show() + } + + }) { }.show() + } + } + + } + fun closeScreen(accessToken: String) { navigator.navigateToPinNew(activity, accessToken) activity?.finish() diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/pair_device/PairDeviceFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/pair_device/PairDeviceFragment.kt index 8f2cf3ae..350a32ea 100755 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/pair_device/PairDeviceFragment.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/pair_device/PairDeviceFragment.kt @@ -36,6 +36,9 @@ class PairDeviceFragment : ToolbarLRFragment = Observable.never() @@ -98,7 +101,7 @@ class PairDeviceFragment : ToolbarLRFragment(), RestoreAccountSuccessView { +class RestoreAccountSuccessFragment : ToolbarLRFragment(), RestoreAccountSuccessView { companion object { private val TOKEN_EXTRA = "TOKEN_EXTRA" + private val IS_EXCHANGE_TOKEN = "IS_EXCHANGE_TOKEN" - fun newIntent(token: String): RestoreAccountSuccessFragment = RestoreAccountSuccessFragment().also { + fun newIntent(token: String, isExchangeToken: Boolean): RestoreAccountSuccessFragment = RestoreAccountSuccessFragment().also { val bundle = Bundle() bundle.putString(TOKEN_EXTRA, token) + bundle.putBoolean(IS_EXCHANGE_TOKEN, isExchangeToken) it.arguments = bundle } } private var token: String = "" - + private var isExchangeToken: Boolean = true private var instructionsAlreadyShown: Boolean = false @@ -54,7 +56,10 @@ class RestoreAccountSuccessFragment : ToolbarLRFragment() override fun exchangeToken() = exchangeToken - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View - = inflater.inflate(R.layout.fragment_account_restore_success, container, false).also { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = inflater.inflate(R.layout.fragment_account_restore_success, container, false).also { val bundle = this.arguments if (bundle != null) { token = bundle.getString(TOKEN_EXTRA, "") + isExchangeToken = bundle.getBoolean(IS_EXCHANGE_TOKEN) } + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -95,78 +101,98 @@ class RestoreAccountSuccessFragment : ToolbarLRFragment) { super.render(vs) - returnToRegistration.visibility = View.GONE + if (isExchangeToken) { + returnToRegistration.visibility = View.GONE - progress.visibility = if(vs.model.sendingRestoreByEmail == true) View.VISIBLE else View.INVISIBLE - if(vs.model.sendingRestoreByEmailSuccess == true && !instructionsAlreadyShown){ + progress.visibility = if (vs.model.sendingRestoreByEmail == true) View.VISIBLE else View.INVISIBLE - navigator.navigateToCheckEmail(context!!) - } + if (vs.model.sendingRestoreByEmailSuccess == true && !instructionsAlreadyShown) { - if(vs.model.sendingRestoreByEmail == true){ - (activity as? BaseActivity)?.hideSoftKeyboard() - } + navigator.navigateToCheckEmail(context!!) + } - if (vs.model.accessToken != null && vs.model.accessToken.isNotBlank()) { - successImage.visibility = View.VISIBLE - title.text = getString(R.string.restore_account_head) - nextStep.visibility = View.VISIBLE - }else{ - successImage.visibility = View.INVISIBLE - title.text = "" - nextStep.visibility = View.INVISIBLE - } + if (vs.model.sendingRestoreByEmail == true) { + (activity as? BaseActivity)?.hideSoftKeyboard() + } + + if (vs.model.accessToken != null && vs.model.accessToken.isNotBlank()) { + successImage.visibility = View.VISIBLE + title.text = getString(R.string.restore_account_head) + nextStep.visibility = View.VISIBLE + } else { + successImage.visibility = View.INVISIBLE + title.text = "" + nextStep.visibility = View.INVISIBLE + } - if(vs.model.exchangeTokenError != null){ - errorImage.visibility = View.VISIBLE - showToastMessage(resources.getString(R.string.restore_email_invalid_link)) - title.text = getString(R.string.restore_email_invalid_link) - }else - if(vs.loadingError != null){ - errorImage.visibility = View.VISIBLE - var message = "" + if (vs.model.exchangeTokenError != null) { + errorImage.visibility = View.VISIBLE + showToastMessage(resources.getString(R.string.restore_email_invalid_link)) + title.text = getString(R.string.restore_email_invalid_link) + } else + if (vs.loadingError != null) { + errorImage.visibility = View.VISIBLE + var message = "" - val error: Throwable = vs.loadingError!! - if (error is RetrofitException && error.kind == RetrofitException.Kind.HTTP) { + val error: Throwable = vs.loadingError + if (error is RetrofitException && error.kind == RetrofitException.Kind.HTTP) { - try { - val newRecordError = retrofitExceptionMapper.mapToBaseApiError(error) - message = if (newRecordError.message == null) "" else newRecordError.message + try { + val newRecordError = retrofitExceptionMapper.mapToBaseApiError(error) + message = if (newRecordError.message == null) "" else newRecordError.message - } catch (e: Exception) { + } catch (e: Exception) { + } + } + title.visibility = View.VISIBLE + title.text = message + + returnToRegistration.visibility = View.VISIBLE + + } else { + errorImage.visibility = View.INVISIBLE } - } - title.visibility = View.VISIBLE - title.text = message - returnToRegistration.visibility = View.VISIBLE - }else{ - errorImage.visibility = View.INVISIBLE - } + returnToRegistration.setOnClickListener { + navigator.navigateToLoginSignUp(activity) + activity?.finish() + } - returnToRegistration.setOnClickListener { - navigator.navigateToLoginSignUp(activity) - activity?.finish() - } - nextStep.setOnClickListener { - if (vs.model.accessToken != null && vs.model.accessToken.isNotBlank()) { - closeScreen(vs.model.accessToken) + nextStep.setOnClickListener { + if (vs.model.accessToken != null && vs.model.accessToken.isNotBlank()) { + closeScreen(vs.model.accessToken) + } + } + + } else { + progress.visibility = View.GONE + successImage.visibility = View.VISIBLE + returnToRegistration.visibility = View.GONE + errorImage.visibility = View.GONE + title.text = getString(R.string.restore_account_head) + nextStep.visibility = View.VISIBLE + nextStep.setOnClickListener { + if (token.isNotBlank()) { + closeScreen(token) + } } } } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/restore_account_success/RestoreAccountSuccessPresenter.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/restore_account_success/RestoreAccountSuccessPresenter.kt index 8f2bf316..48e46d62 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/restore_account_success/RestoreAccountSuccessPresenter.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/restore_account_success/RestoreAccountSuccessPresenter.kt @@ -13,14 +13,20 @@ import io.reactivex.schedulers.Schedulers /** * Created by maestrovs on 22.04.2020. */ -class RestoreAccountSuccessPresenter constructor(private val token: String, private val accountRepository: AccountRepository) : +class RestoreAccountSuccessPresenter constructor(private val token: String, private val accountRepository: AccountRepository,private val isExchangeToken: Boolean) : LRPresenter() { override fun initialModelSingle(): Single { - return if(token.isBlank()) - Single.just("") - else { - Single.fromObservable(accountRepository.registerExchangeToken(token).map { it.accessToken }) + + if (isExchangeToken) { + + return if (token.isBlank()) + Single.just("") + else { + Single.fromObservable(accountRepository.registerExchangeToken(token).map { it.accessToken }) + } + }else{ + return Single.just("") } } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/send_crash_reports/SendReportsFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/send_crash_reports/SendReportsFragment.kt index df8b0516..3e456c66 100755 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/send_crash_reports/SendReportsFragment.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/account/send_crash_reports/SendReportsFragment.kt @@ -1,6 +1,7 @@ package io.forus.me.android.presentation.view.screens.account.send_crash_reports import android.os.Bundle +import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -69,6 +70,17 @@ class SendReportsFragment : ToolbarLRFragment) { @@ -114,4 +128,5 @@ class DashboardActivity : SlidingPanelActivity(), DashboardContract.View { } + } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/dashboard/DashboardFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/dashboard/DashboardFragment.kt index 0bf8f4cf..b3426b6d 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/dashboard/DashboardFragment.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/dashboard/DashboardFragment.kt @@ -1,6 +1,8 @@ package io.forus.me.android.presentation.view.screens.dashboard +import android.content.Intent import android.os.Bundle +import android.os.Handler import android.support.v4.app.Fragment import android.util.Log import android.view.LayoutInflater @@ -8,10 +10,15 @@ import android.view.View import android.view.ViewGroup import com.aurelhubert.ahbottomnavigation.AHBottomNavigation import com.aurelhubert.ahbottomnavigation.AHBottomNavigationAdapter +import com.example.snackbarexample.customsnackbar.chef.UpdateAppSnackbar import io.forus.me.android.presentation.R +import io.forus.me.android.presentation.helpers.SharedPref import io.forus.me.android.presentation.view.activity.CommonActivity import io.forus.me.android.presentation.view.adapters.MainViewPagerAdapter import io.forus.me.android.presentation.view.screens.account.account.AccountFragment +import io.forus.me.android.presentation.view.screens.main.MainActivity +import io.forus.me.android.presentation.view.screens.records.categories.RecordCategoriesFragment +import io.forus.me.android.presentation.view.screens.records.list.RecordsFragment import io.forus.me.android.presentation.view.screens.vouchers.list.VouchersFragment import kotlinx.android.synthetic.main.fragment_dashboard.* @@ -67,7 +74,7 @@ class DashboardFragment : Fragment() { titles.add("") fragments.add(Fragment()) titles.add("") - //fragments.add(RecordCategoriesFragment.newInstance()) + //fragments.add(RecordsFragment()) fragments.add(AccountFragment()) titles.add("") @@ -79,8 +86,26 @@ class DashboardFragment : Fragment() { view_pager.adapter = adapter selectTab(currentPagerPosition, 0) + + + SharedPref.init(context!!) + val neesAppUpdate = SharedPref.read(SharedPref.OPTION_NEED_APP_UPDATE,false) + if(neesAppUpdate) { + h.postDelayed({ + UpdateAppSnackbar + .make(coordinator, View.OnClickListener { + val intent = Intent(context!!, MainActivity::class.java) + context!!.startActivity(intent) + activity!!.finish() + }) + .show() + }, 3000) + } + } + val h = Handler() + private fun selectTab(currentPagerPosition: Int, oldPosition: Int) { bottom_navigation.setCurrentItem(currentPagerPosition, false) try { diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/main/MainActivity.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/main/MainActivity.kt index 9cebd5fa..c55cb39b 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/main/MainActivity.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/main/MainActivity.kt @@ -1,13 +1,24 @@ package io.forus.me.android.presentation.view.screens.main +import android.app.Activity +import android.content.Intent +import android.content.IntentSender import android.os.Bundle -import android.util.Log +import android.os.Handler import io.forus.me.android.presentation.internal.Injection import io.forus.me.android.presentation.view.activity.BaseActivity import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.GoogleApiAvailability - +import com.google.android.play.core.appupdate.AppUpdateInfo +import com.google.android.play.core.appupdate.AppUpdateManager +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.install.model.AppUpdateType +import com.google.android.play.core.install.model.UpdateAvailability +import io.forus.me.android.presentation.BuildConfig +import io.forus.me.android.presentation.api_config.ApiConfig +import io.forus.me.android.presentation.api_config.ApiType +import io.forus.me.android.presentation.helpers.SharedPref /** @@ -20,32 +31,51 @@ class MainActivity : BaseActivity() { private val db = Injection.instance.databaseHelper private val settings = Injection.instance.settingsDataSource + var PACKAGE_NAME: String? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(io.forus.me.android.presentation.R.layout.activity_main) + PACKAGE_NAME = applicationContext.packageName + + SharedPref.init(this@MainActivity) + + + + + if (!BuildConfig.APPLICATION_ID.equals("io.forus.me")) { + val savedApiOption = SharedPref.read(SharedPref.OPTION_API_TYPE,"") ?: "" + if (savedApiOption.isNotEmpty()) { + val apiType = ApiConfig.stringToApiType(savedApiOption) + if (apiType == ApiType.OTHER) { + ApiConfig.changeToCustomApi(SharedPref.read(SharedPref.OPTION_CUSTOM_API_URL, BuildConfig.SERVER_URL)) + } else { + ApiConfig.changeApi(apiType) + } + } + } + val isGooglePlayAvailable = checkPlayServices() - Log.d("GOOGLE_PLAY_AVAILABLE", isGooglePlayAvailable.toString()) - if(db.exists()){ + //Check inApp update + h.postDelayed({ + this.initInAppUpdate() + }, 800) + + if (db.exists()) { val locked = settings.isPinEnabled() - if(locked){ + if (locked) { navigateToPinlock() - } - else { + } else { navigateToDashboard() } - } - else { + } else { //navigateToWelcomeScreen() //old behavior navigateToLogInsignUpScreen() } } - override fun onStop() { - super.onStop() - } - /** * Goes to the welcome screen. @@ -99,4 +129,73 @@ class MainActivity : BaseActivity() { return true } + + var h = Handler() + + val MY_REQUEST_CODE = 2514 + + var appUpdateManager: AppUpdateManager? = null + + private fun initInAppUpdate() { + + appUpdateManager = AppUpdateManagerFactory.create(this@MainActivity) + + if (appUpdateManager == null) return + + + val appUpdateInfoTask = appUpdateManager!!.appUpdateInfo + appUpdateInfoTask.addOnSuccessListener { appUpdateInfo -> + if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE + && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) { + + + SharedPref.write(SharedPref.OPTION_NEED_APP_UPDATE, true) + try { + //Toast.makeText(this@MainActivity, "initInAppUpdate success", Toast.LENGTH_SHORT).show() + updateApp(appUpdateInfo) + + } catch (e: IntentSender.SendIntentException) { + e.printStackTrace() + } + } else { + SharedPref.write(SharedPref.OPTION_NEED_APP_UPDATE, false) + } + } + + appUpdateInfoTask.addOnFailureListener { err -> + processInAppUpdateError(err.localizedMessage) + } + } + + + @Throws(IntentSender.SendIntentException::class) + private fun updateApp(appUpdateInfo: AppUpdateInfo) { + appUpdateManager!!.startUpdateFlowForResult( + // Pass the intent that is returned by 'getAppUpdateInfo()'. + appUpdateInfo, + // Or 'AppUpdateType.FLEXIBLE' for flexible updates. + AppUpdateType.IMMEDIATE, + // The current activity making the update request. + this, + // Include a request code to later monitor this update request. + MY_REQUEST_CODE) + + } + + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == MY_REQUEST_CODE) { + if (resultCode != Activity.RESULT_OK) { + processInAppUpdateError("") + } + } + } + + private fun processInAppUpdateError(error: String) { + + //Toast.makeText(this@MainActivity, "initInAppUpdate error $error", Toast.LENGTH_LONG).show() TODO + + } + + } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/qr/QrActionProcessor.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/qr/QrActionProcessor.kt index 4f53097c..f18c4490 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/qr/QrActionProcessor.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/qr/QrActionProcessor.kt @@ -1,7 +1,6 @@ package io.forus.me.android.presentation.view.screens.qr import android.content.DialogInterface -import android.support.design.widget.Snackbar import android.util.Log import io.forus.me.android.data.repository.settings.SettingsDataSource import io.forus.me.android.domain.exception.RetrofitException @@ -15,6 +14,7 @@ import io.forus.me.android.presentation.internal.Injection import io.forus.me.android.presentation.navigation.Navigator import io.forus.me.android.presentation.view.base.NoInternetDialog import io.forus.me.android.presentation.view.screens.qr.dialogs.* +import io.forus.me.android.presentation.view.screens.records.dialogs.validators_list_dialog.ValidatorsListDialog import io.forus.me.android.presentation.view.screens.vouchers.provider.ProviderActivity import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -47,33 +47,11 @@ class QrActionProcessor(private val scanner: QrScannerActivity, .map { validation -> if (validation.state == Validation.State.pending && validation.uuid != null) { if (scanner.hasWindowFocus()) - ApproveValidationDialog(scanner, - validation, - { - recordsRepository.approveValidation(validation.uuid!!) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .map { onResultValidationApproved() } - .onErrorReturn { - onResultUnexpectedError() - } - .subscribe() - }, - { - recordsRepository.declineValidation(validation.uuid!!) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .map { - onResultValidationDeclined() - } - .onErrorReturn { - onResultUnexpectedError() - } - .subscribe() - }, - reactivateDecoding) - .show() - } else onResultValidationAlreadyDone() + showChooseValidatorDialog(validation) + + } else{ + onResultValidationAlreadyDone() + } } .onErrorReturn { onResultUnexpectedError() @@ -110,45 +88,7 @@ class QrActionProcessor(private val scanner: QrScannerActivity, fun scanVoucher(address: String, isDemoVoucher: Boolean? = false) { if (isDemoVoucher != null && isDemoVoucher) { - vouchersRepository.makeDemoTransaction(address) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .map { - if (it) { - if (scanner.hasWindowFocus()) { - ScanDemoTransactionDialog(scanner) { - reactivateDecoding() - }.show() - } - } else { - ScanVoucherBaseErrorDialog("", scanner, reactivateDecoding).show() - } - } - .onErrorReturn { - var canOnResultVoucherScanned = true - val error: Throwable = it - if (error is RetrofitException && error.kind == RetrofitException.Kind.HTTP) { - - try { - val newRecordError = retrofitExceptionMapper.mapToBaseApiError(error) - - if (error.responseCode == 403) { - canOnResultVoucherScanned = false - val message = if (newRecordError.message == null) "" else newRecordError.message - ScanVoucherBaseErrorDialog(message, scanner, reactivateDecoding).show() - } - } catch (e: Exception) { - } - } - - if (canOnResultVoucherScanned) { - - if (scanner.hasWindowFocus()) { - onResultVoucherScanned(address, false) - } - } - } - .subscribe() + navigator.navigateToVoucherProvider(scanner, address, true) } else { vouchersRepository.getVoucherAsProvider(address) .subscribeOn(Schedulers.io()) @@ -273,6 +213,47 @@ class QrActionProcessor(private val scanner: QrScannerActivity, } + private fun showChooseValidatorDialog(validation: Validation){ + val validators = validation.organizationsAvailable + if(validators != null && validators.size > 0) { + ValidatorsListDialog(scanner, validators!! ){ + //selectOrganization.onNext(it) + ApproveValidationDialog(scanner, + validation, + { + recordsRepository.approveValidation(validation.uuid!!, it.id) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map { onResultValidationApproved() } + .onErrorReturn { + onResultUnexpectedError() + } + .subscribe() + }, + { + recordsRepository.declineValidation(validation.uuid!!) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map { + onResultValidationDeclined() + } + .onErrorReturn { + onResultUnexpectedError() + } + .subscribe() + }, + reactivateDecoding) + .show() + + }.show() + }else{ + + } + + + } + + private fun scanHasProductVouchers(address: String, isAvailableScannedVoucher: Boolean) { diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesAdapter.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesAdapter.kt index bc54913c..71928172 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesAdapter.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesAdapter.kt @@ -2,6 +2,7 @@ package io.forus.me.android.presentation.view.screens.records.categories import android.support.v7.util.DiffUtil import android.support.v7.widget.RecyclerView +import android.util.Log import android.view.ViewGroup import io.forus.me.android.domain.models.records.Record import io.forus.me.android.domain.models.records.RecordCategory @@ -22,7 +23,7 @@ class RecordCategoriesAdapter : RecyclerView.Adapter() { notifyDataSetChanged() } - + init { setHasStableIds(true) } @@ -34,6 +35,16 @@ class RecordCategoriesAdapter : RecyclerView.Adapter() { holder.render(items[position]) } + override fun getItemCount() = items.size override fun getItemId(position: Int) = position.toLong() + + + fun selectItem(item: RecordCategory) { + + items.forEach { + it.selected = it.id == item.id + } + notifyDataSetChanged() + } } \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesFragment.kt index 4e11db9e..2962ce62 100755 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesFragment.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesFragment.kt @@ -11,9 +11,15 @@ import io.forus.me.android.presentation.internal.Injection import io.forus.me.android.presentation.view.fragment.ToolbarLRFragment import io.forus.me.android.presentation.view.screens.records.list.RecordsAdapter import kotlinx.android.synthetic.main.fragment_record_categories.* +import android.support.v7.widget.DividerItemDecoration +import android.util.Log +import io.forus.me.android.presentation.view.component.dividers.FDividerItemDecoration +import io.forus.me.android.presentation.view.screens.records.categories.RecordCategoriesPresenter +import android.content.Context -class RecordCategoriesFragment : ToolbarLRFragment(), RecordCategoriesView{ + +class RecordCategoriesFragment : ToolbarLRFragment(), RecordCategoriesView { companion object { fun newIntent(): RecordCategoriesFragment { @@ -29,35 +35,45 @@ class RecordCategoriesFragment : ToolbarLRFragment -// navigator.navigateToRecordsList(activity, item) -// } -// recycler.layoutManager = LinearLayoutManager(context) -// recycler.adapter = adapter - adapter.clickListener = { item -> - navigator.navigateToRecordDetails(activity, item) + adapter.selectItem(item) + if (itemSelectListener != null) itemSelectListener!!.onItemSelected(item) + } - recycler.layoutManager = LinearLayoutManager(context) + + + val layoutManager = LinearLayoutManager(context) + recycler.layoutManager = layoutManager + val dividerItemDecoration = FDividerItemDecoration(recycler.getContext(), R.drawable.shape_divider_item_record) + + recycler.addItemDecoration(dividerItemDecoration) + recycler.adapter = adapter - btn_new_record.setOnClickListener{ + + + btn_new_record.setOnClickListener { this.navigator.navigateToNewRecord(activity) } } @@ -69,8 +85,11 @@ class RecordCategoriesFragment : ToolbarLRFragment) { super.render(vs) - //adapter.items = vs.model.items - adapter.records = vs.model.items + adapter.items = vs.model.items + } + + interface OnItemSelected { + fun onItemSelected(item: io.forus.me.android.domain.models.records.RecordCategory) } } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesModel.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesModel.kt index 1b354dba..ca0cc288 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesModel.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesModel.kt @@ -6,5 +6,5 @@ import io.forus.me.android.domain.models.records.RecordCategory data class RecordCategoriesModel( //val items: List = emptyList() - val items: List = emptyList() + val items: List = emptyList() ) \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesPresenter.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesPresenter.kt index 5f2629a6..9cd60bd3 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesPresenter.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesPresenter.kt @@ -9,15 +9,15 @@ import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers -class RecordCategoriesPresenter constructor(val recordsRepository: RecordsRepository) : LRPresenter, RecordCategoriesModel, RecordCategoriesView>() { +class RecordCategoriesPresenter constructor(val recordsRepository: RecordsRepository) : LRPresenter, RecordCategoriesModel, RecordCategoriesView>() { - override fun initialModelSingle(): Single> = Single.fromObservable( - //recordsRepository.getCategoriesWithRecordCount() - recordsRepository.getRecords() + override fun initialModelSingle(): Single> = Single.fromObservable( + recordsRepository.getCategoriesWithRecordCount() + //recordsRepository.getRecords() ) - override fun RecordCategoriesModel.changeInitialModel(i: List): RecordCategoriesModel = copy(items = i) + override fun RecordCategoriesModel.changeInitialModel(i: List): RecordCategoriesModel = copy(items = i) override fun bindIntents() { diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesVH.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesVH.kt index e1cddd33..ff81b878 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesVH.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/categories/RecordCategoriesVH.kt @@ -1,7 +1,9 @@ package io.forus.me.android.presentation.view.screens.records.categories import android.support.v7.widget.RecyclerView +import android.util.Log import android.view.ViewGroup +import android.widget.CompoundButton import io.forus.me.android.domain.models.records.RecordCategory import io.forus.me.android.presentation.R import io.forus.me.android.presentation.helpers.inflate @@ -15,14 +17,15 @@ class RecordCategoriesVH(parent: ViewGroup, private val clickListener: ((RecordC fun render(item: RecordCategory) = with(itemView) { - num_records.text = "${item.size} eigenschappen" - name.text = item.name - if(item.logo.isNotEmpty()) logo.setImageUrl(item.logo) + name.text = item.name root.setOnClickListener { clickListener?.invoke(item) } + selectedCheckBox.setChecked(item.selected) } + + } \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/CreateRecordActivity.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/CreateRecordActivity.kt new file mode 100644 index 00000000..5e0a6104 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/CreateRecordActivity.kt @@ -0,0 +1,182 @@ +package io.forus.me.android.presentation.view.screens.records.create_record + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.v4.content.ContextCompat +import android.support.v7.app.AppCompatActivity +import androidx.navigation.NavOptions +import androidx.navigation.fragment.findNavController +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.domain.exception.RetrofitException +import io.forus.me.android.domain.exception.RetrofitExceptionMapper +import io.forus.me.android.domain.models.records.NewRecordRequest +import io.forus.me.android.domain.models.records.RecordType +import io.forus.me.android.presentation.R +import io.forus.me.android.presentation.internal.Injection +import io.forus.me.android.presentation.view.base.NoInternetDialog +import io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment.CreateRecordFragment +import io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment.CreateRecordFragment.Companion.RECORD_TYPE_NAME_EXTRA +import io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment.CreateRecordFragment.Companion.RECORD_INPUT_FIELD_TYPE_EXTRA +import io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment.CreateRecordModel +import io.forus.me.android.presentation.view.screens.records.create_record.dialog.CreateRecordErrorDialog +import io.forus.me.android.presentation.view.screens.records.create_record.dialog.CreateRecordSuccessDialog +import io.forus.me.android.presentation.view.screens.records.create_record.dialog.WaitDialog +import io.forus.me.android.presentation.view.screens.records.types.RecordTypesFragment +import kotlinx.android.synthetic.main.activity_create_category_flow.* +import java.lang.Exception + + +class CreateRecordActivity : AppCompatActivity(), RecordTypesFragment.OnItemSelected, CreateRecordFragment.OnInputRecordNameText { + + + companion object { + + fun getCallingIntent(context: Context): Intent { + val intent = Intent(context, CreateRecordActivity::class.java) + + return intent + } + } + + var step = 1 + var recordType: io.forus.me.android.domain.models.records.RecordType? = null + + var recordNameText: String? = null + + private var retrofitExceptionMapper: RetrofitExceptionMapper = Injection.instance.retrofitExceptionMapper + + var waitDialog: WaitDialog? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_create_category_flow) + + val navOptions = NavOptions.Builder() + .setEnterAnim(R.anim.nav_default_enter_anim) + .setExitAnim(R.anim.nav_default_exit_anim) + .setPopEnterAnim(R.anim.nav_default_pop_enter_anim) + .setPopExitAnim(R.anim.nav_default_pop_exit_anim) + .build() + + statusNextButton(false) + statusCurrentStep(step) + + nextBt.setOnClickListener { + if (step == 1) { + val args = Bundle() + if (recordType != null) { + // args.putString(RECORD_TYPE_KEY_EXTRA, recordType!!.key) + args.putString(RECORD_TYPE_NAME_EXTRA, recordType!!.name) + args.putString(RECORD_INPUT_FIELD_TYPE_EXTRA, recordType!!.type) + nav_host_fragment.findNavController().navigate(R.id.createRecordFragment, args, navOptions) + step = 2 + + statusNextButton(false) + statusCurrentStep(step) + } + } else if (step == 2) { + if (recordNameText != null) { + waitDialog = WaitDialog(this@CreateRecordActivity) + waitDialog!!.show() + val model = CreateRecordModel(Injection.instance.recordsRepository) + model.createRecord(NewRecordRequest(recordType, null, mutableListOf(), recordNameText!!), { createRecordResponse -> + if(waitDialog!=null)waitDialog!!.dismiss() + showSuccessDialog(recordType!!.name, recordNameText!!) + }, { error -> + if(waitDialog!=null)waitDialog!!.dismiss() + parseError(error) + }) + } + } + } + + + + + closeBt.setOnClickListener { + finish() + + } + } + + private fun showSuccessDialog(recordType: String, recordName: String) { + CreateRecordSuccessDialog.display(supportFragmentManager, recordType, + recordName) { finish() } + } + + private fun parseError(error: Throwable) { + + if (error is io.forus.me.android.data.exception.RetrofitException && error.kind == RetrofitException.Kind.NETWORK) { + NoInternetDialog(this@CreateRecordActivity) { }.show(); + } else { + + if (error is RetrofitException && error.kind == RetrofitException.Kind.HTTP) { + + + try { + val detailsError = retrofitExceptionMapper.mapToDetailsApiError(error) + + + CreateRecordErrorDialog(this@CreateRecordActivity,detailsError.message,detailsError.errorsString, + MaterialDialog.SingleButtonCallback { _, _ -> + + } ).show() + + + + } catch (e: Exception) { + } + + } else { + } + + } + } + + + override fun onItemSelected(item: io.forus.me.android.domain.models.records.RecordType) { + recordType = item + statusNextButton(true) + } + + override fun onRecordTypesFragmentResume() { + step = 1 + statusNextButton(false) + statusCurrentStep(step) + } + + + override fun onTextInput(text: String) { + recordNameText = text + if (recordNameText!!.isNotEmpty() && recordNameText!!.length > 0) { + statusNextButton(true) + } else { + statusNextButton(false) + } + } + + override fun onRecordTypesLoaded(list: List) { + } + + private fun statusNextButton(isActive: Boolean) { + nextBt.isEnabled = isActive + nextBt.background = if (isActive) ContextCompat.getDrawable(this@CreateRecordActivity, R.drawable.button_main_round_blue) + else ContextCompat.getDrawable(this@CreateRecordActivity, R.drawable.button_main_raund_reverse) + nextBt.setTextColor(if (isActive) ContextCompat.getColor(this@CreateRecordActivity, R.color.colorAccent) + else ContextCompat.getColor(this@CreateRecordActivity, R.color.body_1_38)) + } + + private fun statusCurrentStep(step: Int) { + + step1View.background = if (step == 1) ContextCompat.getDrawable(this@CreateRecordActivity, R.drawable.button_main_raund) + else ContextCompat.getDrawable(this@CreateRecordActivity, R.drawable.button_main_raund_reverse) + step2View.background = if (step == 2) ContextCompat.getDrawable(this@CreateRecordActivity, R.drawable.button_main_raund) + else ContextCompat.getDrawable(this@CreateRecordActivity, R.drawable.button_main_raund_reverse) + + nextBt.text = if (step == 1) getString(R.string.next_step) else getString(R.string.submit) + + } + + +} diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/EditRecordActivity.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/EditRecordActivity.kt new file mode 100644 index 00000000..5d99dad5 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/EditRecordActivity.kt @@ -0,0 +1,221 @@ +package io.forus.me.android.presentation.view.screens.records.create_record + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.v4.content.ContextCompat +import android.support.v7.app.AppCompatActivity +import android.util.Log +import androidx.navigation.NavOptions +import androidx.navigation.findNavController +import androidx.navigation.fragment.findNavController +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.domain.exception.RetrofitException +import io.forus.me.android.domain.exception.RetrofitExceptionMapper +import io.forus.me.android.domain.models.records.NewRecordRequest +import io.forus.me.android.domain.models.records.RecordType +import io.forus.me.android.presentation.R +import io.forus.me.android.presentation.internal.Injection +import io.forus.me.android.presentation.view.base.NoInternetDialog +import io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment.CreateRecordFragment +import io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment.CreateRecordFragment.Companion.RECORD_INPUT_FIELD_TYPE_EXTRA +import io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment.CreateRecordFragment.Companion.RECORD_TYPE_NAME_EXTRA +import io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment.CreateRecordFragment.Companion.RECORD_TYPE_VALUE_EXTRA +import io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment.CreateRecordModel +import io.forus.me.android.presentation.view.screens.records.create_record.dialog.CreateRecordErrorDialog +import io.forus.me.android.presentation.view.screens.records.create_record.dialog.CreateRecordSuccessDialog +import io.forus.me.android.presentation.view.screens.records.create_record.dialog.WaitDialog +import io.forus.me.android.presentation.view.screens.records.types.RecordTypesFragment +import kotlinx.android.synthetic.main.activity_create_category_flow.* + + +class EditRecordActivity : AppCompatActivity(), RecordTypesFragment.OnItemSelected, CreateRecordFragment.OnInputRecordNameText { + + + companion object { + + + val RECORD_ID = "RECORD_ID" + val RECORD_TYPE = "RECORD_TYPE" + val RECORD_VALUE = "RECORD_VALUE" + + fun getCallingIntent(context: Context, recordId: Long,recordType: String, recordValue: String): Intent { + val intent = Intent(context, EditRecordActivity::class.java) + + intent.putExtra(RECORD_ID, recordId) + intent.putExtra(RECORD_TYPE, recordType) + intent.putExtra(RECORD_VALUE, recordValue) + return intent + } + } + + var waitDialog: WaitDialog? = null + + var recordType: io.forus.me.android.domain.models.records.RecordType? = null + + var recordId: Long? = null + var recordName = "" + var recordValue = "" + + var recordNameText: String? = null + + private var retrofitExceptionMapper: RetrofitExceptionMapper = Injection.instance.retrofitExceptionMapper + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_create_category_flow) + + val navOptions = NavOptions.Builder() + .setEnterAnim(R.anim.nav_default_enter_anim) + .setExitAnim(R.anim.nav_default_exit_anim) + .setPopEnterAnim(R.anim.nav_default_pop_enter_anim) + .setPopExitAnim(R.anim.nav_default_pop_exit_anim) + .build() + + if (savedInstanceState == null) { + recordId = intent.getLongExtra(RECORD_ID,-1) + recordName = intent.getStringExtra(RECORD_TYPE) + recordValue = intent.getStringExtra(RECORD_VALUE) + } + + + val navController = findNavController(R.id.nav_host_fragment) + val bundle = Bundle() + bundle.putInt("showList", 0) + + navController.setGraph(navController.graph, bundle) + + + + + statusNextButton(false) + statusCurrentStep(2) + + nextBt.setOnClickListener { + + waitDialog = WaitDialog(this@EditRecordActivity) + waitDialog!!.show() + if (recordNameText != null) { + val model = CreateRecordModel(Injection.instance.recordsRepository) + model.createRecord(NewRecordRequest(recordType, null, mutableListOf(), recordNameText!!), { createRecordResponse -> + if(recordId != null) { + model.deleteRecord(recordId!!, { + showSuccessDialog(recordType!!.name, recordNameText!!) + }, { error -> + if(waitDialog!=null)waitDialog!!.dismiss() + parseError(error) + }) + } + }, { error -> + if(waitDialog!=null)waitDialog!!.dismiss() + parseError(error) + }) + } + + } + + + closeBt.setOnClickListener { + finish() + } + } + + + + + private fun showSuccessDialog(recordType: String, recordName: String) { + CreateRecordSuccessDialog.display(supportFragmentManager, recordType, + recordName) { finish() } + } + + private fun parseError(error: Throwable) { + + if (error is io.forus.me.android.data.exception.RetrofitException && error.kind == RetrofitException.Kind.NETWORK) { + NoInternetDialog(this@EditRecordActivity) { }.show(); + } else { + + if (error is RetrofitException && error.kind == RetrofitException.Kind.HTTP) { + + try { + val detailsError = retrofitExceptionMapper.mapToDetailsApiError(error) + + + CreateRecordErrorDialog(this@EditRecordActivity, detailsError.message, detailsError.errorsString, + MaterialDialog.SingleButtonCallback { _, _ -> + + }).show() + + + } catch (e: Exception) { + } + + } + + } + } + + + override fun onRecordTypesLoaded(list: List) { + + for (type in list) { + if (type.name == recordName) { + this.recordType = type + break + } + } + + val args = Bundle() + args.putString(RECORD_TYPE_NAME_EXTRA, recordName) + args.putString(RECORD_TYPE_VALUE_EXTRA, recordValue) + if (recordType != null) args.putString(RECORD_INPUT_FIELD_TYPE_EXTRA, recordType!!.type) + nav_host_fragment.findNavController().navigate(R.id.createRecordFragment, args) + statusCurrentStep(2) + + + } + + + override fun onItemSelected(item: io.forus.me.android.domain.models.records.RecordType) { + recordType = item + statusNextButton(true) + } + + override fun onRecordTypesFragmentResume() { + + statusNextButton(false) + statusCurrentStep(2) + } + + + override fun onTextInput(text: String) { + Log.d("forus","onTextInput = $text") + recordNameText = text + if (recordNameText!!.isNotEmpty() && recordNameText!!.length > 0) { + statusNextButton(true) + } else { + statusNextButton(false) + } + } + + private fun statusNextButton(isActive: Boolean) { + nextBt.isEnabled = isActive + nextBt.background = if (isActive) ContextCompat.getDrawable(this@EditRecordActivity, R.drawable.button_main_round_blue) + else ContextCompat.getDrawable(this@EditRecordActivity, R.drawable.button_main_raund_reverse) + nextBt.setTextColor(if (isActive) ContextCompat.getColor(this@EditRecordActivity, R.color.colorAccent) + else ContextCompat.getColor(this@EditRecordActivity, R.color.body_1_38)) + } + + private fun statusCurrentStep(step: Int) { + + step1View.background = if (step == 1) ContextCompat.getDrawable(this@EditRecordActivity, R.drawable.button_main_raund) + else ContextCompat.getDrawable(this@EditRecordActivity, R.drawable.button_main_raund_reverse) + step2View.background = if (step == 2) ContextCompat.getDrawable(this@EditRecordActivity, R.drawable.button_main_raund) + else ContextCompat.getDrawable(this@EditRecordActivity, R.drawable.button_main_raund_reverse) + + nextBt.text = if (step == 1) getString(R.string.next_step) else getString(R.string.submit) + + } + + + +} diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/FFragmentNavigator.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/FFragmentNavigator.kt new file mode 100644 index 00000000..e2a9aa07 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/FFragmentNavigator.kt @@ -0,0 +1,42 @@ +package io.forus.me.android.presentation.view.screens.records.create_record + +import android.content.Context +import android.os.Bundle +import android.support.v4.app.FragmentManager + +import androidx.navigation.NavDestination +import androidx.navigation.NavOptions +import androidx.navigation.Navigator +import androidx.navigation.fragment.FragmentNavigator + +@Navigator.Name("fragment") +class FFragmentNavigator( + context: Context, + fm: FragmentManager, + containerId: Int +) : FragmentNavigator(context, fm, containerId) { + + /*override fun navigate(...): NavDestination? { + val shouldSkip = navOptions?.run { + popUpTo == destination.id && !isPopUpToInclusive + } ?: false + + return if (shouldSkip) null + else super.navigate(destination, args, navOptions, navigatorExtras) + }*/ + + override fun navigate( + destination: Destination, + args: Bundle?, + navOptions: NavOptions?, + navigatorExtras: Navigator.Extras? + ): NavDestination? { + + val shouldSkip = navOptions?.run { + popUpTo == destination.id && !isPopUpToInclusive + } ?: false + + return if (shouldSkip) null + else super.navigate(destination, args, navOptions, navigatorExtras) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/FNavHostFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/FNavHostFragment.kt new file mode 100644 index 00000000..55690e7b --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/FNavHostFragment.kt @@ -0,0 +1,8 @@ +package io.forus.me.android.presentation.view.screens.records.create_record + +import androidx.navigation.fragment.NavHostFragment + +class FNavHostFragment : NavHostFragment() { + override fun createFragmentNavigator() = + FFragmentNavigator(requireContext(), childFragmentManager, id) +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/create_record_fragment/CreateRecordFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/create_record_fragment/CreateRecordFragment.kt new file mode 100644 index 00000000..e46326ec --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/create_record_fragment/CreateRecordFragment.kt @@ -0,0 +1,122 @@ +package io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment + + +import android.content.Context +import android.os.Bundle +import android.support.v4.app.Fragment +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import io.forus.me.android.presentation.R +import io.forus.me.android.presentation.view.screens.records.item.RecordDetailsFragment +import kotlinx.android.synthetic.main.fragment_create_record.* +import android.text.Editable +import android.text.InputType +import android.text.TextWatcher + + +/** + * A simple [Fragment] subclass. + */ +class CreateRecordFragment : Fragment() { + + companion object { + + val RECORD_TYPE_NAME_EXTRA = "RECORD_TYPE_NAME_EXTRA" + val RECORD_TYPE_VALUE_EXTRA = "RECORD_TYPE_VALUE_EXTRA" + val RECORD_INPUT_FIELD_TYPE_EXTRA = "RECORD_INPUT_FIELD_TYPE_EXTRA" + + fun newIntent( recordTypeName: String, recordValue: String, recordTypeType: String): RecordDetailsFragment = RecordDetailsFragment().also { + val bundle = Bundle() + + bundle.putSerializable(RECORD_TYPE_NAME_EXTRA, recordTypeName) + bundle.putSerializable(RECORD_TYPE_VALUE_EXTRA, recordValue) + bundle.putSerializable(RECORD_INPUT_FIELD_TYPE_EXTRA, recordTypeType) + it.arguments = bundle + } + } + + + private var recordTypeName: String = "" + private var recordValue: String = "" + private var recordInputFieldType: String = "" + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + // Inflate the layout for this fragment + + val bundle = this.arguments + if (bundle != null) { + + recordTypeName = bundle.getString(RECORD_TYPE_NAME_EXTRA) ?: "" + recordValue = bundle.getString(RECORD_TYPE_VALUE_EXTRA) ?: "" + recordInputFieldType = bundle.getString(RECORD_INPUT_FIELD_TYPE_EXTRA) ?: "" + + } + + + Log.d("forus", "name = $recordTypeName") + + + + + + return inflater.inflate(R.layout.fragment_create_record, container, false) + } + + var inputTextListener: OnInputRecordNameText? = null + + override fun onAttach(context: Context) { + super.onAttach(context) + inputTextListener = context as OnInputRecordNameText + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + recordGroupNameTV.text = recordTypeName + recordNameEditText.setText(recordValue) + if(inputTextListener!=null){ + if(recordValue.isNotEmpty()){ + inputTextListener!!.onTextInput(recordValue) + } + } + + when (recordInputFieldType) { + "string" -> { + recordNameEditText.maxLines = 1 + recordNameEditText.inputType = InputType.TYPE_CLASS_TEXT + //recordNameEditText.imeOptions = EditorInfo.IME_ACTION_DONE + } + "text" -> { + recordNameEditText.maxLines = 1000 + recordNameEditText.inputType = InputType.TYPE_CLASS_TEXT + } + "number" -> { + recordNameEditText.maxLines = 1 + recordNameEditText.inputType = InputType.TYPE_CLASS_NUMBER + } + } + + recordNameEditText.addTextChangedListener(object : TextWatcher { + + override fun afterTextChanged(s: Editable) { + + if (inputTextListener != null) { + inputTextListener!!.onTextInput(s.toString()) + } + } + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} + }) + } + + interface OnInputRecordNameText { + fun onTextInput(text: String) + } + + +} diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/create_record_fragment/CreateRecordModel.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/create_record_fragment/CreateRecordModel.kt new file mode 100644 index 00000000..d5a2547d --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/create_record_fragment/CreateRecordModel.kt @@ -0,0 +1,52 @@ +package io.forus.me.android.presentation.view.screens.records.create_record.create_record_fragment + +import android.util.Log +import io.forus.me.android.data.repository.records.RecordsRepository +import io.forus.me.android.domain.models.records.CreateRecordResponse +import io.forus.me.android.domain.models.records.NewRecordRequest +import io.forus.me.android.presentation.view.base.lr.PartialChange +import io.forus.me.android.presentation.view.screens.records.item.RecordDetailsPartialChanges +import io.forus.me.android.presentation.view.screens.records.newrecord.NewRecordPartialChanges +import io.reactivex.Observable +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers + +class CreateRecordModel(private val recordRepository: RecordsRepository) { + + + fun createRecord(request: NewRecordRequest, success: (CreateRecordResponse) -> Unit, error: (Throwable) -> Unit) { + Single.fromObservable(recordRepository.newRecord(request!!) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .switchMap { createRecordResponse -> + success(createRecordResponse) + Observable.just(NewRecordPartialChanges.CreateRecordEnd(createRecordResponse)) + } + .onErrorReturn { + error(it) + NewRecordPartialChanges.CreateRecordError(it) + }) + .subscribe() + } + + fun getRecordTypes(success: (CreateRecordResponse) -> Unit, error: (Throwable) -> Unit) { + Single.fromObservable(recordRepository.getRecordTypes()) + .subscribe() + + } + + fun deleteRecord(id: Long, success: (Boolean) -> Unit, error: (Throwable) -> Unit) { + Single.fromObservable(recordRepository.deleteRecord(id) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .switchMap { + success(it) + Observable.just(true) + } + .onErrorReturn { + error(it) + false + }).subscribe() + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/dialog/CreateRecordErrorDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/dialog/CreateRecordErrorDialog.kt new file mode 100644 index 00000000..b3563b7f --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/dialog/CreateRecordErrorDialog.kt @@ -0,0 +1,34 @@ +package io.forus.me.android.presentation.view.screens.records.create_record.dialog + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.text.SpannableString +import android.text.method.LinkMovementMethod +import android.text.util.Linkify +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.presentation.R +import kotlinx.android.synthetic.main.fragment_account_details.* + +class CreateRecordErrorDialog(private val context: Context,private val title: String,private val message: String, callback: MaterialDialog.SingleButtonCallback){ + + private val dialog: MaterialDialog = MaterialDialog.Builder(context) + .title(title) + .customView(R.layout.view_about_me, false) + .positiveText(context.resources.getString(R.string.me_ok)) + .onPositive(callback) + .build() + + + + init { + val view = dialog.customView + val messageTV = view?.findViewById(R.id.message); + + messageTV?.setText(message); + } + + fun show(){ + dialog.show() + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/dialog/CreateRecordSuccessDialog.java b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/dialog/CreateRecordSuccessDialog.java new file mode 100644 index 00000000..ee5d07f6 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/dialog/CreateRecordSuccessDialog.java @@ -0,0 +1,114 @@ +package io.forus.me.android.presentation.view.screens.records.create_record.dialog; + +import android.app.Dialog; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.TextView; + +import io.forus.me.android.presentation.R; + + +public class CreateRecordSuccessDialog extends DialogFragment { + + View rootView; + + private String recordType = ""; + private String recordName = ""; + String positiveButtonText= "OK"; + + SubmitClickListener submitClickListener; + + + + public static CreateRecordSuccessDialog display(FragmentManager fragmentManager, String recordType, String recordNameTV, SubmitClickListener submitClickListener) { + CreateRecordSuccessDialog fullscreenDialog = new CreateRecordSuccessDialog(); + fullscreenDialog.recordType = recordType; + fullscreenDialog.recordName = recordNameTV; + fullscreenDialog.submitClickListener = submitClickListener; + + fullscreenDialog.show(fragmentManager, ""); + return fullscreenDialog; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_FullScreenDialog); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + rootView = inflater.inflate(R.layout.dialog_create_record_success, container, false); + + + return rootView; + } + + @Override + public void onStart() { + super.onStart(); + Dialog dialog = getDialog(); + if (dialog != null) { + int width = ViewGroup.LayoutParams.MATCH_PARENT; + int height = ViewGroup.LayoutParams.MATCH_PARENT; + if (dialog.getWindow() != null) { + dialog.getWindow().setLayout(width, height); + } + } + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + TextView recordTypeTV = rootView.findViewById(R.id.recordTypeTV); + TextView recordNameTV = rootView.findViewById(R.id.recordNameTV); + io.forus.me.android.presentation.view.component.buttons.Button submitButton = rootView.findViewById(R.id.submitButton); + + recordTypeTV.setText(recordType); + recordNameTV.setText(recordName); + + rootView.findViewById(R.id.closeBt).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + CreateRecordSuccessDialog.this.dismiss(); + } + }); + + submitButton.setText(positiveButtonText); + + if (submitClickListener != null) { + submitButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (submitClickListener != null) { + CreateRecordSuccessDialog.this.dismiss(); + submitClickListener.click(CreateRecordSuccessDialog.this); + } + } + }); + } + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Dialog dialog = super.onCreateDialog(savedInstanceState); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + return dialog; + } + + public interface SubmitClickListener { + void click(CreateRecordSuccessDialog dialog); + } + + +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/dialog/WaitDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/dialog/WaitDialog.kt new file mode 100644 index 00000000..62a33e3b --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/create_record/dialog/WaitDialog.kt @@ -0,0 +1,60 @@ +package io.forus.me.android.presentation.view.screens.records.create_record.dialog + +import android.app.Activity +import android.app.AlertDialog +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.net.Uri +import android.text.SpannableString +import android.text.method.LinkMovementMethod +import android.text.util.Linkify +import android.view.Gravity +import android.view.ViewGroup +import android.view.WindowManager +import android.widget.LinearLayout +import android.widget.ProgressBar +import android.widget.TextView +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.presentation.R +import io.forus.me.android.presentation.view.screens.records.item.dialogs.RecordModifyDialog +import kotlinx.android.synthetic.main.fragment_account_details.* + +class WaitDialog(private val context: Activity){ + + var dialog: AlertDialog + + + + init { + val dialogBuilder = AlertDialog.Builder(context) + val inflater = context.layoutInflater + val dialogView = inflater.inflate(R.layout.view_wait_dialog, null) + + dialogBuilder.setView(dialogView) + dialog = dialogBuilder.create() + if (dialog.window != null) { + dialog.window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + } + } + + fun show(){ + dialog.show() + val window = dialog.window + if (window != null) { + val layoutParams = WindowManager.LayoutParams() + layoutParams.copyFrom(dialog.window.attributes) + layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT + layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT + dialog.window.attributes = layoutParams + } + } + + fun dismiss(){ + if(dialog.isShowing){ + dialog.dismiss() + } + + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/dialogs/validators_list_dialog/ValidatorsAdapter.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/dialogs/validators_list_dialog/ValidatorsAdapter.kt new file mode 100644 index 00000000..6ecd3088 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/dialogs/validators_list_dialog/ValidatorsAdapter.kt @@ -0,0 +1,35 @@ +package io.forus.me.android.presentation.view.screens.records.dialogs.validators_list_dialog + +import android.support.v7.widget.RecyclerView +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import io.forus.me.android.presentation.R + + + +class ValidatorsAdapter(var items: List, val callback: Callback) : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = MainHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_validator, parent, false)) + override fun getItemCount() = items.size + override fun onBindViewHolder(holder: MainHolder, position: Int) { + holder.bind(items[position]) + } + + inner class MainHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val iv_organization_icon = itemView.findViewById(R.id.iv_organization_icon) + private val tv_organization_name = itemView.findViewById(R.id.tv_organization_name) + fun bind(item: io.forus.me.android.domain.models.records.ValidatorOrganization) { + tv_organization_name.text = item.name + // if (item.logo?.isBlank() != true) + // iv_organization_icon.setImageUrl(item.logo) + itemView.setOnClickListener { + if (adapterPosition != RecyclerView.NO_POSITION) callback.onItemClicked(items[adapterPosition]) + } + } + } + + interface Callback { + fun onItemClicked(item: io.forus.me.android.domain.models.records.ValidatorOrganization) + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/dialogs/validators_list_dialog/ValidatorsListDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/dialogs/validators_list_dialog/ValidatorsListDialog.kt new file mode 100644 index 00000000..a28ca6f3 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/dialogs/validators_list_dialog/ValidatorsListDialog.kt @@ -0,0 +1,71 @@ +package io.forus.me.android.presentation.view.screens.records.dialogs.validators_list_dialog + +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.support.v7.widget.RecyclerView +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.presentation.R +import android.support.v7.widget.LinearLayoutManager +import android.view.WindowManager +import android.widget.ImageView +import android.support.v7.widget.DividerItemDecoration + +import android.support.v4.content.ContextCompat + + +class ValidatorsListDialog(private val context: Context, + private val organizations: List, + private val selectItemCallback: (io.forus.me.android.domain.models.records.ValidatorOrganization) -> Unit) { + + private val dialog: MaterialDialog = MaterialDialog.Builder(context) + //.title(context.resources.getString(R.string.voucher_dialog_choose_organization)) + .customView(R.layout.view_validators_list, true) + //.negativeText(context.resources.getString(R.string.me_cancel)) + .build() + + init { + + + val view = dialog.customView + val recycler = view?.findViewById(R.id.recycler) + + val mAdapter = ValidatorsAdapter(organizations, object : ValidatorsAdapter.Callback { + override fun onItemClicked(item: io.forus.me.android.domain.models.records.ValidatorOrganization) { + selectItemCallback.invoke(item) + dialog.dismiss() + } + }) + recycler!!.layoutManager = LinearLayoutManager(context) + + + val dividerItemDecoration = DividerItemDecoration(context, + LinearLayoutManager.VERTICAL) + dividerItemDecoration.setDrawable(ContextCompat.getDrawable(context,R.drawable.shape_divider)!!) + recycler.addItemDecoration(dividerItemDecoration) + /*val dividerItemDecorationVertical = DividerItemDecoration(context, + LinearLayoutManager.VERTICAL) + + dividerItemDecorationVertical.setDrawable(getContext().getResources().getDrawable(R.drawable.line_decoration)) + recyclerView.addItemDecoration(dividerItemDecorationVertical)*/ + + + + recycler.adapter = mAdapter + + view?.findViewById(R.id.closeBt).setOnClickListener { dialog.dismiss() } + + + dialog.window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) + // dialog.window!!.setLayout(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + dialog.window!!.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + + } + + fun show() { + dialog.show() + + } + + +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsFragment.kt index e8a1ce39..900e17cd 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsFragment.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsFragment.kt @@ -1,24 +1,30 @@ package io.forus.me.android.presentation.view.screens.records.item +import android.app.Activity import android.os.Bundle import android.support.v7.widget.LinearLayoutManager import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import io.forus.me.android.domain.exception.RetrofitException +import io.forus.me.android.domain.exception.RetrofitExceptionMapper import io.forus.me.android.presentation.view.base.lr.LRViewState import io.forus.me.android.presentation.R import io.forus.me.android.presentation.internal.Injection import io.forus.me.android.presentation.view.fragment.ToolbarLRFragment +import io.forus.me.android.presentation.view.screens.records.create_record.EditRecordActivity +import io.forus.me.android.presentation.view.screens.records.item.dialogs.DeleteRecordErrorDialog +import io.forus.me.android.presentation.view.screens.records.item.dialogs.RecordModifyDialog import io.forus.me.android.presentation.view.screens.records.item.validations.ValidationAdapter -import io.forus.me.android.presentation.view.screens.records.item.validations.ValidationViewModel -import io.forus.me.android.presentation.view.screens.records.item.validators.ValidatorViewModel -import io.forus.me.android.presentation.view.screens.records.item.validators.ValidatorsAdapter import io.reactivex.Observable import io.reactivex.subjects.PublishSubject import kotlinx.android.synthetic.main.fragment_record_detail.* +import kotlinx.android.synthetic.main.toolbar_details_view.* +import java.lang.Exception + +class RecordDetailsFragment : ToolbarLRFragment(), RecordDetailsView { -class RecordDetailsFragment : ToolbarLRFragment(), RecordDetailsView{ companion object { private val RECORD_ID_EXTRA = "RECORD_ID_EXTRA" @@ -36,25 +42,40 @@ class RecordDetailsFragment : ToolbarLRFragment() override fun requestValidation(): Observable = requestValidation + private var retrofitExceptionMapper: RetrofitExceptionMapper = Injection.instance.retrofitExceptionMapper + + + override fun editRecord(): Observable { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + private val deleteRecord = PublishSubject.create() + override fun deleteRecord(): Observable = deleteRecord + override val allowBack: Boolean get() = true override val toolbarTitle: String get() = getString(R.string.record_details_title) + override val showAccount: Boolean + get() = false + + override val showInfo: Boolean + get() = false + override fun viewForSnackbar(): View = root override fun loadRefreshPanel() = lr_panel - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) - = inflater.inflate(R.layout.fragment_record_detail, container, false).also { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = inflater.inflate(R.layout.fragment_record_detail, container, false).also { val bundle = this.arguments if (bundle != null) { recordId = bundle.getLong(RECORD_ID_EXTRA) } - adapter = ValidationAdapter { } + adapter = ValidationAdapter { } /*adapter = ValidationAdapter { item -> if (item.status == ValidatorViewModel.Status.none && item.id != null) requestValidation.onNext(item.id!!) }*/ @@ -69,11 +90,22 @@ class RecordDetailsFragment : ToolbarLRFragment = mutableListOf() - Log.d("forus","adapter_2") - validations.add(ValidationViewModel(ValidationViewModel.Type.header.name)) - Log.d("forus","adapter_3") - for(validation in record!!.validations){ - validations.add(ValidationViewModel(validation)) - Log.d("forus","adapter_*") - } - Log.d("forus","adapter_4")*/ + adapter.items = vs.model.validations - if(vs.model.requestValidationError != null){ + if (vs.model.requestValidationError != null) { showToastMessage(resources.getString(R.string.record_details_validation_request_error)) } + + if (vs.model.recordDeleteSuccess != null) { + if (vs.model.recordDeleteSuccess) { + Log.d("forus", "Record was deleted!!") + activity!!.finish() + } + } + + edit_button.visibility = View.INVISIBLE + edit_button.setOnClickListener { + + + + if(vs.model.item != null) { + RecordModifyDialog(activity as Activity, RecordModifyDialog.Action.EDIT) { + startActivity(EditRecordActivity.getCallingIntent(context!!,vs.model.item.id, vs.model.item.recordType.name,vs.model.item.value)) + activity!!.finish() + }.show(); + } + + } + + + if (vs.model.recordDeleteError != null) { + + val error = vs.model.recordDeleteError + var messageString = error.localizedMessage + if (error is RetrofitException) { + + try { + val delError = retrofitExceptionMapper.mapToBaseApiError(error) + if (error.responseCode == 403) { + messageString = if (delError.message == null) "" else delError.message + } + + } catch (e: Exception) { + } + } + Log.d("forus", "Record delete error =$messageString") + DeleteRecordErrorDialog(messageString,context!!).show() + + } } } \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsModel.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsModel.kt index 635e293a..9b98539e 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsModel.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsModel.kt @@ -8,7 +8,9 @@ import io.forus.me.android.presentation.view.screens.records.item.validators.Val data class RecordDetailsModel( val item: Record? = null, val validations: List = emptyList(), - val requestValidationError: Throwable? = null + val requestValidationError: Throwable? = null, + val recordDeleteSuccess: Boolean? = false, + val recordDeleteError: Throwable? = null ) { fun changeStatus(validatorId: Long) : RecordDetailsModel { diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsPartialChanges.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsPartialChanges.kt index 74304ab5..4677b85b 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsPartialChanges.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsPartialChanges.kt @@ -10,4 +10,8 @@ sealed class RecordDetailsPartialChanges : PartialChange { data class RequestValidationError(val error: Throwable): RecordDetailsPartialChanges() + data class DeleteRecordSuccess(val success: Boolean): RecordDetailsPartialChanges() + + data class DeleteRecordError(val error: Throwable): RecordDetailsPartialChanges() + } \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsPresenter.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsPresenter.kt index 85879142..6413f3eb 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsPresenter.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsPresenter.kt @@ -1,5 +1,6 @@ package io.forus.me.android.presentation.view.screens.records.item +import android.content.Context import io.forus.me.android.presentation.view.base.lr.LRPresenter import io.forus.me.android.presentation.view.base.lr.LRViewState import io.forus.me.android.presentation.view.base.lr.PartialChange @@ -8,6 +9,7 @@ import io.forus.me.android.domain.models.records.Validation import io.forus.me.android.domain.models.validators.SimpleValidator import io.forus.me.android.domain.repository.records.RecordsRepository import io.forus.me.android.domain.repository.validators.ValidatorsRepository +import io.forus.me.android.presentation.R import io.forus.me.android.presentation.view.screens.records.item.validations.ValidationViewModel import io.forus.me.android.presentation.view.screens.records.item.validators.ValidatorViewModel import io.reactivex.Observable @@ -16,7 +18,7 @@ import io.reactivex.functions.Function3 import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers -class RecordDetailsPresenter constructor(private val recordId: Long, private val recordsRepository: RecordsRepository, private val validatorsRepository: ValidatorsRepository) : LRPresenter() { +class RecordDetailsPresenter constructor(private val context: Context, private val recordId: Long, private val recordsRepository: RecordsRepository, private val validatorsRepository: ValidatorsRepository) : LRPresenter() { override fun initialModelSingle(): Single = Single.fromObservable( @@ -26,58 +28,17 @@ class RecordDetailsPresenter constructor(private val recordId: Long, private val val allValidations = mutableListOf() if (it.validations.isNotEmpty()) { - allValidations.add(ValidationViewModel("Validations")) + allValidations.add(ValidationViewModel(context.getString(R.string.validations))) allValidations.addAll(it.validations.map { ValidationViewModel(it) }) } - if (allValidations.isEmpty()) allValidations.add(ValidationViewModel("You have no validations yet")) + if (allValidations.isEmpty()) allValidations.add(ValidationViewModel(context.getString(R.string.validations_empty))) RecordDetailsModel(item = it, validations = allValidations) } ) - /*Single.zip( - Single.fromObservable(recordsRepository.getRecord(recordId)), - Single.fromObservable(validatorsRepository.getValidators()), - Single.fromObservable(validatorsRepository.getValidators(recordId)), - Function3 { record : Record, validators: List, activeValidators: List -> - - val allValidations = mutableListOf() - val p2pValidations = mutableListOf() - val possibleValidators = mutableListOf() - - record.validations.distinctBy { it.identityAddress }.forEach{ - p2pValidations.add(ValidatorViewModel(-1, it.identityAddress ?: "?", "Peer-to-peer validation", Validation.p2pIcon)) - } - - validators.forEach{ - var requestExists = false - for(v in activeValidators){ - if(v.id == it.id && v.organizationId == it.organizationId){ - requestExists = true - break - } - } - if(!requestExists) possibleValidators.add(ValidatorViewModel(it)) - } - - if(activeValidators.isNotEmpty() || possibleValidators.isNotEmpty()){ - allValidations.add(ValidatorViewModel("Validators")) - allValidations.addAll(activeValidators.map { ValidatorViewModel(it) }) - allValidations.addAll(possibleValidators) - } - - if(p2pValidations.isNotEmpty()){ - allValidations.add(ValidatorViewModel("Peer-to-peer validations")) - allValidations.addAll(p2pValidations) - } - - if(allValidations.isEmpty()) allValidations.add(ValidatorViewModel("You have no validations yet")) - - RecordDetailsModel(item = record, validators = allValidations) - } - )*/ override fun RecordDetailsModel.changeInitialModel(i: RecordDetailsModel): RecordDetailsModel = copy(item = i.item, validations = i.validations, requestValidationError = null) @@ -100,7 +61,26 @@ class RecordDetailsPresenter constructor(private val recordId: Long, private val RecordDetailsPartialChanges.RequestValidationError(it) } .startWith(RecordDetailsPartialChanges.RequestValidationStart(validatorId)) - } + }, + + intent(RecordDetailsView::deleteRecord).switchMap { + recordsRepository.deleteRecord(it) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map { + RecordDetailsPartialChanges.DeleteRecordSuccess(it) + } + .onErrorReturn { + RecordDetailsPartialChanges.DeleteRecordError(it) + } + //.startWith(RecordDetailsPartialChanges.RequestValidationStart(validatorId)) + } + + /*, + + intent(RecordDetailsView::editRecord).switchMap { + + }*/ ) @@ -128,6 +108,8 @@ class RecordDetailsPresenter constructor(private val recordId: Long, private val is RecordDetailsPartialChanges.RequestValidationStart -> vs.copy(model = vs.model.copy(requestValidationError = null)) is RecordDetailsPartialChanges.RequestValidationEnd -> vs.copy(model = vs.model.changeStatus(change.validatorId)) is RecordDetailsPartialChanges.RequestValidationError -> vs.copy(model = vs.model.copy(requestValidationError = change.error)) + is RecordDetailsPartialChanges.DeleteRecordSuccess -> vs.copy(model = vs.model.copy(recordDeleteSuccess = change.success)) + is RecordDetailsPartialChanges.DeleteRecordError -> vs.copy(model = vs.model.copy(recordDeleteError = change.error)) } } } \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsView.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsView.kt index 82c13a55..856c290c 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsView.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/RecordDetailsView.kt @@ -7,4 +7,8 @@ interface RecordDetailsView : LRView { fun requestValidation(): Observable + fun deleteRecord(): Observable + + fun editRecord(): Observable + } \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/dialogs/DeleteRecordErrorDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/dialogs/DeleteRecordErrorDialog.kt new file mode 100644 index 00000000..028c5b05 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/dialogs/DeleteRecordErrorDialog.kt @@ -0,0 +1,24 @@ +package io.forus.me.android.presentation.view.screens.records.item.dialogs + +import android.content.Context +import com.afollestad.materialdialogs.MaterialDialog +import io.forus.me.android.presentation.R + + +class DeleteRecordErrorDialog(private val message: String, private val context: Context){ + + private val dialog: MaterialDialog = MaterialDialog.Builder(context) + .title(R.string.qr_popup_error) + .content(message) + .positiveText(context.resources.getString(R.string.me_ok)) + .dismissListener { dismiss()} + .build() + + fun show(){ + dialog.show() + } + + fun dismiss(){ + dialog.dismiss() + } +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/dialogs/RecordModifyDialog.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/dialogs/RecordModifyDialog.kt new file mode 100644 index 00000000..f90e40cf --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/item/dialogs/RecordModifyDialog.kt @@ -0,0 +1,63 @@ +package io.forus.me.android.presentation.view.screens.records.item.dialogs + +import android.app.Activity +import android.app.AlertDialog +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.View +import android.widget.TextView +import io.forus.me.android.presentation.R + + +class RecordModifyDialog(private val context: Activity, val action: Action, private val edit: () -> Unit) { + + private var dialog: AlertDialog + + init { + + val dialogBuilder = AlertDialog.Builder(context) + val inflater = context.layoutInflater + val dialogView = inflater.inflate(R.layout.view_edit_record_dialog, null) + + val cancel_bt = dialogView.findViewById(R.id.cancel_bt) + cancel_bt.setOnClickListener { dismiss() } + val ok_bt = dialogView.findViewById(R.id.ok_bt) + ok_bt.setOnClickListener{ + edit.invoke() + dismiss() + } + + ok_bt.text = when (action) { + Action.EDIT -> context.getString(R.string.dialog_button_edit) + Action.DELETE -> context.getString(R.string.dialog_button_archive) + } + + val description = dialogView.findViewById(R.id.description) + description.text = when (action) { + Action.EDIT -> context.getString(R.string.dialog_record_edit_description) + Action.DELETE -> context.getString(R.string.dialog_record_archive_description) + } + + dialogBuilder.setView(dialogView) + dialog = dialogBuilder.create() + if (dialog.window != null) { + dialog.window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + } + + } + + fun show() { + dialog.show() + } + + fun dismiss() { + dialog.dismiss() + } + + + + public enum class Action { + EDIT, DELETE + } + +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsFragment.kt index 86fedc3d..e36a332b 100755 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsFragment.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsFragment.kt @@ -1,21 +1,40 @@ package io.forus.me.android.presentation.view.screens.records.list +import android.content.Intent import android.os.Bundle +import android.support.v4.content.ContextCompat import android.support.v7.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import io.forus.me.android.presentation.view.base.lr.LRViewState import io.forus.me.android.presentation.R import io.forus.me.android.presentation.internal.Injection +import io.forus.me.android.presentation.view.base.lr.LRViewState import io.forus.me.android.presentation.view.fragment.ToolbarLRFragment +import io.forus.me.android.presentation.view.screens.records.create_record.CreateRecordActivity +import io.forus.me.android.presentation.view.screens.records.item.RecordDetailsActivity +import io.reactivex.Observable +import io.reactivex.subjects.PublishSubject import kotlinx.android.synthetic.main.fragment_records_recycler.* +import kotlinx.android.synthetic.main.toolbar_view.* + /** * Fragment Records Delegates Screen. */ class RecordsFragment : ToolbarLRFragment(), RecordsView{ + private var isRecords =true + + private val records = PublishSubject.create() + override fun records(): Observable = records + + + private val archives = PublishSubject.create() + override fun archives(): Observable = archives + + val LAUNCH_SECOND_ACTIVITY = 111 + companion object { private val CATEGORY_ID_EXTRA = "CATEGORY_ID_EXTRA" private val CATEGORY_NAME_EXTRA = "CATEGORY_NAME_EXTRA" @@ -29,13 +48,19 @@ class RecordsFragment : ToolbarLRFragment - navigator.navigateToRecordDetails(activity, item) + if(isRecords) { + val intentToLaunch = RecordDetailsActivity.getCallingIntent(context!!, item) + startActivityForResult(intentToLaunch, LAUNCH_SECOND_ACTIVITY) + } } recycler.layoutManager = LinearLayoutManager(context) recycler.adapter = adapter + + addRecordBt.setOnClickListener { + startActivity(CreateRecordActivity.getCallingIntent(context!!)) + } + + profile_button.setOnClickListener { + navigator.navigateToAccount(context!!) + } + } @@ -81,7 +117,49 @@ class RecordsFragment : ToolbarLRFragment) { super.render(vs) - adapter.records = vs.model.items + + if(isRecords) { + setToolbarTitle(getString(R.string.dashboard_records)) + adapter.records = vs.model.items + }else{ + adapter.records = vs.model.archives + } + adapter.notifyDataSetChanged() + + + + tab1.setOnClickListener { + tab1title.setTextColor(ContextCompat.getColor(context!!,R.color.colorAccent)) + tab2title.setTextColor(ContextCompat.getColor(context!!,R.color.gray_subtitle)) + + tab1divider.setBackgroundColor(ContextCompat.getColor(context!!,R.color.colorAccent)) + tab2divider.setBackgroundColor(ContextCompat.getColor(context!!,R.color.silver)) + isRecords = true + records.onNext(0) + + addRecordBt.show() + } + + tab2.setOnClickListener { + tab1title.setTextColor(ContextCompat.getColor(context!!,R.color.gray_subtitle)) + tab2title.setTextColor(ContextCompat.getColor(context!!,R.color.colorAccent)) + + tab1divider.setBackgroundColor(ContextCompat.getColor(context!!,R.color.silver)) + tab2divider.setBackgroundColor(ContextCompat.getColor(context!!,R.color.colorAccent)) + isRecords = false + archives.onNext(0) + + addRecordBt.hide() + } + } + + + + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + updateModel() + } } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsModel.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsModel.kt index dc4caf2b..5e0be333 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsModel.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsModel.kt @@ -4,5 +4,7 @@ import io.forus.me.android.domain.models.account.RequestDelegatesQrModel import io.forus.me.android.domain.models.records.Record data class RecordsModel( - val items: List = emptyList() + val items: List = emptyList(), + val archives: List = emptyList(), + val requestError: Throwable? = null ) \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsPartialChanges.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsPartialChanges.kt index f154351c..1f4652af 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsPartialChanges.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsPartialChanges.kt @@ -1,6 +1,16 @@ package io.forus.me.android.presentation.view.screens.records.list +import io.forus.me.android.domain.models.records.Record import io.forus.me.android.presentation.view.base.lr.PartialChange +import io.forus.me.android.presentation.view.screens.records.item.RecordDetailsPartialChanges -sealed class RecordsPartialChanges : PartialChange \ No newline at end of file +sealed class RecordsPartialChanges : PartialChange { + + + data class RequestRecordsSuccess(val recordsR: List): RecordsPartialChanges() + + data class RequestArchivesSuccess(val archiveR: List): RecordsPartialChanges() + + data class RequestError(val error: Throwable): RecordsPartialChanges() +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsPresenter.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsPresenter.kt index 9b076ade..30dace4f 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsPresenter.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsPresenter.kt @@ -5,8 +5,11 @@ import io.forus.me.android.domain.repository.records.RecordsRepository import io.forus.me.android.presentation.view.base.lr.LRPresenter import io.forus.me.android.presentation.view.base.lr.LRViewState import io.forus.me.android.presentation.view.base.lr.PartialChange +import io.forus.me.android.presentation.view.screens.records.item.RecordDetailsPartialChanges +import io.reactivex.Observable import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers class RecordsPresenter constructor(private val recordCategoryId: Long, private val recordsRepository: RecordsRepository) : LRPresenter, RecordsModel, RecordsView>() { @@ -21,7 +24,41 @@ class RecordsPresenter constructor(private val recordCategoryId: Long, private v override fun bindIntents() { - var observable = loadRefreshPartialChanges() + // var observable = loadRefreshPartialChanges() + + val observable = Observable.merge( + + loadRefreshPartialChanges(), + + intent { it.records() } + .switchMap { validatorId -> + recordsRepository.getRecords() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map { + RecordsPartialChanges.RequestRecordsSuccess(it) + } + .onErrorReturn { + RecordsPartialChanges.RequestError(it) + + } + }, + + intent { it.archives() } + .switchMap { validatorId -> + recordsRepository.getRecordsArchived() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map { + RecordsPartialChanges.RequestArchivesSuccess(it) + } + .onErrorReturn { + RecordsPartialChanges.RequestError(it) + + } + } + + ) val initialViewState = LRViewState( @@ -45,11 +82,20 @@ class RecordsPresenter constructor(private val recordCategoryId: Long, private v if (change !is RecordsPartialChanges) return super.stateReducer(vs, change) return when (change) { + is RecordsPartialChanges.RequestRecordsSuccess -> vs.copy( + model = vs.model.copy(items = change.recordsR, requestError = null)) + is RecordsPartialChanges.RequestArchivesSuccess -> vs.copy( + model = vs.model.copy(archives = change.archiveR, requestError = null)) + is RecordsPartialChanges.RequestError -> vs.copy( + model = vs.model.copy(requestError = change.error)) + } + + /*return when (change) { else -> { super.stateReducer(vs, change) } - } + }*/ } } \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsView.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsView.kt index 6586a452..19c3e355 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsView.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/list/RecordsView.kt @@ -1,10 +1,14 @@ package io.forus.me.android.presentation.view.screens.records.list import io.forus.me.android.presentation.view.base.lr.LRView +import io.reactivex.Observable /** * Created by pavelpantuhov on 31.10.2017. */ -interface RecordsView : LRView \ No newline at end of file +interface RecordsView : LRView{ + fun records(): Observable + fun archives(): Observable +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/newrecord/adapters/RecordTypesAdapter.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/newrecord/adapters/RecordTypesAdapter.kt index b02fb145..95ba507a 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/newrecord/adapters/RecordTypesAdapter.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/newrecord/adapters/RecordTypesAdapter.kt @@ -29,7 +29,7 @@ class RecordTypesAdapter(private val clickListener: ((RecordType) -> Unit)?): Re override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = RecordTypeVH(parent) { recordType: RecordType, position: Int -> lastSelectedPosition = position - //notifyDataSetChanged() + notifyDataSetChanged() clickListener?.invoke(recordType) } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/newrecord/viewholders/RecordTypeVH.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/newrecord/viewholders/RecordTypeVH.kt index ffda6af9..7c0286a9 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/newrecord/viewholders/RecordTypeVH.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/newrecord/viewholders/RecordTypeVH.kt @@ -14,8 +14,8 @@ class RecordTypeVH(parent: ViewGroup, private val clickListener: ((RecordType, I fun render(item: RecordType, lastSelectedPosition: Int) = with(itemView) { - rb_select_type.isChecked = lastSelectedPosition == adapterPosition - tv_name.text = item.name + selectedCheckBox.setChecked (lastSelectedPosition == adapterPosition) + name.text = item.name root.setOnClickListener { clickListener?.invoke(item, adapterPosition) } diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesFragment.kt new file mode 100755 index 00000000..9bdcf392 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesFragment.kt @@ -0,0 +1,123 @@ +package io.forus.me.android.presentation.view.screens.records.types + +import android.os.Bundle +import android.support.v7.widget.LinearLayoutManager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import io.forus.me.android.presentation.view.base.lr.LRViewState +import io.forus.me.android.presentation.R +import io.forus.me.android.presentation.internal.Injection +import io.forus.me.android.presentation.view.fragment.ToolbarLRFragment +import io.forus.me.android.presentation.view.screens.records.list.RecordsAdapter +import kotlinx.android.synthetic.main.fragment_record_categories.* +import android.support.v7.widget.DividerItemDecoration +import android.util.Log +import io.forus.me.android.presentation.view.component.dividers.FDividerItemDecoration +import io.forus.me.android.presentation.view.screens.records.categories.RecordCategoriesPresenter + +import android.content.Context +import io.forus.me.android.presentation.view.screens.records.newrecord.adapters.RecordTypesAdapter + + + + +class RecordTypesFragment : ToolbarLRFragment(), RecordTypesView { + + companion object { + fun newIntent(): RecordTypesFragment { + return RecordTypesFragment() + } + } + + override val toolbarTitle: String + get() = getString(R.string.dashboard_records) + + + override val allowBack: Boolean + get() = false + + + private lateinit var adapter: RecordTypesAdapter + + override fun viewForSnackbar(): View = root + + override fun loadRefreshPanel() = lr_panel + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = inflater.inflate(R.layout.fragment_record_categories, container, false).also { + adapter = RecordTypesAdapter({ + if (itemSelectListener != null) { + itemSelectListener!!.onItemSelected(it) + } + }) + } + + var showList = true + + override fun onResume() { + super.onResume() + Log.d("forus","onResume ") + if(itemSelectListener != null){ + itemSelectListener!!.onRecordTypesFragmentResume() + } + } + + var itemSelectListener: OnItemSelected? = null + + override fun onAttach(context: Context) { + super.onAttach(context) + Log.d("forus","onAttach_context = ${context::class}") + itemSelectListener = context as OnItemSelected + Log.d("forus","itemSelectListener = ${itemSelectListener==null}") + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + if(arguments != null) { + showList = arguments!!.getInt("showList") != 0 + } + + val layoutManager = LinearLayoutManager(context) + recycler.layoutManager = layoutManager + val dividerItemDecoration = FDividerItemDecoration(recycler.context, R.drawable.shape_divider_item_record) + + recycler.addItemDecoration(dividerItemDecoration) + + if(showList) recycler.adapter = adapter + + + + btn_new_record.setOnClickListener { + this.navigator.navigateToNewRecord(activity) + } + } + + override fun createPresenter() = RecordTypesPresenter( + Injection.instance.recordsRepository + ) + + override fun render(vs: LRViewState) { + super.render(vs) + + Log.d("forus","vs.model.items="+vs.model.items) + Log.d("forus","showList"+showList) + Log.d("forus","itemSelectListener==null=${itemSelectListener==null}") + if(vs.model.items.isNotEmpty() && !showList && itemSelectListener != null){ + itemSelectListener!!.onRecordTypesLoaded(vs.model.items) + + } + adapter.items = vs.model.items + + } + + interface OnItemSelected { + + fun onRecordTypesLoaded(list: List) + + fun onItemSelected(item: io.forus.me.android.domain.models.records.RecordType) + + fun onRecordTypesFragmentResume() + } +} + diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesModel.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesModel.kt new file mode 100644 index 00000000..72bb6b7f --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesModel.kt @@ -0,0 +1,10 @@ +package io.forus.me.android.presentation.view.screens.records.types + +import io.forus.me.android.domain.models.account.RequestDelegatesQrModel +import io.forus.me.android.domain.models.records.Record +import io.forus.me.android.domain.models.records.RecordCategory + +data class RecordTypesModel( + //val items: List = emptyList() + val items: List = emptyList() + ) \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesPartialChanges.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesPartialChanges.kt new file mode 100644 index 00000000..e6fbc858 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesPartialChanges.kt @@ -0,0 +1,6 @@ +package io.forus.me.android.presentation.view.screens.records.types + + +import io.forus.me.android.presentation.view.base.lr.PartialChange + +sealed class RecordTypesPartialChanges : PartialChange \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesPresenter.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesPresenter.kt new file mode 100644 index 00000000..c43c65b4 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesPresenter.kt @@ -0,0 +1,65 @@ +package io.forus.me.android.presentation.view.screens.records.types + +import io.forus.me.android.presentation.view.base.lr.LRPresenter +import io.forus.me.android.presentation.view.base.lr.LRViewState +import io.forus.me.android.presentation.view.base.lr.PartialChange +import io.forus.me.android.domain.models.records.Record +import io.forus.me.android.domain.repository.records.RecordsRepository +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers + + +class RecordTypesPresenter constructor(val recordsRepository: RecordsRepository) : LRPresenter, RecordTypesModel, RecordTypesView>() { + + + override fun initialModelSingle(): Single> = Single.fromObservable( + recordsRepository.getRecordTypes() + ) + + override fun RecordTypesModel.changeInitialModel(i: List): RecordTypesModel = copy(items = i) + + + override fun bindIntents() { + val observable = loadRefreshPartialChanges() + + + val initialViewState = LRViewState( + false, + null, + false, + false, + null, + false, + RecordTypesModel(), + false) + + subscribeViewState( + observable.scan(initialViewState, this::stateReducer) + .observeOn(AndroidSchedulers.mainThread()), + RecordTypesView::render) + } + + override fun stateReducer(viewState: LRViewState, change: PartialChange): LRViewState { + + if (change !is RecordTypesPartialChanges) return super.stateReducer(viewState, change) + + return when (change) { + + else -> { + super.stateReducer(viewState, change) + } + } + + } + + + + + + + + + + + +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesView.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesView.kt new file mode 100644 index 00000000..fa90f7ab --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/records/types/RecordTypesView.kt @@ -0,0 +1,10 @@ +package io.forus.me.android.presentation.view.screens.records.types + +import io.forus.me.android.presentation.view.base.lr.LRView + +/** + * Created by pavelpantuhov on 31.10.2017. + */ + + +interface RecordTypesView : LRView \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/dialogs/FullscreenDialog.java b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/dialogs/FullscreenDialog.java new file mode 100644 index 00000000..5689c847 --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/dialogs/FullscreenDialog.java @@ -0,0 +1,112 @@ +package io.forus.me.android.presentation.view.screens.vouchers.dialogs; + +import android.app.Dialog; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.TextView; + +import io.forus.me.android.presentation.R; + + +public class FullscreenDialog extends DialogFragment { + + View rootView; + + private String title = ""; + private String description = ""; + String positiveButtonText= "OK"; + + SubmitClickListener submitClickListener; + + public static FullscreenDialog display(FragmentManager fragmentManager, String title, String details, String positiveButtonText, SubmitClickListener submitClickListener) { + FullscreenDialog fullscreenDialog = new FullscreenDialog(); + fullscreenDialog.title = title; + fullscreenDialog.description = details; + fullscreenDialog.submitClickListener = submitClickListener; + + fullscreenDialog.show(fragmentManager, ""); + return fullscreenDialog; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme_FullScreenDialog); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + rootView = inflater.inflate(R.layout.dialog_fullscreen, container, false); + + + return rootView; + } + + @Override + public void onStart() { + super.onStart(); + Dialog dialog = getDialog(); + if (dialog != null) { + int width = ViewGroup.LayoutParams.MATCH_PARENT; + int height = ViewGroup.LayoutParams.MATCH_PARENT; + if (dialog.getWindow() != null) { + dialog.getWindow().setLayout(width, height); + } + } + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + TextView titleTv = rootView.findViewById(R.id.titleTV); + TextView descriptionTv = rootView.findViewById(R.id.description); + io.forus.me.android.presentation.view.component.buttons.Button submitButton = rootView.findViewById(R.id.submitButton); + + titleTv.setText(title); + descriptionTv.setText(description); + + rootView.findViewById(R.id.closeBt).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + FullscreenDialog.this.dismiss(); + } + }); + + submitButton.setText(positiveButtonText); + + if (submitClickListener != null) { + submitButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (submitClickListener != null) { + FullscreenDialog.this.dismiss(); + submitClickListener.click(FullscreenDialog.this); + } + } + }); + } + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Dialog dialog = super.onCreateDialog(savedInstanceState); + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + return dialog; + } + + public interface SubmitClickListener { + void click(FullscreenDialog dialog); + } + + +} \ No newline at end of file diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/dialogs/SuccessDialogActivity.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/dialogs/SuccessDialogActivity.kt new file mode 100644 index 00000000..c3e81d7e --- /dev/null +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/dialogs/SuccessDialogActivity.kt @@ -0,0 +1,53 @@ +package io.forus.me.android.presentation.view.screens.vouchers.dialogs + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.design.widget.Snackbar +import android.support.v7.app.AppCompatActivity +import io.forus.me.android.presentation.R +import kotlinx.android.synthetic.main.dialog_fullscreen.* + + +class SuccessDialogActivity : AppCompatActivity() { + + companion object { + + val TITLE_EXTRA = "TITLE_EXTRA" + val DESCRIPTION_EXTRA = "DESCRIPTION_EXTRA" + val SUBMIT_EXTRA = "SUBMIT_EXTRA" + + fun getCallingIntent(context: Context, title: String, description: String = "", submitButtonText: String? = "OK"): Intent { + val intent = Intent(context, SuccessDialogActivity::class.java) + intent.putExtra(TITLE_EXTRA, title) + intent.putExtra(DESCRIPTION_EXTRA, description) + intent.putExtra(SUBMIT_EXTRA, submitButtonText) + return intent + } + } + + + var titleTxt: String = "" + var descriptionTxt: String = "" + var submitButtonText: String = "" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.dialog_fullscreen) + + if(intent != null) { + titleTxt = intent.getStringExtra(TITLE_EXTRA) + descriptionTxt = intent.getStringExtra(DESCRIPTION_EXTRA) + submitButtonText = intent.getStringExtra(SUBMIT_EXTRA) + } + + + titleTV.text = titleTxt + description.text = descriptionTxt + submitButton.text = submitButtonText + submitButton.setOnClickListener { finish() } + + + } + +} diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/item/VoucherFragment.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/item/VoucherFragment.kt index 0ff40fe1..1e1882b0 100755 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/item/VoucherFragment.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/item/VoucherFragment.kt @@ -6,6 +6,7 @@ import android.graphics.BlurMaskFilter import android.net.Uri import android.os.Bundle import android.support.customtabs.CustomTabsIntent +import android.support.design.widget.Snackbar import android.support.v4.content.ContextCompat import android.support.v7.app.AlertDialog import android.support.v7.widget.LinearLayoutManager @@ -35,12 +36,15 @@ import io.forus.me.android.presentation.models.vouchers.Voucher import io.forus.me.android.presentation.view.base.lr.LRViewState import io.forus.me.android.presentation.view.fragment.ToolbarLRFragment import io.forus.me.android.presentation.view.screens.dashboard.DashboardActivity +import io.forus.me.android.presentation.view.screens.vouchers.dialogs.FullscreenDialog import io.forus.me.android.presentation.view.screens.vouchers.item.dialogs.SendVoucherSuccessDialog import io.forus.me.android.presentation.view.screens.vouchers.item.transactions.TransactionsAdapter import io.reactivex.Observable import io.reactivex.subjects.PublishSubject +import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_voucher.* import kotlinx.android.synthetic.main.toolbar_view.* +import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* @@ -103,6 +107,7 @@ class VoucherFragment : ToolbarLRFragment() override fun getShortToken(): Observable = shortToken @@ -148,19 +153,38 @@ class VoucherFragment : ToolbarLRFragment 0) { activity?.supportFragmentManager?.popBackStack() + } else { activity?.onBackPressed() } @@ -173,6 +197,10 @@ class VoucherFragment : ToolbarLRFragment) { super.render(vs) + Log.d("forus", "render") + name.text = vs.model.item?.name type.text = vs.model.item?.organizationName value.text = "${vs.model.item?.currency?.name} ${vs.model.item?.amount?.toDouble().format(2)}" vs.model.item?.let { voucher -> + + Log.d("forus", "vs.model.item") setToolbarTitle(resources.getString(if (voucher.isProduct) R.string.vouchers_item_product else R.string.vouchers_item)) adapter.transactions = voucher.transactions tv_transactions_title.visibility = @@ -272,18 +306,44 @@ class VoucherFragment : ToolbarLRFragment showEmailSendDialog() - EmailSend.SENT -> showEmailSentDialog() + EmailSend.SENT -> { + FullscreenDialog.display(fragmentManager, context!!.resources.getString(R.string.voucher_send_email_success), + context!!.resources.getString(R.string.voucher_send_email_description), + context!!.resources.getString(R.string.me_ok)) { sentEmailDialogShown.onNext(Unit) + } + } EmailSend.NOTHING -> Unit } } @@ -344,11 +409,7 @@ class VoucherFragment : ToolbarLRFragment(), ProviderView { companion object { private val VOUCHER_ADDRESS_EXTRA = "VOUCHER_ADDRESS_EXTRA" + val IS_DEMO_VOUCHER = "IS_DEMO_VOUCHER" - fun newIntent(id: String): ProviderFragment = ProviderFragment().also { + fun newIntent(id: String, isDemoVoucher: Boolean? = false): ProviderFragment = ProviderFragment().also { val bundle = Bundle() bundle.putSerializable(VOUCHER_ADDRESS_EXTRA, id) + if(isDemoVoucher != null) bundle.putSerializable(IS_DEMO_VOUCHER, isDemoVoucher) it.arguments = bundle } } private lateinit var address: String + private var isDemoVoucher: Boolean? = false private lateinit var categoriesAdapter: CategoriesAdapter override val toolbarTitle: String - get() = getString(R.string.vouchers_provider) + get() = if(isDemoVoucher != null && isDemoVoucher!!) getString(R.string.test_transaction) else getString(R.string.vouchers_provider) override val allowBack: Boolean get() = true + override val showAccount: Boolean + get() = false + override fun viewForSnackbar(): View = root override fun loadRefreshPanel() = lr_panel @@ -70,15 +74,20 @@ class ProviderFragment : ToolbarLRFragment = selectNote private val selectOrganization = PublishSubject.create() - override fun selectOrganization(): Observable = selectOrganization; + override fun selectOrganization(): Observable = selectOrganization private val charge = PublishSubject.create() override fun charge(): Observable = charge + private val demoCharge = PublishSubject.create() + override fun demoCharge(): Observable = demoCharge + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = inflater.inflate(R.layout.fragment_voucher_provider, container, false).also { address = if (arguments == null) "" else arguments!!.getString(VOUCHER_ADDRESS_EXTRA, "") + isDemoVoucher = if (arguments == null) false else arguments!!.getBoolean(IS_DEMO_VOUCHER, false) + categoriesAdapter = CategoriesAdapter() } @@ -113,14 +122,13 @@ class ProviderFragment : ToolbarLRFragment() { +class ProviderPresenter constructor(private val vouchersRepository: VouchersRepository, private val address: String, + private val isDemoVoucher: Boolean? = false) : LRPresenter() { private var organizationId = 0L private var note = "" - override fun initialModelSingle(): Single = Single.fromObservable(vouchersRepository.getVoucherAsProvider(address).map { - VoucherProvider(Voucher(it.voucher?.isProduct ?: false, it.voucher?.isUsed - ?: false, it.voucher.address, it.voucher.name, it.voucher.organizationName, - it.voucher.fundName, it.voucher.fundWebShopUrl, it.voucher.description, it.voucher.createdAt, - Currency(it.voucher.currency?.name, it.voucher.currency?.logoUrl), it.voucher.amount, it.voucher.logo, - it.voucher.transactions.map { transaction -> - Transaction(transaction.id, - Organization(transaction.organization?.id ?: 0, - transaction.organization?.name, - transaction.organization?.logo, - transaction.organization?.lat, - transaction.organization?.lon, - transaction.organization?.address, - transaction.organization?.phone, - transaction.organization?.email), - Currency(transaction.currency?.name, - transaction.currency?.logoUrl), - transaction?.amount ?: 0f.toBigDecimal(), - transaction.createdAt, - Transaction.Type.valueOf(transaction.type.name)) - }, null, ""), - - it.allowedOrganizations.map { organization -> - Organization(organization.id, - organization.name, - organization.logo, - organization.lat, - organization.lon, - organization.address, - organization.phone, - organization.email) - }, - - it.allowedProductCategories.map { productCategory -> - ProductCategory(productCategory.id, productCategory.key, productCategory.name) - }) - }) + override fun initialModelSingle(): Single { + + if (isDemoVoucher != null && isDemoVoucher) { + + return return Single.fromObservable(vouchersRepository.getTestVoucherAsProvider().map { + VoucherProvider(Voucher(it.voucher?.isProduct ?: false, it.voucher?.isUsed + ?: false, it.voucher.address, it.voucher.name, it.voucher.organizationName, + it.voucher.fundName, it.voucher.fundWebShopUrl, it.voucher.description, it.voucher.createdAt, + Currency(it.voucher.currency?.name, it.voucher.currency?.logoUrl), it.voucher.amount, it.voucher.logo, + it.voucher.transactions.map { transaction -> + Transaction(transaction.id, + Organization(transaction.organization?.id ?: 0, + transaction.organization?.name, + transaction.organization?.logo, + transaction.organization?.lat, + transaction.organization?.lon, + transaction.organization?.address, + transaction.organization?.phone, + transaction.organization?.email), + Currency(transaction.currency?.name, + transaction.currency?.logoUrl), + transaction?.amount ?: 0f.toBigDecimal(), + transaction.createdAt, + Transaction.Type.valueOf(transaction.type.name)) + }, null, it.voucher.expired ?: false,""), + + it.allowedOrganizations.map { organization -> + Organization(organization.id, + organization.name, + organization.logo, + organization.lat, + organization.lon, + organization.address, + organization.phone, + organization.email) + }, + + it.allowedProductCategories.map { productCategory -> + ProductCategory(productCategory.id, productCategory.key, productCategory.name) + }) + }) + + } else { + return Single.fromObservable(vouchersRepository.getVoucherAsProvider(address).map { + VoucherProvider(Voucher(it.voucher?.isProduct ?: false, it.voucher?.isUsed + ?: false, it.voucher.address, it.voucher.name, it.voucher.organizationName, + it.voucher.fundName, it.voucher.fundWebShopUrl, it.voucher.description, it.voucher.createdAt, + Currency(it.voucher.currency?.name, it.voucher.currency?.logoUrl), it.voucher.amount, it.voucher.logo, + it.voucher.transactions.map { transaction -> + Transaction(transaction.id, + Organization(transaction.organization?.id ?: 0, + transaction.organization?.name, + transaction.organization?.logo, + transaction.organization?.lat, + transaction.organization?.lon, + transaction.organization?.address, + transaction.organization?.phone, + transaction.organization?.email), + Currency(transaction.currency?.name, + transaction.currency?.logoUrl), + transaction?.amount ?: 0f.toBigDecimal(), + transaction.createdAt, + Transaction.Type.valueOf(transaction.type.name)) + }, null, it.voucher.expired ?: false,""), + + it.allowedOrganizations.map { organization -> + Organization(organization.id, + organization.name, + organization.logo, + organization.lat, + organization.lon, + organization.address, + organization.phone, + organization.email) + }, + + it.allowedProductCategories.map { productCategory -> + ProductCategory(productCategory.id, productCategory.key, productCategory.name) + }) + }) + } + } override fun ProviderModel.changeInitialModel(i: VoucherProvider): ProviderModel { val organization = if (i.allowedOrganizations.isNotEmpty()) i.allowedOrganizations.get(0) else Organization(organizationId, i.voucher.organizationName, "", i.voucher.product?.organization?.lat @@ -72,32 +118,50 @@ class ProviderPresenter constructor(private val vouchersRepository: VouchersRepo Arrays.asList( - loadRefreshPartialChanges(), + loadRefreshPartialChanges(), - intent { it.selectAmount() } - .map { ProviderPartialChanges.SetAmount(it) }, + intent { it.selectAmount() } + .map { ProviderPartialChanges.SetAmount(it) }, - intent { it.selectNote() } - .map { ProviderPartialChanges.SetNote(it) }, + intent { it.selectNote() } + .map { ProviderPartialChanges.SetNote(it) }, - intent { it.charge() } - .switchMap { - vouchersRepository.makeTransaction(address, it, note, organizationId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .map { - ProviderPartialChanges.MakeTransactionEnd() - } - .onErrorReturn { - ProviderPartialChanges.MakeTransactionError(it) - } - .startWith(ProviderPartialChanges.MakeTransactionStart()) - }, + intent { it.charge() } + .switchMap { + vouchersRepository.makeTransaction(address, it, note, organizationId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map { + ProviderPartialChanges.MakeTransactionEnd() + } + .onErrorReturn { + ProviderPartialChanges.MakeTransactionError(it) + } + .startWith(ProviderPartialChanges.MakeTransactionStart()) + }, + + + intent { it.selectOrganization() } + .map { ProviderPartialChanges.SelectOrganization(it) }, + + intent { it.demoCharge() } + .switchMap { + + + vouchersRepository.makeDemoTransaction(address) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .map { + ProviderPartialChanges.MakeTransactionEnd() + } + .onErrorReturn { + ProviderPartialChanges.MakeTransactionError(it) + } + .startWith(ProviderPartialChanges.MakeTransactionStart()) - intent { it.selectOrganization() } - .map { ProviderPartialChanges.SelectOrganization(it) } + } ) ) diff --git a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/provider/ProviderView.kt b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/provider/ProviderView.kt index fbf7f9ec..cd4b1cba 100644 --- a/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/provider/ProviderView.kt +++ b/presentation/src/main/java/io/forus/me/android/presentation/view/screens/vouchers/provider/ProviderView.kt @@ -14,4 +14,6 @@ interface ProviderView : LRView { fun charge(): Observable fun selectOrganization(): Observable + + fun demoCharge(): Observable } \ No newline at end of file diff --git a/presentation/src/main/res/anim/slide_in_left.xml b/presentation/src/main/res/anim/slide_in_left.xml new file mode 100644 index 00000000..fa86a927 --- /dev/null +++ b/presentation/src/main/res/anim/slide_in_left.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/presentation/src/main/res/anim/slide_in_right.xml b/presentation/src/main/res/anim/slide_in_right.xml new file mode 100644 index 00000000..d283f645 --- /dev/null +++ b/presentation/src/main/res/anim/slide_in_right.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/presentation/src/main/res/anim/slide_out_left.xml b/presentation/src/main/res/anim/slide_out_left.xml new file mode 100644 index 00000000..020a1bee --- /dev/null +++ b/presentation/src/main/res/anim/slide_out_left.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/anim/slide_out_right.xml b/presentation/src/main/res/anim/slide_out_right.xml new file mode 100644 index 00000000..c4de8006 --- /dev/null +++ b/presentation/src/main/res/anim/slide_out_right.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/anim/wait_anim.xml b/presentation/src/main/res/anim/wait_anim.xml new file mode 100644 index 00000000..d955e6df --- /dev/null +++ b/presentation/src/main/res/anim/wait_anim.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable-hdpi/ic_google_play_store.xml b/presentation/src/main/res/drawable-hdpi/ic_google_play_store.xml new file mode 100644 index 00000000..33a54113 --- /dev/null +++ b/presentation/src/main/res/drawable-hdpi/ic_google_play_store.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/drawable-mdpi/shape_round_view.xml b/presentation/src/main/res/drawable-mdpi/shape_round_view.xml new file mode 100644 index 00000000..14b83746 --- /dev/null +++ b/presentation/src/main/res/drawable-mdpi/shape_round_view.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable-v21/button_main_round_blue.xml b/presentation/src/main/res/drawable-v21/button_main_round_blue.xml new file mode 100644 index 00000000..92e6fd28 --- /dev/null +++ b/presentation/src/main/res/drawable-v21/button_main_round_blue.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable-xhdpi/shape_round_dialog.xml b/presentation/src/main/res/drawable-xhdpi/shape_round_dialog.xml new file mode 100644 index 00000000..12237518 --- /dev/null +++ b/presentation/src/main/res/drawable-xhdpi/shape_round_dialog.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable-xxxhdpi/forus_white.jpg b/presentation/src/main/res/drawable-xxxhdpi/forus_white.jpg new file mode 100644 index 00000000..b470b63b Binary files /dev/null and b/presentation/src/main/res/drawable-xxxhdpi/forus_white.jpg differ diff --git a/presentation/src/main/res/drawable/button_main_raund_reverse.xml b/presentation/src/main/res/drawable/button_main_raund_reverse.xml index a79683cf..dba92d50 100644 --- a/presentation/src/main/res/drawable/button_main_raund_reverse.xml +++ b/presentation/src/main/res/drawable/button_main_raund_reverse.xml @@ -6,6 +6,7 @@ + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/button_main_round_blue.xml b/presentation/src/main/res/drawable/button_main_round_blue.xml new file mode 100644 index 00000000..19bba12b --- /dev/null +++ b/presentation/src/main/res/drawable/button_main_round_blue.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/dialog_round_bg.xml b/presentation/src/main/res/drawable/dialog_round_bg.xml new file mode 100644 index 00000000..2a8dd7a1 --- /dev/null +++ b/presentation/src/main/res/drawable/dialog_round_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/forus.png b/presentation/src/main/res/drawable/forus.png new file mode 100644 index 00000000..9ad60522 Binary files /dev/null and b/presentation/src/main/res/drawable/forus.png differ diff --git a/presentation/src/main/res/drawable/ic_archived_records.xml b/presentation/src/main/res/drawable/ic_archived_records.xml new file mode 100644 index 00000000..5f078a97 --- /dev/null +++ b/presentation/src/main/res/drawable/ic_archived_records.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/presentation/src/main/res/drawable/ic_create_black_24dp.xml b/presentation/src/main/res/drawable/ic_create_black_24dp.xml new file mode 100644 index 00000000..df26adda --- /dev/null +++ b/presentation/src/main/res/drawable/ic_create_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/presentation/src/main/res/drawable/ic_delete_outline_24px.xml b/presentation/src/main/res/drawable/ic_delete_outline_24px.xml new file mode 100644 index 00000000..09503aed --- /dev/null +++ b/presentation/src/main/res/drawable/ic_delete_outline_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/presentation/src/main/res/drawable/ic_float_add.png b/presentation/src/main/res/drawable/ic_float_add.png new file mode 100644 index 00000000..f19907cd Binary files /dev/null and b/presentation/src/main/res/drawable/ic_float_add.png differ diff --git a/presentation/src/main/res/drawable/ic_float_add_2x.png b/presentation/src/main/res/drawable/ic_float_add_2x.png new file mode 100644 index 00000000..7c86f8c2 Binary files /dev/null and b/presentation/src/main/res/drawable/ic_float_add_2x.png differ diff --git a/presentation/src/main/res/drawable/ic_forus_icon.xml b/presentation/src/main/res/drawable/ic_forus_icon.xml new file mode 100644 index 00000000..4053630c --- /dev/null +++ b/presentation/src/main/res/drawable/ic_forus_icon.xml @@ -0,0 +1,10 @@ + + + diff --git a/presentation/src/main/res/drawable/ic_ic_forus_logo_backgr_w.xml b/presentation/src/main/res/drawable/ic_ic_forus_logo_backgr_w.xml new file mode 100644 index 00000000..45a8208f --- /dev/null +++ b/presentation/src/main/res/drawable/ic_ic_forus_logo_backgr_w.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/presentation/src/main/res/drawable/ic_warning.png b/presentation/src/main/res/drawable/ic_warning.png new file mode 100644 index 00000000..26e449ea Binary files /dev/null and b/presentation/src/main/res/drawable/ic_warning.png differ diff --git a/presentation/src/main/res/drawable/layout_top_corners.xml b/presentation/src/main/res/drawable/layout_top_corners.xml new file mode 100644 index 00000000..7aa65680 --- /dev/null +++ b/presentation/src/main/res/drawable/layout_top_corners.xml @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/qr_blue.xml b/presentation/src/main/res/drawable/qr_blue.xml new file mode 100644 index 00000000..b0612235 --- /dev/null +++ b/presentation/src/main/res/drawable/qr_blue.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + diff --git a/presentation/src/main/res/drawable/shape_card_view_bottom.xml b/presentation/src/main/res/drawable/shape_card_view_bottom.xml new file mode 100644 index 00000000..0cb42185 --- /dev/null +++ b/presentation/src/main/res/drawable/shape_card_view_bottom.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_divider.xml b/presentation/src/main/res/drawable/shape_divider.xml new file mode 100644 index 00000000..8f4da3e9 --- /dev/null +++ b/presentation/src/main/res/drawable/shape_divider.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/drawable/shape_divider_item_record.xml b/presentation/src/main/res/drawable/shape_divider_item_record.xml new file mode 100644 index 00000000..dfc704a0 --- /dev/null +++ b/presentation/src/main/res/drawable/shape_divider_item_record.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/activity_check_email.xml b/presentation/src/main/res/layout/activity_check_email.xml index e4453361..985d9dca 100644 --- a/presentation/src/main/res/layout/activity_check_email.xml +++ b/presentation/src/main/res/layout/activity_check_email.xml @@ -7,6 +7,19 @@ > + + @@ -47,7 +61,7 @@ android:layout_marginEnd="30dp" android:fontFamily="sans-serif-medium" android:gravity="center_horizontal|top" - android:text="@string/check_email_has_been_sent_to_you" + android:text="@string/check_your_email" android:textColor="@color/textColor" android:textSize="32sp" /> @@ -71,18 +85,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/layout/activity_main.xml b/presentation/src/main/res/layout/activity_main.xml index 3cc32587..3ba1e01b 100644 --- a/presentation/src/main/res/layout/activity_main.xml +++ b/presentation/src/main/res/layout/activity_main.xml @@ -1,5 +1,6 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/dialog_fullscreen.xml b/presentation/src/main/res/layout/dialog_fullscreen.xml new file mode 100644 index 00000000..8707806a --- /dev/null +++ b/presentation/src/main/res/layout/dialog_fullscreen.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_account_pair_device.xml b/presentation/src/main/res/layout/fragment_account_pair_device.xml index b5070c2b..56fd3a1e 100644 --- a/presentation/src/main/res/layout/fragment_account_pair_device.xml +++ b/presentation/src/main/res/layout/fragment_account_pair_device.xml @@ -33,21 +33,6 @@ - diff --git a/presentation/src/main/res/layout/fragment_create_record.xml b/presentation/src/main/res/layout/fragment_create_record.xml new file mode 100644 index 00000000..486d5aae --- /dev/null +++ b/presentation/src/main/res/layout/fragment_create_record.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/layout/fragment_dashboard.xml b/presentation/src/main/res/layout/fragment_dashboard.xml index b60591cc..ab85d04e 100644 --- a/presentation/src/main/res/layout/fragment_dashboard.xml +++ b/presentation/src/main/res/layout/fragment_dashboard.xml @@ -20,6 +20,13 @@ android:fillViewport="true" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_popup_qr.xml b/presentation/src/main/res/layout/fragment_popup_qr.xml index 4cd67259..252087a0 100644 --- a/presentation/src/main/res/layout/fragment_popup_qr.xml +++ b/presentation/src/main/res/layout/fragment_popup_qr.xml @@ -1,45 +1,73 @@ - - - - - + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="@dimen/activity_horizontal_margin"> + + + + + - + + + - \ No newline at end of file + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_record_categories.xml b/presentation/src/main/res/layout/fragment_record_categories.xml index 02ffb42b..3470c8db 100644 --- a/presentation/src/main/res/layout/fragment_record_categories.xml +++ b/presentation/src/main/res/layout/fragment_record_categories.xml @@ -5,14 +5,16 @@ android:id="@+id/root" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + > + android:layout_marginBottom="0dp"> - + - + - + android:layout_width="match_parent" + android:layout_height="36dp" + android:drawableStart="@drawable/qr_blue" + /> diff --git a/presentation/src/main/res/layout/fragment_records_recycler.xml b/presentation/src/main/res/layout/fragment_records_recycler.xml index 99d8ae90..77af7ac1 100644 --- a/presentation/src/main/res/layout/fragment_records_recycler.xml +++ b/presentation/src/main/res/layout/fragment_records_recycler.xml @@ -1,38 +1,133 @@ + android:background="@color/secondary200" + android:descendantFocusability="blocksDescendants" + android:orientation="vertical" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + android:layout_height="match_parent" + android:layout_marginBottom="60dp"> - - + + android:layout_height="match_parent" + android:visibility="gone" + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> + + - - + android:layout_height="match_parent" + android:orientation="vertical"> + + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:orientation="horizontal" + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/fragment_send_reports.xml b/presentation/src/main/res/layout/fragment_send_reports.xml index 7a926092..3232b592 100644 --- a/presentation/src/main/res/layout/fragment_send_reports.xml +++ b/presentation/src/main/res/layout/fragment_send_reports.xml @@ -3,9 +3,9 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/root" - android:background="@color/colorPrimary" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:background="@color/colorPrimary"> + android:layout_height="wrap_content" + android:orientation="vertical"> - + + android:layout_marginTop="36dp" + android:orientation="vertical"> + android:textSize="32sp" /> + android:textSize="16sp" /> @@ -69,41 +69,37 @@ - - - + android:src="@drawable/ic_send_reports" /> + android:layout_height="wrap_content" + android:layout_alignParentBottom="true"> + app:cardCornerRadius="@dimen/card_radius" + app:cardElevation="0dp"> + android:textSize="16sp"> - - diff --git a/presentation/src/main/res/layout/fragment_send_reports_nokia1.xml b/presentation/src/main/res/layout/fragment_send_reports_nokia1.xml new file mode 100644 index 00000000..dc6220d5 --- /dev/null +++ b/presentation/src/main/res/layout/fragment_send_reports_nokia1.xml @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/layout/fragment_voucher.xml b/presentation/src/main/res/layout/fragment_voucher.xml index b853c931..e901c569 100644 --- a/presentation/src/main/res/layout/fragment_voucher.xml +++ b/presentation/src/main/res/layout/fragment_voucher.xml @@ -14,6 +14,8 @@ + + + android:paddingStart="16dp" + android:paddingBottom="12dp" + > + android:layout_marginTop="4dp" + > + + + + + diff --git a/presentation/src/main/res/layout/item_record_categories.xml b/presentation/src/main/res/layout/item_record_categories.xml index 5be39d3f..c85dd525 100644 --- a/presentation/src/main/res/layout/item_record_categories.xml +++ b/presentation/src/main/res/layout/item_record_categories.xml @@ -1,68 +1,44 @@ - - - - + android:layout_height="56dp" + android:background="?attr/selectableItemBackground" + android:orientation="horizontal" + android:clickable="true" + android:focusable="true" - + > - - - - - - + - - + - - + - \ No newline at end of file diff --git a/presentation/src/main/res/layout/item_record_select_type.xml b/presentation/src/main/res/layout/item_record_select_type.xml index 0e71204b..5e385d17 100644 --- a/presentation/src/main/res/layout/item_record_select_type.xml +++ b/presentation/src/main/res/layout/item_record_select_type.xml @@ -2,53 +2,42 @@ + android:layout_height="56dp" + android:background="?attr/selectableItemBackground" + android:orientation="horizontal" + android:clickable="true" + android:focusable="true" + + > - + android:layout_centerVertical="true" + android:layout_gravity="center_vertical" + android:layout_marginStart="16dp" + app:type="medium" + tools:text="Nieuwe" /> - - - + - \ No newline at end of file diff --git a/presentation/src/main/res/layout/layout_number_item.xml b/presentation/src/main/res/layout/layout_number_item.xml index f4d608e4..a518a766 100755 --- a/presentation/src/main/res/layout/layout_number_item.xml +++ b/presentation/src/main/res/layout/layout_number_item.xml @@ -3,7 +3,7 @@ android:id="@+id/button" android:layout_width="54dp" android:layout_height="54dp" - android:background="?android:attr/selectableItemBackground" + android:background="?attr/selectableItemBackgroundBorderless" android:fontFamily="sans-serif" android:textColor="@color/textColor" android:textSize="34sp" /> \ No newline at end of file diff --git a/presentation/src/main/res/layout/layout_snackbar_update_app.xml b/presentation/src/main/res/layout/layout_snackbar_update_app.xml new file mode 100644 index 00000000..db8f5647 --- /dev/null +++ b/presentation/src/main/res/layout/layout_snackbar_update_app.xml @@ -0,0 +1,5 @@ + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/row_validator.xml b/presentation/src/main/res/layout/row_validator.xml new file mode 100644 index 00000000..f64fbba1 --- /dev/null +++ b/presentation/src/main/res/layout/row_validator.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/layout/toolbar_details_view.xml b/presentation/src/main/res/layout/toolbar_details_view.xml new file mode 100644 index 00000000..4bbe7adb --- /dev/null +++ b/presentation/src/main/res/layout/toolbar_details_view.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/layout/toolbar_view_records.xml b/presentation/src/main/res/layout/toolbar_view_records.xml new file mode 100644 index 00000000..315f4c9c --- /dev/null +++ b/presentation/src/main/res/layout/toolbar_view_records.xml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/presentation/src/main/res/layout/view_edit_record_dialog.xml b/presentation/src/main/res/layout/view_edit_record_dialog.xml new file mode 100644 index 00000000..4c0badf7 --- /dev/null +++ b/presentation/src/main/res/layout/view_edit_record_dialog.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/view_snackbar_update_app.xml b/presentation/src/main/res/layout/view_snackbar_update_app.xml new file mode 100644 index 00000000..34dab13e --- /dev/null +++ b/presentation/src/main/res/layout/view_snackbar_update_app.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/view_validators_list.xml b/presentation/src/main/res/layout/view_validators_list.xml new file mode 100644 index 00000000..3c7b3b7e --- /dev/null +++ b/presentation/src/main/res/layout/view_validators_list.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/layout/view_wait_dialog.xml b/presentation/src/main/res/layout/view_wait_dialog.xml new file mode 100644 index 00000000..d8b630a8 --- /dev/null +++ b/presentation/src/main/res/layout/view_wait_dialog.xml @@ -0,0 +1,34 @@ + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/menu/bottom_navigation_menu.xml b/presentation/src/main/res/menu/bottom_navigation_menu.xml index 52e9cd29..a21fad18 100755 --- a/presentation/src/main/res/menu/bottom_navigation_menu.xml +++ b/presentation/src/main/res/menu/bottom_navigation_menu.xml @@ -10,12 +10,12 @@ android:icon="@drawable/qr" /> - - - - + + \ No newline at end of file diff --git a/presentation/src/main/res/navigation/nav_create_record.xml b/presentation/src/main/res/navigation/nav_create_record.xml new file mode 100644 index 00000000..9b6df212 --- /dev/null +++ b/presentation/src/main/res/navigation/nav_create_record.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/values-nl/strings.xml b/presentation/src/main/res/values-nl/strings.xml index 4e42dc29..8475e17a 100755 --- a/presentation/src/main/res/values-nl/strings.xml +++ b/presentation/src/main/res/values-nl/strings.xml @@ -130,6 +130,10 @@ Invoerveld VOLGENDE BEVESTIG + Eigenschap is aangemaakt! + Kies type + Wat voor type eigenschap wilt u aanmaken? + Nieuwe eigenschap Eigenschap details @@ -139,6 +143,24 @@ Fout tijdens het verzenden van de validatieaanvraag Validatie goedgekeurd Validatie geweigerd + Kies validator + Validaties + Deze eigenschap heeft geen validaties + + Wil je deze eigenschap bewerken? + Wil u deze eigenschap archiveren? + + Alle validaties worden verwijderd. U dient opnieuw validaties aan te vragen voor de nieuwe eigenschap. + Alle validaties worden verwijderd. + + ANNULEER + BEWERK + VERWIJDER + + Mijn eigenschappen + Archief + + Eigendom informatie @@ -203,7 +225,10 @@ De voucher is verzonden per mail Dit is de QR-code van de Voucher %s\nDit is uw in de vorm van een QR-code - Laat de aanbieder deze QR-Code scannen om een betaling te kunnen doen + Dit is uw voucher in de vorm van een QR-code \nLaat de aanbieder deze QR-code scannen om een betaling te kunnen doen. + Gebruik deze voucher voor %s + Deze voucher is verlopen op %s + Voucher is verlopen E-mail naar mij BEVESTIG Stuur de voucher naar uw e-mailadres? @@ -223,6 +248,7 @@ Er is een email naar je toegestuurd. Klik op de link in de email die u heeft ontvangen op om uw aanmelding af te maken \n + Controleer uw e-mail. Aanmelding geslaagd! @@ -232,7 +258,7 @@ Door uw identificatie nummer te delen, kunnen problemen beter en sneller worden opgelost, en kunnen de ontwikkelaars eventueel contact met u opnemen - VOLGENDE + Volgende Annuleren Open mail app @@ -245,5 +271,13 @@ Kies een organisatie Test transactie succesvol afgerond + + Er is een nieuwe update beschikbaar! + + Test transactie + Test bedrijf + Bevestigen + + Wachten... diff --git a/presentation/src/main/res/values/attrs.xml b/presentation/src/main/res/values/attrs.xml index 7cbb8fd0..badc89f0 100644 --- a/presentation/src/main/res/values/attrs.xml +++ b/presentation/src/main/res/values/attrs.xml @@ -2,46 +2,44 @@ - - - + + - - + + - - - - - - + + + + + - + - - + + - - - + + + @@ -70,12 +68,12 @@ - - + + - + @@ -85,4 +83,13 @@ + + + + + + \ No newline at end of file diff --git a/presentation/src/main/res/values/colors.xml b/presentation/src/main/res/values/colors.xml index 8d770722..fa361cd3 100644 --- a/presentation/src/main/res/values/colors.xml +++ b/presentation/src/main/res/values/colors.xml @@ -3,14 +3,13 @@ #ffffff #f7f7f7 #315EFD - + #f4f4f4 #bababa - #00000000 #000000 #ffffff @@ -28,8 +27,6 @@ #F8F8F8 - - #3c4c62 #556d87 @@ -46,6 +43,8 @@ #3c4c62 #2e3242 #61000000 + #777B82 + #4b5461 #ececec #fbfbfb @@ -57,4 +56,6 @@ #315efd + + #66000000 \ No newline at end of file diff --git a/presentation/src/main/res/values/dimens.xml b/presentation/src/main/res/values/dimens.xml index 65cec92f..92fd952c 100755 --- a/presentation/src/main/res/values/dimens.xml +++ b/presentation/src/main/res/values/dimens.xml @@ -30,6 +30,7 @@ 48.2sp 72dp 34sp + 16dp diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml index cdf32a1a..105207be 100755 --- a/presentation/src/main/res/values/strings.xml +++ b/presentation/src/main/res/values/strings.xml @@ -140,6 +140,13 @@ Value NEXT STEP SUBMIT + Record created successfully! + Choose Type + Select the type under which the new record type should be added + New record + + My records + Archive Record detail @@ -149,6 +156,19 @@ Error while sending validation request Validation approved Validation declined + Choose a validator + Validations + You have no validations yet + + Would you like to archive record? + Would you like to archive record? + + All validations for this record will be removed. You must select validators again for the edited record + All validations for this record will be removed. + + CANCEL + EDIT + ARCHIVE Wallet info @@ -211,7 +231,10 @@ The voucher was sent to your mail This is the QR-code of the Voucher %s\nThis is your voucher in the form of a QR-code - Let the shopkeeper scan this QR-code to make a payment + This is your voucher in the form of QR-code\nLet the shopkeeper scan it to make a payment + Use this voucher until %s + This voucher has expired on %s + Voucher expired Email to me Would you like to send us your feedback by e-mail? Send the voucher to your email? @@ -228,10 +251,12 @@ The offers below are reserved by the customer COMPLETE AN AMOUNT + Check your email Klik op de link in de email die u heeft ontvangen op om uw aanmelding af te maken \n + Check your email @@ -242,7 +267,7 @@ If you agree to share your id number for crash reports, our developers will see the exact problem that might happen with Me and solve it much faster. - NEXT STEP + Next step Cancel Open mail app Registering... @@ -255,4 +280,37 @@ Operation completed + Test transaction + Test organisation + + New update is available,\n update your application! + + + + Hello blank fragment + Submit + + Waiting... + + PROD + STAGING + DEMO + DEV + OTHER + + + @string/api_prod + @string/api_demo + @string/api_staging + @string/api_dev + @string/api_other + + + + FullscreenSuccessActivity + Dummy Button + DUMMY\nCONTENT + SuccessDialogActivity + + diff --git a/presentation/src/main/res/values/styles.xml b/presentation/src/main/res/values/styles.xml index b9312a7d..40c7a136 100755 --- a/presentation/src/main/res/values/styles.xml +++ b/presentation/src/main/res/values/styles.xml @@ -311,4 +311,36 @@ @color/error_email + + + + + + + + + +