diff --git a/AndroidClient/src/main/AndroidManifest.xml b/AndroidClient/src/main/AndroidManifest.xml index 4bb5deb..bc53dd3 100644 --- a/AndroidClient/src/main/AndroidManifest.xml +++ b/AndroidClient/src/main/AndroidManifest.xml @@ -31,7 +31,7 @@ + android:parentActivityName=".context.profile.component.activity.ProfileActivity" /> - - + + - + (15, false)); - } - - @Singleton - @NonNull - @Provides - public EventDatabase provideEventDb(Application app) { - return Room.databaseBuilder(app, EventDatabase.class, "event.db") - .fallbackToDestructiveMigration() - .build(); - } - - @Singleton - @NonNull - @Provides - public EventDao provideEventDao(EventDatabase eventDatabase) { - return eventDatabase.eventDao(); - } - - @Singleton - @NonNull - @Provides - public SettingsService provideSettingsService(Application app) { - return new Retrofit.Builder() - .baseUrl(getServerPath(app)) - .addConverterFactory(JacksonConverterFactory.create()) - //.addConverterFactory(GsonConverterFactory.create()) - .build() - .create(SettingsService.class); + public HttpClient provideHttpClient(Application app) { + return new HttpClient(getServerPath(app)); } @Singleton @NonNull @Provides - public HttpClient provideHttpClient(Application app) { - return new HttpClient(getServerPath(app)); + public UserLoader provideUserLoader(Application app) { + return createBuilder(app, RetrofitBuilder.jtm).create(UserLoader.class); } } diff --git a/AndroidClient/src/main/java/com/tom/meeter/EventModule.java b/AndroidClient/src/main/java/com/tom/meeter/EventModule.java deleted file mode 100644 index 2708b1f..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/EventModule.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.tom.meeter; - -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.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 java.util.TimeZone; - -import javax.inject.Singleton; - -import dagger.Module; -import dagger.Provides; -import retrofit2.Retrofit; -import retrofit2.converter.jackson.JacksonConverterFactory; - -@Module -public class EventModule { - - private static final String TAG = EventModule.class.getCanonicalName(); - - public EventModule() { - logMethod(TAG, this); - } - - @Singleton - @NonNull - @Provides - public EventService provideEventService(Application app) { - return new Retrofit.Builder() - .baseUrl(getServerPath(app)) - .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); - } -} \ No newline at end of file diff --git a/AndroidClient/src/main/java/com/tom/meeter/TokenModule.java b/AndroidClient/src/main/java/com/tom/meeter/TokenModule.java index 0cc023f..28d8532 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/TokenModule.java +++ b/AndroidClient/src/main/java/com/tom/meeter/TokenModule.java @@ -15,7 +15,6 @@ import dagger.Provides; import retrofit2.Retrofit; import retrofit2.converter.jackson.JacksonConverterFactory; -//import retrofit2.converter.gson.GsonConverterFactory; @Module public class TokenModule { @@ -33,7 +32,6 @@ public TokenService providesTokenService(Application app) { return new Retrofit.Builder() .baseUrl(getServerPath(app)) .addConverterFactory(JacksonConverterFactory.create()) - //.addConverterFactory(GsonConverterFactory.create()) .build() .create(TokenService.class); } diff --git a/AndroidClient/src/main/java/com/tom/meeter/UserModule.java b/AndroidClient/src/main/java/com/tom/meeter/UserModule.java deleted file mode 100644 index 544d78b..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/UserModule.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.tom.meeter; - -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 javax.inject.Singleton; - -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); - } - - @Singleton - @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/auth/AuthModule.java b/AndroidClient/src/main/java/com/tom/meeter/context/auth/AuthModule.java index 9878e94..dca1d12 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 @@ -32,7 +32,6 @@ public AuthService provideAuthService(Application app) { return new Retrofit.Builder() .baseUrl(getServerPath(app)) .addConverterFactory(JacksonConverterFactory.create()) - //.addConverterFactory(GsonConverterFactory.create()) .build() .create(AuthService.class); } 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 bbd65e3..0c24cf6 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 @@ -13,7 +13,10 @@ import dagger.Component; @EventScope -@Component(dependencies = {AppComponent.class}) +@Component( + modules = {EventModule.class}, + dependencies = {AppComponent.class} +) public interface EventComponent { @Component.Builder 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 new file mode 100644 index 0000000..b1a5ba5 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/EventModule.java @@ -0,0 +1,30 @@ +package com.tom.meeter.context.event; + +import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; + +import android.app.Application; + +import androidx.annotation.NonNull; + +import com.tom.meeter.context.event.service.EventService; +import com.tom.meeter.infrastructure.common.RetrofitBuilder; + +import dagger.Module; +import dagger.Provides; + +@Module +public class EventModule { + + private static final String TAG = EventModule.class.getCanonicalName(); + + public EventModule() { + logMethod(TAG, this); + } + + @EventScope + @NonNull + @Provides + public EventService provideEventService(Application app) { + return RetrofitBuilder.createBuilder(app).create(EventService.class); + } +} 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 index 5325182..f063b5a 100644 --- 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 @@ -1,7 +1,7 @@ package com.tom.meeter.context.event.activity; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader; -import static com.tom.meeter.context.profile.fragment.GoogleMapsFragment.ZOOM_VALUE; +import static com.tom.meeter.context.profile.component.fragment.GoogleMapsFragment.ZOOM_VALUE; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.showMessage; 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 index f409439..1a2f771 100644 --- 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 @@ -1,7 +1,7 @@ package com.tom.meeter.context.event.activity; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader; -import static com.tom.meeter.context.profile.fragment.GoogleMapsFragment.ZOOM_VALUE; +import static com.tom.meeter.context.profile.component.fragment.GoogleMapsFragment.ZOOM_VALUE; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.showMessage; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/ProfileEventActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/ProfileEventActivity.java index 17b0f62..f9e07c5 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/ProfileEventActivity.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/ProfileEventActivity.java @@ -12,6 +12,7 @@ import static com.tom.meeter.context.image.activity.BaseUploadActivity.PHOTO_PATH_RESULT; 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.handleEventStatus; import static com.tom.meeter.infrastructure.common.CommonHelper.textOrNull; import static com.tom.meeter.infrastructure.common.DateHelper.showDateTimePicker; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; @@ -44,7 +45,7 @@ import com.tom.meeter.context.image.ImageDownloader; import com.tom.meeter.context.image.activity.UploadEventImageActivity; import com.tom.meeter.context.network.dto.EventDTO; -import com.tom.meeter.context.profile.activity.ProfileActivity; +import com.tom.meeter.context.profile.component.activity.ProfileActivity; import com.tom.meeter.context.token.service.TokenService; import com.tom.meeter.databinding.ActivityEventEditableBinding; import com.tom.meeter.infrastructure.common.ImagesHelper; @@ -258,6 +259,7 @@ public void onResponse(Call call, Response resp) { private void updateLayout() { binding.photoPath.setText(eventCache.getPhotoPath()); + handleEventStatus(this, binding.status, eventCache.getStatus()); binding.name.setText(eventCache.getName()); binding.eventCreated.setText(UI_DATE_TIME_FORMAT.format(eventCache.getCreated())); diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/UserEventActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/UserEventActivity.java index e3c1158..0efff9d 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/UserEventActivity.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/event/activity/UserEventActivity.java @@ -7,6 +7,7 @@ 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.handleEventStatus; import static com.tom.meeter.infrastructure.common.CommonHelper.textOrNull; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; @@ -108,6 +109,7 @@ private void initLayout(EventDTO event) { binding.locationMapButton.setOnClickListener( v -> dispatchToEventOnMapActivity(this, event.getId())); + handleEventStatus(this, binding.status, event.getStatus()); binding.name.setText(event.getName()); binding.eventCreated.setText(UI_DATE_TIME_FORMAT.format(event.getCreated())); binding.description.setText(event.getDescription()); diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/gps/service/LocationTrackerService.java b/AndroidClient/src/main/java/com/tom/meeter/context/gps/service/LocationTrackerService.java index 9b03466..d10cdba 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/gps/service/LocationTrackerService.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/gps/service/LocationTrackerService.java @@ -201,7 +201,7 @@ public void removeLocationTrackerListener(LocationTrackerListener me) { } private void dumpCurrentListeners() { - logMethod(TAG, this); + logMethod(TAG, this, "listeners size: " + listeners.size()); for (LocationTrackerListener l : listeners) { Log.d(TAG, l.toString()); } 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 5ecc578..f9d9e85 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,7 +24,7 @@ import com.tom.meeter.App; import com.tom.meeter.R; -import com.tom.meeter.context.profile.activity.ProfileActivity; +import com.tom.meeter.context.profile.component.activity.ProfileActivity; import com.tom.meeter.context.token.service.TokenService; import com.tom.meeter.databinding.LauncherBinding; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/launcher/StartActivityTemp.java b/AndroidClient/src/main/java/com/tom/meeter/context/launcher/StartActivityTemp.java index 9c10516..24d7076 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/launcher/StartActivityTemp.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/launcher/StartActivityTemp.java @@ -9,7 +9,7 @@ import androidx.fragment.app.Fragment; import com.tom.meeter.R; -import com.tom.meeter.context.profile.fragment.ProfileFragment; +import com.tom.meeter.context.profile.component.fragment.ProfileFragment; import com.tom.meeter.databinding.StartActivityTempBinding; @Deprecated diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/domain/SearchForEvents.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/domain/SearchForEvents.java index dd63265..943b02f 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/network/domain/SearchForEvents.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/domain/SearchForEvents.java @@ -1,40 +1,48 @@ package com.tom.meeter.context.network.domain; -import androidx.annotation.NonNull; +import com.tom.meeter.context.network.dto.EventDTO; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.Set; + /** * Created by Tom on 14.01.2017. */ -public record SearchForEvents(double latitude, double longitude, int distance) +public record SearchForEvents( + double latitude, double longitude, int distance, + Set status) implements NetworkEvent { private static final String LATITUDE_KEY = "latitude"; private static final String LONGITUDE_KEY = "longitude"; private static final String DISTANCE_KEY = "distance"; + private static final String STATUS_KEY = "status"; - @NonNull @Override public String toString() { return "SearchForEvents{" + "latitude=" + latitude + ", longitude=" + longitude + ", distance=" + distance + + ", status=" + status + '}'; } @Override public JSONObject toJson() { try { - return new JSONObject() + JSONObject result = new JSONObject() .put(LATITUDE_KEY, latitude) .put(LONGITUDE_KEY, longitude) .put(DISTANCE_KEY, distance); + result.put(STATUS_KEY, new JSONArray(EventDTO.EventStatus.transformToStrings(status))); + return result; } catch (JSONException e) { - throw new RuntimeException("Unable to call SearchForEvents.toJson(): ", e); + throw new RuntimeException("Unable to encode SearchForEvents to json: ", e); } } } diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/EntityBase.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/BaseEntity.java similarity index 54% rename from AndroidClient/src/main/java/com/tom/meeter/context/network/dto/EntityBase.java rename to AndroidClient/src/main/java/com/tom/meeter/context/network/dto/BaseEntity.java index dfe8387..9e4a1eb 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/EntityBase.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/BaseEntity.java @@ -1,5 +1,6 @@ package com.tom.meeter.context.network.dto; -public interface EntityBase { +public interface BaseEntity { + String ID_KEY = "id"; String getId(); } 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 2365aaf..8e6b36d 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 @@ -4,21 +4,23 @@ import static com.tom.meeter.infrastructure.common.JsonHelper.getOffsetDateTimeOrNull; import static com.tom.meeter.infrastructure.common.JsonHelper.getStringOrNull; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import org.json.JSONException; import org.json.JSONObject; import java.time.OffsetDateTime; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; /** * created by Tom on 10.02.2017. */ -public class EventDTO implements EntityBase { +@JsonIgnoreProperties(ignoreUnknown = true) +public class EventDTO extends ServerEntityBase { - private static final String EVENT_ID_KEY = "id"; - private static final String NAME_KEY = "name"; private static final String DESCRIPTION_KEY = "description"; private static final String CREATOR_ID_KEY = "creator_id"; private static final String LATITUDE_KEY = "latitude"; @@ -26,18 +28,14 @@ public class EventDTO implements EntityBase { 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 static final String STATUS_KEY = "status"; //Non nullable, cannot be changed - private String id; @JsonProperty(value = CREATOR_ID_KEY) private String creatorId; private OffsetDateTime created; - //Non nullable, can be changed - private String name; - //Nullable private String description; private Double latitude; @@ -45,34 +43,30 @@ public class EventDTO implements EntityBase { private OffsetDateTime starting; private OffsetDateTime ending; private String city; - @JsonProperty(value = PHOTO_PATH_KEY) - private String photoPath; + private EventStatus status; + + public EventDTO() { + //retrofit... + } - public static EventDTO encode(JSONObject json) { - EventDTO result = new EventDTO(); + public EventDTO(JSONObject json) { + super(json); try { //Non nullable. - result.id = json.getString(EVENT_ID_KEY); - result.name = json.getString(NAME_KEY); - result.creatorId = json.getString(CREATOR_ID_KEY); - result.created = OffsetDateTime.parse(json.getString(CREATED_KEY)); + creatorId = json.getString(CREATOR_ID_KEY); + 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); + description = getStringOrNull(DESCRIPTION_KEY, json); + latitude = getDoubleOrNull(LATITUDE_KEY, json); + longitude = getDoubleOrNull(LONGITUDE_KEY, json); + starting = getOffsetDateTimeOrNull(STARTING_KEY, json); + ending = getOffsetDateTimeOrNull(ENDING_KEY, json); + city = getStringOrNull(CITY_KEY, json); + status = EventStatus.fromString(json.getString(STATUS_KEY)); } catch (JSONException e) { throw new RuntimeException("Unable to encode EventDTO from jsonObject: ", e); } - return result; - } - - public void setName(String name) { - this.name = name; } public void setLatitude(Double latitude) { @@ -83,22 +77,10 @@ public void setLongitude(Double longitude) { this.longitude = longitude; } - public void setId(String id) { - this.id = id; - } - public void setDescription(String description) { this.description = description; } - public void setCreatorId(String creatorId) { - this.creatorId = creatorId; - } - - public void setCreated(OffsetDateTime created) { - this.created = created; - } - public void setStarting(OffsetDateTime starting) { this.starting = starting; } @@ -111,17 +93,8 @@ public void setCity(String city) { this.city = city; } - public void setPhotoPath(String photoPath) { - this.photoPath = photoPath; - } - - @Override - public String getId() { - return id; - } - - public String getName() { - return name; + public void setStatus(EventStatus eventStatus) { + this.status = eventStatus; } public String getDescription() { @@ -152,35 +125,108 @@ public OffsetDateTime getEnding() { return ending; } - public String getPhotoPath() { - return photoPath; - } - public String getCity() { return city; } + public EventStatus getStatus() { + return status; + } + @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; EventDTO eventDTO = (EventDTO) o; - return Objects.equals(id, eventDTO.id) - && Objects.equals(name, eventDTO.name) + return Objects.equals(creatorId, eventDTO.creatorId) + && Objects.equals(created, eventDTO.created) && 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(city, eventDTO.city) - && Objects.equals(photoPath, eventDTO.photoPath); + && Objects.equals(status, eventDTO.status); } @Override public int hashCode() { return Objects.hash( - id, name, description, latitude, longitude, creatorId, - created, starting, ending, city, photoPath); + super.hashCode(), creatorId, created, + description, latitude, longitude, + starting, ending, city, status); + } + + + private static final String CREATED_VALUE = "CREATED"; + private static final String PUBLISHED_VALUE = "PUBLISHED"; + private static final String UNPUBLISHED_VALUE = "UNPUBLISHED"; + private static final String SCHEDULED_VALUE = "SCHEDULED"; + private static final String STARTED_VALUE = "STARTED"; + private static final String PAUSED_VALUE = "PAUSED"; + private static final String RESUMED_VALUE = "RESUMED"; + private static final String FINISHED_VALUE = "FINISHED"; + private static final String CANCELLED_VALUE = "CANCELLED"; + private static final String ARCHIVED_VALUE = "ARCHIVED"; + + public enum EventStatus { + + @JsonProperty(CREATED_VALUE) + CREATED(CREATED_VALUE), + @JsonProperty(PUBLISHED_VALUE) + PUBLISHED(PUBLISHED_VALUE), + @JsonProperty(UNPUBLISHED_VALUE) + UNPUBLISHED(UNPUBLISHED_VALUE), + @JsonProperty(SCHEDULED_VALUE) + SCHEDULED(SCHEDULED_VALUE), + @JsonProperty(STARTED_VALUE) + STARTED(STARTED_VALUE), + @JsonProperty(PAUSED_VALUE) + PAUSED(PAUSED_VALUE), + @JsonProperty(RESUMED_VALUE) + RESUMED(RESUMED_VALUE), + @JsonProperty(FINISHED_VALUE) + FINISHED(FINISHED_VALUE), + @JsonProperty(CANCELLED_VALUE) + CANCELLED(CANCELLED_VALUE), + @JsonProperty(ARCHIVED_VALUE) + ARCHIVED(ARCHIVED_VALUE); + + private final String value; + + EventStatus(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static EventStatus fromString(String text) { + for (EventStatus val : EventStatus.values()) { + if (val.value.equalsIgnoreCase(text)) { + return val; + } + } + throw new IllegalArgumentException("No enum constant with string value " + text); + } + + public static Set transformToStrings( + Set statuses) { + Set result = new HashSet<>(statuses.size()); + for (EventDTO.EventStatus status : statuses) { + result.add(status.getValue()); + } + return result; + } + + public static Set transformToEnums( + Set statuses) { + Set result = new HashSet<>(statuses.size()); + for (String status : statuses) { + result.add(EventDTO.EventStatus.fromString(status)); + } + return result; + } } } diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/ServerEntity.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/ServerEntity.java new file mode 100644 index 0000000..f7a46f0 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/ServerEntity.java @@ -0,0 +1,12 @@ +package com.tom.meeter.context.network.dto; + +public interface ServerEntity extends BaseEntity { + + String NAME_KEY = "name"; + + String getName(); + + String PHOTO_PATH_KEY = "photo_path"; + + String getPhotoPath(); +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/ServerEntityBase.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/ServerEntityBase.java new file mode 100644 index 0000000..c108575 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/dto/ServerEntityBase.java @@ -0,0 +1,73 @@ +package com.tom.meeter.context.network.dto; + +import static com.tom.meeter.infrastructure.common.JsonHelper.getStringOrNull; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Objects; + +public abstract class ServerEntityBase implements ServerEntity { + + protected String id; + protected String name; + @JsonProperty(PHOTO_PATH_KEY) + protected String photoPath; + + @Override + public String getId() { + return id; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getPhotoPath() { + return photoPath; + } + + public ServerEntityBase() { + //retrofit... + } + + public ServerEntityBase(JSONObject json) { + try { + //Non nullable. + id = json.getString(ID_KEY); + name = json.getString(NAME_KEY); + + //Nullable. + photoPath = getStringOrNull(PHOTO_PATH_KEY, json); + } catch (JSONException e) { + throw new RuntimeException( + "Unable to create ServerEntityBase from jsonObject: ", e); + } + } + + public void setName(String name) { + this.name = name; + } + + public void setPhotoPath(String photoPath) { + this.photoPath = photoPath; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ServerEntityBase that = (ServerEntityBase) o; + return Objects.equals(id, that.id) + && Objects.equals(name, that.name) + && Objects.equals(photoPath, that.photoPath); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, photoPath); + } +} 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 index e889dbd..a3c887c 100644 --- 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 @@ -3,61 +3,45 @@ import static com.tom.meeter.infrastructure.common.JsonHelper.getLocalDateOrNull; import static com.tom.meeter.infrastructure.common.JsonHelper.getStringOrNull; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import org.json.JSONException; import org.json.JSONObject; import java.time.LocalDate; +import java.util.Objects; -public class UserDTO implements EntityBase { +@JsonIgnoreProperties(ignoreUnknown = true) +public class UserDTO extends ServerEntityBase { - private static final String USER_ID_KEY = "id"; - private static final String NAME_KEY = "name"; private static final String GENDER_KEY = "gender"; private static final String SURNAME_KEY = "surname"; private static final String INFO_KEY = "info"; private static final String BIRTHDAY_KEY = "birthday"; - private static final String PHOTO_PATH_KEY = "photo_path"; - private String id; - private String name; private UserGender gender; private String surname; private String info; private LocalDate birthday; - @JsonProperty(PHOTO_PATH_KEY) - private String photoPath; public UserDTO() { + //retrofit... } - public static UserDTO encode(JSONObject json) { - UserDTO result = new UserDTO(); + public UserDTO(JSONObject json) { + super(json); try { //Non nullable. - result.id = json.getString(USER_ID_KEY); - result.name = json.getString(NAME_KEY); - result.gender = UserGender.fromString(json.getString(GENDER_KEY)); + gender = UserGender.fromString(json.getString(GENDER_KEY)); //Nullable. - result.surname = getStringOrNull(SURNAME_KEY, json); - result.info = getStringOrNull(INFO_KEY, json); - result.birthday = getLocalDateOrNull(BIRTHDAY_KEY, json); - result.photoPath = getStringOrNull(PHOTO_PATH_KEY, json); + surname = getStringOrNull(SURNAME_KEY, json); + info = getStringOrNull(INFO_KEY, json); + birthday = getLocalDateOrNull(BIRTHDAY_KEY, json); } catch (JSONException e) { throw new RuntimeException("Unable to encode UserDTO from jsonObject: ", e); } - return result; - } - - @Override - public String getId() { - return id; - } - - public String getName() { - return name; } public UserGender getGender() { @@ -76,10 +60,6 @@ public LocalDate getBirthday() { return birthday; } - public String getPhotoPath() { - return photoPath; - } - private static final String MALE_VALUE = "male"; private static final String FEMALE_VALUE = "female"; @@ -109,4 +89,21 @@ public static UserGender fromString(String text) { throw new IllegalArgumentException("No enum constant with string value " + text); } } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + UserDTO userDTO = (UserDTO) o; + return gender == userDTO.gender + && Objects.equals(surname, userDTO.surname) + && Objects.equals(info, userDTO.info) + && Objects.equals(birthday, userDTO.birthday); + } + + @Override + public int hashCode() { + return Objects.hash( + super.hashCode(), gender, surname, info, birthday); + } } diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/exception/IncorrectResponseType.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/exception/IncorrectResponseType.java new file mode 100644 index 0000000..031ec20 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/exception/IncorrectResponseType.java @@ -0,0 +1,7 @@ +package com.tom.meeter.context.network.exception; + +public class IncorrectResponseType extends RuntimeException { + public IncorrectResponseType(String message) { + super(message); + } +} 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 79ef18c..bf20039 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,10 +1,11 @@ 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.network.utils.SocketIOCodes.NEW_SUBSCRIBER_CODE; -import static com.tom.meeter.context.notification.NotificationHelper.sendNotificationEventCreated; +import static com.tom.meeter.context.notification.NotificationHelper.sendEventDeletedNotification; +import static com.tom.meeter.context.notification.NotificationHelper.sendEventNotification; import static com.tom.meeter.context.notification.NotificationHelper.sendNotificationNewSubscriber; +import static com.tom.meeter.infrastructure.common.CommonHelper.getAppLogo; 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; @@ -30,6 +31,8 @@ import com.tom.meeter.context.network.domain.SearchForEvents; import com.tom.meeter.context.network.dto.EventDTO; import com.tom.meeter.context.network.dto.UserDTO; +import com.tom.meeter.context.network.exception.IncorrectResponseType; +import com.tom.meeter.context.network.utils.SocketIOEventCode; import com.tom.meeter.infrastructure.common.Globals; import com.tom.meeter.infrastructure.eventbus.events.IncomeEvents; @@ -68,6 +71,7 @@ public class SocketIOService extends Service { private static final String MESSAGE_KEY = "message"; private static final String USER_KEY = "user"; private static final String EVENT_KEY = "event"; + private static final String EVENT_ID_KEY = "eventId"; private static final String CHANNEL_ID = "socket_channel"; @@ -167,7 +171,7 @@ private Notification buildForegroundNotification() { .setContentTitle(getString(R.string.app_name)) .setContentText(getString(R.string.press_to_open_the_application)) .setContentIntent(pendingIntent) - .setSmallIcon(R.drawable.ic_meeter_lr) + .setSmallIcon(getAppLogo()) .setOngoing(true) .build(); } @@ -254,7 +258,7 @@ private void disconnect() { @Subscribe public void onMessageEvent(SearchForEvents event) { - Log.d(TAG, "onMessageEvent:SearchForEvents: " + event.toString()); + Log.d(TAG, "onMessageEvent: [" + EVENTS_SEARCH_CHANNEL + "] : " + event); socketClient.emit(EVENTS_SEARCH_CHANNEL, event.toJson()); } @@ -262,13 +266,22 @@ private void eventsNotificationsChannel(Object... args) { JSONObject response = getSimpleResponse(JSONObject.class, args); Log.d(TAG, EVENTS_NOTIFICATIONS_CHANNEL + " : " + response); try { - if (response.getInt(CODE_KEY) == EVENT_CREATED_CODE) { - JSONObject message = response.getJSONObject(MESSAGE_KEY); - sendNotificationEventCreated( - this, - UserDTO.encode(message.getJSONObject(USER_KEY)), - EventDTO.encode(message.getJSONObject(EVENT_KEY))); + int code = response.getInt(CODE_KEY); + SocketIOEventCode eventNotifyCode = SocketIOEventCode.fromCode(code); + if (eventNotifyCode == null) { + Log.d(TAG, "Unrecognized event code: " + code); + return; } + JSONObject msg = response.getJSONObject(MESSAGE_KEY); + UserDTO user = new UserDTO(msg.getJSONObject(USER_KEY)); + + if (eventNotifyCode == SocketIOEventCode.DELETED) { + sendEventDeletedNotification(this, user, msg.getString(EVENT_ID_KEY)); + return; + } + sendEventNotification( + this, user, eventNotifyCode, + new EventDTO(msg.getJSONObject(EVENT_KEY))); } catch (JSONException e) { throw new RuntimeException(e); } @@ -281,7 +294,7 @@ private void newSubscriberNotificationsChannel(Object... args) { if (response.getInt(CODE_KEY) == NEW_SUBSCRIBER_CODE) { sendNotificationNewSubscriber( this, - UserDTO.encode(response.getJSONObject(MESSAGE_KEY))); + new UserDTO(response.getJSONObject(MESSAGE_KEY))); } } catch (JSONException e) { throw new RuntimeException(e); @@ -320,15 +333,20 @@ private static void greetingsHandler(Object... args) { } private static void eventsSearchHandler(Object... args) { - JSONArray response = getSimpleResponse(JSONArray.class, args); - Log.d(TAG, EVENTS_SEARCH_CHANNEL + " : " + response); - EventBus.getDefault().post(IncomeEvents.fromJsonArray(response)); + try { + JSONArray response = getSimpleResponse(JSONArray.class, args); + Log.d(TAG, EVENTS_SEARCH_CHANNEL + " : " + response); + EventBus.getDefault().post(IncomeEvents.fromJsonArray(response)); + } catch (IncorrectResponseType e) { + JSONObject response = getSimpleResponse(JSONObject.class, args); + Log.e(TAG, EVENTS_SEARCH_CHANNEL + " : " + response); + } } private static T getSimpleResponse( Class aClass, Object[] args) { if (!validateSingleMessageResponse(aClass, args)) { - throw new RuntimeException("Incorrect response for " + aClass + throw new IncorrectResponseType("Incorrect response for " + aClass + " with response " + Arrays.toString(args)); } return (T) args[0]; 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 index f764552..011c26a 100644 --- 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 @@ -1,8 +1,6 @@ package com.tom.meeter.context.network.utils; public final class SocketIOCodes { - - public static int EVENT_CREATED_CODE = 100; public static int NEW_SUBSCRIBER_CODE = 200; private SocketIOCodes() { diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/network/utils/SocketIOEventCode.java b/AndroidClient/src/main/java/com/tom/meeter/context/network/utils/SocketIOEventCode.java new file mode 100644 index 0000000..0d11a37 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/network/utils/SocketIOEventCode.java @@ -0,0 +1,41 @@ +package com.tom.meeter.context.network.utils; + +import java.util.HashMap; +import java.util.Map; + +public enum SocketIOEventCode { + CREATED(100), + PUBLISHED(101), + UNPUBLISHED(102), + DELETED(103), + UPDATED(104), + SCHEDULED(105), + STARTED(106), + PAUSED(107), + RESUMED(108), + FINISHED(109), + CANCELLED(110), + ARCHIVED(111); + + private final int code; + + SocketIOEventCode(int code) { + this.code = code; + } + + public int getCode() { + return code; + } + + private static final Map mapping = new HashMap<>(); + + static { + for (SocketIOEventCode ec : values()) { + mapping.put(ec.code, ec); + } + } + + public static SocketIOEventCode fromCode(int code) { + return mapping.get(code); + } +} 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 index e983066..b18d123 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/notification/NotificationHelper.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/notification/NotificationHelper.java @@ -1,5 +1,7 @@ package com.tom.meeter.context.notification; +import static com.tom.meeter.infrastructure.common.CommonHelper.getSmallAppLogo; + import android.Manifest; import android.app.Notification; import android.app.NotificationChannel; @@ -10,6 +12,7 @@ import android.content.pm.PackageManager; import android.os.Build; +import androidx.annotation.NonNull; import androidx.annotation.RequiresPermission; import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationCompat; @@ -19,8 +22,11 @@ import com.tom.meeter.context.event.activity.EventDispatcherActivity; import com.tom.meeter.context.network.dto.EventDTO; import com.tom.meeter.context.network.dto.UserDTO; +import com.tom.meeter.context.network.utils.SocketIOEventCode; import com.tom.meeter.context.user.activity.UserActivity; +import java.util.function.Supplier; + public class NotificationHelper { private static final String EVENTS_NOTIFY = "events_notify"; @@ -31,10 +37,10 @@ public static void createNotificationChannel(Context ctx) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( EVENTS_NOTIFY, - ctx.getString(R.string.new_events_channel), + ctx.getString(R.string.events_channel), NotificationManager.IMPORTANCE_DEFAULT); channel.setDescription( - ctx.getString(R.string.information_about_newly_created_events)); + ctx.getString(R.string.information_about_events)); NotificationManager notificationManager = ctx.getSystemService( NotificationManager.class); @@ -42,9 +48,11 @@ public static void createNotificationChannel(Context ctx) { } } - public static void sendNotificationEventCreated( - Context ctx, UserDTO user, EventDTO event) { - if (ActivityCompat.checkSelfPermission(ctx, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + public static void sendEventNotification( + Context ctx, UserDTO user, + SocketIOEventCode eventCode, 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 @@ -55,10 +63,33 @@ public static void sendNotificationEventCreated( return; } NotificationManagerCompat mgr = NotificationManagerCompat.from(ctx); - mgr.notify(event.getId().hashCode(), getNotificationEventCreated(ctx, user, event)); + mgr.notify(event.getId().hashCode(), getNotification(ctx, user, event, eventCode)); notifySummary(ctx, mgr); } + public static void sendEventDeletedNotification( + Context ctx, UserDTO user, String eventId) { + // As nothing to notify... + +/* 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( + eventId.hashCode(), + getEventNotification( + ctx, contentTitle, contentText, bigStyleText, eventId)); + notifySummary(ctx, mgr);*/ + } + @RequiresPermission(Manifest.permission.POST_NOTIFICATIONS) private static void notifySummary(Context ctx, NotificationManagerCompat mgr) { mgr.notify(SUMMARY_ID, getSummaryNotification(ctx)); @@ -80,27 +111,90 @@ public static void sendNotificationNewSubscriber(Context ctx, UserDTO user) { notifySummary(ctx, mgr); } - private static Notification getNotificationEventCreated( - Context ctx, UserDTO user, EventDTO event) { + private static Notification getNotification( + Context ctx, UserDTO user, EventDTO event, SocketIOEventCode eventCode) { + Supplier contentTitleS = null; + Supplier contentTextS = null; + Supplier bigStyleTextS = event::getDescription; + if (eventCode == SocketIOEventCode.CREATED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.notification_new_event); + contentTextS = () -> user.getName() + " " + user.getSurname() + " " + ctx.getString(R.string.created_event); + } else if (eventCode == SocketIOEventCode.UPDATED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_updated); + contentTextS = () -> getContentText(ctx, user, R.string.is_updated); + } else if (eventCode == SocketIOEventCode.PUBLISHED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_published); + contentTextS = () -> getContentText(ctx, user, R.string.is_published); + } else if (eventCode == SocketIOEventCode.UNPUBLISHED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_unpublished); + contentTextS = () -> getContentText(ctx, user, R.string.is_unpublished); + } else if (eventCode == SocketIOEventCode.SCHEDULED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_scheduled); + contentTextS = () -> getContentText(ctx, user, R.string.is_scheduled); + } else if (eventCode == SocketIOEventCode.STARTED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_started); + contentTextS = () -> getContentText(ctx, user, R.string.event_is_started); + } else if (eventCode == SocketIOEventCode.PAUSED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_paused); + contentTextS = () -> getContentText(ctx, user, R.string.event_is_paused); + } else if (eventCode == SocketIOEventCode.RESUMED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_resumed); + contentTextS = () -> getContentText(ctx, user, R.string.event_is_resumed); + } else if (eventCode == SocketIOEventCode.FINISHED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_finished); + contentTextS = () -> getContentText(ctx, user, R.string.event_is_finished); + } else if (eventCode == SocketIOEventCode.CANCELLED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_cancelled); + contentTextS = () -> getContentText(ctx, user, R.string.event_is_cancelled); + } else if (eventCode == SocketIOEventCode.ARCHIVED) { + contentTitleS = () -> getContentTitle(ctx, event.getName(), R.string.event_archived); + contentTextS = () -> getContentText(ctx, user, R.string.event_is_archived); + } + + if (contentTitleS == null) { + throw new IllegalStateException("Unrecognized event notification:" + eventCode); + } + return getEventNotification(ctx, contentTitleS, contentTextS, bigStyleTextS, event.getId()); + } + + @NonNull + private static String getContentText(Context ctx, UserDTO user, int newStatusResId) { + return ctx.getString(R.string.created_by_user) + " " + user.getName() + " " + user.getSurname() + + " " + ctx.getString(newStatusResId); + } + + @NonNull + private static String getContentTitle(Context ctx, String eventName, int resId) { + return ctx.getString(resId) + ": " + eventName + " !"; + } + + private static Notification getEventNotification( + Context ctx, String title, String text, + String bigStyleText, String eventId) { return new NotificationCompat.Builder(ctx, EVENTS_NOTIFY) - .setSmallIcon(R.drawable.ic_meeter_lr) - .setContentTitle( - ctx.getString(R.string.notification_new_event) + ": " + event.getName() + " !") - .setContentText( - user.getName() + " " + user.getSurname() + " " - + ctx.getString(R.string.created_event)) - .setStyle(getBigStyle(event.getDescription())) - .setContentIntent(createEventPendingIntent(ctx, event)) + .setSmallIcon(getSmallAppLogo()) + .setContentTitle(title) + .setContentText(text) + .setStyle(getBigStyle(bigStyleText)) + .setContentIntent(createEventPendingIntent(ctx, eventId)) .setGroup(ALL_EVENTS_GROUP) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setAutoCancel(true) .build(); } + private static Notification getEventNotification( + Context ctx, Supplier titleS, Supplier textS, + Supplier bigStyleTextS, String eventId) { + return getEventNotification( + ctx, titleS.get(), textS.get(), + bigStyleTextS.get(), eventId); + } + private static Notification getNotificationNewSubscriber( Context ctx, UserDTO user) { return new NotificationCompat.Builder(ctx, EVENTS_NOTIFY) - .setSmallIcon(R.drawable.ic_meeter_lr) + .setSmallIcon(getSmallAppLogo()) .setContentTitle(ctx.getString(R.string.new_subscriber)) .setContentText( user.getName() + " " + user.getSurname() + " " @@ -118,7 +212,7 @@ private static Notification getNotificationNewSubscriber( private static Notification getSummaryNotification(Context ctx) { return new NotificationCompat.Builder(ctx, EVENTS_NOTIFY) - .setSmallIcon(R.drawable.ic_meeter_lr) + .setSmallIcon(getSmallAppLogo()) .setGroup(ALL_EVENTS_GROUP) .setGroupSummary(true) .build(); @@ -128,13 +222,18 @@ private static NotificationCompat.BigTextStyle getBigStyle(String descr) { return new NotificationCompat.BigTextStyle().bigText(descr); } - private static PendingIntent createEventPendingIntent(Context ctx, EventDTO event) { - Intent intent = EventDispatcherActivity.createEventActivityIntent(ctx, event.getId()); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + private static PendingIntent createEventPendingIntent( + Context ctx, String eventId) { + Intent intent = EventDispatcherActivity.createEventActivityIntent( + ctx, eventId); + 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); + ctx, eventId.hashCode(), intent, + PendingIntent.FLAG_IMMUTABLE + | PendingIntent.FLAG_UPDATE_CURRENT); } private static PendingIntent createUserPendingIntent(Context ctx, UserDTO user) { diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/ProfileComponent.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/ProfileComponent.java new file mode 100644 index 0000000..e9387c8 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/ProfileComponent.java @@ -0,0 +1,57 @@ +package com.tom.meeter.context.profile; + +import android.app.Application; + +import com.tom.meeter.AppComponent; +import com.tom.meeter.context.profile.component.StatusesFilterDialog; +import com.tom.meeter.context.profile.component.activity.ProfileActivity; +import com.tom.meeter.context.profile.component.activity.SettingsActivity; +import com.tom.meeter.context.profile.component.activity.SubscribersActivity; +import com.tom.meeter.context.profile.component.activity.SubscriptionsActivity; +import com.tom.meeter.context.profile.component.fragment.ActiveEventsFragment; +import com.tom.meeter.context.profile.component.fragment.CreateEventFragment; +import com.tom.meeter.context.profile.component.fragment.GoogleMapsFragment; +import com.tom.meeter.context.profile.component.fragment.ProfileEventsFragment; +import com.tom.meeter.context.profile.component.fragment.ProfileFragment; + +import dagger.BindsInstance; +import dagger.Component; + +@ProfileScope +@Component( + modules = {ProfileModule.class}, + dependencies = {AppComponent.class} +) +public interface ProfileComponent { + + @Component.Builder + interface Builder { + @BindsInstance + Builder application(Application application); + + Builder appComponent(AppComponent appComponent); + + ProfileComponent build(); + } + + void inject(ProfileActivity profileActivity); + + void inject(SettingsActivity settingsActivity); + + void inject(ProfileFragment profileFragment); + + void inject(GoogleMapsFragment googleMapsFragment); + + void inject(ActiveEventsFragment activeEventsFragment); + + void inject(ProfileEventsFragment profileEventsFragment); + + void inject(SubscribersActivity subscribersActivity); + + void inject(SubscriptionsActivity subscriptionsActivity); + + void inject(CreateEventFragment createEventFragment); + + void inject(StatusesFilterDialog statusesFilterDialog); + +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/ProfileModule.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/ProfileModule.java new file mode 100644 index 0000000..e2e5aca --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/ProfileModule.java @@ -0,0 +1,83 @@ +package com.tom.meeter.context.profile; + +import android.app.Application; + +import androidx.annotation.NonNull; +import androidx.room.Room; + +import com.tom.meeter.context.profile.repository.event.database.EventDao; +import com.tom.meeter.context.profile.repository.event.database.EventDatabase; +import com.tom.meeter.context.profile.repository.user.database.UserDao; +import com.tom.meeter.context.profile.repository.user.database.UserDatabase; +import com.tom.meeter.context.profile.service.ProfileService; +import com.tom.meeter.context.profile.service.SettingsService; +import com.tom.meeter.infrastructure.common.RetrofitBuilder; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import dagger.Module; +import dagger.Provides; + +@Module +public class ProfileModule { + + @ProfileScope + @NonNull + @Provides + public SettingsService provideSettingsService(Application app) { + return RetrofitBuilder.createBuilder(app, RetrofitBuilder.jdk8m) + .create(SettingsService.class); + } + + @ProfileScope + @NonNull + @Provides + public ProfileService provideProfileService(Application app) { + return RetrofitBuilder.createBuilder(app) + .create(ProfileService.class); + } + + + @ProfileScope + @NonNull + @Provides + public EventDatabase provideEventDb(Application app) { + return Room.databaseBuilder(app, EventDatabase.class, "event.db") + .fallbackToDestructiveMigration() + .build(); + } + + @ProfileScope + @NonNull + @Provides + public EventDao provideEventDao(EventDatabase eventDatabase) { + return eventDatabase.eventDao(); + } + + @ProfileScope + @NonNull + @Provides + public UserDatabase provideUserDb(Application app) { + return Room.databaseBuilder(app, UserDatabase.class, "user.db") + .fallbackToDestructiveMigration() + .build(); + } + + @ProfileScope + @NonNull + @Provides + public UserDao provideUserDao(UserDatabase userDatabase) { + return userDatabase.userDao(); + } + + @ProfileScope + @NonNull + @Provides + public Executor provideExecutor() { + return new ThreadPoolExecutor(4, 8, 1000, TimeUnit.SECONDS, + new ArrayBlockingQueue<>(15, false)); + } +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/ProfileScope.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/ProfileScope.java new file mode 100644 index 0000000..dde115a --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/ProfileScope.java @@ -0,0 +1,11 @@ +package com.tom.meeter.context.profile; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Scope; + +@Scope +@Retention(RetentionPolicy.RUNTIME) +public @interface ProfileScope { +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/StatusesFilterDialog.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/StatusesFilterDialog.java new file mode 100644 index 0000000..5274747 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/StatusesFilterDialog.java @@ -0,0 +1,116 @@ +package com.tom.meeter.context.profile.component; + +import static com.tom.meeter.infrastructure.common.CommonHelper.resolveStatus; +import static com.tom.meeter.infrastructure.common.PreferencesHelper.savePrefsToServer; + +import android.content.Context; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; + +import com.google.android.material.bottomsheet.BottomSheetDialogFragment; +import com.tom.meeter.App; +import com.tom.meeter.R; +import com.tom.meeter.context.network.dto.EventDTO; +import com.tom.meeter.context.profile.message.SettingsCreateOrUpdate; +import com.tom.meeter.context.profile.service.SettingsService; +import com.tom.meeter.databinding.DialogFilterBottomSheetBinding; +import com.tom.meeter.infrastructure.common.PreferencesHelper; + +import java.util.HashSet; +import java.util.Set; + +import javax.inject.Inject; + +public class StatusesFilterDialog extends BottomSheetDialogFragment { + + private static final String TAG = StatusesFilterDialog.class.getCanonicalName(); + + @Inject + SettingsService service; + + private DialogFilterBottomSheetBinding binding; + + private final Set selected = new HashSet<>(); + private Set beforeSave; + + @Override + public void onAttach(@NonNull Context ctx) { + super.onAttach(ctx); + beforeSave = PreferencesHelper.getVisibleEventsStatuses(ctx); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ((App) getActivity().getApplication()).getProfileComponent().inject(this); + } + + @Nullable + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + + binding = DialogFilterBottomSheetBinding.inflate(inflater, container, false); + + Context ctx = requireContext(); + + for (String statusName : ctx.getResources().getStringArray(R.array.statuses)) { + EventDTO.EventStatus status = resolveStatus(ctx, statusName); + CheckBox checkBox = new CheckBox(ctx); + checkBox.setText(statusName); + if (beforeSave.contains(status)) { + selected.add(status); + checkBox.setChecked(true); + } + checkBox.setOnCheckedChangeListener( + (buttonView, isChecked) -> { + if (isChecked) { + selected.add(status); + } else { + selected.remove(status); + } + }); + binding.statusCheckboxContainer.addView(checkBox); + } + + binding.applyButton.setOnClickListener(v -> { + if (selected.isEmpty()) { + Toast.makeText(ctx, + R.string.select_one_status_at_least, + Toast.LENGTH_SHORT).show(); + return; + } + if (beforeSave.equals(selected)) { + Log.d(TAG, "Same items, not sending the request."); + dismiss(); + return; + } + SettingsCreateOrUpdate req = new SettingsCreateOrUpdate(); + req.setVisibleEventStatuses(selected); + FragmentActivity activity = requireActivity(); + savePrefsToServer( + activity, service, req, + activity::recreate); + dismiss(); + }); + + return binding.getRoot(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + binding = null; + } +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/DrawerUtils.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/DrawerUtils.java new file mode 100644 index 0000000..1cdcefc --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/DrawerUtils.java @@ -0,0 +1,156 @@ +package com.tom.meeter.context.profile.component.activity; + +import static com.tom.meeter.context.profile.component.activity.ProfileActivity.DRAWER_CONTACT_ID; +import static com.tom.meeter.context.profile.component.activity.ProfileActivity.DRAWER_EVENTS_ID; +import static com.tom.meeter.context.profile.component.activity.ProfileActivity.DRAWER_HELP_ID; +import static com.tom.meeter.context.profile.component.activity.ProfileActivity.DRAWER_LOGOUT_ID; +import static com.tom.meeter.context.profile.component.activity.ProfileActivity.DRAWER_NEW_EVENT_ID; +import static com.tom.meeter.context.profile.component.activity.ProfileActivity.DRAWER_NOTIFICATION_ID; +import static com.tom.meeter.context.profile.component.activity.ProfileActivity.DRAWER_OPEN_SOURCE_ID; +import static com.tom.meeter.context.profile.component.activity.ProfileActivity.DRAWER_PROFILE_ID; +import static com.tom.meeter.context.profile.component.activity.ProfileActivity.DRAWER_SETTINGS_ID; + +import android.util.Log; + +import androidx.recyclerview.widget.RecyclerView; + +import com.mikepenz.fastadapter.FastAdapter; +import com.mikepenz.fastadapter.IItem; +import com.mikepenz.fastadapter.listeners.OnBindViewHolderListenerImpl; +import com.mikepenz.fontawesome_typeface_library.FontAwesome; +import com.mikepenz.google_material_typeface_library.GoogleMaterial; +import com.mikepenz.iconics.typeface.IIcon; +import com.mikepenz.materialdrawer.Drawer; +import com.mikepenz.materialdrawer.holder.ImageHolder; +import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; + +import java.util.function.Function; + +public class DrawerUtils { + private DrawerUtils() { + } + + private static final String TAG = DrawerUtils.class.getCanonicalName(); + + static Function getIconProvider( + ProfileActivity.IconPackEnum iconPack) { + return switch (iconPack) { + case FONT_AWESOME -> DrawerUtils::fontAwesomeIconPack; + case GOOGLE_MATERIALS -> DrawerUtils::googleMaterialIconPack; + default -> DrawerUtils::fontAwesomeIconPack; + }; + } + + static IIcon googleMaterialIconPack(Long id) { + if (id == DRAWER_PROFILE_ID) { + //return GoogleMaterial.Icon.gmd_account_box; + return GoogleMaterial.Icon.gmd_person; + } + if (id == DRAWER_EVENTS_ID) { + return GoogleMaterial.Icon.gmd_public; + } + if (id == DRAWER_NEW_EVENT_ID) { + return GoogleMaterial.Icon.gmd_event; + //return GoogleMaterial.Icon.gmd_perm_contact_calendar; + } + if (id == DRAWER_NOTIFICATION_ID) { + //return GoogleMaterial.Icon.gmd_visibility; + //return GoogleMaterial.Icon.gmd_notifications; + return GoogleMaterial.Icon.gmd_notifications_active; + } + if (id == DRAWER_SETTINGS_ID) { + return GoogleMaterial.Icon.gmd_memory; + } + if (id == DRAWER_HELP_ID) { + return GoogleMaterial.Icon.gmd_help; + } + if (id == DRAWER_OPEN_SOURCE_ID) { + return GoogleMaterial.Icon.gmd_live_help; + } + if (id == DRAWER_CONTACT_ID) { + return GoogleMaterial.Icon.gmd_email; + } + if (id == DRAWER_LOGOUT_ID) { + return GoogleMaterial.Icon.gmd_settings_power; + //return GoogleMaterial.Icon.gmd_close; + } + return GoogleMaterial.Icon.gmd_help; + } + + static IIcon fontAwesomeIconPack(Long id) { + if (id == DRAWER_PROFILE_ID) { + return FontAwesome.Icon.faw_user; + } + if (id == DRAWER_EVENTS_ID) { + return FontAwesome.Icon.faw_globe; + } + if (id == DRAWER_NEW_EVENT_ID) { + return FontAwesome.Icon.faw_calendar; + } + if (id == DRAWER_NOTIFICATION_ID) { + return FontAwesome.Icon.faw_eye; + } + if (id == DRAWER_SETTINGS_ID) { + return FontAwesome.Icon.faw_cog; + } + if (id == DRAWER_HELP_ID) { + return FontAwesome.Icon.faw_question_circle; + } + if (id == DRAWER_OPEN_SOURCE_ID) { + return FontAwesome.Icon.faw_question; + } + if (id == DRAWER_CONTACT_ID) { + return FontAwesome.Icon.faw_github; + } + if (id == DRAWER_LOGOUT_ID) { + return FontAwesome.Icon.faw_power_off; + } + return FontAwesome.Icon.faw_coffee; + } + + public static void updateIconFor( + Drawer drawer, Function iconProvider, long itemId) { + IDrawerItem iDrawerItem = drawer.getDrawerItem(itemId); + if (iDrawerItem == null) { + Log.d(TAG, "Drawer item is not exist " + itemId); + return; + } + drawer.updateIcon(itemId, new ImageHolder(iconProvider.apply(itemId))); + } + + /** + * Workaround for https://github.com/mikepenz/MaterialDrawer/issues/2789 + * For base implementation look at the {@link OnBindViewHolderListenerImpl} + */ + public static class OnBindViewHolderListenerImplBase extends OnBindViewHolderListenerImpl { + + // Values was received from revers engineered variables for current library. + private final int fastadapter_item_adapter = 2131296379; + private final int fastadapter_item = 2131296378; + private final int unknown_item_id = 2131296441; + + @Override + public void unBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { + //logMethod(TAG, this); + //IItem item = FastAdapter.getHolderAdapterItemTag(viewHolder); + var item = (IItem) viewHolder.itemView.getTag(fastadapter_item); + if (item != null) { + item.unbindView(viewHolder); + if (viewHolder instanceof FastAdapter.ViewHolder) { + ((FastAdapter.ViewHolder) viewHolder).unbindView(item); + } + //remove set tag's + viewHolder.itemView.setTag(fastadapter_item, null); + viewHolder.itemView.setTag(fastadapter_item_adapter, null); + } + //super.unBindViewHolder(viewHolder, position); + } + + //@Override + public void unBindViewHolderWithoutUnbind(RecyclerView.ViewHolder viewHolder, int position) { + if (FastAdapter.getHolderAdapterItemTag(viewHolder) != null) { + super.unBindViewHolder(viewHolder, position); + } + } + } +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/NewEventOnMapActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/NewEventOnMapActivity.java similarity index 97% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/NewEventOnMapActivity.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/NewEventOnMapActivity.java index ffb2d58..cecf19d 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/NewEventOnMapActivity.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/NewEventOnMapActivity.java @@ -1,6 +1,6 @@ -package com.tom.meeter.context.profile.activity; +package com.tom.meeter.context.profile.component.activity; -import static com.tom.meeter.context.profile.fragment.GoogleMapsFragment.ZOOM_VALUE; +import static com.tom.meeter.context.profile.component.fragment.GoogleMapsFragment.ZOOM_VALUE; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; import android.content.ComponentName; 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/component/activity/ProfileActivity.java similarity index 68% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/ProfileActivity.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/ProfileActivity.java index a899391..20ee2ac 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/ProfileActivity.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/ProfileActivity.java @@ -1,23 +1,24 @@ -package com.tom.meeter.context.profile.activity; +package com.tom.meeter.context.profile.component.activity; -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.context.profile.component.activity.DrawerUtils.getIconProvider; +import static com.tom.meeter.context.profile.component.activity.DrawerUtils.updateIconFor; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; +import static com.tom.meeter.infrastructure.common.PreferencesHelper.cleanLocalPrefs; +import static com.tom.meeter.infrastructure.common.PreferencesHelper.updateLocalPrefs; import static com.tom.meeter.infrastructure.utils.Utils.requireNonNull; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Activity; -import android.content.ComponentName; import android.content.Intent; -import android.content.ServiceConnection; -import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; -import android.os.IBinder; import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.ImageView; @@ -31,18 +32,11 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.mikepenz.fastadapter.FastAdapter; -import com.mikepenz.fastadapter.IItem; -import com.mikepenz.fastadapter.listeners.OnBindViewHolderListenerImpl; -import com.mikepenz.fontawesome_typeface_library.FontAwesome; -import com.mikepenz.google_material_typeface_library.GoogleMaterial; import com.mikepenz.iconics.typeface.IIcon; import com.mikepenz.materialdrawer.Drawer; import com.mikepenz.materialdrawer.DrawerBuilder; -import com.mikepenz.materialdrawer.holder.ImageHolder; import com.mikepenz.materialdrawer.model.DividerDrawerItem; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.SecondaryDrawerItem; @@ -53,13 +47,14 @@ import com.tom.meeter.context.auth.activity.LoginActivity; import com.tom.meeter.context.auth.infrastructure.AuthHelper; import com.tom.meeter.context.network.service.SocketIOService; -import com.tom.meeter.context.profile.fragment.CreateEventFragment; -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.component.StatusesFilterDialog; +import com.tom.meeter.context.profile.component.fragment.CreateEventFragment; +import com.tom.meeter.context.profile.component.fragment.EventsFragment; +import com.tom.meeter.context.profile.component.fragment.ProfileEventsFragment; +import com.tom.meeter.context.profile.component.fragment.ProfileFragment; +import com.tom.meeter.context.profile.message.SettingsResponse; 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.profile.service.SettingsService; import com.tom.meeter.context.token.service.TokenService; import com.tom.meeter.databinding.ProfileActivityBinding; import com.tom.meeter.infrastructure.common.Globals; @@ -81,24 +76,24 @@ public class ProfileActivity extends AppCompatActivity { private static final String TAG = ProfileActivity.class.getCanonicalName(); - private static final long DRAWER_PROFILE_ID = 0; - private static final String PROFILE_FRAGMENT_TAG = "profile_fragment_tag"; + static final long DRAWER_PROFILE_ID = 0; + static final String PROFILE_FRAGMENT_TAG = "profile_fragment_tag"; - private static final long DRAWER_EVENTS_ID = 1; - private static final String EVENTS_FRAGMENT_TAG = "events_fragment_tag"; + static final long DRAWER_EVENTS_ID = 1; + static final String EVENTS_FRAGMENT_TAG = "events_fragment_tag"; - private static final long DRAWER_NEW_EVENT_ID = 2; - private static final String NEW_EVENT_FRAGMENT_TAG = "new_event_fragment_tag"; + static final long DRAWER_NEW_EVENT_ID = 2; + static final String NEW_EVENT_FRAGMENT_TAG = "new_event_fragment_tag"; - private static final long DRAWER_NOTIFICATION_ID = 3; - private static final String NOTIFICATIONS_FRAGMENT_TAG = "notifications_fragment_tag"; + static final long DRAWER_NOTIFICATION_ID = 3; + static final String NOTIFICATIONS_FRAGMENT_TAG = "notifications_fragment_tag"; - private static final long DRAWER_SETTINGS_ID = 10; // -> no need a tag. + static final long DRAWER_SETTINGS_ID = 10; // -> no need a tag. - private static final long DRAWER_HELP_ID = 11; - private static final long DRAWER_OPEN_SOURCE_ID = 12; - private static final long DRAWER_CONTACT_ID = 13; - private static final long DRAWER_LOGOUT_ID = 99; + static final long DRAWER_HELP_ID = 11; + static final long DRAWER_OPEN_SOURCE_ID = 12; + static final long DRAWER_CONTACT_ID = 13; + static final long DRAWER_LOGOUT_ID = 99; private static final Map DRAWER_FRAGMENT_TAGS = new HashMap<>(); @@ -119,7 +114,10 @@ public class ProfileActivity extends AppCompatActivity { */ } - private enum IconPackEnum { + private Toolbar toolbar; + private boolean showMenu = false; + + enum IconPackEnum { FONT_AWESOME, GOOGLE_MATERIALS } @@ -156,6 +154,35 @@ public ProfileActivity() { logMethod(TAG, this); } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + logMethod(TAG, this); + if (showMenu) { + getMenuInflater().inflate(R.menu.events_menu, menu); + return true; + } + return false; + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + logMethod(TAG, this, item.getItemId()); + if (item.getItemId() == R.id.action_filter) { + new StatusesFilterDialog() + .show(getSupportFragmentManager(), "FilterDialog"); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void hideToolbar() { + toolbar.setVisibility(View.GONE); + } + + private void showToolbar() { + toolbar.setVisibility(View.VISIBLE); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -166,7 +193,7 @@ protected void onCreate(Bundle savedInstanceState) { View view = binding.getRoot(); setContentView(view); - ((App) getApplication()).getComponent().inject(this); + ((App) getApplication()).getProfileComponent().inject(this); accountManager = AccountManager.get(this); //setToken(accountManager, Launcher.EXPIRED); @@ -180,9 +207,9 @@ private void onInit(Bundle savedInstanceState) { ContextCompat.startForegroundService( this, new Intent(this, SocketIOService.class)); - setupPreferences(); + loadPrefsFromServer(); - Toolbar toolbar = binding.profileActivityToolbar; + toolbar = binding.profileActivityToolbar; setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); @@ -192,7 +219,7 @@ private void onInit(Bundle savedInstanceState) { setupDrawer(toolbar, icons); drawer.getAdapter() - .withOnBindViewHolderListener(new OnBindViewHolderListenerImplBase()); + .withOnBindViewHolderListener(new DrawerUtils.OnBindViewHolderListenerImplBase()); if (savedInstanceState == null) { lastNavItemId = DRAWER_PROFILE_ID; @@ -202,45 +229,47 @@ private void onInit(Bundle savedInstanceState) { } } - private void setupPreferences() { + private void loadPrefsFromServer() { settingsService.getSettings(AuthHelper.getAuthHeader(accountManager)).enqueue( new ErrorLogger<>(this) { @Override public void onResponse( - Call call, Response res) { - if (res.code() == HttpCodes.NOT_AUTHENTICATED) { + Call call, Response resp) { + if (resp.code() == HttpCodes.NOT_AUTHENTICATED) { invalidateToken(accountManager, ProfileActivity.this, - fresh -> setupPreferencesRetry(fresh), () -> finishAndRemoveTask()); + fresh -> loadPrefsFromServerRetry(fresh), () -> finishAndRemoveTask()); } - if (res.code() == HttpCodes.NOT_FOUND) { + if (resp.code() == HttpCodes.NOT_FOUND) { // As no settings on the server ... + cleanLocalPrefs(ProfileActivity.this); return; } - if (res.body() == null) { + if (resp.body() == null) { return; } // As settings exist on the server... - updatePreferences(res.body()); + updateLocalPrefs(ProfileActivity.this, resp.body()); } }); } - private void setupPreferencesRetry(String freshToken) { + private void loadPrefsFromServerRetry(String freshToken) { settingsService.getSettings(Globals.getAuthHeader(freshToken)) .enqueue(new ErrorLogger<>(this) { @Override public void onResponse( - Call call, Response res) { - if (res.code() == HttpCodes.NOT_FOUND) { + Call call, Response resp) { + if (resp.code() == HttpCodes.NOT_FOUND) { + cleanLocalPrefs(ProfileActivity.this); // no settings on the server etc... return; } - if (res.body() == null) { + if (resp.body() == null) { Log.d(TAG, "ProfileActivity: /settings returns null on retry..."); return; } // As settings exist on the server... - updatePreferences(res.body()); + updateLocalPrefs(ProfileActivity.this, resp.body()); } }); } @@ -271,21 +300,6 @@ public void onBackPressed() { super.onBackPressed(); } - private void updatePreferences(SettingsResponse res) { - SharedPreferences.Editor edit = getDefaultSharedPreferences(this).edit(); - Integer searchArea = res.getSearchArea(); - if (searchArea != null) { - edit.putInt(getString(R.string.prefs_search_area), searchArea); - } - Boolean needTrackUser = res.getNeedTrackUser(); - if (needTrackUser != null) { - edit.putBoolean(getString(R.string.prefs_need_track_user), needTrackUser); - } - if (searchArea != null || needTrackUser != null) { - edit.apply(); - } - } - @Override protected void onPause() { super.onPause(); @@ -330,6 +344,7 @@ private boolean onDrawerItemClickListener( DRAWER_OPEN_SOURCE_ID = 12; DRAWER_CONTACT_ID = 13; */ + showMenu = lastNavItemId == DRAWER_EVENTS_ID; renderSelectedFragment(); return true; } @@ -369,8 +384,11 @@ private void renderSelectedFragment() { private void restoreSettings() { logMethod(TAG, this); - Long fragmentId = getFragmentIdByTag( - getCurrentFragmentTag(getSupportFragmentManager())); + String tag = getCurrentFragmentTag(getSupportFragmentManager()); + if (EVENTS_FRAGMENT_TAG.equals(tag)) { + showMenu = true; + } + Long fragmentId = getFragmentIdByTag(tag); drawer.setSelection(fragmentId, false); setupActionBarTitle(fragmentId); drawer.closeDrawer(); @@ -399,10 +417,6 @@ private static Long getFragmentIdByTag(String tag) { @NonNull private static String getCurrentFragmentTag(FragmentManager fm) { List fragments = fm.getFragments(); - int size = fragments.size(); - if (size != 1) { - throw new IllegalStateException("Not exactly 1 fragments in manager, size {" + size + "}."); - } String tag = fragments.get(0).getTag(); if (tag == null) { throw new IllegalStateException("Fragment tag is null."); @@ -414,8 +428,7 @@ private void handleLogout() { Intent stopIntent = new Intent(this, SocketIOService.class); stopIntent.setAction(SocketIOService.STOP_CMD); startService(stopIntent); - getDefaultSharedPreferences(ProfileActivity.this) - .edit().clear().apply(); + cleanLocalPrefs(this); Account acc = getSingleAccount(accountManager); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { accountManager.removeAccount( @@ -568,126 +581,4 @@ private void updateDrawerIcons(IconPackEnum iconPack) { updateIconFor(drawer, iconProvider, DRAWER_LOGOUT_ID); icons = iconPack; } - - private static Function getIconProvider( - IconPackEnum iconPack) { - return switch (iconPack) { - case FONT_AWESOME -> ProfileActivity::fontAwesomeIconPack; - case GOOGLE_MATERIALS -> ProfileActivity::googleMaterialIconPack; - default -> ProfileActivity::fontAwesomeIconPack; - }; - } - - private static IIcon googleMaterialIconPack(Long id) { - if (id == DRAWER_PROFILE_ID) { - //return GoogleMaterial.Icon.gmd_account_box; - return GoogleMaterial.Icon.gmd_person; - } - if (id == DRAWER_EVENTS_ID) { - return GoogleMaterial.Icon.gmd_public; - } - if (id == DRAWER_NEW_EVENT_ID) { - return GoogleMaterial.Icon.gmd_event; - //return GoogleMaterial.Icon.gmd_perm_contact_calendar; - } - if (id == DRAWER_NOTIFICATION_ID) { - //return GoogleMaterial.Icon.gmd_visibility; - //return GoogleMaterial.Icon.gmd_notifications; - return GoogleMaterial.Icon.gmd_notifications_active; - } - if (id == DRAWER_SETTINGS_ID) { - return GoogleMaterial.Icon.gmd_memory; - } - if (id == DRAWER_HELP_ID) { - return GoogleMaterial.Icon.gmd_help; - } - if (id == DRAWER_OPEN_SOURCE_ID) { - return GoogleMaterial.Icon.gmd_live_help; - } - if (id == DRAWER_CONTACT_ID) { - return GoogleMaterial.Icon.gmd_email; - } - if (id == DRAWER_LOGOUT_ID) { - return GoogleMaterial.Icon.gmd_settings_power; - //return GoogleMaterial.Icon.gmd_close; - } - return GoogleMaterial.Icon.gmd_help; - } - - private static IIcon fontAwesomeIconPack(Long id) { - if (id == DRAWER_PROFILE_ID) { - return FontAwesome.Icon.faw_user; - } - if (id == DRAWER_EVENTS_ID) { - return FontAwesome.Icon.faw_globe; - } - if (id == DRAWER_NEW_EVENT_ID) { - return FontAwesome.Icon.faw_calendar; - } - if (id == DRAWER_NOTIFICATION_ID) { - return FontAwesome.Icon.faw_eye; - } - if (id == DRAWER_SETTINGS_ID) { - return FontAwesome.Icon.faw_cog; - } - if (id == DRAWER_HELP_ID) { - return FontAwesome.Icon.faw_question_circle; - } - if (id == DRAWER_OPEN_SOURCE_ID) { - return FontAwesome.Icon.faw_question; - } - if (id == DRAWER_CONTACT_ID) { - return FontAwesome.Icon.faw_github; - } - if (id == DRAWER_LOGOUT_ID) { - return FontAwesome.Icon.faw_power_off; - } - return FontAwesome.Icon.faw_coffee; - } - - private static void updateIconFor( - Drawer drawer, Function iconProvider, long itemId) { - IDrawerItem iDrawerItem = drawer.getDrawerItem(itemId); - if (iDrawerItem == null) { - Log.d(TAG, "Drawer item is not exist " + itemId); - return; - } - drawer.updateIcon(itemId, new ImageHolder(iconProvider.apply(itemId))); - } - - /** - * Workaround for https://github.com/mikepenz/MaterialDrawer/issues/2789 - * For base implementation look at the {@link OnBindViewHolderListenerImpl} - */ - public static class OnBindViewHolderListenerImplBase extends OnBindViewHolderListenerImpl { - - // Values was received from revers engineered variables for current library. - private final int fastadapter_item_adapter = 2131296379; - private final int fastadapter_item = 2131296378; - private final int unknown_item_id = 2131296441; - - @Override - public void unBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) { - //logMethod(TAG, this); - //IItem item = FastAdapter.getHolderAdapterItemTag(viewHolder); - var item = (IItem) viewHolder.itemView.getTag(fastadapter_item); - if (item != null) { - item.unbindView(viewHolder); - if (viewHolder instanceof FastAdapter.ViewHolder) { - ((FastAdapter.ViewHolder) viewHolder).unbindView(item); - } - //remove set tag's - viewHolder.itemView.setTag(fastadapter_item, null); - viewHolder.itemView.setTag(fastadapter_item_adapter, null); - } - //super.unBindViewHolder(viewHolder, position); - } - - //@Override - public void unBindViewHolderWithoutUnbind(RecyclerView.ViewHolder viewHolder, int position) { - if (FastAdapter.getHolderAdapterItemTag(viewHolder) != null) { - super.unBindViewHolder(viewHolder, position); - } - } - } } 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/component/activity/SettingsActivity.java similarity index 52% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SettingsActivity.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/SettingsActivity.java index 9066c2f..6fd56e3 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SettingsActivity.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/SettingsActivity.java @@ -1,9 +1,8 @@ -package com.tom.meeter.context.profile.activity; +package com.tom.meeter.context.profile.component.activity; -import static com.tom.meeter.context.auth.infrastructure.AuthHelper.invalidateToken; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; +import static com.tom.meeter.infrastructure.common.PreferencesHelper.savePrefsToServer; -import android.accounts.AccountManager; import android.content.Intent; import android.os.Bundle; import android.util.Log; @@ -17,23 +16,14 @@ import com.tom.meeter.App; import com.tom.meeter.R; -import com.tom.meeter.context.auth.infrastructure.AuthHelper; -import com.tom.meeter.context.launcher.Launcher; -import com.tom.meeter.context.profile.fragment.SettingsFragment; -import com.tom.meeter.context.profile.settings.message.SettingsCreateOrUpdate; -import com.tom.meeter.context.profile.settings.message.SettingsResponse; -import com.tom.meeter.context.profile.settings.service.SettingsService; +import com.tom.meeter.context.profile.component.fragment.SettingsFragment; +import com.tom.meeter.context.profile.message.SettingsCreateOrUpdate; +import com.tom.meeter.context.profile.service.SettingsService; 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.HttpCodes; -import com.tom.meeter.infrastructure.http.HttpErrorLogger; import javax.inject.Inject; -import retrofit2.Call; -import retrofit2.Response; - public class SettingsActivity extends AppCompatActivity { private static final String TAG = SettingsActivity.class.getCanonicalName(); @@ -41,9 +31,7 @@ public class SettingsActivity extends AppCompatActivity { @Inject SettingsService settingsService; - SettingsActivityBinding binding; - - private AccountManager accountManager; + private SettingsActivityBinding binding; private boolean trackUserBeforeChange; private int searchAreaBeforeChange; @@ -54,8 +42,7 @@ protected void onCreate(Bundle savedInstanceState) { logMethod(TAG, this); - ((App) getApplication()).getComponent().inject(this); - accountManager = AccountManager.get(this); + ((App) getApplication()).getProfileComponent().inject(this); binding = SettingsActivityBinding.inflate(getLayoutInflater()); View view = binding.getRoot(); @@ -101,55 +88,20 @@ public void onBackPressed() { logMethod(TAG, this); int searchArea = PreferencesHelper.getSearchArea(this); boolean trackUser = PreferencesHelper.getNeedTrackUser(this); - if (searchAreaBeforeChange != searchArea || trackUserBeforeChange != trackUser) { - sendSavePrefs(searchArea, trackUser); + SettingsCreateOrUpdate req = new SettingsCreateOrUpdate(); + if (searchAreaBeforeChange != searchArea) { + req.setSearchArea(searchArea); + } + if (trackUserBeforeChange != trackUser) { + req.setNeedTrackUser(trackUser); + } + if (!req.isEmpty()) { + savePrefsToServer(this, settingsService, req); } startActivity(new Intent(this, ProfileActivity.class)); super.onBackPressed(); } - private void sendSavePrefs(int searchArea, boolean trackUser) { - settingsService.createOrUpdateSettings( - new SettingsCreateOrUpdate(searchArea, trackUser), - Globals.getAuthHeader(AuthHelper.peekToken(accountManager))) - .enqueue(new HttpErrorLogger<>(this) { - @Override - public void onResponse(Call call, Response res) { - super.onResponse(call, res); - if (res.code() == HttpCodes.NOT_AUTHENTICATED) { - invalidateToken( - accountManager, SettingsActivity.this, - (freshToken) -> sendSavePrefsRetry(freshToken, searchArea, trackUser), - () -> { - Log.d(TAG, "SettingsActivity: canceled auth."); - startActivity(new Intent(SettingsActivity.this, Launcher.class)); - }); - return; - } - if (res.code() == HttpCodes.OK || res.code() == HttpCodes.CREATED) { - Log.d(TAG, "SettingsActivity: created/updated server settings."); - return; - } - } - }); - } - - private void sendSavePrefsRetry(String token, int searchArea, boolean trackUser) { - settingsService.createOrUpdateSettings( - new SettingsCreateOrUpdate(searchArea, trackUser), - Globals.getAuthHeader(token)) - .enqueue(new HttpErrorLogger<>(this) { - @Override - public void onResponse(Call call, Response res) { - super.onResponse(call, res); - if (res.code() == HttpCodes.OK || res.code() == HttpCodes.CREATED) { - Log.d(TAG, "SettingsActivity: created/updated server settings on retry."); - return; - } - } - }); - } - private void readCurrentPreferences() { trackUserBeforeChange = PreferencesHelper.getNeedTrackUser(this); searchAreaBeforeChange = PreferencesHelper.getSearchArea(this); diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SubscribersActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/SubscribersActivity.java similarity index 76% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SubscribersActivity.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/SubscribersActivity.java index a1b0035..3d7e687 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SubscribersActivity.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/SubscribersActivity.java @@ -1,7 +1,6 @@ -package com.tom.meeter.context.profile.activity; +package com.tom.meeter.context.profile.component.activity; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.checkToken; -import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; import android.accounts.AccountManager; @@ -13,14 +12,11 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.tom.meeter.App; -import com.tom.meeter.context.image.ImageDownloader; -import com.tom.meeter.context.profile.adapter.SubscribersAdapter; +import com.tom.meeter.context.profile.component.adapter.SubscribersAdapter; +import com.tom.meeter.context.profile.component.viewmodel.ProfileSubscribersViewModel; import com.tom.meeter.context.profile.factory.ProfileSubscribersAssistedFactory; -import com.tom.meeter.context.profile.viewmodel.ProfileSubscribersViewModel; import com.tom.meeter.context.token.service.TokenService; -import com.tom.meeter.context.user.service.UserService; import com.tom.meeter.databinding.ActivityProfileSubscribersBinding; -import com.tom.meeter.infrastructure.components.binder.SubscriberBinderImpl; import javax.inject.Inject; @@ -33,14 +29,11 @@ public class SubscribersActivity extends AppCompatActivity { @Inject ProfileSubscribersAssistedFactory assistedFactory; @Inject - ImageDownloader imgDownloader; - @Inject - UserService service; + SubscribersAdapter adapter; private final Runnable onAuthFail = this::recreate; private AccountManager accountManager; private ActivityProfileSubscribersBinding binding; - private SubscribersAdapter adapter; private ProfileSubscribersViewModel viewModel; @Override @@ -49,7 +42,11 @@ protected void onCreate(Bundle savedInstanceState) { logMethod(TAG, this); - ((App) getApplication()).getComponent().inject(this); + binding = ActivityProfileSubscribersBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); + + ((App) getApplication()).getProfileComponent().inject(this); accountManager = AccountManager.get(this); checkToken( @@ -60,15 +57,7 @@ protected void onCreate(Bundle savedInstanceState) { private void onInit(String token) { logMethod(TAG, this); - binding = ActivityProfileSubscribersBinding.inflate(getLayoutInflater()); - View view = binding.getRoot(); - setContentView(view); - - String auth = getAuthHeader(accountManager); - adapter = new SubscribersAdapter( - service, onAuthFail, this, - new SubscriberBinderImpl( - this, imgDownloader, onAuthFail)); + adapter.initialize(this, onAuthFail); binding.recyclerSubscribers.setLayoutManager(new LinearLayoutManager(this)); binding.recyclerSubscribers.setAdapter(adapter); diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SubscriptionsActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/SubscriptionsActivity.java similarity index 79% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SubscriptionsActivity.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/SubscriptionsActivity.java index 5e7e94d..58e1aff 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/activity/SubscriptionsActivity.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/activity/SubscriptionsActivity.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.activity; +package com.tom.meeter.context.profile.component.activity; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.checkToken; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; @@ -12,14 +12,11 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.tom.meeter.App; -import com.tom.meeter.context.image.ImageDownloader; -import com.tom.meeter.context.profile.adapter.SubscribersAdapter; +import com.tom.meeter.context.profile.component.adapter.SubscribersAdapter; +import com.tom.meeter.context.profile.component.viewmodel.ProfileSubscriptionsViewModel; import com.tom.meeter.context.profile.factory.ProfileSubscriptionsAssistedFactory; -import com.tom.meeter.context.profile.viewmodel.ProfileSubscriptionsViewModel; import com.tom.meeter.context.token.service.TokenService; -import com.tom.meeter.context.user.service.UserService; import com.tom.meeter.databinding.ActivityProfileSubscriptionsBinding; -import com.tom.meeter.infrastructure.components.binder.SubscriberBinderImpl; import javax.inject.Inject; @@ -32,14 +29,11 @@ public class SubscriptionsActivity extends AppCompatActivity { @Inject ProfileSubscriptionsAssistedFactory assistedFactory; @Inject - ImageDownloader imgDownloader; - @Inject - UserService userService; + SubscribersAdapter adapter; private final Runnable onAuthFail = this::recreate; private ActivityProfileSubscriptionsBinding binding; private AccountManager accountManager; - private SubscribersAdapter adapter; private ProfileSubscriptionsViewModel viewModel; @Override @@ -48,7 +42,11 @@ protected void onCreate(Bundle savedInstanceState) { logMethod(TAG, this); - ((App) getApplication()).getComponent().inject(this); + binding = ActivityProfileSubscriptionsBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); + + ((App) getApplication()).getProfileComponent().inject(this); accountManager = AccountManager.get(this); checkToken( @@ -59,13 +57,7 @@ protected void onCreate(Bundle savedInstanceState) { private void onInit() { logMethod(TAG, this); - binding = ActivityProfileSubscriptionsBinding.inflate(getLayoutInflater()); - View view = binding.getRoot(); - setContentView(view); - - adapter = new SubscribersAdapter( - userService, onAuthFail, this, - new SubscriberBinderImpl(this, imgDownloader, onAuthFail)); + adapter.initialize(this, onAuthFail); binding.recyclerSubscriptions.setLayoutManager(new LinearLayoutManager(this)); binding.recyclerSubscriptions.setAdapter(adapter); 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/component/adapter/ActiveEventsAdapter.java similarity index 89% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/ActiveEventsAdapter.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/adapter/ActiveEventsAdapter.java index 285f994..25227ce 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/ActiveEventsAdapter.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/adapter/ActiveEventsAdapter.java @@ -1,14 +1,14 @@ -package com.tom.meeter.context.profile.adapter; +package com.tom.meeter.context.profile.component.adapter; import android.view.LayoutInflater; import android.view.ViewGroup; import androidx.recyclerview.widget.RecyclerView; +import com.tom.meeter.context.profile.component.viewholder.EventViewHolder; import com.tom.meeter.databinding.EventViewBinding; import com.tom.meeter.infrastructure.components.adapter.BaseEventAdapter; import com.tom.meeter.infrastructure.components.binder.EventBinder; -import com.tom.meeter.infrastructure.components.viewholder.EventViewHolder; /** * created by Tom on 10.02.2017. 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/component/adapter/EventsAdapter.java similarity index 59% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/EventsAdapter.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/adapter/EventsAdapter.java index 20d7108..0809f34 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/EventsAdapter.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/adapter/EventsAdapter.java @@ -1,16 +1,21 @@ -package com.tom.meeter.context.profile.adapter; +package com.tom.meeter.context.profile.component.adapter; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; +import android.content.Context; import android.view.LayoutInflater; import android.view.ViewGroup; import androidx.recyclerview.widget.RecyclerView; +import com.tom.meeter.context.profile.component.binder.EventBinderImpl; +import com.tom.meeter.context.profile.component.viewholder.EventViewHolder; import com.tom.meeter.databinding.EventViewBinding; import com.tom.meeter.infrastructure.components.adapter.BaseEventAdapter; +import com.tom.meeter.infrastructure.components.adapter.OnEventClickListener; import com.tom.meeter.infrastructure.components.binder.EventBinder; -import com.tom.meeter.infrastructure.components.viewholder.EventViewHolder; + +import javax.inject.Inject; /** * created by Tom on 10.02.2017. @@ -19,14 +24,25 @@ public class EventsAdapter extends BaseEventAdapter { private static final String TAG = EventsAdapter.class.getCanonicalName(); - public EventsAdapter(EventBinder binder) { + private final EventBinder binder; + + @Inject + public EventsAdapter(EventBinderImpl binder) { super(binder); + this.binder = binder; logMethod(TAG, this); } + public void initialize( + Context ctx, Runnable onAuthFail, + OnEventClickListener listener) { + binder.setContext(ctx); + binder.setOnAuthFailAction(onAuthFail); + binder.setupOnEventClickListener(listener); + } + @Override public EventViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - logMethod(TAG, this); return new EventViewHolder( EventViewBinding.inflate( LayoutInflater.from(parent.getContext()), parent, false)); diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/SubscribersAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/adapter/SubscribersAdapter.java similarity index 75% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/SubscribersAdapter.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/adapter/SubscribersAdapter.java index 951ab03..b8b4a71 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/adapter/SubscribersAdapter.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/adapter/SubscribersAdapter.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.adapter; +package com.tom.meeter.context.profile.component.adapter; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader; import static com.tom.meeter.context.user.activity.UserActivity.dispatchToUserActivity; @@ -11,15 +11,18 @@ import androidx.recyclerview.widget.RecyclerView; -import com.tom.meeter.context.profile.subscriber.Subscriber; -import com.tom.meeter.context.user.service.UserService; +import com.tom.meeter.context.profile.component.binder.SubscriberBinder; +import com.tom.meeter.context.profile.component.binder.SubscriberBinderImpl; +import com.tom.meeter.context.profile.component.viewholder.SubscriberViewHolder; +import com.tom.meeter.context.profile.domain.Subscriber; +import com.tom.meeter.context.profile.service.ProfileService; import com.tom.meeter.databinding.ActivityProfileSubscriberItemBinding; import com.tom.meeter.infrastructure.components.adapter.BaseAdapter; -import com.tom.meeter.infrastructure.components.binder.SubscriberBinder; -import com.tom.meeter.infrastructure.components.viewholder.SubscriberViewHolder; import com.tom.meeter.infrastructure.http.BaseOnNotAuthenticatedCallback; import com.tom.meeter.infrastructure.http.HttpCodes; +import javax.inject.Inject; + import retrofit2.Call; import retrofit2.Response; @@ -28,21 +31,29 @@ public class SubscribersAdapter private static final String TAG = SubscribersAdapter.class.getCanonicalName(); - private final UserService service; - private final Runnable onAuthFail; - private final Context ctx; + private final ProfileService service; + private final SubscriberBinder binder; + + private Context ctx; + private Runnable onAuthFail; + @Inject public SubscribersAdapter( - UserService service, Runnable onAuthFail, Context ctx, - SubscriberBinder binder) { + ProfileService service, SubscriberBinderImpl binder) { super(binder); - logMethod(TAG, this); - binder.setup( - this::onSubUnSubClick, - user -> dispatchToUserActivity(ctx, user.getId())); + this.binder = binder; this.service = service; - this.onAuthFail = onAuthFail; + logMethod(TAG, this); + } + + public void initialize(Context ctx, Runnable onAuthFail) { this.ctx = ctx; + this.onAuthFail = onAuthFail; + + binder.setContext(ctx); + binder.setOnAuthFailAction(onAuthFail); + binder.setOnSubscribeUnsubscribeClickListener(this::onSubUnSubClick); + binder.setOnUserClickListener(user -> dispatchToUserActivity(ctx, user.getId())); } @Override diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/BaseSubscriberBinder.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/BaseSubscriberBinder.java similarity index 54% rename from AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/BaseSubscriberBinder.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/BaseSubscriberBinder.java index cc46c53..d4498f0 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/BaseSubscriberBinder.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/BaseSubscriberBinder.java @@ -1,8 +1,9 @@ -package com.tom.meeter.infrastructure.components.binder; +package com.tom.meeter.context.profile.component.binder; import androidx.recyclerview.widget.RecyclerView; -import com.tom.meeter.context.profile.subscriber.Subscriber; +import com.tom.meeter.context.profile.domain.Subscriber; +import com.tom.meeter.infrastructure.components.binder.BaseViewHolderBinder; public interface BaseSubscriberBinder extends BaseViewHolderBinder { diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/EventBinderImpl.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/EventBinderImpl.java new file mode 100644 index 0000000..b0d318e --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/EventBinderImpl.java @@ -0,0 +1,92 @@ +package com.tom.meeter.context.profile.component.binder; + +import static com.tom.meeter.infrastructure.common.CommonHelper.handleEventStatus; + +import android.accounts.AccountManager; +import android.content.Context; +import android.graphics.Bitmap; +import android.util.Log; + +import com.tom.meeter.context.auth.infrastructure.AuthHelper; +import com.tom.meeter.context.network.dto.EventDTO; +import com.tom.meeter.context.network.dto.UserDTO; +import com.tom.meeter.context.profile.component.viewholder.EventViewHolder; +import com.tom.meeter.infrastructure.components.adapter.OnEventClickListener; +import com.tom.meeter.infrastructure.components.binder.EventBinder; +import com.tom.meeter.infrastructure.components.downloader.EventImageDownloader; +import com.tom.meeter.infrastructure.components.downloader.UserWithCacheDownloader; + +import javax.inject.Inject; + +public class EventBinderImpl implements EventBinder { + + private static final String TAG = EventBinderImpl.class.getCanonicalName(); + + private final EventImageDownloader eventImageDownloader; + private final UserWithCacheDownloader userDownloader; + + private Context ctx; + private OnEventClickListener listener; + + @Inject + public EventBinderImpl( + EventImageDownloader eventImageDownloader, + UserWithCacheDownloader userDownloader) { + this.eventImageDownloader = eventImageDownloader; + this.userDownloader = userDownloader; + } + + @Override + public void setupOnEventClickListener( + OnEventClickListener listener) { + this.listener = listener; + } + + @Override + public void setContext(Context ctx) { + this.ctx = ctx; + eventImageDownloader.setContext(ctx); + userDownloader.setContext(ctx); + } + + @Override + public void setOnAuthFailAction(Runnable onAuthFail) { + eventImageDownloader.setOnAuthFailAction(onAuthFail); + userDownloader.setOnAuthFailAction(onAuthFail); + } + + @Override + public void bind(EventViewHolder holder, EventDTO event) { + //Log.d(TAG, "Current thread: " + Thread.currentThread().getName()); + String photoPath = event.getPhotoPath(); + + Bitmap cachedPhoto = photoPath != null + ? eventImageDownloader.getCachedPhoto(event.getPhotoPath()) : null; + + UserDTO userCache = userDownloader.getUserCache(event.getCreatorId()); + Log.d(TAG, "User by id " + event.getCreatorId() + " is " + userCache); + + holder.bind( + event.getName(), event.getDescription(), + cachedPhoto, getCreator(userCache), + v -> listener.onClick(event), + v -> handleEventStatus(ctx, v, event.getStatus())); + + if (userCache == null) { + userDownloader.loadUser( + AuthHelper.getAuthHeader(AccountManager.get(ctx)), + event.getCreatorId(), + u -> { + Log.d(TAG, "Downloaded user by id " + event.getCreatorId() + " is " + u); + holder.updateCreator(getCreator(u)); + }); + } + if (photoPath != null && cachedPhoto == null) { + eventImageDownloader.loadPhoto(photoPath, holder::updatePhoto); + } + } + + private static String getCreator(UserDTO user) { + return user == null ? null : user.getName() + " " + user.getSurname(); + } +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/OnSubscribeUnsubscribeClickListener.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/OnSubscribeUnsubscribeClickListener.java new file mode 100644 index 0000000..59e9cf7 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/OnSubscribeUnsubscribeClickListener.java @@ -0,0 +1,7 @@ +package com.tom.meeter.context.profile.component.binder; + +import com.tom.meeter.context.profile.domain.Subscriber; + +public interface OnSubscribeUnsubscribeClickListener { + void onSubUnSub(Subscriber sub, int position); +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/SubscriberBinder.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/SubscriberBinder.java new file mode 100644 index 0000000..f7b2370 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/SubscriberBinder.java @@ -0,0 +1,17 @@ +package com.tom.meeter.context.profile.component.binder; + +import androidx.recyclerview.widget.RecyclerView; + +import com.tom.meeter.infrastructure.components.SetContext; +import com.tom.meeter.infrastructure.components.SetOnAuthFailAction; +import com.tom.meeter.infrastructure.components.adapter.OnUserClickListener; + +public interface SubscriberBinder + extends SetContext, SetOnAuthFailAction, + BaseSubscriberBinder { + + void setOnSubscribeUnsubscribeClickListener( + OnSubscribeUnsubscribeClickListener subUnSubClickListener); + + void setOnUserClickListener(OnUserClickListener userClickListener); +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SubscriberBinderImpl.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/SubscriberBinderImpl.java similarity index 59% rename from AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SubscriberBinderImpl.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/SubscriberBinderImpl.java index 48b55f1..fc638c3 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SubscriberBinderImpl.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/binder/SubscriberBinderImpl.java @@ -1,41 +1,59 @@ -package com.tom.meeter.infrastructure.components.binder; +package com.tom.meeter.context.profile.component.binder; import android.content.Context; import android.graphics.Bitmap; -import com.tom.meeter.context.image.ImageDownloader; import com.tom.meeter.context.network.dto.UserDTO; -import com.tom.meeter.context.profile.subscriber.Subscriber; -import com.tom.meeter.infrastructure.components.UserImageDownloader; -import com.tom.meeter.infrastructure.components.adapter.OnSubscribeUnsubscribeClickListener; +import com.tom.meeter.context.profile.component.viewholder.SubscriberViewHolder; +import com.tom.meeter.context.profile.domain.Subscriber; import com.tom.meeter.infrastructure.components.adapter.OnUserClickListener; -import com.tom.meeter.infrastructure.components.viewholder.SubscriberViewHolder; +import com.tom.meeter.infrastructure.components.downloader.UserImageDownloader; -public class SubscriberBinderImpl extends UserImageDownloader +import javax.inject.Inject; + +public class SubscriberBinderImpl implements SubscriberBinder { - private OnUserClickListener userClickListener; + private final UserImageDownloader userImageDownloader; + private OnSubscribeUnsubscribeClickListener subUnSubClickListener; + private OnUserClickListener userClickListener; + @Inject public SubscriberBinderImpl( - Context ctx, ImageDownloader imgDownloader, Runnable onAuthFail) { - super(ctx, imgDownloader, onAuthFail); + UserImageDownloader userImageDownloader) { + this.userImageDownloader = userImageDownloader; } @Override - public void setup( - OnSubscribeUnsubscribeClickListener subUnSubClickListener, - OnUserClickListener userClickListener) { + public void setOnSubscribeUnsubscribeClickListener( + OnSubscribeUnsubscribeClickListener subUnSubClickListener) { this.subUnSubClickListener = subUnSubClickListener; + } + + @Override + public void setOnUserClickListener( + OnUserClickListener userClickListener) { this.userClickListener = userClickListener; } + @Override + public void setContext(Context ctx) { + userImageDownloader.setContext(ctx); + } + + @Override + public void setOnAuthFailAction(Runnable onAuthFail) { + userImageDownloader.setOnAuthFailAction(onAuthFail); + } + @Override public void bind(SubscriberViewHolder holder, Subscriber target) { UserDTO user = target.getUser(); String photoPath = user.getPhotoPath(); - Bitmap cached = photoPath != null ? cache.get(photoPath) : null; + Bitmap cached = photoPath != null + ? userImageDownloader.getCachedPhoto(photoPath) : null; holder.bind( target.isAmISubscribedTo(), @@ -45,7 +63,7 @@ public void bind(SubscriberViewHolder holder, Subscriber target) { ); if (photoPath != null && cached == null) { - loadPhoto(photoPath, holder::updatePhoto); + userImageDownloader.loadPhoto(photoPath, holder::updatePhoto); } } 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/component/fragment/ActiveEventsFragment.java similarity index 73% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ActiveEventsFragment.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/ActiveEventsFragment.java index ff9f51f..016fff0 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ActiveEventsFragment.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/ActiveEventsFragment.java @@ -1,11 +1,10 @@ -package com.tom.meeter.context.profile.fragment; +package com.tom.meeter.context.profile.component.fragment; import static com.tom.meeter.context.event.activity.EventDispatcherActivity.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; import android.view.View; import android.view.ViewGroup; @@ -16,11 +15,9 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.tom.meeter.App; -import com.tom.meeter.context.image.ImageDownloader; -import com.tom.meeter.context.profile.adapter.EventsAdapter; +import com.tom.meeter.context.profile.component.adapter.EventsAdapter; import com.tom.meeter.databinding.SubFragmentActiveEventsBinding; import com.tom.meeter.infrastructure.common.InfrastructureHelper; -import com.tom.meeter.infrastructure.components.binder.EventBinderImpl; import com.tom.meeter.infrastructure.eventbus.events.IncomeEvents; import org.greenrobot.eventbus.EventBus; @@ -37,10 +34,11 @@ public class ActiveEventsFragment extends Fragment { private static final String TAG = ActiveEventsFragment.class.getCanonicalName(); @Inject - ImageDownloader imageDownloader; + EventsAdapter adapter; private SubFragmentActiveEventsBinding binding; - private EventsAdapter adapter; + private final Runnable onAuthFail = + () -> InfrastructureHelper.restartActivityFromFragment(this); public ActiveEventsFragment() { logMethod(TAG, this); @@ -49,20 +47,16 @@ public ActiveEventsFragment() { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - logMethod(TAG, this); + logMethod(TAG, this, "Registering eventBus"); - ((App) getActivity().getApplication()).getComponent().inject(this); + ((App) getActivity().getApplication()).getProfileComponent().inject(this); EventBus.getDefault().register(this); Context ctx = requireContext(); - adapter = new EventsAdapter( - new EventBinderImpl( - ctx, imageDownloader, - (e) -> dispatchToEventActivity(ctx, e.getId()), - () -> InfrastructureHelper.restartActivityFromFragment(this))); - - Log.d(TAG, "ActiveEventsFragment Registering eventBus"); + adapter.initialize( + ctx, onAuthFail, + (e) -> dispatchToEventActivity(ctx, e.getId())); } @Override @@ -93,8 +87,7 @@ public void onMessageEvent(IncomeEvents eventsSearch) { @Override public void onDestroy() { super.onDestroy(); - logMethod(TAG, this); EventBus.getDefault().unregister(this); - Log.d(TAG, "ActiveEventsFragment Unregistered event bus"); + logMethod(TAG, this, "Unregistered event bus"); } } diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/CreateEventFragment.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/CreateEventFragment.java similarity index 96% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/CreateEventFragment.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/CreateEventFragment.java index 83edaaa..4891fb0 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/CreateEventFragment.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/CreateEventFragment.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.fragment; +package com.tom.meeter.context.profile.component.fragment; import static android.app.Activity.RESULT_OK; import static android.content.Context.BIND_AUTO_CREATE; @@ -6,8 +6,9 @@ import static com.tom.meeter.context.event.activity.EventLocationMapActivity.EXTRA_LNG; import static com.tom.meeter.context.event.activity.ProfileEventActivity.dispatchToProfileEventActivity; import static com.tom.meeter.context.image.activity.BaseUploadActivity.PHOTO_PATH_RESULT; -import static com.tom.meeter.context.profile.activity.NewEventOnMapActivity.createNewEventOnMapActivityIntent; +import static com.tom.meeter.context.profile.component.activity.NewEventOnMapActivity.createNewEventOnMapActivityIntent; import static com.tom.meeter.context.profile.utils.Utils.createEventRequest; +import static com.tom.meeter.infrastructure.common.CommonHelper.getAppLogo; import static com.tom.meeter.infrastructure.common.CommonHelper.isEmpty; import static com.tom.meeter.infrastructure.common.DateHelper.isDateValid; import static com.tom.meeter.infrastructure.common.DateHelper.setCurrentDate; @@ -105,7 +106,7 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); logMethod(TAG, this); - ((App) getActivity().getApplication()).getComponent().inject(this); + ((App) getActivity().getApplication()).getProfileComponent().inject(this); Context ctx = requireContext(); accountManager = AccountManager.get(ctx); @@ -235,7 +236,7 @@ private boolean allSet() { private void showEventDialog(EventDTO event) { new AlertDialog.Builder(requireContext()) - .setIcon(R.drawable.ic_meeter_lr) + .setIcon(getAppLogo()) .setTitle(R.string.event_created) .setMessage( getString(R.string.event_is_created, event.getName())) 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/component/fragment/EventsFragment.java similarity index 98% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/EventsFragment.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/EventsFragment.java index d5704a4..268c3dc 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/EventsFragment.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/EventsFragment.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.fragment; +package com.tom.meeter.context.profile.component.fragment; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; 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/component/fragment/GoogleMapsFragment.java similarity index 81% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/GoogleMapsFragment.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/GoogleMapsFragment.java index 8c8eaa5..cd4ef48 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/GoogleMapsFragment.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/GoogleMapsFragment.java @@ -1,8 +1,7 @@ -package com.tom.meeter.context.profile.fragment; +package com.tom.meeter.context.profile.component.fragment; import static android.content.Context.BIND_AUTO_CREATE; import static com.tom.meeter.context.event.activity.EventDispatcherActivity.dispatchToEventActivity; -import static com.tom.meeter.infrastructure.common.ImagesHelper.circleImage; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; import android.content.ComponentName; @@ -22,7 +21,6 @@ import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; @@ -74,26 +72,26 @@ public class GoogleMapsFragment extends Fragment private static final String TAG = GoogleMapsFragment.class.getCanonicalName(); private static final LatLng DEFAULT = new LatLng(0.0, 0.0); + @Inject + ImageDownloader imageDownloader; private ServiceConnection locationServiceConnection; private LocationTrackerService locationService; private String meString; + private BitmapDescriptor userIcon; private boolean trackUser; + private Set visibleEventStatuses; private int searchArea; private boolean firstOpening = true; + private boolean needLocationUpdates = true; private Marker userMarker = null; private Circle searchCircle = null; private GoogleMap gmap = null; private CameraPosition camPosition = null; private final Map events = new HashMap<>(); - private BitmapDescriptor userIcon; - private GMapEvent lastClickedEvent; - @Inject - ImageDownloader imageDownloader; - public GoogleMapsFragment() { logMethod(TAG, this); } @@ -108,12 +106,13 @@ public void onAttach(Context context) { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - logMethod(TAG, this); + logMethod(TAG, this, "Registering event bus"); readPreferences(); - ((App) getActivity().getApplication()).getComponent().inject(this); + ((App) getActivity().getApplication()).getProfileComponent().inject(this); - MapsInitializer.initialize(getContext()); + Context ctx = requireContext(); + MapsInitializer.initialize(ctx); userIcon = BitmapDescriptorFactory.fromBitmap( Bitmap.createScaledBitmap( BitmapFactory.decodeResource( @@ -122,8 +121,6 @@ public void onCreate(Bundle savedInstanceState) { EventBus.getDefault().register(this); - Log.d(TAG, "GoogleMapsFragment registered event bus"); - locationServiceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder binder) { logMethod(TAG, this); @@ -134,14 +131,12 @@ public void onServiceConnected(ComponentName name, IBinder binder) { public void onServiceDisconnected(ComponentName name) { logMethod(TAG, this); locationService = null; + locationServiceConnection = null; } }; - Context ctx = getContext(); - if (ctx == null) { - throw new IllegalStateException("Context is null"); - } - Intent service = new Intent(ctx, LocationTrackerService.class); - ctx.bindService(service, locationServiceConnection, BIND_AUTO_CREATE); + ctx.bindService( + new Intent(ctx, LocationTrackerService.class), + locationServiceConnection, BIND_AUTO_CREATE); } @Override @@ -152,12 +147,10 @@ public View onCreateView( opts.zoomControlsEnabled(true); SupportMapFragment sMapFragment = SupportMapFragment.newInstance(opts); sMapFragment.getMapAsync(this); - FragmentManager fm = getFragmentManager(); - if (fm != null) { - fm.beginTransaction() - .replace(R.id.event_fragment_sub_fragment_gmap, sMapFragment) - .commit(); - } + getParentFragmentManager() + .beginTransaction() + .replace(R.id.event_fragment_sub_fragment_gmap, sMapFragment) + .commit(); return inflater.inflate(R.layout.sub_fragment_gmaps, container, false); } @@ -169,19 +162,17 @@ public void onMapReady(GoogleMap googleMap) { putExistingMarkersOnMap(); if (locationService == null) { - Log.w(TAG, "Location service is not ready..."); + Log.d(TAG, "Location service is not ready yet or already uninstalled."); } LatLng lastKnownUserLocation = null; if (locationService != null && locationService.canGetLocation()) { Location lkl = locationService.getLastKnownLocation(); if (lkl != null) { + needLocationUpdates = false; lastKnownUserLocation = mapToLatTng(lkl); } else { Log.w(TAG, "Can get location, but service returns null."); } - } else { - Toast.makeText(getContext(), R.string.location_is_disabled, Toast.LENGTH_SHORT).show(); - Log.w(TAG, "Unable to get last known user location."); } moveCamera(lastKnownUserLocation, gmap, firstOpening, camPosition); @@ -205,37 +196,61 @@ public void onMapReady(GoogleMap googleMap) { @Override public void onLocationChanged(Location location) { - logMethod(TAG, this); - if (trackUser) { - Toast.makeText(getContext(), R.string.location_changed, Toast.LENGTH_SHORT).show(); + logMethod(TAG, this, + "trackUser? " + trackUser + + ", needLocationUpdates? " + needLocationUpdates); + if (trackUser || needLocationUpdates) { if (gmap == null) { - Log.w(TAG, "Gmap is not ready..."); + Toast.makeText( + requireContext(), + "Skip location update since google map is not ready", + Toast.LENGTH_SHORT) + .show(); + + Log.w(TAG, "Skip location update since google map is not ready."); if (userMarker != null) { userMarker.setPosition(mapToLatTng(location)); } } else { - if (userMarker == null) { - userMarker = gmap.addMarker(createUserMarkerOptions(mapToLatTng(location))); - } else { - userMarker.setPosition(mapToLatTng(location)); - } + needLocationUpdates = false; + moveUserMarker(location); searchCircle.setCenter(userMarker.getPosition()); - if (camPosition != null) { - gmap.animateCamera(CameraUpdateFactory.newLatLngZoom(userMarker.getPosition(), camPosition.zoom), 1200, null); + gmap.animateCamera( + CameraUpdateFactory.newLatLngZoom( + mapToLatTng(location), ZOOM_VALUE), 6000, null); + if (!trackUser) { + uninstallLocationService(); } } + } else { + uninstallLocationService(); + } + } + + private void moveUserMarker(Location location) { + LatLng latLng = mapToLatTng(location); + if (userMarker == null) { + userMarker = gmap.addMarker(createUserMarkerOptions(latLng)); + } else { + userMarker.setPosition(latLng); } } private void idleListener() { + assert searchCircle != null; if (camPosition == null || camPosition.target.latitude != gmap.getCameraPosition().target.latitude || camPosition.target.longitude != gmap.getCameraPosition().target.longitude) { + if (needLocationUpdates) { + return; + } camPosition = gmap.getCameraPosition(); LatLng position = camPosition.target; Log.d(TAG, "onCameraIdleListener() target:" + position + " zoom:" + camPosition.zoom); searchCircle.setCenter(position); - searchForEvents(position.latitude, position.longitude, searchArea); + searchForEvents( + position.latitude, position.longitude, + searchArea, visibleEventStatuses); } else { // As new coordinates income... camPosition = gmap.getCameraPosition(); @@ -251,7 +266,7 @@ private boolean markerClickListener(Marker marker) { } if (target.equals(lastClickedEvent)) { Log.d(TAG, "Double Click on: " + target.getName()); - dispatchToEventActivity(getContext(), target.getId()); + dispatchToEventActivity(requireContext(), target.getId()); return false; } lastClickedEvent = target; @@ -295,13 +310,22 @@ private Marker addMarkerWithPhoto(EventDTO event) { @Override public void onDestroy() { super.onDestroy(); + logMethod(TAG, this); + uninstallLocationService(); + EventBus.getDefault().unregister(this); + Log.d(TAG, "GoogleMapsFragment Unregistered event bus"); + } + + private void uninstallLocationService() { logMethod(TAG, this); if (locationService != null) { locationService.removeLocationTrackerListener(this); } - getContext().unbindService(locationServiceConnection); - EventBus.getDefault().unregister(this); - Log.d(TAG, "GoogleMapsFragment Unregistered event bus"); + if (locationServiceConnection != null) { + requireContext().unbindService(locationServiceConnection); + } + locationServiceConnection = null; + locationService = null; } @Subscribe(threadMode = ThreadMode.MAIN) @@ -353,16 +377,21 @@ photoPath, requireContext(), private void readPreferences() { searchArea = PreferencesHelper.getSearchArea(getContext()); trackUser = PreferencesHelper.getNeedTrackUser(getContext()); + visibleEventStatuses = PreferencesHelper.getVisibleEventsStatuses(getContext()); } public static void moveCamera( - LatLng lastKnownUserLocation, GoogleMap gmap, boolean firstOpening, CameraPosition camPosition) { + LatLng lastKnownUserLocation, GoogleMap gmap, boolean firstOpening, + CameraPosition camPosition) { if (firstOpening) { if (lastKnownUserLocation != null) { - gmap.animateCamera(CameraUpdateFactory.newLatLngZoom(lastKnownUserLocation, ZOOM_VALUE), 6000, null); + gmap.animateCamera( + CameraUpdateFactory.newLatLngZoom( + lastKnownUserLocation, ZOOM_VALUE), 6000, null); } } else if (camPosition != null) { - gmap.moveCamera(CameraUpdateFactory.newCameraPosition(camPosition)); + gmap.moveCamera( + CameraUpdateFactory.newCameraPosition(camPosition)); } } @@ -407,10 +436,14 @@ private static CircleOptions getCircleOptions(LatLng center, int searchArea) { .fillColor(0x3aaaffff); } - private static void searchForEvents(double latitude, double longitude, int searchArea) { + private static void searchForEvents( + double latitude, double longitude, int searchArea, + Set visibleEventStatuses) { if (searchArea > 0) { EventBus.getDefault() - .post(new SearchForEvents(latitude, longitude, searchArea)); + .post(new SearchForEvents( + latitude, longitude, searchArea, + visibleEventStatuses)); } } diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileEventsFragment.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/ProfileEventsFragment.java similarity index 79% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileEventsFragment.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/ProfileEventsFragment.java index 343e8e1..02ca837 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileEventsFragment.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/ProfileEventsFragment.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.fragment; +package com.tom.meeter.context.profile.component.fragment; import static com.tom.meeter.context.event.activity.EventDispatcherActivity.dispatchToEventActivity; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; @@ -16,13 +16,11 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.tom.meeter.App; -import com.tom.meeter.context.image.ImageDownloader; -import com.tom.meeter.context.profile.adapter.EventsAdapter; +import com.tom.meeter.context.profile.component.adapter.EventsAdapter; +import com.tom.meeter.context.profile.component.viewmodel.ProfileEventsViewModel; import com.tom.meeter.context.profile.factory.ProfileEventsAssistedFactory; -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.EventBinderImpl; import javax.inject.Inject; @@ -30,15 +28,15 @@ public class ProfileEventsFragment extends Fragment { private static final String TAG = ProfileEventsFragment.class.getCanonicalName(); - SubFragmentUserEventsBinding binding; - - private EventsAdapter adapter; - @Inject ProfileEventsAssistedFactory assistedFactory; @Inject - ImageDownloader imageDownloader; + EventsAdapter adapter; + + private SubFragmentUserEventsBinding binding; + private final Runnable onAuthFail = + () -> InfrastructureHelper.restartActivityFromFragment(this); private ProfileEventsViewModel viewModel; public ProfileEventsFragment() { @@ -50,15 +48,13 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); logMethod(TAG, this); - ((App) getActivity().getApplication()).getComponent().inject(this); + ((App) getActivity().getApplication()).getProfileComponent().inject(this); Context ctx = requireContext(); - adapter = new EventsAdapter( - new EventBinderImpl( - ctx, imageDownloader, - (e) -> dispatchToEventActivity(ctx, e.getId()), - () -> InfrastructureHelper.restartActivityFromFragment(this))); + adapter.initialize( + ctx, onAuthFail, + (e) -> dispatchToEventActivity(ctx, e.getId())); /* btnDelete.setOnClickListener(v -> { if (onDeleteButtonClickListener != null) 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/component/fragment/ProfileFragment.java similarity index 92% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileFragment.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/ProfileFragment.java index cdbfc03..fb580f6 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/ProfileFragment.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/ProfileFragment.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.fragment; +package com.tom.meeter.context.profile.component.fragment; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader; import static com.tom.meeter.context.event.activity.EventDispatcherActivity.dispatchToEventActivity; @@ -34,17 +34,16 @@ import com.tom.meeter.context.image.ImageDownloader; import com.tom.meeter.context.image.activity.UploadUserImageActivity; import com.tom.meeter.context.network.dto.UserDTO; -import com.tom.meeter.context.profile.activity.SubscribersActivity; -import com.tom.meeter.context.profile.activity.SubscriptionsActivity; +import com.tom.meeter.context.profile.component.activity.SubscribersActivity; +import com.tom.meeter.context.profile.component.activity.SubscriptionsActivity; +import com.tom.meeter.context.profile.component.viewmodel.ProfileViewModel; import com.tom.meeter.context.profile.factory.ProfileAssistedFactory; import com.tom.meeter.context.profile.message.UpdateProfileRequest; import com.tom.meeter.context.profile.service.ProfileService; -import com.tom.meeter.context.profile.viewmodel.ProfileViewModel; import com.tom.meeter.databinding.FragmentProfileBinding; import com.tom.meeter.infrastructure.common.ImagesHelper; import com.tom.meeter.infrastructure.common.InfrastructureHelper; import com.tom.meeter.infrastructure.components.adapter.EventsCardAdapter; -import com.tom.meeter.infrastructure.components.binder.SimpleEventBinderImpl; import com.tom.meeter.infrastructure.http.BaseOnNotAuthenticatedCallback; import com.tom.meeter.infrastructure.http.HttpCodes; @@ -68,12 +67,13 @@ public class ProfileFragment extends Fragment { @Inject ImageDownloader imageDownloader; @Inject - ProfileService profileService; + ProfileService service; + @Inject + EventsCardAdapter adapter; private final Runnable onAuthFail = () -> InfrastructureHelper.restartActivityFromFragment(this); private AccountManager accountManager; - private EventsCardAdapter adapter; private FragmentProfileBinding binding; private ProfileViewModel viewModel; private boolean isEditableModeEnabled = false; @@ -99,15 +99,13 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); logMethod(TAG, this); - ((App) getActivity().getApplication()).getComponent().inject(this); + ((App) getActivity().getApplication()).getProfileComponent().inject(this); Context ctx = requireContext(); accountManager = AccountManager.get(ctx); - - adapter = new EventsCardAdapter( - new SimpleEventBinderImpl(ctx, imageDownloader, - event -> dispatchToEventActivity(ctx, event.getId()), - onAuthFail)); + adapter.initialize( + ctx, onAuthFail, + event -> dispatchToEventActivity(ctx, event.getId())); } @Override @@ -154,7 +152,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat switchEditMode(); return; } - profileService.updateProfile(getAuthHeader(accountManager), req) + service.updateProfile(getAuthHeader(accountManager), req) .enqueue(new BaseOnNotAuthenticatedCallback<>(ctx, onAuthFail) { @Override public void onResponse( diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/SettingsFragment.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/SettingsFragment.java similarity index 97% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/SettingsFragment.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/SettingsFragment.java index 0f54f40..78e9510 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/SettingsFragment.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/SettingsFragment.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.fragment; +package com.tom.meeter.context.profile.component.fragment; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/SettingsFragmentOld.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/SettingsFragmentOld.java similarity index 90% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/SettingsFragmentOld.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/SettingsFragmentOld.java index 51a767b..8e69173 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/fragment/SettingsFragmentOld.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/fragment/SettingsFragmentOld.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.fragment; +package com.tom.meeter.context.profile.component.fragment; import android.os.Bundle; import android.preference.PreferenceFragment; diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/EventViewHolder.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewholder/EventViewHolder.java similarity index 60% rename from AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/EventViewHolder.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewholder/EventViewHolder.java index 7c09313..6c1e37f 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/EventViewHolder.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewholder/EventViewHolder.java @@ -1,12 +1,15 @@ -package com.tom.meeter.infrastructure.components.viewholder; +package com.tom.meeter.context.profile.component.viewholder; import android.graphics.Bitmap; import android.view.View; +import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; import com.tom.meeter.databinding.EventViewBinding; +import java.util.function.Consumer; + public class EventViewHolder extends RecyclerView.ViewHolder { private final EventViewBinding binding; @@ -17,15 +20,21 @@ public EventViewHolder(EventViewBinding binding) { } public void bind( - String name, String description, Bitmap photo, - View.OnClickListener clickListener) { + String name, String description, Bitmap photo, String creator, + View.OnClickListener clickListener, Consumer statusC) { binding.eventNameCardView.setText(name); binding.eventDescriptionCardView.setText(description); + binding.eventCreatorCardView.setText(creator); binding.eventPhotoCardView.setImageBitmap(photo); binding.eventCardView.setOnClickListener(clickListener); + statusC.accept(binding.eventStatusCardView); } public void updatePhoto(Bitmap photo) { binding.eventPhotoCardView.setImageBitmap(photo); } + + public void updateCreator(String creator) { + binding.eventCreatorCardView.setText(creator); + } } diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/SubscriberViewHolder.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewholder/SubscriberViewHolder.java similarity index 85% rename from AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/SubscriberViewHolder.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewholder/SubscriberViewHolder.java index 8684d3e..bbed795 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/viewholder/SubscriberViewHolder.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewholder/SubscriberViewHolder.java @@ -1,4 +1,4 @@ -package com.tom.meeter.infrastructure.components.viewholder; +package com.tom.meeter.context.profile.component.viewholder; import android.graphics.Bitmap; import android.view.View; @@ -20,13 +20,13 @@ public SubscriberViewHolder(ActivityProfileSubscriberItemBinding binding) { public void bind( boolean isAmSubscribedTo, String name, String surname, Bitmap photo, View.OnClickListener cardClickListener, - View.OnClickListener subUnsubClickListener) { + View.OnClickListener subUnSubClickListener) { binding.subscriberName.setText(name + " " + surname); if (photo != null) { binding.photo.setImageBitmap(photo); } - binding.subUnsubBtn.setOnClickListener(subUnsubClickListener); + binding.subUnsubBtn.setOnClickListener(subUnSubClickListener); binding.subCard.setOnClickListener(cardClickListener); binding.subUnsubBtn.setText(isAmSubscribedTo ? R.string.unsubscribe : R.string.subscribe); } 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/component/viewmodel/ProfileEventsViewModel.java similarity index 97% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileEventsViewModel.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileEventsViewModel.java index f5160a5..0f5b0db 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileEventsViewModel.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileEventsViewModel.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.viewmodel; +package com.tom.meeter.context.profile.component.viewmodel; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileSubscribersViewModel.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileSubscribersViewModel.java similarity index 96% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileSubscribersViewModel.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileSubscribersViewModel.java index e5bb19b..dfce7d8 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileSubscribersViewModel.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileSubscribersViewModel.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.viewmodel; +package com.tom.meeter.context.profile.component.viewmodel; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; @@ -11,8 +11,8 @@ import androidx.lifecycle.ViewModel; import com.tom.meeter.context.network.dto.UserDTO; +import com.tom.meeter.context.profile.domain.Subscriber; import com.tom.meeter.context.profile.service.ProfileService; -import com.tom.meeter.context.profile.subscriber.Subscriber; import com.tom.meeter.infrastructure.http.BaseOnNotAuthenticatedCallback; import com.tom.meeter.infrastructure.http.HttpCodes; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileSubscriptionsViewModel.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileSubscriptionsViewModel.java similarity index 95% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileSubscriptionsViewModel.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileSubscriptionsViewModel.java index 53be0a8..75dedc0 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileSubscriptionsViewModel.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileSubscriptionsViewModel.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.viewmodel; +package com.tom.meeter.context.profile.component.viewmodel; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; @@ -11,8 +11,8 @@ import androidx.lifecycle.ViewModel; import com.tom.meeter.context.network.dto.UserDTO; +import com.tom.meeter.context.profile.domain.Subscriber; import com.tom.meeter.context.profile.service.ProfileService; -import com.tom.meeter.context.profile.subscriber.Subscriber; import com.tom.meeter.infrastructure.http.BaseOnNotAuthenticatedCallback; import com.tom.meeter.infrastructure.http.HttpCodes; 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/component/viewmodel/ProfileViewModel.java similarity index 98% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileViewModel.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileViewModel.java index 0b079d3..120b97a 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/ProfileViewModel.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/ProfileViewModel.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.viewmodel; +package com.tom.meeter.context.profile.component.viewmodel; import static com.tom.meeter.context.auth.infrastructure.AuthHelper.getAuthHeader; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; 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/component/viewmodel/UserEventsViewModel.java similarity index 93% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserEventsViewModel.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/UserEventsViewModel.java index b97fc21..ae7e695 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserEventsViewModel.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/UserEventsViewModel.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.viewmodel; +package com.tom.meeter.context.profile.component.viewmodel; import androidx.lifecycle.LiveData; import androidx.lifecycle.ViewModel; 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/component/viewmodel/UserProfileViewModel.java similarity index 95% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserProfileViewModel.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/UserProfileViewModel.java index a0fbbfc..2be194e 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/viewmodel/UserProfileViewModel.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/component/viewmodel/UserProfileViewModel.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.profile.viewmodel; +package com.tom.meeter.context.profile.component.viewmodel; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/subscriber/Subscriber.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/domain/Subscriber.java similarity index 79% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/subscriber/Subscriber.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/domain/Subscriber.java index 7da101a..da1ff93 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/subscriber/Subscriber.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/domain/Subscriber.java @@ -1,9 +1,9 @@ -package com.tom.meeter.context.profile.subscriber; +package com.tom.meeter.context.profile.domain; -import com.tom.meeter.context.network.dto.EntityBase; +import com.tom.meeter.context.network.dto.BaseEntity; import com.tom.meeter.context.network.dto.UserDTO; -public class Subscriber implements EntityBase { +public class Subscriber implements BaseEntity { private UserDTO user; private boolean amISubscribedTo; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileAssistedFactory.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileAssistedFactory.java index 606bca3..5260af2 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileAssistedFactory.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileAssistedFactory.java @@ -1,6 +1,6 @@ package com.tom.meeter.context.profile.factory; -import com.tom.meeter.context.profile.viewmodel.ProfileViewModel; +import com.tom.meeter.context.profile.component.viewmodel.ProfileViewModel; import dagger.assisted.AssistedFactory; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileEventsAssistedFactory.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileEventsAssistedFactory.java index 1c9eb78..0062a85 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileEventsAssistedFactory.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileEventsAssistedFactory.java @@ -1,6 +1,6 @@ package com.tom.meeter.context.profile.factory; -import com.tom.meeter.context.profile.viewmodel.ProfileEventsViewModel; +import com.tom.meeter.context.profile.component.viewmodel.ProfileEventsViewModel; import dagger.assisted.AssistedFactory; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileSubscribersAssistedFactory.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileSubscribersAssistedFactory.java index 3f94bd1..c4bca79 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileSubscribersAssistedFactory.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileSubscribersAssistedFactory.java @@ -1,6 +1,6 @@ package com.tom.meeter.context.profile.factory; -import com.tom.meeter.context.profile.viewmodel.ProfileSubscribersViewModel; +import com.tom.meeter.context.profile.component.viewmodel.ProfileSubscribersViewModel; import dagger.assisted.AssistedFactory; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileSubscriptionsAssistedFactory.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileSubscriptionsAssistedFactory.java index 039e368..7201150 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileSubscriptionsAssistedFactory.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/factory/ProfileSubscriptionsAssistedFactory.java @@ -1,6 +1,6 @@ package com.tom.meeter.context.profile.factory; -import com.tom.meeter.context.profile.viewmodel.ProfileSubscriptionsViewModel; +import com.tom.meeter.context.profile.component.viewmodel.ProfileSubscriptionsViewModel; import dagger.assisted.AssistedFactory; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/message/SettingsCreateOrUpdate.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/message/SettingsCreateOrUpdate.java new file mode 100644 index 0000000..1dfa36b --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/message/SettingsCreateOrUpdate.java @@ -0,0 +1,49 @@ +package com.tom.meeter.context.profile.message; + +import static com.tom.meeter.context.profile.message.SettingsResponse.NEED_TRACK_USER_KEY; +import static com.tom.meeter.context.profile.message.SettingsResponse.SEARCH_AREA_KEY; +import static com.tom.meeter.context.profile.message.SettingsResponse.VISIBLE_EVENT_STATUSES_KEY; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tom.meeter.context.network.dto.EventDTO; + +import java.util.Optional; +import java.util.Set; + +public class SettingsCreateOrUpdate { + + @JsonProperty(value = SEARCH_AREA_KEY) + private Optional searchArea; + @JsonProperty(value = NEED_TRACK_USER_KEY) + private Optional needTrackUser; + @JsonProperty(value = VISIBLE_EVENT_STATUSES_KEY) + private Optional> visibleEventStatuses; + + public Optional getSearchArea() { + return searchArea; + } + + public void setSearchArea(Integer searchArea) { + this.searchArea = Optional.ofNullable(searchArea); + } + + public Optional getNeedTrackUser() { + return needTrackUser; + } + + public void setNeedTrackUser(Boolean needTrackUser) { + this.needTrackUser = Optional.ofNullable(needTrackUser); + } + + public Optional> getVisibleEventStatuses() { + return visibleEventStatuses; + } + + public void setVisibleEventStatuses(Set visibleEventStatuses) { + this.visibleEventStatuses = Optional.ofNullable(visibleEventStatuses); + } + + public boolean isEmpty() { + return searchArea == null && needTrackUser == null && visibleEventStatuses == null; + } +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/message/SettingsResponse.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/message/SettingsResponse.java new file mode 100644 index 0000000..a767a75 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/message/SettingsResponse.java @@ -0,0 +1,59 @@ +package com.tom.meeter.context.profile.message; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.tom.meeter.context.network.dto.EventDTO; + +import java.util.Set; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class SettingsResponse { + + public static final String ID_KEY = "id"; + public static final String USER_ID_KEY = "user_id"; + public static final String SEARCH_AREA_KEY = "search_area"; + public static final String NEED_TRACK_USER_KEY = "need_track_user"; + public static final String VISIBLE_EVENT_STATUSES_KEY = "visible_event_statuses"; + + private final String id; + private final String userId; + private final Integer searchArea; + private final Boolean needTrackUser; + private final Set visibleEventStatuses; + + @JsonCreator + public SettingsResponse( + @JsonProperty(ID_KEY) String id, + @JsonProperty(USER_ID_KEY) String userId, + @JsonProperty(SEARCH_AREA_KEY) Integer searchArea, + @JsonProperty(NEED_TRACK_USER_KEY) Boolean needTrackUser, + @JsonProperty(VISIBLE_EVENT_STATUSES_KEY) Set visibleEventStatuses) { + this.id = id; + this.userId = userId; + this.searchArea = searchArea; + this.needTrackUser = needTrackUser; + this.visibleEventStatuses = visibleEventStatuses; + } + + public String getId() { + return id; + } + + public String getUserId() { + return userId; + } + + public Integer getSearchArea() { + return searchArea; + } + + + public Boolean getNeedTrackUser() { + return needTrackUser; + } + + public Set getVisibleEventStatuses() { + return visibleEventStatuses; + } +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/repository/user/repository/UserRepository.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/repository/user/repository/UserRepository.java index 1be57a4..8ebc374 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/repository/user/repository/UserRepository.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/repository/user/repository/UserRepository.java @@ -7,7 +7,7 @@ import com.tom.meeter.context.network.dto.UserDTO; import com.tom.meeter.context.profile.repository.user.database.UserDao; import com.tom.meeter.context.profile.repository.user.domain.User; -import com.tom.meeter.context.user.service.UserService; +import com.tom.meeter.context.profile.service.ProfileService; import java.time.LocalDate; import java.util.concurrent.Executor; @@ -23,13 +23,14 @@ public class UserRepository { private static final String TAG = UserRepository.class.getCanonicalName(); private static final Object MARKER = new Object(); - private final UserService userService; + private final ProfileService service; private final UserDao userDao; private final Executor executor; //@Inject - public UserRepository(UserService userService, UserDao userDao, Executor executor) { - this.userService = userService; + public UserRepository( + ProfileService service, UserDao userDao, Executor executor) { + this.service = service; this.userDao = userDao; this.executor = executor; } @@ -45,7 +46,7 @@ private void refreshUser(String id) { .flatMapCompletable(ign -> Completable.fromAction(() -> { //TODO null String header = null; - Response response = userService.getUser(header, id).execute(); + Response response = service.getUser(header, id).execute(); if (response.isSuccessful()) { UserDTO body = response.body(); LocalDate birthday = body.getBirthday(); 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 bd05010..736e15e 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 @@ -15,8 +15,10 @@ import retrofit2.http.Header; import retrofit2.http.PATCH; import retrofit2.http.POST; +import retrofit2.http.Path; public interface ProfileService { + @GET("/profile") Call getProfile(@Header(AUTH_HEADER) String authHeader); @@ -33,10 +35,19 @@ Call updateProfile( @GET("/profile/subscriptions") Call> getMySubscriptions(@Header(AUTH_HEADER) String authHeader); + @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); + @POST("/event") Call createEvent(@Header(AUTH_HEADER) String authHeader, @Body CreateEventRequest req); + @Deprecated + @GET("/user/{id}") + Call getUser(@Header(AUTH_HEADER) String authHeader, @Path("id") String userId); /* soon... @GET("/publish") Call publishEvent( diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/service/SettingsService.java b/AndroidClient/src/main/java/com/tom/meeter/context/profile/service/SettingsService.java similarity index 70% rename from AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/service/SettingsService.java rename to AndroidClient/src/main/java/com/tom/meeter/context/profile/service/SettingsService.java index a4ea675..0fb6eaa 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/service/SettingsService.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/profile/service/SettingsService.java @@ -1,9 +1,9 @@ -package com.tom.meeter.context.profile.settings.service; +package com.tom.meeter.context.profile.service; import static com.tom.meeter.infrastructure.common.Globals.AUTH_HEADER; -import com.tom.meeter.context.profile.settings.message.SettingsCreateOrUpdate; -import com.tom.meeter.context.profile.settings.message.SettingsResponse; +import com.tom.meeter.context.profile.message.SettingsCreateOrUpdate; +import com.tom.meeter.context.profile.message.SettingsResponse; import retrofit2.Call; import retrofit2.http.Body; 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 deleted file mode 100644 index 69977cc..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsCreateOrUpdate.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.tom.meeter.context.profile.settings.message; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class SettingsCreateOrUpdate { - - @JsonProperty(value = "search_area") - private Integer searchArea; - @JsonProperty(value = "need_track_user") - private Boolean needTrackUser; - - public SettingsCreateOrUpdate(Integer searchArea, Boolean needTrackUser) { - this.searchArea = searchArea; - this.needTrackUser = needTrackUser; - } - - public Integer getSearchArea() { - return searchArea; - } - - public Boolean getNeedTrackUser() { - return 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 deleted file mode 100644 index 238fe94..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/context/profile/settings/message/SettingsResponse.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.tom.meeter.context.profile.settings.message; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class SettingsResponse { - - 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; - this.userId = userId; - this.searchArea = searchArea; - this.needTrackUser = needTrackUser; - } - - public String getId() { - return id; - } - - public String getUserId() { - return userId; - } - - public Integer getSearchArea() { - return searchArea; - } - - - public Boolean getNeedTrackUser() { - return needTrackUser; - } -} 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 index 0c402f4..553d598 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/user/UserComponent.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/UserComponent.java @@ -12,7 +12,9 @@ @UserScope @Component( - dependencies = {AppComponent.class}) + modules = {UserModule.class}, + dependencies = {AppComponent.class} +) public interface UserComponent { @Component.Builder 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..65768fd --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/UserModule.java @@ -0,0 +1,32 @@ +package com.tom.meeter.context.user; + +import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; +import static com.tom.meeter.infrastructure.common.RetrofitBuilder.createBuilder; + +import android.app.Application; + +import androidx.annotation.NonNull; + +import com.tom.meeter.context.user.service.UserService; +import com.tom.meeter.infrastructure.common.RetrofitBuilder; + +import dagger.Module; +import dagger.Provides; + +@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 createBuilder(app, RetrofitBuilder.jtm) + .create(UserService.class); + } +} 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 dc77abd..f4d8d93 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 @@ -27,8 +27,7 @@ import com.tom.meeter.App; import com.tom.meeter.R; import com.tom.meeter.context.auth.infrastructure.AuthHelper; -import com.tom.meeter.context.image.ImageDownloader; -import com.tom.meeter.context.profile.activity.ProfileActivity; +import com.tom.meeter.context.profile.component.activity.ProfileActivity; import com.tom.meeter.context.token.service.TokenService; import com.tom.meeter.context.user.factory.UserAssistedFactory; import com.tom.meeter.context.user.service.UserService; @@ -36,7 +35,6 @@ 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.SimpleEventBinderImpl; import com.tom.meeter.infrastructure.http.BaseOnNotAuthenticatedCallback; import com.tom.meeter.infrastructure.http.HttpCodes; @@ -59,14 +57,14 @@ public class UserActivity extends AppCompatActivity { @Inject UserAssistedFactory assistedFactory; @Inject - ImageDownloader imgDownloader; + EventsCardAdapter adapter; private ActivityUserBinding binding; private UserViewModel viewModel; private String userId; private AccountManager accountManager; - private EventsCardAdapter adapter; private Boolean amISubscriber; + private final Runnable onAuthFail = this::recreate; @Override protected void onCreate(Bundle savedInstanceState) { @@ -80,10 +78,9 @@ protected void onCreate(Bundle savedInstanceState) { ((App) getApplication()).getUserComponent().inject(this); - adapter = new EventsCardAdapter( - new SimpleEventBinderImpl( - this, imgDownloader, - event -> dispatchToEventActivity(this, event.getId()), this::recreate)); + adapter.initialize( + this, onAuthFail, + event -> dispatchToEventActivity(this, event.getId())); //setToken(accountManager, Launcher.EXPIRED); checkToken(this::onInit, this::finish, accountManager, this, tokenService); @@ -123,7 +120,7 @@ private void onInit(String token) { } if (amISubscriber) { userService.unsubscribe(Globals.getAuthHeader(token), userId).enqueue( - new BaseOnNotAuthenticatedCallback<>(this, this::recreate) { + new BaseOnNotAuthenticatedCallback<>(this, onAuthFail) { @Override public void onResponse(Call call, Response resp) { super.onResponse(call, resp); @@ -136,7 +133,7 @@ public void onResponse(Call call, Response resp) { }); } else { userService.subscribe(Globals.getAuthHeader(token), userId).enqueue( - new BaseOnNotAuthenticatedCallback<>(this, this::recreate) { + new BaseOnNotAuthenticatedCallback<>(this, onAuthFail) { @Override public void onResponse(Call call, Response resp) { super.onResponse(call, resp); @@ -153,7 +150,7 @@ public void onResponse(Call call, Response resp) { viewModel = new ViewModelProvider( this, assistedFactory.factory( - assistedFactory, userId, this, this::recreate)) + assistedFactory, userId, this, onAuthFail)) .get(UserViewModel.class); binding.events.setLayoutManager(new GridLayoutManager(this, 2)); diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserSubscribersActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserSubscribersActivity.java index 2e62cfc..d0b5c7f 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserSubscribersActivity.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserSubscribersActivity.java @@ -18,13 +18,11 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.tom.meeter.App; -import com.tom.meeter.context.image.ImageDownloader; import com.tom.meeter.context.token.service.TokenService; -import com.tom.meeter.context.user.adapter.UsersAdapter; +import com.tom.meeter.context.user.components.adapter.UsersAdapter; import com.tom.meeter.context.user.factory.UserSubscribersAssistedFactory; import com.tom.meeter.context.user.viewmodel.UserSubscribersViewModel; import com.tom.meeter.databinding.ActivityProfileSubscribersBinding; -import com.tom.meeter.infrastructure.components.binder.UserBinderImpl; import javax.inject.Inject; @@ -37,13 +35,12 @@ public class UserSubscribersActivity extends AppCompatActivity { @Inject UserSubscribersAssistedFactory assistedFactory; @Inject - ImageDownloader imgDownloader; + UsersAdapter adapter; private final Runnable onAuthFail = this::recreate; private AccountManager accountManager; private ActivityProfileSubscribersBinding binding; private UserSubscribersViewModel viewModel; - private UsersAdapter adapter; private String userId; @Override @@ -68,9 +65,7 @@ protected void onCreate(Bundle savedInstanceState) { ((App) getApplication()).getUserComponent().inject(this); accountManager = AccountManager.get(this); - adapter = new UsersAdapter( - this, - new UserBinderImpl(this, imgDownloader, onAuthFail)); + adapter.initialize(this, onAuthFail); //setToken(accountManager, Launcher.EXPIRED); checkToken(this::onInit, this::finish, accountManager, this, tokenService); diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserSubscriptionsActivity.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserSubscriptionsActivity.java index 4f52e43..cee895c 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserSubscriptionsActivity.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/activity/UserSubscriptionsActivity.java @@ -18,13 +18,11 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.tom.meeter.App; -import com.tom.meeter.context.image.ImageDownloader; import com.tom.meeter.context.token.service.TokenService; -import com.tom.meeter.context.user.adapter.UsersAdapter; +import com.tom.meeter.context.user.components.adapter.UsersAdapter; import com.tom.meeter.context.user.factory.UserSubscriptionsAssistedFactory; import com.tom.meeter.context.user.viewmodel.UserSubscriptionsViewModel; import com.tom.meeter.databinding.ActivityProfileSubscriptionsBinding; -import com.tom.meeter.infrastructure.components.binder.UserBinderImpl; import javax.inject.Inject; @@ -37,13 +35,13 @@ public class UserSubscriptionsActivity extends AppCompatActivity { @Inject UserSubscriptionsAssistedFactory assistedFactory; @Inject - ImageDownloader imgDownloader; + UsersAdapter adapter; private ActivityProfileSubscriptionsBinding binding; private UserSubscriptionsViewModel viewModel; - private UsersAdapter adapter; private AccountManager accountManager; private String userId; + private final Runnable onAuthFail = this::recreate; @Override protected void onCreate(Bundle savedInstanceState) { @@ -64,12 +62,14 @@ protected void onCreate(Bundle savedInstanceState) { return; } + binding = ActivityProfileSubscriptionsBinding.inflate(getLayoutInflater()); + View view = binding.getRoot(); + setContentView(view); + ((App) getApplication()).getUserComponent().inject(this); accountManager = AccountManager.get(this); - adapter = new UsersAdapter( - this, - new UserBinderImpl(this, imgDownloader, this::recreate)); + adapter.initialize(this, onAuthFail); //setToken(accountManager, Launcher.EXPIRED); checkToken(this::onInit, this::finish, accountManager, this, tokenService); @@ -77,14 +77,11 @@ protected void onCreate(Bundle savedInstanceState) { private void onInit(String token) { logMethod(TAG, this); - binding = ActivityProfileSubscriptionsBinding.inflate(getLayoutInflater()); - View view = binding.getRoot(); - setContentView(view); viewModel = new ViewModelProvider( this, assistedFactory.factory( - assistedFactory, userId, this, this::recreate)) + assistedFactory, userId, this, onAuthFail)) .get(UserSubscriptionsViewModel.class); binding.recyclerSubscriptions.setLayoutManager(new LinearLayoutManager(this)); diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/adapter/GridAdapter.java similarity index 96% rename from AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridAdapter.java rename to AndroidClient/src/main/java/com/tom/meeter/context/user/components/adapter/GridAdapter.java index 105a2fe..cb59a38 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridAdapter.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/adapter/GridAdapter.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.user.adapter; +package com.tom.meeter.context.user.components.adapter; import android.content.Context; import android.view.View; 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/components/adapter/GridViewAdapter.java similarity index 97% rename from AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridViewAdapter.java rename to AndroidClient/src/main/java/com/tom/meeter/context/user/components/adapter/GridViewAdapter.java index 657f567..13dbd39 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/GridViewAdapter.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/adapter/GridViewAdapter.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.user.adapter; +package com.tom.meeter.context.user.components.adapter; import static com.tom.meeter.infrastructure.common.ImagesHelper.circleImage; diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/UsersAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/adapter/UsersAdapter.java similarity index 66% rename from AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/UsersAdapter.java rename to AndroidClient/src/main/java/com/tom/meeter/context/user/components/adapter/UsersAdapter.java index 1571901..d7d9755 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/context/user/adapter/UsersAdapter.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/adapter/UsersAdapter.java @@ -1,4 +1,4 @@ -package com.tom.meeter.context.user.adapter; +package com.tom.meeter.context.user.components.adapter; import static com.tom.meeter.context.user.activity.UserActivity.dispatchToUserActivity; import static com.tom.meeter.infrastructure.common.InfrastructureHelper.logMethod; @@ -10,19 +10,33 @@ import androidx.recyclerview.widget.RecyclerView; import com.tom.meeter.context.network.dto.UserDTO; +import com.tom.meeter.context.user.components.binder.UserBinder; +import com.tom.meeter.context.user.components.binder.UserBinderImpl; import com.tom.meeter.databinding.ActivityUserSubscriberItemBinding; import com.tom.meeter.infrastructure.components.adapter.BaseAdapter; -import com.tom.meeter.infrastructure.components.binder.UserBinder; import com.tom.meeter.infrastructure.components.viewholder.UserViewHolder; +import javax.inject.Inject; + public class UsersAdapter extends BaseAdapter { private static final String TAG = UsersAdapter.class.getCanonicalName(); - public UsersAdapter(Context ctx, UserBinder binder) { + private final UserBinder binder; + + @Inject + public UsersAdapter(UserBinderImpl binder) { super(binder); + this.binder = binder; logMethod(TAG, this); - binder.setup(user -> dispatchToUserActivity(ctx, user.getId())); + } + + public void initialize( + Context ctx, Runnable onAuthFail) { + binder.setContext(ctx); + binder.setOnUserClickListener( + user -> dispatchToUserActivity(ctx, user.getId())); + binder.setOnAuthFailAction(onAuthFail); } @Override diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/BaseUserBinder.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/binder/BaseUserBinder.java similarity index 66% rename from AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/BaseUserBinder.java rename to AndroidClient/src/main/java/com/tom/meeter/context/user/components/binder/BaseUserBinder.java index f0d7e12..eee1544 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/BaseUserBinder.java +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/binder/BaseUserBinder.java @@ -1,8 +1,9 @@ -package com.tom.meeter.infrastructure.components.binder; +package com.tom.meeter.context.user.components.binder; import androidx.recyclerview.widget.RecyclerView; import com.tom.meeter.context.network.dto.UserDTO; +import com.tom.meeter.infrastructure.components.binder.BaseViewHolderBinder; public interface BaseUserBinder extends BaseViewHolderBinder { diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/components/binder/UserBinder.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/binder/UserBinder.java new file mode 100644 index 0000000..1c986f9 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/binder/UserBinder.java @@ -0,0 +1,13 @@ +package com.tom.meeter.context.user.components.binder; + +import androidx.recyclerview.widget.RecyclerView; + +import com.tom.meeter.infrastructure.components.SetContext; +import com.tom.meeter.infrastructure.components.SetOnAuthFailAction; +import com.tom.meeter.infrastructure.components.adapter.OnUserClickListener; + +public interface UserBinder + extends SetContext, SetOnAuthFailAction, + BaseUserBinder { + void setOnUserClickListener(OnUserClickListener listener); +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/context/user/components/binder/UserBinderImpl.java b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/binder/UserBinderImpl.java new file mode 100644 index 0000000..9ad8d48 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/context/user/components/binder/UserBinderImpl.java @@ -0,0 +1,55 @@ +package com.tom.meeter.context.user.components.binder; + +import android.content.Context; +import android.graphics.Bitmap; + +import com.tom.meeter.context.network.dto.UserDTO; +import com.tom.meeter.infrastructure.components.adapter.OnUserClickListener; +import com.tom.meeter.infrastructure.components.downloader.UserImageDownloader; +import com.tom.meeter.infrastructure.components.viewholder.UserViewHolder; + +import javax.inject.Inject; + +public class UserBinderImpl implements UserBinder { + + private final UserImageDownloader userImageDownloader; + + private OnUserClickListener listener; + + @Inject + public UserBinderImpl( + UserImageDownloader userImageDownloader) { + this.userImageDownloader = userImageDownloader; + } + + @Override + public void setOnUserClickListener(OnUserClickListener listener) { + this.listener = listener; + } + + @Override + public void setContext(Context ctx) { + userImageDownloader.setContext(ctx); + } + + @Override + public void setOnAuthFailAction(Runnable onAuthFail) { + userImageDownloader.setOnAuthFailAction(onAuthFail); + } + + @Override + public void bind(UserViewHolder holder, UserDTO user) { + String photoPath = user.getPhotoPath(); + + Bitmap cached = photoPath != null + ? userImageDownloader.getCachedPhoto(photoPath) : null; + + holder.bind( + user.getName(), user.getSurname(), cached, + v -> listener.onClick(user)); + + if (photoPath != null && cached == null) { + userImageDownloader.loadPhoto(photoPath, holder::updatePhoto); + } + } +} 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 4427dd0..f77b9b5 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 @@ -1,11 +1,16 @@ package com.tom.meeter.infrastructure.common; import android.content.Context; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; import android.util.Log; +import android.util.TypedValue; +import android.widget.TextView; import androidx.annotation.Nullable; import com.tom.meeter.R; +import com.tom.meeter.context.network.dto.EventDTO; import com.tom.meeter.context.network.dto.UserDTO; import java.time.LocalDate; @@ -16,6 +21,8 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import java.util.HashMap; +import java.util.Map; public final class CommonHelper { @@ -31,8 +38,35 @@ private CommonHelper() { public static final DateTimeFormatter UI_TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm"); + private static Map nameToStatusMapping; + public static final String EMPTY_STR = ""; + + public static EventDTO.EventStatus resolveStatus(Context ctx, String statusName) { + if (nameToStatusMapping == null) { + initializeNameToStatusMapping(ctx); + } + return nameToStatusMapping.get(statusName); + } + + private static void initializeNameToStatusMapping(Context ctx) { + String[] statuses = ctx.getResources().getStringArray(R.array.statuses); + nameToStatusMapping = new HashMap<>(statuses.length); + nameToStatusMapping.put(statuses[0], EventDTO.EventStatus.CREATED); + nameToStatusMapping.put(statuses[1], EventDTO.EventStatus.PUBLISHED); + nameToStatusMapping.put(statuses[2], EventDTO.EventStatus.UNPUBLISHED); + nameToStatusMapping.put(statuses[3], EventDTO.EventStatus.SCHEDULED); + nameToStatusMapping.put(statuses[4], EventDTO.EventStatus.STARTED); + nameToStatusMapping.put(statuses[5], EventDTO.EventStatus.PAUSED); + nameToStatusMapping.put(statuses[6], EventDTO.EventStatus.RESUMED); + nameToStatusMapping.put(statuses[7], EventDTO.EventStatus.CANCELLED); + nameToStatusMapping.put(statuses[8], EventDTO.EventStatus.FINISHED); + // no name for EventDTO.EventStatus.ARCHIVED status, + // unable to select it for filtering + // nameToStatusMapping.put(statuses[9], EventDTO.EventStatus.ARCHIVED); + } + public static String genderResolver(Context ctx, UserDTO.UserGender gender) { return switch (gender) { case FEMALE -> ctx.getString(R.string.female_gender); @@ -41,6 +75,65 @@ public static String genderResolver(Context ctx, UserDTO.UserGender gender) { }; } + public static String eventStatusResolver(Context ctx, EventDTO.EventStatus status) { + return switch (status) { + case CREATED -> ctx.getString(R.string.created_status); + case PUBLISHED -> ctx.getString(R.string.published_status); + case UNPUBLISHED -> ctx.getString(R.string.unpublished_status); + case SCHEDULED -> ctx.getString(R.string.scheduled_status); + case STARTED -> ctx.getString(R.string.started_status); + case PAUSED -> ctx.getString(R.string.paused_status); + case RESUMED -> ctx.getString(R.string.resumed_status); + case FINISHED -> ctx.getString(R.string.finished_status); + case CANCELLED -> ctx.getString(R.string.cancelled_status); + case ARCHIVED -> ctx.getString(R.string.archived_status); + default -> throw new IllegalArgumentException("#args " + status); + }; + } + + public static void setStatusColor(TextView statusView, int colorRes) { + statusView.setBackgroundResource(colorRes); + } + + public static int getStatusColor(Context ctx, EventDTO.EventStatus status) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + throw new IllegalArgumentException("#Unable to get status color " + status); + } + return switch (status) { + case CREATED -> ctx.getColor(R.color.created_status); + case PUBLISHED -> ctx.getColor(R.color.published_status); + case UNPUBLISHED -> ctx.getColor(R.color.unpublished_status); + case SCHEDULED -> ctx.getColor(R.color.scheduled_status); + case STARTED -> ctx.getColor(R.color.started_status); + case PAUSED -> ctx.getColor(R.color.paused_status); + case RESUMED -> ctx.getColor(R.color.resumed_status); + case FINISHED -> ctx.getColor(R.color.finished_status); + case CANCELLED -> ctx.getColor(R.color.cancelled_status); + case ARCHIVED -> ctx.getColor(R.color.archived_status); + default -> throw new IllegalArgumentException("#args " + status); + }; + } + + public static void handleEventStatus( + Context ctx, TextView view, EventDTO.EventStatus status) { + view.setText(eventStatusResolver(ctx, status)); + setRoundedBackground(view, getStatusColor(ctx, status), 6f); + } + + public static void setRoundedBackground( + TextView view, int backgroundColor, float cornerRadiusDp) { + GradientDrawable drawable = new GradientDrawable(); + drawable.setShape(GradientDrawable.RECTANGLE); + drawable.setColor(backgroundColor); + float radiusPx = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + cornerRadiusDp, + view.getResources().getDisplayMetrics() + ); + drawable.setCornerRadius(radiusPx); + view.setBackground(drawable); + } + @Nullable public static CharSequence dateOrNull(OffsetDateTime date) { return date == null ? null : UI_DATE_TIME_FORMAT.format(date); @@ -114,4 +207,11 @@ public static OffsetDateTime getOffsetDateTimeOrNull( OffsetDateTime.now().getOffset()); } + public static int getAppLogo() { + return R.drawable.meeter_new_logo_512x512; + } + + public static int getSmallAppLogo() { + return R.drawable.meeter_new_logo_64x64; + } } 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 b9fadf7..b3949a4 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 @@ -3,8 +3,12 @@ import android.content.Context; import android.util.Log; +import com.tom.meeter.context.network.dto.EventDTO; + import java.io.IOException; +import java.util.HashSet; import java.util.Properties; +import java.util.Set; /** * Some well knows application constants. @@ -29,6 +33,7 @@ private Globals() { public static final String LOCATION_TIME_PROPERTY = "location.time"; public static final String MAP_EVENTS_AREA_PROPERTY = "map.events_area"; public static final String MAP_TRACK_USER_PROPERTY = "map.track_user"; + public static final String EVENTS_VISIBLE_STATUSES_PROPERTY = "events.visible_statuses"; public static final String AUTH_HEADER = "Authorization"; public static final String BEARER_FORMAT = "Bearer %s"; @@ -37,6 +42,7 @@ private Globals() { private static String socketIOPath; private static Boolean needTrackUserDefault; private static Integer searchAreaDefault; + private static Set visibleEventsStatusesDefault; public static String getServerPath(Context ctx) { @@ -87,6 +93,21 @@ public static int getDefaultSearchArea(Context ctx) { return searchAreaDefault; } + public static Set getDefaultVisibleEventsStatuses( + Context ctx) { + if (visibleEventsStatusesDefault != null) { + return visibleEventsStatusesDefault; + } + String[] statuses = tryGetProps(ctx) + .getProperty(EVENTS_VISIBLE_STATUSES_PROPERTY) + .split(","); + visibleEventsStatusesDefault = new HashSet<>(statuses.length); + for (String s : statuses) { + visibleEventsStatusesDefault.add(EventDTO.EventStatus.fromString(s)); + } + return visibleEventsStatusesDefault; + } + private static Properties tryGetProps(Context ctx) { Properties p = new Properties(); try { diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/PreferencesHelper.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/PreferencesHelper.java index e5657bd..7a3379d 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/PreferencesHelper.java +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/PreferencesHelper.java @@ -1,18 +1,127 @@ package com.tom.meeter.infrastructure.common; import static androidx.preference.PreferenceManager.getDefaultSharedPreferences; +import static com.tom.meeter.context.auth.infrastructure.AuthHelper.invalidateToken; +import static com.tom.meeter.context.network.dto.EventDTO.EventStatus.transformToStrings; import static com.tom.meeter.infrastructure.common.Globals.getDefaultSearchArea; import static com.tom.meeter.infrastructure.common.Globals.getDefaultTrackUser; +import static com.tom.meeter.infrastructure.common.Globals.getDefaultVisibleEventsStatuses; +import android.accounts.AccountManager; +import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.util.Log; import com.tom.meeter.R; +import com.tom.meeter.context.auth.infrastructure.AuthHelper; +import com.tom.meeter.context.launcher.Launcher; +import com.tom.meeter.context.network.dto.EventDTO; +import com.tom.meeter.context.profile.message.SettingsCreateOrUpdate; +import com.tom.meeter.context.profile.message.SettingsResponse; +import com.tom.meeter.context.profile.service.SettingsService; +import com.tom.meeter.infrastructure.http.HttpCodes; +import com.tom.meeter.infrastructure.http.HttpErrorLogger; + +import java.util.Set; + +import retrofit2.Call; +import retrofit2.Response; public class PreferencesHelper { - public static boolean isDefaulted(Context ctx) { - return (getNeedTrackUser(ctx) == getDefaultTrackUser(ctx)) - && (getSearchArea(ctx) == getDefaultSearchArea(ctx)); + private static final String TAG = PreferencesHelper.class.getCanonicalName(); + + private static Set defaultStringEventStatuses; + + public static void cleanLocalPrefs(Context ctx) { + getDefaultSharedPreferences(ctx).edit().clear().apply(); + } + + public static void updateLocalPrefs(Context ctx, SettingsResponse resp) { + SharedPreferences.Editor edit = getDefaultSharedPreferences(ctx).edit(); + + String searchAreaPref = ctx.getString(R.string.prefs_search_area); + Integer searchArea = resp.getSearchArea(); + if (searchArea != null) { + edit.putInt(searchAreaPref, searchArea); + } else { + edit.remove(searchAreaPref); + } + + String needTrackUserPref = ctx.getString(R.string.prefs_need_track_user); + Boolean needTrackUser = resp.getNeedTrackUser(); + if (needTrackUser != null) { + edit.putBoolean(needTrackUserPref, needTrackUser); + } else { + edit.remove(needTrackUserPref); + } + + String visibleStatusesPref = ctx.getString(R.string.prefs_visible_event_statuses); + Set statuses = resp.getVisibleEventStatuses(); + if (statuses != null) { + edit.putStringSet(visibleStatusesPref, transformToStrings(statuses)); + } else { + edit.remove(visibleStatusesPref); + } + + edit.apply(); + } + + public static void savePrefsToServer( + Activity activity, SettingsService service, + SettingsCreateOrUpdate req, Runnable afterSave) { + AccountManager am = AccountManager.get(activity); + service.createOrUpdateSettings(req, AuthHelper.getAuthHeader(am)) + .enqueue(new HttpErrorLogger<>(activity) { + @Override + public void onResponse(Call call, Response resp) { + super.onResponse(call, resp); + if (resp.code() == HttpCodes.NOT_AUTHENTICATED) { + invalidateToken( + am, activity, + (freshToken) -> sendSavePrefsRetry( + ctx, service, freshToken, req, afterSave), + () -> { + Log.d(TAG, "Canceled auth."); + ctx.startActivity(new Intent(ctx, Launcher.class)); + }); + return; + } + if (resp.code() == HttpCodes.OK || resp.code() == HttpCodes.CREATED) { + Log.d(TAG, "created/updated server settings."); + updateLocalPrefs(activity, resp.body()); + afterSave.run(); + return; + } + } + }); + } + + public static void savePrefsToServer( + Activity activity, SettingsService service, + SettingsCreateOrUpdate req) { + savePrefsToServer(activity, service, req, () -> { + }); + } + + private static void sendSavePrefsRetry( + Context ctx, SettingsService service, + String token, SettingsCreateOrUpdate req, Runnable afterSave) { + service.createOrUpdateSettings(req, Globals.getAuthHeader(token)) + .enqueue(new HttpErrorLogger<>(ctx) { + @Override + public void onResponse(Call call, Response resp) { + super.onResponse(call, resp); + if (resp.code() == HttpCodes.OK || resp.code() == HttpCodes.CREATED) { + Log.d(TAG, "Created/updated server settings on retry."); + updateLocalPrefs(ctx, resp.body()); + afterSave.run(); + return; + } + } + }); } public static boolean getNeedTrackUser(Context ctx) { @@ -25,6 +134,24 @@ public static int getSearchArea(Context ctx) { .getInt(ctx.getString(R.string.prefs_search_area), getDefaultSearchArea(ctx)); } + public static Set getVisibleEventsStatuses( + Context ctx) { + return EventDTO.EventStatus.transformToEnums( + getDefaultSharedPreferences(ctx) + .getStringSet(ctx.getString(R.string.prefs_visible_event_statuses), + getDefaultStringEventStatuses(ctx))); + } + + private static Set getDefaultStringEventStatuses( + Context ctx) { + if (defaultStringEventStatuses != null) { + return defaultStringEventStatuses; + } + defaultStringEventStatuses = EventDTO.EventStatus.transformToStrings( + getDefaultVisibleEventsStatuses(ctx)); + return defaultStringEventStatuses; + } + private PreferencesHelper() { // } diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/RetrofitBuilder.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/RetrofitBuilder.java new file mode 100644 index 0000000..512d1d8 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/common/RetrofitBuilder.java @@ -0,0 +1,56 @@ +package com.tom.meeter.infrastructure.common; + +import static com.tom.meeter.infrastructure.common.Globals.getServerPath; + +import android.app.Application; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.Module; +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 java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.TimeZone; + +import retrofit2.Retrofit; +import retrofit2.converter.jackson.JacksonConverterFactory; + +public class RetrofitBuilder { + + public static final JavaTimeModule jtm = new JavaTimeModule(); + + public static final Jdk8Module jdk8m = new Jdk8Module(); + //.addModule(new Jdk8Module().configureReadAbsentAsNull(false)) + + private RetrofitBuilder() { + } + + public static Retrofit createBuilder( + Application app, Module module) { + return createBuilder(app, Collections.singletonList(module)); + } + + public static Retrofit createBuilder(Application app) { + return createBuilder(app, Arrays.asList(jtm, jdk8m)); + } + + public static Retrofit createBuilder( + Application app, List modules) { + JsonMapper.Builder builder = JsonMapper.builder(); + for (Module m : modules) { + builder.addModule(m); + } + return new Retrofit.Builder() + .baseUrl(getServerPath(app)) + .addConverterFactory(JacksonConverterFactory.create( + builder.serializationInclusion(JsonInclude.Include.NON_NULL) + .build() + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .setTimeZone(TimeZone.getDefault()))) + .build(); + } +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/PhotoWithCacheDownloader.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/PhotoWithCacheDownloader.java deleted file mode 100644 index 69d225d..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/PhotoWithCacheDownloader.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.tom.meeter.infrastructure.components; - -import android.content.Context; -import android.graphics.Bitmap; -import android.util.Log; - -import com.tom.meeter.context.image.ImageDownloader; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Consumer; - -public abstract class PhotoWithCacheDownloader { - - private static final String TAG = PhotoWithCacheDownloader.class.getCanonicalName(); - - protected final Context ctx; - protected final ImageDownloader imgDownloader; - protected final Runnable onAuthFail; - protected final Map cache = new ConcurrentHashMap<>(); - - protected PhotoWithCacheDownloader( - Context ctx, ImageDownloader imgDownloader, Runnable onAuthFail) { - this.ctx = ctx; - this.imgDownloader = imgDownloader; - this.onAuthFail = onAuthFail; - } - - protected void loadPhoto(String photoPath, Consumer onImageReady) { - Bitmap cached = cache.get(photoPath); - if (cached != null) { - onImageReady.accept(cached); - return; - } - - downloadImage(photoPath, photo -> { - cache.put(photoPath, photo); - onImageReady.accept(photo); - Log.d(TAG, getClass().getSimpleName() + - ": image downloaded for [" + photoPath + "], cache updated."); - }); - } - - protected abstract void downloadImage(String photoPath, Consumer onDownloaded); -} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/SetContext.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/SetContext.java new file mode 100644 index 0000000..82b245e --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/SetContext.java @@ -0,0 +1,7 @@ +package com.tom.meeter.infrastructure.components; + +import android.content.Context; + +public interface SetContext { + void setContext(Context ctx); +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/SetOnAuthFailAction.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/SetOnAuthFailAction.java new file mode 100644 index 0000000..f284d38 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/SetOnAuthFailAction.java @@ -0,0 +1,5 @@ +package com.tom.meeter.infrastructure.components; + +public interface SetOnAuthFailAction { + void setOnAuthFailAction(Runnable onAuthFail); +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/UserLoader.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/UserLoader.java new file mode 100644 index 0000000..e8b6a8d --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/UserLoader.java @@ -0,0 +1,15 @@ +package com.tom.meeter.infrastructure.components; + +import static com.tom.meeter.infrastructure.common.Globals.AUTH_HEADER; + +import com.tom.meeter.context.network.dto.UserDTO; + +import retrofit2.Call; +import retrofit2.http.GET; +import retrofit2.http.Header; +import retrofit2.http.Path; + +public interface UserLoader { + @GET("/user/{id}") + Call getUser(@Header(AUTH_HEADER) String authHeader, @Path("id") String userId); +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/BaseAdapter.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/BaseAdapter.java index bac68cb..612e592 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/BaseAdapter.java +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/BaseAdapter.java @@ -4,13 +4,13 @@ import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.RecyclerView; -import com.tom.meeter.context.network.dto.EntityBase; +import com.tom.meeter.context.network.dto.BaseEntity; import com.tom.meeter.infrastructure.components.binder.BaseViewHolderBinder; import java.util.ArrayList; import java.util.List; -public abstract class BaseAdapter +public abstract class BaseAdapter extends RecyclerView.Adapter { protected final BaseViewHolderBinder binder; @@ -38,7 +38,7 @@ public int getItemCount() { return targets.size(); } - public static class EntityBaseDiffCallback + public static class EntityBaseDiffCallback extends DiffUtil.Callback { private final List oldTargets, newTargets; 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 index 9190f10..d16d3d3 100644 --- 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 @@ -1,5 +1,6 @@ package com.tom.meeter.infrastructure.components.adapter; +import android.content.Context; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -7,14 +8,29 @@ import com.tom.meeter.databinding.CardItemBinding; import com.tom.meeter.infrastructure.components.binder.EventBinder; +import com.tom.meeter.infrastructure.components.binder.SimpleEventBinderImpl; import com.tom.meeter.infrastructure.components.viewholder.CardItemHolder; +import javax.inject.Inject; + public class EventsCardAdapter extends BaseEventAdapter { private static final String TAG = EventsCardAdapter.class.getCanonicalName(); - public EventsCardAdapter(EventBinder binder) { + private final EventBinder binder; + + @Inject + public EventsCardAdapter(SimpleEventBinderImpl binder) { super(binder); + this.binder = binder; + } + + public void initialize( + Context ctx, Runnable onAuthFail, + OnEventClickListener listener) { + binder.setContext(ctx); + binder.setOnAuthFailAction(onAuthFail); + binder.setupOnEventClickListener(listener); } @NonNull diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/OnSubscribeUnsubscribeClickListener.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/OnSubscribeUnsubscribeClickListener.java deleted file mode 100644 index 57bcc0b..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/adapter/OnSubscribeUnsubscribeClickListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.tom.meeter.infrastructure.components.adapter; - -import com.tom.meeter.context.profile.subscriber.Subscriber; - -public interface OnSubscribeUnsubscribeClickListener { - void onSubUnSub(Subscriber sub, int position); -} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/EventBinder.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/EventBinder.java index 6dcef1a..bacf586 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/EventBinder.java +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/EventBinder.java @@ -3,8 +3,14 @@ import androidx.recyclerview.widget.RecyclerView; import com.tom.meeter.context.network.dto.EventDTO; +import com.tom.meeter.infrastructure.components.SetContext; +import com.tom.meeter.infrastructure.components.SetOnAuthFailAction; +import com.tom.meeter.infrastructure.components.adapter.OnEventClickListener; public interface EventBinder - extends BaseViewHolderBinder { + extends SetContext, SetOnAuthFailAction, + BaseViewHolderBinder { void bind(T holder, EventDTO event); + + void setupOnEventClickListener(OnEventClickListener listener); } diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/EventBinderImpl.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/EventBinderImpl.java deleted file mode 100644 index 1bac6b0..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/EventBinderImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.tom.meeter.infrastructure.components.binder; - -import android.content.Context; -import android.graphics.Bitmap; - -import com.tom.meeter.context.image.ImageDownloader; -import com.tom.meeter.context.network.dto.EventDTO; -import com.tom.meeter.infrastructure.common.ImagesHelper; -import com.tom.meeter.infrastructure.components.PhotoWithCacheDownloader; -import com.tom.meeter.infrastructure.components.adapter.OnEventClickListener; -import com.tom.meeter.infrastructure.components.viewholder.EventViewHolder; - -import java.util.function.Consumer; - -public class EventBinderImpl - extends PhotoWithCacheDownloader - implements EventBinder { - - private final OnEventClickListener listener; - - public EventBinderImpl( - Context ctx, ImageDownloader imgDownloader, - OnEventClickListener listener, Runnable onAuthFail) { - super(ctx, imgDownloader, onAuthFail); - this.listener = listener; - } - - @Override - public void bind(EventViewHolder holder, EventDTO event) { - String photoPath = event.getPhotoPath(); - - Bitmap cached = photoPath != null ? cache.get(photoPath) : null; - - holder.bind( - event.getName(), event.getDescription(), cached, - v -> listener.onClick(event)); - - if (photoPath != null && cached == null) { - loadPhoto(photoPath, holder::updatePhoto); - } - } - - @Override - protected void downloadImage(String photoPath, Consumer onDownloaded) { - imgDownloader.downloadEventImage( - photoPath, ctx, - ImagesHelper::circleImage, - onDownloaded, - onAuthFail); - } -} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SimpleEventBinderImpl.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SimpleEventBinderImpl.java index 310af3d..2bdc64d 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SimpleEventBinderImpl.java +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SimpleEventBinderImpl.java @@ -8,21 +8,35 @@ import com.tom.meeter.infrastructure.components.adapter.OnEventClickListener; import com.tom.meeter.infrastructure.components.viewholder.CardItemHolder; +import javax.inject.Inject; + public class SimpleEventBinderImpl implements EventBinder { private static final String TAG = SimpleEventBinderImpl.class.getCanonicalName(); - private final Context ctx; private final ImageDownloader imageDownloader; - private final OnEventClickListener listener; - private final Runnable onAuthFail; - public SimpleEventBinderImpl( - Context ctx, ImageDownloader imageDownloader, - OnEventClickListener listener, Runnable onAuthFail) { - this.ctx = ctx; + private Context ctx; + private OnEventClickListener listener; + private Runnable onAuthFail; + + @Inject + public SimpleEventBinderImpl(ImageDownloader imageDownloader) { this.imageDownloader = imageDownloader; + } + + @Override + public void setupOnEventClickListener(OnEventClickListener listener) { this.listener = listener; + } + + @Override + public void setContext(Context ctx) { + this.ctx = ctx; + } + + @Override + public void setOnAuthFailAction(Runnable onAuthFail) { this.onAuthFail = onAuthFail; } diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SubscriberBinder.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SubscriberBinder.java deleted file mode 100644 index eec7566..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/SubscriberBinder.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.tom.meeter.infrastructure.components.binder; - -import androidx.recyclerview.widget.RecyclerView; - -import com.tom.meeter.infrastructure.components.adapter.OnSubscribeUnsubscribeClickListener; -import com.tom.meeter.infrastructure.components.adapter.OnUserClickListener; - -public interface SubscriberBinder - extends BaseSubscriberBinder { - void setup( - OnSubscribeUnsubscribeClickListener subUnSubClickListener, - OnUserClickListener userClickListener); -} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/UserBinder.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/UserBinder.java deleted file mode 100644 index 402ad6e..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/UserBinder.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.tom.meeter.infrastructure.components.binder; - -import androidx.recyclerview.widget.RecyclerView; - -import com.tom.meeter.infrastructure.components.adapter.OnUserClickListener; - -public interface UserBinder - extends BaseUserBinder { - void setup(OnUserClickListener userClickListener); -} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/UserBinderImpl.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/UserBinderImpl.java deleted file mode 100644 index 59dca5c..0000000 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/binder/UserBinderImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.tom.meeter.infrastructure.components.binder; - -import android.content.Context; -import android.graphics.Bitmap; - -import com.tom.meeter.context.image.ImageDownloader; -import com.tom.meeter.context.network.dto.UserDTO; -import com.tom.meeter.infrastructure.components.UserImageDownloader; -import com.tom.meeter.infrastructure.components.adapter.OnUserClickListener; -import com.tom.meeter.infrastructure.components.viewholder.UserViewHolder; - -public class UserBinderImpl extends UserImageDownloader - implements UserBinder { - - private OnUserClickListener userClickListener; - - public UserBinderImpl( - Context ctx, ImageDownloader imgDownloader, Runnable onAuthFail) { - super(ctx, imgDownloader, onAuthFail); - } - - @Override - public void bind(UserViewHolder holder, UserDTO user) { - String photoPath = user.getPhotoPath(); - - Bitmap cached = photoPath != null ? cache.get(photoPath) : null; - - holder.bind( - user.getName(), user.getSurname(), cached, - v -> userClickListener.onClick(user)); - - if (photoPath != null && cached == null) { - loadPhoto(photoPath, holder::updatePhoto); - } - } - - @Override - public void setup(OnUserClickListener userClickListener) { - this.userClickListener = userClickListener; - } -} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/EventImageDownloader.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/EventImageDownloader.java new file mode 100644 index 0000000..325dacc --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/EventImageDownloader.java @@ -0,0 +1,27 @@ +package com.tom.meeter.infrastructure.components.downloader; + +import android.graphics.Bitmap; + +import com.tom.meeter.context.image.ImageDownloader; +import com.tom.meeter.infrastructure.common.ImagesHelper; + +import java.util.function.Consumer; + +import javax.inject.Inject; + +public class EventImageDownloader extends PhotoWithCacheDownloader { + + @Inject + public EventImageDownloader(ImageDownloader imgDownloader) { + super(imgDownloader); + } + + @Override + protected void downloadImage( + String photoPath, Consumer onDownloaded) { + imgDownloader.downloadEventImage( + photoPath, ctx, + ImagesHelper::circleImage, + onDownloaded, onAuthFail); + } +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/PhotoWithCacheDownloader.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/PhotoWithCacheDownloader.java new file mode 100644 index 0000000..f49e851 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/PhotoWithCacheDownloader.java @@ -0,0 +1,62 @@ +package com.tom.meeter.infrastructure.components.downloader; + +import android.content.Context; +import android.graphics.Bitmap; +import android.util.Log; + +import com.tom.meeter.context.image.ImageDownloader; +import com.tom.meeter.infrastructure.components.SetContext; +import com.tom.meeter.infrastructure.components.SetOnAuthFailAction; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +public abstract class PhotoWithCacheDownloader + implements SetContext, SetOnAuthFailAction { + + private static final String TAG = PhotoWithCacheDownloader.class.getCanonicalName(); + + protected ImageDownloader imgDownloader; + + protected Context ctx; + protected Runnable onAuthFail; + + protected final Map cache = new ConcurrentHashMap<>(); + + protected PhotoWithCacheDownloader(ImageDownloader imgDownloader) { + this.imgDownloader = imgDownloader; + } + + @Override + public void setContext(Context ctx) { + this.ctx = ctx; + } + + @Override + public void setOnAuthFailAction(Runnable onAuthFail) { + this.onAuthFail = onAuthFail; + } + + public Bitmap getCachedPhoto(String photoPath) { + return cache.get(photoPath); + } + + public void loadPhoto(String photoPath, Consumer onImageReady) { + Bitmap cached = cache.get(photoPath); + if (cached != null) { + onImageReady.accept(cached); + return; + } + + downloadImage( + photoPath, photo -> { + cache.put(photoPath, photo); + onImageReady.accept(photo); + Log.d(TAG, getClass().getSimpleName() + + ": image downloaded for [" + photoPath + "], cache updated."); + }); + } + + protected abstract void downloadImage(String photoPath, Consumer onDownloaded); +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/UserImageDownloader.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/UserImageDownloader.java similarity index 68% rename from AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/UserImageDownloader.java rename to AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/UserImageDownloader.java index 157e60d..90112e9 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/UserImageDownloader.java +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/UserImageDownloader.java @@ -1,6 +1,5 @@ -package com.tom.meeter.infrastructure.components; +package com.tom.meeter.infrastructure.components.downloader; -import android.content.Context; import android.graphics.Bitmap; import com.tom.meeter.context.image.ImageDownloader; @@ -8,11 +7,13 @@ import java.util.function.Consumer; +import javax.inject.Inject; + public class UserImageDownloader extends PhotoWithCacheDownloader { - protected UserImageDownloader( - Context ctx, ImageDownloader imgDownloader, Runnable onAuthFail) { - super(ctx, imgDownloader, onAuthFail); + @Inject + public UserImageDownloader(ImageDownloader imgDownloader) { + super(imgDownloader); } @Override diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/UserWithCacheDownloader.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/UserWithCacheDownloader.java new file mode 100644 index 0000000..10b7896 --- /dev/null +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/components/downloader/UserWithCacheDownloader.java @@ -0,0 +1,83 @@ +package com.tom.meeter.infrastructure.components.downloader; + +import android.content.Context; +import android.util.Log; + +import com.tom.meeter.context.network.dto.UserDTO; +import com.tom.meeter.infrastructure.components.SetContext; +import com.tom.meeter.infrastructure.components.SetOnAuthFailAction; +import com.tom.meeter.infrastructure.components.UserLoader; +import com.tom.meeter.infrastructure.http.BaseOnNotAuthenticatedCallback; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +import javax.inject.Inject; + +import retrofit2.Call; +import retrofit2.Response; + +public class UserWithCacheDownloader + implements SetContext, SetOnAuthFailAction { + + private static final String TAG = PhotoWithCacheDownloader.class.getCanonicalName(); + + private final UserLoader service; + + private Context ctx; + private Runnable onAuthFail; + + protected final Map cache = new ConcurrentHashMap<>(); + + @Inject + public UserWithCacheDownloader(UserLoader service) { + this.service = service; + } + + @Override + public void setContext(Context ctx) { + this.ctx = ctx; + } + + @Override + public void setOnAuthFailAction(Runnable onAuthFail) { + this.onAuthFail = onAuthFail; + } + + public UserDTO getUserCache(String userId) { + return cache.get(userId); + } + + public void loadUser( + String auth, String userId, Consumer onUserReady) { + UserDTO cached = cache.get(userId); + if (cached != null) { + onUserReady.accept(cached); + return; + } + + fetchUser( + auth, userId, + user -> { + cache.put(user.getId(), user); + onUserReady.accept(user); + Log.d(TAG, getClass().getSimpleName() + + ": user downloaded for [" + user.getId() + "], cache updated."); + }); + } + + private void fetchUser( + String auth, String userId, Consumer onDownloaded) { + service.getUser(auth, userId).enqueue(new BaseOnNotAuthenticatedCallback<>(ctx, onAuthFail) { + @Override + public void onResponse(Call call, Response resp) { + super.onResponse(call, resp); + if (!resp.isSuccessful() || resp.body() == null) { + return; + } + onDownloaded.accept(resp.body()); + } + }); + } +} diff --git a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/eventbus/events/IncomeEvents.java b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/eventbus/events/IncomeEvents.java index 3046db8..a4c5eab 100644 --- a/AndroidClient/src/main/java/com/tom/meeter/infrastructure/eventbus/events/IncomeEvents.java +++ b/AndroidClient/src/main/java/com/tom/meeter/infrastructure/eventbus/events/IncomeEvents.java @@ -17,7 +17,7 @@ public record IncomeEvents(List events) { public static IncomeEvents fromJsonArray(JSONArray msg) { List events = new ArrayList<>(); for (int i = 0; i < msg.length(); i++) { - events.add(EventDTO.encode((JSONObject) msg.opt(i))); + events.add(new EventDTO((JSONObject) msg.opt(i))); } return new IncomeEvents(events); } diff --git a/AndroidClient/src/main/res/drawable-hdpi/meeter_new_logo.png b/AndroidClient/src/main/res/drawable-hdpi/meeter_new_logo.png new file mode 100644 index 0000000..ad73e21 Binary files /dev/null and b/AndroidClient/src/main/res/drawable-hdpi/meeter_new_logo.png differ diff --git a/AndroidClient/src/main/res/drawable-hdpi/meeter_new_logo_512x512.png b/AndroidClient/src/main/res/drawable-hdpi/meeter_new_logo_512x512.png new file mode 100644 index 0000000..db293ea Binary files /dev/null and b/AndroidClient/src/main/res/drawable-hdpi/meeter_new_logo_512x512.png differ diff --git a/AndroidClient/src/main/res/drawable-hdpi/meeter_new_logo_64x64.png b/AndroidClient/src/main/res/drawable-hdpi/meeter_new_logo_64x64.png new file mode 100644 index 0000000..60b5d80 Binary files /dev/null and b/AndroidClient/src/main/res/drawable-hdpi/meeter_new_logo_64x64.png differ diff --git a/AndroidClient/src/main/res/layout/activity_event_editable.xml b/AndroidClient/src/main/res/layout/activity_event_editable.xml index 82f6bbe..0645dff 100644 --- a/AndroidClient/src/main/res/layout/activity_event_editable.xml +++ b/AndroidClient/src/main/res/layout/activity_event_editable.xml @@ -13,6 +13,7 @@ @@ -41,6 +42,28 @@ android:text="@string/select_photo" android:textAllCaps="false" /> + + + + + + @@ -32,6 +33,28 @@ android:text="@string/creator" android:textAllCaps="false" /> + + + + + + @@ -95,35 +117,7 @@ - - - - - - - + android:orientation="vertical"> + + + + + + +