This commit is contained in:
MeuNome
2026-05-18 11:36:22 +01:00
parent 82b4b72d48
commit 01b54cd8d0
26 changed files with 1133 additions and 43 deletions

View File

@@ -184,6 +184,18 @@
<option name="screenX" value="1080" />
<option name="screenY" value="2408" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="a13x" />
<option name="id" value="a13x" />
<option name="labId" value="google" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy A13 5G" />
<option name="screenDensity" value="300" />
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="samsung" />
@@ -340,6 +352,18 @@
<option name="screenX" value="1080" />
<option name="screenY" value="2340" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="33" />
<option name="brand" value="samsung" />
<option name="codename" value="a32" />
<option name="id" value="a32" />
<option name="labId" value="google" />
<option name="manufacturer" value="Samsung" />
<option name="name" value="Galaxy A32" />
<option name="screenDensity" value="420" />
<option name="screenX" value="1080" />
<option name="screenY" value="2400" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="36" />
<option name="brand" value="samsung" />

View File

@@ -32,8 +32,7 @@
<activity android:name=".FindFriendsActivity" />
<activity android:name=".TrophiesActivity" />
<activity android:name=".MyFriendsActivity" />
<activity android:name=".AvatarEditorActivity" />
</application>

View File

@@ -0,0 +1,26 @@
package com.fluxup.app;
public class AvatarData {
public String skinColor = "#FFD1B3";
public String bodyFormat = "slim"; // slim, athletic, plus
public int heightOffset = 0; // -10, 0, 10
public String hairStyle = "short"; // short, spiky, long, bald, fade, curly
public String hairColor = "#4A4A4A";
public String eyesStyle = "normal"; // normal, happy, cool, cute
public String eyebrowsStyle = "normal"; // normal, thick, angry, sad
public String mouthStyle = "smile"; // smile, smirk, open, neutral
public String clothesStyle = "tshirt"; // tshirt, hoodie, outfit, sport
public String clothesColor = "#7C3AED";
public String beardStyle = "none"; // none, goatee, full
public String accessory = "none"; // none, glasses, headphones, chain, cap
// Novas propriedades de cosméticos desbloqueáveis
public String effect = "none"; // none, fire_aura, glow
public String frame = "none"; // none, gold, neon
public AvatarData() {}
}

View File

@@ -0,0 +1,253 @@
package com.fluxup.app;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.firestore.FirebaseFirestore;
public class AvatarEditorActivity extends AppCompatActivity {
private AvatarView previewAvatar;
private AvatarData currentData;
private Usuario currentUser;
// Novas listas expandidas com suporte a progressão
private String[] skinColors = {"#FFD1B3", "#FFDBAC", "#F1C27D", "#E0AC69", "#C68642", "#8D5524", "#3E2723"};
private String[] bodyFormats = {"slim", "athletic", "plus"};
private String[] hairStyles = {"short", "spiky", "long", "bald", "fade", "curly"};
private String[] hairColors = {"#4A4A4A", "#1A1A1A", "#C68642", "#E8B253", "#EF4444", "#3B82F6", "#10B981"};
private String[] eyeStyles = {"normal", "happy", "cool", "cute"};
private String[] eyebrowStyles = {"normal", "thick", "angry", "sad"};
private String[] mouthStyles = {"smile", "smirk", "open", "neutral"};
private String[] clothesStyles = {"tshirt", "hoodie", "outfit", "sport"};
private String[] clothesColors = {"#7C3AED", "#EF4444", "#3B82F6", "#10B981", "#F59E0B", "#1E293B", "#F8FAFC"};
private String[] beardStyles = {"none", "goatee", "full"};
private String[] accessories = {"none", "glasses", "headphones", "chain", "cap"};
private String[] frames = {"none", "gold", "neon"};
private String[] effects = {"none", "fire_aura", "glow"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_avatar_editor);
previewAvatar = findViewById(R.id.previewAvatar);
findViewById(R.id.btnBackEditor).setOnClickListener(v -> finish());
findViewById(R.id.btnSaveTop).setOnClickListener(v -> saveAvatar());
setupTabs();
loadCurrentAvatar();
}
private void loadCurrentAvatar() {
FirebaseUser fUser = AuthManager.getInstance().getCurrentUser();
if (fUser != null) {
FirestoreManager.getInstance().getUser(fUser.getUid(), user -> {
if (user != null) {
this.currentUser = user;
this.currentData = user.avatar != null ? user.avatar : new AvatarData();
previewAvatar.setAvatarData(this.currentData);
populateAllOptions();
}
});
}
}
private void setupTabs() {
com.google.android.material.tabs.TabLayout tabs = findViewById(R.id.tabsCategories);
tabs.addOnTabSelectedListener(new com.google.android.material.tabs.TabLayout.OnTabSelectedListener() {
@Override public void onTabSelected(com.google.android.material.tabs.TabLayout.Tab tab) {
findViewById(R.id.panelCorpo).setVisibility(android.view.View.GONE);
findViewById(R.id.panelCabelo).setVisibility(android.view.View.GONE);
findViewById(R.id.panelRosto).setVisibility(android.view.View.GONE);
findViewById(R.id.panelRoupa).setVisibility(android.view.View.GONE);
findViewById(R.id.panelAcessorios).setVisibility(android.view.View.GONE);
findViewById(R.id.panelMolduras).setVisibility(android.view.View.GONE);
findViewById(R.id.panelEfeitos).setVisibility(android.view.View.GONE);
int pos = tab.getPosition();
if (pos == 0) findViewById(R.id.panelCorpo).setVisibility(android.view.View.VISIBLE);
else if (pos == 1) findViewById(R.id.panelCabelo).setVisibility(android.view.View.VISIBLE);
else if (pos == 2) findViewById(R.id.panelRosto).setVisibility(android.view.View.VISIBLE);
else if (pos == 3) findViewById(R.id.panelRoupa).setVisibility(android.view.View.VISIBLE);
else if (pos == 4) findViewById(R.id.panelAcessorios).setVisibility(android.view.View.VISIBLE);
else if (pos == 5) findViewById(R.id.panelMolduras).setVisibility(android.view.View.VISIBLE);
else if (pos == 6) findViewById(R.id.panelEfeitos).setVisibility(android.view.View.VISIBLE);
}
@Override public void onTabUnselected(com.google.android.material.tabs.TabLayout.Tab tab) {}
@Override public void onTabReselected(com.google.android.material.tabs.TabLayout.Tab tab) {}
});
}
private void populateAllOptions() {
populateColorList(R.id.llSkinColors, skinColors, "skinColor");
populateTextList(R.id.llBodyFormats, bodyFormats, "bodyFormat");
populateTextList(R.id.llHairStyles, hairStyles, "hairStyle");
populateColorList(R.id.llHairColors, hairColors, "hairColor");
populateTextList(R.id.llEyes, eyeStyles, "eyesStyle");
populateTextList(R.id.llEyebrows, eyebrowStyles, "eyebrowsStyle");
populateTextList(R.id.llMouth, mouthStyles, "mouthStyle");
populateTextList(R.id.llBeard, beardStyles, "beardStyle");
populateTextList(R.id.llClothes, clothesStyles, "clothesStyle");
populateColorList(R.id.llClothesColors, clothesColors, "clothesColor");
populateTextList(R.id.llAccessories, accessories, "accessory");
populateTextList(R.id.llFrames, frames, "frame");
populateTextList(R.id.llEffects, effects, "effect");
}
private void populateColorList(int containerId, String[] colors, String property) {
android.widget.LinearLayout container = findViewById(containerId);
container.removeAllViews();
for (String color : colors) {
androidx.cardview.widget.CardView card = new androidx.cardview.widget.CardView(this);
android.widget.LinearLayout.LayoutParams params = new android.widget.LinearLayout.LayoutParams(120, 120);
params.setMargins(0, 0, 24, 0);
card.setLayoutParams(params);
card.setRadius(60f);
card.setCardElevation(4f);
card.setCardBackgroundColor(android.graphics.Color.parseColor(color));
// Checkmark no selecionado
String currentValue = getCurrentPropertyValue(property);
if (color.equalsIgnoreCase(currentValue)) {
android.widget.TextView check = new android.widget.TextView(this);
check.setText("");
check.setTextColor(android.graphics.Color.WHITE);
check.setGravity(android.view.Gravity.CENTER);
check.setTextSize(16f);
check.setTypeface(null, android.graphics.Typeface.BOLD);
card.addView(check);
}
card.setOnClickListener(v -> {
updateProperty(property, color);
populateAllOptions();
});
container.addView(card);
}
}
private void populateTextList(int containerId, String[] options, String property) {
android.widget.LinearLayout container = findViewById(containerId);
container.removeAllViews();
for (String opt : options) {
android.widget.TextView tv = new android.widget.TextView(this);
android.widget.LinearLayout.LayoutParams params = new android.widget.LinearLayout.LayoutParams(
android.widget.LinearLayout.LayoutParams.WRAP_CONTENT, 120);
params.setMargins(0, 0, 24, 0);
tv.setLayoutParams(params);
boolean isLocked = UnlockManager.isItemLockedForUser(currentUser, property, opt);
CosmeticItem lockInfo = UnlockManager.findCosmetic(property, opt);
if (isLocked && lockInfo != null) {
tv.setText("🔒 " + opt.toUpperCase());
tv.setGravity(android.view.Gravity.CENTER);
tv.setPadding(40, 0, 40, 0);
tv.setTextColor(android.graphics.Color.parseColor("#94A3B8"));
tv.setTextSize(12f);
tv.setTypeface(null, android.graphics.Typeface.BOLD);
android.graphics.drawable.GradientDrawable gd = new android.graphics.drawable.GradientDrawable();
gd.setColor(android.graphics.Color.parseColor("#E2E8F0"));
gd.setCornerRadius(60f);
gd.setStroke(2, android.graphics.Color.parseColor("#CBD5E1"));
tv.setBackground(gd);
tv.setOnClickListener(v -> {
Toast.makeText(this, lockInfo.unlockRequirement, Toast.LENGTH_SHORT).show();
});
} else {
tv.setText(opt.toUpperCase());
tv.setGravity(android.view.Gravity.CENTER);
tv.setPadding(40, 0, 40, 0);
tv.setTextColor(android.graphics.Color.parseColor("#333333"));
tv.setTextSize(12f);
tv.setTypeface(null, android.graphics.Typeface.BOLD);
android.graphics.drawable.GradientDrawable gd = new android.graphics.drawable.GradientDrawable();
String currentValue = getCurrentPropertyValue(property);
if (opt.equals(currentValue)) {
gd.setColor(android.graphics.Color.parseColor("#7C3AED"));
tv.setTextColor(android.graphics.Color.WHITE);
} else {
gd.setColor(android.graphics.Color.parseColor("#E2E8F0"));
}
gd.setCornerRadius(60f);
tv.setBackground(gd);
tv.setOnClickListener(v -> {
updateProperty(property, opt);
populateAllOptions();
});
}
container.addView(tv);
}
}
private String getCurrentPropertyValue(String property) {
if (currentData == null) return "";
switch (property) {
case "skinColor": return currentData.skinColor;
case "bodyFormat": return currentData.bodyFormat;
case "hairStyle": return currentData.hairStyle;
case "hairColor": return currentData.hairColor;
case "eyesStyle": return currentData.eyesStyle;
case "eyebrowsStyle": return currentData.eyebrowsStyle;
case "mouthStyle": return currentData.mouthStyle;
case "clothesStyle": return currentData.clothesStyle;
case "clothesColor": return currentData.clothesColor;
case "beardStyle": return currentData.beardStyle;
case "accessory": return currentData.accessory;
case "frame": return currentData.frame;
case "effect": return currentData.effect;
}
return "";
}
private void updateProperty(String property, String value) {
if (currentData == null) return;
switch (property) {
case "skinColor": currentData.skinColor = value; break;
case "bodyFormat": currentData.bodyFormat = value; break;
case "hairStyle": currentData.hairStyle = value; break;
case "hairColor": currentData.hairColor = value; break;
case "eyesStyle": currentData.eyesStyle = value; break;
case "eyebrowsStyle": currentData.eyebrowsStyle = value; break;
case "mouthStyle": currentData.mouthStyle = value; break;
case "clothesStyle": currentData.clothesStyle = value; break;
case "clothesColor": currentData.clothesColor = value; break;
case "beardStyle": currentData.beardStyle = value; break;
case "accessory": currentData.accessory = value; break;
case "frame": currentData.frame = value; break;
case "effect": currentData.effect = value; break;
}
previewAvatar.setAvatarData(currentData);
}
private void saveAvatar() {
if (currentUser == null) return;
currentUser.avatar = currentData;
FirebaseFirestore.getInstance().collection("users").document(currentUser.id_usuario)
.update("avatar", currentData)
.addOnSuccessListener(aVoid -> {
Toast.makeText(this, "Avatar atualizado com sucesso!", Toast.LENGTH_SHORT).show();
finish();
})
.addOnFailureListener(e -> {
Toast.makeText(this, "Erro ao guardar", Toast.LENGTH_SHORT).show();
});
}
}

View File

@@ -0,0 +1,370 @@
package com.fluxup.app;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
public class AvatarView extends View {
private AvatarData avatarData = new AvatarData();
private Paint paint;
private Path path;
public AvatarView(Context context) {
super(context);
init();
}
public AvatarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
path = new Path();
}
private String league = "Bronze";
public void setAvatarData(AvatarData data) {
if (data != null) {
this.avatarData = data;
invalidate(); // Redraw
}
}
public void setLeague(String league) {
if (league != null) {
this.league = league;
invalidate();
}
}
private int parseSafeColor(String colorStr, String defaultHex) {
try {
if (colorStr == null || colorStr.isEmpty()) return Color.parseColor(defaultHex);
return Color.parseColor(colorStr);
} catch (Exception e) {
return Color.parseColor(defaultHex);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int w = getWidth();
int h = getHeight();
if (w == 0 || h == 0) return;
// Fundo circular premium
paint.setColor(Color.parseColor("#F8FAFC"));
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f, paint);
canvas.save();
float scale = Math.min(w / 100f, h / 100f);
canvas.translate((w - 100f * scale) / 2f, (h - 100f * scale) / 2f);
canvas.scale(scale, scale);
// Translacao da altura
canvas.translate(0, avatarData.heightOffset);
// --- EFEITOS (Desenhados atrás do avatar) ---
if ("fire_aura".equals(avatarData.effect)) {
paint.setColor(Color.parseColor("#F97316")); // Laranja fogo
canvas.drawCircle(25, 75, 18, paint);
canvas.drawCircle(75, 75, 18, paint);
paint.setColor(Color.parseColor("#F59E0B")); // Amarelo fogo
canvas.drawCircle(35, 80, 12, paint);
canvas.drawCircle(65, 80, 12, paint);
canvas.drawCircle(50, 85, 14, paint);
} else if ("glow".equals(avatarData.effect)) {
paint.setColor(Color.parseColor("#E9D5FF")); // Roxo suave brilhante
canvas.drawCircle(50, 50, 42, paint);
paint.setColor(Color.parseColor("#F5F3FF")); // Fundo interior leve
canvas.drawCircle(50, 50, 36, paint);
}
// Cores
int skinCol = parseSafeColor(avatarData.skinColor, "#FFD1B3");
int clothesCol = parseSafeColor(avatarData.clothesColor, "#7C3AED");
int hairCol = parseSafeColor(avatarData.hairColor, "#4A4A4A");
// --- CORPO (Muito pequeno e simplificado) ---
float bodyW = 40f;
if ("athletic".equals(avatarData.bodyFormat)) bodyW = 48f;
else if ("plus".equals(avatarData.bodyFormat)) bodyW = 56f;
float bodyLeft = 50f - (bodyW / 2f);
float bodyRight = 50f + (bodyW / 2f);
// PESCOÇO
paint.setColor(darkenColor(skinCol, 0.8f)); // Sombra do pescoço
canvas.drawRoundRect(new RectF(43, 65, 57, 85), 6, 6, paint);
// ROUPA
paint.setColor(clothesCol);
paint.setShadowLayer(4f, 0f, 4f, Color.parseColor("#33000000")); // Sombra 3D
if ("hoodie".equals(avatarData.clothesStyle)) {
// Capuz atrás
canvas.drawRoundRect(new RectF(bodyLeft - 5, 72, bodyRight + 5, 110), 16, 16, paint);
// Corpo
canvas.drawRoundRect(new RectF(bodyLeft, 78, bodyRight, 110), 12, 12, paint);
// Cordões
paint.clearShadowLayer();
paint.setColor(Color.WHITE);
canvas.drawRoundRect(new RectF(45, 82, 47, 95), 1, 1, paint);
canvas.drawRoundRect(new RectF(53, 82, 55, 95), 1, 1, paint);
} else if ("sport".equals(avatarData.clothesStyle)) {
canvas.drawRoundRect(new RectF(bodyLeft, 78, bodyRight, 110), 8, 8, paint);
paint.clearShadowLayer();
paint.setColor(Color.WHITE);
canvas.drawRect(bodyLeft + 10, 78, bodyRight - 10, 110, paint);
} else if ("outfit".equals(avatarData.clothesStyle)) {
// Casaco aberto
canvas.drawRoundRect(new RectF(bodyLeft - 2, 78, bodyRight + 2, 110), 10, 10, paint);
paint.clearShadowLayer();
paint.setColor(Color.WHITE); // T-shirt interior
canvas.drawRect(42, 78, 58, 110, paint);
} else { // tshirt
canvas.drawRoundRect(new RectF(bodyLeft, 78, bodyRight, 110), 10, 10, paint);
paint.clearShadowLayer();
paint.setColor(skinCol);
canvas.drawArc(new RectF(40, 72, 60, 84), 0, 180, false, paint);
}
paint.clearShadowLayer();
// --- CABEÇA (Grande e expressiva) ---
paint.setColor(skinCol);
paint.setShadowLayer(6f, 0f, 6f, Color.parseColor("#22000000"));
// Orelhas
canvas.drawCircle(22, 45, 6, paint);
canvas.drawCircle(78, 45, 6, paint);
// Formato principal da cabeça
canvas.drawRoundRect(new RectF(20, 15, 80, 70), 28, 28, paint);
paint.clearShadowLayer();
// --- CABELO Atrás / Base ---
paint.setColor(hairCol);
if ("long".equals(avatarData.hairStyle)) {
canvas.drawRoundRect(new RectF(16, 30, 32, 85), 8, 8, paint);
canvas.drawRoundRect(new RectF(68, 30, 84, 85), 8, 8, paint);
} else if ("curly".equals(avatarData.hairStyle)) {
canvas.drawCircle(18, 35, 12, paint);
canvas.drawCircle(82, 35, 12, paint);
}
// --- BARBA ---
if (!"none".equals(avatarData.beardStyle)) {
paint.setColor(darkenColor(hairCol, 0.9f));
if ("goatee".equals(avatarData.beardStyle)) {
canvas.drawRoundRect(new RectF(44, 58, 56, 68), 6, 6, paint);
} else if ("full".equals(avatarData.beardStyle)) {
canvas.drawRoundRect(new RectF(25, 45, 75, 70), 15, 15, paint);
paint.setColor(skinCol);
canvas.drawRoundRect(new RectF(35, 45, 65, 58), 10, 10, paint); // Área da boca limpa
}
}
// --- OLHOS ---
paint.setColor(Color.parseColor("#1E293B"));
if ("happy".equals(avatarData.eyesStyle)) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3f);
paint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawArc(new RectF(32, 40, 42, 48), 180, 180, false, paint);
canvas.drawArc(new RectF(58, 40, 68, 48), 180, 180, false, paint);
paint.setStyle(Paint.Style.FILL);
} else if ("cool".equals(avatarData.eyesStyle)) {
canvas.drawRoundRect(new RectF(32, 42, 42, 46), 2, 2, paint);
canvas.drawRoundRect(new RectF(58, 42, 68, 46), 2, 2, paint);
} else if ("cute".equals(avatarData.eyesStyle)) {
canvas.drawCircle(37, 44, 5, paint);
canvas.drawCircle(63, 44, 5, paint);
paint.setColor(Color.WHITE);
canvas.drawCircle(38, 42, 1.5f, paint);
canvas.drawCircle(64, 42, 1.5f, paint);
} else { // normal
canvas.drawCircle(37, 44, 4.5f, paint);
canvas.drawCircle(63, 44, 4.5f, paint);
}
// --- SOBRANCELHAS ---
paint.setColor(darkenColor(hairCol, 0.8f));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth("thick".equals(avatarData.eyebrowsStyle) ? 4f : 2.5f);
paint.setStrokeCap(Paint.Cap.ROUND);
if ("angry".equals(avatarData.eyebrowsStyle)) {
canvas.drawLine(30, 32, 44, 38, paint);
canvas.drawLine(70, 32, 56, 38, paint);
} else if ("sad".equals(avatarData.eyebrowsStyle)) {
canvas.drawLine(30, 38, 44, 32, paint);
canvas.drawLine(70, 38, 56, 32, paint);
} else { // normal / thick
canvas.drawLine(32, 35, 42, 35, paint);
canvas.drawLine(58, 35, 68, 35, paint);
}
paint.setStyle(Paint.Style.FILL);
// --- BOCA ---
paint.setColor(Color.parseColor("#BE123C")); // Vermelho suave
if ("smile".equals(avatarData.mouthStyle)) {
canvas.drawArc(new RectF(42, 53, 58, 62), 0, 180, false, paint);
} else if ("open".equals(avatarData.mouthStyle)) {
canvas.drawRoundRect(new RectF(44, 55, 56, 62), 4, 4, paint);
paint.setColor(Color.WHITE); // Dentes
canvas.drawRect(45, 55, 55, 57, paint);
} else if ("smirk".equals(avatarData.mouthStyle)) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2f);
canvas.drawArc(new RectF(45, 54, 55, 60), 0, 130, false, paint);
canvas.drawLine(54, 55, 57, 53, paint); // sorrisinho de lado
paint.setStyle(Paint.Style.FILL);
} else { // neutral
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2.5f);
canvas.drawLine(46, 57, 54, 57, paint);
paint.setStyle(Paint.Style.FILL);
}
// --- CABELO Topo / Franja ---
paint.setColor(hairCol);
if ("short".equals(avatarData.hairStyle) || "long".equals(avatarData.hairStyle)) {
canvas.drawArc(new RectF(18, 8, 82, 40), 180, 180, false, paint);
canvas.drawRoundRect(new RectF(18, 20, 30, 45), 4, 4, paint); // patilhas
canvas.drawRoundRect(new RectF(70, 20, 82, 45), 4, 4, paint);
} else if ("spiky".equals(avatarData.hairStyle)) {
path.reset();
path.moveTo(18, 30);
path.lineTo(30, 5);
path.lineTo(40, 15);
path.lineTo(50, 0);
path.lineTo(60, 15);
path.lineTo(70, 5);
path.lineTo(82, 30);
path.close();
canvas.drawPath(path, paint);
} else if ("fade".equals(avatarData.hairStyle)) {
canvas.drawRoundRect(new RectF(25, 5, 75, 25), 8, 8, paint);
paint.setColor(darkenColor(skinCol, 0.9f));
canvas.drawRoundRect(new RectF(18, 20, 25, 40), 2, 2, paint);
canvas.drawRoundRect(new RectF(75, 20, 82, 40), 2, 2, paint);
} else if ("curly".equals(avatarData.hairStyle)) {
canvas.drawCircle(30, 15, 14, paint);
canvas.drawCircle(50, 10, 16, paint);
canvas.drawCircle(70, 15, 14, paint);
canvas.drawCircle(22, 25, 12, paint);
canvas.drawCircle(78, 25, 12, paint);
}
// --- ACESSÓRIOS ---
if ("glasses".equals(avatarData.accessory)) {
paint.setColor(Color.parseColor("#1E293B"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3f);
canvas.drawRoundRect(new RectF(28, 38, 46, 50), 6, 6, paint);
canvas.drawRoundRect(new RectF(54, 38, 72, 50), 6, 6, paint);
canvas.drawLine(46, 44, 54, 44, paint); // ponte
canvas.drawLine(20, 42, 28, 44, paint); // hastes
canvas.drawLine(80, 42, 72, 44, paint);
paint.setStyle(Paint.Style.FILL);
} else if ("headphones".equals(avatarData.accessory)) {
paint.setColor(Color.parseColor("#334155"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(4f);
canvas.drawArc(new RectF(18, 12, 82, 60), 180, 180, false, paint); // aro
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#E2E8F0"));
canvas.drawRoundRect(new RectF(12, 35, 22, 55), 4, 4, paint); // concha esq
canvas.drawRoundRect(new RectF(78, 35, 88, 55), 4, 4, paint); // concha dir
} else if ("chain".equals(avatarData.accessory)) {
paint.setColor(Color.parseColor("#FBBF24")); // Dourado
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2.5f);
canvas.drawArc(new RectF(40, 72, 60, 88), 0, 180, false, paint);
paint.setStyle(Paint.Style.FILL);
} else if ("cap".equals(avatarData.accessory)) {
paint.setColor(Color.parseColor("#0F172A")); // Boné escuro
canvas.drawArc(new RectF(18, 5, 82, 35), 180, 180, false, paint); // copa
canvas.drawRoundRect(new RectF(15, 25, 90, 32), 4, 4, paint); // aba
}
canvas.restore();
// --- MOLDURA (Desenhada no limite exterior do círculo) ---
String activeFrame = avatarData.frame;
if ("none".equals(activeFrame) || activeFrame == null) {
// Desenhar molduras automáticas por Liga
if ("Prata".equalsIgnoreCase(league) || "Silver".equalsIgnoreCase(league)) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Math.min(w, h) * 0.06f);
paint.setColor(Color.parseColor("#CBD5E1")); // Prata
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint);
paint.setStyle(Paint.Style.FILL);
} else if ("Ouro".equalsIgnoreCase(league) || "Gold".equalsIgnoreCase(league)) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Math.min(w, h) * 0.06f);
paint.setColor(Color.parseColor("#F59E0B")); // Ouro
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint);
paint.setStyle(Paint.Style.FILL);
} else if ("Platina".equalsIgnoreCase(league) || "Platinum".equalsIgnoreCase(league)) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Math.min(w, h) * 0.06f);
paint.setColor(Color.parseColor("#06B6D4")); // Platina Ciano
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint);
paint.setStyle(Paint.Style.FILL);
} else if ("Diamante".equalsIgnoreCase(league) || "Diamond".equalsIgnoreCase(league)) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Math.min(w, h) * 0.06f);
paint.setColor(Color.parseColor("#6366F1")); // Diamante Indigo/Violeta
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint);
paint.setStyle(Paint.Style.FILL);
}
} else {
// Moldura manual equipada explícita
if ("gold".equals(activeFrame)) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Math.min(w, h) * 0.08f); // 8% da espessura
paint.setColor(Color.parseColor("#FBBF24")); // Dourado
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint);
// Linha interior metálica
paint.setStrokeWidth(Math.min(w, h) * 0.02f);
paint.setColor(Color.parseColor("#D97706")); // Dourado Escuro
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - Math.min(w, h)*0.07f, paint);
paint.setStyle(Paint.Style.FILL);
} else if ("neon".equals(activeFrame)) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Math.min(w, h) * 0.08f);
paint.setColor(Color.parseColor("#C084FC")); // Roxo Neon
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - paint.getStrokeWidth()/2f, paint);
// Linha interior ciano
paint.setStrokeWidth(Math.min(w, h) * 0.02f);
paint.setColor(Color.parseColor("#22D3EE")); // Ciano Neon
canvas.drawCircle(w/2f, h/2f, Math.min(w, h)/2f - Math.min(w, h)*0.07f, paint);
paint.setStyle(Paint.Style.FILL);
}
}
}
private int darkenColor(int color, float factor) {
int a = Color.alpha(color);
int r = Math.round(Color.red(color) * factor);
int g = Math.round(Color.green(color) * factor);
int b = Math.round(Color.blue(color) * factor);
return Color.argb(a, Math.min(r, 255), Math.min(g, 255), Math.min(b, 255));
}
}

View File

@@ -0,0 +1,40 @@
package com.fluxup.app;
public class CosmeticItem {
public String id;
public String name;
public String category; // "corpo", "cabelo", "rosto", "roupa", "acessorios", "efeitos", "molduras"
public String property; // "clothesStyle", "accessory", "frame", "effect", etc.
public String value;
public String unlockRequirement;
public CosmeticItem(String id, String name, String category, String property, String value, String unlockRequirement) {
this.id = id;
this.name = name;
this.category = category;
this.property = property;
this.value = value;
this.unlockRequirement = unlockRequirement;
}
public boolean isUnlocked(Usuario user) {
if (user == null) return false;
switch (id) {
case "effect_fire":
return user.streak >= 7;
case "effect_glow":
return user.xp >= 5000;
case "clothes_outfit":
return user.xp >= 1000;
case "frame_gold":
return "Ouro".equalsIgnoreCase(user.league) || "Gold".equalsIgnoreCase(user.league) || user.xp >= 3000;
case "accessory_cap":
return user.total_tasks_concluidas >= 50;
case "accessory_headphones":
return user.tempo_foco_total >= 600; // 10 horas * 60 minutos
default:
return true; // Itens normais estão sempre desbloqueados
}
}
}

View File

@@ -111,6 +111,12 @@ public class FindFriendsActivity extends AppCompatActivity {
tvName.setText(user.usuario);
tvStats.setText("Nível " + user.level + "" + user.xp + " XP");
AvatarView ivAvatar = view.findViewById(R.id.ivFriendAvatar);
if (ivAvatar != null && user.avatar != null) {
ivAvatar.setAvatarData(user.avatar);
ivAvatar.setLeague(user.league);
}
btnAction.setOnClickListener(v -> {
btnAction.setText("Pendente");
btnAction.setEnabled(false);

View File

@@ -40,6 +40,7 @@ public class InicioFragment extends Fragment {
private FocusState currentFocusState = FocusState.NOT_STARTED;
private TextView tvTimer, tvGreeting, tvMotivationalSubtitle, tvFocusStatus, tvFocusTitle;
private AvatarView ivUserAvatar;
private TextView tvTodayStreak, tvTodayXP, tvTodayTasksCount, tvNoTasksIncentive;
private TextView tvDailyRewardGoal;
private ProgressBar pbDailyTasksProgress;
@@ -90,6 +91,7 @@ public class InicioFragment extends Fragment {
// Header
tvGreeting = view.findViewById(R.id.tvGreeting);
tvMotivationalSubtitle = view.findViewById(R.id.tvMotivationalSubtitle);
ivUserAvatar = view.findViewById(R.id.ivUserAvatar);
view.findViewById(R.id.cardProfileAvatar).setOnClickListener(v -> {
if (getActivity() instanceof MainActivity) {
BottomNavigationView nav = ((MainActivity) getActivity()).findViewById(R.id.bottom_navigation);
@@ -193,6 +195,10 @@ public class InicioFragment extends Fragment {
if (tvGreeting != null) {
tvGreeting.setText("Olá, " + user.usuario + "!");
}
if (ivUserAvatar != null && user.avatar != null) {
ivUserAvatar.setAvatarData(user.avatar);
ivUserAvatar.setLeague(user.league);
}
refreshHomeStats();
@@ -535,23 +541,28 @@ public class InicioFragment extends Fragment {
for (com.google.firebase.firestore.DocumentSnapshot doc : snapshots.getDocuments()) {
Usuario u = doc.toObject(Usuario.class);
if (u != null) {
addMiniRankingItem(rank++, u.usuario, u.xp_hoje);
addMiniRankingItem(rank++, u);
}
}
});
}
private void addMiniRankingItem(int rank, String name, int xp) {
private void addMiniRankingItem(int rank, Usuario u) {
if (getContext() == null || miniRankingContainer == null) return;
View item = getLayoutInflater().inflate(R.layout.item_ranking_user, miniRankingContainer, false);
TextView tvRank = item.findViewById(R.id.tvRankPosition);
TextView tvName = item.findViewById(R.id.tvRankingName);
TextView tvXP = item.findViewById(R.id.tvRankingXP);
AvatarView ivAvatar = item.findViewById(R.id.ivRankingAvatar);
tvRank.setText("#" + rank);
tvName.setText(name);
tvXP.setText(xp + " XP");
tvName.setText(u.usuario);
tvXP.setText(u.xp_hoje + " XP");
if (ivAvatar != null && u.avatar != null) {
ivAvatar.setAvatarData(u.avatar);
ivAvatar.setLeague(u.league);
}
// Remove elevation for mini ranking to keep it clean
if (item instanceof androidx.cardview.widget.CardView) {

View File

@@ -88,6 +88,12 @@ public class MyFriendsActivity extends AppCompatActivity {
tvLabel.setVisibility(View.VISIBLE);
tvLabel.setTextColor(getResources().getColor(R.color.primary_purple));
AvatarView ivAvatar = view.findViewById(R.id.ivRankingAvatar);
if (ivAvatar != null && friend.avatar != null) {
ivAvatar.setAvatarData(friend.avatar);
ivAvatar.setLeague(friend.league);
}
view.setOnClickListener(v -> {
// Open public profile (future)
Toast.makeText(this, "Perfil de " + friend.usuario, Toast.LENGTH_SHORT).show();

View File

@@ -43,7 +43,8 @@ import java.util.Locale;
public class ProfileFragment extends Fragment {
private TextView tvProfileName, tvProfileHandle;
private ImageView ivProfileAvatar, ivLeagueBadgeSmall, ivLeagueIcon;
private AvatarView ivProfileAvatar;
private ImageView ivLeagueBadgeSmall, ivLeagueIcon;
private TextView tvUserLevel, tvXpFraction, tvLeagueName, tvLeagueMotivation, tvConsistencyPhrase;
private ProgressBar pbXpLevel;
private RecyclerView rvMiniCalendar;
@@ -124,6 +125,12 @@ public class ProfileFragment extends Fragment {
});
}
// Avatar Editor
View cardAvatar = view.findViewById(R.id.cardAvatar);
if (cardAvatar != null) {
cardAvatar.setOnClickListener(v -> startActivity(new Intent(getActivity(), AvatarEditorActivity.class)));
}
// Add Friends (+)
View btnAddFriends = view.findViewById(R.id.btnAddFriends);
if (btnAddFriends != null) {
@@ -182,6 +189,10 @@ public class ProfileFragment extends Fragment {
private void updateBasicInfo(Usuario user) {
tvProfileName.setText(user.usuario);
tvProfileHandle.setText(user.handle);
if (user.avatar != null && ivProfileAvatar != null) {
ivProfileAvatar.setAvatarData(user.avatar);
ivProfileAvatar.setLeague(user.league);
}
}
private void updateXpBar(Usuario user) {
@@ -201,10 +212,10 @@ public class ProfileFragment extends Fragment {
int progressPercent = (int) ((progressInLevel * 100.0f) / levelXpSpan);
// Animate Progress Bar
ObjectAnimator.ofInt(pbXpLevel, "progress", pbXpLevel.getProgress(), progressPercent)
.setDuration(1000)
.setInterpolator(new DecelerateInterpolator())
.start();
ObjectAnimator animator = ObjectAnimator.ofInt(pbXpLevel, "progress", pbXpLevel.getProgress(), progressPercent);
animator.setDuration(1000);
animator.setInterpolator(new DecelerateInterpolator());
animator.start();
}
private int calculateLevel(int totalXp) {
@@ -423,6 +434,12 @@ public class ProfileFragment extends Fragment {
tvStreak.setText("🔥 " + friend.streak);
}
AvatarView ivAvatar = v.findViewById(R.id.ivRankingAvatar);
if (ivAvatar != null && friend.avatar != null) {
ivAvatar.setAvatarData(friend.avatar);
ivAvatar.setLeague(friend.league);
}
// Gamify friend item
TextView tvRank = v.findViewById(R.id.tvRankPosition);
if (tvRank != null) tvRank.setText("#" + (friendsListContainer.getChildCount() + 1));

View File

@@ -310,7 +310,10 @@ public class SearchFragment extends Fragment {
Usuario user = items.get(position);
holder.name.setText(user.usuario);
holder.stats.setText("🔥 " + user.streak + " dias • " + user.league);
// holder.avatar setup if needed
if (holder.avatar != null && user.avatar != null) {
holder.avatar.setAvatarData(user.avatar);
holder.avatar.setLeague(user.league);
}
holder.btnAdd.setOnClickListener(v -> Toast.makeText(getContext(), "Pedido enviado!", Toast.LENGTH_SHORT).show());
}
@@ -318,7 +321,7 @@ public class SearchFragment extends Fragment {
public int getItemCount() { return items.size(); }
class ViewHolder extends RecyclerView.ViewHolder {
ImageView avatar;
AvatarView avatar;
TextView name, stats;
View btnAdd;
ViewHolder(View v) {

View File

@@ -136,6 +136,11 @@ public class TrophiesActivity extends AppCompatActivity {
}
// Bottom Card
AvatarView ivAvatar = findViewById(R.id.ivUserAvatar);
if (ivAvatar != null && user.avatar != null) {
ivAvatar.setAvatarData(user.avatar);
ivAvatar.setLeague(user.league);
}
tvUserName.setText(user.usuario);
tvUserDivision.setText(current.name + "" + user.xp + " XP");
if (next != null) {
@@ -243,10 +248,15 @@ public class TrophiesActivity extends AppCompatActivity {
TextView tvXP = view.findViewById(R.id.tvRankingXP);
TextView tvTu = view.findViewById(R.id.tvRankingLabelTu);
androidx.cardview.widget.CardView card = view.findViewById(R.id.cardRankingUser);
AvatarView ivAvatar = view.findViewById(R.id.ivRankingAvatar);
tvPos.setText("#" + pos);
tvName.setText(user.usuario);
tvXP.setText(user.xp_semanal + " XP");
if (ivAvatar != null && user.avatar != null) {
ivAvatar.setAvatarData(user.avatar);
ivAvatar.setLeague(user.league);
}
if (user.id_usuario.equals(myUid)) {
tvTu.setVisibility(View.VISIBLE);

View File

@@ -0,0 +1,42 @@
package com.fluxup.app;
import java.util.ArrayList;
import java.util.List;
public class UnlockManager {
private static List<CosmeticItem> items;
public static List<CosmeticItem> getLockedCosmetics() {
if (items == null) {
items = new ArrayList<>();
// 🔥 7 dias ofensiva -> aura de fogo
items.add(new CosmeticItem("effect_fire", "Aura de Fogo", "efeitos", "effect", "fire_aura", "Desbloqueia com 7 dias de ofensiva"));
// ⚡ 5000 XP -> brilho especial
items.add(new CosmeticItem("effect_glow", "Brilho Roxo", "efeitos", "effect", "glow", "Desbloqueia com 5000 XP"));
// ⚡ 1000 XP -> roupa premium (outfit)
items.add(new CosmeticItem("clothes_outfit", "Casaco Premium", "roupa", "clothesStyle", "outfit", "Desbloqueia com 1000 XP"));
// 🏆 Liga Ouro -> moldura dourada
items.add(new CosmeticItem("frame_gold", "Moldura Dourada", "molduras", "frame", "gold", "Desbloqueia na Liga Ouro"));
// 🎯 50 tarefas concluídas -> boné de campeão
items.add(new CosmeticItem("accessory_cap", "Boné de Campeão", "acessorios", "accessory", "cap", "Desbloqueia com 50 tarefas concluídas"));
// ⏱ 10h de foco -> headphones de foco
items.add(new CosmeticItem("accessory_headphones", "Headphones de Foco", "acessorios", "accessory", "headphones", "Desbloqueia com 10h de foco"));
}
return items;
}
public static CosmeticItem findCosmetic(String property, String value) {
for (CosmeticItem item : getLockedCosmetics()) {
if (item.property.equals(property) && item.value.equals(value)) {
return item;
}
}
return null;
}
public static boolean isItemLockedForUser(Usuario user, String property, String value) {
CosmeticItem item = findCosmetic(property, value);
if (item == null) return false; // Se não estiver na lista de bloqueados, está livre
return !item.isUnlocked(user);
}
}

View File

@@ -37,6 +37,7 @@ public class Usuario {
public String last_streak_completed_date = ""; // YYYY-MM-DD
public String last_reward_claim_date = ""; // YYYY-MM-DD
public String avatar_url = "";
public AvatarData avatar = new AvatarData(); // Avatar gamificado
public java.util.List<String> dias_concluidos = new java.util.ArrayList<>();
public Usuario() {}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#407C3AED"
android:endColor="#007C3AED"
android:angle="270" />
</shape>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="12dp" />
<solid android:color="#E5E7EB" />
</shape>
</item>
<item android:id="@android:id/progress">
<scale android:scaleWidth="100%">
<shape>
<corners android:radius="12dp" />
<gradient
android:startColor="#A35AFF"
android:endColor="#7C3AED"
android:angle="0" />
</shape>
</scale>
</item>
</layer-list>

View File

@@ -0,0 +1,201 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/background_light">
<!-- HEADER -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="64dp"
android:paddingHorizontal="16dp"
android:background="@color/card_background"
android:elevation="4dp">
<ImageButton
android:id="@+id/btnBackEditor"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_back"
app:tint="@color/text_primary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Avatar"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@color/text_primary"
android:layout_centerInParent="true"/>
<Button
android:id="@+id/btnSaveTop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:text="Guardar"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:textColor="@color/primary_purple"
android:textStyle="bold" />
</RelativeLayout>
<!-- AVATAR PREVIEW -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#F8FAFC">
<androidx.cardview.widget.CardView
android:layout_width="220dp"
android:layout_height="220dp"
android:layout_gravity="center"
app:cardCornerRadius="110dp"
app:cardElevation="12dp">
<com.fluxup.app.AvatarView
android:id="@+id/previewAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
</FrameLayout>
<!-- EDITOR BOTTOM SHEET -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/card_background"
android:elevation="16dp">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabsCategories"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable"
app:tabIndicatorColor="@color/primary_purple"
app:tabSelectedTextColor="@color/primary_purple"
app:tabTextColor="@color/text_secondary">
<com.google.android.material.tabs.TabItem android:text="Corpo" />
<com.google.android.material.tabs.TabItem android:text="Cabelo" />
<com.google.android.material.tabs.TabItem android:text="Rosto" />
<com.google.android.material.tabs.TabItem android:text="Roupa" />
<com.google.android.material.tabs.TabItem android:text="Acessórios" />
<com.google.android.material.tabs.TabItem android:text="Molduras" />
<com.google.android.material.tabs.TabItem android:text="Efeitos" />
</com.google.android.material.tabs.TabLayout>
<!-- OPTIONS CONTAINER -->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:padding="16dp">
<!-- CORPO -->
<ScrollView android:id="@+id/panelCorpo" android:layout_width="match_parent" android:layout_height="match_parent">
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:text="Tom de Pele" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp">
<LinearLayout android:id="@+id/llSkinColors" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
<TextView android:text="Formato do Corpo" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content">
<LinearLayout android:id="@+id/llBodyFormats" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
<!-- CABELO -->
<ScrollView android:id="@+id/panelCabelo" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone">
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:text="Estilo de Cabelo" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp">
<LinearLayout android:id="@+id/llHairStyles" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
<TextView android:text="Cor do Cabelo" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content">
<LinearLayout android:id="@+id/llHairColors" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
<!-- ROSTO -->
<ScrollView android:id="@+id/panelRosto" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone">
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:text="Olhos" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp">
<LinearLayout android:id="@+id/llEyes" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
<TextView android:text="Sobrancelhas" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp">
<LinearLayout android:id="@+id/llEyebrows" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
<TextView android:text="Boca" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp">
<LinearLayout android:id="@+id/llMouth" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
<TextView android:text="Barba" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content">
<LinearLayout android:id="@+id/llBeard" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
<!-- ROUPA -->
<ScrollView android:id="@+id/panelRoupa" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone">
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:text="Estilo" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp">
<LinearLayout android:id="@+id/llClothes" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
<TextView android:text="Cor" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content">
<LinearLayout android:id="@+id/llClothesColors" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
<!-- ACESSÓRIOS -->
<ScrollView android:id="@+id/panelAcessorios" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone">
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:text="Cabeça / Rosto" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content">
<LinearLayout android:id="@+id/llAccessories" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
<!-- MOLDURAS -->
<ScrollView android:id="@+id/panelMolduras" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone">
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:text="Molduras Especiais" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content">
<LinearLayout android:id="@+id/llFrames" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
<!-- EFEITOS -->
<ScrollView android:id="@+id/panelEfeitos" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone">
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content">
<TextView android:text="Auras e Efeitos" android:textStyle="bold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp"/>
<HorizontalScrollView android:scrollbars="none" android:layout_width="match_parent" android:layout_height="wrap_content">
<LinearLayout android:id="@+id/llEffects" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</HorizontalScrollView>
</LinearLayout>
</ScrollView>
</FrameLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -197,12 +197,10 @@
android:layout_height="48dp"
app:cardCornerRadius="24dp"
app:cardElevation="0dp">
<ImageView
<com.fluxup.app.AvatarView
android:id="@+id/ivUserAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_nav_profile" />
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
<LinearLayout

View File

@@ -53,13 +53,10 @@
app:cardCornerRadius="28dp"
app:cardElevation="2dp">
<ImageView
<com.fluxup.app.AvatarView
android:id="@+id/ivUserAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_nav_profile"
app:tint="@color/primary_purple" />
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
</RelativeLayout>

View File

@@ -50,12 +50,10 @@
android:layout_centerInParent="true"
app:cardCornerRadius="55dp"
app:cardElevation="8dp">
<ImageView
<com.fluxup.app.AvatarView
android:id="@+id/ivProfileAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_nav_profile" />
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
<ImageView

View File

@@ -20,12 +20,10 @@
app:cardCornerRadius="24dp"
app:cardElevation="0dp">
<ImageView
<com.fluxup.app.AvatarView
android:id="@+id/ivUserAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_nav_profile" />
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
<LinearLayout

View File

@@ -19,12 +19,10 @@
android:layout_height="50dp"
app:cardCornerRadius="25dp"
app:cardElevation="0dp">
<ImageView
<com.fluxup.app.AvatarView
android:id="@+id/ivFriendAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_nav_profile" />
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
<LinearLayout

View File

@@ -22,13 +22,10 @@
app:cardCornerRadius="32dp"
app:cardElevation="0dp">
<ImageView
<com.fluxup.app.AvatarView
android:id="@+id/ivFriendAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_nav_profile"
app:tint="@color/primary_purple" />
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
<TextView

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:paddingHorizontal="4dp">
<TextView
android:id="@+id/tvDayName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="S"
android:textSize="10sp"
android:textColor="@color/text_secondary"
android:layout_marginBottom="4dp"/>
<View
android:id="@+id/vDayIndicator"
android:layout_width="28dp"
android:layout_height="28dp"
android:background="@drawable/node_circle_bg"
android:backgroundTint="#E0E0E0" />
</LinearLayout>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="16dp"
app:cardElevation="1dp"
app:cardBackgroundColor="@color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="12dp"
android:gravity="center">
<TextView
android:id="@+id/tvStatIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="🎯"
android:textSize="20sp"
android:layout_marginBottom="4dp"/>
<TextView
android:id="@+id/tvStatValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="120"
android:textStyle="bold"
android:textSize="18sp"
android:textColor="@color/text_primary"/>
<TextView
android:id="@+id/tvStatLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tarefas"
android:textSize="11sp"
android:textColor="@color/text_secondary"
android:textAllCaps="true"
android:letterSpacing="0.05"/>
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -31,12 +31,10 @@
android:layout_marginStart="8dp"
app:cardCornerRadius="20dp"
app:cardElevation="0dp">
<ImageView
<com.fluxup.app.AvatarView
android:id="@+id/ivRankingAvatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_nav_profile" />
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
<LinearLayout