diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt index 3bb3dce1b..d8ad96d3a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ContentRepositoryImpl.kt @@ -24,7 +24,7 @@ abstract class ContentRepositoryImpl(localRepository val now = Date().time return if (forced || now - this.lastContentSync > 3600000) { lastContentSync = now - apiClient.content.doOnNext({ localRepository.saveContent(it) }) + apiClient.getContent().doOnNext({ localRepository.saveContent(it) }) } else { Observable.just(null) } @@ -34,7 +34,7 @@ abstract class ContentRepositoryImpl(localRepository val now = Date().time return if (now - this.lastWorldStateSync > 3600000) { lastWorldStateSync = now - apiClient.worldState.doOnNext({ localRepository.saveWorldState(it) }) + apiClient.getWorldState().doOnNext({ localRepository.saveWorldState(it) }) } else { Observable.just(null) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt index 71ec0a5b6..da7687265 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/InventoryRepositoryImpl.kt @@ -279,7 +279,7 @@ class InventoryRepositoryImpl(localRepository: InventoryLocalRepository, apiClie override fun togglePinnedItem(item: ShopItem): Observable> { return if (!item.isValid) { Observable.just(null) - } else apiClient.togglePinnedItem(item.pinType, item.path) + } else apiClient.togglePinnedItem(item.pinType ?: "", item.path ?: "") .flatMap { retrieveInAppRewards() } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt index 5b0a1fc0a..c91891175 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/SocialRepositoryImpl.kt @@ -43,7 +43,7 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap override fun flagMessage(chatMessage: ChatMessage): Observable { return if (chatMessage.id == "") { Observable.just(null) - } else apiClient.flagMessage(chatMessage.groupId, chatMessage.id) + } else apiClient.flagMessage(chatMessage.groupId ?: "", chatMessage.id) } override fun likeMessage(chatMessage: ChatMessage): Observable { @@ -52,12 +52,12 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap } val liked = chatMessage.userLikesMessage(userId) localRepository.likeMessage(chatMessage, userId, !liked) - return apiClient.likeMessage(chatMessage.groupId, chatMessage.id) + return apiClient.likeMessage(chatMessage.groupId ?: "", chatMessage.id) .doOnError { localRepository.likeMessage(chatMessage, userId, liked) } } override fun deleteMessage(chatMessage: ChatMessage): Observable { - return apiClient.deleteMessage(chatMessage.groupId, chatMessage.id) + return apiClient.deleteMessage(chatMessage.groupId ?: "", chatMessage.id) .doOnNext { localRepository.deleteMessage(chatMessage.id) } } @@ -159,7 +159,7 @@ class SocialRepositoryImpl(localRepository: SocialLocalRepository, apiClient: Ap override fun inviteToGroup(id: String, inviteData: Map): Observable> = apiClient.inviteToGroup(id, inviteData) - override fun getUserChallenges(): Observable> = apiClient.userChallenges + override fun getUserChallenges(): Observable> = apiClient.getUserChallenges() override fun getMember(userId: String?): Observable { return if (userId == null) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt index 0957e5969..e25fbcf73 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt @@ -36,7 +36,7 @@ class TaskRepositoryImpl(localRepository: TaskLocalRepository, apiClient: ApiCli } override fun retrieveTasks(userId: String, tasksOrder: TasksOrder): Observable { - return this.apiClient.tasks + return this.apiClient.getTasks() .doOnNext { res -> this.localRepository.saveTasks(userId, tasksOrder, res) } } @@ -58,11 +58,15 @@ class TaskRepositoryImpl(localRepository: TaskLocalRepository, apiClient: ApiCli override fun taskChecked(user: User?, task: Task, up: Boolean, force: Boolean): Observable { val now = Date().time + val id = task.id if (lastTaskAction > now - 500 && !force) { return Observable.just(null) } + if (id == null) { + return Observable.just(null) + } lastTaskAction = now - return this.apiClient.postTaskDirection(task.id, (if (up) TaskDirection.up else TaskDirection.down).toString()) + return this.apiClient.postTaskDirection(id, (if (up) TaskDirection.up else TaskDirection.down).toString()) .map { res -> // save local task changes val result = TaskScoringResult() @@ -152,8 +156,12 @@ class TaskRepositoryImpl(localRepository: TaskLocalRepository, apiClient: ApiCli return Observable.just(task) } lastTaskAction = now - return localRepository.getTaskCopy(task.id ?: "").first() - .flatMap { task1 -> apiClient.updateTask(task1.id, task1) } + val id = task.id + if (id == null) { + return Observable.just(task) + } + return localRepository.getTaskCopy(id).first() + .flatMap { task1 -> apiClient.updateTask(id, task1) } .map { task1 -> task1.position = task.position task1 @@ -193,7 +201,7 @@ class TaskRepositoryImpl(localRepository: TaskLocalRepository, apiClient: ApiCli .first() .flatMap { task -> if (task.isValid) { - return@flatMap apiClient.postTaskNewPosition(task.id, newPosition) + return@flatMap apiClient.postTaskNewPosition(task.id ?: "", newPosition) } return@flatMap Observable.just>(ArrayList()) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Outfit.java b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Outfit.java deleted file mode 100644 index cf6300ea8..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Outfit.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.habitrpg.android.habitica.models.user; - -import android.text.TextUtils; - -import com.google.gson.annotations.SerializedName; - -import io.realm.RealmObject; -import io.realm.annotations.PrimaryKey; - -public class Outfit extends RealmObject { - - @PrimaryKey - private String userId; - - Gear gear; - String armor, back, body, head, shield, weapon; - @SerializedName("eyewear") - String eyeWear; - String headAccessory; - - public String getArmor() { - return armor; - } - - public void setArmor(String armor) { - this.armor = armor; - } - - public String getBack() { - return back; - } - - public void setBack(String back) { - this.back = back; - } - - public String getBody() { - return body; - } - - public void setBody(String body) { - this.body = body; - } - - public String getEyeWear() { - return eyeWear; - } - - public void setEyeWear(String eyeWear) { - this.eyeWear = eyeWear; - } - - public String getHead() { - return head; - } - - public void setHead(String head) { - this.head = head; - } - - public String getHeadAccessory() { - return headAccessory; - } - - public void setHeadAccessory(String headAccessory) { - this.headAccessory = headAccessory; - } - - public String getShield() { - return shield; - } - - public void setShield(String shield) { - this.shield = shield; - } - - public String getWeapon() { - return weapon; - } - - public void setWeapon(String weapon) { - this.weapon = weapon; - } - - public boolean isAvailable(String outfit) { - return !TextUtils.isEmpty(outfit) && !outfit.endsWith("base_0"); - } - - public void updateWith(Outfit newOutfit) { - this.setArmor(newOutfit.getArmor()); - this.setBack(newOutfit.getBack()); - this.setBody(newOutfit.getBody()); - this.setEyeWear(newOutfit.getEyeWear()); - this.setHead(newOutfit.getHead()); - this.setHeadAccessory(newOutfit.getHeadAccessory()); - this.setShield(newOutfit.getShield()); - } - - public String getUserId() { - return userId; - } - - public void setUserId(String userId) { - this.userId = userId; - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Outfit.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Outfit.kt new file mode 100644 index 000000000..90249bc88 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Outfit.kt @@ -0,0 +1,39 @@ +package com.habitrpg.android.habitica.models.user + +import android.text.TextUtils + +import com.google.gson.annotations.SerializedName + +import io.realm.RealmObject +import io.realm.annotations.PrimaryKey + +open class Outfit : RealmObject() { + + @PrimaryKey + var userId: String? = null + + internal var gear: Gear? = null + var armor: String = "" + var back: String = "" + var body: String = "" + var head: String = "" + var shield: String = "" + var weapon: String = "" + @SerializedName("eyewear") + var eyeWear: String = "" + var headAccessory: String = "" + + fun isAvailable(outfit: String): Boolean { + return !TextUtils.isEmpty(outfit) && !outfit.endsWith("base_0") + } + + fun updateWith(newOutfit: Outfit) { + this.armor = newOutfit.armor + this.back = newOutfit.back + this.body = newOutfit.body + this.eyeWear = newOutfit.eyeWear + this.head = newOutfit.head + this.headAccessory = newOutfit.headAccessory + this.shield = newOutfit.shield + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.java deleted file mode 100644 index 521b9782d..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.java +++ /dev/null @@ -1,599 +0,0 @@ -package com.habitrpg.android.habitica.ui; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.Animatable; -import android.graphics.drawable.Drawable; -import android.support.annotation.NonNull; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; - -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.drawee.controller.BaseControllerListener; -import com.facebook.drawee.generic.GenericDraweeHierarchy; -import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; -import com.facebook.drawee.interfaces.DraweeController; -import com.facebook.drawee.view.DraweeHolder; -import com.facebook.drawee.view.MultiDraweeHolder; -import com.facebook.imagepipeline.image.ImageInfo; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.models.Avatar; -import com.habitrpg.android.habitica.models.AvatarPreferences; -import com.habitrpg.android.habitica.models.user.Buffs; -import com.habitrpg.android.habitica.models.user.Hair; -import com.habitrpg.android.habitica.models.user.Outfit; - -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -public class AvatarView extends View { - public static final String IMAGE_URI_ROOT = "https://habitica-assets.s3.amazonaws.com/mobileApp/images/"; - private static final String TAG = "AvatarView"; - private static final Map FILENAME_MAP; - private static final Rect FULL_HERO_RECT = new Rect(0, 0, 140, 147); - private static final Rect COMPACT_HERO_RECT = new Rect(0, 0, 114, 114); - private static final Rect HERO_ONLY_RECT = new Rect(0, 0, 90, 90); - - static { - Map tempMap = new HashMap<>(); - tempMap.put("head_special_1", "ContributorOnly-Equip-CrystalHelmet.gif"); - tempMap.put("armor_special_1", "ContributorOnly-Equip-CrystalArmor.gif"); - tempMap.put("weapon_special_critical", "weapon_special_critical.gif"); - tempMap.put("Pet-Wolf-Cerberus", "Pet-Wolf-Cerberus.gif"); - FILENAME_MAP = Collections.unmodifiableMap(tempMap); - } - - private boolean showBackground = true; - private boolean showMount = true; - private boolean showPet = true; - private boolean showSleeping = true; - private boolean hasBackground; - private boolean hasMount; - private boolean hasPet; - private boolean isOrphan; - private MultiDraweeHolder multiDraweeHolder = new MultiDraweeHolder<>(); - private Avatar avatar; - private RectF avatarRectF; - private Matrix matrix = new Matrix(); - private AtomicInteger numberLayersInProcess = new AtomicInteger(0); - private Consumer avatarImageConsumer; - private Bitmap avatarBitmap; - private Canvas avatarCanvas; - private Map currentLayers; - - public AvatarView(Context context) { - super(context); - init(null, 0); - } - - public AvatarView(Context context, AttributeSet attrs) { - super(context, attrs); - init(attrs, 0); - } - - public AvatarView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(attrs, defStyle); - } - - public AvatarView(Context context, boolean showBackground, boolean showMount, boolean showPet) { - super(context); - - this.showBackground = showBackground; - this.showMount = showMount; - this.showPet = showPet; - isOrphan = true; - } - - public void configureView(boolean showBackground, boolean showMount, boolean showPet) { - this.showBackground = showBackground; - this.showMount = showMount; - this.showPet = showPet; - } - - private void init(AttributeSet attrs, int defStyle) { - // Load attributes - final TypedArray a = getContext().obtainStyledAttributes( - attrs, R.styleable.AvatarView, defStyle, 0); - - try { - showBackground = a.getBoolean(R.styleable.AvatarView_showBackground, true); - showMount = a.getBoolean(R.styleable.AvatarView_showMount, true); - showPet = a.getBoolean(R.styleable.AvatarView_showPet, true); - showSleeping = a.getBoolean(R.styleable.AvatarView_showSleeping, true); - } finally { - a.recycle(); - } - } - - private void showLayers(@NonNull Map layerMap) { - if (multiDraweeHolder.size() > 0) return; - int i = 0; - - currentLayers = layerMap; - - numberLayersInProcess.set(layerMap.size()); - - for (Map.Entry entry : layerMap.entrySet()) { - final LayerType layerKey = entry.getKey(); - final String layerName = entry.getValue(); - final int layerNumber = i++; - - GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources()) - .setFadeDuration(0) - .build(); - - DraweeHolder draweeHolder = DraweeHolder.create(hierarchy, getContext()); - draweeHolder.getTopLevelDrawable().setCallback(this); - multiDraweeHolder.add(draweeHolder); - - DraweeController controller = Fresco.newDraweeControllerBuilder() - .setUri(IMAGE_URI_ROOT + getFileName(layerName)) - .setControllerListener(new BaseControllerListener() { - @Override - public void onFinalImageSet( - String id, - ImageInfo imageInfo, - Animatable anim) { - if (imageInfo != null) { - if (multiDraweeHolder.size() > layerNumber) { - multiDraweeHolder.get(layerNumber).getTopLevelDrawable().setBounds(getLayerBounds(layerKey, layerName, imageInfo)); - } - onLayerComplete(); - } - } - - @Override - public void onFailure(String id, Throwable throwable) { - Log.e(TAG, "Error loading layer: " + layerName, throwable); - onLayerComplete(); - } - }) - .setAutoPlayAnimations(!isOrphan) - .build(); - draweeHolder.setController(controller); - } - - if (isOrphan) multiDraweeHolder.onAttach(); - } - - private Map getLayerMap() { - assert avatar != null; - return getLayerMap(avatar, true); - } - - private Map getLayerMap(@NonNull Avatar avatar, boolean resetHasAttributes) { - EnumMap layerMap = getAvatarLayerMap(avatar); - - if (resetHasAttributes) hasBackground = hasMount = hasPet = false; - - String mountName = avatar.getCurrentMount(); - if (showMount && !TextUtils.isEmpty(mountName)) { - layerMap.put(LayerType.MOUNT_BODY, "Mount_Body_" + mountName); - layerMap.put(LayerType.MOUNT_HEAD, "Mount_Head_" + mountName); - if (resetHasAttributes) hasMount = true; - } - - String petName = avatar.getCurrentPet(); - if (showPet && !TextUtils.isEmpty(petName)) { - layerMap.put(LayerType.PET, "Pet-" + petName); - if (resetHasAttributes) hasPet = true; - } - - String backgroundName = avatar.getBackground(); - if (showBackground && !TextUtils.isEmpty(backgroundName)) { - layerMap.put(LayerType.BACKGROUND, "background_" + backgroundName); - if (resetHasAttributes) hasBackground = true; - } - - if (showSleeping && avatar.getSleep()) { - layerMap.put(AvatarView.LayerType.ZZZ, "zzz"); - } - - return layerMap; - } - - public EnumMap getAvatarLayerMap(Avatar avatar) { - EnumMap layerMap = new EnumMap<>(AvatarView.LayerType.class); - - if (!avatar.isValid()) { - return layerMap; - } - - AvatarPreferences prefs = avatar.getPreferences(); - if (prefs == null) { - return layerMap; - } - Outfit outfit; - if (prefs.getCostume()) { - outfit = avatar.getCostume(); - } else { - outfit = avatar.getEquipped(); - } - - boolean hasVisualBuffs = false; - - if (avatar.getStats() != null && avatar.getStats().getBuffs() != null) { - Buffs buffs = avatar.getStats().getBuffs(); - - if (buffs.getSnowball()) { - layerMap.put(AvatarView.LayerType.VISUAL_BUFF, "snowman"); - hasVisualBuffs = true; - } - - if (buffs.getSeafoam()) { - layerMap.put(AvatarView.LayerType.VISUAL_BUFF, "seafoam_star"); - hasVisualBuffs = true; - } - - if (buffs.getShinySeed()) { - layerMap.put(AvatarView.LayerType.VISUAL_BUFF, "avatar_floral_" + avatar.getStats().getHabitClass()); - hasVisualBuffs = true; - } - - if (buffs.getSpookySparkles()) { - layerMap.put(AvatarView.LayerType.VISUAL_BUFF, "ghost"); - hasVisualBuffs = true; - } - } - - if (!hasVisualBuffs) { - if (!TextUtils.isEmpty(prefs.getChair())) { - layerMap.put(AvatarView.LayerType.CHAIR, prefs.getChair()); - } - - if (outfit != null) { - if (!TextUtils.isEmpty(outfit.getBack()) && !"back_base_0".equals(outfit.getBack())) { - layerMap.put(AvatarView.LayerType.BACK, outfit.getBack()); - } - if (outfit.isAvailable(outfit.getArmor())) { - layerMap.put(AvatarView.LayerType.ARMOR, prefs.getSize() + "_" + outfit.getArmor()); - } - if (outfit.isAvailable(outfit.getBody())) { - layerMap.put(AvatarView.LayerType.BODY, outfit.getBody()); - } - if (outfit.isAvailable(outfit.getEyeWear())) { - layerMap.put(AvatarView.LayerType.EYEWEAR, outfit.getEyeWear()); - } - if (outfit.isAvailable(outfit.getHead())) { - layerMap.put(AvatarView.LayerType.HEAD, outfit.getHead()); - } - if (outfit.isAvailable(outfit.getHeadAccessory())) { - layerMap.put(AvatarView.LayerType.HEAD_ACCESSORY, outfit.getHeadAccessory()); - } - if (outfit.isAvailable(outfit.getShield())) { - layerMap.put(AvatarView.LayerType.SHIELD, outfit.getShield()); - } - if (outfit.isAvailable(outfit.getWeapon())) { - layerMap.put(AvatarView.LayerType.WEAPON, outfit.getWeapon()); - } - } - - layerMap.put(AvatarView.LayerType.SKIN, "skin_" + prefs.getSkin() + ((prefs.getSleep()) ? "_sleep" : "")); - layerMap.put(AvatarView.LayerType.SHIRT, prefs.getSize() + "_shirt_" + prefs.getShirt()); - layerMap.put(AvatarView.LayerType.HEAD_0, "head_0"); - - Hair hair = prefs.getHair(); - if (hair != null) { - String hairColor = hair.getColor(); - - if (hair.isAvailable(hair.getBase())) { - layerMap.put(AvatarView.LayerType.HAIR_BASE, "hair_base_" + hair.getBase() + "_" + hairColor); - } - if (hair.isAvailable(hair.getBangs())) { - layerMap.put(AvatarView.LayerType.HAIR_BANGS, "hair_bangs_" + hair.getBangs() + "_" + hairColor); - } - if (hair.isAvailable(hair.getMustache())) { - layerMap.put(AvatarView.LayerType.HAIR_MUSTACHE, "hair_mustache_" + hair.getMustache() + "_" + hairColor); - } - if (hair.isAvailable(hair.getBeard())) { - layerMap.put(AvatarView.LayerType.HAIR_BEARD, "hair_beard_" + hair.getBeard() + "_" + hairColor); - } - if (hair.isAvailable(hair.getFlower())) { - layerMap.put(AvatarView.LayerType.HAIR_FLOWER, "hair_flower_" + hair.getFlower()); - } - } - } else { - Hair hair = prefs.getHair(); - - // Show flower all the time! - if (hair != null && hair.isAvailable(hair.getFlower())) { - layerMap.put(AvatarView.LayerType.HAIR_FLOWER, "hair_flower_" + hair.getFlower()); - } - } - - return layerMap; - } - - private Rect getLayerBounds(@NonNull LayerType layerType, @NonNull String layerName, @NonNull ImageInfo layerImageInfo) { - PointF offset = null; - Rect bounds = new Rect(0, 0, layerImageInfo.getWidth(), layerImageInfo.getHeight()); - RectF boundsF = new RectF(bounds); - - // lookup layer specific offset - switch (layerName) { - case "weapon_special_critical": - if (showMount || showPet) { - // full hero box - if (hasMount) { - offset = new PointF(13.0f, 12.0f); - } else if (hasPet) { - offset = new PointF(13.0f, 24.5f + 12.0f); - } else { - offset = new PointF(13.0f, 28.0f + 12.0f); - } - } else if (showBackground) { - // compact hero box - offset = new PointF(-12.0f, 18.0f + 12.0f); - } else { - // hero only box - offset = new PointF(-12.0f, 12.0f); - } - break; - default: - break; - } - - // otherwise lookup default layer type based offset - if (offset == null) { - switch (layerType) { - case BACKGROUND: - if (!(showMount || showPet)) { - offset = new PointF(-25.0f, 0.0f); // compact hero box - } - break; - case MOUNT_BODY: - case MOUNT_HEAD: - offset = new PointF(25.0f, 18.0f); // full hero box - break; - case CHAIR: - case BACK: - case SKIN: - case SHIRT: - case ARMOR: - case BODY: - case HEAD_0: - case HAIR_BASE: - case HAIR_BANGS: - case HAIR_MUSTACHE: - case HAIR_BEARD: - case EYEWEAR: - case VISUAL_BUFF: - case HEAD: - case HEAD_ACCESSORY: - case HAIR_FLOWER: - case SHIELD: - case WEAPON: - case ZZZ: - if (showMount || showPet) { - // full hero box - if (hasMount) { - offset = new PointF(25.0f, 0); - } else if (hasPet) { - offset = new PointF(25.0f, 24.5f); - } else { - offset = new PointF(25.0f, 28.0f); - } - } else if (showBackground) { - // compact hero box - offset = new PointF(0.0f, 18.0f); - } - break; - case PET: - offset = new PointF(0, FULL_HERO_RECT.height() - layerImageInfo.getHeight()); - break; - default: - break; - } - } - - if (offset != null) { - Matrix translateMatrix = new Matrix(); - translateMatrix.setTranslate(offset.x, offset.y); - translateMatrix.mapRect(boundsF); - } - - // resize bounds to fit and keep original aspect ratio - matrix.mapRect(boundsF); - boundsF.round(bounds); - - return bounds; - } - - private String getFileName(@NonNull String imageName) { - if (FILENAME_MAP.containsKey(imageName)) { - return FILENAME_MAP.get(imageName); - } - - return imageName + ".png"; - } - - private void onLayerComplete() { - if (numberLayersInProcess.decrementAndGet() == 0) { - if (avatarImageConsumer != null) { - avatarImageConsumer.accept(getAvatarImage()); - } - } - } - - public void onAvatarImageReady(@NonNull Consumer consumer) { - avatarImageConsumer = consumer; - if (multiDraweeHolder.size() > 0 && numberLayersInProcess.get() == 0) { - avatarImageConsumer.accept(getAvatarImage()); - } else { - initAvatarRectMatrix(); - showLayers(getLayerMap()); - } - } - - public void setAvatar(@NonNull Avatar avatar) { - Avatar oldUser = this.avatar; - this.avatar = avatar; - - if (oldUser != null) { - Map newLayerMap = getLayerMap(avatar, false); - - boolean equals = currentLayers != null && currentLayers.equals(newLayerMap); - - if (!equals) { - multiDraweeHolder.clear(); - numberLayersInProcess.set(0); - } - } - invalidate(); - } - - - - private Rect getOriginalRect() { - return (showMount || showPet) ? FULL_HERO_RECT : ((showBackground) ? COMPACT_HERO_RECT : HERO_ONLY_RECT); - } - - private Bitmap getAvatarImage() { - assert avatar != null; - assert avatarRectF != null; - Rect canvasRect = new Rect(); - avatarRectF.round(canvasRect); - avatarBitmap = Bitmap.createBitmap(canvasRect.width(), canvasRect.height(), Bitmap.Config.ARGB_8888); - avatarCanvas = new Canvas(avatarBitmap); - draw(avatarCanvas); - - return avatarBitmap; - } - - private void initAvatarRectMatrix() { - if (avatarRectF == null) { - Rect srcRect = getOriginalRect(); - - if (isOrphan) { - avatarRectF = new RectF(srcRect); - - // change scale to not be 1:1 - // a quick fix as fresco AnimatedDrawable/ScaleTypeDrawable - // will not translate matrix properly - matrix.setScale(1.2f, 1.2f); - matrix.mapRect(avatarRectF); - } else { - // full hero box when showMount and showPet is enabled (140w * 147h) - // compact hero box when only showBackground is enabled (114w * 114h) - // hero only box when all show settings disabled (90w * 90h) - avatarRectF = new RectF(0, 0, getWidth(), getHeight()); - matrix.setRectToRect(new RectF(srcRect), avatarRectF, Matrix.ScaleToFit.START); // TODO support other ScaleToFit - avatarRectF = new RectF(srcRect); - matrix.mapRect(avatarRectF); - } - } - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - initAvatarRectMatrix(); - - // draw only when user is set - if (avatar == null || !avatar.isValid()) return; - - // request image layers if not yet processed - if (multiDraweeHolder.size() == 0) { - showLayers(getLayerMap()); - } - - // manually call onAttach/onDetach if view is without parent as they will never be called otherwise - if (isOrphan) multiDraweeHolder.onAttach(); - multiDraweeHolder.draw(canvas); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - multiDraweeHolder.onDetach(); - } - - @Override - public void onStartTemporaryDetach() { - super.onStartTemporaryDetach(); - multiDraweeHolder.onDetach(); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - multiDraweeHolder.onAttach(); - } - - @Override - public void onFinishTemporaryDetach() { - super.onFinishTemporaryDetach(); - multiDraweeHolder.onAttach(); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - return multiDraweeHolder.onTouchEvent(event) || super.onTouchEvent(event); - } - - @Override - public boolean performClick() { - return super.performClick(); - } - - @Override - protected boolean verifyDrawable(Drawable who) { - return multiDraweeHolder.verifyDrawable(who) || super.verifyDrawable(who); - } - - @Override - public void invalidateDrawable(@NonNull Drawable drawable) { - invalidate(); - if (avatarCanvas != null) draw(avatarCanvas); - } - - public enum LayerType { - BACKGROUND(0), - MOUNT_BODY(1), - CHAIR(2), - BACK(3), - SKIN(4), - SHIRT(5), - ARMOR(6), - BODY(7), - HEAD_0(8), - HAIR_BASE(9), - HAIR_BANGS(10), - HAIR_MUSTACHE(11), - HAIR_BEARD(12), - EYEWEAR(13), - VISUAL_BUFF(14), - HEAD(15), - HEAD_ACCESSORY(16), - HAIR_FLOWER(17), - SHIELD(18), - WEAPON(19), - MOUNT_HEAD(20), - ZZZ(21), - PET(22); - - final int order; - - LayerType(int order) { - this.order = order; - } - } - - public interface Consumer { - void accept(T t); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt new file mode 100644 index 000000000..b573972d5 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarView.kt @@ -0,0 +1,540 @@ +package com.habitrpg.android.habitica.ui + +import android.content.Context +import android.graphics.* +import android.graphics.drawable.Animatable +import android.graphics.drawable.Drawable +import android.text.TextUtils +import android.util.AttributeSet +import android.util.Log +import android.view.View +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.controller.BaseControllerListener +import com.facebook.drawee.generic.GenericDraweeHierarchy +import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder +import com.facebook.drawee.view.DraweeHolder +import com.facebook.drawee.view.MultiDraweeHolder +import com.facebook.imagepipeline.image.ImageInfo +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.models.Avatar +import java.util.* +import java.util.concurrent.atomic.AtomicInteger + +class AvatarView : View { + + private var showBackground = true + private var showMount = true + private var showPet = true + private var showSleeping = true + private var hasBackground: Boolean = false + private var hasMount: Boolean = false + private var hasPet: Boolean = false + private var isOrphan: Boolean = false + private val multiDraweeHolder = MultiDraweeHolder() + private var avatar: Avatar? = null + private var avatarRectF: RectF? = null + private val avatarMatrix = Matrix() + private val numberLayersInProcess = AtomicInteger(0) + private var avatarImageConsumer: Consumer? = null + private var avatarBitmap: Bitmap? = null + private var avatarCanvas: Canvas? = null + private var currentLayers: Map? = null + + private val layerMap: Map + get() { + assert(avatar != null) + return getLayerMap(avatar!!, true) + } + + + private val originalRect: Rect + get() = if (showMount || showPet) FULL_HERO_RECT else if (showBackground) COMPACT_HERO_RECT else HERO_ONLY_RECT + + private val avatarImage: Bitmap? + get() { + assert(avatar != null) + assert(avatarRectF != null) + val canvasRect = Rect() + avatarRectF?.round(canvasRect) + avatarBitmap = Bitmap.createBitmap(canvasRect.width(), canvasRect.height(), Bitmap.Config.ARGB_8888) + avatarCanvas = Canvas(avatarBitmap) + draw(avatarCanvas) + + return avatarBitmap + } + + constructor(context: Context) : super(context) { + init(null, 0) + } + + constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { + init(attrs, 0) + } + + constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { + init(attrs, defStyle) + } + + constructor(context: Context, showBackground: Boolean, showMount: Boolean, showPet: Boolean) : super(context) { + + this.showBackground = showBackground + this.showMount = showMount + this.showPet = showPet + isOrphan = true + } + + fun configureView(showBackground: Boolean, showMount: Boolean, showPet: Boolean) { + this.showBackground = showBackground + this.showMount = showMount + this.showPet = showPet + } + + private fun init(attrs: AttributeSet?, defStyle: Int) { + // Load attributes + val a = context.obtainStyledAttributes( + attrs, R.styleable.AvatarView, defStyle, 0) + + try { + showBackground = a.getBoolean(R.styleable.AvatarView_showBackground, true) + showMount = a.getBoolean(R.styleable.AvatarView_showMount, true) + showPet = a.getBoolean(R.styleable.AvatarView_showPet, true) + showSleeping = a.getBoolean(R.styleable.AvatarView_showSleeping, true) + } finally { + a.recycle() + } + } + + private fun showLayers(layerMap: Map) { + if (multiDraweeHolder.size() > 0) return + var i = 0 + + currentLayers = layerMap + + numberLayersInProcess.set(layerMap.size) + + for ((layerKey, layerName) in layerMap) { + val layerNumber = i++ + + val hierarchy = GenericDraweeHierarchyBuilder(resources) + .setFadeDuration(0) + .build() + + val draweeHolder = DraweeHolder.create(hierarchy, context) + draweeHolder.topLevelDrawable.callback = this + multiDraweeHolder.add(draweeHolder) + + val controller = Fresco.newDraweeControllerBuilder() + .setUri(IMAGE_URI_ROOT + getFileName(layerName)) + .setControllerListener(object : BaseControllerListener() { + override fun onFinalImageSet( + id: String?, + imageInfo: ImageInfo?, + anim: Animatable?) { + if (imageInfo != null) { + if (multiDraweeHolder.size() > layerNumber) { + multiDraweeHolder.get(layerNumber).topLevelDrawable.bounds = getLayerBounds(layerKey, layerName, imageInfo) + } + onLayerComplete() + } + } + + override fun onFailure(id: String?, throwable: Throwable?) { + Log.e(TAG, "Error loading layer: " + layerName, throwable) + onLayerComplete() + } + }) + .setAutoPlayAnimations(!isOrphan) + .build() + draweeHolder.controller = controller + } + + if (isOrphan) multiDraweeHolder.onAttach() + } + + private fun getLayerMap(avatar: Avatar, resetHasAttributes: Boolean): Map { + val layerMap = getAvatarLayerMap(avatar) + + if (resetHasAttributes) { + hasPet = false + hasMount = hasPet + hasBackground = hasMount + } + + val mountName = avatar.currentMount + if (showMount && !TextUtils.isEmpty(mountName)) { + layerMap[LayerType.MOUNT_BODY] = "Mount_Body_" + mountName + layerMap[LayerType.MOUNT_HEAD] = "Mount_Head_" + mountName + if (resetHasAttributes) hasMount = true + } + + val petName = avatar.currentPet + if (showPet && !TextUtils.isEmpty(petName)) { + layerMap[LayerType.PET] = "Pet-" + petName + if (resetHasAttributes) hasPet = true + } + + val backgroundName = avatar.background + if (showBackground && !TextUtils.isEmpty(backgroundName)) { + layerMap[LayerType.BACKGROUND] = "background_" + backgroundName + if (resetHasAttributes) hasBackground = true + } + + if (showSleeping && avatar.sleep) { + layerMap[AvatarView.LayerType.ZZZ] = "zzz" + } + + return layerMap + } + + private fun getAvatarLayerMap(avatar: Avatar): EnumMap { + val layerMap = EnumMap(AvatarView.LayerType::class.java) + + if (!avatar.isValid) { + return layerMap + } + + val prefs = avatar.preferences ?: return layerMap + val outfit = if (prefs.costume) { + avatar.costume + } else { + avatar.equipped + } + + var hasVisualBuffs = false + + if (avatar.stats != null && avatar.stats.getBuffs() != null) { + val buffs = avatar.stats.getBuffs() + + if (buffs.snowball) { + layerMap[AvatarView.LayerType.VISUAL_BUFF] = "snowman" + hasVisualBuffs = true + } + + if (buffs.seafoam) { + layerMap[AvatarView.LayerType.VISUAL_BUFF] = "seafoam_star" + hasVisualBuffs = true + } + + if (buffs.shinySeed) { + layerMap[AvatarView.LayerType.VISUAL_BUFF] = "avatar_floral_" + avatar.stats.getHabitClass() + hasVisualBuffs = true + } + + if (buffs.spookySparkles) { + layerMap[AvatarView.LayerType.VISUAL_BUFF] = "ghost" + hasVisualBuffs = true + } + } + + if (!hasVisualBuffs) { + if (!TextUtils.isEmpty(prefs.chair)) { + layerMap[AvatarView.LayerType.CHAIR] = prefs.chair + } + + if (outfit != null) { + if (!TextUtils.isEmpty(outfit.back) && "back_base_0" != outfit.back) { + layerMap[AvatarView.LayerType.BACK] = outfit.back + } + if (outfit.isAvailable(outfit.armor)) { + layerMap[AvatarView.LayerType.ARMOR] = prefs.size + "_" + outfit.armor + } + if (outfit.isAvailable(outfit.body)) { + layerMap[AvatarView.LayerType.BODY] = outfit.body + } + if (outfit.isAvailable(outfit.eyeWear)) { + layerMap[AvatarView.LayerType.EYEWEAR] = outfit.eyeWear + } + if (outfit.isAvailable(outfit.head)) { + layerMap[AvatarView.LayerType.HEAD] = outfit.head + } + if (outfit.isAvailable(outfit.headAccessory)) { + layerMap[AvatarView.LayerType.HEAD_ACCESSORY] = outfit.headAccessory + } + if (outfit.isAvailable(outfit.shield)) { + layerMap[AvatarView.LayerType.SHIELD] = outfit.shield + } + if (outfit.isAvailable(outfit.weapon)) { + layerMap[AvatarView.LayerType.WEAPON] = outfit.weapon + } + } + + layerMap[AvatarView.LayerType.SKIN] = "skin_" + prefs.skin + if (prefs.sleep) "_sleep" else "" + layerMap[AvatarView.LayerType.SHIRT] = prefs.size + "_shirt_" + prefs.shirt + layerMap[AvatarView.LayerType.HEAD_0] = "head_0" + + val hair = prefs.hair + if (hair != null) { + val hairColor = hair.color + + if (hair.isAvailable(hair.base)) { + layerMap[AvatarView.LayerType.HAIR_BASE] = "hair_base_" + hair.base + "_" + hairColor + } + if (hair.isAvailable(hair.bangs)) { + layerMap[AvatarView.LayerType.HAIR_BANGS] = "hair_bangs_" + hair.bangs + "_" + hairColor + } + if (hair.isAvailable(hair.mustache)) { + layerMap[AvatarView.LayerType.HAIR_MUSTACHE] = "hair_mustache_" + hair.mustache + "_" + hairColor + } + if (hair.isAvailable(hair.beard)) { + layerMap[AvatarView.LayerType.HAIR_BEARD] = "hair_beard_" + hair.beard + "_" + hairColor + } + if (hair.isAvailable(hair.flower)) { + layerMap[AvatarView.LayerType.HAIR_FLOWER] = "hair_flower_" + hair.flower + } + } + } else { + val hair = prefs.hair + + // Show flower all the time! + if (hair != null && hair.isAvailable(hair.flower)) { + layerMap[AvatarView.LayerType.HAIR_FLOWER] = "hair_flower_" + hair.flower + } + } + + return layerMap + } + + private fun getLayerBounds(layerType: LayerType, layerName: String, layerImageInfo: ImageInfo): Rect { + var offset: PointF? = null + val bounds = Rect(0, 0, layerImageInfo.width, layerImageInfo.height) + val boundsF = RectF(bounds) + + // lookup layer specific offset + when (layerName) { + "weapon_special_critical" -> offset = if (showMount || showPet) { + // full hero box + when { + hasMount -> PointF(13.0f, 12.0f) + hasPet -> PointF(13.0f, 24.5f + 12.0f) + else -> PointF(13.0f, 28.0f + 12.0f) + } + } else if (showBackground) { + // compact hero box + PointF(-12.0f, 18.0f + 12.0f) + } else { + // hero only box + PointF(-12.0f, 12.0f) + } + } + + // otherwise lookup default layer type based offset + if (offset == null) { + when (layerType) { + AvatarView.LayerType.BACKGROUND -> if (!(showMount || showPet)) { + offset = PointF(-25.0f, 0.0f) // compact hero box + } + AvatarView.LayerType.MOUNT_BODY, AvatarView.LayerType.MOUNT_HEAD -> offset = PointF(25.0f, 18.0f) // full hero box + AvatarView.LayerType.CHAIR, AvatarView.LayerType.BACK, AvatarView.LayerType.SKIN, AvatarView.LayerType.SHIRT, AvatarView.LayerType.ARMOR, AvatarView.LayerType.BODY, AvatarView.LayerType.HEAD_0, AvatarView.LayerType.HAIR_BASE, AvatarView.LayerType.HAIR_BANGS, AvatarView.LayerType.HAIR_MUSTACHE, AvatarView.LayerType.HAIR_BEARD, AvatarView.LayerType.EYEWEAR, AvatarView.LayerType.VISUAL_BUFF, AvatarView.LayerType.HEAD, AvatarView.LayerType.HEAD_ACCESSORY, AvatarView.LayerType.HAIR_FLOWER, AvatarView.LayerType.SHIELD, AvatarView.LayerType.WEAPON, AvatarView.LayerType.ZZZ -> if (showMount || showPet) { + // full hero box + offset = when { + hasMount -> PointF(25.0f, 0f) + hasPet -> PointF(25.0f, 24.5f) + else -> PointF(25.0f, 28.0f) + } + } else if (showBackground) { + // compact hero box + offset = PointF(0.0f, 18.0f) + } + AvatarView.LayerType.PET -> offset = PointF(0f, (FULL_HERO_RECT.height() - layerImageInfo.height).toFloat()) + } + } + + + if (offset != null) { + when (layerName) { + "head_special_0" -> offset = PointF(offset.x, offset.y+3) + "head_special_1" -> offset = PointF(offset.x, offset.y+3) + } + + val translateMatrix = Matrix() + translateMatrix.setTranslate(offset.x, offset.y) + translateMatrix.mapRect(boundsF) + } + + // resize bounds to fit and keep original aspect ratio + avatarMatrix.mapRect(boundsF) + boundsF.round(bounds) + + return bounds + } + + private fun getFileName(imageName: String): String { + val name = if (FILENAME_MAP.containsKey(imageName)) { + FILENAME_MAP[imageName] + } else { + imageName + } + return name + if (FILEFORMAT_MAP.containsKey(imageName)) { + "." + FILEFORMAT_MAP[imageName] + } else { + ".png" + } + } + + private fun onLayerComplete() { + if (numberLayersInProcess.decrementAndGet() == 0) { + avatarImageConsumer?.accept(avatarImage) + } + } + + fun onAvatarImageReady(consumer: Consumer) { + avatarImageConsumer = consumer + if (multiDraweeHolder.size() > 0 && numberLayersInProcess.get() == 0) { + avatarImageConsumer?.accept(avatarImage) + } else { + initAvatarRectMatrix() + showLayers(layerMap) + } + } + + fun setAvatar(avatar: Avatar) { + val oldUser = this.avatar + this.avatar = avatar + + if (oldUser != null) { + val newLayerMap = getLayerMap(avatar, false) + + val equals = currentLayers != null && currentLayers == newLayerMap + + if (!equals) { + multiDraweeHolder.clear() + numberLayersInProcess.set(0) + } + } + invalidate() + } + + private fun initAvatarRectMatrix() { + if (avatarRectF == null) { + val srcRect = originalRect + + if (isOrphan) { + avatarRectF = RectF(srcRect) + + // change scale to not be 1:1 + // a quick fix as fresco AnimatedDrawable/ScaleTypeDrawable + // will not translate matrix properly + avatarMatrix.setScale(1.2f, 1.2f) + avatarMatrix.mapRect(avatarRectF) + } else { + // full hero box when showMount and showPet is enabled (140w * 147h) + // compact hero box when only showBackground is enabled (114w * 114h) + // hero only box when all show settings disabled (90w * 90h) + avatarRectF = RectF(0f, 0f, width.toFloat(), height.toFloat()) + avatarMatrix.setRectToRect(RectF(srcRect), avatarRectF, Matrix.ScaleToFit.START) // TODO support other ScaleToFit + avatarRectF = RectF(srcRect) + avatarMatrix.mapRect(avatarRectF) + } + } + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + initAvatarRectMatrix() + + // draw only when user is set + if (avatar == null || !avatar!!.isValid) return + + // request image layers if not yet processed + if (multiDraweeHolder.size() == 0) { + showLayers(layerMap) + } + + // manually call onAttach/onDetach if view is without parent as they will never be called otherwise + if (isOrphan) multiDraweeHolder.onAttach() + multiDraweeHolder.draw(canvas) + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + multiDraweeHolder.onDetach() + } + + override fun onStartTemporaryDetach() { + super.onStartTemporaryDetach() + multiDraweeHolder.onDetach() + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + multiDraweeHolder.onAttach() + } + + override fun onFinishTemporaryDetach() { + super.onFinishTemporaryDetach() + multiDraweeHolder.onAttach() + } + + override fun verifyDrawable(who: Drawable): Boolean { + return multiDraweeHolder.verifyDrawable(who) || super.verifyDrawable(who) + } + + override fun invalidateDrawable(drawable: Drawable) { + invalidate() + if (avatarCanvas != null) draw(avatarCanvas) + } + + enum class LayerType(internal val order: Int) { + BACKGROUND(0), + MOUNT_BODY(1), + CHAIR(2), + BACK(3), + SKIN(4), + SHIRT(5), + ARMOR(6), + BODY(7), + HEAD_0(8), + HAIR_BASE(9), + HAIR_BANGS(10), + HAIR_MUSTACHE(11), + HAIR_BEARD(12), + EYEWEAR(13), + VISUAL_BUFF(14), + HEAD(15), + HEAD_ACCESSORY(16), + HAIR_FLOWER(17), + SHIELD(18), + WEAPON(19), + MOUNT_HEAD(20), + ZZZ(21), + PET(22) + } + + interface Consumer { + fun accept(t: T) + } + + companion object { + const val IMAGE_URI_ROOT = "https://habitica-assets.s3.amazonaws.com/mobileApp/images/" + private const val TAG = "AvatarView" + val FILEFORMAT_MAP: Map + val FILENAME_MAP: Map + private val FULL_HERO_RECT = Rect(0, 0, 140, 147) + private val COMPACT_HERO_RECT = Rect(0, 0, 114, 114) + private val HERO_ONLY_RECT = Rect(0, 0, 90, 90) + + init { + val tempMap = HashMap() + tempMap["head_special_1"] = "gif" + tempMap["broad_armor_special_1"] = "gif" + tempMap["slim_armor_special_1"] = "gif" + tempMap["head_special_0"] = "gif" + tempMap["slim_armor_special_0"] = "gif" + tempMap["broad_armor_special_0"] = "gif" + tempMap["weapon_special_critical"] = "gif" + tempMap["weapon_special_0"] = "gif" + tempMap["shield_special_0"] = "gif" + tempMap["Pet-Wolf-Cerberus"] = "gif" + FILEFORMAT_MAP = Collections.unmodifiableMap(tempMap) + + + val tempNameMap = HashMap() + tempNameMap["head_special_1"] = "ContributorOnly-Equip-CrystalHelmet" + tempNameMap["armor_special_1"] = "ContributorOnly-Equip-CrystalArmor" + tempNameMap["weapon_special_critical"] = "weapon_special_critical" + tempNameMap["Pet-Wolf-Cerberus"] = "Pet-Wolf-Cerberus" + FILENAME_MAP = Collections.unmodifiableMap(tempNameMap) + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java index b29397430..531e14e1d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java @@ -116,6 +116,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import javax.inject.Inject; @@ -426,8 +427,12 @@ public class MainActivity extends BaseActivity implements TutorialView.OnTutoria Preferences preferences = user.getPreferences(); if (preferences != null) { - apiClient.setLanguageCode(preferences.getLanguage()); - soundManager.setSoundTheme(preferences.getSound()); + if (preferences.getLanguage() != null) { + apiClient.setLanguageCode(preferences.getLanguage()); + } + if (preferences.getSound() != null) { + soundManager.setSoundTheme(preferences.getSound()); + } } runOnUiThread(() -> { updateHeader(); @@ -594,7 +599,7 @@ public class MainActivity extends BaseActivity implements TutorialView.OnTutoria return; } - if (rewardKey.equals("potion")) { + if (Objects.equals(rewardKey, "potion")) { int currentHp = user.getStats().getHp().intValue(); int maxHp = user.getStats().getMaxHealth(); diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/EquipmentRecyclerViewAdapter.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/EquipmentRecyclerViewAdapter.java deleted file mode 100644 index db528c0e5..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/EquipmentRecyclerViewAdapter.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.habitrpg.android.habitica.ui.adapter.inventory; - -import android.content.Context; -import android.net.Uri; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.facebook.drawee.view.SimpleDraweeView; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.models.inventory.Equipment; - -import butterknife.BindView; -import butterknife.ButterKnife; -import io.realm.OrderedRealmCollection; -import io.realm.RealmRecyclerViewAdapter; -import rx.Observable; -import rx.subjects.PublishSubject; - -public class EquipmentRecyclerViewAdapter extends RealmRecyclerViewAdapter { - - public String equippedGear; - public Boolean isCostume; - public String type; - - private PublishSubject equipEvents = PublishSubject.create(); - - public EquipmentRecyclerViewAdapter(@Nullable OrderedRealmCollection data, boolean autoUpdate) { - super(data, autoUpdate); - } - - @Override - public GearViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.gear_list_item, parent, false); - - return new GearViewHolder(view); - } - - @Override - public void onBindViewHolder(GearViewHolder holder, int position) { - if (getData() != null) { - holder.bind(getData().get(position)); - } - } - - public Observable getEquipEvents() { - return equipEvents.asObservable(); - } - - class GearViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - - @BindView(R.id.gear_container) - View gearContainer; - - @BindView(R.id.gear_text) - TextView gearNameTextView; - - @BindView(R.id.gear_notes) - TextView gearNotesTextView; - - @BindView(R.id.gear_image) - SimpleDraweeView imageView; - - @BindView(R.id.equippedIndicator) - View equippedIndicator; - - Equipment gear; - - Context context; - - public GearViewHolder(View itemView) { - super(itemView); - - ButterKnife.bind(this, itemView); - - context = itemView.getContext(); - itemView.setOnClickListener(this); - } - - public void bind(Equipment gear) { - this.gear = gear; - this.gearNameTextView.setText(this.gear.getText()); - this.gearNotesTextView.setText(this.gear.getNotes()); - - if (gear.getKey().equals(equippedGear)) { - this.equippedIndicator.setVisibility(View.VISIBLE); - this.gearContainer.setBackgroundColor(ContextCompat.getColor(context, R.color.brand_700)); - } else { - this.equippedIndicator.setVisibility(View.GONE); - this.gearContainer.setBackgroundResource(R.drawable.selection_highlight); - } - - String imageUrl = "https://habitica-assets.s3.amazonaws.com/mobileApp/images/shop_" + gear.getKey() + ".png"; - imageView.setImageURI(Uri.parse(imageUrl)); - } - - @Override - public void onClick(View v) { - equipEvents.onNext(this.gear.getKey()); - if (this.gear.getKey().equals(equippedGear)) { - equippedGear = type + "_base_0"; - } else { - equippedGear = this.gear.getKey(); - } - notifyDataSetChanged(); - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/EquipmentRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/EquipmentRecyclerViewAdapter.kt new file mode 100644 index 000000000..f92c89db5 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/EquipmentRecyclerViewAdapter.kt @@ -0,0 +1,86 @@ +package com.habitrpg.android.habitica.ui.adapter.inventory + +import android.content.Context +import android.net.Uri +import android.support.v4.content.ContextCompat +import android.support.v7.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView + +import com.facebook.drawee.view.SimpleDraweeView +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.models.inventory.Equipment + +import butterknife.BindView +import butterknife.ButterKnife +import com.habitrpg.android.habitica.extensions.bindView +import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils +import io.realm.OrderedRealmCollection +import io.realm.RealmRecyclerViewAdapter +import rx.Observable +import rx.subjects.PublishSubject + +class EquipmentRecyclerViewAdapter(data: OrderedRealmCollection?, autoUpdate: Boolean) : RealmRecyclerViewAdapter(data, autoUpdate) { + + var equippedGear: String? = null + var isCostume: Boolean? = null + var type: String? = null + + val equipEvents: PublishSubject = PublishSubject.create() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GearViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.gear_list_item, parent, false) + return GearViewHolder(view) + } + + override fun onBindViewHolder(holder: GearViewHolder, position: Int) { + if (data != null) { + holder.bind(data!![position]) + } + } + + inner class GearViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + private val gearContainer: View by bindView(itemView, R.id.gear_container) + private val gearNameTextView: TextView by bindView(itemView, R.id.gear_text) + private val gearNotesTextView: TextView by bindView(itemView, R.id.gear_notes) + private val imageView: SimpleDraweeView by bindView(itemView, R.id.gear_image) + private val equippedIndicator: View by bindView(itemView, R.id.equippedIndicator) + + var gear: Equipment? = null + var context: Context = itemView.context + + init { + context = itemView.context + itemView.setOnClickListener { + val key = gear?.key + if (key != null) { + equipEvents.onNext(key) + equippedGear = if (key == equippedGear) { + type + "_base_0" + } else { + key + } + notifyDataSetChanged() + } + } + } + + fun bind(gear: Equipment) { + this.gear = gear + this.gearNameTextView.text = this.gear?.text + this.gearNotesTextView.text = this.gear?.notes + + if (gear.key == equippedGear) { + this.equippedIndicator.visibility = View.VISIBLE + this.gearContainer.setBackgroundColor(ContextCompat.getColor(context, R.color.brand_700)) + } else { + this.equippedIndicator.visibility = View.GONE + this.gearContainer.setBackgroundResource(R.drawable.selection_highlight) + } + DataBindingUtils.loadImage(imageView, "shop_"+gear.key) + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt index ee6d8c92e..d15ece46f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/ShopRecyclerAdapter.kt @@ -245,7 +245,6 @@ class ShopRecyclerAdapter : RecyclerView.Adapter() { private val namePlate: TextView by bindView(itemView, R.id.namePlate) init { - ButterKnife.bind(this, itemView) context = itemView.context descriptionView.movementMethod = LinkMovementMethod.getInstance() } @@ -278,7 +277,6 @@ class ShopRecyclerAdapter : RecyclerView.Adapter() { private val subscribeButton: Button? by bindView(itemView, R.id.subscribeButton) private val textView: TextView? by bindView(itemView, R.id.textView) init { - ButterKnife.bind(this, view) subscribeButton?.setOnClickListener { EventBus.getDefault().post(OpenGemPurchaseFragmentCommand()) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt index d596b6363..a2d4a6aa9 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarOverviewFragment.kt @@ -19,7 +19,7 @@ class AvatarOverviewFragment : BaseMainFragment(), AdapterView.OnItemSelectedLis super.onCreate(savedInstanceState) if (apiClient != null) { - apiClient.content + apiClient.getContent() .subscribe(Action1 { }, RxErrorHandler.handleEmptyError()) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.java deleted file mode 100644 index f5710a5f3..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.habitrpg.android.habitica.ui.fragments.inventory.equipment; - -import android.os.Bundle; -import android.support.v7.widget.DividerItemDecoration; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.InventoryRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.ui.adapter.inventory.EquipmentRecyclerViewAdapter; -import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment; -import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; - -public class EquipmentDetailFragment extends BaseMainFragment { - - @Inject - InventoryRepository inventoryRepository; - - public String type; - public String equippedGear; - public Boolean isCostume; - - @BindView(R.id.recyclerView) - RecyclerView recyclerView; - - EquipmentRecyclerViewAdapter adapter; - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - View v = inflater.inflate(R.layout.fragment_recyclerview, container, false); - - setUnbinder(ButterKnife.bind(this, v)); - - - this.adapter = new EquipmentRecyclerViewAdapter(null, true); - this.adapter.equippedGear = this.equippedGear; - this.adapter.isCostume = this.isCostume; - this.adapter.type = this.type; - this.adapter.getEquipEvents() - .flatMap(key -> inventoryRepository.equipGear(user, key, isCostume)) - .subscribe(items -> {}, RxErrorHandler.handleEmptyError()); - - - this.recyclerView.setAdapter(this.adapter); - this.recyclerView.setLayoutManager(new LinearLayoutManager(activity)); - this.recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL)); - recyclerView.setItemAnimator(new SafeDefaultItemAnimator()); - - inventoryRepository.getOwnedEquipment(type).first().subscribe(this.adapter::updateData, RxErrorHandler.handleEmptyError()); - return v; - } - - @Override - public void onDestroy() { - inventoryRepository.close(); - super.onDestroy(); - } - - @Override - public void injectFragment(AppComponent component) { - component.inject(this); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt new file mode 100644 index 000000000..9565fb3fc --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentDetailFragment.kt @@ -0,0 +1,66 @@ +package com.habitrpg.android.habitica.ui.fragments.inventory.equipment + +import android.os.Bundle +import android.support.v7.widget.DividerItemDecoration +import android.support.v7.widget.LinearLayoutManager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.inventory.Equipment +import com.habitrpg.android.habitica.models.user.Items +import com.habitrpg.android.habitica.ui.adapter.inventory.EquipmentRecyclerViewAdapter +import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment +import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator +import io.realm.RealmResults +import kotlinx.android.synthetic.main.fragment_recyclerview.* +import rx.functions.Action1 +import javax.inject.Inject + +class EquipmentDetailFragment : BaseMainFragment() { + + @Inject + lateinit var inventoryRepository: InventoryRepository + + var type: String? = null + var equippedGear: String? = null + var isCostume: Boolean? = null + + private var adapter: EquipmentRecyclerViewAdapter = EquipmentRecyclerViewAdapter(null, true) + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + super.onCreateView(inflater, container, savedInstanceState) + val v = inflater.inflate(R.layout.fragment_recyclerview, container, false) + + this.adapter.equippedGear = this.equippedGear + this.adapter.isCostume = this.isCostume + this.adapter.type = this.type + this.adapter.equipEvents.flatMap { key -> inventoryRepository.equipGear(user, key, isCostume!!) } + .subscribe(Action1 { }, RxErrorHandler.handleEmptyError()) + return v + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + recyclerView.adapter = this.adapter + recyclerView.layoutManager = LinearLayoutManager(activity) + recyclerView.addItemDecoration(DividerItemDecoration(getActivity()!!, DividerItemDecoration.VERTICAL)) + recyclerView.itemAnimator = SafeDefaultItemAnimator() + + inventoryRepository.getOwnedEquipment(type).first().subscribe(Action1> { this.adapter.updateData(it) }, RxErrorHandler.handleEmptyError()) + } + + override fun onDestroy() { + inventoryRepository.close() + super.onDestroy() + } + + override fun injectFragment(component: AppComponent) { + component.inject(this) + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentOverviewFragment.kt index 76810088e..ce2840279 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentOverviewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/equipment/EquipmentOverviewFragment.kt @@ -8,6 +8,7 @@ import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.AppComponent import com.habitrpg.android.habitica.data.InventoryRepository import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.ui.AvatarView import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment import kotlinx.android.synthetic.main.fragment_equipment_overview.* import rx.functions.Action1 @@ -56,7 +57,7 @@ class EquipmentOverviewFragment : BaseMainFragment() { if (this.nameMapping.isEmpty()) { compositeSubscription.add(inventoryRepository.ownedEquipment.subscribe(Action1 { for (gear in it) { - this.nameMapping.put(gear.key ?: "", gear.text) + this.nameMapping[gear.key ?: ""] = gear.text } setEquipmentNames() @@ -120,9 +121,7 @@ class EquipmentOverviewFragment : BaseMainFragment() { fragment.type = type fragment.isCostume = isCostume fragment.equippedGear = equipped - if (activity != null) { - activity!!.displayFragment(fragment) - } + activity?.displayFragment(fragment) } override fun customTitle(): String { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.kt index 99122bca9..592ba4082 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/DataBindingUtils.kt @@ -24,15 +24,23 @@ import com.habitrpg.android.habitica.R object DataBindingUtils { - fun loadImage(view: SimpleDraweeView?, imageName: String?) { + fun loadImage(view: SimpleDraweeView?, imageName: String) { + loadImage(view, imageName, "png") + } + + fun loadImage(view: SimpleDraweeView?, imageName: String?, imageFormat: String = "png") { if (view != null && imageName != null && view.visibility == View.VISIBLE) { - view.setImageURI("https://habitica-assets.s3.amazonaws.com/mobileApp/images/$imageName.png") + view.setImageURI("https://habitica-assets.s3.amazonaws.com/mobileApp/images/$imageName.$imageFormat") } } fun loadImage(imageName: String, imageResult: (Bitmap) -> Unit) { + loadImage(imageName, "png", imageResult) + } + + fun loadImage(imageName: String, imageFormat: String = "png", imageResult: (Bitmap) -> Unit) { val imageRequest = ImageRequestBuilder - .newBuilderWithSource(Uri.parse("https://habitica-assets.s3.amazonaws.com/mobileApp/images/$imageName.png")) + .newBuilderWithSource(Uri.parse("https://habitica-assets.s3.amazonaws.com/mobileApp/images/$imageName.$imageFormat")) .build() val imagePipeline = Fresco.getImagePipeline() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/Debounce.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/Debounce.java deleted file mode 100644 index 93b6f4bf3..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/Debounce.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.habitrpg.android.habitica.ui.helpers; - -import java.util.Timer; -import java.util.TimerTask; - -// from https://code.google.com/p/tarsius/source/browse/branches/tarsius_0.00_dev2/src/org/tarsius/util/Debounce.java - -public abstract class Debounce { - private Timer timer = null; - private long lastHit = 0; - private long debounceDelay = 0; - private long checkDelay = 0; - - public Debounce(long debounceDelay, long checkDelay) { - this.debounceDelay = debounceDelay; - this.checkDelay = checkDelay; - } - - public abstract void execute(); - - public void hit() { - lastHit = System.currentTimeMillis(); - if (this.timer != null) { - this.timer.cancel(); - this.timer = null; - } - this.timer = new Timer("Debounce", true); - this.timer.schedule(new DebounceTask(this), 0, checkDelay); - } - - private void checkExecute() { - if ((System.currentTimeMillis() - lastHit) > debounceDelay) { - this.timer.cancel(); - this.timer = null; - execute(); - } - } - - private class DebounceTask extends TimerTask { - - private Debounce debounceInstance = null; - - public DebounceTask(Debounce debounceInstance) { - this.debounceInstance = debounceInstance; - } - - @Override - public void run() { - debounceInstance.checkExecute(); - } - } - -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/EmojiKeyboard.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/EmojiKeyboard.java deleted file mode 100644 index a6ffd1454..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/EmojiKeyboard.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.habitrpg.android.habitica.ui.helpers; - -import android.content.Context; -import android.support.v4.content.ContextCompat; -import android.view.KeyEvent; -import android.view.View; -import android.view.inputmethod.InputMethodManager; -import android.widget.ImageButton; - -import com.habitrpg.android.habitica.R; - -import net.pherth.android.emoji_library.EmojiEditText; -import net.pherth.android.emoji_library.EmojiPopup; - -/** - * @author data5tream - */ -public class EmojiKeyboard { - - /** - * Create a Emoji keyboard - * - * @param itemView Must contain views with the following IDs: - * 'emoji.toggle.btn' for the ImageButton that is used to enable/disable the - * emoji keyboard - * 'edit.new.message.text' for the EmojiEditText where the emojis are put into - * @param context The context from the calling Activity - */ - public static void createKeyboard(View itemView, final Context context) { - - final ImageButton emojiButton = (ImageButton) itemView.findViewById(R.id.emoji_toggle_btn); - final EmojiEditText emojiEditText = (EmojiEditText) itemView.findViewById(R.id.edit_new_message_text); - final EmojiPopup popup = new EmojiPopup(itemView.getRootView(), context, ContextCompat.getColor(context, R.color.brand)); - - popup.setSizeForSoftKeyboard(); - - popup.setOnDismissListener(() -> changeEmojiKeyboardIcon(emojiButton, context, false)); - - popup.setOnSoftKeyboardOpenCloseListener(new EmojiPopup.OnSoftKeyboardOpenCloseListener() { - - @Override - public void onKeyboardOpen(int keyBoardHeight) { - - } - - @Override - public void onKeyboardClose() { - if (popup.isShowing()) - popup.dismiss(); - } - }); - - popup.setOnEmojiconClickedListener(emojicon -> { - if (emojiEditText == null || emojicon == null) { - return; - } - - int start = emojiEditText.getSelectionStart(); - int end = emojiEditText.getSelectionEnd(); - if (start < 0) { - emojiEditText.append(emojicon.getEmoji()); - } else { - emojiEditText.getText().replace(Math.min(start, end), - Math.max(start, end), emojicon.getEmoji(), 0, - emojicon.getEmoji().length()); - } - }); - - popup.setOnEmojiconBackspaceClickedListener(v -> { - KeyEvent event = new KeyEvent( - 0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL); - emojiEditText.dispatchKeyEvent(event); - }); - - emojiButton.setOnClickListener(v -> { - - if (!popup.isShowing()) { - - if (popup.isKeyBoardOpen()) { - popup.showAtBottom(); - changeEmojiKeyboardIcon(emojiButton, context, true); - } else { - emojiEditText.setFocusableInTouchMode(true); - emojiEditText.requestFocus(); - popup.showAtBottomPending(); - final InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); - inputMethodManager.showSoftInput(emojiEditText, InputMethodManager.SHOW_IMPLICIT); - changeEmojiKeyboardIcon(emojiButton, context, true); - } - } else { - popup.dismiss(); - changeEmojiKeyboardIcon(emojiButton, context, false); - } - }); - } - - private static void changeEmojiKeyboardIcon(ImageButton view, Context context, Boolean keyboardOpened) { - - if (keyboardOpened) { - view.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_keyboard_grey600_24dp)); - } else { - view.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_emoticon_grey600_24dp)); - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EquipmentItemRow.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EquipmentItemRow.kt index a94fcf70d..67b541840 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EquipmentItemRow.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/EquipmentItemRow.kt @@ -5,6 +5,7 @@ import android.util.AttributeSet import android.view.View import android.widget.LinearLayout import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.ui.AvatarView import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils import kotlinx.android.synthetic.main.item_image_row.view.* @@ -13,14 +14,14 @@ class EquipmentItemRow(context: Context?, attrs: AttributeSet?) : LinearLayout(c var equipmentIdentifier: String? = null set(value) { field = value - val imageName = if (equipmentIdentifier != null && equipmentIdentifier != "") "shop_"+equipmentIdentifier else "head_0" + val imageName = if (equipmentIdentifier?.isNotEmpty() == true && equipmentIdentifier?.endsWith("base_0") == false) "shop_"+equipmentIdentifier else "head_0" DataBindingUtils.loadImage(imageView, imageName) } var customizationIdentifier: String? = null set(value) { field = value - val imageName = if (customizationIdentifier != null && customizationIdentifier != "") customizationIdentifier else "head_0" + val imageName = if (customizationIdentifier?.isNotEmpty() == true) customizationIdentifier else "head_0" DataBindingUtils.loadImage(imageView, imageName) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/settings/FixValuesEditText.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/settings/FixValuesEditText.kt index dbbe6a275..b6647c041 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/settings/FixValuesEditText.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/settings/FixValuesEditText.kt @@ -10,6 +10,7 @@ import android.view.View import android.widget.FrameLayout import butterknife.ButterKnife import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.extensions.backgroundCompat import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper import kotlinx.android.synthetic.main.fixvalues_edittext.view.* @@ -28,12 +29,11 @@ class FixValuesEditText(context: Context, attrs: AttributeSet) : FrameLayout(con val backgroundDrawable = ContextCompat.getDrawable(context, R.drawable.layout_rounded_bg) backgroundDrawable?.setColorFilter(field, PorterDuff.Mode.MULTIPLY) backgroundDrawable?.alpha = 50 - iconBackgroundView.background = backgroundDrawable + iconBackgroundView.backgroundCompat = backgroundDrawable } init { View.inflate(context, R.layout.fixvalues_edittext, this) - ButterKnife.bind(this) val attributes = context.theme.obtainStyledAttributes( attrs,