936 lines
36 KiB
Java
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;
|
|
}
|
|
}
|
|
|