diff --git a/AndroidClient/build.gradle b/AndroidClient/build.gradle
index 74add1e..46f57fd 100644
--- a/AndroidClient/build.gradle
+++ b/AndroidClient/build.gradle
@@ -40,7 +40,6 @@ android {
baseline = file("lint-baseline.xml")
}
}
-
dependencies {
implementation libs.appcompat
@@ -48,8 +47,8 @@ dependencies {
implementation libs.material
implementation libs.cardview
- implementation libs.lifecycleextensions
- annotationProcessor libs.lifecyclecompiler
+ implementation libs.lifecycle.extensions
+ annotationProcessor libs.lifecycle.compiler
implementation libs.socketioclient
implementation libs.eventbus
@@ -76,17 +75,23 @@ dependencies {
// Retrofit & OkHttp
implementation libs.retrofit
- implementation libs.retrofitconvertergson
+ implementation libs.retrofit.converter.jackson
+ //implementation libs.retrofitconvertergson
+ // https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310
+ implementation(libs.jackson.datatype.jsr310)
+ // https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jdk8
+ implementation(libs.jackson.datatype.jdk8)
+
implementation libs.okhttp
// Dagger 2
implementation libs.dagger
- annotationProcessor libs.daggercompiler
+ annotationProcessor libs.dagger.compiler
// Room
- implementation libs.roomruntime
- implementation libs.roomrxjava2
- annotationProcessor libs.roomcompiler
+ implementation libs.room.runtime
+ implementation libs.room.rxjava2
+ annotationProcessor libs.room.compiler
//LocalDateTime features
@@ -97,6 +102,8 @@ dependencies {
// https://mvnrepository.com/artifact/androidx.preference/preference
implementation 'androidx.preference:preference:1.2.1'
+ implementation libs.core
+
/*TESTS*/
@@ -112,3 +119,4 @@ dependencies {
//testImplementation("org.powermock:powermock-module-junit4:2.0.9")
}
+
diff --git a/AndroidClient/src/main/AndroidManifest.xml b/AndroidClient/src/main/AndroidManifest.xml
index 7fdabcf..b06c7d5 100644
--- a/AndroidClient/src/main/AndroidManifest.xml
+++ b/AndroidClient/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
+
+
+
+
diff --git a/AndroidClient/src/main/java/com/tom/meeter/App.java b/AndroidClient/src/main/java/com/tom/meeter/App.java
index ce76f46..6356188 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/App.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/App.java
@@ -1,5 +1,6 @@
package com.tom.meeter;
+import static com.tom.meeter.context.notification.NotificationHelper.createNotificationChannel;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
import android.app.Application;
@@ -8,8 +9,12 @@
import com.tom.meeter.context.auth.DaggerAuthComponent;
import com.tom.meeter.context.event.DaggerEventComponent;
import com.tom.meeter.context.event.EventComponent;
+import com.tom.meeter.context.image.DaggerImageComponent;
+import com.tom.meeter.context.image.ImageComponent;
import com.tom.meeter.context.token.DaggerTokenComponent;
import com.tom.meeter.context.token.TokenComponent;
+import com.tom.meeter.context.user.DaggerUserComponent;
+import com.tom.meeter.context.user.UserComponent;
public class App extends Application {
@@ -17,18 +22,27 @@ public class App extends Application {
private AppComponent component;
private AuthComponent authComponent;
private TokenComponent tokenComponent;
+ private ImageComponent imageComponent;
private EventComponent eventComponent;
+ private UserComponent userComponent;
@Override
public void onCreate() {
super.onCreate();
logMethod(TAG, this);
+ /* Independent */
tokenComponent = buildTokenComponent();
authComponent = buildAuthComponent();
+ imageComponent = buildImageComponent();
+
+ /* Dependent */
eventComponent = buildEventComponent();
+ userComponent = buildUserComponent();
component = buildComponent();
+
+ createNotificationChannel(this);
}
@Override
@@ -40,8 +54,10 @@ public void onTerminate() {
protected AppComponent buildComponent() {
return DaggerAppComponent.builder()
.tokenComponent(tokenComponent)
+ .imageComponent(imageComponent)
.authComponent(authComponent)
.eventComponent(eventComponent)
+ .userComponent(userComponent)
.application(this)
.build();
}
@@ -58,9 +74,25 @@ protected TokenComponent buildTokenComponent() {
.build();
}
+ protected ImageComponent buildImageComponent() {
+ return DaggerImageComponent.builder()
+ .application(this)
+ .build();
+ }
+
protected EventComponent buildEventComponent() {
return DaggerEventComponent.builder()
.application(this)
+ .tokenComponent(tokenComponent)
+ .imageComponent(imageComponent)
+ .build();
+ }
+
+ protected UserComponent buildUserComponent() {
+ return DaggerUserComponent.builder()
+ .application(this)
+ .tokenComponent(tokenComponent)
+ .imageComponent(imageComponent)
.build();
}
@@ -79,4 +111,12 @@ public TokenComponent getTokenComponent() {
public EventComponent getEventComponent() {
return eventComponent;
}
+
+ public ImageComponent getImageComponent() {
+ return imageComponent;
+ }
+
+ public UserComponent getUserComponent() {
+ return userComponent;
+ }
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/AppComponent.java b/AndroidClient/src/main/java/com/tom/meeter/AppComponent.java
index f61c54b..c15f536 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/AppComponent.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/AppComponent.java
@@ -4,21 +4,33 @@
import com.tom.meeter.context.auth.AuthComponent;
import com.tom.meeter.context.event.EventComponent;
-import com.tom.meeter.context.event.activity.EventActivity;
+import com.tom.meeter.context.image.ImageComponent;
import com.tom.meeter.context.profile.activity.ProfileActivity;
import com.tom.meeter.context.profile.activity.SettingsActivity;
+import com.tom.meeter.context.profile.fragment.ActiveEventsFragment;
+import com.tom.meeter.context.profile.fragment.GoogleMapsFragment;
import com.tom.meeter.context.profile.fragment.ProfileFragment;
-import com.tom.meeter.context.profile.fragment.UserEventsFragment;
+import com.tom.meeter.context.profile.fragment.ProfileEventsFragment;
import com.tom.meeter.context.token.TokenComponent;
-import com.tom.meeter.context.user.activity.UserActivity;
+import com.tom.meeter.context.user.UserComponent;
import com.tom.meeter.infrastructure.injection.viewmodel.ViewModelModule;
import dagger.BindsInstance;
import dagger.Component;
@Component(
- modules = {AppModule.class, ViewModelModule.class},
- dependencies = {TokenComponent.class, AuthComponent.class, EventComponent.class})
+ modules = {
+ AppModule.class,
+ ViewModelModule.class
+ },
+ dependencies = {
+ TokenComponent.class,
+ AuthComponent.class,
+ ImageComponent.class,
+
+ EventComponent.class,
+ UserComponent.class
+ })
@AppScope
public interface AppComponent {
@@ -29,21 +41,27 @@ interface Builder {
Builder application(Application application);
Builder authComponent(AuthComponent authComponent);
+
Builder tokenComponent(TokenComponent tokenComponent);
+
Builder eventComponent(EventComponent eventComponent);
+ Builder imageComponent(ImageComponent imageComponent);
+
+ Builder userComponent(UserComponent userComponent);
+
AppComponent build();
}
void inject(ProfileActivity profileActivity);
- void inject(ProfileFragment profileFragment);
+ void inject(SettingsActivity settingsActivity);
- void inject(UserEventsFragment userEventsFragment);
+ void inject(ProfileFragment profileFragment);
- void inject(SettingsActivity settingsActivity);
+ void inject(GoogleMapsFragment googleMapsFragment);
- void inject(UserActivity userActivity);
+ void inject(ActiveEventsFragment activeEventsFragment);
- void inject(EventActivity eventActivity);
+ void inject(ProfileEventsFragment profileEventsFragment);
}
\ No newline at end of file
diff --git a/AndroidClient/src/main/java/com/tom/meeter/AppModule.java b/AndroidClient/src/main/java/com/tom/meeter/AppModule.java
index 26855bd..efc0c8c 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/AppModule.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/AppModule.java
@@ -8,15 +8,20 @@
import androidx.annotation.NonNull;
import androidx.room.Room;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.tom.meeter.context.profile.event.database.EventDao;
import com.tom.meeter.context.profile.event.database.EventDatabase;
-import com.tom.meeter.context.profile.event.service.EventService;
import com.tom.meeter.context.profile.service.ProfileService;
import com.tom.meeter.context.profile.settings.service.SettingsService;
import com.tom.meeter.context.profile.user.database.UserDao;
import com.tom.meeter.context.profile.user.database.UserDatabase;
-import com.tom.meeter.context.user.service.UserService;
+import com.tom.meeter.infrastructure.http.HttpClient;
+import java.util.TimeZone;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@@ -25,7 +30,7 @@
import dagger.Module;
import dagger.Provides;
import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
+import retrofit2.converter.jackson.JacksonConverterFactory;
@Module
public class AppModule {
@@ -42,22 +47,18 @@ public AppModule() {
public ProfileService provideProfileService(Application app) {
return new Retrofit.Builder()
.baseUrl(getServerPath(app))
- .addConverterFactory(GsonConverterFactory.create())
+ .addConverterFactory(JacksonConverterFactory.create(
+ JsonMapper.builder()
+ .addModule(new JavaTimeModule())
+ .addModule(new Jdk8Module())
+ .serializationInclusion(JsonInclude.Include.NON_NULL)
+ .build()
+ .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .setTimeZone(TimeZone.getDefault())))
.build()
.create(ProfileService.class);
}
- @AppScope
- @NonNull
- @Provides
- public UserService provideUserService(Application app) {
- return new Retrofit.Builder()
- .baseUrl(getServerPath(app))
- .addConverterFactory(GsonConverterFactory.create())
- .build()
- .create(UserService.class);
- }
-
@AppScope
@NonNull
@Provides
@@ -82,17 +83,6 @@ public Executor provideExecutor() {
new ArrayBlockingQueue<>(15, false));
}
- @AppScope
- @NonNull
- @Provides
- public EventService provideEventService(Application app) {
- return new Retrofit.Builder()
- .baseUrl(getServerPath(app))
- .addConverterFactory(GsonConverterFactory.create())
- .build()
- .create(EventService.class);
- }
-
@AppScope
@NonNull
@Provides
@@ -115,8 +105,16 @@ public EventDao provideEventDao(EventDatabase eventDatabase) {
public SettingsService provideSettingsService(Application app) {
return new Retrofit.Builder()
.baseUrl(getServerPath(app))
- .addConverterFactory(GsonConverterFactory.create())
+ .addConverterFactory(JacksonConverterFactory.create())
+ //.addConverterFactory(GsonConverterFactory.create())
.build()
.create(SettingsService.class);
}
+
+ @AppScope
+ @NonNull
+ @Provides
+ public HttpClient provideHttpClient(Application app) {
+ return new HttpClient(getServerPath(app));
+ }
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/components/ExpandableHeightGridView.java b/AndroidClient/src/main/java/com/tom/meeter/components/ExpandableHeightGridView.java
deleted file mode 100644
index b9afe48..0000000
--- a/AndroidClient/src/main/java/com/tom/meeter/components/ExpandableHeightGridView.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.tom.meeter.components;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.ViewGroup;
-import android.widget.GridView;
-
-public class ExpandableHeightGridView extends GridView {
- private boolean expanded = false;
-
- public ExpandableHeightGridView(Context context) {
- super(context);
- }
-
- public ExpandableHeightGridView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ExpandableHeightGridView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public boolean isExpanded() {
- return expanded;
- }
-
- @Override
- public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (isExpanded()) {
- // Calculate entire height by providing a very large height hint.
- // View.MEASURED_SIZE_MASK represents the largest height possible.
- int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK,
- MeasureSpec.AT_MOST);
- super.onMeasure(widthMeasureSpec, expandSpec);
-
- ViewGroup.LayoutParams params = getLayoutParams();
- params.height = getMeasuredHeight();
- } else {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- public void setExpanded(boolean expanded) {
- this.expanded = expanded;
- }
-}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/auth/AuthModule.java b/AndroidClient/src/main/java/com/tom/meeter/context/auth/AuthModule.java
index 2738374..3ab22e7 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/auth/AuthModule.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/auth/AuthModule.java
@@ -15,7 +15,8 @@
import dagger.Module;
import dagger.Provides;
import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
+import retrofit2.converter.jackson.JacksonConverterFactory;
+//import retrofit2.converter.gson.GsonConverterFactory;
@Module
public class AuthModule {
@@ -32,7 +33,8 @@ public AuthModule() {
public AuthService provideAuthService(Application app) {
return new Retrofit.Builder()
.baseUrl(getServerPath(app))
- .addConverterFactory(GsonConverterFactory.create())
+ .addConverterFactory(JacksonConverterFactory.create())
+ //.addConverterFactory(GsonConverterFactory.create())
.build()
.create(AuthService.class);
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/auth/activity/LoginActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/auth/activity/LoginActivity.java
index 8e5c9f1..6051647 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/auth/activity/LoginActivity.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/auth/activity/LoginActivity.java
@@ -2,6 +2,7 @@
import static com.tom.meeter.context.auth.infrastructure.AccountAuthenticator.ACCOUNT_TYPE;
import static com.tom.meeter.context.auth.infrastructure.AccountAuthenticator.USER_PASS_KEY;
+import static com.tom.meeter.context.auth.infrastructure.AccountAuthenticator.USER_UUID_KEY;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
import android.accounts.Account;
@@ -170,8 +171,10 @@ public void onResponse(Call call, Response respons
Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, userLogin);
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
- intent.putExtra(AccountManager.KEY_AUTHTOKEN, response.body().getToken());
+ TokenResponse res = response.body();
+ intent.putExtra(AccountManager.KEY_AUTHTOKEN, res.getToken());
intent.putExtra(USER_PASS_KEY, userPass);
+ intent.putExtra(USER_UUID_KEY, res.getUuid());
finishLogin(intent);
} else {
new AlertDialog.Builder(LoginActivity.this)
@@ -198,6 +201,7 @@ private void finishLogin(Intent intent) {
String login = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
String accountType = intent.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
String token = intent.getStringExtra(AccountManager.KEY_AUTHTOKEN);
+ String uuid = intent.getStringExtra(USER_UUID_KEY);
String pass = intent.getStringExtra(AccountAuthenticator.USER_PASS_KEY);
Account account = new Account(login, accountType);
if (getIntent().getBooleanExtra(AccountAuthenticator.IS_ADDING_NEW_ACCOUNT_KEY, false)) {
@@ -205,6 +209,7 @@ private void finishLogin(Intent intent) {
// (Not setting the auth token will cause another call to the server to authenticate the user)
accountManager.addAccountExplicitly(account, pass, null);
accountManager.setAuthToken(account, AccountAuthenticator.AUTH_TYPE, token);
+ accountManager.setUserData(account, USER_UUID_KEY, uuid);
} else {
accountManager.setPassword(account, pass);
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/auth/activity/RegistrationActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/auth/activity/RegistrationActivity.java
index 3cb67e4..be555fb 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/auth/activity/RegistrationActivity.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/auth/activity/RegistrationActivity.java
@@ -1,6 +1,7 @@
package com.tom.meeter.context.auth.activity;
import static com.tom.meeter.context.auth.infrastructure.AccountAuthenticator.ACCOUNT_TYPE;
+import static com.tom.meeter.context.auth.infrastructure.AccountAuthenticator.USER_UUID_KEY;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
import android.accounts.AccountManager;
@@ -131,12 +132,14 @@ public void submit() {
registerCall.enqueue(new Callback<>() {
@Override
public void onResponse(Call call, Response response) {
+ TokenResponse authRes = response.body();
if (response.code() == HttpCodes.OK) {
Bundle bundle = new Bundle();
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, userLogin);
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
- bundle.putString(AccountManager.KEY_AUTHTOKEN, response.body().getToken());
+ bundle.putString(AccountManager.KEY_AUTHTOKEN, authRes.getToken());
bundle.putString(AccountAuthenticator.USER_PASS_KEY, userPass);
+ bundle.putString(USER_UUID_KEY, authRes.getUuid());
Intent res = new Intent();
res.putExtras(bundle);
@@ -147,12 +150,12 @@ public void onResponse(Call call, Response respons
.show();*/
new AlertDialog.Builder(RegistrationActivity.this)
.setTitle(getString(R.string.register_error))
- .setMessage(response.code() + ":" + response.body())
+ .setMessage(response.code() + ":" + authRes)
.setNegativeButton(getString(R.string.ok), (dialog, id) -> dialog.cancel())
.create()
.show();
Log.d(TAG, "Response failed with [" + response.code()
- + "] code and body {" + response.body() + "}");
+ + "] code and body {" + authRes + "}");
}
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/auth/infrastructure/AccountAuthenticator.java b/AndroidClient/src/main/java/com/tom/meeter/context/auth/infrastructure/AccountAuthenticator.java
index a77443f..d3dd061 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/auth/infrastructure/AccountAuthenticator.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/auth/infrastructure/AccountAuthenticator.java
@@ -36,6 +36,7 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
public static final String AUTH_TYPE = "jwt_auth";
public static final String IS_ADDING_NEW_ACCOUNT_KEY = "is-adding-new-account";
public static final String USER_PASS_KEY = "the-password";
+ public static final String USER_UUID_KEY = "the-uuid";
private static final String LABEL = " label";
//public static final String ACCOUNT_NAME = "Meeter";
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/auth/infrastructure/AuthHelper.java b/AndroidClient/src/main/java/com/tom/meeter/context/auth/infrastructure/AuthHelper.java
index 4cb135c..fb8044d 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/auth/infrastructure/AuthHelper.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/auth/infrastructure/AuthHelper.java
@@ -2,6 +2,7 @@
import static com.tom.meeter.context.auth.infrastructure.AccountAuthenticator.ACCOUNT_TYPE;
import static com.tom.meeter.context.auth.infrastructure.AccountAuthenticator.AUTH_TYPE;
+import static com.tom.meeter.context.auth.infrastructure.AccountAuthenticator.USER_UUID_KEY;
import android.accounts.Account;
import android.accounts.AccountManager;
@@ -15,7 +16,7 @@
import com.tom.meeter.R;
import com.tom.meeter.context.token.service.TokenService;
import com.tom.meeter.infrastructure.common.Globals;
-import com.tom.meeter.infrastructure.http.DisconnectLogger;
+import com.tom.meeter.infrastructure.http.ErrorLogger;
import com.tom.meeter.infrastructure.http.HttpCodes;
import java.io.IOException;
@@ -39,6 +40,14 @@ public static String peekToken(AccountManager am) {
return am.peekAuthToken(getSingleAccount(am), AccountAuthenticator.AUTH_TYPE);
}
+ public static String getAuthHeader(AccountManager am) {
+ return Globals.getAuthHeader(peekToken(am));
+ }
+
+ public static String getUserUuid(AccountManager am) {
+ return am.getUserData(getSingleAccount(am), USER_UUID_KEY);
+ }
+
public static void setToken(AccountManager am, String token) {
am.setAuthToken(getSingleAccount(am), AccountAuthenticator.AUTH_TYPE, token);
}
@@ -76,7 +85,7 @@ public static void checkToken(
return;
}
tokenService.checkToken(Globals.getAuthHeader(token)).enqueue(
- new DisconnectLogger<>(activity) {
+ new ErrorLogger<>(activity) {
@Override
public void onResponse(Call call, Response response) {
if (response.code() == HttpCodes.NOT_AUTHENTICATED) {
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/auth/message/TokenResponse.java b/AndroidClient/src/main/java/com/tom/meeter/context/auth/message/TokenResponse.java
index a891f7e..9e4318c 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/auth/message/TokenResponse.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/auth/message/TokenResponse.java
@@ -1,13 +1,54 @@
package com.tom.meeter.context.auth.message;
-public class TokenResponse {
+import java.util.Objects;
+
+public final class TokenResponse {
private String token;
+ private String uuid;
+
+ public TokenResponse() {
+ }
- public TokenResponse(String token) {
+ public TokenResponse(String token, String uuid) {
this.token = token;
+ this.uuid = uuid;
}
public String getToken() {
return token;
}
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) return true;
+ if (obj == null || obj.getClass() != this.getClass()) return false;
+ var that = (TokenResponse) obj;
+ return Objects.equals(this.token, that.token) &&
+ Objects.equals(this.uuid, that.uuid);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(token, uuid);
+ }
+
+ @Override
+ public String toString() {
+ return "TokenResponse[" +
+ "token=" + token + ", " +
+ "uuid=" + uuid + ']';
+ }
+
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/EventComponent.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/EventComponent.java
index 4c46446..e834b05 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/event/EventComponent.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/EventComponent.java
@@ -2,15 +2,27 @@
import android.app.Application;
+import com.tom.meeter.context.event.activity.EventActivity;
+import com.tom.meeter.context.event.activity.EventLocationMapActivity;
+import com.tom.meeter.context.event.activity.EventOnMapActivity;
import com.tom.meeter.context.event.service.EventService;
-
-import javax.inject.Singleton;
+import com.tom.meeter.context.event.viewmodel.EventViewModelModule;
+import com.tom.meeter.context.image.ImageComponent;
+import com.tom.meeter.context.token.TokenComponent;
import dagger.BindsInstance;
import dagger.Component;
-@Singleton
-@Component(modules = {EventModule.class})
+@EventScope
+@Component(
+ modules = {
+ EventModule.class,
+ EventViewModelModule.class
+ },
+ dependencies = {
+ TokenComponent.class,
+ ImageComponent.class
+ })
public interface EventComponent {
EventService provideEventService();
@@ -20,6 +32,16 @@ interface Builder {
@BindsInstance
Builder application(Application application);
+ Builder tokenComponent(TokenComponent tokenComponent);
+
+ Builder imageComponent(ImageComponent tokenComponent);
+
EventComponent build();
}
+
+ void inject(EventActivity eventActivity);
+
+ void inject(EventOnMapActivity eventOnMapActivity);
+
+ void inject(EventLocationMapActivity eventLocationMapActivity);
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/EventModule.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/EventModule.java
index 8c54e61..5bb0b9c 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/event/EventModule.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/EventModule.java
@@ -7,14 +7,19 @@
import androidx.annotation.NonNull;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.tom.meeter.context.event.service.EventService;
-import javax.inject.Singleton;
+import java.util.TimeZone;
import dagger.Module;
import dagger.Provides;
import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
+import retrofit2.converter.jackson.JacksonConverterFactory;
@Module
public class EventModule {
@@ -25,13 +30,21 @@ public EventModule() {
logMethod(TAG, this);
}
- @Singleton
+ @EventScope
@NonNull
@Provides
public EventService provideEventService(Application app) {
return new Retrofit.Builder()
.baseUrl(getServerPath(app))
- .addConverterFactory(GsonConverterFactory.create())
+ .addConverterFactory(JacksonConverterFactory.create(
+ JsonMapper.builder()
+ .addModule(new JavaTimeModule())
+ //.addModule(new Jdk8Module().configureReadAbsentAsNull(false))
+ .addModule(new Jdk8Module())
+ .serializationInclusion(JsonInclude.Include.NON_NULL)
+ .build()
+ .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .setTimeZone(TimeZone.getDefault())))
.build()
.create(EventService.class);
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/EventScope.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/EventScope.java
new file mode 100644
index 0000000..85a3b66
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/EventScope.java
@@ -0,0 +1,11 @@
+package com.tom.meeter.context.event;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Scope;
+
+@Scope
+@Retention(RetentionPolicy.RUNTIME)
+public @interface EventScope {
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventActivity.java
index 7e9bd4a..0d97e26 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventActivity.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventActivity.java
@@ -1,50 +1,101 @@
package com.tom.meeter.context.event.activity;
import static com.tom.meeter.context.auth.infrastructure.AuthHelper.checkToken;
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.getCircleBitmap;
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.randomPicResource;
+import static com.tom.meeter.context.event.activity.EventLocationMapActivity.createEventLocationMapActivityIntent;
+import static com.tom.meeter.context.event.activity.EventOnMapActivity.dispatchToEventOnMapActivity;
+import static com.tom.meeter.context.event.utils.Utils.createUpdateEventRequest;
+import static com.tom.meeter.context.user.activity.UserActivity.dispatchToUserActivity;
+import static com.tom.meeter.infrastructure.common.CommonHelper.UI_DATE_TIME_FORMAT;
+import static com.tom.meeter.infrastructure.common.CommonHelper.dateOrNull;
+import static com.tom.meeter.infrastructure.common.CommonHelper.textOrNull;
+import static com.tom.meeter.infrastructure.common.ImagesHelper.circleImage;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.showMessage;
import android.accounts.AccountManager;
+import android.app.DatePickerDialog;
+import android.app.TimePickerDialog;
import android.content.Context;
import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
+import android.widget.EditText;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProviders;
+import androidx.viewbinding.ViewBinding;
+import com.google.android.material.datepicker.CalendarConstraints;
+import com.google.android.material.datepicker.DateValidatorPointForward;
+import com.google.android.material.datepicker.MaterialDatePicker;
import com.tom.meeter.App;
+import com.tom.meeter.context.auth.infrastructure.AuthHelper;
+import com.tom.meeter.context.event.message.UpdateEventRequest;
+import com.tom.meeter.context.event.service.EventService;
import com.tom.meeter.context.event.viewmodel.EventViewModel;
+import com.tom.meeter.context.network.dto.EventDTO;
import com.tom.meeter.context.token.service.TokenService;
-import com.tom.meeter.context.user.activity.UserActivity;
-import com.tom.meeter.databinding.EventLayoutBinding;
+import com.tom.meeter.databinding.ActivityEventEditableBinding;
+import com.tom.meeter.databinding.ActivityEventReadableBinding;
+import com.tom.meeter.infrastructure.common.Globals;
+import com.tom.meeter.infrastructure.http.HttpErrorLogger;
import com.tom.meeter.infrastructure.injection.viewmodel.ViewModelFactory;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
import javax.inject.Inject;
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Response;
+
public class EventActivity extends AppCompatActivity {
+
public static final String EVENT_ID_KEY = "event_id";
+ public static final String EXTRA_LAT = "extra_lat";
+ public static final String EXTRA_LNG = "extra_lng";
+
private static final String TAG = EventActivity.class.getCanonicalName();
- EventLayoutBinding binding;
+
+ ViewBinding binding;
@Inject
TokenService tokenService;
@Inject
+ EventService eventService;
+ @Inject
ViewModelFactory viewModelFactory;
private EventViewModel eventViewModel;
- private String eventId;
private AccountManager accountManager;
+ private ActivityResultLauncher mapResult;
+
+ private EventDTO eventCache;
+ private ResponseBody photoCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mapResult = registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ if (result.getResultCode() == RESULT_OK && result.getData() != null) {
+ double lat = result.getData().getDoubleExtra(EXTRA_LAT, 0.0);
+ double lng = result.getData().getDoubleExtra(EXTRA_LNG, 0.0);
+ if (binding instanceof ActivityEventEditableBinding eBinding) {
+ eBinding.eventLatitude.setText(String.valueOf(lat));
+ eBinding.eventLongitude.setText(String.valueOf(lng));
+ }
+ }
+ });
+
logMethod(TAG, this);
Bundle extras = getIntent().getExtras();
@@ -53,45 +104,137 @@ protected void onCreate(Bundle savedInstanceState) {
finish();
return;
}
- eventId = extras.getString(EVENT_ID_KEY);
+ String eventId = extras.getString(EVENT_ID_KEY);
if (eventId == null) {
Log.d(TAG, "Unable to create event activity without 'event_id' provided.");
finish();
return;
}
- ((App) getApplication()).getComponent().inject(this);
+ ((App) getApplication()).getEventComponent().inject(this);
accountManager = AccountManager.get(this);
//setToken(accountManager, Launcher.EXPIRED);
- checkToken(this::onInit, this::finish, accountManager, this, tokenService);
+ checkToken((token) -> onInit(token, eventId), this::finish, accountManager, this, tokenService);
}
- private void onInit(String token) {
- binding = EventLayoutBinding.inflate(getLayoutInflater());
- View view = binding.getRoot();
- setContentView(view);
-
+ private void onInit(String token, String eventId) {
eventViewModel = ViewModelProviders.of(this, viewModelFactory)
.get(EventViewModel.class);
eventViewModel.fetchEventInformation(token, eventId, this);
eventViewModel.getEventLiveData()
.observe(this, event -> {
- if (event != null) {
- Bitmap src = BitmapFactory.decodeResource(getResources(), randomPicResource());
- Bitmap scaled = Bitmap.createScaledBitmap(src, 150, 150, true);
- Bitmap circled = getCircleBitmap(scaled);
- binding.eventPhoto.setImageBitmap(circled);
- binding.eventName.setText(event.getName());
- binding.eventDescription.setText(event.getDescription());
- binding.eventCreatorIdBtn.setOnClickListener(v -> {
- startActivity(new Intent(this, UserActivity.class)
- .putExtra(UserActivity.USER_ID_KEY, event.getCreatorId()));
- });
+ eventCache = event;
+ if (AuthHelper.getUserUuid(accountManager).equals(eventCache.getCreatorId())) {
+ initEditableLayout(token);
+ } else {
+ initReadableLayout();
}
});
}
+ private void initReadableLayout() {
+ binding = ActivityEventReadableBinding.inflate(getLayoutInflater());
+ ActivityEventReadableBinding rBinding = (ActivityEventReadableBinding) binding;
+ View view = rBinding.getRoot();
+ setContentView(view);
+
+ updateReadableLayout();
+ rBinding.eventCreator.setOnClickListener(
+ v -> dispatchToUserActivity(this, eventCache.getCreatorId()));
+ rBinding.btnEventLocationMap.setOnClickListener(
+ v -> dispatchToEventOnMapActivity(this, eventCache.getId()));
+
+
+ eventViewModel.getEventPhotoLiveData()
+ .observe(
+ this, photo -> rBinding.eventPhoto.setImageBitmap(
+ circleImage(photo, 600, 600)));
+ }
+
+ private void initEditableLayout(String token) {
+ binding = ActivityEventEditableBinding.inflate(getLayoutInflater());
+ ActivityEventEditableBinding eBinding = (ActivityEventEditableBinding) binding;
+ View view = eBinding.getRoot();
+ setContentView(view);
+
+ eBinding.saveEventButton.setOnClickListener(v -> {
+ UpdateEventRequest req = createUpdateEventRequest(eventCache, eBinding);
+ if (req.isEmpty()) {
+ showMessage(this, "Empty update request is not sent.");
+ return;
+ }
+ eventService.updateEvent(Globals.getAuthHeader(token), eventCache.getId(), req).enqueue(
+ new HttpErrorLogger<>(getApplicationContext()) {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful()) {
+ eventCache = response.body();
+ updateEditableLayout();
+ showMessage(EventActivity.this, "Saved.");
+ }
+ }
+ });
+ });
+
+
+ /*
+ TODO photoPath;
+ * */
+
+ updateEditableLayout();
+
+ eBinding.selectStartingDateButton.setOnClickListener(
+ v -> showDateTimePicker(eBinding.eventStarting));
+ eBinding.selectEndingDateButton.setOnClickListener(
+ v -> showDateTimePicker(eBinding.eventEnding));
+ eBinding.btnEventLocationMap.setOnClickListener(
+ v -> mapResult.launch(
+ createEventLocationMapActivityIntent(this, eventCache.getId())));
+ eBinding.selectPhotoButton.setOnClickListener(
+ v -> showMessage(EventActivity.this, "Кнопка пока не работает..."));
+
+ eventViewModel.getEventPhotoLiveData()
+ .observe(
+ this, photo -> {
+ photoCache = photo;
+ updateEditablePhoto();
+ });
+ }
+
+ private void updateEditablePhoto() {
+ if (binding instanceof ActivityEventEditableBinding eBinding) {
+ eBinding.eventPhoto.setImageBitmap(circleImage(photoCache, 600, 600));
+ }
+ }
+
+ private void updateEditableLayout() {
+ ActivityEventEditableBinding eBinding = (ActivityEventEditableBinding) binding;
+ eBinding.eventName.setText(eventCache.getName());
+ eBinding.eventCreated.setText(UI_DATE_TIME_FORMAT.format(eventCache.getCreated()));
+
+ eBinding.eventDescription.setText(eventCache.getDescription());
+ eBinding.eventLatitude.setText(textOrNull(eventCache.getLatitude()));
+ eBinding.eventLongitude.setText(textOrNull(eventCache.getLongitude()));
+ eBinding.eventStarting.setText(dateOrNull(eventCache.getStarting()));
+ eBinding.eventEnding.setText(dateOrNull(eventCache.getEnding()));
+ eBinding.eventCity.setText(eventCache.getCity());
+
+ }
+
+ private void updateReadableLayout() {
+ ActivityEventReadableBinding rBinding = (ActivityEventReadableBinding) binding;
+ rBinding.eventName.setText(eventCache.getName());
+ rBinding.eventCreated.setText(UI_DATE_TIME_FORMAT.format(eventCache.getCreated()));
+ rBinding.eventDescription.setText(eventCache.getDescription());
+ rBinding.eventLatitude.setText(textOrNull(eventCache.getLatitude()));
+ rBinding.eventLongitude.setText(textOrNull(eventCache.getLongitude()));
+ rBinding.eventStarting.setText(dateOrNull(eventCache.getStarting()));
+ rBinding.eventEnding.setText(dateOrNull(eventCache.getEnding()));
+ rBinding.eventCity.setText(eventCache.getCity());
+ }
+
+
@Nullable
@Override
public View onCreateView(
@@ -99,4 +242,100 @@ public View onCreateView(
@NonNull AttributeSet attrs) {
return super.onCreateView(parent, name, ctx, attrs);
}
+
+ // Метод для отображения DatePickerDialog
+ private void showDatePickerDialog(final EditText targetEditText) {
+ // Получаем текущую дату
+ Calendar calendar = Calendar.getInstance();
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+ // Создаем и показываем DatePickerDialog
+ DatePickerDialog datePickerDialog = new DatePickerDialog(this,
+ (view, selectedYear, selectedMonth, selectedDay) -> {
+ // Устанавливаем выбранную дату в EditText
+ String selectedDate = selectedDay + "/" + (selectedMonth + 1) + "/" + selectedYear;
+ targetEditText.setText(selectedDate);
+ }, year, month, day);
+
+ // Показываем диалог
+ datePickerDialog.show();
+ }
+
+ // Метод для отображения Material DatePicker
+ private void showMaterialDatePicker(final EditText targetEditText) {
+ // Создаём constraints (ограничения для выбора даты)
+ CalendarConstraints.Builder constraintsBuilder = new CalendarConstraints.Builder();
+ Calendar calendar = Calendar.getInstance();
+ constraintsBuilder.setValidator(DateValidatorPointForward.from(calendar.getTimeInMillis()));
+
+ // Создаем Material DatePicker
+ MaterialDatePicker.Builder builder = MaterialDatePicker.Builder.datePicker();
+ builder.setCalendarConstraints(constraintsBuilder.build());
+ builder.setTitleText("Select Date");
+
+ MaterialDatePicker datePicker = builder.build();
+
+ // Устанавливаем слушатель на выбор даты
+ datePicker.addOnPositiveButtonClickListener(selection -> {
+ // Форматируем выбранную дату
+ Calendar selectedDate = Calendar.getInstance();
+ selectedDate.setTimeInMillis(selection);
+ String selectedDateString = selectedDate.get(Calendar.DAY_OF_MONTH) + "/" +
+ (selectedDate.get(Calendar.MONTH) + 1) + "/" +
+ selectedDate.get(Calendar.YEAR);
+
+ // Устанавливаем выбранную дату в поле
+ targetEditText.setText(selectedDateString);
+ });
+
+ // Показываем диалог
+ datePicker.show(getSupportFragmentManager(), datePicker.toString());
+ }
+
+ private void showDateTimePicker(EditText target) {
+ final Calendar calendar = Calendar.getInstance();
+
+ DatePickerDialog datePickerDialog = new DatePickerDialog(
+ this,
+ (view, year, month, dayOfMonth) -> {
+ calendar.set(Calendar.YEAR, year);
+ calendar.set(Calendar.MONTH, month);
+ calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+
+ TimePickerDialog timePickerDialog = new TimePickerDialog(
+ this,
+ (timeView, hourOfDay, minute) -> {
+ calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
+ calendar.set(Calendar.MINUTE, minute);
+
+ SimpleDateFormat sdf = new SimpleDateFormat(
+ "yyyy-MM-dd HH:mm", Locale.getDefault());
+ String formatted = sdf.format(calendar.getTime());
+ target.setText(formatted);
+ },
+ calendar.get(Calendar.HOUR_OF_DAY),
+ calendar.get(Calendar.MINUTE),
+ true
+ );
+
+ timePickerDialog.show();
+ },
+ calendar.get(Calendar.YEAR),
+ calendar.get(Calendar.MONTH),
+ calendar.get(Calendar.DAY_OF_MONTH)
+ );
+
+ datePickerDialog.show();
+ }
+
+ public static void dispatchToEventActivity(Context ctx, String eventId) {
+ ctx.startActivity(createEventActivityIntent(ctx, eventId));
+ }
+
+ public static Intent createEventActivityIntent(Context ctx, String eventId) {
+ return new Intent(ctx, EventActivity.class)
+ .putExtra(EventActivity.EVENT_ID_KEY, eventId);
+ }
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventLocationMapActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventLocationMapActivity.java
new file mode 100644
index 0000000..c83cc54
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventLocationMapActivity.java
@@ -0,0 +1,235 @@
+package com.tom.meeter.context.event.activity;
+
+import static com.tom.meeter.context.profile.fragment.GoogleMapsFragment.ZOOM_VALUE;
+import static com.tom.meeter.infrastructure.common.ImagesHelper.circleImage;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.showMessage;
+
+import android.accounts.AccountManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+import android.widget.FrameLayout;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.UiSettings;
+import com.google.android.gms.maps.model.BitmapDescriptorFactory;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.Marker;
+import com.google.android.gms.maps.model.MarkerOptions;
+import com.tom.meeter.App;
+import com.tom.meeter.R;
+import com.tom.meeter.context.auth.infrastructure.AuthHelper;
+import com.tom.meeter.context.event.service.EventService;
+import com.tom.meeter.context.gps.domain.LocationTrackerListener;
+import com.tom.meeter.context.gps.service.LocationTrackerService;
+import com.tom.meeter.context.image.ImageDownloader;
+import com.tom.meeter.context.network.dto.EventDTO;
+import com.tom.meeter.databinding.ActivityEventPositionBinding;
+import com.tom.meeter.infrastructure.common.Globals;
+import com.tom.meeter.infrastructure.http.ErrorLogger;
+import com.tom.meeter.infrastructure.http.HttpCodes;
+
+import javax.inject.Inject;
+
+import retrofit2.Call;
+import retrofit2.Response;
+
+public class EventLocationMapActivity extends AppCompatActivity
+ implements OnMapReadyCallback {
+
+ private static final String TAG = EventLocationMapActivity.class.getCanonicalName();
+ private static final String EVENT_ID_KEY = "event_id";
+ private Marker eventMarker;
+ private GoogleMap gmap;
+ private ActivityEventPositionBinding binding;
+
+ private ServiceConnection locationServiceConn;
+ private LocationTrackerService locationService;
+ private boolean cameraMoved = false;
+
+ private LocationTrackerListener singleLocationUpdateListener;
+ private String eventId;
+
+ @Inject
+ EventService eventService;
+ @Inject
+ ImageDownloader imageDownloader;
+ private AccountManager accountManager;
+ private LatLng userLocation;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle extras = getIntent().getExtras();
+ if (extras == null) {
+ showMessage(this, "Unable to show map without extras provided.");
+ finish();
+ }
+ eventId = extras.getString(EVENT_ID_KEY);
+ if (eventId == null) {
+ showMessage(this, "Unable to show map without event_id provided.");
+ finish();
+ }
+
+ binding = ActivityEventPositionBinding.inflate(getLayoutInflater());
+ FrameLayout view = binding.getRoot();
+ setContentView(view);
+
+ ((App) getApplication()).getEventComponent().inject(this);
+ accountManager = AccountManager.get(this);
+
+ SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.eventSelectPosition);
+ if (mapFragment != null) {
+ mapFragment.getMapAsync(this);
+ }
+ binding.btnConfirm.setOnClickListener(v -> {
+ if (eventMarker != null) {
+ Intent resultIntent = new Intent();
+ LatLng position = eventMarker.getPosition();
+ resultIntent.putExtra(EventActivity.EXTRA_LAT, position.latitude);
+ resultIntent.putExtra(EventActivity.EXTRA_LNG, position.longitude);
+ setResult(RESULT_OK, resultIntent);
+ finish();
+ } else {
+ Toast.makeText(this, "Выберите точку на карте", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+
+ private void setupSingleLocationListener() {
+ singleLocationUpdateListener = new LocationTrackerListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ logMethod(TAG, this);
+ if (gmap != null && !cameraMoved) {
+ userLocation = new LatLng(location.getLatitude(), location.getLongitude());
+ gmap.moveCamera(CameraUpdateFactory.newLatLngZoom(userLocation, ZOOM_VALUE));
+ cameraMoved = true;
+ locationService.removeLocationTrackerListener(this);
+ singleLocationUpdateListener = null;
+ unbindService(locationServiceConn);
+ locationService = null;
+ locationServiceConn = null;
+ }
+ }
+ };
+ locationServiceConn = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ logMethod(TAG, this);
+ locationService = ((LocationTrackerService.ServiceBinder) binder).getService();
+ locationService.addLocationTrackerListener(singleLocationUpdateListener);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ logMethod(TAG, this);
+ locationService = null;
+ locationServiceConn = null;
+ }
+ };
+ Intent service = new Intent(this, LocationTrackerService.class);
+ bindService(service, locationServiceConn, BIND_AUTO_CREATE);
+ }
+
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ gmap = googleMap;
+ UiSettings uiSettings = gmap.getUiSettings();
+ uiSettings.setZoomControlsEnabled(true);
+
+ String token = AuthHelper.peekToken(accountManager);
+ String authHeader = Globals.getAuthHeader(token);
+ eventService.getEvent(authHeader, eventId).enqueue(new ErrorLogger<>(this) {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.code() == HttpCodes.OK) {
+ EventDTO event = response.body();
+
+ gmap.setOnMapClickListener(
+ latLng -> {
+ if (eventMarker != null) {
+ eventMarker.setPosition(latLng);
+ return;
+ }
+ if (userLocation != null) {
+ eventMarker = createEventMarker(latLng, event);
+ downloadEventImage(event);
+ }
+ Log.d(TAG, "Event marker is null, user location is null, nothing to do...");
+ });
+
+ if (event.getLatitude() == null || event.getLongitude() == null) {
+ setupSingleLocationListener();
+ return;
+ }
+ LatLng eventLatLng = new LatLng(event.getLatitude(), event.getLongitude());
+ eventMarker = createEventMarker(eventLatLng, event);
+ gmap.moveCamera(CameraUpdateFactory.newLatLngZoom(eventLatLng, ZOOM_VALUE));
+ downloadEventImage(event);
+ return;
+ }
+ if (response.code() == HttpCodes.NOT_AUTHENTICATED) {
+ EventLocationMapActivity.this.recreate();
+ }
+ Log.i(TAG, "/event/{id}: " + response.code() + " : " + response.body());
+ }
+ });
+ }
+
+ @Nullable
+ private Marker createEventMarker(LatLng latLng, EventDTO event) {
+ return gmap.addMarker(
+ new MarkerOptions()
+ .position(latLng)
+ .title(event.getName()));
+ }
+
+ private void downloadEventImage(EventDTO event) {
+ assert eventMarker != null;
+ String photoPath = event.getPhotoPath();
+ if (photoPath != null) {
+ imageDownloader.downloadEventImage(
+ photoPath,
+ EventLocationMapActivity.this.getApplicationContext(),
+ (photo) -> eventMarker.setIcon(BitmapDescriptorFactory.fromBitmap(circleImage(photo))),
+ EventLocationMapActivity.this::recreate);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ logMethod(TAG, this);
+ if (locationService != null && singleLocationUpdateListener != null) {
+ locationService.removeLocationTrackerListener(singleLocationUpdateListener);
+ }
+ if (locationServiceConn != null) {
+ unbindService(locationServiceConn);
+ }
+ }
+
+ public static void dispatchToEventLocationMapActivity(
+ Context ctx, String eventId) {
+ ctx.startActivity(createEventLocationMapActivityIntent(ctx, eventId));
+ }
+
+ public static Intent createEventLocationMapActivityIntent(Context ctx, String eventId) {
+ Intent result = new Intent(ctx, EventLocationMapActivity.class);
+ result.putExtra(EVENT_ID_KEY, eventId);
+ return result;
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventOnMapActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventOnMapActivity.java
new file mode 100644
index 0000000..b253c25
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/EventOnMapActivity.java
@@ -0,0 +1,157 @@
+package com.tom.meeter.context.event.activity;
+
+import static com.tom.meeter.context.profile.fragment.GoogleMapsFragment.ZOOM_VALUE;
+import static com.tom.meeter.infrastructure.common.ImagesHelper.circleImage;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.showMessage;
+
+import android.accounts.AccountManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.FrameLayout;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.UiSettings;
+import com.google.android.gms.maps.model.BitmapDescriptorFactory;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.MarkerOptions;
+import com.tom.meeter.App;
+import com.tom.meeter.R;
+import com.tom.meeter.context.auth.infrastructure.AuthHelper;
+import com.tom.meeter.context.event.service.EventService;
+import com.tom.meeter.context.image.ImageDownloader;
+import com.tom.meeter.context.network.dto.EventDTO;
+import com.tom.meeter.databinding.ActivityEventOnMapBinding;
+import com.tom.meeter.infrastructure.common.Globals;
+import com.tom.meeter.infrastructure.http.ErrorLogger;
+import com.tom.meeter.infrastructure.http.HttpCodes;
+
+import javax.inject.Inject;
+
+import retrofit2.Call;
+import retrofit2.Response;
+
+public class EventOnMapActivity extends AppCompatActivity
+ implements OnMapReadyCallback {
+
+ private static final String TAG = EventOnMapActivity.class.getCanonicalName();
+ private static final String EVENT_ID_KEY = "event_id";
+ private GoogleMap gmap;
+ private ActivityEventOnMapBinding binding;
+
+ @Inject
+ EventService eventService;
+ @Inject
+ ImageDownloader imageDownloader;
+
+ private AccountManager accountManager;
+ private String eventId;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle extras = getIntent().getExtras();
+ if (extras == null) {
+ showMessage(this, "Unable to show map without extras provided.");
+ finish();
+ }
+ eventId = extras.getString(EVENT_ID_KEY);
+ if (eventId == null) {
+ showMessage(this, "Unable to show map without event_id provided.");
+ finish();
+ }
+
+ binding = ActivityEventOnMapBinding.inflate(getLayoutInflater());
+ FrameLayout view = binding.getRoot();
+ setContentView(view);
+
+ ((App) getApplication()).getEventComponent().inject(this);
+ accountManager = AccountManager.get(this);
+
+
+ SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
+ .findFragmentById(R.id.eventOnMap);
+ if (mapFragment != null) {
+ mapFragment.getMapAsync(this);
+ }
+ }
+
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ gmap = googleMap;
+ UiSettings uiSettings = gmap.getUiSettings();
+ uiSettings.setZoomControlsEnabled(true);
+
+ String token = AuthHelper.peekToken(accountManager);
+ String authHeader = Globals.getAuthHeader(token);
+ eventService.getEvent(authHeader, eventId).enqueue(new ErrorLogger<>(this) {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.code() == HttpCodes.OK) {
+ EventDTO event = response.body();
+ Double latitude = event.getLatitude();
+ Double longitude = event.getLongitude();
+ if (latitude == null || longitude == null) {
+ showMessage(EventOnMapActivity.this, "Event location is not set yet.");
+ return;
+ }
+ LatLng eventLatLng = new LatLng(latitude, longitude);
+ String photoPath = event.getPhotoPath();
+ if (photoPath == null) {
+ gmap.addMarker(
+ new MarkerOptions()
+ .position(eventLatLng)
+ .title(event.getName()));
+ gmap.moveCamera(CameraUpdateFactory.newLatLngZoom(eventLatLng, ZOOM_VALUE));
+ } else {
+ imageDownloader.downloadEventImage(
+ photoPath,
+ EventOnMapActivity.this.getApplicationContext(),
+ (photo) -> {
+ gmap.addMarker(
+ new MarkerOptions()
+ .position(eventLatLng)
+ .icon(BitmapDescriptorFactory.fromBitmap(circleImage(photo)))
+ .title(event.getName()));
+ gmap.moveCamera(CameraUpdateFactory.newLatLngZoom(eventLatLng, ZOOM_VALUE));
+ },
+ EventOnMapActivity.this::recreate);
+ }
+ return;
+ }
+ if (response.code() == HttpCodes.NOT_AUTHENTICATED) {
+ EventOnMapActivity.this.recreate();
+ }
+ Log.i(TAG, "/event/{id}: " + response.code() + " : " + response.body());
+ }
+ });
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ logMethod(TAG, this);
+ }
+
+ public static void dispatchToEventOnMapActivity(Context ctx, String eventId) {
+ if (ctx == null || eventId == null) {
+ throw new IllegalArgumentException("ctx and event_id should present");
+ }
+ ctx.startActivity(createEventOnMapActivityIntent(ctx, eventId));
+ }
+
+ public static Intent createEventOnMapActivityIntent(
+ Context ctx, String eventId) {
+ Intent result = new Intent(ctx, EventOnMapActivity.class);
+ result.putExtra(EVENT_ID_KEY, eventId);
+ return result;
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/message/UpdateEventRequest.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/message/UpdateEventRequest.java
new file mode 100644
index 0000000..bf6535c
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/message/UpdateEventRequest.java
@@ -0,0 +1,154 @@
+package com.tom.meeter.context.event.message;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.time.OffsetDateTime;
+import java.util.Optional;
+
+public class UpdateEventRequest {
+ private final String PHOTO_PATH_KEY = "photo_path";
+
+ private Optional name;
+ private Optional description;
+ private Optional starting;
+ private Optional ending;
+ private Optional city;
+ private Optional latitude;
+ private Optional longitude;
+ @JsonProperty(value = PHOTO_PATH_KEY)
+ private Optional photoPath;
+
+ public UpdateEventRequest() {
+ }
+
+ public Optional getName() {
+ return name;
+ }
+
+ public Optional getDescription() {
+ return description;
+ }
+
+ public Optional getStarting() {
+ return starting;
+ }
+
+ public Optional getEnding() {
+ return ending;
+ }
+
+ public Optional getCity() {
+ return city;
+ }
+
+ public Optional getLatitude() {
+ return latitude;
+ }
+
+ public Optional getLongitude() {
+ return longitude;
+ }
+
+ public Optional getPhotoPath() {
+ return photoPath;
+ }
+
+ public void setName(String name) {
+ this.name = Optional.ofNullable(name);
+ }
+
+ public void setDescription(String description) {
+ this.description = Optional.ofNullable(description);
+ }
+
+ public void setStarting(OffsetDateTime starting) {
+ this.starting = Optional.ofNullable(starting);
+ }
+
+ public void setEnding(OffsetDateTime ending) {
+ this.ending = Optional.ofNullable(ending);
+ }
+
+ public void setCity(String city) {
+ this.city = Optional.ofNullable(city);
+ }
+
+ public void setLatitude(Float latitude) {
+ this.latitude = Optional.ofNullable(latitude);
+ }
+
+ public void setLongitude(Float longitude) {
+ this.longitude = Optional.ofNullable(longitude);
+ }
+
+ public void setPhotoPath(String photoPath) {
+ this.photoPath = Optional.ofNullable(photoPath);
+ }
+
+ public boolean isEmpty() {
+ return name == null && description == null && starting == null && ending == null
+ && city == null && latitude == null && longitude == null && photoPath == null;
+ }
+
+/*
+
+
+ private final String NAME_KEY = "name";
+ private final String DESCR_KEY = "description";
+ private final String STARTING_KEY = "starting";
+ private final String ENDING_KEY = "ending";
+ private final String CITY_KEY = "city";
+ private final String LATITUDE_KEY = "latitude";
+ private final String LONGITUDE_KEY = "longitude";
+
+
+ public Map toUpdateMap() {
+ Map result = new HashMap<>();
+ putIfNotNull(name, NAME_KEY, result);
+ putIfNotNull(description, DESCR_KEY, result);
+ putIfNotNull(starting, STARTING_KEY, result);
+ putIfNotNull(ending, ENDING_KEY, result);
+ putIfNotNull(city, CITY_KEY, result);
+ putIfNotNull(latitude, LATITUDE_KEY, result);
+ putIfNotNull(longitude, LONGITUDE_KEY, result);
+ putIfNotNull(photoPath, PHOTO_PATH_KEY, result);
+ return result;
+ }
+
+ public JSONObject toJson() {
+ JSONObject result = new JSONObject();
+ putIfNotNull(name, NAME_KEY, result);
+ putIfNotNull(description, DESCR_KEY, result);
+ putIfNotNull(starting, STARTING_KEY, result);
+ putIfNotNull(ending, ENDING_KEY, result);
+ putIfNotNull(city, CITY_KEY, result);
+ putIfNotNull(latitude, LATITUDE_KEY, result);
+ putIfNotNull(longitude, LONGITUDE_KEY, result);
+ putIfNotNull(photoPath, PHOTO_PATH_KEY, result);
+ return result;
+ }
+
+ private void putIfNotNull(Optional> field, String key, Map result) {
+ if (field != null) {
+ result.put(key, field.orElse(null));
+ }
+ }
+
+ private void putIfNotNull(Optional> field, String key, JSONObject result) {
+ if (field != null) {
+ if (field.isPresent()) {
+ try {
+ result.put(key, field.get());
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ try {
+ result.put(key, JSONObject.NULL);
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }*/
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/service/EventService.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/service/EventService.java
index 993dc69..e8b107e 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/event/service/EventService.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/service/EventService.java
@@ -2,14 +2,23 @@
import static com.tom.meeter.infrastructure.common.Globals.AUTH_HEADER;
+import com.tom.meeter.context.event.message.UpdateEventRequest;
import com.tom.meeter.context.network.dto.EventDTO;
import retrofit2.Call;
+import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Header;
+import retrofit2.http.PATCH;
import retrofit2.http.Path;
public interface EventService {
+
@GET("/event/{id}")
Call getEvent(@Header(AUTH_HEADER) String authHeader, @Path("id") String eventId);
+
+ @PATCH("/event/{id}")
+ Call updateEvent(
+ @Header(AUTH_HEADER) String authHeader, @Path("id") String eventId,
+ @Body UpdateEventRequest req);
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/utils/Utils.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/utils/Utils.java
new file mode 100644
index 0000000..7f57b4c
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/utils/Utils.java
@@ -0,0 +1,55 @@
+package com.tom.meeter.context.event.utils;
+
+import static com.tom.meeter.infrastructure.common.CommonHelper.getFloatOrNull;
+import static com.tom.meeter.infrastructure.common.CommonHelper.getOffsetDateTime;
+import static com.tom.meeter.infrastructure.common.CommonHelper.getStringOrNull;
+
+import com.tom.meeter.context.event.message.UpdateEventRequest;
+import com.tom.meeter.context.network.dto.EventDTO;
+import com.tom.meeter.databinding.ActivityEventEditableBinding;
+
+import java.time.OffsetDateTime;
+import java.util.Objects;
+
+public class Utils {
+ private Utils() {
+ }
+
+ public static UpdateEventRequest createUpdateEventRequest(
+ EventDTO event, ActivityEventEditableBinding eBinding) {
+ UpdateEventRequest req = new UpdateEventRequest();
+
+ String eventNameChange = getStringOrNull(eBinding.eventName.getText());
+ if (!Objects.equals(event.getName(), eventNameChange)) {
+ req.setName(eventNameChange);
+ }
+ String eventDescrChange = getStringOrNull(eBinding.eventDescription.getText());
+ if (!Objects.equals(event.getDescription(), eventDescrChange)) {
+ req.setDescription(eventDescrChange);
+ }
+ OffsetDateTime eventStartingChange = getOffsetDateTime(eBinding.eventStarting.getText());
+ if (!Objects.equals(event.getStarting(), eventStartingChange)) {
+ req.setStarting(eventStartingChange);
+ }
+ OffsetDateTime eventEndingChange = getOffsetDateTime(eBinding.eventEnding.getText());
+ if (!Objects.equals(event.getEnding(), eventEndingChange)) {
+ req.setEnding(eventEndingChange);
+ }
+ String eventCityChange = getStringOrNull(eBinding.eventCity.getText());
+ if (!Objects.equals(event.getCity(), eventCityChange)) {
+ req.setCity(eventCityChange);
+ }
+ //TODO FLOAT -> DOUBLE
+ Float eventLatitudeChange = getFloatOrNull(eBinding.eventLatitude.getText());
+ if (!Objects.equals(event.getLatitude(), eventLatitudeChange)) {
+ req.setLatitude(eventLatitudeChange);
+ }
+ //TODO FLOAT -> DOUBLE
+ Float eventLongitudeChange = getFloatOrNull(eBinding.eventLongitude.getText());
+ if (!Objects.equals(event.getLongitude(), eventLongitudeChange)) {
+ req.setLongitude(eventLongitudeChange);
+ }
+ //TODO: eventCache.getPhotoPath();
+ return req;
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/viewmodel/EventViewModel.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/viewmodel/EventViewModel.java
index 3d8546a..c403dc6 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/event/viewmodel/EventViewModel.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/viewmodel/EventViewModel.java
@@ -10,14 +10,16 @@
import androidx.lifecycle.ViewModel;
import com.tom.meeter.context.event.service.EventService;
+import com.tom.meeter.context.image.ImageDownloader;
import com.tom.meeter.context.network.dto.EventDTO;
import com.tom.meeter.context.user.viewmodel.UserViewModel;
import com.tom.meeter.infrastructure.common.Globals;
-import com.tom.meeter.infrastructure.http.DisconnectLogger;
+import com.tom.meeter.infrastructure.http.ErrorLogger;
import com.tom.meeter.infrastructure.http.HttpCodes;
import javax.inject.Inject;
+import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Response;
@@ -26,28 +28,40 @@ public class EventViewModel extends ViewModel {
private static final String TAG = UserViewModel.class.getCanonicalName();
private final MutableLiveData eventLiveData = new MutableLiveData<>();
+ private final MutableLiveData eventPhotoLiveData = new MutableLiveData<>();
private final EventService eventService;
+ private final ImageDownloader imageDownloader;
@Inject
- public EventViewModel(EventService eventService) {
+ public EventViewModel(EventService eventService, ImageDownloader imageDownloader) {
logMethod(TAG, this);
this.eventService = eventService;
+ this.imageDownloader = imageDownloader;
}
public void fetchEventInformation(String token, String eventId, Activity activity) {
eventService.getEvent(Globals.getAuthHeader(token), eventId).enqueue(
- new DisconnectLogger<>(activity) {
+ new ErrorLogger<>(activity) {
@Override
public void onResponse(Call call, Response response) {
- if (response.code() == HttpCodes.OK && response.body() != null) {
- eventLiveData.setValue(response.body());
+ EventDTO body = response.body();
+ if (response.code() == HttpCodes.OK && body != null) {
+ eventLiveData.setValue(body);
+ String photoPath = body.getPhotoPath();
+ if (photoPath != null) {
+ imageDownloader.downloadEventImage(
+ photoPath,
+ activity.getApplicationContext(),
+ eventPhotoLiveData::setValue,
+ activity::recreate);
+ }
return;
}
if (response.code() == HttpCodes.NOT_AUTHENTICATED) {
activity.recreate();
}
- Log.i(TAG, "/event/{id}: " + response.code() + " : " + response.body());
+ Log.i(TAG, "/event/{id}: " + response.code() + " : " + body);
}
}
);
@@ -62,5 +76,9 @@ protected void onCleared() {
public LiveData getEventLiveData() {
return eventLiveData;
}
+
+ public LiveData getEventPhotoLiveData() {
+ return eventPhotoLiveData;
+ }
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/viewmodel/EventViewModelModule.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/viewmodel/EventViewModelModule.java
new file mode 100644
index 0000000..d79434b
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/viewmodel/EventViewModelModule.java
@@ -0,0 +1,17 @@
+package com.tom.meeter.context.event.viewmodel;
+
+import androidx.lifecycle.ViewModel;
+
+import com.tom.meeter.infrastructure.injection.viewmodel.ViewModelKey;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.IntoMap;
+
+@Module
+public abstract class EventViewModelModule {
+ @Binds
+ @IntoMap
+ @ViewModelKey(EventViewModel.class)
+ abstract ViewModel eventViewModel(EventViewModel eventViewModel);
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/image/ImageComponent.java b/AndroidClient/src/main/java/com/tom/meeter/context/image/ImageComponent.java
new file mode 100644
index 0000000..c4e103f
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/image/ImageComponent.java
@@ -0,0 +1,23 @@
+package com.tom.meeter.context.image;
+
+import android.app.Application;
+
+import javax.inject.Singleton;
+
+import dagger.BindsInstance;
+import dagger.Component;
+
+@Singleton
+@Component(modules = {ImageModule.class})
+public interface ImageComponent {
+
+ ImageDownloader provideImageDownloader();
+
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ Builder application(Application application);
+
+ ImageComponent build();
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/image/ImageDownloader.java b/AndroidClient/src/main/java/com/tom/meeter/context/image/ImageDownloader.java
new file mode 100644
index 0000000..d143032
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/image/ImageDownloader.java
@@ -0,0 +1,75 @@
+package com.tom.meeter.context.image;
+
+import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+
+import android.accounts.AccountManager;
+import android.content.Context;
+import android.util.Log;
+
+import com.tom.meeter.context.image.service.ImageService;
+import com.tom.meeter.infrastructure.http.ErrorLogger;
+import com.tom.meeter.infrastructure.http.HttpCodes;
+
+import java.util.function.Consumer;
+
+import javax.inject.Inject;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Response;
+
+public class ImageDownloader {
+
+ private static final String TAG = ImageDownloader.class.getCanonicalName();
+ private final ImageService imageService;
+
+ @Inject
+ public ImageDownloader(ImageService imageService) {
+ logMethod(TAG, this);
+ this.imageService = imageService;
+ }
+
+ public void downloadEventImage(
+ String photoPath, Context ctx,
+ Consumer onDownloaded, Runnable onNotAuthenticated) {
+ imageService.downloadEventImage(getAuthHeader(AccountManager.get(ctx)), photoPath)
+ .enqueue(new ErrorLogger<>(ctx) {
+ @Override
+ public void onResponse(Call call, Response response) {
+ //Log.d(TAG, "/images/event" + photoPath + " downloaded...");
+ try (ResponseBody body = response.body()) {
+ if (response.code() == HttpCodes.OK && body != null) {
+ onDownloaded.accept(response.body());
+ return;
+ }
+ if (response.code() == HttpCodes.NOT_AUTHENTICATED) {
+ onNotAuthenticated.run();
+ }
+ Log.i(TAG, "/images/event/: " + response.code() + " : " + body);
+ }
+ }
+ });
+ }
+
+ public void downloadUserImage(
+ String photoPath, Context ctx,
+ Consumer onDownloaded, Runnable onNotAuthenticated) {
+ imageService.downloadUserImage(getAuthHeader(AccountManager.get(ctx)), photoPath)
+ .enqueue(new ErrorLogger<>(ctx) {
+ @Override
+ public void onResponse(Call call, Response response) {
+ try (ResponseBody body = response.body()) {
+ if (response.code() == HttpCodes.OK && body != null) {
+ onDownloaded.accept(response.body());
+ return;
+ }
+ if (response.code() == HttpCodes.NOT_AUTHENTICATED) {
+ onNotAuthenticated.run();
+ }
+ Log.i(TAG, "/images/user/: " + response.code() + " : " + body);
+ }
+ }
+ });
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/image/ImageModule.java b/AndroidClient/src/main/java/com/tom/meeter/context/image/ImageModule.java
new file mode 100644
index 0000000..54f7b68
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/image/ImageModule.java
@@ -0,0 +1,43 @@
+package com.tom.meeter.context.image;
+
+import static com.tom.meeter.infrastructure.common.Globals.getServerPath;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+
+import android.app.Application;
+
+import androidx.annotation.NonNull;
+
+import com.tom.meeter.context.image.service.ImageService;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+import retrofit2.Retrofit;
+
+@Module
+public class ImageModule {
+
+ private static final String TAG = ImageModule.class.getCanonicalName();
+
+ public ImageModule() {
+ logMethod(TAG, this);
+ }
+
+ @Singleton
+ @NonNull
+ @Provides
+ public ImageService provideImageService(Application app) {
+ return new Retrofit.Builder()
+ .baseUrl(getServerPath(app))
+ .build()
+ .create(ImageService.class);
+ }
+
+ @Singleton
+ @NonNull
+ @Provides
+ public ImageDownloader provideImageDownloader(ImageService imageService) {
+ return new ImageDownloader(imageService);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/image/service/ImageService.java b/AndroidClient/src/main/java/com/tom/meeter/context/image/service/ImageService.java
new file mode 100644
index 0000000..03964a3
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/image/service/ImageService.java
@@ -0,0 +1,22 @@
+package com.tom.meeter.context.image.service;
+
+import static com.tom.meeter.infrastructure.common.Globals.AUTH_HEADER;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.GET;
+import retrofit2.http.Header;
+import retrofit2.http.Path;
+
+public interface ImageService {
+
+ @GET("/images/event{imagePath}")
+ Call downloadEventImage(
+ @Header(AUTH_HEADER) String authHeader,
+ @Path(value = "imagePath", encoded = true) String imagePath);
+
+ @GET("/images/user{imagePath}")
+ Call downloadUserImage(
+ @Header(AUTH_HEADER) String authHeader,
+ @Path(value = "imagePath", encoded = true) String imagePath);
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/launcher/Launcher.java b/AndroidClient/src/main/java/com/tom/meeter/context/launcher/Launcher.java
index 2167c48..b437678 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/launcher/Launcher.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/launcher/Launcher.java
@@ -24,8 +24,8 @@
import com.tom.meeter.App;
import com.tom.meeter.R;
-import com.tom.meeter.context.token.service.TokenService;
import com.tom.meeter.context.profile.activity.ProfileActivity;
+import com.tom.meeter.context.token.service.TokenService;
import com.tom.meeter.databinding.LauncherBinding;
import java.io.IOException;
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/EventDTO.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/EventDTO.java
index 0813221..65cb544 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/EventDTO.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/EventDTO.java
@@ -1,16 +1,18 @@
package com.tom.meeter.context.network.dto;
-import com.google.gson.annotations.SerializedName;
+import androidx.annotation.Nullable;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
import org.json.JSONException;
import org.json.JSONObject;
+import java.time.OffsetDateTime;
import java.util.Objects;
/**
* created by Tom on 10.02.2017.
*/
-
public class EventDTO {
private static final String EVENT_ID_KEY = "id";
@@ -22,130 +24,171 @@ public class EventDTO {
private static final String CREATED_KEY = "created";
private static final String STARTING_KEY = "starting";
private static final String ENDING_KEY = "ending";
+ private static final String PHOTO_PATH_KEY = "photo_path";
+ private static final String CITY_KEY = "city";
private String id;
private String name;
private String description;
- private double latitude;
- private double longitude;
- @SerializedName(value = CREATOR_ID_KEY)
+ private Double latitude;
+ private Double longitude;
+ @JsonProperty(value = CREATOR_ID_KEY)
private String creatorId;
- private String created;
- private String starting;
- private String ending;
-
- public EventDTO() {
- }
+ private OffsetDateTime created;
+ private OffsetDateTime starting;
+ private OffsetDateTime ending;
+ private String city;
+ @JsonProperty(value = PHOTO_PATH_KEY)
+ private String photoPath;
public static EventDTO encode(JSONObject json) {
EventDTO result = new EventDTO();
try {
+ //Non nullable.
result.id = json.getString(EVENT_ID_KEY);
result.name = json.getString(NAME_KEY);
- result.description = json.getString(DESCRIPTION_KEY);
result.creatorId = json.getString(CREATOR_ID_KEY);
- result.latitude = json.getDouble(LATITUDE_KEY);
- result.longitude = json.getDouble(LONGITUDE_KEY);
- result.created = json.getString(CREATED_KEY);
- result.starting = json.getString(STARTING_KEY);
- result.ending = json.getString(ENDING_KEY);
+ result.created = OffsetDateTime.parse(json.getString(CREATED_KEY));
+
+ //Nullable.
+ result.description = getStringOrNull(DESCRIPTION_KEY, json);
+ result.latitude = getDoubleOrNull(LATITUDE_KEY, json);
+ result.longitude = getDoubleOrNull(LONGITUDE_KEY, json);
+ result.starting = getOffsetDateTimeOrNull(STARTING_KEY, json);
+ result.ending = getOffsetDateTimeOrNull(ENDING_KEY, json);
+ result.photoPath = getStringOrNull(PHOTO_PATH_KEY, json);
+ result.city = getStringOrNull(CITY_KEY, json);
} catch (JSONException e) {
- throw new RuntimeException("Unable to encode EventDTO from jsonObject, ", e);
+ throw new RuntimeException("Unable to encode EventDTO from jsonObject: ", e);
}
return result;
}
- public String getId() {
- return id;
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setLatitude(Double latitude) {
+ this.latitude = latitude;
+ }
+
+ public void setLongitude(Double longitude) {
+ this.longitude = longitude;
}
public void setId(String id) {
this.id = id;
}
- public String getName() {
- return name;
+ public void setDescription(String description) {
+ this.description = description;
}
- public void setName(String name) {
- this.name = name;
+ public void setCreatorId(String creatorId) {
+ this.creatorId = creatorId;
}
- public String getDescription() {
- return description;
+ public void setCreated(OffsetDateTime created) {
+ this.created = created;
}
- public void setDescription(String description) {
- this.description = description;
+ public void setStarting(OffsetDateTime starting) {
+ this.starting = starting;
}
- public double getLatitude() {
- return latitude;
+ public void setEnding(OffsetDateTime ending) {
+ this.ending = ending;
}
- public void setLatitude(double latitude) {
- this.latitude = latitude;
+ public void setCity(String city) {
+ this.city = city;
}
- public double getLongitude() {
- return longitude;
+ public void setPhotoPath(String photoPath) {
+ this.photoPath = photoPath;
}
- public void setLongitude(double longitude) {
- this.longitude = longitude;
+ public String getId() {
+ return id;
}
- public String getCreatorId() {
- return creatorId;
+ public String getName() {
+ return name;
}
- public void setCreatorId(String creatorId) {
- this.creatorId = creatorId;
+ public String getDescription() {
+ return description;
}
- public String getCreated() {
- return created;
+ public Double getLatitude() {
+ return latitude;
}
- public void setCreated(String created) {
- this.created = created;
+ public Double getLongitude() {
+ return longitude;
}
- public String getStarting() {
- return starting;
+ public String getCreatorId() {
+ return creatorId;
}
- public void setStarting(String starting) {
- this.starting = starting;
+ public OffsetDateTime getCreated() {
+ return created;
+ }
+
+ public OffsetDateTime getStarting() {
+ return starting;
}
- public String getEnding() {
+ public OffsetDateTime getEnding() {
return ending;
}
- public void setEnding(String ending) {
- this.ending = ending;
+ public String getPhotoPath() {
+ return photoPath;
+ }
+
+ public String getCity() {
+ return city;
}
@Override
public boolean equals(Object o) {
- if (!(o instanceof EventDTO eventDTO)) {
- return false;
- }
- return Double.compare(latitude, eventDTO.latitude) == 0
- && Double.compare(longitude, eventDTO.longitude) == 0
- && Objects.equals(id, eventDTO.id)
+ if (o == null || getClass() != o.getClass()) return false;
+ EventDTO eventDTO = (EventDTO) o;
+ return Objects.equals(id, eventDTO.id)
&& Objects.equals(name, eventDTO.name)
&& Objects.equals(description, eventDTO.description)
+ && Objects.equals(latitude, eventDTO.latitude)
+ && Objects.equals(longitude, eventDTO.longitude)
&& Objects.equals(creatorId, eventDTO.creatorId)
&& Objects.equals(created, eventDTO.created)
&& Objects.equals(starting, eventDTO.starting)
- && Objects.equals(ending, eventDTO.ending);
+ && Objects.equals(ending, eventDTO.ending)
+ && Objects.equals(city, eventDTO.city)
+ && Objects.equals(photoPath, eventDTO.photoPath);
}
@Override
public int hashCode() {
- return Objects.hash(id, name, description, latitude, longitude,
- creatorId, created, starting, ending);
+ return Objects.hash(
+ id, name, description, latitude, longitude, creatorId,
+ created, starting, ending, city, photoPath);
+ }
+
+ @Nullable
+ private static String getStringOrNull(String key, JSONObject json) throws JSONException {
+ return json.isNull(key) ? null : json.getString(key);
+ }
+
+ @Nullable
+ private static OffsetDateTime getOffsetDateTimeOrNull(
+ String key, JSONObject json) throws JSONException {
+ return json.isNull(key) ? null : OffsetDateTime.parse(json.getString(key));
+ }
+
+ @Nullable
+ private static Double getDoubleOrNull(String key, JSONObject json) throws JSONException {
+ return json.isNull(key) ? null : json.getDouble(key);
}
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/UserDTO.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/UserDTO.java
new file mode 100644
index 0000000..695772a
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/UserDTO.java
@@ -0,0 +1,6 @@
+package com.tom.meeter.context.network.dto;
+
+
+//TODO: make me
+public class UserDTO {
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/service/SocketIOService.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/service/SocketIOService.java
index 847ec05..e13b3c5 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/network/service/SocketIOService.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/service/SocketIOService.java
@@ -1,6 +1,8 @@
package com.tom.meeter.context.network.service;
import static com.tom.meeter.context.auth.infrastructure.AuthHelper.peekToken;
+import static com.tom.meeter.context.network.utils.SocketIOCodes.EVENT_CREATED_CODE;
+import static com.tom.meeter.context.notification.NotificationHelper.sendNotificationEventCreated;
import static com.tom.meeter.infrastructure.common.Globals.AUTH_HEADER;
import static com.tom.meeter.infrastructure.common.Globals.getSocketIOPath;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
@@ -17,6 +19,7 @@
import com.tom.meeter.context.network.domain.CreateNewEventAttempt;
import com.tom.meeter.context.network.domain.SearchForEvents;
+import com.tom.meeter.context.network.dto.EventDTO;
import com.tom.meeter.infrastructure.common.Globals;
import com.tom.meeter.infrastructure.common.JsonHelper;
import com.tom.meeter.infrastructure.eventbus.events.FailureEventCreation;
@@ -26,6 +29,7 @@
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.json.JSONArray;
+import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
@@ -48,6 +52,7 @@ public class SocketIOService extends Service {
private static final String GREETINGS_CHANNEL = "greetings";
private static final String EVENTS_CREATE_CHANNEL = "events:create";
private static final String EVENTS_SEARCH_CHANNEL = "events:search";
+ private static final String EVENTS_NOTIFICATIONS_CHANNEL = "events:notifications";
private static final String CODE_KEY = "code";
private static final String ID_KEY = "id";
@@ -133,7 +138,7 @@ private void initializeSocketClient(
Log.d(TAG, "SocketIOService is not going to initialize, since it is already initialized.");
return;
}
- String uri = getSocketIOPath(getBaseContext());
+ String uri = getSocketIOPath(getApplicationContext());
Log.d(TAG, "Configuring SocketIOClient for server: " + uri);
socketClient = IO.socket(uri, setupOptions(authToken));
@@ -154,7 +159,7 @@ private void initializeSocketClient(
if (cause instanceof IOException ioException) {
- if (cause instanceof SocketTimeoutException socketTimeoutException) {
+ if (cause instanceof SocketTimeoutException ste) {
Log.i(TAG, "SocketIOService received SocketTimeoutException. Server is unavailable.");
return;
}
@@ -172,6 +177,7 @@ private void initializeSocketClient(
socketClient.on(GREETINGS_CHANNEL, SocketIOService::greetingsHandler);
socketClient.on(EVENTS_SEARCH_CHANNEL, SocketIOService::eventsSearchHandler);
+ socketClient.on(EVENTS_NOTIFICATIONS_CHANNEL, this::eventsNotificationsChannel);
socketClient.on(EVENTS_CREATE_CHANNEL, SocketIOService::eventsCreateHandler);
socketClient.connect();
EventBus.getDefault().register(this);
@@ -204,6 +210,7 @@ private void disconnect() {
socketClient.disconnect();
socketClient.off(GREETINGS_CHANNEL, SocketIOService::greetingsHandler);
socketClient.off(EVENTS_SEARCH_CHANNEL, SocketIOService::eventsSearchHandler);
+ socketClient.off(EVENTS_NOTIFICATIONS_CHANNEL, this::eventsNotificationsChannel);
socketClient.off(EVENTS_CREATE_CHANNEL, SocketIOService::eventsCreateHandler);
initialized = false;
}
@@ -253,6 +260,19 @@ private static void eventsSearchHandler(Object... args) {
EventBus.getDefault().post(IncomeEvents.fromJsonArray(response));
}
+ private void eventsNotificationsChannel(Object... args) {
+ JSONObject response = getSimpleResponse(JSONObject.class, args);
+ Log.d(TAG, EVENTS_NOTIFICATIONS_CHANNEL + " : " + response);
+ try {
+ if (response.getInt("code") == EVENT_CREATED_CODE) {
+ EventDTO event = EventDTO.encode(response.getJSONObject("message"));
+ sendNotificationEventCreated(this, event);
+ }
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private static T getSimpleResponse(Class aClass, Object[] args) {
if (!validateSingleMessageResponse(aClass, args)) {
throw new RuntimeException("Incorrect response for " + aClass + " with response " + Arrays.toString(args));
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/utils/SocketIOCodes.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/utils/SocketIOCodes.java
new file mode 100644
index 0000000..733029f
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/utils/SocketIOCodes.java
@@ -0,0 +1,9 @@
+package com.tom.meeter.context.network.utils;
+
+public final class SocketIOCodes {
+
+ public static int EVENT_CREATED_CODE = 100;
+
+ private SocketIOCodes() {
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/notification/NotificationHelper.java b/AndroidClient/src/main/java/com/tom/meeter/context/notification/NotificationHelper.java
new file mode 100644
index 0000000..def249c
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/notification/NotificationHelper.java
@@ -0,0 +1,91 @@
+package com.tom.meeter.context.notification;
+
+import android.Manifest;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+import androidx.core.app.ActivityCompat;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
+import com.tom.meeter.R;
+import com.tom.meeter.context.event.activity.EventActivity;
+import com.tom.meeter.context.network.dto.EventDTO;
+
+public class NotificationHelper {
+
+ private static final String EVENTS_NOTIFY = "events_notify";
+ private static final String ALL_EVENTS_GROUP = "com.tom.meeter.all_events_group";
+ private static final int SUMMARY_ID = 9999;
+
+ public static void createNotificationChannel(Context ctx) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ CharSequence name = "New events";
+ String description = "Information about newly created events";
+ int importance = NotificationManager.IMPORTANCE_DEFAULT;
+
+ NotificationChannel channel = new NotificationChannel(EVENTS_NOTIFY, name, importance);
+ channel.setDescription(description);
+
+ NotificationManager notificationManager = ctx.getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(channel);
+ }
+ }
+
+ public static void sendNotificationEventCreated(Context ctx, EventDTO event) {
+ if (ActivityCompat.checkSelfPermission(ctx, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
+ // TODO: Consider calling
+ // ActivityCompat#requestPermissions
+ // here to request the missing permissions, and then overriding
+ // public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ // int[] grantResults)
+ // to handle the case where the user grants the permission. See the documentation
+ // for ActivityCompat#requestPermissions for more details.
+ return;
+ }
+ NotificationManagerCompat mgr = NotificationManagerCompat.from(ctx);
+ mgr.notify(event.getId().hashCode(), getNotification(ctx, event));
+ mgr.notify(SUMMARY_ID, getSummaryNotification(ctx));
+ }
+
+ private static Notification getNotification(Context ctx, EventDTO event) {
+ return new NotificationCompat.Builder(ctx, EVENTS_NOTIFY)
+ .setSmallIcon(R.drawable.ic_meeter_lr)
+ .setContentTitle(ctx.getString(R.string.notification_new_event))
+ //.setContentTitle(event.getCreatorId() + " published new event!")
+ .setContentText(event.getName())
+ .setStyle(getBigStyle(event.getDescription()))
+ .setContentIntent(createPendingIntent(ctx, event))
+ .setGroup(ALL_EVENTS_GROUP)
+ .setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ .setAutoCancel(true)
+ .build();
+ }
+
+ private static Notification getSummaryNotification(Context ctx) {
+ return new NotificationCompat.Builder(ctx, EVENTS_NOTIFY)
+ .setSmallIcon(R.drawable.ic_meeter_lr)
+ .setGroup(ALL_EVENTS_GROUP)
+ .setGroupSummary(true)
+ .build();
+ }
+
+ private static NotificationCompat.BigTextStyle getBigStyle(String descr) {
+ return new NotificationCompat.BigTextStyle().bigText(descr);
+ }
+
+ private static PendingIntent createPendingIntent(Context ctx, EventDTO event) {
+ Intent intent = EventActivity.createEventActivityIntent(ctx, event.getId());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+ return PendingIntent.getActivity(
+ ctx, event.getId().hashCode(), intent,
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/ProfileActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/ProfileActivity.java
index 4a78c3b..42478b6 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/ProfileActivity.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/ProfileActivity.java
@@ -2,6 +2,7 @@
import static androidx.preference.PreferenceManager.getDefaultSharedPreferences;
import static com.tom.meeter.context.auth.infrastructure.AuthHelper.checkToken;
+import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getSingleAccount;
import static com.tom.meeter.context.auth.infrastructure.AuthHelper.invalidateToken;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
@@ -48,19 +49,18 @@
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
import com.tom.meeter.App;
import com.tom.meeter.R;
-import com.tom.meeter.context.auth.infrastructure.AccountAuthenticator;
-import com.tom.meeter.context.token.service.TokenService;
import com.tom.meeter.context.network.service.SocketIOService;
import com.tom.meeter.context.profile.fragment.CreateNewEventFragment;
import com.tom.meeter.context.profile.fragment.EventsFragment;
+import com.tom.meeter.context.profile.fragment.ProfileEventsFragment;
import com.tom.meeter.context.profile.fragment.ProfileFragment;
-import com.tom.meeter.context.profile.fragment.UserEventsFragment;
import com.tom.meeter.context.profile.service.ProfileService;
import com.tom.meeter.context.profile.settings.message.SettingsResponse;
import com.tom.meeter.context.profile.settings.service.SettingsService;
+import com.tom.meeter.context.token.service.TokenService;
import com.tom.meeter.databinding.ProfileActivityBinding;
import com.tom.meeter.infrastructure.common.Globals;
-import com.tom.meeter.infrastructure.http.DisconnectLogger;
+import com.tom.meeter.infrastructure.http.ErrorLogger;
import com.tom.meeter.infrastructure.http.HttpCodes;
import java.util.HashMap;
@@ -200,7 +200,7 @@ private void onInit(String token, boolean isSavedInstanceStateExist) {
private void setupPreferences(String token) {
Call settings = settingsService.getSettings(Globals.getAuthHeader(token));
settings.enqueue(
- new DisconnectLogger<>(this) {
+ new ErrorLogger<>(this) {
@Override
public void onResponse(Call call, Response res) {
if (res.code() == HttpCodes.NOT_AUTHENTICATED) {
@@ -222,7 +222,7 @@ public void onResponse(Call call, Response r
private void setupPreferencesRetry(String freshToken) {
settingsService.getSettings(Globals.getAuthHeader(freshToken))
- .enqueue(new DisconnectLogger<>(this) {
+ .enqueue(new ErrorLogger<>(this) {
@Override
public void onResponse(Call call, Response res) {
if (res.code() == HttpCodes.NOT_FOUND) {
@@ -388,11 +388,11 @@ private void renderSelectedFragment() {
private void handleLogout() {
getDefaultSharedPreferences(ProfileActivity.this)
.edit().clear().apply();
- Account[] accs = accountManager.getAccountsByType(AccountAuthenticator.ACCOUNT_TYPE);
+ Account acc = getSingleAccount(accountManager);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
accountManager.removeAccount(
- accs[0], this, future -> {
- Log.d(TAG, "Account '" + accs[0].name + "' removed.");
+ acc, this, future -> {
+ Log.d(TAG, "Account '" + acc.name + "' removed.");
unbindSocketService();
finishAndRemoveTask();
}, null);
@@ -431,7 +431,7 @@ private static Fragment createFragment(long navigationMenuIndex) {
} else if (navigationMenuIndex == DRAWER_NEW_EVENT_ID) {
result = new CreateNewEventFragment();
} else if (navigationMenuIndex == DRAWER_NOTIFICATION_ID) {
- result = new UserEventsFragment();
+ result = new ProfileEventsFragment();
} else {
result = new ProfileFragment();
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SettingsActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SettingsActivity.java
index 54420e1..bdd37cd 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SettingsActivity.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SettingsActivity.java
@@ -26,7 +26,7 @@
import com.tom.meeter.databinding.SettingsActivityBinding;
import com.tom.meeter.infrastructure.common.Globals;
import com.tom.meeter.infrastructure.common.PreferencesHelper;
-import com.tom.meeter.infrastructure.http.DisconnectLogger;
+import com.tom.meeter.infrastructure.http.ErrorLogger;
import com.tom.meeter.infrastructure.http.HttpCodes;
import javax.inject.Inject;
@@ -112,7 +112,7 @@ private void sendSavePrefs(int searchArea, boolean trackUser) {
settingsService.createOrUpdateSettings(
new SettingsCreateOrUpdate(searchArea, trackUser),
Globals.getAuthHeader(AuthHelper.peekToken(accountManager)))
- .enqueue(new DisconnectLogger<>(this) {
+ .enqueue(new ErrorLogger<>(this) {
@Override
public void onResponse(Call call, Response res) {
if (res.code() == HttpCodes.NOT_AUTHENTICATED) {
@@ -135,7 +135,7 @@ private void sendSavePrefsRetry(String token, int searchArea, boolean trackUser)
settingsService.createOrUpdateSettings(
new SettingsCreateOrUpdate(searchArea, trackUser),
Globals.getAuthHeader(token))
- .enqueue(new DisconnectLogger<>(this) {
+ .enqueue(new ErrorLogger<>(this) {
@Override
public void onResponse(Call call, Response res) {
if (res.code() == HttpCodes.OK || res.code() == HttpCodes.CREATED) {
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/ActiveEventsAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/ActiveEventsAdapter.java
new file mode 100644
index 0000000..9c563b8
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/ActiveEventsAdapter.java
@@ -0,0 +1,35 @@
+package com.tom.meeter.context.profile.adapter;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.tom.meeter.databinding.EventViewBinding;
+import com.tom.meeter.infrastructure.components.adapter.BaseEventAdapter;
+import com.tom.meeter.infrastructure.components.binder.ViewHolderEventBinder;
+import com.tom.meeter.infrastructure.components.viewholder.EventViewHolder;
+
+/**
+ * created by Tom on 10.02.2017.
+ */
+public class ActiveEventsAdapter extends BaseEventAdapter {
+
+ private static final String TAG = ActiveEventsAdapter.class.getCanonicalName();
+
+ public ActiveEventsAdapter(ViewHolderEventBinder binder) {
+ super(binder);
+ }
+
+ @Override
+ public EventViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new EventViewHolder(
+ EventViewBinding.inflate(
+ LayoutInflater.from(parent.getContext()), parent, false));
+ }
+
+ @Override
+ public void onAttachedToRecyclerView(RecyclerView recyclerView) {
+ super.onAttachedToRecyclerView(recyclerView);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/EventsAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/EventsAdapter.java
new file mode 100644
index 0000000..444dc25
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/EventsAdapter.java
@@ -0,0 +1,40 @@
+package com.tom.meeter.context.profile.adapter;
+
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.tom.meeter.databinding.EventViewBinding;
+import com.tom.meeter.infrastructure.components.adapter.BaseEventAdapter;
+import com.tom.meeter.infrastructure.components.binder.ViewHolderEventBinder;
+import com.tom.meeter.infrastructure.components.viewholder.EventViewHolder;
+
+/**
+ * created by Tom on 10.02.2017.
+ */
+public class EventsAdapter extends BaseEventAdapter {
+
+ private static final String TAG = EventsAdapter.class.getCanonicalName();
+
+ public EventsAdapter(ViewHolderEventBinder binder) {
+ super(binder);
+ logMethod(TAG, this);
+ }
+
+ @Override
+ public EventViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ logMethod(TAG, this);
+ return new EventViewHolder(
+ EventViewBinding.inflate(
+ LayoutInflater.from(parent.getContext()), parent, false));
+ }
+
+ @Override
+ public void onAttachedToRecyclerView(RecyclerView recyclerView) {
+ super.onAttachedToRecyclerView(recyclerView);
+ logMethod(TAG, this);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/RecycleViewActiveEventsAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/RecycleViewActiveEventsAdapter.java
deleted file mode 100644
index 8da7a35..0000000
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/RecycleViewActiveEventsAdapter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.tom.meeter.context.profile.adapter;
-
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.getCircleBitmap;
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.randomPicResource;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.tom.meeter.context.event.activity.EventActivity;
-import com.tom.meeter.context.network.dto.EventDTO;
-import com.tom.meeter.databinding.EventViewBinding;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * created by Tom on 10.02.2017.
- */
-public class RecycleViewActiveEventsAdapter extends RecyclerView.Adapter {
- private static final String TAG = RecycleViewUserEventsAdapter.class.getCanonicalName();
- private final List events = new ArrayList<>();
- private Context ctx;
-
- public RecycleViewActiveEventsAdapter() {
- }
-
- //TODO remove me when image can be downloaded from the server
- public RecycleViewActiveEventsAdapter(Context ctx) {
- this.ctx = ctx;
- }
-
- public List getEvents() {
- return events;
- }
-
- public void addEvent(EventDTO event) {
- events.add(event);
- }
-
- public void addEvents(List events) {
- this.events.addAll(events);
- }
-
- public void cleanEvents() {
- events.clear();
- }
-
-
- @Override
- public EventViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new EventViewHolder(
- EventViewBinding.inflate(
- LayoutInflater.from(parent.getContext()), parent, false));
- }
-
- @Override
- public void onBindViewHolder(EventViewHolder holder, int position) {
- EventDTO event = events.get(position);
-
- Bitmap src = BitmapFactory.decodeResource(ctx.getResources(), randomPicResource());
- Bitmap scaled = Bitmap.createScaledBitmap(src, 150, 150, true);
- Bitmap circled = getCircleBitmap(scaled);
-
- holder.bind(event.getName(), event.getDescription(), circled,
- v -> ctx.startActivity(
- new Intent(ctx, EventActivity.class)
- .putExtra(EventActivity.EVENT_ID_KEY, event.getId())));
- }
-
- @Override
- public int getItemCount() {
- return events.size();
- }
-
- @Override
- public void onAttachedToRecyclerView(RecyclerView recyclerView) {
- super.onAttachedToRecyclerView(recyclerView);
- }
-}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/RecycleViewUserEventsAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/RecycleViewUserEventsAdapter.java
deleted file mode 100644
index c0b3f8c..0000000
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/RecycleViewUserEventsAdapter.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package com.tom.meeter.context.profile.adapter;
-
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.getCircleBitmap;
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.randomPicResource;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.DiffUtil;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.tom.meeter.context.event.activity.EventActivity;
-import com.tom.meeter.context.network.dto.EventDTO;
-import com.tom.meeter.databinding.EventViewBinding;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * created by Tom on 10.02.2017.
- */
-
-public class RecycleViewUserEventsAdapter extends RecyclerView.Adapter {
- private final List events = new ArrayList<>();
-
- //TODO remove me when ...
- private Context ctx;
- public RecycleViewUserEventsAdapter(Context ctx) {
- this.ctx = ctx;
- }
-
- public RecycleViewUserEventsAdapter() {
- }
-
- public void setData(List events) {
- EventDiffCallback eventDiffCallback = new EventDiffCallback(this.events, events);
- DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(eventDiffCallback);
- this.events.clear();
- this.events.addAll(events);
- diffResult.dispatchUpdatesTo(this);
- //Do we need it?
- notifyDataSetChanged();
- }
-
- @Override
- public EventViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new EventViewHolder(
- EventViewBinding.inflate(
- LayoutInflater.from(parent.getContext()), parent, false));
- }
-
- @Override
- public void onBindViewHolder(EventViewHolder holder, int position) {
- EventDTO event = events.get(position);
-
- Bitmap src = BitmapFactory.decodeResource(ctx.getResources(), randomPicResource());
- Bitmap scaled = Bitmap.createScaledBitmap(src, 150, 150, true);
- Bitmap circled = getCircleBitmap(scaled);
-
- holder.bind(event.getName(), event.getDescription(), circled,
- v -> ctx.startActivity(
- new Intent(ctx, EventActivity.class)
- .putExtra(EventActivity.EVENT_ID_KEY, event.getId())));
- /*
- btnDelete.setOnClickListener(v -> {
- if (onDeleteButtonClickListener != null)
- onDeleteButtonClickListener.onDeleteButtonClicked(post);
- });*/
- }
-
- @Override
- public int getItemCount() {
- return events.size();
- }
-
- @Override
- public void onAttachedToRecyclerView(RecyclerView recyclerView) {
- super.onAttachedToRecyclerView(recyclerView);
- }
-
- private static class EventDiffCallback extends DiffUtil.Callback {
-
- private final List oldPosts, newPosts;
-
- EventDiffCallback(List oldPosts, List newPosts) {
- this.oldPosts = oldPosts;
- this.newPosts = newPosts;
- }
-
- @Override
- public int getOldListSize() {
- return oldPosts.size();
- }
-
- @Override
- public int getNewListSize() {
- return newPosts.size();
- }
-
- @Override
- public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
- return oldPosts.get(oldItemPosition).getId().equals(newPosts.get(newItemPosition).getId());
- }
-
- @Override
- public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
- return oldPosts.get(oldItemPosition).equals(newPosts.get(newItemPosition));
- }
- }
-
-}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/domain/GMapEvent.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/domain/GMapEvent.java
index 071d9b2..75cee25 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/domain/GMapEvent.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/domain/GMapEvent.java
@@ -8,7 +8,7 @@
public class GMapEvent {
- private EventDTO event;
+ private final EventDTO event;
private Marker marker;
public GMapEvent(EventDTO event, Marker marker) {
@@ -21,6 +21,10 @@ public void removeMarker() {
marker.remove();
}
+ public EventDTO getEvent() {
+ return event;
+ }
+
public String getName() {
return event.getName();
}
@@ -98,4 +102,4 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(event, marker);
}
-}
\ No newline at end of file
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/event/repository/EventRepository.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/event/repository/EventRepository.java
index ff0b100..0f1fa67 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/event/repository/EventRepository.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/event/repository/EventRepository.java
@@ -4,7 +4,6 @@
import androidx.lifecycle.LiveData;
-import com.tom.meeter.AppScope;
import com.tom.meeter.context.network.dto.EventDTO;
import com.tom.meeter.context.profile.event.database.EventDao;
import com.tom.meeter.context.profile.event.domain.Event;
@@ -15,11 +14,10 @@
import java.util.List;
import java.util.concurrent.Executor;
-import javax.inject.Inject;
-
import retrofit2.Response;
-@AppScope
+//@AppScope
+@Deprecated
public class EventRepository {
private static final String TAG = EventRepository.class.getCanonicalName();
@@ -28,7 +26,7 @@ public class EventRepository {
private final EventDao eventDao;
private final Executor executor;
- @Inject
+ //@Inject
public EventRepository(EventService eventService, EventDao eventDao, Executor executor) {
this.eventService = eventService;
this.eventDao = eventDao;
@@ -60,8 +58,8 @@ private void refreshUserEvents(String userId) {
body.stream()
.forEach(i -> result.add(
new Event(i.getId(), i.getName(), i.getDescription(), i.getLatitude(),
- i.getLongitude(), i.getCreatorId(), i.getCreated(), i.getStarting(),
- i.getEnding())
+ i.getLongitude(), i.getCreatorId(), null/*i.getCreated()*/, null/*i.getStarting()*/,
+ null/*i.getEnding()*/)
));
eventDao.deleteByUserId(userId);
eventDao.saveAll(result);
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ActiveEventsFragment.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ActiveEventsFragment.java
index 56fc0c1..40bc7a6 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ActiveEventsFragment.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ActiveEventsFragment.java
@@ -4,8 +4,10 @@
* Created by Tom on 09.12.2016.
*/
+import static com.tom.meeter.context.event.activity.EventActivity.dispatchToEventActivity;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@@ -17,21 +19,30 @@
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
-import com.tom.meeter.context.profile.adapter.RecycleViewActiveEventsAdapter;
+import com.tom.meeter.App;
+import com.tom.meeter.context.image.ImageDownloader;
+import com.tom.meeter.context.profile.adapter.EventsAdapter;
import com.tom.meeter.databinding.SubFragmentActiveEventsBinding;
+import com.tom.meeter.infrastructure.common.InfrastructureHelper;
+import com.tom.meeter.infrastructure.components.binder.PhotoDownloaderWithCacheEventEventBinder;
import com.tom.meeter.infrastructure.eventbus.events.IncomeEvents;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
+import javax.inject.Inject;
+
public class ActiveEventsFragment extends Fragment {
private static final String TAG = ActiveEventsFragment.class.getCanonicalName();
SubFragmentActiveEventsBinding binding;
- private RecycleViewActiveEventsAdapter recycleViewActiveEventsAdapter;
+ @Inject
+ ImageDownloader imageDownloader;
+
+ private EventsAdapter adapter;
public ActiveEventsFragment() {
logMethod(TAG, this);
@@ -41,7 +52,18 @@ public ActiveEventsFragment() {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
logMethod(TAG, this);
+
+ ((App) getActivity().getApplication()).getComponent().inject(this);
+
EventBus.getDefault().register(this);
+
+ Context ctx = getContext();
+ adapter = new EventsAdapter(
+ new PhotoDownloaderWithCacheEventEventBinder(
+ ctx, imageDownloader,
+ (e) -> dispatchToEventActivity(ctx, e.getId()),
+ () -> InfrastructureHelper.restartActivityFromFragment(this)));
+
Log.d(TAG, "ActiveEventsFragment Registering eventBus");
}
@@ -57,27 +79,13 @@ public View onCreateView(
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
logMethod(TAG, this);
-
- // use this setting to improve performance if you know that changes
- // in content do not change the layout size of the RecyclerView
- //rView.setHasFixedSize(true);
-
- // use a linear layout manager
binding.activeEventsFragmentRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
-
- // specify an adapter (see also next example)
- recycleViewActiveEventsAdapter = new RecycleViewActiveEventsAdapter(getContext());
- binding.activeEventsFragmentRecyclerView.setAdapter(recycleViewActiveEventsAdapter);
- binding.activeEventsFragmentRecyclerView.invalidate();
+ binding.activeEventsFragmentRecyclerView.setAdapter(adapter);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(IncomeEvents eventsSearch) {
- recycleViewActiveEventsAdapter.cleanEvents();
- if (!eventsSearch.events().isEmpty()) {
- recycleViewActiveEventsAdapter.addEvents(eventsSearch.events());
- }
- binding.activeEventsFragmentRecyclerView.requestLayout();
+ adapter.setData(eventsSearch.events());
}
@Override
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/CreateNewEventFragment.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/CreateNewEventFragment.java
index 368c7b2..ed7f8a7 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/CreateNewEventFragment.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/CreateNewEventFragment.java
@@ -1,6 +1,7 @@
package com.tom.meeter.context.profile.fragment;
import static android.content.Context.BIND_AUTO_CREATE;
+import static com.tom.meeter.infrastructure.common.CommonHelper.EMPTY_STR;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
import android.annotation.SuppressLint;
@@ -50,7 +51,6 @@
public class CreateNewEventFragment extends Fragment {
private static final String TAG = CreateNewEventFragment.class.getCanonicalName();
- private static final String EMPTY_STR = "";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/EventsFragment.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/EventsFragment.java
index 84b3783..d5704a4 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/EventsFragment.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/EventsFragment.java
@@ -65,7 +65,7 @@ private static ViewPagerAdapter createViewPagerAdapter(
ViewPagerAdapter adapter = new ViewPagerAdapter(fMgr);
adapter.addFragment(new GoogleMapsFragment(), mapTitle);
adapter.addFragment(new ActiveEventsFragment(), eventsTitle);
- adapter.addFragment(new UserEventsFragment(), yourEventsTitle);
+ adapter.addFragment(new ProfileEventsFragment(), yourEventsTitle);
return adapter;
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/GoogleMapsFragment.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/GoogleMapsFragment.java
index a04d448..f74e018 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/GoogleMapsFragment.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/GoogleMapsFragment.java
@@ -1,8 +1,8 @@
package com.tom.meeter.context.profile.fragment;
import static android.content.Context.BIND_AUTO_CREATE;
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.getCircleBitmap;
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.randomPicResource;
+import static com.tom.meeter.context.event.activity.EventActivity.dispatchToEventActivity;
+import static com.tom.meeter.infrastructure.common.ImagesHelper.circleImage;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
import android.content.ComponentName;
@@ -39,13 +39,15 @@
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.common.collect.Sets;
+import com.tom.meeter.App;
import com.tom.meeter.R;
-import com.tom.meeter.context.event.activity.EventActivity;
import com.tom.meeter.context.gps.domain.LocationTrackerListener;
import com.tom.meeter.context.gps.service.LocationTrackerService;
+import com.tom.meeter.context.image.ImageDownloader;
import com.tom.meeter.context.network.domain.SearchForEvents;
import com.tom.meeter.context.network.dto.EventDTO;
import com.tom.meeter.context.profile.domain.GMapEvent;
+import com.tom.meeter.infrastructure.common.InfrastructureHelper;
import com.tom.meeter.infrastructure.common.PreferencesHelper;
import com.tom.meeter.infrastructure.eventbus.events.IncomeEvents;
@@ -59,14 +61,16 @@
import java.util.Objects;
import java.util.Set;
+import javax.inject.Inject;
+
/**
* Created by Tom on 09.12.2016.
*/
public class GoogleMapsFragment extends Fragment
implements OnMapReadyCallback, LocationTrackerListener {
+ public static final float ZOOM_VALUE = 17;
private static final String TAG = GoogleMapsFragment.class.getCanonicalName();
- private static final float ZOOM_VALUE = 17;
private static final LatLng DEFAULT = new LatLng(0.0, 0.0);
private ServiceConnection locationServiceConnection;
@@ -86,6 +90,9 @@ public class GoogleMapsFragment extends Fragment
private GMapEvent lastClickedEvent;
+ @Inject
+ ImageDownloader imageDownloader;
+
public GoogleMapsFragment() {
logMethod(TAG, this);
}
@@ -103,6 +110,8 @@ public void onCreate(Bundle savedInstanceState) {
logMethod(TAG, this);
readPreferences();
+ ((App) getActivity().getApplication()).getComponent().inject(this);
+
MapsInitializer.initialize(getContext());
userIcon = BitmapDescriptorFactory.fromBitmap(
Bitmap.createScaledBitmap(
@@ -241,7 +250,7 @@ private boolean markerClickListener(Marker marker) {
}
if (target.equals(lastClickedEvent)) {
Log.d(TAG, "Double Click on: " + target.getName());
- startEventActivityFor(target.getId());
+ dispatchToEventActivity(getContext(), target.getId());
return false;
}
lastClickedEvent = target;
@@ -261,26 +270,27 @@ private GMapEvent searchForEvent(Marker marker) {
}
private void infoWindowClickListener(Marker marker) {
- Log.d(TAG, "infoWindowClickListener() " + marker.getId() + ", is info shown ? " + marker.isInfoWindowShown());
+ Log.d(TAG, "infoWindowClickListener() " + marker.getId()
+ + ", is info shown ? " + marker.isInfoWindowShown());
GMapEvent gMapEvent = searchForEvent(marker);
if (gMapEvent != null) {
- startEventActivityFor(gMapEvent.getId());
+ dispatchToEventActivity(getContext(), gMapEvent.getId());
}
}
- private void startEventActivityFor(String eventId) {
- startActivity(new Intent(this.getContext(), EventActivity.class)
- .putExtra(EventActivity.EVENT_ID_KEY, eventId));
- }
-
private void putExistingMarkersOnMap() {
for (GMapEvent e : events.values()) {
- e.replaceMarker(
- gmap.addMarker(
- createEventMarkerOptions(e.getName(), e.getLatitude(), e.getLongitude())));
+ e.replaceMarker(addMarkerWithPhoto(e.getEvent()));
}
}
+ private Marker addMarkerWithPhoto(EventDTO event) {
+ Marker marker = gmap.addMarker(createEventMarkerOptions(
+ event.getName(), event.getLatitude(), event.getLongitude()));
+ downloadPhotoForMarker(event.getPhotoPath(), marker);
+ return marker;
+ }
+
@Override
public void onDestroy() {
super.onDestroy();
@@ -315,11 +325,7 @@ public void onMessageEvent(IncomeEvents msg) {
// Events to add -
for (String eId : toAdd) {
EventDTO ev = incomeEvents.get(eId);
- events.put(
- eId,
- new GMapEvent(
- ev, gmap.addMarker(
- createEventMarkerOptions(ev.getName(), ev.getLatitude(), ev.getLongitude()))));
+ events.put(eId, new GMapEvent(ev, addMarkerWithPhoto(ev)));
}
// Intersection - need to apply events update, if any
@@ -328,12 +334,26 @@ public void onMessageEvent(IncomeEvents msg) {
}
}
+ private void downloadPhotoForMarker(String photoPath, Marker marker) {
+ if (photoPath == null) {
+ return;
+ }
+ imageDownloader.downloadEventImage(
+ photoPath, getContext(),
+ photo -> {
+ if (photo != null) {
+ marker.setIcon(BitmapDescriptorFactory.fromBitmap(circleImage(photo)));
+ }
+ },
+ () -> InfrastructureHelper.restartActivityFromFragment(this));
+ }
+
private void readPreferences() {
searchArea = PreferencesHelper.getSearchArea(getContext());
trackUser = PreferencesHelper.getNeedTrackUser(getContext());
}
- private static void moveCamera(
+ public static void moveCamera(
LatLng lastKnownUserLocation, GoogleMap gmap, boolean firstOpening, CameraPosition camPosition) {
if (firstOpening) {
if (lastKnownUserLocation != null) {
@@ -354,6 +374,7 @@ private static void updateWith(GMapEvent me, EventDTO update) {
+ " " + update.getId() + " is changed. Moving the marker.");
me.updatePosition(update.getLatitude(), update.getLongitude());
}
+ //TODO Every field could changed...
}
private MarkerOptions createUserMarkerOptions(LatLng latLng) {
@@ -363,17 +384,14 @@ private MarkerOptions createUserMarkerOptions(LatLng latLng) {
.position(latLng);
}
- private MarkerOptions createEventMarkerOptions(String name, double latitude, double longitude) {
- //TODO Download event photo
- Bitmap src = BitmapFactory.decodeResource(getContext().getResources(), randomPicResource());
+ private MarkerOptions createEventMarkerOptions(
+ String name, double latitude, double longitude) {
return new MarkerOptions()
.position(new LatLng(latitude, longitude))
- .title(name)
- .icon(BitmapDescriptorFactory.fromBitmap(
- getCircleBitmap(Bitmap.createScaledBitmap(src, 150, 150, true))));
+ .title(name);
}
- private static LatLng mapToLatTng(Location location) {
+ public static LatLng mapToLatTng(Location location) {
return new LatLng(location.getLatitude(), location.getLongitude());
}
@@ -444,4 +462,22 @@ public int hashCode() {
return Objects.hash(id, latitude, longitude);
}
}
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ logMethod(TAG, this);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ logMethod(TAG, this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ logMethod(TAG, this);
+ }
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/UserEventsFragment.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileEventsFragment.java
similarity index 54%
rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/UserEventsFragment.java
rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileEventsFragment.java
index 4718c19..3f3ee8e 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/UserEventsFragment.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileEventsFragment.java
@@ -1,9 +1,11 @@
package com.tom.meeter.context.profile.fragment;
-import static com.tom.meeter.context.auth.infrastructure.AuthHelper.peekToken;
+import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader;
+import static com.tom.meeter.context.event.activity.EventActivity.dispatchToEventActivity;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
import android.accounts.AccountManager;
+import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -16,29 +18,34 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import com.tom.meeter.App;
-import com.tom.meeter.context.profile.adapter.RecycleViewUserEventsAdapter;
+import com.tom.meeter.context.image.ImageDownloader;
+import com.tom.meeter.context.profile.adapter.EventsAdapter;
import com.tom.meeter.context.profile.viewmodel.ProfileEventsViewModel;
import com.tom.meeter.databinding.SubFragmentUserEventsBinding;
+import com.tom.meeter.infrastructure.common.InfrastructureHelper;
+import com.tom.meeter.infrastructure.components.binder.PhotoDownloaderWithCacheEventEventBinder;
import com.tom.meeter.infrastructure.injection.viewmodel.ViewModelFactory;
import javax.inject.Inject;
-public class UserEventsFragment extends Fragment {
+public class ProfileEventsFragment extends Fragment {
- private static final String TAG = UserEventsFragment.class.getCanonicalName();
+ private static final String TAG = ProfileEventsFragment.class.getCanonicalName();
SubFragmentUserEventsBinding binding;
- private RecycleViewUserEventsAdapter adapter;
+ private EventsAdapter adapter;
@Inject
ViewModelFactory viewModelFactory;
+ @Inject
+ ImageDownloader imageDownloader;
private ProfileEventsViewModel profileEventsViewModel;
private AccountManager accountManager;
- public UserEventsFragment() {
+ public ProfileEventsFragment() {
logMethod(TAG, this);
}
@@ -46,8 +53,22 @@ public UserEventsFragment() {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
logMethod(TAG, this);
+
((App) getActivity().getApplication()).getComponent().inject(this);
- accountManager = AccountManager.get(this.getContext());
+
+ Context ctx = getContext();
+ accountManager = AccountManager.get(ctx);
+
+ adapter = new EventsAdapter(
+ new PhotoDownloaderWithCacheEventEventBinder(
+ ctx, imageDownloader,
+ (e) -> dispatchToEventActivity(ctx, e.getId()),
+ () -> InfrastructureHelper.restartActivityFromFragment(this)));
+ /*
+ btnDelete.setOnClickListener(v -> {
+ if (onDeleteButtonClickListener != null)
+ onDeleteButtonClickListener.onDeleteButtonClicked(post);
+ });*/
}
@Override
@@ -64,21 +85,34 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
logMethod(TAG, this);
profileEventsViewModel = ViewModelProviders.of(this, viewModelFactory)
.get(ProfileEventsViewModel.class);
-
- profileEventsViewModel.getProfileEvents(peekToken(accountManager), this);
-
- adapter = new RecycleViewUserEventsAdapter(getContext());
+ profileEventsViewModel.fetchProfileEvents(getAuthHeader(accountManager), this);
profileEventsViewModel.getProfileEventsLiveData()
- .observe(getViewLifecycleOwner(), ev -> adapter.setData(ev));
-
+ .observe(getViewLifecycleOwner(), events -> adapter.setData(events));
binding.userEventsFragmentRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
binding.userEventsFragmentRecyclerView.setAdapter(adapter);
- binding.userEventsFragmentRecyclerView.invalidate();
+ }
- /*
- adapter = new RecycleViewUserEventsAdapter(events);
- rView.swapAdapter(adapter, false);
- */
+ @Override
+ public void onPause() {
+ super.onPause();
+ logMethod(TAG, this);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ logMethod(TAG, this);
+ }
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ logMethod(TAG, this);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ logMethod(TAG, this);
}
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileFragment.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileFragment.java
index 6de7f25..42016ee 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileFragment.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileFragment.java
@@ -1,12 +1,16 @@
package com.tom.meeter.context.profile.fragment;
-import static com.tom.meeter.context.auth.infrastructure.AuthHelper.peekToken;
+import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader;
+import static com.tom.meeter.context.event.activity.EventActivity.dispatchToEventActivity;
import static com.tom.meeter.infrastructure.common.CommonHelper.genderResolver;
+import static com.tom.meeter.infrastructure.common.CommonHelper.getLocalDateOrNull;
+import static com.tom.meeter.infrastructure.common.CommonHelper.getStringOrNull;
import static com.tom.meeter.infrastructure.common.DateHelper.getAgeFromDate;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.showMessage;
import android.accounts.AccountManager;
-import android.content.Intent;
+import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -16,32 +20,54 @@
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
+import androidx.recyclerview.widget.GridLayoutManager;
import com.tom.meeter.App;
import com.tom.meeter.R;
-import com.tom.meeter.context.event.activity.EventActivity;
+import com.tom.meeter.context.image.ImageDownloader;
+import com.tom.meeter.context.profile.message.UpdateProfileRequest;
+import com.tom.meeter.context.profile.service.ProfileService;
+import com.tom.meeter.context.profile.user.domain.User;
import com.tom.meeter.context.profile.viewmodel.ProfileViewModel;
-import com.tom.meeter.context.user.GridViewAdapter;
import com.tom.meeter.databinding.FragmentProfileBinding;
+import com.tom.meeter.infrastructure.common.InfrastructureHelper;
+import com.tom.meeter.infrastructure.components.adapter.EventsCardAdapter;
+import com.tom.meeter.infrastructure.components.binder.PhotoDownloaderEventBinder;
+import com.tom.meeter.infrastructure.http.ActivityRestarterOnAuthFailure;
+import com.tom.meeter.infrastructure.http.HttpCodes;
import com.tom.meeter.infrastructure.injection.viewmodel.ViewModelFactory;
+import java.time.LocalDate;
+import java.util.Objects;
+
import javax.inject.Inject;
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Response;
+
/**
* Created by Tom on 14.12.2016.
*/
public class ProfileFragment extends Fragment {
private static final String TAG = ProfileFragment.class.getCanonicalName();
+ private boolean isEditableModeEnabled = false;
private FragmentProfileBinding binding;
@Inject
ViewModelFactory viewModelFactory;
-
+ @Inject
+ ImageDownloader imageDownloader;
+ @Inject
+ ProfileService profileService;
private ProfileViewModel profileViewModel;
-
private AccountManager accountManager;
+ private EventsCardAdapter adapter;
+
+ private User userCache;
+ private ResponseBody photoCache;
public ProfileFragment() {
logMethod(TAG, this);
@@ -50,9 +76,17 @@ public ProfileFragment() {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- ((App) getActivity().getApplication()).getComponent().inject(this);
- accountManager = AccountManager.get(this.getContext());
logMethod(TAG, this);
+
+ ((App) getActivity().getApplication()).getComponent().inject(this);
+
+ Context ctx = getContext();
+ accountManager = AccountManager.get(ctx);
+
+ adapter = new EventsCardAdapter(
+ new PhotoDownloaderEventBinder(ctx, imageDownloader,
+ event -> dispatchToEventActivity(ctx, event.getId()),
+ () -> InfrastructureHelper.restartActivityFromFragment(this)));
}
@Override
@@ -67,31 +101,96 @@ public View onCreateView(
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
logMethod(TAG, this);
+
profileViewModel = ViewModelProviders.of(this, viewModelFactory)
.get(ProfileViewModel.class);
- profileViewModel.getProfile(peekToken(accountManager), this);
- profileViewModel.getProfileLiveData().observe(
- getViewLifecycleOwner(),
- user -> {
- if (user != null) {
- binding.profileId.setText(getString(R.string.profile_user_id_format, user.getId()));
- binding.profileName.setText(getString(R.string.profile_user_name_format, user.getName(), user.getSurname()));
- binding.profileGender.setText(getString(R.string.profile_gender_format, genderResolver(getContext(), user.getGender())));
- binding.profileAge.setText(getString(R.string.profile_age_format, getAgeFromDate(user.getBirthday())));
- binding.profileInfo.setText(getString(R.string.profile_info_format, user.getInfo()));
- }
- });
- profileViewModel.getProfileEventsLiveData().observe(
- getViewLifecycleOwner(),
- events -> {
- binding.profileEventsGrid.setAdapter(new GridViewAdapter(getContext(), events));
- binding.profileEventsGrid.setExpanded(true);
- binding.profileEventsGrid.setOnItemClickListener(
- (parent, view1, position, id) ->
- startActivity(new Intent(getActivity(), EventActivity.class)
- .putExtra(EventActivity.EVENT_ID_KEY, events.get(position).getId())));
- }
- );
+ String authHeader = getAuthHeader(accountManager);
+
+ profileViewModel.fetchProfile(authHeader, this);
+
+ profileViewModel.getProfileLiveData()
+ .observe(
+ getViewLifecycleOwner(),
+ user -> {
+ userCache = user;
+ updateLayoutValues();
+ });
+
+ profileViewModel.getProfileEventsLiveData()
+ .observe(getViewLifecycleOwner(), events -> adapter.setData(events));
+
+ binding.events.setLayoutManager(new GridLayoutManager(getContext(), 2));
+ binding.events.setAdapter(adapter);
+
+ binding.btnEdit.setOnClickListener(v -> {
+ if (isEditableModeEnabled) {
+ UpdateProfileRequest req = createUpdateProfileRequest();
+ if (req.isEmpty()) {
+ showMessage(this.getActivity(), "Empty update request is not sent.");
+ updateLayoutValues();
+ switchEditMode();
+ return;
+ }
+ profileService.updateProfile(authHeader, req)
+ .enqueue(new ActivityRestarterOnAuthFailure<>(this) {
+ @Override
+ public void onResponse(Call call, Response response) {
+ super.onResponse(call, response);
+ if (response.code() == HttpCodes.OK && response.body() != null) {
+ userCache = response.body();
+ showMessage(ProfileFragment.this.getActivity(), "Saved");
+ }
+ updateLayoutValues();
+ }
+ });
+ }
+ switchEditMode();
+ });
+ }
+
+ private void updateLayoutValues() {
+ /*binding.profileId.setText(userCache.getId());*/
+ binding.name.setText(userCache.getName());
+ binding.surname.setText(userCache.getSurname());
+ binding.gender.setText(genderResolver(getContext(), userCache.getGender()));
+ binding.birthday.setText(userCache.getBirthday());
+ binding.age.setText(getString(R.string.profile_age_format, getAgeFromDate(userCache.getBirthday())));
+ binding.info.setText(userCache.getInfo());
+ }
+
+ private void switchEditMode() {
+ isEditableModeEnabled = !isEditableModeEnabled;
+ binding.name.setEnabled(isEditableModeEnabled);
+ binding.surname.setEnabled(isEditableModeEnabled);
+ binding.birthday.setEnabled(isEditableModeEnabled);
+ binding.info.setEnabled(isEditableModeEnabled);
+ binding.btnEdit.setText(isEditableModeEnabled ? "Save" : "Edit");
+ }
+
+ private UpdateProfileRequest createUpdateProfileRequest() {
+ UpdateProfileRequest req = new UpdateProfileRequest();
+
+ String nameChange = getStringOrNull(binding.name.getText());
+ if (!Objects.equals(userCache.getName(), nameChange)) {
+ req.setName(nameChange);
+ }
+ String surnameChange = getStringOrNull(binding.surname.getText());
+ if (!Objects.equals(userCache.getSurname(), surnameChange)) {
+ req.setSurname(surnameChange);
+ }
+ LocalDate birthdayChange = getLocalDateOrNull(binding.birthday.getText());
+ //todo userCache.getBirthday() [String -> LocalDate]
+ if (!Objects.equals(
+ userCache.getBirthday(),
+ birthdayChange == null ? null : birthdayChange.toString())) {
+ req.setBirthday(birthdayChange);
+ }
+ String infoChange = getStringOrNull(binding.info.getText());
+ if (!Objects.equals(userCache.getInfo(), infoChange)) {
+ req.setInfo(infoChange);
+ }
+ //TODO: userCache.getPhotoPath();
+ return req;
}
@Override
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/message/UpdateProfileRequest.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/message/UpdateProfileRequest.java
new file mode 100644
index 0000000..1d003bd
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/message/UpdateProfileRequest.java
@@ -0,0 +1,81 @@
+package com.tom.meeter.context.profile.message;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.time.LocalDate;
+import java.util.Optional;
+
+public class UpdateProfileRequest {
+
+ private Optional name;
+ private Optional surname;
+ private Optional info;
+ @JsonProperty(value = "old_password")
+ private Optional oldPassword;
+ @JsonProperty(value = "new_password")
+ private Optional newPassword;
+ private Optional birthday;
+ @JsonProperty(value = "photo_path")
+ private Optional photoPath;
+
+ public boolean isEmpty() {
+ return (name == null && surname == null && info == null && oldPassword == null
+ && newPassword == null && birthday == null && photoPath == null);
+ }
+
+ public Optional getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = Optional.ofNullable(name);
+ }
+
+ public Optional getSurname() {
+ return surname;
+ }
+
+ public void setSurname(String surname) {
+ this.surname = Optional.ofNullable(surname);
+ }
+
+ public Optional getInfo() {
+ return info;
+ }
+
+ public void setInfo(String info) {
+ this.info = Optional.ofNullable(info);
+ }
+
+ public Optional getOldPassword() {
+ return oldPassword;
+ }
+
+ public void setOldPassword(String oldPassword) {
+ this.oldPassword = Optional.ofNullable(oldPassword);
+ }
+
+ public Optional getNewPassword() {
+ return newPassword;
+ }
+
+ public void setNewPassword(String newPassword) {
+ this.newPassword = Optional.ofNullable(newPassword);
+ }
+
+ public Optional getBirthday() {
+ return birthday;
+ }
+
+ public void setBirthday(LocalDate birthday) {
+ this.birthday = Optional.ofNullable(birthday);
+ }
+
+ public Optional getPhotoPath() {
+ return photoPath;
+ }
+
+ public void setPhotoPath(String photoPath) {
+ this.photoPath = Optional.ofNullable(photoPath);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/service/ProfileService.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/service/ProfileService.java
index b0f1e1a..398c56e 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/service/ProfileService.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/service/ProfileService.java
@@ -3,18 +3,25 @@
import static com.tom.meeter.infrastructure.common.Globals.AUTH_HEADER;
import com.tom.meeter.context.network.dto.EventDTO;
+import com.tom.meeter.context.profile.message.UpdateProfileRequest;
import com.tom.meeter.context.profile.user.domain.User;
import java.util.List;
import retrofit2.Call;
+import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Header;
+import retrofit2.http.PATCH;
public interface ProfileService {
@GET("/profile")
Call getProfile(@Header(AUTH_HEADER) String authHeader);
+ @PATCH("/profile/update")
+ Call updateProfile(
+ @Header(AUTH_HEADER) String authHeader, @Body UpdateProfileRequest req);
+
@GET("/profile/events")
Call> getProfileEvents(@Header(AUTH_HEADER) String authHeader);
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsCreateOrUpdate.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsCreateOrUpdate.java
index 1cc6219..69977cc 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsCreateOrUpdate.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsCreateOrUpdate.java
@@ -1,12 +1,12 @@
package com.tom.meeter.context.profile.settings.message;
-import com.google.gson.annotations.SerializedName;
+import com.fasterxml.jackson.annotation.JsonProperty;
public class SettingsCreateOrUpdate {
- @SerializedName(value = "search_area")
+ @JsonProperty(value = "search_area")
private Integer searchArea;
- @SerializedName(value = "need_track_user")
+ @JsonProperty(value = "need_track_user")
private Boolean needTrackUser;
public SettingsCreateOrUpdate(Integer searchArea, Boolean needTrackUser) {
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsResponse.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsResponse.java
index 9183f0b..238fe94 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsResponse.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsResponse.java
@@ -1,16 +1,20 @@
package com.tom.meeter.context.profile.settings.message;
-import com.google.gson.annotations.SerializedName;
+import com.fasterxml.jackson.annotation.JsonProperty;
public class SettingsResponse {
- private final String id;
- @SerializedName(value = "user_id")
- private final String userId;
- @SerializedName(value = "search_area")
- private final Integer searchArea;
- @SerializedName(value = "need_track_user")
- private final Boolean needTrackUser;
+ private String id;
+ @JsonProperty(value = "user_id")
+ private String userId;
+ @JsonProperty(value = "search_area")
+ private Integer searchArea;
+ @JsonProperty(value = "need_track_user")
+ private Boolean needTrackUser;
+
+ public SettingsResponse() {
+ //Jackson requires empty c-tor
+ }
public SettingsResponse(String id, String userId, Integer searchArea, Boolean needTrackUser) {
this.id = id;
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/user/domain/User.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/user/domain/User.java
index 48f49fa..d92a6b3 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/user/domain/User.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/user/domain/User.java
@@ -4,7 +4,11 @@
import androidx.room.Entity;
import androidx.room.PrimaryKey;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+
@Entity
+@JsonIgnoreProperties(ignoreUnknown = true)
+//login?
public class User {
@PrimaryKey
@@ -16,6 +20,10 @@ public class User {
private String info;
private String birthday;
+ public User() {
+ //Jackson.
+ }
+
public User(
@NonNull String id, String name, String gender, String surname,
String info, String birthday) {
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/user/repository/UserRepository.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/user/repository/UserRepository.java
index 4907453..36b8560 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/user/repository/UserRepository.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/user/repository/UserRepository.java
@@ -4,20 +4,18 @@
import androidx.lifecycle.LiveData;
-import com.tom.meeter.AppScope;
import com.tom.meeter.context.profile.user.database.UserDao;
import com.tom.meeter.context.profile.user.domain.User;
import com.tom.meeter.context.user.service.UserService;
import java.util.concurrent.Executor;
-import javax.inject.Inject;
-
import io.reactivex.Completable;
import io.reactivex.Maybe;
import retrofit2.Response;
-@AppScope
+//@AppScope
+@Deprecated
public class UserRepository {
private static final String TAG = UserRepository.class.getCanonicalName();
@@ -27,7 +25,7 @@ public class UserRepository {
private final UserDao userDao;
private final Executor executor;
- @Inject
+ //@Inject
public UserRepository(UserService userService, UserDao userDao, Executor executor) {
this.userService = userService;
this.userDao = userDao;
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileEventsViewModel.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileEventsViewModel.java
index fe1bf13..6469205 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileEventsViewModel.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileEventsViewModel.java
@@ -11,7 +11,6 @@
import com.tom.meeter.context.network.dto.EventDTO;
import com.tom.meeter.context.profile.service.ProfileService;
-import com.tom.meeter.infrastructure.common.Globals;
import com.tom.meeter.infrastructure.http.ActivityRestarterOnAuthFailure;
import com.tom.meeter.infrastructure.http.HttpCodes;
@@ -36,8 +35,8 @@ public ProfileEventsViewModel(ProfileService profileService) {
this.profileService = profileService;
}
- public void getProfileEvents(String token, Fragment fragment) {
- profileService.getProfileEvents(Globals.getAuthHeader(token)).enqueue(
+ public void fetchProfileEvents(String auth, Fragment fragment) {
+ profileService.getProfileEvents(auth).enqueue(
new ActivityRestarterOnAuthFailure<>(fragment) {
@Override
public void onResponse(Call> call, Response> response) {
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileViewModel.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileViewModel.java
index ebaf70e..3be0d01 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileViewModel.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileViewModel.java
@@ -12,7 +12,6 @@
import com.tom.meeter.context.network.dto.EventDTO;
import com.tom.meeter.context.profile.service.ProfileService;
import com.tom.meeter.context.profile.user.domain.User;
-import com.tom.meeter.infrastructure.common.Globals;
import com.tom.meeter.infrastructure.http.ActivityRestarterOnAuthFailure;
import com.tom.meeter.infrastructure.http.HttpCodes;
@@ -38,8 +37,8 @@ public ProfileViewModel(ProfileService profileService) {
this.profileService = profileService;
}
- public void getProfile(String token, Fragment fragment) {
- profileService.getProfile(Globals.getAuthHeader(token)).enqueue(
+ public void fetchProfile(String auth, Fragment fragment) {
+ profileService.getProfile(auth).enqueue(
new ActivityRestarterOnAuthFailure<>(fragment) {
@Override
public void onResponse(Call call, Response response) {
@@ -52,7 +51,7 @@ public void onResponse(Call call, Response response) {
}
}
);
- profileService.getProfileEvents(Globals.getAuthHeader(token)).enqueue(
+ profileService.getProfileEvents(auth).enqueue(
new ActivityRestarterOnAuthFailure<>(fragment) {
@Override
public void onResponse(Call> call, Response> response) {
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserEventsViewModel.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserEventsViewModel.java
index 3eee107..158ee14 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserEventsViewModel.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserEventsViewModel.java
@@ -8,8 +8,6 @@
import java.util.List;
-import javax.inject.Inject;
-
@Deprecated
public class UserEventsViewModel extends ViewModel {
@@ -18,7 +16,7 @@ public class UserEventsViewModel extends ViewModel {
private final EventRepository eventRepository;
- @Inject
+ //@Inject
public UserEventsViewModel(EventRepository eventRepository) {
this.eventRepository = eventRepository;
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserProfileViewModel.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserProfileViewModel.java
index d75e6d7..36f2082 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserProfileViewModel.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserProfileViewModel.java
@@ -8,8 +8,6 @@
import com.tom.meeter.context.profile.user.domain.User;
import com.tom.meeter.context.profile.user.repository.UserRepository;
-import javax.inject.Inject;
-
@Deprecated
public class UserProfileViewModel extends ViewModel {
@@ -20,7 +18,7 @@ public class UserProfileViewModel extends ViewModel {
private final UserRepository userRepository;
- @Inject
+ //@Inject
public UserProfileViewModel(UserRepository userRepository) {
logMethod(TAG, this);
this.userRepository = userRepository;
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/token/TokenModule.java b/AndroidClient/src/main/java/com/tom/meeter/context/token/TokenModule.java
index e3372ba..b76c7b4 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/token/TokenModule.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/token/TokenModule.java
@@ -14,7 +14,8 @@
import dagger.Module;
import dagger.Provides;
import retrofit2.Retrofit;
-import retrofit2.converter.gson.GsonConverterFactory;
+import retrofit2.converter.jackson.JacksonConverterFactory;
+//import retrofit2.converter.gson.GsonConverterFactory;
@Module
public class TokenModule {
@@ -31,7 +32,8 @@ public TokenModule() {
public TokenService providesTokenService(Application app) {
return new Retrofit.Builder()
.baseUrl(getServerPath(app))
- .addConverterFactory(GsonConverterFactory.create())
+ .addConverterFactory(JacksonConverterFactory.create())
+ //.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TokenService.class);
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/token/service/TokenService.java b/AndroidClient/src/main/java/com/tom/meeter/context/token/service/TokenService.java
index 1d4ac03..e172ccf 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/token/service/TokenService.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/token/service/TokenService.java
@@ -7,10 +7,10 @@
import retrofit2.http.Header;
/**
- * Purpose is to check the token and fail auth in case of failed authorization.
+ * Purpose is to check the token and fail-fast the request ASAP, starting auth process
+ * for requesting new token or even new credentials in case of failed authentication.
*/
public interface TokenService {
- //TODO change GET path to '/check-token' when backend will be done
- @GET("/profile")
+ @GET("/api/token_check")
Call checkToken(@Header(AUTH_HEADER) String authHeader);
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/GridViewAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/GridViewAdapter.java
deleted file mode 100644
index 9e87a37..0000000
--- a/AndroidClient/src/main/java/com/tom/meeter/context/user/GridViewAdapter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.tom.meeter.context.user;
-
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.getCircleBitmap;
-import static com.tom.meeter.infrastructure.Image.ImagesHelper.randomPicResource;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.tom.meeter.R;
-import com.tom.meeter.context.network.dto.EventDTO;
-
-import java.util.List;
-
-public class GridViewAdapter extends ArrayAdapter {
-
- private final Context ctx;
-
- public GridViewAdapter(Context context, List list) {
- super(context, 0, list);
- ctx = context;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
-
- View itemView = convertView;
- if (itemView == null) {
- itemView = LayoutInflater.from(getContext())
- .inflate(R.layout.card_item, parent, false);
- }
-
- EventDTO event = getItem(position);
-
- TextView textView = itemView.findViewById(R.id.text_view);
- ImageView imageView = itemView.findViewById(R.id.image_view);
-
- if (event != null) {
- textView.setText(event.getName());
-
- Bitmap src = BitmapFactory.decodeResource(ctx.getResources(), randomPicResource());
- Bitmap scaled = Bitmap.createScaledBitmap(src, 150, 150, true);
- Bitmap circled = getCircleBitmap(scaled);
-
- imageView.setImageBitmap(circled);
- }
-
- return itemView;
- }
-}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/UserComponent.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/UserComponent.java
new file mode 100644
index 0000000..3b8c309
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/UserComponent.java
@@ -0,0 +1,38 @@
+package com.tom.meeter.context.user;
+
+import android.app.Application;
+
+import com.tom.meeter.context.image.ImageComponent;
+import com.tom.meeter.context.token.TokenComponent;
+import com.tom.meeter.context.user.activity.UserActivity;
+import com.tom.meeter.context.user.viewmodel.UserViewModelModule;
+
+import dagger.BindsInstance;
+import dagger.Component;
+
+@UserScope
+@Component(
+ modules = {
+ UserModule.class,
+ UserViewModelModule.class
+ },
+ dependencies = {
+ TokenComponent.class,
+ ImageComponent.class
+ })
+public interface UserComponent {
+
+ @Component.Builder
+ interface Builder {
+ @BindsInstance
+ Builder application(Application application);
+
+ Builder tokenComponent(TokenComponent tokenComponent);
+
+ Builder imageComponent(ImageComponent tokenComponent);
+
+ UserComponent build();
+ }
+
+ void inject(UserActivity userActivity);
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/UserModule.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/UserModule.java
new file mode 100644
index 0000000..ebfa342
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/UserModule.java
@@ -0,0 +1,46 @@
+package com.tom.meeter.context.user;
+
+import static com.tom.meeter.infrastructure.common.Globals.getServerPath;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+
+import android.app.Application;
+
+import androidx.annotation.NonNull;
+
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.json.JsonMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.tom.meeter.context.user.service.UserService;
+
+import java.util.TimeZone;
+
+import dagger.Module;
+import dagger.Provides;
+import retrofit2.Retrofit;
+import retrofit2.converter.jackson.JacksonConverterFactory;
+
+@Module
+public class UserModule {
+
+ private static final String TAG = UserModule.class.getCanonicalName();
+
+ public UserModule() {
+ logMethod(TAG, this);
+ }
+
+ @UserScope
+ @NonNull
+ @Provides
+ public UserService provideUserService(Application app) {
+ return new Retrofit.Builder()
+ .baseUrl(getServerPath(app))
+ .addConverterFactory(JacksonConverterFactory.create(
+ JsonMapper.builder()
+ .addModule(new JavaTimeModule())
+ .build()
+ .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .setTimeZone(TimeZone.getDefault())))
+ .build()
+ .create(UserService.class);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/UserScope.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/UserScope.java
new file mode 100644
index 0000000..e747bcc
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/UserScope.java
@@ -0,0 +1,11 @@
+package com.tom.meeter.context.user;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Scope;
+
+@Scope
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UserScope {
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserActivity.java
index b790775..45cee42 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserActivity.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserActivity.java
@@ -1,9 +1,11 @@
package com.tom.meeter.context.user.activity;
import static com.tom.meeter.context.auth.infrastructure.AuthHelper.checkToken;
+import static com.tom.meeter.context.event.activity.EventActivity.dispatchToEventActivity;
import static com.tom.meeter.infrastructure.common.CommonHelper.genderResolver;
import static com.tom.meeter.infrastructure.common.DateHelper.getAgeFromDate;
import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.showMessage;
import android.accounts.AccountManager;
import android.content.Context;
@@ -17,29 +19,46 @@
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProviders;
+import androidx.recyclerview.widget.GridLayoutManager;
import com.tom.meeter.App;
import com.tom.meeter.R;
-import com.tom.meeter.context.event.activity.EventActivity;
+import com.tom.meeter.context.image.ImageDownloader;
import com.tom.meeter.context.token.service.TokenService;
-import com.tom.meeter.context.user.GridViewAdapter;
+import com.tom.meeter.context.user.service.UserService;
import com.tom.meeter.context.user.viewmodel.UserViewModel;
-import com.tom.meeter.databinding.UserLayoutBinding;
+import com.tom.meeter.databinding.ActivityUserBinding;
+import com.tom.meeter.infrastructure.common.Globals;
+import com.tom.meeter.infrastructure.components.adapter.EventsCardAdapter;
+import com.tom.meeter.infrastructure.components.binder.PhotoDownloaderEventBinder;
+import com.tom.meeter.infrastructure.http.HttpCodes;
+import com.tom.meeter.infrastructure.http.HttpErrorLogger;
import com.tom.meeter.infrastructure.injection.viewmodel.ViewModelFactory;
import javax.inject.Inject;
+import retrofit2.Call;
+import retrofit2.Response;
+
public class UserActivity extends AppCompatActivity {
+
private static final String TAG = UserActivity.class.getCanonicalName();
- public static final String USER_ID_KEY = "user_id";
- UserLayoutBinding binding;
+ private static final String USER_ID_KEY = "user_id";
+
@Inject
TokenService tokenService;
@Inject
+ UserService userService;
+ @Inject
ViewModelFactory viewModelFactory;
+ @Inject
+ ImageDownloader imgDownloader;
+ private ActivityUserBinding binding;
private UserViewModel userViewModel;
private String userId;
private AccountManager accountManager;
+ private EventsCardAdapter adapter;
+ private Boolean amISubscriber;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -60,45 +79,99 @@ protected void onCreate(Bundle savedInstanceState) {
return;
}
- ((App) getApplication()).getComponent().inject(this);
+ ((App) getApplication()).getUserComponent().inject(this);
accountManager = AccountManager.get(this);
+ adapter = new EventsCardAdapter(
+ new PhotoDownloaderEventBinder(
+ this, imgDownloader,
+ event -> dispatchToEventActivity(this, event.getId()), this::recreate));
+
//setToken(accountManager, Launcher.EXPIRED);
checkToken(this::onInit, this::finish, accountManager, this, tokenService);
}
private void onInit(String token) {
- binding = UserLayoutBinding.inflate(getLayoutInflater());
+ binding = ActivityUserBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
+ binding.subscribeBtn.setOnClickListener(v -> {
+ if (amISubscriber == null) {
+ // As not initialized atm...
+ return;
+ }
+ if (amISubscriber) {
+ userService.unsubscribe(Globals.getAuthHeader(token), userId).enqueue(
+ new HttpErrorLogger<>(this) {
+ @Override
+ public void onResponse(Call call, Response resp) {
+ super.onResponse(call, resp);
+ if (resp.code() == HttpCodes.OK) {
+ amISubscriber = false;
+ updateSubscribeButtonText();
+ showMessage(UserActivity.this, "Successfully unsubscribed.");
+ return;
+ }
+ if (resp.code() == HttpCodes.NOT_AUTHENTICATED) {
+ UserActivity.this.recreate();
+ }
+ }
+ });
+ } else {
+ userService.subscribe(Globals.getAuthHeader(token), userId).enqueue(
+ new HttpErrorLogger<>(this) {
+ @Override
+ public void onResponse(Call call, Response resp) {
+ super.onResponse(call, resp);
+ if (resp.code() == HttpCodes.OK) {
+ amISubscriber = true;
+ updateSubscribeButtonText();
+ showMessage(UserActivity.this, "Successfully subscribed.");
+ return;
+ }
+ if (resp.code() == HttpCodes.NOT_AUTHENTICATED) {
+ UserActivity.this.recreate();
+ }
+ }
+ });
+ }
+ });
+
userViewModel = ViewModelProviders.of(this, viewModelFactory)
.get(UserViewModel.class);
userViewModel.fetchUserInformation(token, userId, this);
userViewModel.getUserLiveData()
.observe(this, user -> {
- if (user != null) {
- binding.userFormat.setText(getString(
- R.string.user_format, user.getName(), user.getSurname(),
- getAgeFromDate(user.getBirthday()),
- genderResolver(this, user.getGender())));
- binding.userInfo.setText(user.getInfo());
- }
+ binding.name.setText(user.getName());
+ binding.surname.setText(user.getSurname());
+ binding.gender.setText(genderResolver(getApplicationContext(), user.getGender()));
+ binding.birthday.setText(user.getBirthday());
+ binding.age.setText(getString(R.string.profile_age_format, getAgeFromDate(user.getBirthday())));
+ binding.info.setText(user.getInfo());
+ //binding.userPhoto.setImageBitmap();
});
- userViewModel.getUserEventsLiveData()
- .observe(this, events -> {
- if (events != null && !events.isEmpty()) {
- //GridAdapter adapter = new GridAdapter(this);
- //binding.userEventsGrid.setAdapter(adapter);
-
- GridViewAdapter adapter = new GridViewAdapter(this, events);
- binding.userEventsGrid.setAdapter(adapter);
- binding.userEventsGrid.setExpanded(true);
- binding.userEventsGrid.setOnItemClickListener(
- (parent, view1, position, id) ->
- startActivity(new Intent(UserActivity.this, EventActivity.class).putExtra(EventActivity.EVENT_ID_KEY, events.get(position).getId())));
- }
+ userViewModel.getAmISubscriber()
+ .observe(this, val -> {
+ amISubscriber = val;
+ updateSubscribeButtonText();
});
+ userViewModel.getUserEventsLiveData()
+ .observe(this, events -> adapter.setData(events));
+
+ binding.events.setLayoutManager(new GridLayoutManager(this, 2));
+ binding.events.setAdapter(adapter);
+ }
+
+ private void updateSubscribeButtonText() {
+ if (amISubscriber == null) {
+ binding.subscribeBtn.setText("...");
+ }
+ if (amISubscriber) {
+ binding.subscribeBtn.setText("Unsubscribe");
+ } else {
+ binding.subscribeBtn.setText("Subscribe");
+ }
}
@Nullable
@@ -108,4 +181,13 @@ public View onCreateView(
@NonNull AttributeSet attrs) {
return super.onCreateView(parent, name, ctx, attrs);
}
+
+ public static void dispatchToUserActivity(Context ctx, String userId) {
+ ctx.startActivity(createUserActivityIntent(ctx, userId));
+ }
+
+ private static Intent createUserActivityIntent(Context ctx, String userId) {
+ return new Intent(ctx, UserActivity.class)
+ .putExtra(USER_ID_KEY, userId);
+ }
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/GridAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridAdapter.java
similarity index 95%
rename from AndroidClient/src/main/java/com/tom/meeter/context/user/GridAdapter.java
rename to AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridAdapter.java
index 8231a2e..105a2fe 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/user/GridAdapter.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridAdapter.java
@@ -1,4 +1,4 @@
-package com.tom.meeter.context.user;
+package com.tom.meeter.context.user.adapter;
import android.content.Context;
import android.view.View;
@@ -7,6 +7,7 @@
import android.widget.GridView;
import android.widget.ImageView;
+@Deprecated
public class GridAdapter extends BaseAdapter {
private Context mContext;
private String[] mThumbIds = {
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridViewAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridViewAdapter.java
new file mode 100644
index 0000000..3ef5748
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridViewAdapter.java
@@ -0,0 +1,73 @@
+package com.tom.meeter.context.user.adapter;
+
+import static com.tom.meeter.infrastructure.common.ImagesHelper.circleImage;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+import com.tom.meeter.context.image.ImageDownloader;
+import com.tom.meeter.context.network.dto.EventDTO;
+import com.tom.meeter.databinding.CardItemBinding;
+
+import java.util.List;
+
+public class GridViewAdapter extends ArrayAdapter {
+
+ private static final String TAG = GridViewAdapter.class.getCanonicalName();
+
+ private final Context ctx;
+ private final ImageDownloader imageDownloader;
+ private final Runnable onAuthFail;
+
+ public GridViewAdapter(
+ Context ctx, List events,
+ ImageDownloader imgDownloader, Runnable onAuthFail) {
+ super(ctx, 0, events);
+ this.ctx = ctx;
+ this.imageDownloader = imgDownloader;
+ this.onAuthFail = onAuthFail;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+ if (convertView == null) {
+ CardItemBinding iBinding = CardItemBinding.inflate(
+ LayoutInflater.from(parent.getContext()), parent, false);
+ holder = new ViewHolder(iBinding);
+ holder.view = iBinding.getRoot();
+ holder.view.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ EventDTO event = getItem(position);
+ if (event != null) {
+ holder.binding.textView.setText(event.getName());
+ String photoPath = event.getPhotoPath();
+ if (photoPath != null) {
+ imageDownloader.downloadEventImage(
+ photoPath, ctx,
+ (photo) -> holder.binding.imageView.setImageBitmap(circleImage(photo)),
+ onAuthFail);
+ }
+ } else {
+ Log.w(TAG, "null event at [" + position + "].");
+ }
+ return holder.view;
+ }
+
+ private static class ViewHolder {
+ private View view;
+ private final CardItemBinding binding;
+
+ ViewHolder(CardItemBinding binding) {
+ this.view = binding.getRoot();
+ this.binding = binding;
+ }
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/service/UserService.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/service/UserService.java
index 9d68982..54270d5 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/user/service/UserService.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/service/UserService.java
@@ -23,4 +23,13 @@ public interface UserService {
@GET("/user/{id}/events")
Call> getUserEvents(@Header(AUTH_HEADER) String authHeader, @Path("id") String userId);
+
+ @GET("/user/{id}/am_i_subscribed")
+ Call amISubscribed(@Header(AUTH_HEADER) String authHeader, @Path("id") String userId);
+
+ @GET("/user/{id}/subscribe")
+ Call subscribe(@Header(AUTH_HEADER) String authHeader, @Path("id") String userId);
+
+ @GET("/user/{id}/unsubscribe")
+ Call unsubscribe(@Header(AUTH_HEADER) String authHeader, @Path("id") String userId);
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/viewmodel/UserViewModel.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/viewmodel/UserViewModel.java
index bf4279c..8af6f68 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/user/viewmodel/UserViewModel.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/viewmodel/UserViewModel.java
@@ -13,7 +13,7 @@
import com.tom.meeter.context.profile.user.domain.User;
import com.tom.meeter.context.user.service.UserService;
import com.tom.meeter.infrastructure.common.Globals;
-import com.tom.meeter.infrastructure.http.DisconnectLogger;
+import com.tom.meeter.infrastructure.http.ErrorLogger;
import com.tom.meeter.infrastructure.http.HttpCodes;
import java.util.List;
@@ -28,6 +28,7 @@ public class UserViewModel extends ViewModel {
private static final String TAG = UserViewModel.class.getCanonicalName();
private final MutableLiveData userLiveData = new MutableLiveData<>();
+ private final MutableLiveData amISubscriber = new MutableLiveData<>();
private final MutableLiveData> userEventsLiveData = new MutableLiveData<>();
private final UserService userService;
@@ -40,7 +41,7 @@ public UserViewModel(UserService userService) {
public void fetchUserInformation(String token, String userId, Activity activity) {
userService.getUser(Globals.getAuthHeader(token), userId).enqueue(
- new DisconnectLogger<>(activity) {
+ new ErrorLogger<>(activity) {
@Override
public void onResponse(Call call, Response response) {
if (response.code() == HttpCodes.OK && response.body() != null) {
@@ -55,8 +56,24 @@ public void onResponse(Call call, Response response) {
}
);
+ userService.amISubscribed(Globals.getAuthHeader(token), userId).enqueue(
+ new ErrorLogger<>(activity) {
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.code() == HttpCodes.OK && response.body() != null) {
+ amISubscriber.setValue(response.body());
+ return;
+ }
+ if (response.code() == HttpCodes.NOT_AUTHENTICATED) {
+ activity.recreate();
+ }
+ Log.i(TAG, "/user/{id}/am_i_subscribed: " + response.code() + " : " + response.body());
+ }
+ }
+ );
+
userService.getUserEvents(Globals.getAuthHeader(token), userId).enqueue(
- new DisconnectLogger<>(activity) {
+ new ErrorLogger<>(activity) {
@Override
public void onResponse(Call> call, Response> response) {
if (response.code() == HttpCodes.OK && response.body() != null) {
@@ -68,8 +85,7 @@ public void onResponse(Call> call, Response> respo
}
Log.i(TAG, "/user/{id}/events: " + response.code() + " : " + response.body());
}
- }
- );
+ });
}
@Override
@@ -85,5 +101,8 @@ public LiveData getUserLiveData() {
public LiveData> getUserEventsLiveData() {
return userEventsLiveData;
}
-}
+ public MutableLiveData getAmISubscriber() {
+ return amISubscriber;
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/viewmodel/UserViewModelModule.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/viewmodel/UserViewModelModule.java
new file mode 100644
index 0000000..92249c4
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/viewmodel/UserViewModelModule.java
@@ -0,0 +1,17 @@
+package com.tom.meeter.context.user.viewmodel;
+
+import androidx.lifecycle.ViewModel;
+
+import com.tom.meeter.infrastructure.injection.viewmodel.ViewModelKey;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.IntoMap;
+
+@Module
+public abstract class UserViewModelModule {
+ @Binds
+ @IntoMap
+ @ViewModelKey(UserViewModel.class)
+ abstract ViewModel userViewModel(UserViewModel userViewModel);
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/CommonHelper.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/CommonHelper.java
index 103582c..488418d 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/CommonHelper.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/CommonHelper.java
@@ -2,12 +2,30 @@
import android.content.Context;
+import androidx.annotation.Nullable;
+
import com.tom.meeter.R;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
public final class CommonHelper {
+
private CommonHelper() {
}
+ public static final DateTimeFormatter UI_DATE_TIME_FORMAT =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+
+ public static final DateTimeFormatter UI_DATE_FORMAT =
+ DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
+ public static final String EMPTY_STR = "";
+
public static String genderResolver(Context ctx, String gender) {
return switch (gender.toLowerCase()) {
case "female" -> ctx.getString(R.string.female_gender);
@@ -15,4 +33,44 @@ public static String genderResolver(Context ctx, String gender) {
default -> throw new IllegalArgumentException("#args " + gender);
};
}
+
+ @Nullable
+ public static CharSequence dateOrNull(OffsetDateTime date) {
+ return date == null ? null : UI_DATE_TIME_FORMAT.format(date);
+ }
+
+ @Nullable
+ public static CharSequence textOrNull(Double val) {
+ return val == null ? null : val.toString();
+ }
+
+ public static String getStringOrNull(CharSequence input) {
+ if (input == null || EMPTY_STR.contentEquals(input)) {
+ return null;
+ }
+ return input.toString();
+ }
+
+ public static Float getFloatOrNull(CharSequence input) {
+ if (input == null || EMPTY_STR.contentEquals(input)) {
+ return null;
+ }
+ return Float.valueOf(input.toString());
+ }
+
+ public static OffsetDateTime getOffsetDateTime(CharSequence input) {
+ if (input == null || EMPTY_STR.contentEquals(input)) {
+ return null;
+ }
+ LocalDateTime localDateTime = LocalDateTime.parse(input, UI_DATE_TIME_FORMAT);
+ ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
+ return zonedDateTime.toOffsetDateTime();
+ }
+
+ public static LocalDate getLocalDateOrNull(CharSequence input) {
+ if (input == null || EMPTY_STR.contentEquals(input)) {
+ return null;
+ }
+ return LocalDate.parse(input, UI_DATE_FORMAT);
+ }
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/Globals.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/Globals.java
index 158b117..b9fadf7 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/Globals.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/Globals.java
@@ -32,7 +32,6 @@ private Globals() {
public static final String AUTH_HEADER = "Authorization";
public static final String BEARER_FORMAT = "Bearer %s";
- public static final String TOKEN_KEY = "token";
private static String serverPath;
private static String socketIOPath;
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/Image/ImagesHelper.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/ImagesHelper.java
similarity index 81%
rename from AndroidClient/src/main/java/com/tom/meeter/infrastructure/Image/ImagesHelper.java
rename to AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/ImagesHelper.java
index 0328a4b..222dab4 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/Image/ImagesHelper.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/ImagesHelper.java
@@ -1,4 +1,4 @@
-package com.tom.meeter.infrastructure.Image;
+package com.tom.meeter.infrastructure.common;
import android.content.Context;
import android.graphics.Bitmap;
@@ -25,18 +25,41 @@
import java.util.Random;
+import okhttp3.ResponseBody;
+
public class ImagesHelper {
private static final FontAwesome FONT_AWESOME = new FontAwesome();
- public static Bitmap getCircleBitmap(Bitmap bitmap) {
- final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
- bitmap.getHeight(), Bitmap.Config.ARGB_8888);
+ public static Bitmap circleImage(ResponseBody body) {
+ return circleImage(body, 150, 150);
+ }
+
+ public static Bitmap circleImage(ResponseBody body, int scaleWidth, int scaleHeight) {
+ Bitmap from = from(body);
+ if (from == null) {
+ return null;
+ }
+ return getCircleBitmap(Bitmap.createScaledBitmap(from, scaleWidth, scaleHeight, true));
+ }
+
+ public static Bitmap circleImage(Bitmap src) {
+ return getCircleBitmap(Bitmap.createScaledBitmap(src, 150, 150, true));
+ }
+
+ public static Bitmap from(ResponseBody body) {
+ return BitmapFactory.decodeStream(body.byteStream());
+ }
+
+
+ public static Bitmap getCircleBitmap(Bitmap src) {
+ final Bitmap output = Bitmap.createBitmap(src.getWidth(),
+ src.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
- final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ final Rect rect = new Rect(0, 0, src.getWidth(), src.getHeight());
final RectF rectF = new RectF(rect);
paint.setAntiAlias(true);
@@ -45,9 +68,9 @@ public static Bitmap getCircleBitmap(Bitmap bitmap) {
canvas.drawOval(rectF, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(bitmap, rect, rect, paint);
+ canvas.drawBitmap(src, rect, rect, paint);
- bitmap.recycle();
+ src.recycle();
return output;
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/InfrastructureHelper.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/InfrastructureHelper.java
index 0f9b3b0..a6fbe81 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/InfrastructureHelper.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/InfrastructureHelper.java
@@ -1,6 +1,7 @@
package com.tom.meeter.infrastructure.common;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
@@ -26,6 +27,12 @@ public static void showMessage(Activity activity, String msg) {
() -> Toast.makeText(activity.getApplicationContext(), msg, Toast.LENGTH_SHORT).show());
}
+ public static void showMessage(Context ctx, String msg) {
+ if (TextUtils.isEmpty(msg))
+ return;
+ Toast.makeText(ctx, msg, Toast.LENGTH_SHORT).show();
+ }
+
public static void logMethod(String tag, Object obj) {
if (obj.getClass().isAnonymousClass()) {
Log.d(tag, obj.getClass().getName() + " " + getCurrentMethodName());
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/BaseEventAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/BaseEventAdapter.java
new file mode 100644
index 0000000..65e81c0
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/BaseEventAdapter.java
@@ -0,0 +1,72 @@
+package com.tom.meeter.infrastructure.components.adapter;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.tom.meeter.context.network.dto.EventDTO;
+import com.tom.meeter.infrastructure.components.binder.ViewHolderEventBinder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class BaseEventAdapter
+ extends RecyclerView.Adapter {
+
+ private final ViewHolderEventBinder binder;
+ private final List events = new ArrayList<>();
+
+ protected BaseEventAdapter(ViewHolderEventBinder binder) {
+ this.binder = binder;
+ }
+
+ public void setData(List newEvents) {
+ DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(
+ new EventsDiffCallback(events, newEvents));
+ this.events.clear();
+ this.events.addAll(newEvents);
+ diffResult.dispatchUpdatesTo(this);
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull T holder, int position) {
+ binder.bind(holder, events.get(position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return events.size();
+ }
+
+ static class EventsDiffCallback extends DiffUtil.Callback {
+
+ private final List oldEvents, newEvents;
+
+ public EventsDiffCallback(List oldEvents, List newEvents) {
+ this.oldEvents = oldEvents;
+ this.newEvents = newEvents;
+ }
+
+ @Override
+ public int getOldListSize() {
+ return oldEvents.size();
+ }
+
+ @Override
+ public int getNewListSize() {
+ return newEvents.size();
+ }
+
+ @Override
+ public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+ return oldEvents.get(oldItemPosition).getId()
+ .equals(newEvents.get(newItemPosition).getId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+ return oldEvents.get(oldItemPosition)
+ .equals(newEvents.get(newItemPosition));
+ }
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/EventsCardAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/EventsCardAdapter.java
new file mode 100644
index 0000000..d9495b4
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/EventsCardAdapter.java
@@ -0,0 +1,27 @@
+package com.tom.meeter.infrastructure.components.adapter;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+
+import com.tom.meeter.databinding.CardItemBinding;
+import com.tom.meeter.infrastructure.components.binder.ViewHolderEventBinder;
+import com.tom.meeter.infrastructure.components.viewholder.CardItemHolder;
+
+public class EventsCardAdapter extends BaseEventAdapter {
+
+ private static final String TAG = EventsCardAdapter.class.getCanonicalName();
+
+ public EventsCardAdapter(ViewHolderEventBinder binder) {
+ super(binder);
+ }
+
+ @NonNull
+ @Override
+ public CardItemHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new CardItemHolder(
+ CardItemBinding.inflate(
+ LayoutInflater.from(parent.getContext()), parent, false));
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/OnEventClickListener.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/OnEventClickListener.java
new file mode 100644
index 0000000..a17d354
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/OnEventClickListener.java
@@ -0,0 +1,7 @@
+package com.tom.meeter.infrastructure.components.adapter;
+
+import com.tom.meeter.context.network.dto.EventDTO;
+
+public interface OnEventClickListener {
+ void onEventClick(EventDTO event);
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/PhotoDownloaderEventBinder.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/PhotoDownloaderEventBinder.java
new file mode 100644
index 0000000..c1a6a17
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/PhotoDownloaderEventBinder.java
@@ -0,0 +1,41 @@
+package com.tom.meeter.infrastructure.components.binder;
+
+import static com.tom.meeter.infrastructure.common.ImagesHelper.circleImage;
+
+import android.content.Context;
+
+import com.tom.meeter.context.image.ImageDownloader;
+import com.tom.meeter.context.network.dto.EventDTO;
+import com.tom.meeter.infrastructure.components.adapter.OnEventClickListener;
+import com.tom.meeter.infrastructure.components.viewholder.CardItemHolder;
+
+public class PhotoDownloaderEventBinder
+ implements ViewHolderEventBinder {
+
+ private static final String TAG = PhotoDownloaderEventBinder.class.getCanonicalName();
+
+ private final Context ctx;
+ private final ImageDownloader imageDownloader;
+ private final OnEventClickListener listener;
+ private final Runnable onAuthFail;
+
+ public PhotoDownloaderEventBinder(
+ Context ctx, ImageDownloader imageDownloader,
+ OnEventClickListener listener, Runnable onAuthFail) {
+ this.ctx = ctx;
+ this.imageDownloader = imageDownloader;
+ this.listener = listener;
+ this.onAuthFail = onAuthFail;
+ }
+
+ @Override
+ public void bind(CardItemHolder holder, EventDTO event) {
+ holder.bind(event.getName(), null, (view) -> listener.onEventClick(event));
+ String photoPath = event.getPhotoPath();
+ if (photoPath == null) {
+ return;
+ }
+ imageDownloader.downloadEventImage(
+ photoPath, ctx, (photo) -> holder.updatePhoto(circleImage(photo)), onAuthFail);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/PhotoDownloaderWithCacheEventEventBinder.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/PhotoDownloaderWithCacheEventEventBinder.java
new file mode 100644
index 0000000..1469321
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/PhotoDownloaderWithCacheEventEventBinder.java
@@ -0,0 +1,66 @@
+package com.tom.meeter.infrastructure.components.binder;
+
+import static com.tom.meeter.infrastructure.common.ImagesHelper.circleImage;
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import com.tom.meeter.context.image.ImageDownloader;
+import com.tom.meeter.context.network.dto.EventDTO;
+import com.tom.meeter.infrastructure.components.adapter.OnEventClickListener;
+import com.tom.meeter.infrastructure.components.viewholder.EventViewHolder;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class PhotoDownloaderWithCacheEventEventBinder
+ implements ViewHolderEventBinder {
+
+ private static final String TAG = PhotoDownloaderWithCacheEventEventBinder.class.getCanonicalName();
+
+ private final Context ctx;
+ private final ImageDownloader imageDownloader;
+ private final OnEventClickListener listener;
+ private final Runnable onAuthFail;
+ private final Map imagesCache = new ConcurrentHashMap<>();
+
+ public PhotoDownloaderWithCacheEventEventBinder(
+ Context ctx, ImageDownloader imgDownloader,
+ OnEventClickListener listener, Runnable onAuthFail) {
+ logMethod(TAG, this);
+ this.ctx = ctx;
+ this.imageDownloader = imgDownloader;
+ this.listener = listener;
+ this.onAuthFail = onAuthFail;
+ }
+
+ @Override
+ public void bind(EventViewHolder holder, EventDTO event) {
+ String photoPath = event.getPhotoPath();
+ if (photoPath == null) {
+ holder.bind(
+ event.getName(), event.getDescription(), null,
+ (v) -> listener.onEventClick(event));
+ return;
+ }
+ Bitmap circledPhotoCache = imagesCache.get(photoPath);
+ holder.bind(
+ event.getName(), event.getDescription(), circledPhotoCache,
+ (v) -> listener.onEventClick(event));
+ if (circledPhotoCache != null) {
+ return;
+ }
+ imageDownloader.downloadEventImage(
+ photoPath, ctx,
+ photo -> {
+ Bitmap circled = circleImage(photo);
+ holder.updatePhoto(circled);
+ imagesCache.put(photoPath, circled);
+ Log.d(TAG, "PhotoDownloaderWithCacheEventEventBinder: event " +
+ "image downloaded for [" + photoPath + "], cache updated.");
+ },
+ onAuthFail);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/ViewHolderEventBinder.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/ViewHolderEventBinder.java
new file mode 100644
index 0000000..528ab09
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/ViewHolderEventBinder.java
@@ -0,0 +1,10 @@
+package com.tom.meeter.infrastructure.components.binder;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.tom.meeter.context.network.dto.EventDTO;
+
+public interface ViewHolderEventBinder {
+
+ void bind(T holder, EventDTO event);
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/CardItemHolder.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/CardItemHolder.java
new file mode 100644
index 0000000..9a2d5c3
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/CardItemHolder.java
@@ -0,0 +1,29 @@
+package com.tom.meeter.infrastructure.components.viewholder;
+
+import android.graphics.Bitmap;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.tom.meeter.databinding.CardItemBinding;
+
+public class CardItemHolder extends RecyclerView.ViewHolder {
+
+ private final CardItemBinding binding;
+
+ public CardItemHolder(CardItemBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+
+ public void bind(
+ String name, Bitmap photo, View.OnClickListener clickListener) {
+ binding.textView.setText(name);
+ binding.imageView.setImageBitmap(photo);
+ binding.card.setOnClickListener(clickListener);
+ }
+
+ public void updatePhoto(Bitmap photo) {
+ binding.imageView.setImageBitmap(photo);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/EventViewHolder.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/EventViewHolder.java
similarity index 68%
rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/EventViewHolder.java
rename to AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/EventViewHolder.java
index 0d62b6c..7c09313 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/EventViewHolder.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/EventViewHolder.java
@@ -1,4 +1,4 @@
-package com.tom.meeter.context.profile.adapter;
+package com.tom.meeter.infrastructure.components.viewholder;
import android.graphics.Bitmap;
import android.view.View;
@@ -8,6 +8,7 @@
import com.tom.meeter.databinding.EventViewBinding;
public class EventViewHolder extends RecyclerView.ViewHolder {
+
private final EventViewBinding binding;
public EventViewHolder(EventViewBinding binding) {
@@ -15,10 +16,16 @@ public EventViewHolder(EventViewBinding binding) {
this.binding = binding;
}
- public void bind(String name, String description, Bitmap photo, View.OnClickListener clickListener) {
+ public void bind(
+ String name, String description, Bitmap photo,
+ View.OnClickListener clickListener) {
binding.eventNameCardView.setText(name);
binding.eventDescriptionCardView.setText(description);
binding.eventPhotoCardView.setImageBitmap(photo);
binding.eventCardView.setOnClickListener(clickListener);
}
+
+ public void updatePhoto(Bitmap photo) {
+ binding.eventPhotoCardView.setImageBitmap(photo);
+ }
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/ActivityRestarterOnAuthFailure.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/ActivityRestarterOnAuthFailure.java
index 7e42a26..cfa13ea 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/ActivityRestarterOnAuthFailure.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/ActivityRestarterOnAuthFailure.java
@@ -7,7 +7,8 @@
import retrofit2.Call;
import retrofit2.Response;
-public class ActivityRestarterOnAuthFailure extends DisconnectLogger {
+public class ActivityRestarterOnAuthFailure extends HttpErrorLogger {
+
private static final String TAG = ActivityRestarterOnAuthFailure.class.getCanonicalName();
private final Fragment fragment;
@@ -18,6 +19,7 @@ public ActivityRestarterOnAuthFailure(Fragment fragment) {
@Override
public void onResponse(Call call, Response response) {
+ super.onResponse(call, response);
if (response.code() == HttpCodes.NOT_AUTHENTICATED) {
InfrastructureHelper.restartActivityFromFragment(fragment);
// TODO when restore from persisted state will be done, if necessary
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/DisconnectLogger.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/DisconnectLogger.java
index 1f8c54e..bee1312 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/DisconnectLogger.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/DisconnectLogger.java
@@ -6,6 +6,9 @@
import com.tom.meeter.R;
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+
import retrofit2.Call;
import retrofit2.Callback;
@@ -21,9 +24,19 @@ public DisconnectLogger(Context ctx) {
@Override
public void onFailure(Call call, Throwable t) {
- Toast.makeText(ctx, R.string.server_is_unreachable, Toast.LENGTH_SHORT).show();
- Log.i(TAG, "DisconnectLogger for " + ctx.getPackageName()
- + " : " + ctx.getResources().getString(R.string.server_is_unreachable)
- + ", error: " + t.getMessage());
+ if (supportedErrorMapping(t)) {
+ Toast.makeText(ctx, R.string.server_is_unreachable, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "DisconnectLogger for " + ctx.getClass().getSimpleName()
+ + " : " + ctx.getResources().getString(R.string.server_is_unreachable)
+ + ", error: " + t.getMessage());
+ }
+ }
+
+ protected boolean supportedErrorMapping(Throwable t) {
+ if (t instanceof SocketTimeoutException
+ || t instanceof ConnectException) {
+ return true;
+ }
+ return false;
}
}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/ErrorLogger.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/ErrorLogger.java
new file mode 100644
index 0000000..2e8e51e
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/ErrorLogger.java
@@ -0,0 +1,32 @@
+package com.tom.meeter.infrastructure.http;
+
+import android.content.Context;
+import android.util.Log;
+import android.widget.Toast;
+
+import retrofit2.Call;
+
+public abstract class ErrorLogger extends DisconnectLogger {
+
+ private static final String TAG = ErrorLogger.class.getCanonicalName();
+
+ private final Context ctx;
+
+ public ErrorLogger(Context ctx) {
+ super(ctx);
+ this.ctx = ctx;
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ if (super.supportedErrorMapping(t)) {
+ super.onFailure(call, t);
+ } else {
+ String message = t.getMessage();
+ Toast.makeText(ctx, "ErrorLogger: " + message, Toast.LENGTH_LONG)
+ .show();
+ Log.e(TAG, "ErrorLogger for " + ctx.getClass().getSimpleName()
+ + ", error: " + message);
+ }
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/HttpClient.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/HttpClient.java
index cff713c..7a0dc9b 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/HttpClient.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/HttpClient.java
@@ -1,61 +1,58 @@
package com.tom.meeter.infrastructure.http;
-import android.util.Log;
-
-import org.jetbrains.annotations.NotNull;
+import static com.tom.meeter.infrastructure.common.Globals.AUTH_HEADER;
-import java.io.IOException;
+import android.util.Log;
import okhttp3.Call;
-import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
-import okhttp3.Response;
public class HttpClient {
- private static final String TAG = HttpClient.class.getCanonicalName();
- private static final MediaType JSON = MediaType.get("application/json");
- private static final Callback CALLBACK_LOGGER = new Callback() {
- @Override
- public void onFailure(@NotNull Call call, @NotNull IOException e) {
- Log.d(TAG, "IOException: ", e);
- }
+ private final String serverUrl;
- @Override
- public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
- Log.d(TAG, "usersResp: " + response.body().string());
- }
- };
+ public HttpClient(String serverUrl) {
+ this.serverUrl = serverUrl;
+ }
+
+ private static final String TAG = HttpClient.class.getCanonicalName();
+ private static final MediaType JSON = MediaType.get("application/json");
private final OkHttpClient client = new OkHttpClient();
- public String post(String url, String json) throws IOException {
- RequestBody body = RequestBody.create(JSON, json);
- Request request = new Request.Builder()
- .url(url)
- .post(body)
- .build();
- try (Response response = client.newCall(request).execute()) {
- return response.body().string();
- }
+ public Call post(String json) {
+ return post(json, serverUrl);
+ }
+
+ public Call post(String url, String json) {
+ return client.newCall(
+ new Request.Builder()
+ .url(url)
+ .post(RequestBody.create(json, JSON))
+ .build());
}
- public Call get(String url, Callback callback) {
+ public Call get(String url) {
Log.d(TAG, "Making GET: '" + url + "'...");
- Request request = new Request.Builder()
- .url(url)
- .get()
- .build();
- Call call = client.newCall(request);
- call.enqueue(callback);
- return call;
+ return client.newCall(
+ new Request.Builder()
+ .url(url)
+ .get()
+ .build());
}
- public void get(String url) {
- get(url, CALLBACK_LOGGER);
+ public Call patch(String json, String url, String authHeader) {
+ String fullPath = serverUrl + url;
+ Log.d(TAG, "Making PATCH: '" + fullPath + "' ...");
+ return client.newCall(
+ new Request.Builder()
+ .url(fullPath)
+ .header(AUTH_HEADER, authHeader)
+ .patch(RequestBody.create(json, JSON))
+ .build());
}
//new HttpClient().get(initServerPath(getBaseContext()) + "/users");
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/HttpErrorLogger.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/HttpErrorLogger.java
new file mode 100644
index 0000000..95b3b08
--- /dev/null
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/http/HttpErrorLogger.java
@@ -0,0 +1,40 @@
+package com.tom.meeter.infrastructure.http;
+
+import static com.tom.meeter.infrastructure.common.InfrastructureHelper.showMessage;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.IOException;
+
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.Response;
+
+public class HttpErrorLogger extends ErrorLogger {
+
+ private static final String TAG = HttpErrorLogger.class.getCanonicalName();
+
+ private final Context ctx;
+
+ public HttpErrorLogger(Context ctx) {
+ super(ctx);
+ this.ctx = ctx;
+ }
+
+ @Override
+ public void onResponse(Call call, Response response) {
+ if (response.isSuccessful()) {
+ return;
+ }
+ String errorMessage;
+ try (ResponseBody r = response.errorBody()) {
+ errorMessage = r.string();
+ } catch (IOException e) {
+ errorMessage = "Unable to extract error body.";
+ }
+ int code = response.code();
+ showMessage(ctx, code + "/" + errorMessage);
+ Log.e(TAG, "HTTP request error: " + code + "/" + errorMessage);
+ }
+}
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/injection/viewmodel/ViewModelKey.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/injection/viewmodel/ViewModelKey.java
index 3c07fec..822e2ff 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/injection/viewmodel/ViewModelKey.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/injection/viewmodel/ViewModelKey.java
@@ -12,6 +12,6 @@
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
-@interface ViewModelKey {
+public @interface ViewModelKey {
Class extends ViewModel> value();
}
\ No newline at end of file
diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/injection/viewmodel/ViewModelModule.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/injection/viewmodel/ViewModelModule.java
index 3080e89..56b3568 100644
--- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/injection/viewmodel/ViewModelModule.java
+++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/injection/viewmodel/ViewModelModule.java
@@ -4,12 +4,8 @@
import androidx.lifecycle.ViewModel;
-import com.tom.meeter.context.event.viewmodel.EventViewModel;
import com.tom.meeter.context.profile.viewmodel.ProfileEventsViewModel;
import com.tom.meeter.context.profile.viewmodel.ProfileViewModel;
-import com.tom.meeter.context.profile.viewmodel.UserEventsViewModel;
-import com.tom.meeter.context.profile.viewmodel.UserProfileViewModel;
-import com.tom.meeter.context.user.viewmodel.UserViewModel;
import dagger.Binds;
import dagger.Module;
@@ -24,16 +20,6 @@ public ViewModelModule() {
Log.d(TAG, "Configuring ViewModelModule...");
}
- @Binds
- @IntoMap
- @ViewModelKey(UserProfileViewModel.class)
- abstract ViewModel userProfileViewModel(UserProfileViewModel userProfileViewModel);
-
- @Binds
- @IntoMap
- @ViewModelKey(UserEventsViewModel.class)
- abstract ViewModel userEventViewModel(UserEventsViewModel userEventsViewModel);
-
@Binds
@IntoMap
@ViewModelKey(ProfileViewModel.class)
@@ -43,15 +29,4 @@ public ViewModelModule() {
@IntoMap
@ViewModelKey(ProfileEventsViewModel.class)
abstract ViewModel profileEventsViewModel(ProfileEventsViewModel profileEventsViewModel);
-
- @Binds
- @IntoMap
- @ViewModelKey(UserViewModel.class)
- abstract ViewModel userViewModel(UserViewModel userViewModel);
-
- @Binds
- @IntoMap
- @ViewModelKey(EventViewModel.class)
- abstract ViewModel eventViewModel(EventViewModel eventViewModel);
-
}
diff --git a/AndroidClient/src/main/res/drawable/bg_button_selected.xml b/AndroidClient/src/main/res/drawable/bg_button_selected.xml
new file mode 100644
index 0000000..cecfec4
--- /dev/null
+++ b/AndroidClient/src/main/res/drawable/bg_button_selected.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/AndroidClient/src/main/res/drawable/bg_button_unselected.xml b/AndroidClient/src/main/res/drawable/bg_button_unselected.xml
new file mode 100644
index 0000000..215c9fe
--- /dev/null
+++ b/AndroidClient/src/main/res/drawable/bg_button_unselected.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AndroidClient/src/main/res/layout/activity_event_editable.xml b/AndroidClient/src/main/res/layout/activity_event_editable.xml
new file mode 100644
index 0000000..102f0f0
--- /dev/null
+++ b/AndroidClient/src/main/res/layout/activity_event_editable.xml
@@ -0,0 +1,272 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AndroidClient/src/main/res/layout/activity_event_on_map.xml b/AndroidClient/src/main/res/layout/activity_event_on_map.xml
new file mode 100644
index 0000000..b79180b
--- /dev/null
+++ b/AndroidClient/src/main/res/layout/activity_event_on_map.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/AndroidClient/src/main/res/layout/activity_event_position.xml b/AndroidClient/src/main/res/layout/activity_event_position.xml
new file mode 100644
index 0000000..c2ba2bf
--- /dev/null
+++ b/AndroidClient/src/main/res/layout/activity_event_position.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AndroidClient/src/main/res/layout/activity_event_readable.xml b/AndroidClient/src/main/res/layout/activity_event_readable.xml
new file mode 100644
index 0000000..fc24b83
--- /dev/null
+++ b/AndroidClient/src/main/res/layout/activity_event_readable.xml
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AndroidClient/src/main/res/layout/activity_user.xml b/AndroidClient/src/main/res/layout/activity_user.xml
new file mode 100644
index 0000000..2780fe7
--- /dev/null
+++ b/AndroidClient/src/main/res/layout/activity_user.xml
@@ -0,0 +1,217 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AndroidClient/src/main/res/layout/activity_user_profile_old.xml b/AndroidClient/src/main/res/layout/activity_user_profile_old.xml
new file mode 100644
index 0000000..a04cf68
--- /dev/null
+++ b/AndroidClient/src/main/res/layout/activity_user_profile_old.xml
@@ -0,0 +1,102 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AndroidClient/src/main/res/layout/card_item.xml b/AndroidClient/src/main/res/layout/card_item.xml
index 3fa147a..62a5a73 100644
--- a/AndroidClient/src/main/res/layout/card_item.xml
+++ b/AndroidClient/src/main/res/layout/card_item.xml
@@ -4,6 +4,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
+ android:id="@+id/card"
app:cardCornerRadius="24dp"
app:cardElevation="5dp"
app:cardUseCompatPadding="true">
diff --git a/AndroidClient/src/main/res/layout/event_layout.xml b/AndroidClient/src/main/res/layout/event_layout_editable.xml
similarity index 66%
rename from AndroidClient/src/main/res/layout/event_layout.xml
rename to AndroidClient/src/main/res/layout/event_layout_editable.xml
index bdf5edf..b9a7f75 100644
--- a/AndroidClient/src/main/res/layout/event_layout.xml
+++ b/AndroidClient/src/main/res/layout/event_layout_editable.xml
@@ -37,21 +37,40 @@
android:text="@string/creator"
android:textAllCaps="false" />
-
+ android:layout_below="@id/event_creator_id_btn"
+ android:orientation="horizontal">
-
+
+
+
+
+
+
diff --git a/AndroidClient/src/main/res/layout/fragment_profile.xml b/AndroidClient/src/main/res/layout/fragment_profile.xml
index bddc852..0f7d262 100644
--- a/AndroidClient/src/main/res/layout/fragment_profile.xml
+++ b/AndroidClient/src/main/res/layout/fragment_profile.xml
@@ -2,8 +2,8 @@
+ android:layout_height="match_parent"
+ android:background="@color/c0deef">
+
- />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
-
+ android:layout_below="@+id/birthday"
+ android:orientation="horizontal">
-
+
+
+
+
+
+
+
+
+ android:layout_below="@+id/btnEdit"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="20dp"
+ android:hint="Your events"
+ android:inputType="none"
+ android:textAlignment="center"
+ android:textSize="20sp" />
-
diff --git a/AndroidClient/src/main/res/layout/fragment_profile_old.xml b/AndroidClient/src/main/res/layout/fragment_profile_old.xml
new file mode 100644
index 0000000..c55e05b
--- /dev/null
+++ b/AndroidClient/src/main/res/layout/fragment_profile_old.xml
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AndroidClient/src/main/res/layout/user_layout.xml b/AndroidClient/src/main/res/layout/user_layout.xml
deleted file mode 100644
index 5e38cf4..0000000
--- a/AndroidClient/src/main/res/layout/user_layout.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-
-
-
-
-
-
-
-
-
- />
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/AndroidClient/src/main/res/values-en/strings.xml b/AndroidClient/src/main/res/values-en/strings.xml
index c3708de..ba86a7c 100644
--- a/AndroidClient/src/main/res/values-en/strings.xml
+++ b/AndroidClient/src/main/res/values-en/strings.xml
@@ -162,6 +162,7 @@
Refreshing the token.
Event information.
Creator
+ New event!
- 360p
diff --git a/AndroidClient/src/main/res/values-ru-rRU/strings.xml b/AndroidClient/src/main/res/values-ru-rRU/strings.xml
index b617c75..387de85 100644
--- a/AndroidClient/src/main/res/values-ru-rRU/strings.xml
+++ b/AndroidClient/src/main/res/values-ru-rRU/strings.xml
@@ -161,6 +161,7 @@
title_copyable_preference
Информация о событии.
Создатель
+ Новое событие!
- 360p
diff --git a/AndroidClient/src/main/res/values/strings.xml b/AndroidClient/src/main/res/values/strings.xml
index 1ad63e6..e635c40 100644
--- a/AndroidClient/src/main/res/values/strings.xml
+++ b/AndroidClient/src/main/res/values/strings.xml
@@ -160,6 +160,7 @@
Refreshing the token.
Event information.
Creator
+ New event!
- 360p
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 8ca2af6..c9a453c 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,17 +2,18 @@
agp = "8.10.1"
#android
-appcompat = "1.0.0"
-recyclerview = "1.0.0"
+appcompat = "1.7.1"
+core = "1.16.0"
+recyclerview = "1.4.0"
cardview = "1.0.0"
lifecycleextensions = "2.0.0"
lifecyclecompiler = "2.0.0"
-material = "1.0.0"
+material = "1.12.0"
#Core
playServicesMaps = "19.2.0"
-retrofit = "2.5.0"
+retrofit = "3.0.0"
room = "2.0.0"
dagger = "2.56"
eventbus = "3.3.1"
@@ -24,7 +25,7 @@ rxjava = "2.2.6"
socketIoClient = "2.1.2"
gson = "2.8.5"
-jacksonCore = "2.4.1"
+jackson = "2.19.0"
desugar_jdk_libs = "2.0.3"
@@ -45,14 +46,15 @@ junit = "4.13.2"
[libraries]
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
+core = { module = "androidx.core:core", version.ref = "core" }
recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
material = { module = "com.google.android.material:material", version.ref = "material" }
cardview = { module = "androidx.cardview:cardview", version.ref = "cardview" }
-lifecycleextensions = { module = "androidx.lifecycle:lifecycle-extensions", version.ref = "lifecycleextensions" }
-lifecyclecompiler = { module = "androidx.lifecycle:lifecycle-compiler", version.ref = "lifecyclecompiler" }
+lifecycle-extensions = { module = "androidx.lifecycle:lifecycle-extensions", version.ref = "lifecycleextensions" }
+lifecycle-compiler = { module = "androidx.lifecycle:lifecycle-compiler", version.ref = "lifecyclecompiler" }
dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" }
-daggercompiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
+dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
eventbus = { module = "org.greenrobot:eventbus", version.ref = "eventbus" }
materialdrawer = { module = "com.mikepenz:materialdrawer", version.ref = "materialdrawer" }
socketioclient = { module = "io.socket:socket.io-client", version.ref = "socketIoClient" }
@@ -61,19 +63,21 @@ gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
play-services-maps = { module = "com.google.android.gms:play-services-maps", version.ref = "playServicesMaps" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
-retrofitconvertergson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
+retrofit-converter-jackson = { module = "com.squareup.retrofit2:converter-jackson", version.ref = "retrofit" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
rxjava = { module = "io.reactivex.rxjava2:rxjava", version.ref = "rxjava" }
rxandroid = { module = "io.reactivex.rxjava2:rxandroid", version.ref = "rxandroid" }
-roomruntime = { module = "androidx.room:room-runtime", version.ref = "room" }
-roomcompiler = { module = "androidx.room:room-compiler", version.ref = "room" }
-roomrxjava2 = { module = "androidx.room:room-rxjava2", version.ref = "room" }
+room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
+room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
+room-rxjava2 = { module = "androidx.room:room-rxjava2", version.ref = "room" }
-jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jacksonCore" }
-jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jacksonCore" }
-jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jacksonCore" }
+jackson-datatype-jdk8 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jdk8", version.ref = "jackson" }
+jackson-datatype-jsr310 = { module = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", version.ref = "jackson" }
+jackson-annotations = { module = "com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" }
+jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson" }
+jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }