AeThex-OS/android/app/src/main/java/com/aethex/os/ClearanceSwitchOverlay.java
MrPiglr b3c308b2c8 Add functional marketplace modules, bottom nav bar, root terminal, arcade games
- ModuleManager: Central tracking for installed marketplace modules
- DataAnalyzerWidget: Real-time CPU/RAM/Battery/Storage widget (unlocked by Data Analyzer module)
- BottomNavBar: Navigation bar for Projects/Chat/Marketplace/Settings
- RootShell: Real root command execution utility
- TerminalActivity: Full root shell with neofetch, sysinfo, real Linux commands
- Terminal Pro module: Adds aliases (ll, la, h), command history
- ArcadeActivity + SnakeGame: Pixel Arcade module unlocks retro games
- fade_in/fade_out animations for smooth transitions
2026-02-18 22:03:50 -07:00

244 lines
9.9 KiB
Java

package com.aethex.os;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.os.Handler;
import android.os.Looper;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.content.res.ResourcesCompat;
/**
* Fullscreen overlay animation for clearance switching.
* Shows a cinematic 5-step animation: fade-in → spinner → text → progress line → fade-out.
*/
public class ClearanceSwitchOverlay {
public interface Callback {
void onSwitchComplete();
}
private static final String OVERLAY_TAG = "clearance_switch_overlay";
/**
* Shows the clearance switch overlay animation.
*
* @param activity The current activity
* @param targetMode The clearance mode being switched to (ThemeManager.CLEARANCE_CORP or CLEARANCE_FOUNDATION)
* @param callback Called when animation completes and theme should be applied
*/
public static void show(Activity activity, String targetMode, Callback callback) {
if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
if (callback != null) callback.onSwitchComplete();
return;
}
activity.runOnUiThread(() -> {
// Remove any existing overlay
FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();
View existing = decorView.findViewWithTag(OVERLAY_TAG);
if (existing != null) {
decorView.removeView(existing);
}
// Build the overlay
FrameLayout overlay = createOverlay(activity, targetMode);
decorView.addView(overlay);
// Run the 5-step animation
runAnimation(activity, overlay, targetMode, callback);
});
}
private static FrameLayout createOverlay(Activity activity, String targetMode) {
int bgColor = ThemeManager.getSwitchOverlayBackground(targetMode);
int spinnerColor = ThemeManager.getSwitchSpinnerColor(targetMode);
String label = ThemeManager.getSwitchLabel(targetMode);
FrameLayout overlay = new FrameLayout(activity);
overlay.setTag(OVERLAY_TAG);
overlay.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
overlay.setBackgroundColor(bgColor);
overlay.setAlpha(0f);
overlay.setClickable(true); // Block touches below
// Center content container
LinearLayout center = new LinearLayout(activity);
center.setOrientation(LinearLayout.VERTICAL);
center.setGravity(Gravity.CENTER);
FrameLayout.LayoutParams centerParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
centerParams.gravity = Gravity.CENTER;
center.setLayoutParams(centerParams);
// Spinner ring (circular border)
View spinner = new View(activity);
int spinnerSize = dpToPx(activity, 48);
LinearLayout.LayoutParams spinnerParams = new LinearLayout.LayoutParams(spinnerSize, spinnerSize);
spinnerParams.gravity = Gravity.CENTER_HORIZONTAL;
spinner.setLayoutParams(spinnerParams);
GradientDrawable spinnerBg = new GradientDrawable();
spinnerBg.setShape(GradientDrawable.OVAL);
spinnerBg.setColor(Color.TRANSPARENT);
spinnerBg.setStroke(dpToPx(activity, 3), spinnerColor);
spinner.setBackground(spinnerBg);
spinner.setAlpha(0f);
spinner.setTag("switch_spinner");
center.addView(spinner);
// Label text
TextView labelView = new TextView(activity);
labelView.setText(label);
labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
labelView.setTextColor(Color.argb((int) (255 * 0.8f), 255, 255, 255));
labelView.setLetterSpacing(0.2f);
try {
Typeface font = ResourcesCompat.getFont(activity, R.font.source_code_pro);
if (font != null) labelView.setTypeface(font, Typeface.BOLD);
} catch (Exception ignored) {
labelView.setTypeface(Typeface.MONOSPACE, Typeface.BOLD);
}
LinearLayout.LayoutParams labelParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
labelParams.gravity = Gravity.CENTER_HORIZONTAL;
labelParams.topMargin = dpToPx(activity, 20);
labelView.setLayoutParams(labelParams);
labelView.setAlpha(0f);
labelView.setTag("switch_label");
center.addView(labelView);
// Sub-label (mode name)
TextView subLabel = new TextView(activity);
String modeName = ThemeManager.CLEARANCE_CORP.equals(targetMode)
? "CORP CLEARANCE" : "FOUNDATION CLEARANCE";
subLabel.setText(modeName);
subLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
subLabel.setTextColor(spinnerColor);
try {
Typeface font = ResourcesCompat.getFont(activity, R.font.source_code_pro);
if (font != null) subLabel.setTypeface(font);
} catch (Exception ignored) {
subLabel.setTypeface(Typeface.MONOSPACE);
}
LinearLayout.LayoutParams subParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
subParams.gravity = Gravity.CENTER_HORIZONTAL;
subParams.topMargin = dpToPx(activity, 6);
subLabel.setLayoutParams(subParams);
subLabel.setAlpha(0f);
subLabel.setTag("switch_sub_label");
center.addView(subLabel);
overlay.addView(center);
// Bottom progress line
View progressLine = new View(activity);
int lineHeight = dpToPx(activity, 2);
FrameLayout.LayoutParams lineParams = new FrameLayout.LayoutParams(0, lineHeight);
lineParams.gravity = Gravity.BOTTOM;
progressLine.setLayoutParams(lineParams);
progressLine.setBackgroundColor(spinnerColor);
progressLine.setTag("switch_progress");
overlay.addView(progressLine);
return overlay;
}
private static void runAnimation(Activity activity, FrameLayout overlay,
String targetMode, Callback callback) {
Handler handler = new Handler(Looper.getMainLooper());
int spinnerColor = ThemeManager.getSwitchSpinnerColor(targetMode);
View spinner = overlay.findViewWithTag("switch_spinner");
View label = overlay.findViewWithTag("switch_label");
View subLabel = overlay.findViewWithTag("switch_sub_label");
View progress = overlay.findViewWithTag("switch_progress");
// Play switch sound
SoundManager.getInstance().play(SoundManager.Sound.SWITCH);
// Step 1: Fade in overlay (200ms)
overlay.animate().alpha(1f).setDuration(200).withEndAction(() -> {
// Step 2: Show spinner + spin (400ms)
spinner.setAlpha(1f);
spinner.setScaleX(0.5f);
spinner.setScaleY(0.5f);
spinner.animate().scaleX(1f).scaleY(1f).setDuration(300)
.setInterpolator(new AccelerateDecelerateInterpolator()).start();
ObjectAnimator rotation = ObjectAnimator.ofFloat(spinner, "rotation", 0f, 360f);
rotation.setDuration(800);
rotation.setRepeatCount(1);
rotation.setInterpolator(new LinearInterpolator());
rotation.start();
handler.postDelayed(() -> {
// Step 3: Show text (300ms)
label.animate().alpha(1f).setDuration(200).start();
subLabel.animate().alpha(1f).setDuration(200).setStartDelay(100).start();
handler.postDelayed(() -> {
// Step 4: Progress line sweeps across (600ms)
int screenWidth = activity.getWindow().getDecorView().getWidth();
ValueAnimator lineAnim = ValueAnimator.ofInt(0, screenWidth);
lineAnim.setDuration(600);
lineAnim.setInterpolator(new AccelerateDecelerateInterpolator());
lineAnim.addUpdateListener(anim -> {
int val = (int) anim.getAnimatedValue();
ViewGroup.LayoutParams lp = progress.getLayoutParams();
lp.width = val;
progress.setLayoutParams(lp);
});
lineAnim.start();
handler.postDelayed(() -> {
// Step 5: Fade out (300ms)
overlay.animate().alpha(0f).setDuration(300).withEndAction(() -> {
// Remove overlay
FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();
decorView.removeView(overlay);
// Invoke callback
if (callback != null) {
callback.onSwitchComplete();
}
}).start();
}, 650);
}, 350);
}, 500);
}).start();
}
private static int dpToPx(Activity activity, float dp) {
return Math.round(TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp,
activity.getResources().getDisplayMetrics()));
}
}