package com.aethex.os; import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.Typeface; import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; import android.view.WindowInsetsController; import android.widget.EditText; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import org.json.JSONArray; import org.json.JSONObject; import java.util.ArrayList; import java.util.List; /** * Projects — Interactive Kanban-style project tracking board. * Supports create, edit, delete, and status cycling. */ public class ProjectsActivity extends AppCompatActivity { private ThemeManager themeManager; private Typeface displayFont; private Typeface monoFont; private SharedPreferences prefs; private LinearLayout projectListContainer; private static final String PREFS_NAME = "aethex_projects"; private static final String KEY_PROJECTS = "projects_json"; private static final String[] STATUSES = {"Planned", "In Progress", "Review", "Complete"}; private static final String[] STATUS_COLORS = {"#66FFFFFF", "#06B6D4", "#FBBF24", "#22C55E"}; private List projects = new ArrayList<>(); static class ProjectItem { String name; String description; int statusIndex; // 0=Planned, 1=In Progress, 2=Review, 3=Complete ProjectItem(String name, String description, int statusIndex) { this.name = name; this.description = description; this.statusIndex = statusIndex; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_app); hideSystemUI(); themeManager = new ThemeManager(this); displayFont = themeManager.getDisplayFont(this); monoFont = themeManager.getMonoFont(this); prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE); SoundManager.getInstance().play(SoundManager.Sound.OPEN); TextView title = findViewById(R.id.app_title); title.setText("Projects"); TextView nameDisplay = findViewById(R.id.app_name_display); findViewById(R.id.app_back).setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLOSE); finish(); overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out); }); LinearLayout content = (LinearLayout) nameDisplay.getParent(); content.removeAllViews(); content.setGravity(Gravity.TOP); content.setPadding(0, 0, 0, 0); loadProjects(); buildProjectsUI(content); View root = findViewById(R.id.app_root); root.setAlpha(0f); root.animate().alpha(1f).setDuration(300).start(); // Add bottom navigation bar BottomNavBar.attach(this, (ViewGroup) root, BottomNavBar.TAB_PROJECTS); } private void buildProjectsUI(LinearLayout parent) { int primaryColor = themeManager.getPrimaryColor(this); // Header row with title + add button LinearLayout headerRow = new LinearLayout(this); headerRow.setOrientation(LinearLayout.HORIZONTAL); headerRow.setGravity(Gravity.CENTER_VERTICAL); headerRow.setPadding(dpToPx(16), dpToPx(14), dpToPx(16), dpToPx(10)); TextView header = new TextView(this); header.setText("PROJECT BOARD"); header.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11); header.setTextColor(Color.parseColor("#66FFFFFF")); header.setTypeface(monoFont); header.setLetterSpacing(0.15f); header.setLayoutParams(new LinearLayout.LayoutParams( 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)); headerRow.addView(header); // Project count TextView countView = new TextView(this); countView.setText(projects.size() + " projects"); countView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); countView.setTextColor(Color.parseColor("#4DFFFFFF")); countView.setTypeface(monoFont); countView.setTag("project_count"); LinearLayout.LayoutParams cvP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); cvP.setMarginEnd(dpToPx(10)); countView.setLayoutParams(cvP); headerRow.addView(countView); // Add button TextView addBtn = new TextView(this); addBtn.setText("+ NEW"); addBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); addBtn.setTextColor(primaryColor); addBtn.setTypeface(monoFont, Typeface.BOLD); addBtn.setLetterSpacing(0.1f); addBtn.setPadding(dpToPx(12), dpToPx(6), dpToPx(12), dpToPx(6)); GradientDrawable addBg = new GradientDrawable(); addBg.setCornerRadius(dpToPx(6)); addBg.setStroke(dpToPx(1), primaryColor); addBg.setColor(Color.parseColor("#0D000000")); addBtn.setBackground(addBg); addBtn.setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); showCreateProjectOverlay(); }); headerRow.addView(addBtn); parent.addView(headerRow); // Status filter row LinearLayout filterRow = new LinearLayout(this); filterRow.setOrientation(LinearLayout.HORIZONTAL); filterRow.setPadding(dpToPx(16), 0, dpToPx(16), dpToPx(10)); filterRow.setGravity(Gravity.CENTER_VERTICAL); for (int i = 0; i < STATUSES.length; i++) { final int statusIdx = i; int count = 0; for (ProjectItem p : projects) if (p.statusIndex == statusIdx) count++; TextView chip = new TextView(this); chip.setText(STATUSES[i] + " " + count); chip.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9); chip.setTextColor(Color.parseColor(STATUS_COLORS[i])); chip.setTypeface(monoFont); chip.setPadding(dpToPx(8), dpToPx(4), dpToPx(8), dpToPx(4)); GradientDrawable chipBg = new GradientDrawable(); chipBg.setCornerRadius(dpToPx(4)); chipBg.setColor(Color.parseColor("#0D000000")); chip.setBackground(chipBg); LinearLayout.LayoutParams chipP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); chipP.setMarginEnd(dpToPx(6)); chip.setLayoutParams(chipP); filterRow.addView(chip); } parent.addView(filterRow); // Divider View div = new View(this); LinearLayout.LayoutParams divP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(1)); div.setLayoutParams(divP); div.setBackgroundColor(Color.parseColor("#1AFFFFFF")); parent.addView(div); // Scrollable project list ScrollView scroll = new ScrollView(this); scroll.setLayoutParams(new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f)); scroll.setOverScrollMode(View.OVER_SCROLL_NEVER); projectListContainer = new LinearLayout(this); projectListContainer.setOrientation(LinearLayout.VERTICAL); projectListContainer.setPadding(dpToPx(16), dpToPx(10), dpToPx(16), dpToPx(16)); refreshProjectList(); scroll.addView(projectListContainer); parent.addView(scroll); } private void refreshProjectList() { projectListContainer.removeAllViews(); if (projects.isEmpty()) { TextView empty = new TextView(this); empty.setText("No projects yet.\nTap + NEW to create one."); empty.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12); empty.setTextColor(Color.parseColor("#4DFFFFFF")); empty.setTypeface(monoFont); empty.setGravity(Gravity.CENTER); empty.setPadding(0, dpToPx(40), 0, 0); projectListContainer.addView(empty); return; } for (int i = 0; i < projects.size(); i++) { projectListContainer.addView(buildProjectCard(i)); } // Update count View root = findViewById(R.id.app_root); if (root != null) { TextView countView = root.findViewWithTag("project_count"); if (countView != null) { countView.setText(projects.size() + " projects"); } } } private View buildProjectCard(int index) { ProjectItem project = projects.get(index); int statusColor = Color.parseColor(STATUS_COLORS[project.statusIndex]); LinearLayout card = new LinearLayout(this); card.setOrientation(LinearLayout.HORIZONTAL); card.setGravity(Gravity.CENTER_VERTICAL); LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); cardParams.bottomMargin = dpToPx(8); card.setLayoutParams(cardParams); card.setPadding(dpToPx(16), dpToPx(14), dpToPx(12), dpToPx(14)); GradientDrawable bg = new GradientDrawable(); bg.setCornerRadius(dpToPx(12)); bg.setColor(Color.parseColor("#0D0F172A")); bg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF")); card.setBackground(bg); // Status dot — tap to cycle status View dot = new View(this); int dotSize = dpToPx(10); LinearLayout.LayoutParams dotParams = new LinearLayout.LayoutParams(dotSize, dotSize); dotParams.setMarginEnd(dpToPx(12)); dot.setLayoutParams(dotParams); GradientDrawable dotBg = new GradientDrawable(); dotBg.setShape(GradientDrawable.OVAL); dotBg.setColor(statusColor); dot.setBackground(dotBg); dot.setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); project.statusIndex = (project.statusIndex + 1) % STATUSES.length; saveProjects(); // Rebuild entire UI to update filter counts LinearLayout content = (LinearLayout) projectListContainer.getParent().getParent(); content.removeAllViews(); buildProjectsUI(content); AeThexToast.show(this, project.name + " → " + STATUSES[project.statusIndex], AeThexToast.Type.INFO); }); card.addView(dot); // Info column LinearLayout info = new LinearLayout(this); info.setOrientation(LinearLayout.VERTICAL); info.setLayoutParams(new LinearLayout.LayoutParams( 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)); TextView name = new TextView(this); name.setText(project.name); name.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13); name.setTextColor(Color.parseColor("#CCFFFFFF")); name.setTypeface(displayFont); info.addView(name); if (project.description != null && !project.description.isEmpty()) { TextView desc = new TextView(this); desc.setText(project.description); desc.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); desc.setTextColor(Color.parseColor("#4DFFFFFF")); desc.setTypeface(monoFont); desc.setMaxLines(1); LinearLayout.LayoutParams dP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); dP.topMargin = dpToPx(2); desc.setLayoutParams(dP); info.addView(desc); } TextView status = new TextView(this); status.setText(STATUSES[project.statusIndex]); status.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); status.setTextColor(statusColor); status.setTypeface(monoFont); LinearLayout.LayoutParams sParams = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); sParams.topMargin = dpToPx(4); status.setLayoutParams(sParams); info.addView(status); card.addView(info); // Edit button TextView editBtn = new TextView(this); editBtn.setText("✎"); editBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); editBtn.setTextColor(Color.parseColor("#66FFFFFF")); editBtn.setPadding(dpToPx(8), dpToPx(4), dpToPx(8), dpToPx(4)); editBtn.setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); showEditProjectOverlay(index); }); card.addView(editBtn); // Delete button TextView delBtn = new TextView(this); delBtn.setText("✕"); delBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); delBtn.setTextColor(Color.parseColor("#EF4444")); delBtn.setPadding(dpToPx(8), dpToPx(4), dpToPx(8), dpToPx(4)); delBtn.setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); showDeleteConfirmation(index); }); card.addView(delBtn); // Tap card for detail toast card.setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); String detail = project.name + "\n" + STATUSES[project.statusIndex]; if (project.description != null && !project.description.isEmpty()) { detail += "\n" + project.description; } AeThexToast.show(this, detail, AeThexToast.Type.INFO); }); return card; } private void showCreateProjectOverlay() { showProjectFormOverlay(-1, "", "", 0); } private void showEditProjectOverlay(int index) { ProjectItem p = projects.get(index); showProjectFormOverlay(index, p.name, p.description, p.statusIndex); } private void showProjectFormOverlay(int editIndex, String currentName, String currentDesc, int currentStatus) { boolean isEdit = editIndex >= 0; int primaryColor = themeManager.getPrimaryColor(this); FrameLayout root = findViewById(R.id.app_root); FrameLayout scrim = new FrameLayout(this); scrim.setTag("project_form_overlay"); scrim.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); scrim.setBackgroundColor(Color.parseColor("#CC000000")); LinearLayout panel = new LinearLayout(this); panel.setOrientation(LinearLayout.VERTICAL); FrameLayout.LayoutParams pp = new FrameLayout.LayoutParams( dpToPx(300), ViewGroup.LayoutParams.WRAP_CONTENT); pp.gravity = Gravity.CENTER; panel.setLayoutParams(pp); panel.setPadding(dpToPx(20), dpToPx(20), dpToPx(20), dpToPx(20)); GradientDrawable panelBg = new GradientDrawable(); panelBg.setCornerRadius(dpToPx(16)); panelBg.setColor(Color.parseColor("#E6111827")); panelBg.setStroke(dpToPx(1), Color.parseColor("#33FFFFFF")); panel.setBackground(panelBg); // Title TextView title = new TextView(this); title.setText(isEdit ? "EDIT PROJECT" : "NEW PROJECT"); title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11); title.setTextColor(Color.parseColor("#66FFFFFF")); title.setTypeface(monoFont); title.setLetterSpacing(0.15f); LinearLayout.LayoutParams tP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); tP.bottomMargin = dpToPx(16); title.setLayoutParams(tP); panel.addView(title); // Name label TextView nameLabel = new TextView(this); nameLabel.setText("PROJECT NAME"); nameLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9); nameLabel.setTextColor(Color.parseColor("#66FFFFFF")); nameLabel.setTypeface(monoFont); panel.addView(nameLabel); // Name input EditText nameInput = new EditText(this); nameInput.setText(currentName); nameInput.setTextColor(Color.WHITE); nameInput.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); nameInput.setTypeface(monoFont); nameInput.setSingleLine(true); nameInput.setHint("Enter project name"); nameInput.setHintTextColor(Color.parseColor("#33FFFFFF")); nameInput.setPadding(dpToPx(12), dpToPx(10), dpToPx(12), dpToPx(10)); nameInput.setShowSoftInputOnFocus(false); GradientDrawable niBg = new GradientDrawable(); niBg.setCornerRadius(dpToPx(8)); niBg.setColor(Color.parseColor("#0DFFFFFF")); niBg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF")); nameInput.setBackground(niBg); LinearLayout.LayoutParams niP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); niP.topMargin = dpToPx(4); niP.bottomMargin = dpToPx(14); nameInput.setLayoutParams(niP); nameInput.setOnFocusChangeListener((v, hasFocus) -> { if (hasFocus) AeThexKeyboard.attachToEditText(this, nameInput); }); panel.addView(nameInput); // Description label TextView descLabel = new TextView(this); descLabel.setText("DESCRIPTION"); descLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9); descLabel.setTextColor(Color.parseColor("#66FFFFFF")); descLabel.setTypeface(monoFont); panel.addView(descLabel); // Description input EditText descInput = new EditText(this); descInput.setText(currentDesc); descInput.setTextColor(Color.WHITE); descInput.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12); descInput.setTypeface(monoFont); descInput.setMaxLines(3); descInput.setHint("Optional description"); descInput.setHintTextColor(Color.parseColor("#33FFFFFF")); descInput.setPadding(dpToPx(12), dpToPx(10), dpToPx(12), dpToPx(10)); descInput.setShowSoftInputOnFocus(false); GradientDrawable diBg = new GradientDrawable(); diBg.setCornerRadius(dpToPx(8)); diBg.setColor(Color.parseColor("#0DFFFFFF")); diBg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF")); descInput.setBackground(diBg); LinearLayout.LayoutParams diP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); diP.topMargin = dpToPx(4); diP.bottomMargin = dpToPx(14); descInput.setLayoutParams(diP); descInput.setOnFocusChangeListener((v, hasFocus) -> { if (hasFocus) AeThexKeyboard.attachToEditText(this, descInput); }); panel.addView(descInput); // Status selector (only for edit) final int[] selectedStatus = {currentStatus}; if (isEdit) { TextView statusLabel = new TextView(this); statusLabel.setText("STATUS"); statusLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9); statusLabel.setTextColor(Color.parseColor("#66FFFFFF")); statusLabel.setTypeface(monoFont); panel.addView(statusLabel); LinearLayout statusRow = new LinearLayout(this); statusRow.setOrientation(LinearLayout.HORIZONTAL); LinearLayout.LayoutParams srP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); srP.topMargin = dpToPx(6); srP.bottomMargin = dpToPx(16); statusRow.setLayoutParams(srP); TextView[] statusChips = new TextView[STATUSES.length]; for (int i = 0; i < STATUSES.length; i++) { final int idx = i; statusChips[i] = new TextView(this); statusChips[i].setText(STATUSES[i]); statusChips[i].setTextSize(TypedValue.COMPLEX_UNIT_SP, 9); statusChips[i].setTypeface(monoFont); statusChips[i].setPadding(dpToPx(8), dpToPx(6), dpToPx(8), dpToPx(6)); statusChips[i].setGravity(Gravity.CENTER); LinearLayout.LayoutParams scP = new LinearLayout.LayoutParams( 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f); scP.setMarginEnd(dpToPx(4)); statusChips[i].setLayoutParams(scP); updateStatusChipStyle(statusChips[i], idx, selectedStatus[0] == idx); statusChips[i].setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); selectedStatus[0] = idx; for (int j = 0; j < statusChips.length; j++) { updateStatusChipStyle(statusChips[j], j, j == idx); } }); statusRow.addView(statusChips[i]); } panel.addView(statusRow); } // Button row LinearLayout btnRow = new LinearLayout(this); btnRow.setOrientation(LinearLayout.HORIZONTAL); btnRow.setGravity(Gravity.END); // Cancel TextView cancelBtn = new TextView(this); cancelBtn.setText("CANCEL"); cancelBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11); cancelBtn.setTextColor(Color.parseColor("#66FFFFFF")); cancelBtn.setTypeface(monoFont); cancelBtn.setPadding(dpToPx(16), dpToPx(10), dpToPx(16), dpToPx(10)); cancelBtn.setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); AeThexKeyboard.dismissKeyboard(this); dismissOverlay("project_form_overlay"); }); btnRow.addView(cancelBtn); // Save TextView saveBtn = new TextView(this); saveBtn.setText(isEdit ? "SAVE" : "CREATE"); saveBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11); saveBtn.setTextColor(Color.WHITE); saveBtn.setTypeface(monoFont, Typeface.BOLD); saveBtn.setGravity(Gravity.CENTER); saveBtn.setPadding(dpToPx(20), dpToPx(10), dpToPx(20), dpToPx(10)); GradientDrawable saveBg = new GradientDrawable(); saveBg.setCornerRadius(dpToPx(8)); saveBg.setColor(primaryColor); saveBtn.setBackground(saveBg); LinearLayout.LayoutParams svP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); svP.setMarginStart(dpToPx(8)); saveBtn.setLayoutParams(svP); saveBtn.setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); String name = nameInput.getText().toString().trim(); if (name.isEmpty()) { AeThexToast.show(this, "Project name required", AeThexToast.Type.INFO); return; } String desc = descInput.getText().toString().trim(); if (isEdit) { ProjectItem p = projects.get(editIndex); p.name = name; p.description = desc; p.statusIndex = selectedStatus[0]; } else { projects.add(new ProjectItem(name, desc, 0)); } saveProjects(); AeThexKeyboard.dismissKeyboard(this); dismissOverlay("project_form_overlay"); // Rebuild UI LinearLayout content = (LinearLayout) projectListContainer.getParent().getParent(); content.removeAllViews(); buildProjectsUI(content); AeThexToast.show(this, isEdit ? "Project updated" : "Project created", AeThexToast.Type.SUCCESS); }); btnRow.addView(saveBtn); panel.addView(btnRow); scrim.addView(panel); scrim.setAlpha(0f); root.addView(scrim); scrim.animate().alpha(1f).setDuration(200).start(); nameInput.requestFocus(); nameInput.postDelayed(() -> AeThexKeyboard.attachToEditText(this, nameInput), 200); } private void updateStatusChipStyle(TextView chip, int statusIdx, boolean selected) { int color = Color.parseColor(STATUS_COLORS[statusIdx]); GradientDrawable bg = new GradientDrawable(); bg.setCornerRadius(dpToPx(6)); if (selected) { bg.setColor(Color.argb(40, Color.red(color), Color.green(color), Color.blue(color))); bg.setStroke(dpToPx(1), color); chip.setTextColor(color); } else { bg.setColor(Color.parseColor("#0DFFFFFF")); bg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF")); chip.setTextColor(Color.parseColor("#4DFFFFFF")); } chip.setBackground(bg); } private void showDeleteConfirmation(int index) { int primaryColor = themeManager.getPrimaryColor(this); FrameLayout root = findViewById(R.id.app_root); FrameLayout scrim = new FrameLayout(this); scrim.setTag("project_delete_overlay"); scrim.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); scrim.setBackgroundColor(Color.parseColor("#CC000000")); LinearLayout panel = new LinearLayout(this); panel.setOrientation(LinearLayout.VERTICAL); panel.setGravity(Gravity.CENTER); FrameLayout.LayoutParams pp = new FrameLayout.LayoutParams( dpToPx(260), ViewGroup.LayoutParams.WRAP_CONTENT); pp.gravity = Gravity.CENTER; panel.setLayoutParams(pp); panel.setPadding(dpToPx(20), dpToPx(20), dpToPx(20), dpToPx(20)); GradientDrawable panelBg = new GradientDrawable(); panelBg.setCornerRadius(dpToPx(16)); panelBg.setColor(Color.parseColor("#E6111827")); panelBg.setStroke(dpToPx(1), Color.parseColor("#33FFFFFF")); panel.setBackground(panelBg); // Warning icon TextView icon = new TextView(this); icon.setText("⚠"); icon.setTextSize(TypedValue.COMPLEX_UNIT_SP, 28); icon.setGravity(Gravity.CENTER); panel.addView(icon); // Message TextView msg = new TextView(this); msg.setText("Delete \"" + projects.get(index).name + "\"?"); msg.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); msg.setTextColor(Color.WHITE); msg.setTypeface(displayFont); msg.setGravity(Gravity.CENTER); LinearLayout.LayoutParams mP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); mP.topMargin = dpToPx(10); mP.bottomMargin = dpToPx(6); msg.setLayoutParams(mP); panel.addView(msg); TextView sub = new TextView(this); sub.setText("This action cannot be undone."); sub.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); sub.setTextColor(Color.parseColor("#66FFFFFF")); sub.setTypeface(monoFont); sub.setGravity(Gravity.CENTER); LinearLayout.LayoutParams sP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); sP.bottomMargin = dpToPx(16); sub.setLayoutParams(sP); panel.addView(sub); // Button row LinearLayout btnRow = new LinearLayout(this); btnRow.setOrientation(LinearLayout.HORIZONTAL); btnRow.setGravity(Gravity.CENTER); TextView cancelBtn = new TextView(this); cancelBtn.setText("CANCEL"); cancelBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11); cancelBtn.setTextColor(Color.parseColor("#66FFFFFF")); cancelBtn.setTypeface(monoFont); cancelBtn.setPadding(dpToPx(20), dpToPx(10), dpToPx(20), dpToPx(10)); cancelBtn.setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); dismissOverlay("project_delete_overlay"); }); btnRow.addView(cancelBtn); TextView delBtn = new TextView(this); delBtn.setText("DELETE"); delBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11); delBtn.setTextColor(Color.WHITE); delBtn.setTypeface(monoFont, Typeface.BOLD); delBtn.setGravity(Gravity.CENTER); delBtn.setPadding(dpToPx(20), dpToPx(10), dpToPx(20), dpToPx(10)); GradientDrawable delBg = new GradientDrawable(); delBg.setCornerRadius(dpToPx(8)); delBg.setColor(Color.parseColor("#EF4444")); delBtn.setBackground(delBg); LinearLayout.LayoutParams dbP = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); dbP.setMarginStart(dpToPx(10)); delBtn.setLayoutParams(dbP); delBtn.setOnClickListener(v -> { SoundManager.getInstance().play(SoundManager.Sound.CLICK); String removedName = projects.get(index).name; projects.remove(index); saveProjects(); dismissOverlay("project_delete_overlay"); // Rebuild UI LinearLayout content = (LinearLayout) projectListContainer.getParent().getParent(); content.removeAllViews(); buildProjectsUI(content); AeThexToast.show(this, removedName + " deleted", AeThexToast.Type.INFO); }); btnRow.addView(delBtn); panel.addView(btnRow); scrim.addView(panel); scrim.setAlpha(0f); root.addView(scrim); scrim.animate().alpha(1f).setDuration(200).start(); } private void dismissOverlay(String tag) { FrameLayout root = findViewById(R.id.app_root); View overlay = root.findViewWithTag(tag); if (overlay != null) { overlay.animate().alpha(0f).setDuration(150).withEndAction(() -> { root.removeView(overlay); }).start(); } } // ── Persistence ── private void loadProjects() { projects.clear(); String json = prefs.getString(KEY_PROJECTS, null); if (json != null) { try { JSONArray arr = new JSONArray(json); for (int i = 0; i < arr.length(); i++) { JSONObject obj = arr.getJSONObject(i); projects.add(new ProjectItem( obj.getString("name"), obj.optString("desc", ""), obj.getInt("status") )); } } catch (Exception ignored) {} } // Seed default projects if none exist if (projects.isEmpty()) { projects.add(new ProjectItem("AeThex Mobile", "Android native client", 1)); projects.add(new ProjectItem("Security Audit", "Q1 security review", 2)); projects.add(new ProjectItem("Data Pipeline", "Streaming analytics pipeline", 3)); projects.add(new ProjectItem("UI Redesign", "Glass morphism theme system", 1)); projects.add(new ProjectItem("API Gateway", "REST & WebSocket endpoints", 0)); saveProjects(); } } private void saveProjects() { try { JSONArray arr = new JSONArray(); for (ProjectItem p : projects) { JSONObject obj = new JSONObject(); obj.put("name", p.name); obj.put("desc", p.description); obj.put("status", p.statusIndex); arr.put(obj); } prefs.edit().putString(KEY_PROJECTS, arr.toString()).apply(); } catch (Exception ignored) {} } @SuppressWarnings("deprecation") @Override public void onBackPressed() { FrameLayout root = findViewById(R.id.app_root); // Check for overlays View formOverlay = root.findViewWithTag("project_form_overlay"); if (formOverlay != null) { AeThexKeyboard.dismissKeyboard(this); dismissOverlay("project_form_overlay"); return; } View delOverlay = root.findViewWithTag("project_delete_overlay"); if (delOverlay != null) { dismissOverlay("project_delete_overlay"); return; } SoundManager.getInstance().play(SoundManager.Sound.CLOSE); super.onBackPressed(); overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out); } private int dpToPx(int dp) { return (int) (dp * getResources().getDisplayMetrics().density); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) hideSystemUI(); } private void hideSystemUI() { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { final WindowInsetsController c = getWindow().getInsetsController(); if (c != null) { c.hide(WindowInsets.Type.systemBars()); c.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); } } else { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN); } } }