Compare commits
15 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
d08b7df5f0 | |
|
|
333cb3fa64 | |
|
|
098db7d2e3 | |
|
|
e1eed0c37f | |
|
|
de538e14ba | |
|
|
054b62424c | |
|
|
a863285016 | |
|
|
8383a41bca | |
|
|
b6d90da359 | |
|
|
f66d85c9c6 | |
|
|
4049f4e458 | |
|
|
dc2b9ef3ff | |
|
|
20a9597c35 | |
|
|
f7f98f0106 | |
|
|
2dbe8a45e3 |
39
build.gradle
39
build.gradle
|
|
@ -1,9 +1,7 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'me.tatarka.retrolambda'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion "25.0.3"
|
||||
compileSdkVersion compileSdk
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
|
|
@ -17,37 +15,40 @@ android {
|
|||
|
||||
repositories {
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
maven { url "http://dl.bintray.com/touchin/touchin-tools" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':libraries:components')
|
||||
api project(path: ':libraries:components')
|
||||
|
||||
compile 'net.danlew:android.joda:2.9.9'
|
||||
compile 'com.android.support:multidex:1.0.1'
|
||||
compile 'io.reactivex:rxandroid:1.2.1'
|
||||
compile 'io.reactivex:rxjava:1.3.0'
|
||||
api 'net.danlew:android.joda:2.9.9'
|
||||
api 'com.android.support:multidex:1.0.2'
|
||||
api "io.reactivex:rxandroid:$rxAndroidVersion"
|
||||
api "io.reactivex:rxjava:$rxJavaVersion"
|
||||
|
||||
provided 'com.android.support:appcompat-v7:25.3.1'
|
||||
provided 'com.android.support:recyclerview-v7:25.3.1'
|
||||
compileOnly "com.android.support:appcompat-v7:$supportLibraryVersion"
|
||||
compileOnly "com.android.support:recyclerview-v7:$supportLibraryVersion"
|
||||
|
||||
provided 'com.squareup.retrofit2:retrofit:2.3.0'
|
||||
provided('com.google.http-client:google-http-client-jackson2:1.22.0') {
|
||||
compileOnly 'com.squareup.retrofit2:retrofit:2.3.0'
|
||||
compileOnly('com.google.http-client:google-http-client-jackson2:1.23.0') {
|
||||
exclude(group: 'org.apache.httpcomponents', module: 'httpclient')
|
||||
}
|
||||
|
||||
provided 'com.facebook.fresco:fresco:1.3.0'
|
||||
provided 'com.bluelinelabs:logansquare:1.3.7'
|
||||
compileOnly 'com.facebook.fresco:fresco:1.5.0'
|
||||
compileOnly 'ru.touchin:logansquare:1.4.1'
|
||||
|
||||
provided 'com.scottyab:aes-crypto:0.0.4'
|
||||
compileOnly 'com.scottyab:aes-crypto:0.0.4'
|
||||
|
||||
provided('io.socket:socket.io-client:0.9.0') {
|
||||
// don't use latest(1.0 and above) because they don't support Socket.IO server 1.x version
|
||||
//noinspection NewerVersionAvailable
|
||||
compileOnly('io.socket:socket.io-client:0.9.0') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
provided('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
|
||||
transitive = true;
|
||||
compileOnly('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
|
||||
transitive = true
|
||||
}
|
||||
|
||||
provided 'com.facebook.stetho:stetho:1.5.0'
|
||||
compileOnly 'com.facebook.stetho:stetho:1.5.0'
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ package ru.touchin.templates;
|
|||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.ColorRes;
|
||||
|
|
@ -62,9 +62,14 @@ public abstract class TouchinActivity<TLogic extends Logic> extends ViewControll
|
|||
* @param primaryColorRes Color of application to show in task bar.
|
||||
*/
|
||||
protected void setupTaskDescriptor(@NonNull final String label, @DrawableRes final int iconRes, @ColorRes final int primaryColorRes) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
final ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription(label,
|
||||
((BitmapDrawable) ContextCompat.getDrawable(this, iconRes)).getBitmap(),
|
||||
iconRes,
|
||||
ContextCompat.getColor(this, primaryColorRes));
|
||||
setTaskDescription(taskDescription);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
final ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription(label,
|
||||
BitmapFactory.decodeResource(getResources(), iconRes),
|
||||
ContextCompat.getColor(this, primaryColorRes));
|
||||
setTaskDescription(taskDescription);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public final class CalendarUtils {
|
|||
* @param calendarItems List of {@link CalendarItem} where need to find specific element;
|
||||
* @param position Position of adapter;
|
||||
* @return Position of Header that respond to requested position.
|
||||
* Returns null if Header or related CalendarItem was not found for specified position.
|
||||
* Returns null if Header or related CalendarItem was not found for specified position.
|
||||
*/
|
||||
@Nullable
|
||||
public static Integer findPositionOfSelectedMonth(@NonNull final List<CalendarItem> calendarItems, final long position) {
|
||||
|
|
@ -76,7 +76,7 @@ public final class CalendarUtils {
|
|||
* @param calendarItems List of {@link CalendarItem} where need to find specific element;
|
||||
* @param date Requested date in milliseconds.
|
||||
* @return Position of Calendar cell that that has specific date.
|
||||
* Returns null if CalendarItem was not found for specified position.
|
||||
* Returns null if CalendarItem was not found for specified position.
|
||||
*/
|
||||
@Nullable
|
||||
public static Integer findPositionByDate(@NonNull final List<CalendarItem> calendarItems, final long date) {
|
||||
|
|
|
|||
|
|
@ -168,6 +168,14 @@ public abstract class Chat<TOutgoingMessage> {
|
|||
retrySendingRequest.onNext(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to cancel sending current message.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> observeCancelEvent(@NonNull final TOutgoingMessage message) {
|
||||
return Observable.never();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates chat so it will stop sending messages.
|
||||
*/
|
||||
|
|
@ -194,6 +202,7 @@ public abstract class Chat<TOutgoingMessage> {
|
|||
.first()
|
||||
.switchMap(shouldSendMessage -> shouldSendMessage
|
||||
? createSendMessageObservable(message).ignoreElements() : Observable.empty())
|
||||
.takeUntil(observeCancelEvent(message))
|
||||
.retryWhen(attempts -> attempts.switchMap(ignored -> {
|
||||
isSendingInError.onNext(true);
|
||||
return Observable
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public class LoganSquareJodaTimeConverter implements TypeConverter<DateTime> {
|
|||
@Override
|
||||
public DateTime parse(@NonNull final JsonParser jsonParser) throws IOException {
|
||||
final String dateString = jsonParser.getValueAsString();
|
||||
if (dateString == null) {
|
||||
if (dateString == null || dateString.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
|
|
@ -60,9 +60,9 @@ public class LoganSquareJodaTimeConverter implements TypeConverter<DateTime> {
|
|||
@NonNull final JsonGenerator jsonGenerator)
|
||||
throws IOException {
|
||||
if (fieldName != null) {
|
||||
jsonGenerator.writeStringField(fieldName, object != null ? object.toString() : null);
|
||||
jsonGenerator.writeStringField(fieldName, object != null && !object.toString().isEmpty() ? object.toString() : null);
|
||||
} else {
|
||||
jsonGenerator.writeString(object != null ? object.toString() : null);
|
||||
jsonGenerator.writeString(object != null && !object.toString().isEmpty() ? object.toString() : null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,13 +20,15 @@
|
|||
package ru.touchin.templates.logansquare;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.bluelinelabs.logansquare.ConverterUtils;
|
||||
import com.bluelinelabs.logansquare.LoganSquare;
|
||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
|
@ -42,7 +44,7 @@ import ru.touchin.templates.retrofit.JsonResponseBodyConverter;
|
|||
|
||||
/**
|
||||
* Created by Gavriil Sitnikov on 2/06/2016.
|
||||
* LoganSquareConverter class to use with {@link Retrofit} to parse and generate models based on Google Jackson library {@link JacksonFactory}.
|
||||
* LoganSquareConverter class to use with {@link Retrofit} to parse and generate models based on Logan Square library.
|
||||
*/
|
||||
public class LoganSquareJsonFactory extends Converter.Factory {
|
||||
|
||||
|
|
@ -63,6 +65,16 @@ public class LoganSquareJsonFactory extends Converter.Factory {
|
|||
return new LoganSquareRequestBodyConverter<>();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Converter<?, String> stringConverter(@NonNull final Type type, @NonNull final Annotation[] annotations, @NonNull final Retrofit retrofit) {
|
||||
if (type instanceof Class && ((Class) type).getSuperclass() == Enum.class) {
|
||||
return new LoganSquareStringEnumConverter<>();
|
||||
} else {
|
||||
return super.stringConverter(type, annotations, retrofit);
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoganSquareJsonResponseBodyConverter<T> extends JsonResponseBodyConverter<T> {
|
||||
|
||||
@NonNull
|
||||
|
|
@ -101,11 +113,29 @@ public class LoganSquareJsonFactory extends Converter.Factory {
|
|||
public static class LoganSquareRequestBodyConverter<T> extends JsonRequestBodyConverter<T> {
|
||||
|
||||
@Override
|
||||
protected void writeValueToByteArray(@NonNull final T value, @NonNull final ByteArrayOutputStream byteArrayOutputStream)
|
||||
throws IOException {
|
||||
protected void writeValueToByteArray(@NonNull final T value, @NonNull final ByteArrayOutputStream byteArrayOutputStream) throws IOException {
|
||||
LoganSquare.serialize(value, byteArrayOutputStream);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public static class LoganSquareStringEnumConverter<T> implements Converter<T, String> {
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"unchecked", "TryFinallyCanBeTryWithResources"})
|
||||
@Override
|
||||
public String convert(@NonNull final T value) throws IOException {
|
||||
final StringWriter writer = new StringWriter();
|
||||
try {
|
||||
final JsonGenerator generator = LoganSquare.JSON_FACTORY.createGenerator(writer);
|
||||
LoganSquare.typeConverterFor((Class<T>) value.getClass()).serialize(value, null, false, generator);
|
||||
generator.close();
|
||||
return writer.toString().replaceAll("\"", "");
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public abstract class JsonResponseBodyConverter<T> implements Converter<Response
|
|||
validateCollection((Collection) result);
|
||||
}
|
||||
if (result instanceof Map) {
|
||||
validateCollection(((Map)result).values());
|
||||
validateCollection(((Map) result).values());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ public class ValidationState implements Serializable {
|
|||
|
||||
/**
|
||||
* Returns unique code of the {@link ValidationState}.
|
||||
*
|
||||
* @return code or the ValidationState.
|
||||
*/
|
||||
public int getCode() {
|
||||
|
|
@ -67,6 +68,7 @@ public class ValidationState implements Serializable {
|
|||
|
||||
/**
|
||||
* Don't forget to override this method!
|
||||
*
|
||||
* @param object that you want to compare.
|
||||
* @return true if objects equals and false otherwise.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ public interface ViewWithError {
|
|||
* Shows error
|
||||
* Pass here error state.
|
||||
* It is not correct to pass here {@link ValidationState#VALID} or {@link ValidationState#INITIAL}
|
||||
*
|
||||
* @param validationState error state. Can be other than {@link ValidationState} if you have successor of base {@link ValidationState}.
|
||||
*/
|
||||
void showError(@NonNull final ValidationState validationState);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ public class BooleanValidationController extends ValidationController<Boolean, B
|
|||
|
||||
/**
|
||||
* This method validates bounded view.
|
||||
*
|
||||
* @param activatedObservable emits true when we need to show error on empty fields. Eg when user clicks on Done button but he missed some
|
||||
* necessary fields to fill.
|
||||
* @return observable without any concrete type. Simply subscribe to this method to make it works.
|
||||
|
|
|
|||
|
|
@ -98,8 +98,7 @@ public class EditTextValidationController<TModel extends Serializable>
|
|||
/**
|
||||
* If we don't want to show error when focus is lost.
|
||||
*
|
||||
* @param showErrorOnFocusOut show an error or don't show an error.
|
||||
*
|
||||
* @param showErrorOnFocusOut show an error or don't show an error.
|
||||
*/
|
||||
public void setShowErrorOnFocusOut(final boolean showErrorOnFocusOut) {
|
||||
this.showErrorOnFocusOut = showErrorOnFocusOut;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ import rx.Observable;
|
|||
* Created by Ilia Kurtov on 24/01/2017.
|
||||
* {@link ValidationController} for models that have the same modal as wrapper model. You can use it when you simply need to be sure
|
||||
* that user have selected some item and it is not null.
|
||||
* @param <TModel> type of the model.
|
||||
*
|
||||
* @param <TModel> type of the model.
|
||||
* @param <TValidator> corresponding {@link Validator}
|
||||
*/
|
||||
public class SimpleValidationController<TModel extends Serializable, TValidator extends Validator<TModel, TModel>>
|
||||
|
|
@ -24,6 +25,7 @@ public class SimpleValidationController<TModel extends Serializable, TValidator
|
|||
|
||||
/**
|
||||
* This method validates bounded view.
|
||||
*
|
||||
* @param activatedObservable emits true when we need to show error on empty fields. Eg when user clicks on Done button but he missed some
|
||||
* necessary fields to fill.
|
||||
* @return observable without any concrete type. Simply subscribe to this method to make it works.
|
||||
|
|
|
|||
|
|
@ -53,10 +53,11 @@ public class ValidationController
|
|||
/**
|
||||
* Bind to this observable to connect view and model. If you provide first argument (viewStateObservable) - the connection would be two-way.
|
||||
* If not - one-way. This method changes updates view with current {@link ValidationState}.
|
||||
*
|
||||
* @param viewStateObservable input view state {@link Observable}.
|
||||
* Eg it can be observable with input text from the {@link android.widget.EditText}
|
||||
* @param updateViewAction action that updates current state of the bounded view.
|
||||
* @param viewWithError view that implements {@link ViewWithError} interface and could reacts to the validation errors.
|
||||
* @param updateViewAction action that updates current state of the bounded view.
|
||||
* @param viewWithError view that implements {@link ViewWithError} interface and could reacts to the validation errors.
|
||||
* @return observable without any concrete type. Simply subscribe to this method to make it works.
|
||||
*/
|
||||
@NonNull
|
||||
|
|
@ -68,7 +69,7 @@ public class ValidationController
|
|||
: Observable.empty();
|
||||
return Observable
|
||||
.merge(getValidator().getWrapperModel().observe()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext(updateViewAction),
|
||||
getValidator().getValidationState().observe()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
@ -84,6 +85,7 @@ public class ValidationController
|
|||
|
||||
/**
|
||||
* Helper function to check if validation state in error state ot not
|
||||
*
|
||||
* @param validationState the state you want to check for the errors.
|
||||
* @return true if validation state is in error and false otherwise.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
|
|||
* This flag needed to force showing errors. You don't want to show final error when you start to enter data in some field at first time.
|
||||
* But if user leaves this view and final check not passed - you need to force to show an error till user not enters correct data and leaves
|
||||
* the view.
|
||||
*
|
||||
* @return {@link NonNullChangeable} with current state of the flag - do we need to show errors from final checks while user types.
|
||||
*/
|
||||
@NonNull
|
||||
|
|
@ -60,6 +61,7 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
|
|||
|
||||
/**
|
||||
* Use this method to get or set final check.
|
||||
*
|
||||
* @return final check.
|
||||
*/
|
||||
@NonNull
|
||||
|
|
@ -69,6 +71,7 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
|
|||
|
||||
/**
|
||||
* Use this method to get or set primary check.
|
||||
*
|
||||
* @return primary check.
|
||||
*/
|
||||
@NonNull
|
||||
|
|
@ -119,6 +122,7 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
|
|||
|
||||
/**
|
||||
* Validates text with primary check.
|
||||
*
|
||||
* @param text - input text.
|
||||
* @return {@link Observable} with the result of the primary check.
|
||||
*/
|
||||
|
|
@ -129,6 +133,7 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
|
|||
|
||||
/**
|
||||
* Validates text with final check.
|
||||
*
|
||||
* @param text - input text.
|
||||
* @return {@link Observable} with the result of the final check.
|
||||
*/
|
||||
|
|
@ -140,9 +145,10 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
|
|||
/**
|
||||
* Validates text with primary and final check consequentially and returns {@link Observable} with {@link HalfNullablePair} of final state
|
||||
* and resulting model.
|
||||
*
|
||||
* @param text - input text.
|
||||
* @return pair with final {@link ValidationState} that is always not null and a model that we get after converting the text.
|
||||
* Model can be null if validation fails on primary or final checks.
|
||||
* Model can be null if validation fails on primary or final checks.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -12,12 +12,14 @@ import rx.Observable;
|
|||
/**
|
||||
* Created by Ilia Kurtov on 24/01/2017.
|
||||
* Class that simplifies work with {@link Validator}'s that have the same wrapper model and model type.
|
||||
*
|
||||
* @param <TModel> model that should be bounded with a view.
|
||||
*/
|
||||
public class SameTypeValidator<TModel extends Serializable> extends Validator<TModel, TModel> {
|
||||
|
||||
/**
|
||||
* Simply returns the same model without any converting.
|
||||
*
|
||||
* @param wrapperModel input model.
|
||||
* @return the same model as input parameter.
|
||||
* @throws Throwable - in this case no throwable would be thrown.
|
||||
|
|
@ -31,9 +33,10 @@ public class SameTypeValidator<TModel extends Serializable> extends Validator<TM
|
|||
|
||||
/**
|
||||
* Validates {@link TModel} and returns {@link Observable} with {@link HalfNullablePair} of final state and resulting model.
|
||||
*
|
||||
* @param wrapperModel - not null value that should be validated.
|
||||
* @return pair with final {@link ValidationState} that is always not null and a model that we get after converting the {@link TModel}.
|
||||
* Model can be null if validation fails.
|
||||
* Model can be null if validation fails.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
|
|||
|
||||
/**
|
||||
* This method converts {@link TWrapperModel} into a {@link TModel}.
|
||||
*
|
||||
* @param wrapperModel - not null value that should be converted into a {@link TModel} object.
|
||||
* @return converted wrapperModel into a {@link TModel}.
|
||||
* @throws Throwable for the cases when converting cannot be processed.
|
||||
|
|
@ -63,6 +64,7 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
|
|||
|
||||
/**
|
||||
* Call this method to get {@link Changeable} with {@link TWrapperModel} inside it that should be connected to its bounded view.
|
||||
*
|
||||
* @return {@link Changeable} with {@link TWrapperModel}.
|
||||
*/
|
||||
@NonNull
|
||||
|
|
@ -72,6 +74,7 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
|
|||
|
||||
/**
|
||||
* Returns current {@link ValidationState} or its successor. Needed to connect with bounded view and react to this state changes.
|
||||
*
|
||||
* @return current validation state.
|
||||
*/
|
||||
@NonNull
|
||||
|
|
@ -82,6 +85,7 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
|
|||
/**
|
||||
* This method needed to get {@link ValidationState} that needed to be shown when bounded view is empty and you need to show to user reminder,
|
||||
* that he or she needs to fill this view.
|
||||
*
|
||||
* @return {@link ValidationState} that should be shown for an empty field.
|
||||
*/
|
||||
@NonNull
|
||||
|
|
@ -91,9 +95,10 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
|
|||
|
||||
/**
|
||||
* Validates {@link TWrapperModel} and returns {@link Observable} with {@link HalfNullablePair} of final state and resulting model.
|
||||
*
|
||||
* @param wrapperModel - not null value that should be validated.
|
||||
* @return pair with final {@link ValidationState} that is always not null and a model that we get after converting the {@link TWrapperModel}.
|
||||
* Model can be null if validation fails.
|
||||
* Model can be null if validation fails.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract Observable<HalfNullablePair<ValidationState, TModel>> fullValidateAndGetModel(@NonNull final TWrapperModel wrapperModel);
|
||||
|
|
|
|||
Loading…
Reference in New Issue