Files
Client_Old/src/main/java/de/winniepat/citrus/gui/ProfileScreen.java
T
2026-05-01 18:54:57 +02:00

936 lines
36 KiB
Java

package de.winniepat.citrus.gui;
import de.winniepat.citrus.managers.ProfileManager;
import de.winniepat.citrus.managers.ProfileManager.PlayerProfile;
import de.winniepat.citrus.managers.ProfileManager.SearchResult;
import de.winniepat.citrus.managers.WallpaperManager;
import de.winniepat.citrus.managers.ConfigManager;
import de.winniepat.citrus.utils.texture.CdnTextureDrawable;
import icyllis.modernui.animation.*;
import icyllis.modernui.core.Context;
import icyllis.modernui.fragment.Fragment;
import icyllis.modernui.graphics.drawable.*;
import icyllis.modernui.mc.*;
import icyllis.modernui.text.InputFilter;
import icyllis.modernui.text.TextPaint;
import icyllis.modernui.util.DataSet;
import icyllis.modernui.view.*;
import icyllis.modernui.widget.*;
import net.minecraft.client.MinecraftClient;
import org.jetbrains.annotations.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ProfileScreen extends Fragment implements ScreenCallback {
private LinearLayout mContentContainer;
private LinearLayout mSearchResultsContainer;
private EditText mSearchInput;
private boolean mIsEditMode = false;
private PlayerProfile mCurrentProfile;
private UUID mViewingUuid;
private CdnTextureDrawable mBackground1Drawable;
private final List<View> mStaggeredViews = new ArrayList<>();
private EditText mBioEdit;
private EditText mDiscordEdit;
private EditText mTwitterEdit;
private EditText mYoutubeEdit;
private EditText mTwitchEdit;
private EditText mInstagramEdit;
private EditText mGithubEdit;
private EditText mWebsiteEdit;
private float getGuiScale() {
return (float) MinecraftClient.getInstance().getWindow().getScaleFactor();
}
private void setFixedTextSize(TextView tv, float px) {
float scale = getGuiScale();
tv.setTextSize(px / scale);
}
@Override
public View onCreateView(@NotNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable DataSet savedInstanceState) {
Context context = requireContext();
FrameLayout root = new FrameLayout(context);
root.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
));
FrameLayout backgroundView = new FrameLayout(context);
backgroundView.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
));
backgroundView.setScaleX(1.1f);
backgroundView.setScaleY(1.1f);
View backgroundView1 = new View(context);
backgroundView1.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
));
backgroundView.addView(backgroundView1);
root.addView(backgroundView);
mBackground1Drawable = new CdnTextureDrawable();
backgroundView1.setBackground(mBackground1Drawable);
String currentWallpaper = ConfigManager.config.mainmenuWallpaper;
if (currentWallpaper == null || currentWallpaper.isEmpty()) {
currentWallpaper = "default.png";
}
WallpaperManager.loadWallpaperWithData(currentWallpaper, imageData -> {
MuiModApi.postToUiThread(() -> mBackground1Drawable.setImageData(imageData));
});
ObjectAnimator bgPulseX = ObjectAnimator.ofFloat(backgroundView, View.SCALE_X, 1.1f, 1.15f);
ObjectAnimator bgPulseY = ObjectAnimator.ofFloat(backgroundView, View.SCALE_Y, 1.1f, 1.15f);
bgPulseX.setDuration(15000);
bgPulseY.setDuration(15000);
bgPulseX.setRepeatCount(ValueAnimator.INFINITE);
bgPulseY.setRepeatCount(ValueAnimator.INFINITE);
bgPulseX.setRepeatMode(ValueAnimator.REVERSE);
bgPulseY.setRepeatMode(ValueAnimator.REVERSE);
bgPulseX.start();
bgPulseY.start();
FrameLayout contentWrapper = new FrameLayout(context);
contentWrapper.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
));
root.addView(contentWrapper);
LinearLayout leftPanel = new LinearLayout(context);
leftPanel.setOrientation(LinearLayout.VERTICAL);
FrameLayout.LayoutParams leftPanelLp = new FrameLayout.LayoutParams(
320,
ViewGroup.LayoutParams.WRAP_CONTENT
);
leftPanelLp.gravity = Gravity.CENTER_VERTICAL;
leftPanelLp.setMargins(60, 0, 0, 0);
leftPanel.setLayoutParams(leftPanelLp);
TextView title = new TextView(context);
title.setText("PROFILES");
setFixedTextSize(title, 96);
title.getPaint().setTextStyle(TextPaint.BOLD);
title.setTextColor(0xFFFFFFFF);
leftPanel.addView(title);
mStaggeredViews.add(title);
TextView subtitle = new TextView(context);
subtitle.setText("View and edit player profiles");
setFixedTextSize(subtitle, 32);
subtitle.setTextColor(0xCCFFFFFF);
LinearLayout.LayoutParams subtitleLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
subtitleLp.setMargins(0, 0, 0, dp(30));
subtitle.setLayoutParams(subtitleLp);
leftPanel.addView(subtitle);
mStaggeredViews.add(subtitle);
LinearLayout searchSection = new LinearLayout(context);
searchSection.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams searchSectionLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
searchSectionLp.setMargins(0, 0, 0, dp(20));
searchSection.setLayoutParams(searchSectionLp);
TextView searchLabel = new TextView(context);
searchLabel.setText("Search Players");
setFixedTextSize(searchLabel, 28);
searchLabel.setTextColor(0x88FFFFFF);
searchLabel.setPadding(0, 0, 0, dp(8));
searchSection.addView(searchLabel);
mSearchInput = new EditText(context);
mSearchInput.setHint("Enter player name...");
setFixedTextSize(mSearchInput, 32);
mSearchInput.setTextColor(0xFFFFFFFF);
mSearchInput.setHintTextColor(0x66FFFFFF);
mSearchInput.setPadding(dp(16), dp(12), dp(16), dp(12));
mSearchInput.setSingleLine(true);
ShapeDrawable inputBg = new ShapeDrawable();
inputBg.setShape(ShapeDrawable.RECTANGLE);
inputBg.setColor(0x26FFFFFF);
inputBg.setCornerRadius(dp(10));
mSearchInput.setBackground(inputBg);
LinearLayout.LayoutParams inputLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
inputLp.setMargins(0, 0, 0, dp(8));
mSearchInput.setLayoutParams(inputLp);
searchSection.addView(mSearchInput);
leftPanel.addView(searchSection);
mStaggeredViews.add(searchSection);
addNavButton(leftPanel, "Search", "multiplayer_icon", v -> performSearch());
addNavButton(leftPanel, "My Profile", "settings_icon", v -> loadOwnProfile());
addNavButton(leftPanel, "Back to Menu", "door_icon", v -> {
MinecraftClient.getInstance().execute(() -> {
MinecraftClient.getInstance().setScreen(MuiModApi.get().createScreen(new MainMenuScreen(), null, null));
});
});
contentWrapper.addView(leftPanel);
LinearLayout rightPanel = new LinearLayout(context);
rightPanel.setOrientation(LinearLayout.VERTICAL);
FrameLayout.LayoutParams rightPanelLp = new FrameLayout.LayoutParams(
500,
500
);
rightPanelLp.gravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT;
rightPanelLp.setMargins(0, 0, 60, 0);
rightPanel.setLayoutParams(rightPanelLp);
mSearchResultsContainer = new LinearLayout(context);
mSearchResultsContainer.setOrientation(LinearLayout.VERTICAL);
mSearchResultsContainer.setVisibility(View.GONE);
LinearLayout.LayoutParams resultsLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
resultsLp.setMargins(0, 0, 0, dp(12));
mSearchResultsContainer.setLayoutParams(resultsLp);
ShapeDrawable resultsBg = new ShapeDrawable();
resultsBg.setShape(ShapeDrawable.RECTANGLE);
resultsBg.setColor(0x33000000);
resultsBg.setCornerRadius(dp(16));
mSearchResultsContainer.setBackground(resultsBg);
mSearchResultsContainer.setPadding(dp(16), dp(16), dp(16), dp(16));
rightPanel.addView(mSearchResultsContainer);
LinearLayout profilePanel = new LinearLayout(context);
profilePanel.setOrientation(LinearLayout.VERTICAL);
profilePanel.setPadding(dp(16), dp(16), dp(16), dp(16));
LinearLayout.LayoutParams profilePanelLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
0, 1f
);
profilePanel.setLayoutParams(profilePanelLp);
ShapeDrawable panelBg = new ShapeDrawable();
panelBg.setShape(ShapeDrawable.RECTANGLE);
panelBg.setColor(0x33000000);
panelBg.setCornerRadius(dp(16));
profilePanel.setBackground(panelBg);
TextView panelTitle = new TextView(context);
panelTitle.setText("PROFILE");
setFixedTextSize(panelTitle, 48);
panelTitle.getPaint().setTextStyle(TextPaint.BOLD);
panelTitle.setTextColor(0xFF4a90d9);
panelTitle.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams panelTitleLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
panelTitleLp.setMargins(0, 0, 0, dp(12));
panelTitle.setLayoutParams(panelTitleLp);
profilePanel.addView(panelTitle);
ScrollView scrollView = new ScrollView(context);
LinearLayout.LayoutParams scrollLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
0, 1f
);
scrollView.setLayoutParams(scrollLp);
scrollView.setScrollBarSize(dp(6));
mContentContainer = new LinearLayout(context);
mContentContainer.setOrientation(LinearLayout.VERTICAL);
scrollView.addView(mContentContainer);
profilePanel.addView(scrollView);
rightPanel.addView(profilePanel);
mStaggeredViews.add(rightPanel);
contentWrapper.addView(rightPanel);
TextView infoText = new TextView(context);
infoText.setText("Share your profile with friends • Add your socials to connect");
setFixedTextSize(infoText, 24);
infoText.setTextColor(0x66FFFFFF);
FrameLayout.LayoutParams infoLp = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
infoLp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
infoLp.setMargins(0, 0, 0, dp(20));
infoText.setLayoutParams(infoLp);
contentWrapper.addView(infoText);
mStaggeredViews.add(infoText);
loadOwnProfile();
animateEntrance();
return root;
}
private void addNavButton(LinearLayout parent, String text, String iconPath, View.OnClickListener listener) {
Context context = requireContext();
FrameLayout btnWrapper = new FrameLayout(context);
LinearLayout.LayoutParams wrapperLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
56
);
wrapperLp.setMargins(0, 0, 0, 8);
btnWrapper.setLayoutParams(wrapperLp);
ShapeDrawable btnBg = new ShapeDrawable();
btnBg.setShape(ShapeDrawable.RECTANGLE);
btnBg.setColor(0x1AFFFFFF);
btnBg.setCornerRadius(12);
btnWrapper.setBackground(btnBg);
LinearLayout content = new LinearLayout(context);
content.setOrientation(LinearLayout.HORIZONTAL);
content.setGravity(Gravity.CENTER_VERTICAL);
content.setPadding(20, 0, 20, 0);
ImageDrawable icon = new ImageDrawable("citrus", "startscreen/" + iconPath + ".png");
icon.setBounds(0, 0, 24, 24);
TextView tv = new TextView(context);
tv.setText(text);
setFixedTextSize(tv, 36);
tv.setTextColor(0xFFFFFFFF);
tv.setCompoundDrawables(icon, null, null, null);
tv.setCompoundDrawablePadding(16);
content.addView(tv);
btnWrapper.addView(content);
btnWrapper.setClickable(true);
btnWrapper.setFocusable(true);
btnWrapper.setOnClickListener(listener);
btnWrapper.setOnHoverListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
ObjectAnimator animator = ObjectAnimator.ofFloat(v, View.TRANSLATION_X, 0f, (float) dp(15));
animator.setDuration(250);
animator.setInterpolator(TimeInterpolator.DECELERATE);
animator.start();
btnBg.setColor(0x33FFFFFF);
return true;
} else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
ObjectAnimator animator = ObjectAnimator.ofFloat(v, View.TRANSLATION_X, (float) dp(15), 0f);
animator.setDuration(250);
animator.setInterpolator(TimeInterpolator.DECELERATE);
animator.start();
btnBg.setColor(0x1AFFFFFF);
return true;
}
return false;
});
parent.addView(btnWrapper);
mStaggeredViews.add(btnWrapper);
}
private void animateEntrance() {
long delay = 100;
for (View v : mStaggeredViews) {
v.setAlpha(0);
float startX = -dp(40);
ViewGroup.LayoutParams lp = v.getLayoutParams();
if (lp instanceof FrameLayout.LayoutParams flp) {
if ((flp.gravity & Gravity.RIGHT) == Gravity.RIGHT) {
startX = dp(40);
}
}
v.setTranslationX(startX);
ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
ObjectAnimator trans = ObjectAnimator.ofFloat(v, View.TRANSLATION_X, startX, 0f);
AnimatorSet set = new AnimatorSet();
set.playTogether(alpha, trans);
set.setDuration(800);
set.setStartDelay(delay);
set.setInterpolator(TimeInterpolator.DECELERATE);
set.start();
delay += 60;
}
}
private void loadOwnProfile() {
mSearchResultsContainer.setVisibility(View.GONE);
showLoading();
UUID uuid = MinecraftClient.getInstance().getSession().getUuidOrNull();
if (uuid == null) {
showError("Not logged in");
return;
}
mViewingUuid = uuid;
ProfileManager.getOwnProfile().thenAccept(profile -> {
MuiModApi.postToUiThread(() -> {
if (profile != null) {
mCurrentProfile = profile;
showProfileView(profile, true);
} else {
mCurrentProfile = new PlayerProfile(
uuid.toString(),
MinecraftClient.getInstance().getSession().getUsername()
);
showProfileEdit(mCurrentProfile);
}
});
});
}
private void loadProfile(UUID uuid) {
mSearchResultsContainer.setVisibility(View.GONE);
showLoading();
mViewingUuid = uuid;
UUID ownUuid = MinecraftClient.getInstance().getSession().getUuidOrNull();
boolean isOwn = ownUuid != null && ownUuid.equals(uuid);
ProfileManager.getProfile(uuid).thenAccept(profile -> {
MuiModApi.postToUiThread(() -> {
if (profile != null) {
mCurrentProfile = profile;
showProfileView(profile, isOwn);
} else {
showError("Profile not found");
}
});
});
}
private void performSearch() {
String query = mSearchInput.getText().toString().trim();
if (query.isEmpty()) return;
mSearchResultsContainer.removeAllViews();
mSearchResultsContainer.setVisibility(View.VISIBLE);
TextView searching = new TextView(requireContext());
searching.setText("Searching...");
setFixedTextSize(searching, 28);
searching.setTextColor(0x88FFFFFF);
mSearchResultsContainer.addView(searching);
ProfileManager.searchProfiles(query).thenAccept(results -> {
MuiModApi.postToUiThread(() -> {
mSearchResultsContainer.removeAllViews();
if (results.isEmpty()) {
TextView noResults = new TextView(requireContext());
noResults.setText("No profiles found");
setFixedTextSize(noResults, 28);
noResults.setTextColor(0x88FFFFFF);
mSearchResultsContainer.addView(noResults);
} else {
TextView resultsLabel = new TextView(requireContext());
resultsLabel.setText("Results (" + results.size() + ")");
setFixedTextSize(resultsLabel, 32);
resultsLabel.getPaint().setTextStyle(TextPaint.BOLD);
resultsLabel.setTextColor(0xFFFFD700);
resultsLabel.setPadding(0, 0, 0, dp(10));
mSearchResultsContainer.addView(resultsLabel);
for (SearchResult result : results) {
mSearchResultsContainer.addView(createSearchResultCard(result));
}
}
});
});
}
private View createSearchResultCard(SearchResult result) {
Context context = requireContext();
LinearLayout card = new LinearLayout(context);
card.setOrientation(LinearLayout.HORIZONTAL);
card.setGravity(Gravity.CENTER_VERTICAL);
card.setPadding(dp(12), dp(10), dp(12), dp(10));
LinearLayout.LayoutParams cardLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
cardLp.setMargins(0, 0, 0, dp(8));
card.setLayoutParams(cardLp);
ShapeDrawable cardBg = new ShapeDrawable();
cardBg.setShape(ShapeDrawable.RECTANGLE);
cardBg.setColor(0x20FFFFFF);
cardBg.setCornerRadius(dp(10));
card.setBackground(cardBg);
card.setClickable(true);
card.setOnClickListener(v -> {
try {
UUID uuid = UUID.fromString(result.uuid);
loadProfile(uuid);
} catch (Exception ignored) {}
});
card.setOnHoverListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
cardBg.setColor(0x33FFFFFF);
} else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
cardBg.setColor(0x20FFFFFF);
}
return false;
});
TextView nameTv = new TextView(context);
nameTv.setText(result.playerName);
setFixedTextSize(nameTv, 30);
nameTv.setTextColor(0xFFFFFFFF);
LinearLayout.LayoutParams nameLp = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f
);
nameTv.setLayoutParams(nameLp);
card.addView(nameTv);
if (result.hasSocials) {
TextView socialsTag = new TextView(context);
socialsTag.setText("Socials");
setFixedTextSize(socialsTag, 22);
socialsTag.setTextColor(0xFF5cb85c);
socialsTag.setPadding(dp(8), dp(4), dp(8), dp(4));
ShapeDrawable tagBg = new ShapeDrawable();
tagBg.setShape(ShapeDrawable.RECTANGLE);
tagBg.setColor(0x335cb85c);
tagBg.setCornerRadius(dp(4));
socialsTag.setBackground(tagBg);
card.addView(socialsTag);
}
return card;
}
private void showLoading() {
mContentContainer.removeAllViews();
LinearLayout loadingContainer = new LinearLayout(requireContext());
loadingContainer.setOrientation(LinearLayout.VERTICAL);
loadingContainer.setGravity(Gravity.CENTER);
loadingContainer.setPadding(dp(20), dp(60), dp(20), dp(60));
TextView loading = new TextView(requireContext());
loading.setText("Loading profile...");
setFixedTextSize(loading, 32);
loading.setTextColor(0x88FFFFFF);
loading.setGravity(Gravity.CENTER);
loadingContainer.addView(loading);
mContentContainer.addView(loadingContainer);
}
private void showError(String message) {
mContentContainer.removeAllViews();
LinearLayout errorContainer = new LinearLayout(requireContext());
errorContainer.setOrientation(LinearLayout.VERTICAL);
errorContainer.setGravity(Gravity.CENTER);
errorContainer.setPadding(dp(20), dp(60), dp(20), dp(60));
TextView error = new TextView(requireContext());
error.setText(message);
setFixedTextSize(error, 32);
error.setTextColor(0xFFFF6B6B);
error.setGravity(Gravity.CENTER);
errorContainer.addView(error);
mContentContainer.addView(errorContainer);
}
private void showProfileView(PlayerProfile profile, boolean isOwn) {
mIsEditMode = false;
mContentContainer.removeAllViews();
Context context = requireContext();
LinearLayout header = new LinearLayout(context);
header.setOrientation(LinearLayout.HORIZONTAL);
header.setGravity(Gravity.CENTER_VERTICAL);
LinearLayout.LayoutParams headerLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
headerLp.setMargins(0, 0, 0, dp(16));
header.setLayoutParams(headerLp);
TextView nameTv = new TextView(context);
nameTv.setText(profile.playerName);
setFixedTextSize(nameTv, 42);
nameTv.getPaint().setTextStyle(TextPaint.BOLD);
nameTv.setTextColor(0xFFFFFFFF);
LinearLayout.LayoutParams nameLp = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f
);
nameTv.setLayoutParams(nameLp);
header.addView(nameTv);
if (isOwn) {
TextView editBtn = createSmallActionButton(context, "Edit", 0xFF4a90d9);
editBtn.setOnClickListener(v -> showProfileEdit(profile));
header.addView(editBtn);
}
mContentContainer.addView(header);
if (profile.bio != null && !profile.bio.trim().isEmpty()) {
mContentContainer.addView(createSectionLabel(context, "About"));
TextView bioTv = new TextView(context);
bioTv.setText(profile.bio);
setFixedTextSize(bioTv, 28);
bioTv.setTextColor(0xCCFFFFFF);
bioTv.setPadding(0, 0, 0, dp(16));
mContentContainer.addView(bioTv);
}
boolean hasSocials = profile.hasSocialLinks();
if (hasSocials) {
mContentContainer.addView(createSectionLabel(context, "Social Links"));
LinearLayout socialsGrid = new LinearLayout(context);
socialsGrid.setOrientation(LinearLayout.VERTICAL);
if (!isEmpty(profile.discord)) {
socialsGrid.addView(createSocialLink(context, "Discord", profile.discord, 0xFF5865F2));
}
if (!isEmpty(profile.twitter)) {
socialsGrid.addView(createSocialLink(context, "Twitter/X", profile.twitter, 0xFF1DA1F2));
}
if (!isEmpty(profile.youtube)) {
socialsGrid.addView(createSocialLink(context, "YouTube", profile.youtube, 0xFFFF0000));
}
if (!isEmpty(profile.twitch)) {
socialsGrid.addView(createSocialLink(context, "Twitch", profile.twitch, 0xFF9146FF));
}
if (!isEmpty(profile.instagram)) {
socialsGrid.addView(createSocialLink(context, "Instagram", profile.instagram, 0xFFE1306C));
}
if (!isEmpty(profile.github)) {
socialsGrid.addView(createSocialLink(context, "GitHub", profile.github, 0xFFFFFFFF));
}
if (!isEmpty(profile.website)) {
socialsGrid.addView(createSocialLink(context, "Website", profile.website, 0xFF4a90d9));
}
mContentContainer.addView(socialsGrid);
}
if (!hasSocials && (profile.bio == null || profile.bio.trim().isEmpty())) {
LinearLayout emptyContainer = new LinearLayout(context);
emptyContainer.setOrientation(LinearLayout.VERTICAL);
emptyContainer.setGravity(Gravity.CENTER);
emptyContainer.setPadding(dp(16), dp(40), dp(16), dp(40));
TextView emptyTitle = new TextView(context);
emptyTitle.setText(isOwn ? "Profile Empty" : "No Info");
setFixedTextSize(emptyTitle, 32);
emptyTitle.setTextColor(0x88FFFFFF);
emptyTitle.setGravity(Gravity.CENTER);
emptyContainer.addView(emptyTitle);
TextView emptySubtitle = new TextView(context);
emptySubtitle.setText(isOwn ? "Click 'Edit' to add your info!" : "This player hasn't added info yet.");
setFixedTextSize(emptySubtitle, 24);
emptySubtitle.setTextColor(0x55FFFFFF);
emptySubtitle.setGravity(Gravity.CENTER);
emptySubtitle.setPadding(0, dp(4), 0, 0);
emptyContainer.addView(emptySubtitle);
mContentContainer.addView(emptyContainer);
}
}
private void showProfileEdit(PlayerProfile profile) {
mIsEditMode = true;
mContentContainer.removeAllViews();
Context context = requireContext();
TextView headerTv = new TextView(context);
headerTv.setText("Edit Your Profile");
setFixedTextSize(headerTv, 36);
headerTv.getPaint().setTextStyle(TextPaint.BOLD);
headerTv.setTextColor(0xFFFFFFFF);
headerTv.setPadding(0, 0, 0, dp(16));
mContentContainer.addView(headerTv);
mContentContainer.addView(createInputLabel(context, "Bio"));
mBioEdit = createEditText(context, profile.bio, "Tell others about yourself...", false);
mBioEdit.setMinLines(2);
mBioEdit.setMaxLines(4);
mBioEdit.setFilters(new InputFilter[]{new InputFilter.LengthFilter(500)});
mContentContainer.addView(mBioEdit);
mContentContainer.addView(createSectionLabel(context, "Social Links"));
mContentContainer.addView(createInputLabel(context, "Discord"));
mDiscordEdit = createEditText(context, profile.discord, "username", true);
mContentContainer.addView(mDiscordEdit);
mContentContainer.addView(createInputLabel(context, "Twitter/X"));
mTwitterEdit = createEditText(context, profile.twitter, "@username", true);
mContentContainer.addView(mTwitterEdit);
mContentContainer.addView(createInputLabel(context, "YouTube"));
mYoutubeEdit = createEditText(context, profile.youtube, "Channel URL", true);
mContentContainer.addView(mYoutubeEdit);
mContentContainer.addView(createInputLabel(context, "Twitch"));
mTwitchEdit = createEditText(context, profile.twitch, "username", true);
mContentContainer.addView(mTwitchEdit);
mContentContainer.addView(createInputLabel(context, "Instagram"));
mInstagramEdit = createEditText(context, profile.instagram, "@username", true);
mContentContainer.addView(mInstagramEdit);
mContentContainer.addView(createInputLabel(context, "GitHub"));
mGithubEdit = createEditText(context, profile.github, "username", true);
mContentContainer.addView(mGithubEdit);
mContentContainer.addView(createInputLabel(context, "Website"));
mWebsiteEdit = createEditText(context, profile.website, "https://...", true);
mContentContainer.addView(mWebsiteEdit);
LinearLayout buttonRow = new LinearLayout(context);
buttonRow.setOrientation(LinearLayout.HORIZONTAL);
buttonRow.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams buttonRowLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
buttonRowLp.setMargins(0, dp(16), 0, 0);
buttonRow.setLayoutParams(buttonRowLp);
TextView cancelBtn = createSmallActionButton(context, "Cancel", 0xFF666666);
cancelBtn.setOnClickListener(v -> showProfileView(profile, true));
LinearLayout.LayoutParams cancelLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
cancelLp.setMargins(0, 0, dp(10), 0);
cancelBtn.setLayoutParams(cancelLp);
buttonRow.addView(cancelBtn);
TextView saveBtn = createSmallActionButton(context, "Save", 0xFF5cb85c);
saveBtn.setOnClickListener(v -> saveProfile());
buttonRow.addView(saveBtn);
mContentContainer.addView(buttonRow);
}
private void saveProfile() {
PlayerProfile updated = new PlayerProfile();
updated.uuid = mCurrentProfile.uuid;
updated.playerName = MinecraftClient.getInstance().getSession().getUsername();
updated.bio = mBioEdit.getText().toString().trim();
updated.discord = mDiscordEdit.getText().toString().trim();
updated.twitter = mTwitterEdit.getText().toString().trim();
updated.youtube = mYoutubeEdit.getText().toString().trim();
updated.twitch = mTwitchEdit.getText().toString().trim();
updated.instagram = mInstagramEdit.getText().toString().trim();
updated.github = mGithubEdit.getText().toString().trim();
updated.website = mWebsiteEdit.getText().toString().trim();
showLoading();
ProfileManager.updateProfile(updated).thenAccept(error -> {
MuiModApi.postToUiThread(() -> {
if (error == null) {
mCurrentProfile = updated;
ProfileManager.invalidateCache(UUID.fromString(updated.uuid));
showProfileView(updated, true);
} else {
showError("Failed to save: " + error);
}
});
});
}
private View createSocialLink(Context context, String platform, String value, int color) {
LinearLayout row = new LinearLayout(context);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(Gravity.CENTER_VERTICAL);
row.setPadding(dp(10), dp(8), dp(10), dp(8));
LinearLayout.LayoutParams rowLp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
rowLp.setMargins(0, 0, 0, dp(6));
row.setLayoutParams(rowLp);
ShapeDrawable rowBg = new ShapeDrawable();
rowBg.setShape(ShapeDrawable.RECTANGLE);
rowBg.setColor(0x20FFFFFF);
rowBg.setCornerRadius(dp(8));
row.setBackground(rowBg);
View colorIndicator = new View(context);
colorIndicator.setLayoutParams(new LinearLayout.LayoutParams(dp(4), dp(20)));
ShapeDrawable indicatorBg = new ShapeDrawable();
indicatorBg.setShape(ShapeDrawable.RECTANGLE);
indicatorBg.setColor(color);
indicatorBg.setCornerRadius(dp(2));
colorIndicator.setBackground(indicatorBg);
row.addView(colorIndicator);
TextView platformTv = new TextView(context);
platformTv.setText(platform);
setFixedTextSize(platformTv, 26);
platformTv.getPaint().setTextStyle(TextPaint.BOLD);
platformTv.setTextColor(0xFFFFFFFF);
platformTv.setPadding(dp(10), 0, dp(8), 0);
row.addView(platformTv);
TextView valueTv = new TextView(context);
valueTv.setText(value);
setFixedTextSize(valueTv, 24);
valueTv.setTextColor(0xCCFFFFFF);
LinearLayout.LayoutParams valueLp = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f
);
valueTv.setLayoutParams(valueLp);
row.addView(valueTv);
TextView copyBtn = new TextView(context);
copyBtn.setText("Copy");
setFixedTextSize(copyBtn, 22);
copyBtn.setTextColor(0xFF4a90d9);
copyBtn.setPadding(dp(8), dp(4), dp(8), dp(4));
copyBtn.setClickable(true);
copyBtn.setOnClickListener(v -> {
MinecraftClient.getInstance().keyboard.setClipboard(value);
copyBtn.setText("");
v.postDelayed(() -> copyBtn.setText("Copy"), 2000);
});
row.addView(copyBtn);
return row;
}
private TextView createSmallActionButton(Context context, String text, int color) {
TextView btn = new TextView(context);
btn.setText(text);
setFixedTextSize(btn, 26);
btn.setTextColor(0xFFFFFFFF);
btn.setPadding(dp(14), dp(8), dp(14), dp(8));
btn.setGravity(Gravity.CENTER);
ShapeDrawable bg = new ShapeDrawable();
bg.setShape(ShapeDrawable.RECTANGLE);
bg.setColor(color);
bg.setCornerRadius(dp(8));
btn.setBackground(bg);
btn.setClickable(true);
btn.setOnHoverListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
bg.setColor(lightenColor(color, 0.2f));
} else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
bg.setColor(color);
}
return false;
});
return btn;
}
private TextView createSectionLabel(Context context, String text) {
TextView label = new TextView(context);
label.setText(text);
setFixedTextSize(label, 30);
label.getPaint().setTextStyle(TextPaint.BOLD);
label.setTextColor(0xFFFFFFFF);
label.setPadding(0, dp(8), 0, dp(8));
return label;
}
private TextView createInputLabel(Context context, String text) {
TextView label = new TextView(context);
label.setText(text);
setFixedTextSize(label, 24);
label.setTextColor(0xAAFFFFFF);
label.setPadding(0, dp(6), 0, dp(4));
return label;
}
private EditText createEditText(Context context, String value, String hint, boolean singleLine) {
EditText edit = new EditText(context);
if (value != null) edit.setText(value);
edit.setHint(hint);
setFixedTextSize(edit, 26);
edit.setTextColor(0xFFFFFFFF);
edit.setHintTextColor(0x66FFFFFF);
edit.setPadding(dp(12), dp(10), dp(12), dp(10));
edit.setSingleLine(singleLine);
ShapeDrawable bg = new ShapeDrawable();
bg.setShape(ShapeDrawable.RECTANGLE);
bg.setColor(0x26FFFFFF);
bg.setCornerRadius(dp(8));
edit.setBackground(bg);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
lp.setMargins(0, 0, 0, dp(4));
edit.setLayoutParams(lp);
return edit;
}
private boolean isEmpty(String s) {
return s == null || s.trim().isEmpty();
}
private int lightenColor(int color, float factor) {
int a = (color >> 24) & 0xFF;
int r = Math.min(255, (int) (((color >> 16) & 0xFF) * (1 + factor)));
int g = Math.min(255, (int) (((color >> 8) & 0xFF) * (1 + factor)));
int b = Math.min(255, (int) ((color & 0xFF) * (1 + factor)));
return (a << 24) | (r << 16) | (g << 8) | b;
}
private int dp(int value) {
return (int) (value * getContext().getResources().getDisplayMetrics().density);
}
@Override
public boolean isPauseScreen() {
return false;
}
@Override
public boolean shouldBlurBackground() {
return false;
}
@Override
public boolean hasDefaultBackground() {
return false;
}
}