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
This commit is contained in:
MrPiglr 2026-02-18 22:03:50 -07:00
parent b04b8f8dca
commit b3c308b2c8
1842 changed files with 328426 additions and 717 deletions

View file

@ -0,0 +1,36 @@
{
"permissions": {
"allow": [
"Bash(curl:*)",
"Bash(python3:*)",
"Bash(gradlew.bat assembleDebug:*)",
"Bash(cmd /c \"gradlew.bat assembleDebug\")",
"Bash(cmd.exe /c \"gradlew.bat assembleDebug 2>&1\")",
"Bash(./gradlew assembleDebug:*)",
"Bash(.\\\\gradlew assembleDebug:*)",
"Bash(.\\\\gradlew.bat assembleDebug:*)",
"Bash(cmd /c:*)",
"Bash(powershell -Command:*)",
"Bash(Select-String -Pattern \"HOME\" -Context 1,3)",
"Bash(Select-String -Pattern \"HOME\" -Context 0,5)",
"Bash(powershell -ExecutionPolicy Bypass -File:*)",
"Bash(node:*)",
"Bash(ren:*)",
"Bash(adb push:*)",
"WebFetch(domain:xdaforums.com)",
"WebFetch(domain:topjohnwu.github.io)",
"WebFetch(domain:www.needrom.com)",
"WebFetch(domain:firmwaredrive.com)",
"WebFetch(domain:phonefirmware.com)",
"Bash(adb devices:*)",
"Bash(adb shell getprop:*)",
"Bash(adb shell:*)",
"Bash(where:*)",
"Bash(C:\\\\Users\\\\PCOEM\\\\platform-tools\\\\fastboot.exe reboot:*)",
"Bash(adb kill-server:*)",
"Bash(adb start-server:*)",
"Bash(powershell:*)",
"Bash(adb connect:*)"
]
}
}

@ -0,0 +1 @@
Subproject commit b04b8f8dca4364d3359e405fdda40cc5d453f564

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>

View file

@ -1,20 +1,15 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
namespace = "com.aethex.os" namespace "com.aethex.os"
compileSdk = rootProject.ext.compileSdkVersion compileSdk 34
defaultConfig { defaultConfig {
applicationId "com.aethex.os" applicationId "com.aethex.os"
minSdkVersion rootProject.ext.minSdkVersion minSdk 24
targetSdkVersion rootProject.ext.targetSdkVersion targetSdk 34
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
// Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
ignoreAssetsPattern = '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
} }
buildTypes { buildTypes {
release { release {
@ -24,31 +19,12 @@ android {
} }
} }
repositories {
flatDir{
dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
}
}
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.6.1'
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" implementation 'com.google.android.material:material:1.9.0'
implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation project(':capacitor-android') testImplementation 'junit:junit:4.13.2'
testImplementation "junit:junit:$junitVersion" androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
}
apply from: 'capacitor.build.gradle'
try {
def servicesJSON = file('google-services.json')
if (servicesJSON.text) {
apply plugin: 'com.google.gms.google-services'
}
} catch(Exception e) {
logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
} }

View file

@ -11,20 +11,155 @@
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<activity <activity
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode|navigation|density"
android:name=".MainActivity" android:name=".MainActivity"
android:label="@string/title_activity_main" android:exported="true"
android:theme="@style/AppTheme.NoActionBarLaunch"
android:launchMode="singleTask" android:launchMode="singleTask"
android:exported="true"> android:screenOrientation="portrait"
android:stateNotNeeded="true"
android:excludeFromRecents="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<!-- Register as a home launcher so user can set AeThexOS as default -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity> </activity>
<activity
android:name=".SystemActivity"
android:label="AeThex System"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:excludeFromRecents="true" />
<activity
android:name=".AppActivity"
android:label="App"
android:screenOrientation="portrait" />
<activity
android:name=".SettingsActivity"
android:label="Settings"
android:screenOrientation="portrait" />
<activity
android:name=".CalculatorActivity"
android:label="Calculator"
android:screenOrientation="portrait" />
<activity
android:name=".TerminalActivity"
android:label="Terminal"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".ClockActivity"
android:label="Clock"
android:screenOrientation="portrait" />
<activity
android:name=".SnakeActivity"
android:label="Snake"
android:screenOrientation="portrait" />
<activity
android:name=".NotesActivity"
android:label="Notes"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".WeatherActivity"
android:label="Weather"
android:screenOrientation="portrait" />
<activity
android:name=".MinesweeperActivity"
android:label="Minesweeper"
android:screenOrientation="portrait" />
<activity
android:name=".FileManagerActivity"
android:label="File Manager"
android:screenOrientation="portrait" />
<activity
android:name=".MusicActivity"
android:label="Music"
android:screenOrientation="portrait" />
<activity
android:name=".ChatActivity"
android:label="Messages"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".PhotosActivity"
android:label="Photos"
android:screenOrientation="portrait" />
<activity
android:name=".BrowserActivity"
android:label="Browser"
android:screenOrientation="portrait" />
<activity
android:name=".PassportActivity"
android:label="Passport"
android:screenOrientation="portrait" />
<activity
android:name=".ProjectsActivity"
android:label="Projects"
android:screenOrientation="portrait" />
<activity
android:name=".MarketplaceActivity"
android:label="Marketplace"
android:screenOrientation="portrait" />
<activity
android:name=".ArcadeActivity"
android:label="Arcade"
android:screenOrientation="portrait" />
<activity
android:name=".AnalyticsActivity"
android:label="Analytics"
android:screenOrientation="portrait" />
<activity
android:name=".MailActivity"
android:label="Mail"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".CameraActivity"
android:label="Camera"
android:screenOrientation="portrait" />
<activity
android:name=".AchievementsActivity"
android:label="Achievements"
android:screenOrientation="portrait" />
<!-- Notification Listener for real Android notifications -->
<service
android:name=".AeThexNotificationService"
android:exported="true"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider" android:authorities="${applicationId}.fileprovider"
@ -32,18 +167,21 @@
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data <meta-data
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"></meta-data> android:resource="@xml/file_paths" />
</provider> </provider>
</application> </application>
<!-- Permissions --> <!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-feature android:name="android.hardware.camera" android:required="false" /> <uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.front" android:required="false" /> <uses-feature android:name="android.hardware.camera.front" android:required="false" />
</manifest> </manifest>

View file

@ -0,0 +1,346 @@
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.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class AchievementsActivity extends AppCompatActivity {
private ThemeManager themeManager;
private Typeface monoFont;
private Typeface displayFont;
private SharedPreferences prefs;
private static final String PREFS_NAME = "aethex_achievements";
// {id, icon, title, description, condition_key}
private static final String[][] ACHIEVEMENTS = {
{"first_boot", "", "First Boot", "Boot into AeThex OS for the first time", "true"},
{"explorer", "📁", "Explorer", "Open the File Manager", "files"},
{"codebreaker", "💻", "Codebreaker", "Execute 5 commands in Terminal", "terminal_cmds"},
{"snake_master", "🐍", "Snake Master", "Score 50+ points in Snake", "snake_score"},
{"minesweeper_win", "💣", "Bomb Defuser", "Win a game of Minesweeper", "minesweeper_win"},
{"note_taker", "📝", "Note Taker", "Create 3 notes in the Notes app", "notes_count"},
{"clearance_swap", "🔄", "Identity Crisis", "Switch clearance mode 3 times", "clearance_swaps"},
{"music_lover", "🎵", "Music Lover", "Play 5 tracks in the Radio app", "tracks_played"},
{"calculator_pro", "🔢", "Number Cruncher", "Perform 20 calculations", "calc_ops"},
{"photographer", "📷", "Photographer", "Take 5 photos in the Camera app", "photos_taken"},
{"messenger", "💬", "Social Butterfly", "Send 10 messages in Chat", "messages_sent"},
{"weatherman", "🌤", "Weatherman", "Check the weather 5 times", "weather_checks"},
{"time_keeper", "", "Time Keeper", "Use the stopwatch for 60 seconds", "stopwatch_time"},
{"browser_surfer", "🌐", "Web Surfer", "Visit 5 URLs in the Browser", "urls_visited"},
{"konami", "🎮", "Konami Master", "Enter the Konami code in Settings", "konami_unlocked"},
{"marathon", "🏃", "Marathon", "Keep AeThex OS running for 30 minutes", "uptime_30"},
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app);
hideSystemUI();
themeManager = new ThemeManager(this);
monoFont = themeManager.getMonoFont(this);
displayFont = themeManager.getDisplayFont(this);
prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
// Mark first boot achievement
if (!prefs.getBoolean("first_boot", false)) {
prefs.edit().putBoolean("first_boot", true).apply();
}
TextView title = findViewById(R.id.app_title);
title.setText("Achievements");
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);
buildAchievementsUI(content);
View root = findViewById(R.id.app_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
}
private void buildAchievementsUI(LinearLayout parent) {
int primaryColor = themeManager.getPrimaryColor(this);
// Stats header
LinearLayout statsBar = new LinearLayout(this);
statsBar.setOrientation(LinearLayout.HORIZONTAL);
statsBar.setGravity(Gravity.CENTER_VERTICAL);
statsBar.setPadding(dpToPx(16), dpToPx(14), dpToPx(16), dpToPx(14));
int unlockedCount = countUnlocked();
TextView statsLabel = new TextView(this);
statsLabel.setText("ACHIEVEMENTS");
statsLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
statsLabel.setTextColor(Color.parseColor("#66FFFFFF"));
statsLabel.setTypeface(monoFont);
statsLabel.setLetterSpacing(0.15f);
statsBar.addView(statsLabel);
View sp = new View(this);
sp.setLayoutParams(new LinearLayout.LayoutParams(0, 1, 1f));
statsBar.addView(sp);
// Progress text
TextView progress = new TextView(this);
progress.setText(unlockedCount + " / " + ACHIEVEMENTS.length);
progress.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
progress.setTextColor(primaryColor);
progress.setTypeface(displayFont);
statsBar.addView(progress);
parent.addView(statsBar);
// Progress bar
FrameLayout progressBarFrame = new FrameLayout(this);
LinearLayout.LayoutParams pbfP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(4));
pbfP.setMarginStart(dpToPx(16));
pbfP.setMarginEnd(dpToPx(16));
pbfP.bottomMargin = dpToPx(4);
progressBarFrame.setLayoutParams(pbfP);
// Track
View track = new View(this);
track.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
GradientDrawable trackBg = new GradientDrawable();
trackBg.setCornerRadius(dpToPx(2));
trackBg.setColor(Color.parseColor("#1AFFFFFF"));
track.setBackground(trackBg);
progressBarFrame.addView(track);
// Fill
float pct = (float) unlockedCount / ACHIEVEMENTS.length;
View fill = new View(this);
fill.setLayoutParams(new FrameLayout.LayoutParams(
0, ViewGroup.LayoutParams.MATCH_PARENT));
GradientDrawable fillBg = new GradientDrawable();
fillBg.setCornerRadius(dpToPx(2));
fillBg.setColor(primaryColor);
fill.setBackground(fillBg);
progressBarFrame.addView(fill);
// Measure after layout
progressBarFrame.post(() -> {
int totalWidth = progressBarFrame.getWidth();
int fillWidth = (int) (totalWidth * pct);
fill.getLayoutParams().width = fillWidth;
fill.requestLayout();
});
parent.addView(progressBarFrame);
// Divider
View div = new View(this);
LinearLayout.LayoutParams divP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(1));
divP.topMargin = dpToPx(8);
div.setLayoutParams(divP);
div.setBackgroundColor(Color.parseColor("#1AFFFFFF"));
parent.addView(div);
// Achievement list
ScrollView scroll = new ScrollView(this);
scroll.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
scroll.setOverScrollMode(View.OVER_SCROLL_NEVER);
LinearLayout list = new LinearLayout(this);
list.setOrientation(LinearLayout.VERTICAL);
list.setPadding(dpToPx(12), dpToPx(8), dpToPx(12), dpToPx(12));
for (String[] ach : ACHIEVEMENTS) {
boolean unlocked = isUnlocked(ach[0]);
list.addView(buildAchievementCard(ach, unlocked, primaryColor));
}
scroll.addView(list);
parent.addView(scroll);
}
private View buildAchievementCard(String[] ach, boolean unlocked, int primaryColor) {
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.HORIZONTAL);
card.setGravity(Gravity.CENTER_VERTICAL);
card.setPadding(dpToPx(14), dpToPx(12), dpToPx(14), dpToPx(12));
LinearLayout.LayoutParams cardP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
cardP.bottomMargin = dpToPx(6);
card.setLayoutParams(cardP);
GradientDrawable cardBg = new GradientDrawable();
cardBg.setCornerRadius(dpToPx(10));
if (unlocked) {
cardBg.setColor(Color.argb(26, Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor)));
cardBg.setStroke(dpToPx(1), Color.argb(51, Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor)));
} else {
cardBg.setColor(Color.parseColor("#0DFFFFFF"));
cardBg.setStroke(dpToPx(1), Color.parseColor("#0DFFFFFF"));
}
card.setBackground(cardBg);
// Icon
TextView icon = new TextView(this);
icon.setText(ach[1]);
icon.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
icon.setAlpha(unlocked ? 1f : 0.3f);
icon.setGravity(Gravity.CENTER);
icon.setLayoutParams(new LinearLayout.LayoutParams(dpToPx(40), dpToPx(40)));
card.addView(icon);
// Text column
LinearLayout textCol = new LinearLayout(this);
textCol.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams tcP = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
tcP.setMarginStart(dpToPx(12));
textCol.setLayoutParams(tcP);
TextView titleTv = new TextView(this);
titleTv.setText(ach[2]);
titleTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
titleTv.setTextColor(unlocked ? Color.WHITE : Color.parseColor("#66FFFFFF"));
titleTv.setTypeface(displayFont);
textCol.addView(titleTv);
TextView descTv = new TextView(this);
descTv.setText(ach[3]);
descTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
descTv.setTextColor(unlocked ? Color.parseColor("#99FFFFFF") : Color.parseColor("#33FFFFFF"));
descTv.setTypeface(monoFont);
LinearLayout.LayoutParams dP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
dP.topMargin = dpToPx(2);
descTv.setLayoutParams(dP);
textCol.addView(descTv);
card.addView(textCol);
// Status indicator
if (unlocked) {
TextView check = new TextView(this);
check.setText("");
check.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
check.setTextColor(primaryColor);
check.setTypeface(monoFont, Typeface.BOLD);
card.addView(check);
} else {
// Locked icon
TextView lock = new TextView(this);
lock.setText("🔒");
lock.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
lock.setAlpha(0.3f);
card.addView(lock);
}
return card;
}
private boolean isUnlocked(String id) {
switch (id) {
case "first_boot":
return true; // Always unlocked
case "konami":
return new ThemeManager(this).isKonamiUnlocked();
case "clearance_swap":
return prefs.getInt("clearance_swaps", 0) >= 3;
case "codebreaker":
return prefs.getInt("terminal_cmds", 0) >= 5;
case "snake_master":
return prefs.getInt("snake_score", 0) >= 50;
case "minesweeper_win":
return prefs.getBoolean("minesweeper_win", false);
case "note_taker":
return prefs.getInt("notes_count", 0) >= 3;
case "music_lover":
return prefs.getInt("tracks_played", 0) >= 5;
case "calculator_pro":
return prefs.getInt("calc_ops", 0) >= 20;
case "photographer":
return prefs.getInt("photos_taken", 0) >= 5;
case "messenger":
return prefs.getInt("messages_sent", 0) >= 10;
case "weatherman":
return prefs.getInt("weather_checks", 0) >= 5;
case "time_keeper":
return prefs.getInt("stopwatch_time", 0) >= 60;
case "browser_surfer":
return prefs.getInt("urls_visited", 0) >= 5;
case "explorer":
return prefs.getBoolean("files_opened", false);
case "marathon":
return prefs.getBoolean("uptime_30", false);
default:
return prefs.getBoolean(id, false);
}
}
private int countUnlocked() {
int count = 0;
for (String[] ach : ACHIEVEMENTS) {
if (isUnlocked(ach[0])) count++;
}
return count;
}
public static void incrementStat(android.content.Context ctx, String key) {
SharedPreferences p = ctx.getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
p.edit().putInt(key, p.getInt(key, 0) + 1).apply();
}
public static void setBoolStat(android.content.Context ctx, String key) {
SharedPreferences p = ctx.getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
p.edit().putBoolean(key, true).apply();
}
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);
}
}
}

View file

@ -0,0 +1,191 @@
package com.aethex.os;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.content.res.ResourcesCompat;
/**
* Context menu overlay for long-press on app icons and desktop.
* Shows a floating menu with options like "Open", "Info", etc.
*/
public class AeThexContextMenu {
public interface MenuAction {
void onAction(String actionId);
}
private static final String OVERLAY_TAG = "aethex_context_menu";
public static class MenuItem {
public final String id;
public final String label;
public final String iconChar; // emoji or unicode
public MenuItem(String id, String label, String iconChar) {
this.id = id;
this.label = label;
this.iconChar = iconChar;
}
}
/**
* Shows a context menu at the given coordinates.
*/
public static void show(Activity activity, float x, float y,
String title, MenuItem[] items, MenuAction action) {
if (activity == null || activity.isFinishing() || activity.isDestroyed()) return;
dismiss(activity); // Remove any existing
FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();
// Scrim (semi-transparent background that dismisses on tap)
FrameLayout scrim = new FrameLayout(activity);
scrim.setTag(OVERLAY_TAG);
scrim.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
scrim.setBackgroundColor(Color.parseColor("#40000000"));
scrim.setOnClickListener(v -> dismiss(activity));
// Menu card
LinearLayout menu = new LinearLayout(activity);
menu.setOrientation(LinearLayout.VERTICAL);
int menuWidth = dpToPx(activity, 180);
FrameLayout.LayoutParams menuParams = new FrameLayout.LayoutParams(
menuWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
// Position the menu near the tap point, clamping to screen
int screenW = decorView.getWidth();
int screenH = decorView.getHeight();
int menuX = (int) Math.min(x, screenW - menuWidth - dpToPx(activity, 16));
int menuY = (int) Math.min(y, screenH - dpToPx(activity, 200));
menuX = Math.max(menuX, dpToPx(activity, 8));
menuY = Math.max(menuY, dpToPx(activity, 8));
menuParams.leftMargin = menuX;
menuParams.topMargin = menuY;
menu.setLayoutParams(menuParams);
GradientDrawable menuBg = new GradientDrawable();
menuBg.setCornerRadius(dpToPx(activity, 12));
menuBg.setColor(Color.parseColor("#E6111827"));
menuBg.setStroke(dpToPx(activity, 1), Color.parseColor("#33FFFFFF"));
menu.setBackground(menuBg);
menu.setElevation(dpToPx(activity, 8));
menu.setClipToOutline(true);
menu.setPadding(0, dpToPx(activity, 6), 0, dpToPx(activity, 6));
Typeface monoFont;
try {
monoFont = ResourcesCompat.getFont(activity, R.font.source_code_pro);
} catch (Exception e) {
monoFont = Typeface.MONOSPACE;
}
// Title header
if (title != null) {
TextView titleView = new TextView(activity);
titleView.setText(title);
titleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
titleView.setTextColor(Color.parseColor("#66FFFFFF"));
titleView.setTypeface(monoFont);
titleView.setLetterSpacing(0.1f);
titleView.setPadding(dpToPx(activity, 14), dpToPx(activity, 6),
dpToPx(activity, 14), dpToPx(activity, 6));
menu.addView(titleView);
View div = new View(activity);
div.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(activity, 1)));
div.setBackgroundColor(Color.parseColor("#1AFFFFFF"));
LinearLayout.LayoutParams divParams = (LinearLayout.LayoutParams) div.getLayoutParams();
divParams.bottomMargin = dpToPx(activity, 2);
divParams.topMargin = dpToPx(activity, 2);
menu.addView(div);
}
// Menu items
Typeface finalMonoFont = monoFont;
for (MenuItem item : items) {
LinearLayout row = new LinearLayout(activity);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(Gravity.CENTER_VERTICAL);
row.setPadding(dpToPx(activity, 14), dpToPx(activity, 10),
dpToPx(activity, 14), dpToPx(activity, 10));
// Icon
if (item.iconChar != null) {
TextView icon = new TextView(activity);
icon.setText(item.iconChar);
icon.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams(
dpToPx(activity, 24), ViewGroup.LayoutParams.WRAP_CONTENT);
icon.setLayoutParams(iconParams);
icon.setGravity(Gravity.CENTER);
row.addView(icon);
}
// Label
TextView label = new TextView(activity);
label.setText(item.label);
label.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
label.setTextColor(Color.parseColor("#CCFFFFFF"));
label.setTypeface(finalMonoFont);
LinearLayout.LayoutParams labelParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
labelParams.setMarginStart(dpToPx(activity, 8));
label.setLayoutParams(labelParams);
row.addView(label);
row.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
dismiss(activity);
if (action != null) {
action.onAction(item.id);
}
});
menu.addView(row);
}
scrim.addView(menu);
// Animate in
scrim.setAlpha(0f);
menu.setScaleX(0.8f);
menu.setScaleY(0.8f);
decorView.addView(scrim);
scrim.animate().alpha(1f).setDuration(150).start();
menu.animate().scaleX(1f).scaleY(1f).setDuration(150).start();
}
public static void dismiss(Activity activity) {
if (activity == null) return;
FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();
View existing = decorView.findViewWithTag(OVERLAY_TAG);
if (existing != null) {
existing.animate().alpha(0f).setDuration(100).withEndAction(() -> {
decorView.removeView(existing);
}).start();
}
}
private static int dpToPx(Activity activity, float dp) {
return Math.round(TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp,
activity.getResources().getDisplayMetrics()));
}
}

View file

@ -0,0 +1,503 @@
package com.aethex.os;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.text.Editable;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.content.res.ResourcesCompat;
/**
* Custom in-app keyboard overlay that replaces the system soft keyboard.
* Styled to match the AeThex OS clearance theme (Foundation red/gold or Corp blue/silver).
*
* Usage:
* AeThexKeyboard.attach(activity); // in onCreate, after setContentView
* AeThexKeyboard.detach(activity); // optional cleanup in onDestroy
*/
public class AeThexKeyboard {
private static final String KEYBOARD_TAG = "aethex_keyboard";
private static final String KEYBOARD_OVERLAY_TAG = "aethex_keyboard_overlay";
// Key layouts
private static final String[][] ALPHA_ROWS = {
{"q", "w", "e", "r", "t", "y", "u", "i", "o", "p"},
{"a", "s", "d", "f", "g", "h", "j", "k", "l"},
{"", "z", "x", "c", "v", "b", "n", "m", ""},
{"?123", " ", ".", ""}
};
private static final String[][] SYMBOL_ROWS = {
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"},
{"@", "#", "$", "%", "&", "-", "+", "(", ")"},
{"=", "*", "\"", "'", ":", ";", "!", "?", ""},
{"ABC", " ", "/", ""}
};
// State
private static boolean showingSymbols = false;
private static boolean shiftActive = false;
private static EditText currentEditText = null;
/**
* Attach the custom keyboard to an Activity.
* Suppresses the system keyboard and shows the themed AeThex keyboard instead.
*/
public static void attach(Activity activity) {
if (activity == null) return;
// Prevent the system keyboard from showing automatically
activity.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
// Walk the view tree and find all EditTexts hook into their focus
View rootView = activity.getWindow().getDecorView().getRootView();
hookEditTexts(activity, rootView);
}
/**
* Attach to a specific EditText programmatically (for dynamic EditTexts like Spotlight).
*/
public static void attachToEditText(Activity activity, EditText editText) {
if (activity == null || editText == null) return;
editText.setShowSoftInputOnFocus(false);
editText.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
hideSystemKeyboard(activity, editText);
currentEditText = editText;
showKeyboard(activity);
}
});
// Also handle click (EditText may already have focus when tapped)
editText.setOnClickListener(v -> {
hideSystemKeyboard(activity, editText);
currentEditText = editText;
if (!isKeyboardShowing(activity)) {
showKeyboard(activity);
}
});
}
/**
* Detach and remove the keyboard overlay from an activity.
*/
public static void detach(Activity activity) {
dismissKeyboard(activity);
currentEditText = null;
showingSymbols = false;
shiftActive = false;
}
/**
* Dismiss the keyboard if showing.
*/
public static void dismissKeyboard(Activity activity) {
if (activity == null) return;
FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();
View existing = decorView.findViewWithTag(KEYBOARD_OVERLAY_TAG);
if (existing != null) {
existing.animate().translationY(existing.getHeight()).alpha(0.5f)
.setDuration(150).withEndAction(() -> {
decorView.removeView(existing);
}).start();
}
}
public static boolean isKeyboardShowing(Activity activity) {
if (activity == null) return false;
FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();
return decorView.findViewWithTag(KEYBOARD_OVERLAY_TAG) != null;
}
//
// Private: Hook all EditTexts
//
private static void hookEditTexts(Activity activity, View view) {
if (view instanceof EditText) {
EditText editText = (EditText) view;
// Prevent system keyboard from appearing
editText.setShowSoftInputOnFocus(false);
// Save original focus listener if any
editText.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
hideSystemKeyboard(activity, editText);
currentEditText = editText;
showKeyboard(activity);
}
});
editText.setOnClickListener(v -> {
hideSystemKeyboard(activity, editText);
currentEditText = editText;
if (!isKeyboardShowing(activity)) {
showKeyboard(activity);
}
});
} else if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
for (int i = 0; i < group.getChildCount(); i++) {
hookEditTexts(activity, group.getChildAt(i));
}
}
}
private static void hideSystemKeyboard(Activity activity, View view) {
InputMethodManager imm = (InputMethodManager) activity.getSystemService(
Activity.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
}
//
// Private: Build & Show keyboard
//
private static void showKeyboard(Activity activity) {
if (activity == null || activity.isFinishing() || activity.isDestroyed()) return;
FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();
// Remove existing keyboard if any
View existing = decorView.findViewWithTag(KEYBOARD_OVERLAY_TAG);
if (existing != null) {
decorView.removeView(existing);
}
// Build keyboard
LinearLayout keyboard = buildKeyboard(activity);
keyboard.setTag(KEYBOARD_OVERLAY_TAG);
FrameLayout.LayoutParams kbParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
kbParams.gravity = Gravity.BOTTOM;
keyboard.setLayoutParams(kbParams);
// Animate in from bottom
decorView.addView(keyboard);
keyboard.setTranslationY(400f);
keyboard.animate().translationY(0f).setDuration(200).start();
}
private static LinearLayout buildKeyboard(Activity activity) {
ThemeManager tm = new ThemeManager(activity);
boolean isFoundation = tm.isFoundation();
int primaryColor = tm.getPrimaryColor(activity);
int primaryAlpha15 = Color.argb(38, Color.red(primaryColor),
Color.green(primaryColor), Color.blue(primaryColor));
int primaryAlpha30 = Color.argb(77, Color.red(primaryColor),
Color.green(primaryColor), Color.blue(primaryColor));
// Glass background colors
String glassBg = isFoundation ? "#F2140808" : "#F20D1220";
String topBorderColor = isFoundation ? "#4DD4AF37" : "#4D3B82F6";
Typeface monoFont;
try {
monoFont = ResourcesCompat.getFont(activity, R.font.source_code_pro);
} catch (Exception e) {
monoFont = Typeface.MONOSPACE;
}
// Container
LinearLayout container = new LinearLayout(activity);
container.setOrientation(LinearLayout.VERTICAL);
container.setTag(KEYBOARD_TAG);
GradientDrawable bg = new GradientDrawable();
bg.setColor(Color.parseColor(glassBg));
bg.setCornerRadii(new float[]{
dpToPx(activity, 12), dpToPx(activity, 12),
dpToPx(activity, 12), dpToPx(activity, 12),
0, 0, 0, 0
});
container.setBackground(bg);
container.setElevation(dpToPx(activity, 12));
container.setPadding(dpToPx(activity, 4), dpToPx(activity, 6),
dpToPx(activity, 4), dpToPx(activity, 10));
// Top border line
View topLine = new View(activity);
topLine.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(activity, 1)));
topLine.setBackgroundColor(Color.parseColor(topBorderColor));
container.addView(topLine);
// Key rows
String[][] rows = showingSymbols ? SYMBOL_ROWS : ALPHA_ROWS;
for (int rowIdx = 0; rowIdx < rows.length; rowIdx++) {
LinearLayout row = new LinearLayout(activity);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rowParams.topMargin = dpToPx(activity, 4);
row.setLayoutParams(rowParams);
for (String key : rows[rowIdx]) {
View keyView = buildKey(activity, key, monoFont,
primaryColor, primaryAlpha15, isFoundation);
row.addView(keyView);
}
container.addView(row);
}
return container;
}
private static View buildKey(Activity activity, String key, Typeface font,
int primaryColor, int primaryAlpha15, boolean isFoundation) {
boolean isSpecial = key.equals("") || key.equals("") || key.equals("")
|| key.equals("?123") || key.equals("ABC");
boolean isSpace = key.equals(" ");
boolean isEnter = key.equals("");
boolean isShift = key.equals("");
// Display text
String displayText;
if (isSpace) {
displayText = "SPACE";
} else if (shiftActive && key.length() == 1 && Character.isLetter(key.charAt(0))) {
displayText = key.toUpperCase();
} else {
displayText = key;
}
TextView tv = new TextView(activity);
tv.setText(displayText);
tv.setGravity(Gravity.CENTER);
tv.setTypeface(font);
// Sizing
int height = dpToPx(activity, 42);
float weight;
if (isSpace) {
weight = 5f;
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
tv.setLetterSpacing(0.15f);
} else if (key.equals("?123") || key.equals("ABC")) {
weight = 1.5f;
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
} else if (isShift || key.equals("")) {
weight = 1.3f;
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
} else if (isEnter) {
weight = 1.3f;
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
} else {
weight = 1f;
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
0, height, weight);
params.setMargins(dpToPx(activity, 2), dpToPx(activity, 2),
dpToPx(activity, 2), dpToPx(activity, 2));
tv.setLayoutParams(params);
// Colors
GradientDrawable keyBg = new GradientDrawable();
keyBg.setCornerRadius(dpToPx(activity, 6));
if (isEnter) {
keyBg.setColor(primaryColor);
tv.setTextColor(Color.WHITE);
} else if (isSpecial || isSpace) {
keyBg.setColor(primaryAlpha15);
tv.setTextColor(Color.parseColor("#CCFFFFFF"));
} else {
keyBg.setColor(Color.parseColor("#0DFFFFFF"));
tv.setTextColor(Color.parseColor("#CCFFFFFF"));
}
// Shift active indicator
if (isShift && shiftActive) {
keyBg.setStroke(dpToPx(activity, 1), primaryColor);
}
tv.setBackground(keyBg);
// Touch feedback + key action
tv.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
keyBg.setColor(Color.parseColor("#1AFFFFFF"));
tv.setBackground(keyBg);
} else if (event.getAction() == MotionEvent.ACTION_UP
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
// Restore
if (isEnter) {
keyBg.setColor(primaryColor);
} else if (isSpecial || isSpace) {
keyBg.setColor(primaryAlpha15);
} else {
keyBg.setColor(Color.parseColor("#0DFFFFFF"));
}
tv.setBackground(keyBg);
}
return false; // Let onClick also fire
});
tv.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
handleKeyPress(activity, key);
});
return tv;
}
//
// Private: Key press handling
//
private static void handleKeyPress(Activity activity, String key) {
if (currentEditText == null) return;
switch (key) {
case "":
shiftActive = !shiftActive;
rebuildKeyboard(activity);
return;
case "":
handleBackspace();
return;
case "":
handleEnter(activity);
return;
case "?123":
showingSymbols = true;
rebuildKeyboard(activity);
return;
case "ABC":
showingSymbols = false;
rebuildKeyboard(activity);
return;
case " ":
insertText(" ");
return;
default:
String text = key;
if (shiftActive && key.length() == 1 && Character.isLetter(key.charAt(0))) {
text = key.toUpperCase();
shiftActive = false;
rebuildKeyboard(activity);
}
insertText(text);
return;
}
}
private static void insertText(String text) {
if (currentEditText == null) return;
Editable editable = currentEditText.getText();
int start = currentEditText.getSelectionStart();
int end = currentEditText.getSelectionEnd();
if (start < 0) start = 0;
if (end < 0) end = start;
if (start != end) {
// Replace selection
editable.replace(start, end, text);
} else {
editable.insert(start, text);
}
}
private static void handleBackspace() {
if (currentEditText == null) return;
Editable editable = currentEditText.getText();
int start = currentEditText.getSelectionStart();
int end = currentEditText.getSelectionEnd();
if (start != end && start >= 0 && end >= 0) {
// Delete selection
editable.delete(Math.min(start, end), Math.max(start, end));
} else if (start > 0) {
editable.delete(start - 1, start);
}
}
private static void handleEnter(Activity activity) {
if (currentEditText == null) return;
// Check if the EditText has a single-line IME action
int imeOptions = currentEditText.getImeOptions();
int inputType = currentEditText.getInputType();
// For single-line fields, fire the IME action (like Send, Go, etc.)
boolean isSingleLine = (inputType & android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE) == 0;
if (isSingleLine) {
// Dispatch the editor action
currentEditText.onEditorAction(imeOptions & EditorInfo.IME_MASK_ACTION);
} else {
// Multi-line: insert newline
insertText("\n");
}
}
private static void rebuildKeyboard(Activity activity) {
if (activity == null || activity.isFinishing() || activity.isDestroyed()) return;
FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();
View existing = decorView.findViewWithTag(KEYBOARD_OVERLAY_TAG);
if (existing != null) {
decorView.removeView(existing);
}
LinearLayout keyboard = buildKeyboard(activity);
keyboard.setTag(KEYBOARD_OVERLAY_TAG);
FrameLayout.LayoutParams kbParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
kbParams.gravity = Gravity.BOTTOM;
keyboard.setLayoutParams(kbParams);
decorView.addView(keyboard);
}
//
// Utility
//
private static int dpToPx(Activity activity, float dp) {
return Math.round(TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp,
activity.getResources().getDisplayMetrics()));
}
}

View file

@ -0,0 +1,139 @@
package com.aethex.os;
import android.app.Notification;
import android.content.Intent;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import java.util.ArrayList;
import java.util.List;
/**
* Intercepts real Android notifications so AeThexOS can display them
* in its own notification panel instead of the Android shade.
*/
public class AeThexNotificationService extends NotificationListenerService {
private static AeThexNotificationService instance;
private static final List<NotificationData> activeNotifications = new ArrayList<>();
private static OnNotificationChangeListener listener;
public static class NotificationData {
public String packageName;
public String title;
public String text;
public long postTime;
public String key;
public NotificationData(String packageName, String title, String text, long postTime, String key) {
this.packageName = packageName;
this.title = title;
this.text = text;
this.postTime = postTime;
this.key = key;
}
}
public interface OnNotificationChangeListener {
void onNotificationsChanged();
}
public static void setListener(OnNotificationChangeListener l) {
listener = l;
}
public static List<NotificationData> getNotifications() {
synchronized (activeNotifications) {
return new ArrayList<>(activeNotifications);
}
}
public static int getCount() {
synchronized (activeNotifications) {
return activeNotifications.size();
}
}
public static boolean isRunning() {
return instance != null;
}
@Override
public void onCreate() {
super.onCreate();
instance = this;
}
@Override
public void onDestroy() {
super.onDestroy();
instance = null;
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
if (notification == null) return;
Bundle extras = notification.extras;
String title = extras != null ? extras.getString(Notification.EXTRA_TITLE, "") : "";
CharSequence textCs = extras != null ? extras.getCharSequence(Notification.EXTRA_TEXT) : null;
String text = textCs != null ? textCs.toString() : "";
// Skip empty notifications
if (title.isEmpty() && text.isEmpty()) return;
// Skip our own notifications
if (sbn.getPackageName().equals(getPackageName())) return;
synchronized (activeNotifications) {
// Remove existing with same key
activeNotifications.removeIf(n -> n.key.equals(sbn.getKey()));
// Add new
activeNotifications.add(0, new NotificationData(
sbn.getPackageName(), title, text, sbn.getPostTime(), sbn.getKey()));
// Cap at 20
while (activeNotifications.size() > 20) {
activeNotifications.remove(activeNotifications.size() - 1);
}
}
if (listener != null) listener.onNotificationsChanged();
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
synchronized (activeNotifications) {
activeNotifications.removeIf(n -> n.key.equals(sbn.getKey()));
}
if (listener != null) listener.onNotificationsChanged();
}
@Override
public void onListenerConnected() {
// Load existing notifications
try {
StatusBarNotification[] current = super.getActiveNotifications();
if (current != null) {
synchronized (activeNotifications) {
activeNotifications.clear();
for (StatusBarNotification sbn : current) {
Notification n = sbn.getNotification();
if (n == null) continue;
Bundle extras = n.extras;
String title = extras != null ? extras.getString(Notification.EXTRA_TITLE, "") : "";
CharSequence textCs = extras != null ? extras.getCharSequence(Notification.EXTRA_TEXT) : null;
String text = textCs != null ? textCs.toString() : "";
if (title.isEmpty() && text.isEmpty()) continue;
if (sbn.getPackageName().equals(getPackageName())) continue;
activeNotifications.add(new NotificationData(
sbn.getPackageName(), title, text, sbn.getPostTime(), sbn.getKey()));
}
}
}
} catch (Exception ignored) {}
if (listener != null) listener.onNotificationsChanged();
}
}

View file

@ -0,0 +1,208 @@
package com.aethex.os;
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.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.content.res.ResourcesCompat;
public class AeThexToast {
private static final String CONTAINER_TAG = "aethex_toast_container";
private static final int ANIM_DURATION = 200;
private static final int DISPLAY_DURATION = 4000;
public enum Type {
INFO("#06B6D4"),
SUCCESS("#22C55E"),
WARNING("#FBBF24"),
ERROR("#EF4444");
public final String color;
Type(String color) {
this.color = color;
}
}
public static void show(Activity activity, String message, Type type) {
if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
return;
}
activity.runOnUiThread(() -> {
LinearLayout container = getOrCreateContainer(activity);
View toastView = createToastView(activity, message, type);
container.addView(toastView);
// Animate in: slide from right + fade in
toastView.setTranslationX(100f);
toastView.setAlpha(0f);
toastView.animate()
.translationX(0f)
.alpha(1f)
.setDuration(ANIM_DURATION)
.start();
// Auto-dismiss after delay
new Handler(Looper.getMainLooper()).postDelayed(() -> {
toastView.animate()
.translationX(100f)
.alpha(0f)
.setDuration(ANIM_DURATION)
.withEndAction(() -> {
ViewGroup parent = (ViewGroup) toastView.getParent();
if (parent != null) {
parent.removeView(toastView);
if (parent.getChildCount() == 0) {
ViewGroup grandParent = (ViewGroup) parent.getParent();
if (grandParent != null) {
grandParent.removeView(parent);
}
}
}
})
.start();
}, DISPLAY_DURATION);
});
}
private static LinearLayout getOrCreateContainer(Activity activity) {
FrameLayout decorView = (FrameLayout) activity.getWindow().getDecorView();
View existing = decorView.findViewWithTag(CONTAINER_TAG);
if (existing instanceof LinearLayout) {
return (LinearLayout) existing;
}
LinearLayout container = new LinearLayout(activity);
container.setOrientation(LinearLayout.VERTICAL);
container.setGravity(Gravity.TOP | Gravity.END);
container.setTag(CONTAINER_TAG);
int paddingTop = dpToPx(activity, 16);
int paddingEnd = dpToPx(activity, 12);
container.setPaddingRelative(0, paddingTop, paddingEnd, 0);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
);
params.gravity = Gravity.TOP | Gravity.END;
container.setLayoutParams(params);
container.setClickable(false);
container.setFocusable(false);
decorView.addView(container);
return container;
}
private static View createToastView(Activity activity, String message, Type type) {
int dp1 = dpToPx(activity, 1);
int dp4 = dpToPx(activity, 4);
int dp8 = dpToPx(activity, 8);
int dp12 = dpToPx(activity, 12);
int maxWidth = dpToPx(activity, 280);
// Resolve theme colors
ThemeManager tm = new ThemeManager(activity);
boolean isFoundation = tm.isFoundation();
// Glass background + border adapt to clearance mode
String glassColor = isFoundation ? "#E61A0A0A" : "#E60F172A";
String borderColor = isFoundation ? "#33D4AF37" : "#333B82F6";
int themeGlowColor = isFoundation
? Color.parseColor("#33D4AF37") // gold glow
: Color.parseColor("#333B82F6"); // blue glow
// Outer wrapper with themed glass background
LinearLayout outerWrapper = new LinearLayout(activity);
outerWrapper.setOrientation(LinearLayout.VERTICAL);
GradientDrawable borderDrawable = new GradientDrawable();
borderDrawable.setShape(GradientDrawable.RECTANGLE);
borderDrawable.setCornerRadius(dpToPx(activity, 8));
borderDrawable.setStroke(dp1, Color.parseColor(borderColor));
borderDrawable.setColor(Color.parseColor(glassColor));
outerWrapper.setBackground(borderDrawable);
outerWrapper.setElevation(dp4);
outerWrapper.setClipToOutline(true);
// Toast horizontal layout (accent bar + message)
LinearLayout toastLayout = new LinearLayout(activity);
toastLayout.setOrientation(LinearLayout.HORIZONTAL);
toastLayout.setGravity(Gravity.CENTER_VERTICAL);
toastLayout.setPadding(0, dp12, dp12, dp12);
// Accent bar: 4dp wide, full height, colored by type
View accentBar = new View(activity);
LinearLayout.LayoutParams accentParams = new LinearLayout.LayoutParams(
dp4, LinearLayout.LayoutParams.MATCH_PARENT);
accentBar.setLayoutParams(accentParams);
accentBar.setBackgroundColor(Color.parseColor(type.color));
// Message TextView
TextView messageView = new TextView(activity);
messageView.setText(message);
messageView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
messageView.setTextColor(Color.argb((int) (255 * 0.8f), 255, 255, 255));
messageView.setMaxWidth(maxWidth - dp4 - dp12 - dp12);
try {
Typeface font = ResourcesCompat.getFont(activity, R.font.source_code_pro);
if (font != null) {
messageView.setTypeface(font);
}
} catch (Exception ignored) {
messageView.setTypeface(Typeface.MONOSPACE);
}
LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
textParams.setMarginStart(dp12);
messageView.setLayoutParams(textParams);
toastLayout.addView(accentBar);
toastLayout.addView(messageView);
outerWrapper.addView(toastLayout, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
// Bottom glow line (clearance-colored, 1dp)
View glowLine = new View(activity);
LinearLayout.LayoutParams glowParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, dp1);
glowLine.setLayoutParams(glowParams);
glowLine.setBackgroundColor(themeGlowColor);
outerWrapper.addView(glowLine);
// Container item params with 8dp bottom spacing between toasts
LinearLayout.LayoutParams containerItemParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
containerItemParams.setMargins(0, 0, 0, dp8);
containerItemParams.gravity = Gravity.END;
outerWrapper.setLayoutParams(containerItemParams);
return outerWrapper;
}
private static int dpToPx(Activity activity, float dp) {
return Math.round(TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp,
activity.getResources().getDisplayMetrics()));
}
}

View file

@ -0,0 +1,671 @@
package com.aethex.os;
import android.animation.ValueAnimator;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
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.WindowInsets;
import android.view.WindowInsetsController;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Random;
/**
* Analytics -- Live simulated analytics dashboard with stats, bar chart,
* network metrics, live activity log, and export functionality.
*/
public class AnalyticsActivity extends AppCompatActivity {
private ThemeManager themeManager;
private Handler handler;
private Random random = new Random();
// Stat card value TextViews
private TextView statUsers;
private TextView statSessions;
private TextView statUptime;
private TextView statRequests;
// Network metric TextViews
private TextView netBandwidth;
private TextView netLatency;
private TextView netPacketLoss;
// Live activity log
private LinearLayout logContainer;
private final List<View> logEntries = new ArrayList<>();
private static final int MAX_LOG_ENTRIES = 10;
// Bar chart bars for animation
private final List<View> chartBars = new ArrayList<>();
private final List<Integer> chartTargetHeights = new ArrayList<>();
// Stat breakdown details for tap-to-inspect
private static final String[] STAT_LABELS = {"ACTIVE USERS", "SESSIONS", "UPTIME", "REQUESTS/S"};
// Log message templates
private static final String[] LOG_USERS = {
"architect@aethex", "admin@corp", "dev.team@aethex", "ops@foundation",
"security@aethex", "analyst@corp", "root@system", "ci-bot@pipeline",
"monitor@infra", "deploy@staging"
};
private static final String[] LOG_ACTIONS = {
"User login", "API call", "Module loaded", "Session start",
"Config update", "Cache cleared", "DB query", "Auth token refresh",
"Webhook fired", "Service restart", "Log rotation", "Health check",
"Schema migration", "Backup snapshot", "Rate limit hit"
};
private static final String[] LOG_TARGETS = {
"/v2/analytics/stream", "cipher_toolkit", "device:android",
"theme_engine", "/api/v1/users", "auth_service", "redis_cache",
"/v2/telemetry", "kernel_module", "mesh_network", "/v1/health",
"storage_layer", "dns_resolver", "proxy_gateway", "cert_manager"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app);
hideSystemUI();
themeManager = new ThemeManager(this);
handler = new Handler(Looper.getMainLooper());
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
TextView title = findViewById(R.id.app_title);
title.setText("Analytics");
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 | Gravity.CENTER_HORIZONTAL);
content.setPadding(0, 0, 0, 0);
// Wrap everything in a ScrollView
ScrollView scrollView = new ScrollView(this);
scrollView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
scrollView.setFillViewport(true);
scrollView.setVerticalScrollBarEnabled(false);
LinearLayout scrollContent = new LinearLayout(this);
scrollContent.setOrientation(LinearLayout.VERTICAL);
scrollContent.setLayoutParams(new ScrollView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
scrollContent.setPadding(dpToPx(16), dpToPx(16), dpToPx(16), dpToPx(24));
buildAnalyticsUI(scrollContent);
scrollView.addView(scrollContent);
content.addView(scrollView);
View root = findViewById(R.id.app_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
// Start live updaters
startStatsUpdater();
startNetworkUpdater();
startLogUpdater();
// Animate bar chart on entry (delayed to let layout settle)
handler.postDelayed(this::animateChartBars, 400);
}
//
// BUILD UI
//
private void buildAnalyticsUI(LinearLayout parent) {
Typeface displayFont = themeManager.getDisplayFont(this);
Typeface monoFont = themeManager.getMonoFont(this);
// Section: LIVE DASHBOARD header
addSectionHeader(parent, "LIVE DASHBOARD", monoFont);
// Stats grid -- 2x2
LinearLayout row1 = createHorizontalRow(dpToPx(10));
statUsers = createStatCard(row1, STAT_LABELS[0], "1,247", "#06B6D4", displayFont, monoFont, 0);
statSessions = createStatCard(row1, STAT_LABELS[1], "3,891", "#22C55E", displayFont, monoFont, 1);
parent.addView(row1);
LinearLayout row2 = createHorizontalRow(dpToPx(16));
statUptime = createStatCard(row2, STAT_LABELS[2], "99.97%", "#A855F7", displayFont, monoFont, 2);
statRequests = createStatCard(row2, STAT_LABELS[3], "842", "#F97316", displayFont, monoFont, 3);
parent.addView(row2);
// Section: TRAFFIC (bar chart)
addDivider(parent);
addSectionHeader(parent, "WEEKLY TRAFFIC", monoFont);
parent.addView(buildBarChart(displayFont, monoFont));
// Section: NETWORK
addDivider(parent);
addSectionHeader(parent, "NETWORK", monoFont);
parent.addView(buildNetworkSection(displayFont, monoFont));
// Section: RECENT ACTIVITY (live log)
addDivider(parent);
addSectionHeader(parent, "RECENT ACTIVITY", monoFont);
logContainer = new LinearLayout(this);
logContainer.setOrientation(LinearLayout.VERTICAL);
logContainer.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
parent.addView(logContainer);
// Seed initial log entries
seedInitialLogEntries(monoFont);
// Export Report button
LinearLayout.LayoutParams btnParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(48));
btnParams.topMargin = dpToPx(24);
parent.addView(buildExportButton(monoFont, btnParams));
}
//
// STAT CARDS
//
private TextView createStatCard(LinearLayout parent, String label, String value,
String colorHex, Typeface displayFont, Typeface monoFont,
int statIndex) {
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.VERTICAL);
card.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
cardParams.setMarginEnd(dpToPx(8));
card.setLayoutParams(cardParams);
card.setPadding(dpToPx(12), dpToPx(16), dpToPx(12), dpToPx(16));
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(dpToPx(12));
bg.setColor(Color.parseColor("#0D0F172A"));
bg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
card.setBackground(bg);
TextView valueView = new TextView(this);
valueView.setText(value);
valueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 22);
valueView.setTextColor(Color.parseColor(colorHex));
valueView.setTypeface(displayFont);
valueView.setGravity(Gravity.CENTER);
valueView.setTag("stat_value");
card.addView(valueView);
TextView labelView = new TextView(this);
labelView.setText(label);
labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
labelView.setTextColor(Color.parseColor("#66FFFFFF"));
labelView.setTypeface(monoFont);
labelView.setGravity(Gravity.CENTER);
labelView.setLetterSpacing(0.1f);
LinearLayout.LayoutParams lParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lParams.topMargin = dpToPx(4);
labelView.setLayoutParams(lParams);
card.addView(labelView);
// Tappable: show detailed breakdown
card.setClickable(true);
card.setFocusable(true);
card.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
String detail = getStatBreakdown(statIndex, valueView.getText().toString());
AeThexToast.show(AnalyticsActivity.this, detail, AeThexToast.Type.INFO);
});
parent.addView(card);
return valueView;
}
private String getStatBreakdown(int statIndex, String currentValue) {
switch (statIndex) {
case 0: // Active Users
int peakHour = 10 + random.nextInt(10);
int peakUsers = 1400 + random.nextInt(200);
return "Active Users: " + currentValue
+ " \u2014 Peak: " + String.format(Locale.US, "%,d", peakUsers)
+ " at " + String.format(Locale.US, "%02d:00", peakHour);
case 1: // Sessions
int avgDuration = 4 + random.nextInt(8);
int bounceRate = 15 + random.nextInt(20);
return "Sessions: " + currentValue
+ " \u2014 Avg duration: " + avgDuration + "m"
+ " \u2014 Bounce: " + bounceRate + "%";
case 2: // Uptime
int daysUp = 30 + random.nextInt(60);
return "Uptime: " + currentValue
+ " \u2014 " + daysUp + " days since last restart"
+ " \u2014 SLA target: 99.95%";
case 3: // Requests/s
int p99 = 80 + random.nextInt(120);
int errRate = random.nextInt(3);
return "Requests/s: " + currentValue
+ " \u2014 p99 latency: " + p99 + "ms"
+ " \u2014 Error rate: 0." + errRate + "%";
default:
return currentValue;
}
}
//
// BAR CHART
//
private LinearLayout buildBarChart(Typeface displayFont, Typeface monoFont) {
String[] days = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
String[] barColors = {"#06B6D4", "#22C55E", "#A855F7", "#F97316", "#EF4444", "#FBBF24", "#06B6D4"};
int maxBarHeight = dpToPx(100);
int minBarHeight = dpToPx(20);
LinearLayout chartContainer = new LinearLayout(this);
chartContainer.setOrientation(LinearLayout.VERTICAL);
chartContainer.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
// Card background
GradientDrawable chartBg = new GradientDrawable();
chartBg.setCornerRadius(dpToPx(12));
chartBg.setColor(Color.parseColor("#0D0F172A"));
chartBg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
chartContainer.setBackground(chartBg);
chartContainer.setPadding(dpToPx(16), dpToPx(16), dpToPx(16), dpToPx(12));
// Row of bars
LinearLayout barsRow = new LinearLayout(this);
barsRow.setOrientation(LinearLayout.HORIZONTAL);
barsRow.setGravity(Gravity.BOTTOM);
barsRow.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, maxBarHeight + dpToPx(4)));
for (int i = 0; i < days.length; i++) {
int targetHeight = minBarHeight + random.nextInt(maxBarHeight - minBarHeight);
LinearLayout barColumn = new LinearLayout(this);
barColumn.setOrientation(LinearLayout.VERTICAL);
barColumn.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
LinearLayout.LayoutParams colParams = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.MATCH_PARENT, 1f);
colParams.setMarginStart(dpToPx(3));
colParams.setMarginEnd(dpToPx(3));
barColumn.setLayoutParams(colParams);
// The bar itself
View bar = new View(this);
GradientDrawable barDrawable = new GradientDrawable();
barDrawable.setCornerRadii(new float[]{
dpToPx(4), dpToPx(4), dpToPx(4), dpToPx(4), 0, 0, 0, 0});
barDrawable.setColor(Color.parseColor(barColors[i]));
bar.setBackground(barDrawable);
// Start at 0 height for animation
LinearLayout.LayoutParams barParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0);
bar.setLayoutParams(barParams);
barColumn.addView(bar);
chartBars.add(bar);
chartTargetHeights.add(targetHeight);
barsRow.addView(barColumn);
}
chartContainer.addView(barsRow);
// Day labels row
LinearLayout labelsRow = new LinearLayout(this);
labelsRow.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams labelsParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
labelsParams.topMargin = dpToPx(8);
labelsRow.setLayoutParams(labelsParams);
for (String day : days) {
TextView dayLabel = new TextView(this);
dayLabel.setText(day);
dayLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
dayLabel.setTextColor(Color.parseColor("#66FFFFFF"));
dayLabel.setTypeface(monoFont);
dayLabel.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams dlParams = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
dayLabel.setLayoutParams(dlParams);
labelsRow.addView(dayLabel);
}
chartContainer.addView(labelsRow);
return chartContainer;
}
private void animateChartBars() {
for (int i = 0; i < chartBars.size(); i++) {
final View bar = chartBars.get(i);
final int targetHeight = chartTargetHeights.get(i);
ValueAnimator animator = ValueAnimator.ofInt(0, targetHeight);
animator.setDuration(400);
animator.setStartDelay(i * 50L);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(animation -> {
ViewGroup.LayoutParams params = bar.getLayoutParams();
params.height = (int) animation.getAnimatedValue();
bar.setLayoutParams(params);
});
animator.start();
}
}
//
// NETWORK SECTION
//
private LinearLayout buildNetworkSection(Typeface displayFont, Typeface monoFont) {
LinearLayout networkRow = createHorizontalRow(dpToPx(16));
netBandwidth = createNetworkMetricCard(networkRow, "BANDWIDTH", "12.4 MB/s", "#06B6D4", displayFont, monoFont);
netLatency = createNetworkMetricCard(networkRow, "LATENCY", "23ms", "#22C55E", displayFont, monoFont);
netPacketLoss = createNetworkMetricCard(networkRow, "PACKET LOSS", "0.01%", "#FBBF24", displayFont, monoFont);
return networkRow;
}
private TextView createNetworkMetricCard(LinearLayout parent, String label, String value,
String colorHex, Typeface displayFont, Typeface monoFont) {
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.VERTICAL);
card.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
cardParams.setMarginEnd(dpToPx(6));
card.setLayoutParams(cardParams);
card.setPadding(dpToPx(8), dpToPx(12), dpToPx(8), dpToPx(12));
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(dpToPx(12));
bg.setColor(Color.parseColor("#0D0F172A"));
bg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
card.setBackground(bg);
TextView valueView = new TextView(this);
valueView.setText(value);
valueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15);
valueView.setTextColor(Color.parseColor(colorHex));
valueView.setTypeface(displayFont);
valueView.setGravity(Gravity.CENTER);
card.addView(valueView);
TextView labelView = new TextView(this);
labelView.setText(label);
labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 8);
labelView.setTextColor(Color.parseColor("#66FFFFFF"));
labelView.setTypeface(monoFont);
labelView.setGravity(Gravity.CENTER);
labelView.setLetterSpacing(0.1f);
LinearLayout.LayoutParams lParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lParams.topMargin = dpToPx(4);
labelView.setLayoutParams(lParams);
card.addView(labelView);
parent.addView(card);
return valueView;
}
//
// LIVE ACTIVITY LOG
//
private void seedInitialLogEntries(Typeface monoFont) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
long now = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
long ts = now - (i * 15000L) - random.nextInt(10000);
String time = sdf.format(new Date(ts));
String entry = time + " " + LOG_ACTIONS[random.nextInt(LOG_ACTIONS.length)]
+ " \u2014 " + LOG_TARGETS[random.nextInt(LOG_TARGETS.length)];
addLogEntryView(entry, monoFont, false);
}
}
private void addLogEntryView(String text, Typeface monoFont, boolean animate) {
TextView logLine = new TextView(this);
logLine.setText(text);
logLine.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
logLine.setTextColor(Color.parseColor("#4DFFFFFF"));
logLine.setTypeface(monoFont);
LinearLayout.LayoutParams llParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
llParams.bottomMargin = dpToPx(4);
logLine.setLayoutParams(llParams);
if (animate) {
logLine.setAlpha(0f);
logLine.setTranslationY(-dpToPx(8));
}
// Prepend (add at index 0)
logContainer.addView(logLine, 0);
logEntries.add(0, logLine);
if (animate) {
logLine.animate().alpha(1f).translationY(0).setDuration(250).start();
}
// Trim to max entries
while (logEntries.size() > MAX_LOG_ENTRIES) {
View old = logEntries.remove(logEntries.size() - 1);
logContainer.removeView(old);
}
}
private String generateLogEntry() {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
String time = sdf.format(new Date());
String action = LOG_ACTIONS[random.nextInt(LOG_ACTIONS.length)];
String target = LOG_TARGETS[random.nextInt(LOG_TARGETS.length)];
// Occasionally include a user
if (random.nextInt(3) == 0) {
String user = LOG_USERS[random.nextInt(LOG_USERS.length)];
return time + " " + action + " \u2014 " + user;
}
return time + " " + action + " \u2014 " + target;
}
//
// EXPORT BUTTON
//
private LinearLayout buildExportButton(Typeface monoFont, LinearLayout.LayoutParams params) {
LinearLayout button = new LinearLayout(this);
button.setOrientation(LinearLayout.HORIZONTAL);
button.setGravity(Gravity.CENTER);
button.setLayoutParams(params);
int primaryColor = themeManager.getPrimaryColor(this);
GradientDrawable btnBg = new GradientDrawable();
btnBg.setCornerRadius(dpToPx(10));
btnBg.setColor(Color.argb(40,
Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor)));
btnBg.setStroke(dpToPx(1), Color.argb(80,
Color.red(primaryColor), Color.green(primaryColor), Color.blue(primaryColor)));
button.setBackground(btnBg);
TextView btnText = new TextView(this);
btnText.setText("EXPORT REPORT");
btnText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
btnText.setTextColor(Color.parseColor("#CCFFFFFF"));
btnText.setTypeface(monoFont);
btnText.setLetterSpacing(0.15f);
button.addView(btnText);
button.setClickable(true);
button.setFocusable(true);
button.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
AeThexToast.show(AnalyticsActivity.this,
"Report exported to /system/reports/", AeThexToast.Type.SUCCESS);
});
return button;
}
//
// LIVE UPDATERS
//
private void startStatsUpdater() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (isFinishing() || isDestroyed()) return;
int users = 1200 + random.nextInt(100);
int sessions = 3800 + random.nextInt(200);
int requests = 800 + random.nextInt(100);
statUsers.setText(String.format(Locale.US, "%,d", users));
statSessions.setText(String.format(Locale.US, "%,d", sessions));
statRequests.setText(String.valueOf(requests));
handler.postDelayed(this, 3000);
}
}, 3000);
}
private void startNetworkUpdater() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (isFinishing() || isDestroyed()) return;
float bandwidth = 8.0f + random.nextFloat() * 12.0f;
int latency = 10 + random.nextInt(50);
float packetLoss = random.nextFloat() * 0.1f;
netBandwidth.setText(String.format(Locale.US, "%.1f MB/s", bandwidth));
netLatency.setText(latency + "ms");
netPacketLoss.setText(String.format(Locale.US, "%.2f%%", packetLoss));
handler.postDelayed(this, 3000);
}
}, 3000);
}
private void startLogUpdater() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (isFinishing() || isDestroyed()) return;
Typeface monoFont = themeManager.getMonoFont(AnalyticsActivity.this);
String entry = generateLogEntry();
addLogEntryView(entry, monoFont, true);
handler.postDelayed(this, 5000);
}
}, 5000);
}
//
// HELPERS
//
private void addSectionHeader(LinearLayout parent, String text, Typeface monoFont) {
TextView header = new TextView(this);
header.setText(text);
header.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
header.setTextColor(Color.parseColor("#66FFFFFF"));
header.setTypeface(monoFont);
header.setLetterSpacing(0.15f);
LinearLayout.LayoutParams hParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
hParams.bottomMargin = dpToPx(12);
header.setLayoutParams(hParams);
parent.addView(header);
}
private void addDivider(LinearLayout parent) {
View divider = new View(this);
LinearLayout.LayoutParams dParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(1));
dParams.topMargin = dpToPx(20);
dParams.bottomMargin = dpToPx(16);
divider.setLayoutParams(dParams);
divider.setBackgroundColor(Color.parseColor("#1AFFFFFF"));
parent.addView(divider);
}
private LinearLayout createHorizontalRow(int bottomMargin) {
LinearLayout row = new LinearLayout(this);
row.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rowParams.bottomMargin = bottomMargin;
row.setLayoutParams(rowParams);
return row;
}
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
//
// LIFECYCLE
//
@Override
protected void onDestroy() {
super.onDestroy();
if (handler != null) handler.removeCallbacksAndMessages(null);
}
@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);
}
}
}

View file

@ -0,0 +1,66 @@
package com.aethex.os;
import android.os.Bundle;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class AppActivity extends AppCompatActivity {
private ThemeManager themeManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app);
hideSystemUI();
themeManager = new ThemeManager(this);
String appName = getIntent().getStringExtra("app_name");
String appId = getIntent().getStringExtra("app_id");
if (appName != null) {
TextView title = findViewById(R.id.app_title);
title.setText(appName);
TextView nameDisplay = findViewById(R.id.app_name_display);
nameDisplay.setText(appName);
}
findViewById(R.id.app_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Entrance animation for the content
View root = findViewById(R.id.app_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
}
@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);
}
}
}

View file

@ -0,0 +1,254 @@
package com.aethex.os;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Outline;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class AppAdapter extends RecyclerView.Adapter<AppAdapter.ViewHolder> {
private List<AppInfo> appList;
private LayoutInflater inflater;
private Context context;
private ScreenTimeTracker screenTimeTracker;
private OnAppListChangedListener appListChangedListener;
// Track touch coordinates for context menu positioning
private float lastTouchX = 0f;
private float lastTouchY = 0f;
// Monochrome filter for third-party app icons
private static final ColorMatrixColorFilter MONO_FILTER;
static {
ColorMatrix matrix = new ColorMatrix();
matrix.setSaturation(0);
MONO_FILTER = new ColorMatrixColorFilter(matrix);
}
public interface OnAppListChangedListener {
void onAppListChanged();
}
public AppAdapter(Context context, List<AppInfo> appList) {
this.inflater = LayoutInflater.from(context);
this.context = context;
this.appList = appList;
this.screenTimeTracker = new ScreenTimeTracker(context);
}
public void setOnAppListChangedListener(OnAppListChangedListener listener) {
this.appListChangedListener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.item_app_icon, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
AppInfo app = appList.get(position);
holder.appName.setText(app.getName());
// Set icon drawable for real apps, resource for built-in
if (app.hasDrawableIcon()) {
holder.appIcon.setImageDrawable(app.getDrawableIcon());
holder.appIcon.setColorFilter(MONO_FILTER);
// Clip into uniform rounded square
holder.appIcon.setBackgroundResource(R.drawable.bg_app_icon_mask);
int pad = dpToPx(4);
holder.appIcon.setPadding(pad, pad, pad, pad);
holder.appIcon.setClipToOutline(true);
holder.appIcon.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
float radius = dpToPx(10);
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), radius);
}
});
} else {
holder.appIcon.setImageResource(app.getIcon());
holder.appIcon.clearColorFilter();
holder.appIcon.setBackground(null);
holder.appIcon.setPadding(0, 0, 0, 0);
holder.appIcon.setClipToOutline(false);
}
// Track touch position for context menu placement
holder.itemView.setOnTouchListener((v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
lastTouchX = event.getRawX();
lastTouchY = event.getRawY();
}
return false;
});
// Scale animation on click
holder.itemView.setOnClickListener(v -> {
v.animate().scaleX(0.9f).scaleY(0.9f).setDuration(80).withEndAction(() -> {
v.animate().scaleX(1f).scaleY(1f).setDuration(80).start();
screenTimeTracker.recordLaunch(app.getAppId());
launchApp(app);
}).start();
});
// Long-press context menu with pin/hide options
holder.itemView.setOnLongClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
if (!(context instanceof Activity)) return false;
Activity activity = (Activity) context;
boolean pinned = screenTimeTracker.isPinned(app.getAppId());
String pinLabel = pinned ? "Unpin" : "Pin";
String pinEmoji = pinned ? "" : "";
AeThexContextMenu.MenuItem[] items;
if (app.isSystemApp()) {
items = new AeThexContextMenu.MenuItem[] {
new AeThexContextMenu.MenuItem("open", "Open", ""),
new AeThexContextMenu.MenuItem("pin", pinLabel, pinEmoji),
new AeThexContextMenu.MenuItem("info", "App Info", ""),
};
} else {
items = new AeThexContextMenu.MenuItem[] {
new AeThexContextMenu.MenuItem("open", "Open", ""),
new AeThexContextMenu.MenuItem("pin", pinLabel, pinEmoji),
new AeThexContextMenu.MenuItem("hide", "Hide", ""),
new AeThexContextMenu.MenuItem("info", "App Info", ""),
};
}
AeThexContextMenu.show(activity, lastTouchX, lastTouchY,
app.getName().toUpperCase(), items, actionId -> {
switch (actionId) {
case "open":
launchApp(app);
break;
case "pin":
boolean newPinState = !screenTimeTracker.isPinned(app.getAppId());
screenTimeTracker.setPinned(app.getAppId(), newPinState);
AeThexToast.show(activity,
app.getName() + (newPinState ? " pinned" : " unpinned"),
AeThexToast.Type.SUCCESS);
if (appListChangedListener != null) appListChangedListener.onAppListChanged();
break;
case "hide":
screenTimeTracker.setHidden(app.getAppId(), true);
AeThexToast.show(activity,
app.getName() + " hidden (restore in Settings)",
AeThexToast.Type.INFO);
if (appListChangedListener != null) appListChangedListener.onAppListChanged();
break;
case "info":
int launches = screenTimeTracker.getLaunchCount(app.getAppId());
String cat = screenTimeTracker.categorize(app.getAppId());
String info = app.getName() + " · " + launches + " launches · " + cat;
if (app.getPackageName() != null) {
info += "\n" + app.getPackageName();
}
AeThexToast.show(activity, info, AeThexToast.Type.INFO);
break;
}
});
return true;
});
// Stagger fade-in animation for grid items
holder.itemView.setAlpha(0f);
holder.itemView.setTranslationY(20f);
holder.itemView.animate()
.alpha(1f)
.translationY(0f)
.setDuration(300)
.setStartDelay(position * 30L)
.start();
}
/**
* Launch the app either built-in AeThexOS activity or real Android app.
*/
private void launchApp(AppInfo app) {
// If it's a real Android app, launch via package manager
if (!app.isSystemApp() && app.getPackageName() != null) {
PackageManager pm = context.getPackageManager();
Intent launchIntent = pm.getLaunchIntentForPackage(app.getPackageName());
if (launchIntent != null) {
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
context.startActivity(launchIntent);
return;
}
}
// Built-in AeThexOS app
Intent intent;
switch (app.getAppId()) {
case "settings": intent = new Intent(context, SettingsActivity.class); break;
case "calculator": intent = new Intent(context, CalculatorActivity.class); break;
case "terminal": intent = new Intent(context, TerminalActivity.class); break;
case "clock": intent = new Intent(context, ClockActivity.class); break;
case "snake": intent = new Intent(context, SnakeActivity.class); break;
case "notes": intent = new Intent(context, NotesActivity.class); break;
case "weather": intent = new Intent(context, WeatherActivity.class); break;
case "minesweeper": intent = new Intent(context, MinesweeperActivity.class); break;
case "files": intent = new Intent(context, FileManagerActivity.class); break;
case "music": intent = new Intent(context, MusicActivity.class); break;
case "chat": intent = new Intent(context, ChatActivity.class); break;
case "photos": intent = new Intent(context, PhotosActivity.class); break;
case "browser": intent = new Intent(context, BrowserActivity.class); break;
case "passport": intent = new Intent(context, PassportActivity.class); break;
case "projects": intent = new Intent(context, ProjectsActivity.class); break;
case "marketplace": intent = new Intent(context, MarketplaceActivity.class); break;
case "analytics": intent = new Intent(context, AnalyticsActivity.class); break;
case "mail": intent = new Intent(context, MailActivity.class); break;
case "camera": intent = new Intent(context, CameraActivity.class); break;
case "achievements": intent = new Intent(context, AchievementsActivity.class); break;
default:
intent = new Intent(context, AppActivity.class);
intent.putExtra("app_name", app.getName());
intent.putExtra("app_id", app.getAppId());
break;
}
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
context.startActivity(intent);
if (context instanceof Activity) {
((Activity) context).overridePendingTransition(R.anim.slide_up_in, R.anim.scale_out);
}
}
@Override
public int getItemCount() {
return appList.size();
}
private int dpToPx(int dp) {
return (int) (dp * context.getResources().getDisplayMetrics().density);
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView appName;
ImageView appIcon;
ViewHolder(View itemView) {
super(itemView);
appName = itemView.findViewById(R.id.app_name_text_view);
appIcon = itemView.findViewById(R.id.app_icon_image_view);
}
}
}

View file

@ -0,0 +1,50 @@
package com.aethex.os;
import android.graphics.drawable.Drawable;
public class AppInfo {
private String name;
private int icon;
private String appId;
private Drawable drawableIcon; // For real Android app icons
private String packageName; // For real Android apps
private boolean isSystemApp; // AeThexOS built-in vs third-party
private boolean isPinned;
private boolean isHidden;
// Built-in AeThexOS app
public AppInfo(String name, int icon, String appId) {
this.name = name;
this.icon = icon;
this.appId = appId;
this.isSystemApp = true;
this.isPinned = false;
this.isHidden = false;
}
// Real Android app from PackageManager
public AppInfo(String name, Drawable drawableIcon, String appId, String packageName) {
this.name = name;
this.drawableIcon = drawableIcon;
this.appId = appId;
this.packageName = packageName;
this.icon = 0;
this.isSystemApp = false;
this.isPinned = false;
this.isHidden = false;
}
public String getName() { return name; }
public int getIcon() { return icon; }
public String getAppId() { return appId; }
public Drawable getDrawableIcon() { return drawableIcon; }
public String getPackageName() { return packageName; }
public boolean isSystemApp() { return isSystemApp; }
public boolean hasDrawableIcon() { return drawableIcon != null; }
public boolean isPinned() { return isPinned; }
public void setPinned(boolean pinned) { this.isPinned = pinned; }
public boolean isHidden() { return isHidden; }
public void setHidden(boolean hidden) { this.isHidden = hidden; }
}

View file

@ -0,0 +1,238 @@
package com.aethex.os;
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.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
/**
* ArcadeActivity - Retro game center for Pixel Arcade module.
* Launch classic games with AeThex styling.
*/
public class ArcadeActivity extends AppCompatActivity {
private ThemeManager themeManager;
private FrameLayout gameContainer;
private LinearLayout menuContainer;
private SnakeGame snakeGame;
private boolean inGame = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
hideSystemUI();
themeManager = new ThemeManager(this);
// Root container
FrameLayout root = new FrameLayout(this);
root.setBackgroundColor(Color.parseColor("#0A0C14"));
// Menu container
menuContainer = new LinearLayout(this);
menuContainer.setOrientation(LinearLayout.VERTICAL);
menuContainer.setGravity(Gravity.CENTER);
menuContainer.setPadding(dpToPx(24), dpToPx(40), dpToPx(24), dpToPx(24));
// Game container (for fullscreen games)
gameContainer = new FrameLayout(this);
gameContainer.setVisibility(View.GONE);
root.addView(menuContainer, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
root.addView(gameContainer, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(root);
buildMenu();
}
private void buildMenu() {
menuContainer.removeAllViews();
Typeface displayFont = themeManager.getDisplayFont(this);
Typeface monoFont = themeManager.getMonoFont(this);
int primaryColor = themeManager.getPrimaryColor(this);
// Back button
TextView backBtn = new TextView(this);
backBtn.setText("← ARCADE");
backBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
backBtn.setTextColor(Color.parseColor("#88FFFFFF"));
backBtn.setTypeface(monoFont);
backBtn.setGravity(Gravity.START);
backBtn.setPadding(0, 0, 0, dpToPx(20));
backBtn.setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
menuContainer.addView(backBtn);
// Title
TextView title = new TextView(this);
title.setText("PIXEL ARCADE");
title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 32);
title.setTextColor(primaryColor);
title.setTypeface(displayFont, Typeface.BOLD);
title.setGravity(Gravity.CENTER);
title.setLetterSpacing(0.1f);
LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
titleParams.bottomMargin = dpToPx(8);
titleParams.gravity = Gravity.CENTER;
title.setLayoutParams(titleParams);
menuContainer.addView(title);
// Subtitle
TextView subtitle = new TextView(this);
subtitle.setText("Retro games for agents");
subtitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
subtitle.setTextColor(Color.parseColor("#66FFFFFF"));
subtitle.setTypeface(monoFont);
subtitle.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams subParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
subParams.bottomMargin = dpToPx(40);
subParams.gravity = Gravity.CENTER;
subtitle.setLayoutParams(subParams);
menuContainer.addView(subtitle);
// Games list
menuContainer.addView(createGameCard("SNAKE", "Classic snake game\nSwipe to control, eat to grow",
"#22C55E", () -> launchSnake()));
menuContainer.addView(createGameCard("PONG", "Coming soon\nClassic paddle game",
"#66FFFFFF", null));
menuContainer.addView(createGameCard("TETRIS", "Coming soon\nBlock stacking puzzle",
"#66FFFFFF", null));
}
private LinearLayout createGameCard(String name, String desc, String accentColor, Runnable onLaunch) {
Typeface displayFont = themeManager.getDisplayFont(this);
Typeface monoFont = themeManager.getMonoFont(this);
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.VERTICAL);
card.setPadding(dpToPx(18), dpToPx(16), dpToPx(18), dpToPx(16));
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(dpToPx(12));
bg.setColor(Color.parseColor("#0DFFFFFF"));
int color = Color.parseColor(accentColor);
bg.setStroke(dpToPx(1), Color.argb(60, Color.red(color), Color.green(color), Color.blue(color)));
card.setBackground(bg);
LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
cardParams.bottomMargin = dpToPx(12);
card.setLayoutParams(cardParams);
// Name
TextView nameView = new TextView(this);
nameView.setText(name);
nameView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
nameView.setTextColor(Color.parseColor(accentColor));
nameView.setTypeface(displayFont, Typeface.BOLD);
card.addView(nameView);
// Description
TextView descView = new TextView(this);
descView.setText(desc);
descView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
descView.setTextColor(Color.parseColor("#88FFFFFF"));
descView.setTypeface(monoFont);
descView.setPadding(0, dpToPx(4), 0, 0);
card.addView(descView);
if (onLaunch != null) {
card.setOnClickListener(v -> {
v.animate().scaleX(0.97f).scaleY(0.97f).setDuration(80).withEndAction(() -> {
v.animate().scaleX(1f).scaleY(1f).setDuration(80).withEndAction(onLaunch).start();
}).start();
});
} else {
card.setAlpha(0.5f);
}
return card;
}
private void launchSnake() {
inGame = true;
menuContainer.setVisibility(View.GONE);
gameContainer.setVisibility(View.VISIBLE);
gameContainer.removeAllViews();
snakeGame = new SnakeGame(this);
gameContainer.addView(snakeGame, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
@Override
public void onBackPressed() {
if (inGame) {
inGame = false;
if (snakeGame != null) {
snakeGame.pause();
snakeGame = null;
}
gameContainer.removeAllViews();
gameContainer.setVisibility(View.GONE);
menuContainer.setVisibility(View.VISIBLE);
} else {
super.onBackPressed();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
}
}
@Override
protected void onPause() {
super.onPause();
if (snakeGame != null) snakeGame.pause();
}
@Override
protected void onResume() {
super.onResume();
if (snakeGame != null) snakeGame.resume();
}
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);
}
}
}

View file

@ -0,0 +1,216 @@
package com.aethex.os;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* Bottom Navigation Bar - Reusable component for AeThexOS apps.
* Shows Home, Projects, Chat, Marketplace, Settings
*/
public class BottomNavBar {
public static final String TAB_HOME = "home";
public static final String TAB_PROJECTS = "projects";
public static final String TAB_CHAT = "chat";
public static final String TAB_MARKETPLACE = "marketplace";
public static final String TAB_SETTINGS = "settings";
private static final int NAV_HEIGHT_DP = 60;
public interface OnTabSelectedListener {
void onTabSelected(String tabId);
}
/**
* Creates and attaches a bottom navigation bar to the activity.
*
* @param activity The activity to attach to
* @param rootLayout The root FrameLayout or parent ViewGroup
* @param activeTab Which tab is currently active (use TAB_* constants)
* @param listener Optional callback for tab selection
* @return The created nav bar view
*/
public static View attach(Activity activity, ViewGroup rootLayout, String activeTab, OnTabSelectedListener listener) {
Context context = activity;
ThemeManager themeManager = new ThemeManager(context);
int primaryColor = themeManager.getPrimaryColor(context);
Typeface monoFont = themeManager.getMonoFont(context);
// Create the nav bar container
LinearLayout navBar = new LinearLayout(context);
navBar.setOrientation(LinearLayout.HORIZONTAL);
navBar.setGravity(Gravity.CENTER);
// Glassmorphic background
GradientDrawable navBg = new GradientDrawable();
navBg.setColor(Color.parseColor("#E6000000")); // 90% black
navBg.setCornerRadii(new float[]{
dpToPx(context, 16), dpToPx(context, 16), // top corners
dpToPx(context, 16), dpToPx(context, 16),
0, 0, 0, 0 // bottom corners
});
navBg.setStroke(dpToPx(context, 1), Color.parseColor("#33FFFFFF"));
navBar.setBackground(navBg);
navBar.setElevation(dpToPx(context, 8));
// Layout params - fixed at bottom
FrameLayout.LayoutParams navParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
dpToPx(context, NAV_HEIGHT_DP)
);
navParams.gravity = Gravity.BOTTOM;
navBar.setLayoutParams(navParams);
// Add tabs
String[][] tabs = {
{TAB_HOME, "Home", ""},
{TAB_PROJECTS, "Projects", ""},
{TAB_CHAT, "Chat", ""},
{TAB_MARKETPLACE, "Market", ""},
{TAB_SETTINGS, "Settings", ""},
};
for (String[] tab : tabs) {
View tabView = createTab(context, tab[0], tab[1], tab[2],
tab[0].equals(activeTab), primaryColor, monoFont, () -> {
if (listener != null) {
listener.onTabSelected(tab[0]);
} else {
navigateTo(activity, tab[0]);
}
});
navBar.addView(tabView);
}
// Add to root layout
rootLayout.addView(navBar);
return navBar;
}
/**
* Simplified attach without listener - uses default navigation
*/
public static View attach(Activity activity, ViewGroup rootLayout, String activeTab) {
return attach(activity, rootLayout, activeTab, null);
}
private static View createTab(Context context, String id, String label, String icon,
boolean isActive, int primaryColor, Typeface font, Runnable onClick) {
LinearLayout tab = new LinearLayout(context);
tab.setOrientation(LinearLayout.VERTICAL);
tab.setGravity(Gravity.CENTER);
tab.setPadding(dpToPx(context, 8), dpToPx(context, 8), dpToPx(context, 8), dpToPx(context, 8));
LinearLayout.LayoutParams tabParams = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.MATCH_PARENT, 1f
);
tab.setLayoutParams(tabParams);
// Icon
TextView iconView = new TextView(context);
iconView.setText(icon);
iconView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
iconView.setGravity(Gravity.CENTER);
if (isActive) {
iconView.setTextColor(primaryColor);
} else {
iconView.setTextColor(Color.parseColor("#66FFFFFF"));
}
tab.addView(iconView);
// Label
TextView labelView = new TextView(context);
labelView.setText(label);
labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
labelView.setTypeface(font);
labelView.setGravity(Gravity.CENTER);
labelView.setLetterSpacing(0.05f);
if (isActive) {
labelView.setTextColor(primaryColor);
} else {
labelView.setTextColor(Color.parseColor("#66FFFFFF"));
}
LinearLayout.LayoutParams labelParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
);
labelParams.topMargin = dpToPx(context, 2);
labelView.setLayoutParams(labelParams);
tab.addView(labelView);
// Active indicator dot
if (isActive) {
View dot = new View(context);
GradientDrawable dotBg = new GradientDrawable();
dotBg.setShape(GradientDrawable.OVAL);
dotBg.setColor(primaryColor);
dot.setBackground(dotBg);
LinearLayout.LayoutParams dotParams = new LinearLayout.LayoutParams(
dpToPx(context, 4), dpToPx(context, 4)
);
dotParams.topMargin = dpToPx(context, 4);
dot.setLayoutParams(dotParams);
tab.addView(dot);
}
// Click handler
tab.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
onClick.run();
});
return tab;
}
private static void navigateTo(Activity activity, String tabId) {
Class<?> targetClass;
switch (tabId) {
case TAB_HOME:
targetClass = SystemActivity.class;
break;
case TAB_PROJECTS:
targetClass = ProjectsActivity.class;
break;
case TAB_CHAT:
targetClass = ChatActivity.class;
break;
case TAB_MARKETPLACE:
targetClass = MarketplaceActivity.class;
break;
case TAB_SETTINGS:
targetClass = SettingsActivity.class;
break;
default:
return;
}
// Don't navigate if already on this activity
if (activity.getClass().equals(targetClass)) {
return;
}
Intent intent = new Intent(activity, targetClass);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
activity.startActivity(intent);
activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
private static int dpToPx(Context context, int dp) {
return (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()
);
}
}

View file

@ -0,0 +1,978 @@
package com.aethex.os;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.res.ResourcesCompat;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class BrowserActivity extends AppCompatActivity {
private ThemeManager themeManager;
private EditText urlBar;
private ProgressBar progressBar;
private View divider;
private LinearLayout tabStrip;
private TextView navBack, navForward, securityIcon, bookmarkBtn;
private FrameLayout webViewContainer;
// Find-in-page
private LinearLayout findBar;
private EditText findInput;
private TextView findCount, findPrev, findNext, findClose;
// Tabs
private List<BrowserTab> tabs = new ArrayList<>();
private int activeTabIndex = -1;
// Bookmarks / History
private static final String PREFS_BROWSER = "aethex_browser";
private static final String KEY_BOOKMARKS = "bookmarks";
private static final String KEY_HISTORY = "history";
private static class BrowserTab {
WebView webView;
String title;
String url;
boolean isHome;
BrowserTab(WebView wv) {
this.webView = wv;
this.title = "New Tab";
this.url = "";
this.isHome = true;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_browser);
hideSystemUI();
themeManager = new ThemeManager(this);
// Bind views
urlBar = findViewById(R.id.browser_url_bar);
progressBar = findViewById(R.id.browser_progress);
divider = findViewById(R.id.browser_divider);
tabStrip = findViewById(R.id.browser_tab_strip);
navBack = findViewById(R.id.browser_nav_back);
navForward = findViewById(R.id.browser_nav_forward);
securityIcon = findViewById(R.id.browser_security_icon);
bookmarkBtn = findViewById(R.id.browser_bookmark_btn);
webViewContainer = findViewById(R.id.browser_webview_container);
// Find-in-page
findBar = findViewById(R.id.browser_find_bar);
findInput = findViewById(R.id.browser_find_input);
findCount = findViewById(R.id.browser_find_count);
findPrev = findViewById(R.id.browser_find_prev);
findNext = findViewById(R.id.browser_find_next);
findClose = findViewById(R.id.browser_find_close);
// Remove the XML WebView we manage WebViews per tab
WebView xmlWebView = findViewById(R.id.browser_webview);
if (xmlWebView != null) {
webViewContainer.removeView(xmlWebView);
xmlWebView.destroy();
}
// Close button
findViewById(R.id.browser_close_btn).setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLOSE);
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Navigation
navBack.setOnClickListener(v -> {
BrowserTab tab = activeTab();
if (tab != null && tab.webView.canGoBack()) {
tab.webView.goBack();
}
});
navForward.setOnClickListener(v -> {
BrowserTab tab = activeTab();
if (tab != null && tab.webView.canGoForward()) {
tab.webView.goForward();
}
});
// Refresh
findViewById(R.id.browser_refresh_btn).setOnClickListener(v -> {
BrowserTab tab = activeTab();
if (tab != null) {
if (tab.isHome) {
loadHomePage(tab);
} else {
tab.webView.reload();
}
}
});
// URL bar
urlBar.setSelectAllOnFocus(true);
urlBar.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_GO ||
(event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER
&& event.getAction() == KeyEvent.ACTION_DOWN)) {
navigateToInput();
return true;
}
return false;
});
// Bookmark toggle
bookmarkBtn.setOnClickListener(v -> toggleBookmark());
// Menu button
findViewById(R.id.browser_menu_btn).setOnClickListener(v -> showBrowserMenu());
// New tab button
findViewById(R.id.browser_new_tab_btn).setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
addNewTab(null);
});
// Find-in-page controls
setupFindInPage();
// Open first tab
String intentUrl = getIntent().getStringExtra("url");
addNewTab(intentUrl);
// Entrance animation
View root = findViewById(R.id.browser_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
AeThexKeyboard.attach(this);
}
//
// Tab Management
//
private void addNewTab(String url) {
WebView wv = createWebView();
BrowserTab tab = new BrowserTab(wv);
tabs.add(tab);
// Add to container but hide (will be shown by switchToTab)
wv.setVisibility(View.GONE);
// Insert before the find bar
webViewContainer.addView(wv, 0, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
switchToTab(tabs.size() - 1);
if (url != null && !url.isEmpty()) {
navigateTab(tab, url);
} else {
loadHomePage(tab);
}
}
private void switchToTab(int index) {
if (index < 0 || index >= tabs.size()) return;
// Hide current
if (activeTabIndex >= 0 && activeTabIndex < tabs.size()) {
tabs.get(activeTabIndex).webView.setVisibility(View.GONE);
}
activeTabIndex = index;
BrowserTab tab = tabs.get(index);
tab.webView.setVisibility(View.VISIBLE);
// Update URL bar
if (tab.isHome) {
urlBar.setText("");
urlBar.setHint("Search or enter URL");
securityIcon.setText("Æ");
securityIcon.setTextColor(themeManager.getPrimaryColor(this));
} else {
urlBar.setText(tab.url);
updateSecurityIcon(tab.url);
}
updateNavButtons();
updateBookmarkButton();
rebuildTabStrip();
}
private void closeTab(int index) {
if (tabs.size() <= 1) {
// Last tab just go home
loadHomePage(tabs.get(0));
return;
}
BrowserTab tab = tabs.get(index);
webViewContainer.removeView(tab.webView);
tab.webView.stopLoading();
tab.webView.destroy();
tabs.remove(index);
if (activeTabIndex >= tabs.size()) {
activeTabIndex = tabs.size() - 1;
} else if (activeTabIndex > index) {
activeTabIndex--;
} else if (activeTabIndex == index) {
activeTabIndex = Math.min(index, tabs.size() - 1);
}
switchToTab(activeTabIndex);
}
private BrowserTab activeTab() {
if (activeTabIndex >= 0 && activeTabIndex < tabs.size()) {
return tabs.get(activeTabIndex);
}
return null;
}
private void rebuildTabStrip() {
tabStrip.removeAllViews();
Typeface mono = ResourcesCompat.getFont(this, R.font.source_code_pro);
int primary = themeManager.getPrimaryColor(this);
for (int i = 0; i < tabs.size(); i++) {
final int tabIndex = i;
BrowserTab tab = tabs.get(i);
boolean isActive = (i == activeTabIndex);
LinearLayout tabView = new LinearLayout(this);
tabView.setOrientation(LinearLayout.HORIZONTAL);
tabView.setGravity(Gravity.CENTER_VERTICAL);
LinearLayout.LayoutParams tlp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, dpToPx(28));
tlp.setMarginEnd(dpToPx(2));
tabView.setLayoutParams(tlp);
tabView.setPadding(dpToPx(10), 0, dpToPx(4), 0);
GradientDrawable tabBg = new GradientDrawable();
tabBg.setCornerRadii(new float[]{
dpToPx(8), dpToPx(8), dpToPx(8), dpToPx(8), 0, 0, 0, 0});
if (isActive) {
tabBg.setColor(Color.parseColor("#1AFFFFFF"));
tabBg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
} else {
tabBg.setColor(Color.parseColor("#0DFFFFFF"));
}
tabView.setBackground(tabBg);
// Tab title
TextView title = new TextView(this);
String displayTitle = tab.title;
if (displayTitle.length() > 14) displayTitle = displayTitle.substring(0, 14) + "";
title.setText(displayTitle);
title.setTextSize(10f);
title.setTextColor(isActive ? Color.parseColor("#E0FFFFFF") : Color.parseColor("#66FFFFFF"));
title.setTypeface(mono);
title.setSingleLine(true);
tabView.addView(title);
// Close X on tab
TextView closeX = new TextView(this);
closeX.setText("×");
closeX.setTextSize(14f);
closeX.setTextColor(Color.parseColor("#4DFFFFFF"));
closeX.setPadding(dpToPx(6), 0, dpToPx(2), 0);
closeX.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
closeTab(tabIndex);
});
tabView.addView(closeX);
tabView.setOnClickListener(v -> {
if (tabIndex != activeTabIndex) {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
switchToTab(tabIndex);
}
});
tabStrip.addView(tabView);
}
}
//
// WebView Factory
//
private WebView createWebView() {
WebView wv = new WebView(this);
WebSettings settings = wv.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setBuiltInZoomControls(true);
settings.setDisplayZoomControls(false);
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
settings.setSupportMultipleWindows(false);
settings.setAllowFileAccess(false);
settings.setAllowContentAccess(false);
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
String ua = settings.getUserAgentString();
settings.setUserAgentString(ua + " AeThexBrowser/2.0");
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return false;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
BrowserTab tab = findTab(view);
if (tab != null && url != null && !url.startsWith("data:")) {
tab.url = url;
tab.isHome = false;
if (view == activeWebView()) {
urlBar.setText(url);
updateSecurityIcon(url);
}
}
updateNavButtons();
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
BrowserTab tab = findTab(view);
if (tab != null) {
if (url != null && !url.startsWith("data:")) {
tab.url = url;
tab.title = view.getTitle() != null ? view.getTitle() : url;
tab.isHome = false;
addToHistory(url, tab.title);
}
if (view == activeWebView()) {
if (url != null && url.startsWith("data:")) {
urlBar.setText("");
urlBar.setHint("Search or enter URL");
} else {
urlBar.setText(url);
}
updateBookmarkButton();
}
rebuildTabStrip();
}
updateNavButtons();
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
int primary = themeManager.getPrimaryColor(BrowserActivity.this);
String primaryHex = String.format("#%06X", (0xFFFFFF & primary));
String errorHtml = "<html><body style='background:#0A0E1A;color:#ff6b6b;font-family:monospace;padding:40px;text-align:center;'>"
+ "<div style='font-size:48px;margin-bottom:16px;'>⚠</div>"
+ "<h2 style='color:" + primaryHex + ";font-size:16px;'>CONNECTION FAILED</h2>"
+ "<p style='color:rgba(255,255,255,0.5);font-size:13px;margin-top:12px;'>Error " + errorCode + ": " + description + "</p>"
+ "<p style='color:rgba(255,255,255,0.2);font-size:11px;margin-top:16px;word-break:break-all;'>" + failingUrl + "</p>"
+ "</body></html>";
view.loadDataWithBaseURL(null, errorHtml, "text/html", "UTF-8", null);
}
});
wv.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (view == activeWebView()) {
if (newProgress < 100) {
progressBar.setVisibility(View.VISIBLE);
divider.setVisibility(View.GONE);
progressBar.setProgress(newProgress);
} else {
progressBar.setVisibility(View.GONE);
divider.setVisibility(View.VISIBLE);
}
}
}
@Override
public void onReceivedTitle(WebView view, String title) {
BrowserTab tab = findTab(view);
if (tab != null && title != null) {
tab.title = title;
rebuildTabStrip();
}
}
});
wv.setBackgroundColor(0xFF0A0E1A);
return wv;
}
private BrowserTab findTab(WebView wv) {
for (BrowserTab t : tabs) {
if (t.webView == wv) return t;
}
return null;
}
private WebView activeWebView() {
BrowserTab tab = activeTab();
return tab != null ? tab.webView : null;
}
//
// Navigation
//
private void navigateToInput() {
String input = urlBar.getText().toString().trim();
if (input.isEmpty()) return;
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
if (imm != null) imm.hideSoftInputFromWindow(urlBar.getWindowToken(), 0);
urlBar.clearFocus();
BrowserTab tab = activeTab();
if (tab != null) {
navigateTab(tab, input);
}
}
private void navigateTab(BrowserTab tab, String input) {
String url;
if (input.contains(".") && !input.contains(" ")) {
if (!input.startsWith("http://") && !input.startsWith("https://")) {
url = "https://" + input;
} else {
url = input;
}
} else {
url = "https://www.google.com/search?q=" + android.net.Uri.encode(input);
}
tab.isHome = false;
tab.url = url;
tab.webView.loadUrl(url);
}
private void updateNavButtons() {
BrowserTab tab = activeTab();
if (tab != null) {
navBack.setTextColor(tab.webView.canGoBack()
? Color.parseColor("#CCFFFFFF") : Color.parseColor("#33FFFFFF"));
navForward.setTextColor(tab.webView.canGoForward()
? Color.parseColor("#CCFFFFFF") : Color.parseColor("#33FFFFFF"));
}
}
private void updateSecurityIcon(String url) {
if (url == null || url.startsWith("data:")) {
securityIcon.setText("Æ");
securityIcon.setTextColor(themeManager.getPrimaryColor(this));
} else if (url.startsWith("https://")) {
securityIcon.setText("🔒");
securityIcon.setTextColor(Color.parseColor("#22C55E"));
} else {
securityIcon.setText("");
securityIcon.setTextColor(Color.parseColor("#EF4444"));
}
}
//
// Home Page
//
private void loadHomePage(BrowserTab tab) {
tab.title = "New Tab";
tab.url = "";
tab.isHome = true;
if (tab == activeTab()) {
urlBar.setText("");
urlBar.setHint("Search or enter URL");
securityIcon.setText("Æ");
securityIcon.setTextColor(themeManager.getPrimaryColor(this));
updateBookmarkButton();
}
int primaryInt = themeManager.getPrimaryColor(this);
String ph = String.format("#%06X", (0xFFFFFF & primaryInt));
// Build bookmarks grid HTML
List<String[]> bookmarks = getBookmarks();
StringBuilder bmHtml = new StringBuilder();
for (String[] bm : bookmarks) {
String initial = bm[0].length() > 0 ? bm[0].substring(0, 1).toUpperCase() : "?";
bmHtml.append("<a class='qlink' href='").append(bm[1]).append("'>")
.append("<div class='ql-icon'>").append(initial).append("</div>")
.append("<div class='ql-label'>").append(escapeHtml(bm[0])).append("</div>")
.append("</a>");
}
// If no bookmarks, add defaults
if (bookmarks.isEmpty()) {
bmHtml.append("<a class='qlink' href='https://www.google.com'><div class='ql-icon'>G</div><div class='ql-label'>Google</div></a>");
bmHtml.append("<a class='qlink' href='https://github.com'><div class='ql-icon'>⬡</div><div class='ql-label'>GitHub</div></a>");
bmHtml.append("<a class='qlink' href='https://wikipedia.org'><div class='ql-icon'>W</div><div class='ql-label'>Wikipedia</div></a>");
bmHtml.append("<a class='qlink' href='https://youtube.com'><div class='ql-icon'>▶</div><div class='ql-label'>YouTube</div></a>");
bmHtml.append("<a class='qlink' href='https://reddit.com'><div class='ql-icon'>R</div><div class='ql-label'>Reddit</div></a>");
bmHtml.append("<a class='qlink' href='https://twitter.com'><div class='ql-icon'>X</div><div class='ql-label'>X / Twitter</div></a>");
}
String html = "<!DOCTYPE html><html><head>"
+ "<meta name='viewport' content='width=device-width,initial-scale=1.0'>"
+ "<style>"
+ "*{margin:0;padding:0;box-sizing:border-box}"
+ "body{background:#0A0E1A;color:rgba(255,255,255,0.8);font-family:'Courier New',monospace;"
+ "display:flex;flex-direction:column;align-items:center;min-height:100vh;padding:48px 20px 40px}"
+ ".brand{font-size:32px;font-weight:200;color:" + ph + ";letter-spacing:4px;margin-bottom:4px}"
+ ".sub{font-size:10px;color:rgba(255,255,255,0.3);letter-spacing:3px;margin-bottom:32px;text-transform:uppercase}"
+ ".search-wrap{width:100%;max-width:480px;margin-bottom:40px}"
+ ".search-wrap input{width:100%;padding:14px 18px;border-radius:12px;"
+ "border:1px solid rgba(255,255,255,0.08);background:rgba(255,255,255,0.04);"
+ "color:rgba(255,255,255,0.8);font-size:14px;font-family:'Courier New',monospace;outline:none;"
+ "transition:border-color 0.2s}"
+ ".search-wrap input:focus{border-color:" + ph + "40}"
+ ".search-wrap input::placeholder{color:rgba(255,255,255,0.25)}"
+ ".sec-label{font-size:9px;color:rgba(255,255,255,0.25);letter-spacing:2px;text-transform:uppercase;"
+ "margin-bottom:14px;width:100%;max-width:480px}"
+ ".grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;width:100%;max-width:480px}"
+ ".qlink{background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.05);"
+ "border-radius:12px;padding:16px 8px;text-align:center;text-decoration:none;"
+ "transition:all 0.15s}"
+ ".qlink:active{background:" + ph + "15;border-color:" + ph + "30}"
+ ".ql-icon{font-size:20px;color:" + ph + ";margin-bottom:6px}"
+ ".ql-label{font-size:10px;color:rgba(255,255,255,0.5);font-family:'Courier New',monospace;"
+ "white-space:nowrap;overflow:hidden;text-overflow:ellipsis}"
+ ".footer{margin-top:auto;padding-top:32px;font-size:9px;color:rgba(255,255,255,0.15);"
+ "letter-spacing:1px}"
+ "</style></head><body>"
+ "<div class='brand'>Æ</div>"
+ "<div class='sub'>AeThex Browser</div>"
+ "<div class='search-wrap'>"
+ "<form onsubmit=\"doSearch(event)\">"
+ "<input id='si' type='text' placeholder='Search the web...' autocomplete='off'>"
+ "</form></div>"
+ "<div class='sec-label'>QUICK ACCESS</div>"
+ "<div class='grid'>" + bmHtml.toString() + "</div>"
+ "<div class='footer'>AeThex Corporation · Secure Connection</div>"
+ "<script>"
+ "function doSearch(e){e.preventDefault();var q=document.getElementById('si').value.trim();"
+ "if(q)window.location.href='https://www.google.com/search?q='+encodeURIComponent(q)}"
+ "</script></body></html>";
tab.webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);
rebuildTabStrip();
}
private String escapeHtml(String s) {
return s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
.replace("\"", "&quot;").replace("'", "&#39;");
}
//
// Bookmarks
//
private List<String[]> getBookmarks() {
SharedPreferences prefs = getSharedPreferences(PREFS_BROWSER, MODE_PRIVATE);
String json = prefs.getString(KEY_BOOKMARKS, "[]");
List<String[]> list = new ArrayList<>();
try {
JSONArray arr = new JSONArray(json);
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = arr.getJSONObject(i);
list.add(new String[]{obj.getString("title"), obj.getString("url")});
}
} catch (Exception ignored) {}
return list;
}
private void saveBookmarks(List<String[]> bookmarks) {
try {
JSONArray arr = new JSONArray();
for (String[] bm : bookmarks) {
JSONObject obj = new JSONObject();
obj.put("title", bm[0]);
obj.put("url", bm[1]);
arr.put(obj);
}
getSharedPreferences(PREFS_BROWSER, MODE_PRIVATE)
.edit().putString(KEY_BOOKMARKS, arr.toString()).apply();
} catch (Exception ignored) {}
}
private boolean isBookmarked(String url) {
if (url == null || url.isEmpty()) return false;
for (String[] bm : getBookmarks()) {
if (bm[1].equals(url)) return true;
}
return false;
}
private void toggleBookmark() {
BrowserTab tab = activeTab();
if (tab == null || tab.isHome || tab.url.isEmpty()) return;
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
List<String[]> bookmarks = getBookmarks();
if (isBookmarked(tab.url)) {
// Remove
bookmarks.removeIf(bm -> bm[1].equals(tab.url));
saveBookmarks(bookmarks);
AeThexToast.show(this, "Bookmark removed", AeThexToast.Type.INFO);
} else {
// Add
bookmarks.add(0, new String[]{tab.title, tab.url});
saveBookmarks(bookmarks);
AeThexToast.show(this, "Bookmarked", AeThexToast.Type.SUCCESS);
}
updateBookmarkButton();
}
private void updateBookmarkButton() {
BrowserTab tab = activeTab();
if (tab == null || tab.isHome) {
bookmarkBtn.setText("");
bookmarkBtn.setTextColor(Color.parseColor("#4DFFFFFF"));
return;
}
if (isBookmarked(tab.url)) {
bookmarkBtn.setText("");
bookmarkBtn.setTextColor(themeManager.getPrimaryColor(this));
} else {
bookmarkBtn.setText("");
bookmarkBtn.setTextColor(Color.parseColor("#4DFFFFFF"));
}
}
//
// History
//
private void addToHistory(String url, String title) {
if (url == null || url.startsWith("data:") || url.isEmpty()) return;
try {
SharedPreferences prefs = getSharedPreferences(PREFS_BROWSER, MODE_PRIVATE);
String json = prefs.getString(KEY_HISTORY, "[]");
JSONArray arr = new JSONArray(json);
// Remove duplicate
JSONArray filtered = new JSONArray();
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = arr.getJSONObject(i);
if (!obj.getString("url").equals(url)) {
filtered.put(obj);
}
}
// Add to front
JSONObject entry = new JSONObject();
entry.put("url", url);
entry.put("title", title != null ? title : url);
entry.put("time", System.currentTimeMillis());
JSONArray newArr = new JSONArray();
newArr.put(entry);
for (int i = 0; i < Math.min(filtered.length(), 99); i++) {
newArr.put(filtered.get(i));
}
prefs.edit().putString(KEY_HISTORY, newArr.toString()).apply();
} catch (Exception ignored) {}
}
private List<String[]> getHistory() {
SharedPreferences prefs = getSharedPreferences(PREFS_BROWSER, MODE_PRIVATE);
String json = prefs.getString(KEY_HISTORY, "[]");
List<String[]> list = new ArrayList<>();
try {
JSONArray arr = new JSONArray(json);
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = arr.getJSONObject(i);
list.add(new String[]{obj.getString("title"), obj.getString("url")});
}
} catch (Exception ignored) {}
return list;
}
//
// Browser Menu
//
private void showBrowserMenu() {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
AeThexContextMenu.MenuItem[] items = new AeThexContextMenu.MenuItem[] {
new AeThexContextMenu.MenuItem("home", "Home", ""),
new AeThexContextMenu.MenuItem("newtab", "New Tab", "+"),
new AeThexContextMenu.MenuItem("bookmarks", "Bookmarks", ""),
new AeThexContextMenu.MenuItem("history", "History", ""),
new AeThexContextMenu.MenuItem("find", "Find in Page", ""),
new AeThexContextMenu.MenuItem("clear", "Clear Data", ""),
};
// Position menu at top-right of screen
float x = getResources().getDisplayMetrics().widthPixels - dpToPx(16);
float y = dpToPx(90);
AeThexContextMenu.show(this, x, y, "BROWSER", items, actionId -> {
switch (actionId) {
case "home":
BrowserTab tab = activeTab();
if (tab != null) loadHomePage(tab);
break;
case "newtab":
addNewTab(null);
break;
case "bookmarks":
showBookmarksPage();
break;
case "history":
showHistoryPage();
break;
case "find":
showFindBar();
break;
case "clear":
clearBrowsingData();
break;
}
});
}
private void showBookmarksPage() {
List<String[]> bookmarks = getBookmarks();
int primaryInt = themeManager.getPrimaryColor(this);
String ph = String.format("#%06X", (0xFFFFFF & primaryInt));
StringBuilder rows = new StringBuilder();
if (bookmarks.isEmpty()) {
rows.append("<div style='text-align:center;color:rgba(255,255,255,0.3);padding:40px;font-size:13px'>No bookmarks yet</div>");
} else {
for (String[] bm : bookmarks) {
rows.append("<a class='item' href='").append(escapeHtml(bm[1])).append("'>")
.append("<div class='title'>").append(escapeHtml(bm[0])).append("</div>")
.append("<div class='url'>").append(escapeHtml(bm[1])).append("</div>")
.append("</a>");
}
}
String html = "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width,initial-scale=1.0'>"
+ "<style>*{margin:0;padding:0;box-sizing:border-box}"
+ "body{background:#0A0E1A;color:#fff;font-family:'Courier New',monospace;padding:24px 16px}"
+ "h1{font-size:12px;color:rgba(255,255,255,0.4);letter-spacing:2px;margin-bottom:16px}"
+ ".item{display:block;padding:14px;border-radius:10px;background:rgba(255,255,255,0.03);"
+ "border:1px solid rgba(255,255,255,0.05);margin-bottom:8px;text-decoration:none;"
+ "transition:all 0.15s}"
+ ".item:active{background:" + ph + "15;border-color:" + ph + "30}"
+ ".title{font-size:13px;color:rgba(255,255,255,0.8);margin-bottom:4px}"
+ ".url{font-size:10px;color:" + ph + ";overflow:hidden;text-overflow:ellipsis;white-space:nowrap}"
+ "</style></head><body><h1>★ BOOKMARKS</h1>" + rows.toString() + "</body></html>";
BrowserTab tab = activeTab();
if (tab != null) {
tab.title = "Bookmarks";
tab.isHome = true;
tab.webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);
urlBar.setText("");
urlBar.setHint("Bookmarks");
rebuildTabStrip();
}
}
private void showHistoryPage() {
List<String[]> history = getHistory();
int primaryInt = themeManager.getPrimaryColor(this);
String ph = String.format("#%06X", (0xFFFFFF & primaryInt));
StringBuilder rows = new StringBuilder();
if (history.isEmpty()) {
rows.append("<div style='text-align:center;color:rgba(255,255,255,0.3);padding:40px;font-size:13px'>No history yet</div>");
} else {
int max = Math.min(history.size(), 50);
for (int i = 0; i < max; i++) {
String[] h = history.get(i);
rows.append("<a class='item' href='").append(escapeHtml(h[1])).append("'>")
.append("<div class='title'>").append(escapeHtml(h[0])).append("</div>")
.append("<div class='url'>").append(escapeHtml(h[1])).append("</div>")
.append("</a>");
}
}
String html = "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width,initial-scale=1.0'>"
+ "<style>*{margin:0;padding:0;box-sizing:border-box}"
+ "body{background:#0A0E1A;color:#fff;font-family:'Courier New',monospace;padding:24px 16px}"
+ "h1{font-size:12px;color:rgba(255,255,255,0.4);letter-spacing:2px;margin-bottom:16px}"
+ ".item{display:block;padding:14px;border-radius:10px;background:rgba(255,255,255,0.03);"
+ "border:1px solid rgba(255,255,255,0.05);margin-bottom:8px;text-decoration:none;transition:all 0.15s}"
+ ".item:active{background:" + ph + "15;border-color:" + ph + "30}"
+ ".title{font-size:13px;color:rgba(255,255,255,0.8);margin-bottom:4px}"
+ ".url{font-size:10px;color:" + ph + ";overflow:hidden;text-overflow:ellipsis;white-space:nowrap}"
+ "</style></head><body><h1>↺ HISTORY</h1>" + rows.toString() + "</body></html>";
BrowserTab tab = activeTab();
if (tab != null) {
tab.title = "History";
tab.isHome = true;
tab.webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);
urlBar.setText("");
urlBar.setHint("History");
rebuildTabStrip();
}
}
private void clearBrowsingData() {
BrowserTab tab = activeTab();
if (tab != null) {
tab.webView.clearCache(true);
tab.webView.clearHistory();
}
getSharedPreferences(PREFS_BROWSER, MODE_PRIVATE)
.edit().remove(KEY_HISTORY).apply();
AeThexToast.show(this, "Browsing data cleared", AeThexToast.Type.SUCCESS);
}
//
// Find in Page
//
private void setupFindInPage() {
findInput.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
BrowserTab tab = activeTab();
if (tab != null) {
tab.webView.findAllAsync(s.toString());
}
}
});
findInput.setOnEditorActionListener((v, actionId, event) -> {
BrowserTab tab = activeTab();
if (tab != null) tab.webView.findNext(true);
return true;
});
findNext.setOnClickListener(v -> {
BrowserTab tab = activeTab();
if (tab != null) tab.webView.findNext(true);
});
findPrev.setOnClickListener(v -> {
BrowserTab tab = activeTab();
if (tab != null) tab.webView.findNext(false);
});
findClose.setOnClickListener(v -> hideFindBar());
}
private void showFindBar() {
findBar.setVisibility(View.VISIBLE);
findInput.setText("");
findInput.requestFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
if (imm != null) imm.showSoftInput(findInput, 0);
}
private void hideFindBar() {
findBar.setVisibility(View.GONE);
findInput.setText("");
BrowserTab tab = activeTab();
if (tab != null) tab.webView.clearMatches();
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
if (imm != null) imm.hideSoftInputFromWindow(findInput.getWindowToken(), 0);
}
//
// Lifecycle
//
@SuppressWarnings("deprecation")
@Override
public void onBackPressed() {
if (findBar.getVisibility() == View.VISIBLE) {
hideFindBar();
return;
}
BrowserTab tab = activeTab();
if (tab != null && tab.webView.canGoBack()) {
tab.webView.goBack();
} else {
super.onBackPressed();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
}
}
@Override
protected void onDestroy() {
for (BrowserTab tab : tabs) {
if (tab.webView != null) {
tab.webView.stopLoading();
tab.webView.destroy();
}
}
tabs.clear();
super.onDestroy();
}
@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);
}
}
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
}

View file

@ -0,0 +1,187 @@
package com.aethex.os;
import android.os.Bundle;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class CalculatorActivity extends AppCompatActivity {
private ThemeManager themeManager;
private TextView display;
private TextView expression;
private StringBuilder currentInput = new StringBuilder();
private String operator = "";
private double operand1 = 0;
private boolean newNumber = true;
private String fullExpression = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calculator);
hideSystemUI();
themeManager = new ThemeManager(this);
display = findViewById(R.id.calc_display);
expression = findViewById(R.id.calc_expression);
findViewById(R.id.calc_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Number buttons
int[] numIds = {R.id.btn_0, R.id.btn_1, R.id.btn_2, R.id.btn_3,
R.id.btn_4, R.id.btn_5, R.id.btn_6, R.id.btn_7,
R.id.btn_8, R.id.btn_9};
for (int i = 0; i < numIds.length; i++) {
final int num = i;
findViewById(numIds[i]).setOnClickListener(v -> appendDigit(String.valueOf(num)));
}
// Decimal
findViewById(R.id.btn_decimal).setOnClickListener(v -> {
if (newNumber) {
currentInput = new StringBuilder("0.");
newNumber = false;
} else if (!currentInput.toString().contains(".")) {
currentInput.append(".");
}
display.setText(currentInput.toString());
});
// Operators
findViewById(R.id.btn_add).setOnClickListener(v -> setOperator("+"));
findViewById(R.id.btn_subtract).setOnClickListener(v -> setOperator("-"));
findViewById(R.id.btn_multiply).setOnClickListener(v -> setOperator("×"));
findViewById(R.id.btn_divide).setOnClickListener(v -> setOperator("÷"));
// Equals
findViewById(R.id.btn_equals).setOnClickListener(v -> calculate());
// Clear
findViewById(R.id.btn_clear).setOnClickListener(v -> {
currentInput = new StringBuilder();
operator = "";
operand1 = 0;
newNumber = true;
fullExpression = "";
display.setText("0");
expression.setText("");
});
// Negate
findViewById(R.id.btn_negate).setOnClickListener(v -> {
if (currentInput.length() > 0) {
double val = Double.parseDouble(currentInput.toString());
val = -val;
currentInput = new StringBuilder(formatNumber(val));
display.setText(currentInput.toString());
}
});
// Parentheses (append to expression)
findViewById(R.id.btn_paren_open).setOnClickListener(v -> appendDigit("("));
findViewById(R.id.btn_paren_close).setOnClickListener(v -> appendDigit(")"));
}
private void appendDigit(String digit) {
if (newNumber) {
currentInput = new StringBuilder(digit);
newNumber = false;
} else {
currentInput.append(digit);
}
display.setText(currentInput.toString());
}
private void setOperator(String op) {
if (currentInput.length() > 0) {
if (!operator.isEmpty()) {
calculate();
}
try {
operand1 = Double.parseDouble(currentInput.toString());
} catch (NumberFormatException e) {
return;
}
fullExpression = formatNumber(operand1) + " " + op;
expression.setText(fullExpression);
operator = op;
newNumber = true;
}
}
private void calculate() {
if (operator.isEmpty() || currentInput.length() == 0) return;
double operand2;
try {
operand2 = Double.parseDouble(currentInput.toString());
} catch (NumberFormatException e) {
return;
}
double result = 0;
switch (operator) {
case "+": result = operand1 + operand2; break;
case "-": result = operand1 - operand2; break;
case "×": result = operand1 * operand2; break;
case "÷":
if (operand2 == 0) {
display.setText("Error");
expression.setText("");
currentInput = new StringBuilder();
operator = "";
newNumber = true;
return;
}
result = operand1 / operand2;
break;
}
fullExpression = formatNumber(operand1) + " " + operator + " " + formatNumber(operand2) + " =";
expression.setText(fullExpression);
currentInput = new StringBuilder(formatNumber(result));
display.setText(currentInput.toString());
operator = "";
operand1 = result;
newNumber = true;
}
private String formatNumber(double num) {
if (num == (long) num) {
return String.valueOf((long) num);
}
return String.valueOf(num);
}
@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);
}
}
}

View file

@ -0,0 +1,390 @@
package com.aethex.os;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.graphics.Bitmap;
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.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
public class CameraActivity extends AppCompatActivity {
private ThemeManager themeManager;
private LinearLayout galleryGrid;
private FrameLayout viewfinderFrame;
private boolean inViewfinder = true;
private Random random = new Random();
private Typeface monoFont;
private Typeface displayFont;
private int photoCount = 0;
// Simulated gallery of gradient "photos"
private static final int[][] GALLERY_GRADIENTS = {
{0xFF1a1a2e, 0xFF16213e, 0xFF0f3460},
{0xFF2d132c, 0xFF801336, 0xFFc72c41},
{0xFF0d7377, 0xFF14a3c7, 0xFF32e0c4},
{0xFF1b0a2e, 0xFF4a1942, 0xFF8b2fc9},
{0xFF0a1628, 0xFF0d3b66, 0xFF23689b},
{0xFF1a0505, 0xFF6b0f1a, 0xFFb91c1c},
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app);
hideSystemUI();
themeManager = new ThemeManager(this);
monoFont = themeManager.getMonoFont(this);
displayFont = themeManager.getDisplayFont(this);
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
TextView title = findViewById(R.id.app_title);
title.setText("Camera");
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);
buildCameraUI(content);
View root = findViewById(R.id.app_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
}
private void buildCameraUI(LinearLayout parent) {
// Viewfinder area (simulated camera preview)
viewfinderFrame = new FrameLayout(this);
LinearLayout.LayoutParams vfP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f);
viewfinderFrame.setLayoutParams(vfP);
// Dark gradient simulating live preview
GradientDrawable previewBg = new GradientDrawable(
GradientDrawable.Orientation.TL_BR,
new int[]{Color.parseColor("#0a0a1a"), Color.parseColor("#111827"), Color.parseColor("#0a0a1a")});
viewfinderFrame.setBackground(previewBg);
// Viewfinder crosshair overlay
LinearLayout crosshair = new LinearLayout(this);
crosshair.setOrientation(LinearLayout.VERTICAL);
crosshair.setGravity(Gravity.CENTER);
FrameLayout.LayoutParams chP = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
crosshair.setLayoutParams(chP);
// Center focus brackets
TextView focusBrackets = new TextView(this);
focusBrackets.setText("┌─────────────┐\n│ │\n│ │\n│ │\n└─────────────┘");
focusBrackets.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
focusBrackets.setTextColor(Color.parseColor("#4DFFFFFF"));
focusBrackets.setTypeface(monoFont);
focusBrackets.setGravity(Gravity.CENTER);
crosshair.addView(focusBrackets);
// Camera info overlay
TextView cameraInfo = new TextView(this);
cameraInfo.setText("AeThex Camera • AUTO");
cameraInfo.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
cameraInfo.setTextColor(Color.parseColor("#33FFFFFF"));
cameraInfo.setTypeface(monoFont);
cameraInfo.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams ciP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ciP.topMargin = dpToPx(12);
cameraInfo.setLayoutParams(ciP);
crosshair.addView(cameraInfo);
viewfinderFrame.addView(crosshair);
// Top-left info
TextView topInfo = new TextView(this);
topInfo.setText("f/1.8 1/60s ISO 400");
topInfo.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
topInfo.setTextColor(Color.parseColor("#4DFFFFFF"));
topInfo.setTypeface(monoFont);
topInfo.setPadding(dpToPx(16), dpToPx(12), 0, 0);
FrameLayout.LayoutParams tiP = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
tiP.gravity = Gravity.TOP | Gravity.START;
topInfo.setLayoutParams(tiP);
viewfinderFrame.addView(topInfo);
parent.addView(viewfinderFrame);
// Controls bar
LinearLayout controls = new LinearLayout(this);
controls.setOrientation(LinearLayout.HORIZONTAL);
controls.setGravity(Gravity.CENTER);
controls.setPadding(dpToPx(20), dpToPx(16), dpToPx(20), dpToPx(16));
controls.setBackgroundColor(Color.parseColor("#E6050510"));
// Gallery button (left)
TextView galleryBtn = new TextView(this);
galleryBtn.setText("Gallery");
galleryBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
galleryBtn.setTextColor(Color.parseColor("#99FFFFFF"));
galleryBtn.setTypeface(monoFont);
galleryBtn.setPadding(dpToPx(14), dpToPx(8), dpToPx(14), dpToPx(8));
GradientDrawable galBg = new GradientDrawable();
galBg.setCornerRadius(dpToPx(8));
galBg.setColor(Color.parseColor("#1AFFFFFF"));
galleryBtn.setBackground(galBg);
galleryBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
showGallery();
});
controls.addView(galleryBtn);
View spacer = new View(this);
spacer.setLayoutParams(new LinearLayout.LayoutParams(0, 1, 1f));
controls.addView(spacer);
// Shutter button (center)
FrameLayout shutterOuter = new FrameLayout(this);
int outerSize = dpToPx(64);
shutterOuter.setLayoutParams(new LinearLayout.LayoutParams(outerSize, outerSize));
GradientDrawable outerRing = new GradientDrawable();
outerRing.setShape(GradientDrawable.OVAL);
outerRing.setStroke(dpToPx(3), Color.WHITE);
outerRing.setColor(Color.TRANSPARENT);
shutterOuter.setBackground(outerRing);
View shutterInner = new View(this);
int innerSize = dpToPx(52);
FrameLayout.LayoutParams innerP = new FrameLayout.LayoutParams(innerSize, innerSize);
innerP.gravity = Gravity.CENTER;
shutterInner.setLayoutParams(innerP);
GradientDrawable innerBg = new GradientDrawable();
innerBg.setShape(GradientDrawable.OVAL);
innerBg.setColor(Color.WHITE);
shutterInner.setBackground(innerBg);
shutterOuter.addView(shutterInner);
shutterOuter.setOnClickListener(v -> takePhoto());
controls.addView(shutterOuter);
View spacer2 = new View(this);
spacer2.setLayoutParams(new LinearLayout.LayoutParams(0, 1, 1f));
controls.addView(spacer2);
// Switch camera button (right)
TextView switchBtn = new TextView(this);
switchBtn.setText("⟳ Flip");
switchBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
switchBtn.setTextColor(Color.parseColor("#99FFFFFF"));
switchBtn.setTypeface(monoFont);
switchBtn.setPadding(dpToPx(14), dpToPx(8), dpToPx(14), dpToPx(8));
GradientDrawable swBg = new GradientDrawable();
swBg.setCornerRadius(dpToPx(8));
swBg.setColor(Color.parseColor("#1AFFFFFF"));
switchBtn.setBackground(swBg);
switchBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
AeThexToast.show(this, "Camera flipped", AeThexToast.Type.INFO);
});
controls.addView(switchBtn);
parent.addView(controls);
}
private void takePhoto() {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
photoCount++;
// Flash effect
View flash = new View(this);
flash.setBackgroundColor(Color.WHITE);
flash.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
viewfinderFrame.addView(flash);
flash.setAlpha(0.8f);
flash.animate().alpha(0f).setDuration(300).withEndAction(() -> {
viewfinderFrame.removeView(flash);
}).start();
AeThexToast.show(this, "Photo captured (" + photoCount + ")", AeThexToast.Type.SUCCESS);
}
private void showGallery() {
FrameLayout root = (FrameLayout) findViewById(R.id.app_root);
FrameLayout overlay = new FrameLayout(this);
overlay.setTag("gallery_overlay");
overlay.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
overlay.setBackgroundColor(Color.parseColor("#F2080810"));
LinearLayout panel = new LinearLayout(this);
panel.setOrientation(LinearLayout.VERTICAL);
panel.setPadding(dpToPx(16), dpToPx(16), dpToPx(16), dpToPx(16));
FrameLayout.LayoutParams pp = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
panel.setLayoutParams(pp);
// Header
LinearLayout hdr = new LinearLayout(this);
hdr.setGravity(Gravity.CENTER_VERTICAL);
TextView backBtn = new TextView(this);
backBtn.setText("← Camera");
backBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
backBtn.setTextColor(themeManager.getPrimaryColor(this));
backBtn.setTypeface(monoFont);
backBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
root.removeView(overlay);
});
hdr.addView(backBtn);
View sp = new View(this);
sp.setLayoutParams(new LinearLayout.LayoutParams(0, 1, 1f));
hdr.addView(sp);
TextView countTv = new TextView(this);
countTv.setText((GALLERY_GRADIENTS.length + photoCount) + " photos");
countTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
countTv.setTextColor(Color.parseColor("#66FFFFFF"));
countTv.setTypeface(monoFont);
hdr.addView(countTv);
panel.addView(hdr);
// Label
TextView label = new TextView(this);
label.setText("GALLERY");
label.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
label.setTextColor(Color.parseColor("#66FFFFFF"));
label.setTypeface(monoFont);
label.setLetterSpacing(0.15f);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.topMargin = dpToPx(16);
lp.bottomMargin = dpToPx(12);
label.setLayoutParams(lp);
panel.addView(label);
// Photo grid (2 columns)
ScrollView scroll = new ScrollView(this);
scroll.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
LinearLayout grid = new LinearLayout(this);
grid.setOrientation(LinearLayout.VERTICAL);
int totalPhotos = GALLERY_GRADIENTS.length + photoCount;
for (int i = 0; i < totalPhotos; i += 2) {
LinearLayout row = new LinearLayout(this);
row.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams rowP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rowP.bottomMargin = dpToPx(6);
row.setLayoutParams(rowP);
for (int j = 0; j < 2 && (i + j) < totalPhotos; j++) {
FrameLayout cell = new FrameLayout(this);
LinearLayout.LayoutParams cellP = new LinearLayout.LayoutParams(0, dpToPx(120), 1f);
if (j == 0) cellP.setMarginEnd(dpToPx(3));
else cellP.setMarginStart(dpToPx(3));
cell.setLayoutParams(cellP);
int idx = (i + j) % GALLERY_GRADIENTS.length;
GradientDrawable grad = new GradientDrawable(
GradientDrawable.Orientation.TL_BR,
GALLERY_GRADIENTS[idx]);
grad.setCornerRadius(dpToPx(8));
cell.setBackground(grad);
// Date label
TextView dateLbl = new TextView(this);
dateLbl.setText("IMG_" + String.format("%04d", i + j + 1));
dateLbl.setTextSize(TypedValue.COMPLEX_UNIT_SP, 8);
dateLbl.setTextColor(Color.parseColor("#80FFFFFF"));
dateLbl.setTypeface(monoFont);
dateLbl.setPadding(dpToPx(6), 0, 0, dpToPx(4));
FrameLayout.LayoutParams dlP = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
dlP.gravity = Gravity.BOTTOM | Gravity.START;
dateLbl.setLayoutParams(dlP);
cell.addView(dateLbl);
row.addView(cell);
}
grid.addView(row);
}
scroll.addView(grid);
panel.addView(scroll);
overlay.addView(panel);
overlay.setAlpha(0f);
root.addView(overlay);
overlay.animate().alpha(1f).setDuration(200).start();
}
@SuppressWarnings("deprecation")
@Override
public void onBackPressed() {
FrameLayout root = (FrameLayout) findViewById(R.id.app_root);
View gallery = root.findViewWithTag("gallery_overlay");
if (gallery != null) {
root.removeView(gallery);
} else {
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);
}
}
}

View file

@ -0,0 +1,277 @@
package com.aethex.os;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Random;
public class ChatActivity extends AppCompatActivity {
private ThemeManager themeManager;
private RecyclerView recyclerView;
private ChatAdapter adapter;
private EditText inputField;
private final List<Message> messages = new ArrayList<>();
private final Handler handler = new Handler(Looper.getMainLooper());
private final Random random = new Random();
private static final String[] AI_RESPONSES = {
"Processing your request through the AeThex neural core...",
"Interesting query. Let me analyze that for you.",
"The AeThex system is operating at optimal capacity.",
"I've updated my knowledge base with that information.",
"Running diagnostics... All systems nominal.",
"That's an excellent observation. The data corroborates your findings.",
"I've cross-referenced the AeThex database. Here's what I found...",
"System resources are currently at 67% utilization.",
"Your clearance level grants access to this information.",
"Acknowledged. I'll process that through the quantum mesh network."
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
hideSystemUI();
themeManager = new ThemeManager(this);
// Back button
findViewById(R.id.chat_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Entrance animation
View root = findViewById(R.id.chat_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
// Set up RecyclerView
recyclerView = findViewById(R.id.chat_recycler);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(layoutManager);
adapter = new ChatAdapter();
recyclerView.setAdapter(adapter);
// Set up input
inputField = findViewById(R.id.chat_input);
TextView sendButton = findViewById(R.id.chat_send);
sendButton.setOnClickListener(v -> sendMessage());
inputField.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEND ||
(event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER
&& event.getAction() == KeyEvent.ACTION_DOWN)) {
sendMessage();
return true;
}
return false;
});
// Pre-populated conversation
loadInitialConversation();
// Custom themed keyboard
AeThexKeyboard.attach(this);
// Add bottom navigation bar
BottomNavBar.attach(this, (ViewGroup) findViewById(R.id.chat_root), BottomNavBar.TAB_CHAT);
}
private void loadInitialConversation() {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());
// Create timestamps that look like they happened earlier
long now = System.currentTimeMillis();
String time1 = formatTime(now - 300000); // 5 min ago
String time2 = formatTime(now - 240000); // 4 min ago
String time3 = formatTime(now - 180000); // 3 min ago
messages.add(new Message(
"Welcome to AeThex OS. I'm your system assistant. How can I help you today?",
false, time1));
messages.add(new Message(
"What can you do?",
true, time2));
messages.add(new Message(
"I can help with system navigation, app recommendations, and general queries. I'm always learning and expanding my capabilities within the AeThex ecosystem.",
false, time3));
adapter.notifyDataSetChanged();
scrollToBottom();
}
private void sendMessage() {
String text = inputField.getText().toString().trim();
if (text.isEmpty()) return;
// Add user message
String timestamp = formatTime(System.currentTimeMillis());
messages.add(new Message(text, true, timestamp));
adapter.notifyItemInserted(messages.size() - 1);
scrollToBottom();
inputField.setText("");
// AI auto-reply after 1 second delay
handler.postDelayed(() -> {
String reply = AI_RESPONSES[random.nextInt(AI_RESPONSES.length)];
String replyTime = formatTime(System.currentTimeMillis());
messages.add(new Message(reply, false, replyTime));
adapter.notifyItemInserted(messages.size() - 1);
scrollToBottom();
}, 1000);
}
private String formatTime(long millis) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm", Locale.getDefault());
return sdf.format(new Date(millis));
}
private void scrollToBottom() {
recyclerView.post(() -> {
if (messages.size() > 0) {
recyclerView.smoothScrollToPosition(messages.size() - 1);
}
});
}
// Inner classes
private static class Message {
final String text;
final boolean isUser;
final String timestamp;
Message(String text, boolean isUser, String timestamp) {
this.text = text;
this.isUser = isUser;
this.timestamp = timestamp;
}
}
private class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ViewHolder> {
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_chat_message, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Message msg = messages.get(position);
holder.bind(msg);
}
@Override
public int getItemCount() {
return messages.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private final LinearLayout bubble;
private final TextView textView;
private final TextView timeView;
ViewHolder(@NonNull View itemView) {
super(itemView);
bubble = itemView.findViewById(R.id.msg_bubble);
textView = itemView.findViewById(R.id.msg_text);
timeView = itemView.findViewById(R.id.msg_time);
}
void bind(Message msg) {
textView.setText(msg.text);
timeView.setText(msg.timestamp);
// Create rounded background programmatically
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(dpToPx(16));
// Set bubble alignment and colors based on sender
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) bubble.getLayoutParams();
if (msg.isUser) {
// User message: right-aligned, primary-tinted
params.gravity = Gravity.END;
int primary = themeManager.getPrimaryColor(ChatActivity.this);
bg.setColor((primary & 0x00FFFFFF) | 0x20000000);
textView.setTextColor(0xCCFFFFFF); // text_white_80
timeView.setTextColor(0x66FFFFFF); // text_white_40
timeView.setGravity(Gravity.END);
} else {
// AI message: left-aligned, white-10%
params.gravity = Gravity.START;
bg.setColor(0x1AFFFFFF);
textView.setTextColor(0xCCFFFFFF); // text_white_80
timeView.setTextColor(0x66FFFFFF); // text_white_40
timeView.setGravity(Gravity.START);
}
bubble.setLayoutParams(params);
bubble.setBackground(bg);
}
}
}
private int dpToPx(int dp) {
float density = getResources().getDisplayMetrics().density;
return Math.round(dp * density);
}
// System UI
@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);
}
}
}

View file

@ -0,0 +1,244 @@
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()));
}
}

View file

@ -0,0 +1,605 @@
package com.aethex.os;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Looper;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.ViewFlipper;
import androidx.appcompat.app.AppCompatActivity;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
public class ClockActivity extends AppCompatActivity {
private ThemeManager themeManager;
// --- Tab views ---
private TextView tabClock, tabStopwatch, tabTimer;
private View tabIndicator;
private ViewFlipper viewFlipper;
// --- Clock tab ---
private TextView clockTimeDisplay, clockSecondsDisplay, clockAmPm;
private TextView clockDateDisplay, clockTimezone;
private Handler clockHandler;
// --- Stopwatch tab ---
private TextView stopwatchDisplay;
private TextView stopwatchStart, stopwatchReset, stopwatchLap;
private LinearLayout lapContainer;
private LinearLayout lapHeader;
private ScrollView lapScroll;
private Handler stopwatchHandler;
private long stopwatchStartTime = 0;
private long stopwatchElapsed = 0;
private boolean stopwatchRunning = false;
private List<Long> lapTimes = new ArrayList<>();
private long lastLapTime = 0;
// --- Timer tab ---
private TextView timerHours, timerMinutes, timerSeconds;
private TextView timerCountdownDisplay, timerProgressLabel;
private LinearLayout timerInputArea;
private TextView timerStart, timerReset;
private int timerH = 0, timerM = 5, timerS = 0;
private CountDownTimer countDownTimer;
private long timerRemainingMs = 0;
private long timerTotalMs = 0;
private boolean timerRunning = false;
private boolean timerPaused = false;
private int currentTab = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clock);
hideSystemUI();
themeManager = new ThemeManager(this);
// Entrance animation
View root = findViewById(R.id.clock_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
// Back button
findViewById(R.id.clock_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Tabs
tabClock = findViewById(R.id.tab_clock);
tabStopwatch = findViewById(R.id.tab_stopwatch);
tabTimer = findViewById(R.id.tab_timer);
tabIndicator = findViewById(R.id.tab_indicator);
viewFlipper = findViewById(R.id.view_flipper);
tabClock.setOnClickListener(v -> switchTab(0));
tabStopwatch.setOnClickListener(v -> switchTab(1));
tabTimer.setOnClickListener(v -> switchTab(2));
// Clock tab views
clockTimeDisplay = findViewById(R.id.clock_time_display);
clockSecondsDisplay = findViewById(R.id.clock_seconds_display);
clockAmPm = findViewById(R.id.clock_ampm);
clockDateDisplay = findViewById(R.id.clock_date_display);
clockTimezone = findViewById(R.id.clock_timezone);
// Stopwatch tab views
stopwatchDisplay = findViewById(R.id.stopwatch_display);
stopwatchStart = findViewById(R.id.stopwatch_start);
stopwatchReset = findViewById(R.id.stopwatch_reset);
stopwatchLap = findViewById(R.id.stopwatch_lap);
lapContainer = findViewById(R.id.lap_container);
lapHeader = findViewById(R.id.lap_header);
lapScroll = findViewById(R.id.lap_scroll);
// Timer tab views
timerHours = findViewById(R.id.timer_hours);
timerMinutes = findViewById(R.id.timer_minutes);
timerSeconds = findViewById(R.id.timer_seconds);
timerCountdownDisplay = findViewById(R.id.timer_countdown_display);
timerProgressLabel = findViewById(R.id.timer_progress_label);
timerInputArea = findViewById(R.id.timer_input_area);
timerStart = findViewById(R.id.timer_start);
timerReset = findViewById(R.id.timer_reset);
// Initialize handlers
clockHandler = new Handler(Looper.getMainLooper());
stopwatchHandler = new Handler(Looper.getMainLooper());
// Setup clock
startClockUpdates();
// Setup stopwatch
setupStopwatch();
// Setup timer
setupTimer();
// Set initial tab indicator position
tabClock.post(() -> updateTabIndicator(0));
}
// ========================================================================
// TAB SWITCHING
// ========================================================================
private void switchTab(int tab) {
if (tab == currentTab) return;
currentTab = tab;
viewFlipper.setDisplayedChild(tab);
updateTabStyles(tab);
updateTabIndicator(tab);
}
private void updateTabStyles(int activeTab) {
int activeColor = themeManager.getPrimaryColor(ClockActivity.this);
int inactiveColor = 0x66FFFFFF;
tabClock.setTextColor(activeTab == 0 ? activeColor : inactiveColor);
tabStopwatch.setTextColor(activeTab == 1 ? activeColor : inactiveColor);
tabTimer.setTextColor(activeTab == 2 ? activeColor : inactiveColor);
tabClock.setTextSize(13);
tabStopwatch.setTextSize(13);
tabTimer.setTextSize(13);
}
private void updateTabIndicator(int tab) {
int parentWidth = ((View) tabIndicator.getParent()).getWidth();
if (parentWidth == 0) return;
int tabWidth = parentWidth / 3;
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) tabIndicator.getLayoutParams();
params.width = tabWidth;
params.gravity = Gravity.START;
tabIndicator.setLayoutParams(params);
tabIndicator.animate()
.translationX(tab * tabWidth)
.setDuration(200)
.start();
}
// ========================================================================
// CLOCK TAB
// ========================================================================
private void startClockUpdates() {
clockHandler.post(new Runnable() {
@Override
public void run() {
updateClockDisplay();
clockHandler.postDelayed(this, 500);
}
});
}
private void updateClockDisplay() {
Date now = new Date();
// Time (HH:mm)
SimpleDateFormat timeFmt = new SimpleDateFormat("HH:mm", Locale.getDefault());
clockTimeDisplay.setText(timeFmt.format(now));
// Seconds
SimpleDateFormat secFmt = new SimpleDateFormat(":ss", Locale.getDefault());
clockSecondsDisplay.setText(secFmt.format(now));
// AM/PM (only show if 12-hour format is used by locale)
SimpleDateFormat ampmFmt = new SimpleDateFormat("a", Locale.getDefault());
String ampm = ampmFmt.format(now);
// Check if device uses 24h - if the formatted hour goes above 12 it's 24h
SimpleDateFormat hourCheck = new SimpleDateFormat("H", Locale.getDefault());
int hour24 = Integer.parseInt(hourCheck.format(now));
if (android.text.format.DateFormat.is24HourFormat(this)) {
clockAmPm.setVisibility(View.GONE);
} else {
clockAmPm.setVisibility(View.VISIBLE);
clockAmPm.setText(ampm);
// Reformat to 12h
SimpleDateFormat time12Fmt = new SimpleDateFormat("hh:mm", Locale.getDefault());
clockTimeDisplay.setText(time12Fmt.format(now));
}
// Date
SimpleDateFormat dateFmt = new SimpleDateFormat("EEEE, MMMM d, yyyy", Locale.getDefault());
clockDateDisplay.setText(dateFmt.format(now));
// Timezone
TimeZone tz = TimeZone.getDefault();
String tzName = tz.getDisplayName(tz.inDaylightTime(now), TimeZone.SHORT, Locale.getDefault());
String tzId = tz.getID();
clockTimezone.setText(tzName + " (" + tzId + ")");
}
// ========================================================================
// STOPWATCH TAB
// ========================================================================
private void setupStopwatch() {
stopwatchStart.setOnClickListener(v -> {
if (stopwatchRunning) {
stopStopwatch();
} else {
startStopwatch();
}
});
stopwatchReset.setOnClickListener(v -> resetStopwatch());
stopwatchLap.setOnClickListener(v -> {
if (stopwatchRunning) {
recordLap();
}
});
}
private void startStopwatch() {
stopwatchRunning = true;
stopwatchStartTime = System.currentTimeMillis() - stopwatchElapsed;
stopwatchStart.setText("Stop");
stopwatchStart.setTextColor(0xFFF87171); // red tint for stop
stopwatchHandler.post(stopwatchRunnable);
}
private void stopStopwatch() {
stopwatchRunning = false;
stopwatchElapsed = System.currentTimeMillis() - stopwatchStartTime;
stopwatchStart.setText("Start");
stopwatchStart.setTextColor(0xFFFFFFFF);
stopwatchHandler.removeCallbacks(stopwatchRunnable);
}
private void resetStopwatch() {
stopwatchRunning = false;
stopwatchElapsed = 0;
stopwatchStartTime = 0;
lastLapTime = 0;
lapTimes.clear();
stopwatchDisplay.setText("00:00.00");
stopwatchStart.setText("Start");
stopwatchStart.setTextColor(0xFFFFFFFF);
lapContainer.removeAllViews();
lapHeader.setVisibility(View.GONE);
stopwatchHandler.removeCallbacks(stopwatchRunnable);
}
private final Runnable stopwatchRunnable = new Runnable() {
@Override
public void run() {
if (stopwatchRunning) {
long elapsed = System.currentTimeMillis() - stopwatchStartTime;
stopwatchDisplay.setText(formatStopwatchTime(elapsed));
stopwatchHandler.postDelayed(this, 10);
}
}
};
private void recordLap() {
long elapsed = System.currentTimeMillis() - stopwatchStartTime;
long lapTime = elapsed - lastLapTime;
lastLapTime = elapsed;
lapTimes.add(elapsed);
lapHeader.setVisibility(View.VISIBLE);
int lapNum = lapTimes.size();
// Create lap row
LinearLayout row = new LinearLayout(this);
row.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
row.setOrientation(LinearLayout.HORIZONTAL);
row.setPadding(dpToPx(12), dpToPx(10), dpToPx(12), dpToPx(10));
row.setBackgroundResource(R.drawable.bg_card_surface);
LinearLayout.LayoutParams rowMargin = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rowMargin.setMargins(0, dpToPx(4), 0, 0);
row.setLayoutParams(rowMargin);
// Lap number
TextView lapNumTv = new TextView(this);
lapNumTv.setLayoutParams(new LinearLayout.LayoutParams(0,
ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
lapNumTv.setText(String.format(Locale.getDefault(), "#%d", lapNum));
lapNumTv.setTextSize(13);
lapNumTv.setTextColor(themeManager.getPrimaryColor(ClockActivity.this));
lapNumTv.setTypeface(themeManager.getMonoFont(ClockActivity.this));
// Lap split time
TextView lapTimeTv = new TextView(this);
lapTimeTv.setLayoutParams(new LinearLayout.LayoutParams(0,
ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
lapTimeTv.setText(formatStopwatchTime(lapTime));
lapTimeTv.setTextSize(13);
lapTimeTv.setTextColor(0xCCFFFFFF);
lapTimeTv.setTypeface(themeManager.getMonoFont(ClockActivity.this));
lapTimeTv.setGravity(Gravity.CENTER);
// Total time
TextView totalTv = new TextView(this);
totalTv.setLayoutParams(new LinearLayout.LayoutParams(0,
ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
totalTv.setText(formatStopwatchTime(elapsed));
totalTv.setTextSize(13);
totalTv.setTextColor(0x66FFFFFF);
totalTv.setTypeface(themeManager.getMonoFont(ClockActivity.this));
totalTv.setGravity(Gravity.END);
row.addView(lapNumTv);
row.addView(lapTimeTv);
row.addView(totalTv);
// Add at top (most recent first)
lapContainer.addView(row, 0);
// Scroll to top to show new lap
lapScroll.post(() -> lapScroll.fullScroll(View.FOCUS_UP));
}
private String formatStopwatchTime(long ms) {
long totalSecs = ms / 1000;
long mins = totalSecs / 60;
long secs = totalSecs % 60;
long centis = (ms % 1000) / 10;
return String.format(Locale.getDefault(), "%02d:%02d.%02d", mins, secs, centis);
}
// ========================================================================
// TIMER TAB
// ========================================================================
private void setupTimer() {
// Up/Down buttons for hours
findViewById(R.id.timer_hour_up).setOnClickListener(v -> {
if (!timerRunning) {
timerH = (timerH + 1) % 24;
timerHours.setText(String.format(Locale.getDefault(), "%02d", timerH));
}
});
findViewById(R.id.timer_hour_down).setOnClickListener(v -> {
if (!timerRunning) {
timerH = (timerH - 1 + 24) % 24;
timerHours.setText(String.format(Locale.getDefault(), "%02d", timerH));
}
});
// Up/Down buttons for minutes
findViewById(R.id.timer_min_up).setOnClickListener(v -> {
if (!timerRunning) {
timerM = (timerM + 1) % 60;
timerMinutes.setText(String.format(Locale.getDefault(), "%02d", timerM));
}
});
findViewById(R.id.timer_min_down).setOnClickListener(v -> {
if (!timerRunning) {
timerM = (timerM - 1 + 60) % 60;
timerMinutes.setText(String.format(Locale.getDefault(), "%02d", timerM));
}
});
// Up/Down buttons for seconds
findViewById(R.id.timer_sec_up).setOnClickListener(v -> {
if (!timerRunning) {
timerS = (timerS + 1) % 60;
timerSeconds.setText(String.format(Locale.getDefault(), "%02d", timerS));
}
});
findViewById(R.id.timer_sec_down).setOnClickListener(v -> {
if (!timerRunning) {
timerS = (timerS - 1 + 60) % 60;
timerSeconds.setText(String.format(Locale.getDefault(), "%02d", timerS));
}
});
// Start/Pause button
timerStart.setOnClickListener(v -> {
if (timerRunning) {
pauseTimer();
} else {
startTimer();
}
});
// Reset button
timerReset.setOnClickListener(v -> resetTimer());
}
private void startTimer() {
long totalMs;
if (timerPaused && timerRemainingMs > 0) {
totalMs = timerRemainingMs;
} else {
totalMs = (timerH * 3600L + timerM * 60L + timerS) * 1000L;
timerTotalMs = totalMs;
}
if (totalMs <= 0) return;
timerRunning = true;
timerPaused = false;
// Switch to countdown display
timerInputArea.setVisibility(View.GONE);
timerCountdownDisplay.setVisibility(View.VISIBLE);
timerProgressLabel.setVisibility(View.VISIBLE);
timerStart.setText("Pause");
timerStart.setTextColor(0xFFFBBF24); // amber for pause
countDownTimer = new CountDownTimer(totalMs, 50) {
@Override
public void onTick(long millisUntilFinished) {
timerRemainingMs = millisUntilFinished;
updateTimerDisplay(millisUntilFinished);
// Progress percentage
if (timerTotalMs > 0) {
int pct = (int) (((timerTotalMs - millisUntilFinished) * 100) / timerTotalMs);
timerProgressLabel.setText(pct + "% elapsed");
}
}
@Override
public void onFinish() {
timerRemainingMs = 0;
timerRunning = false;
timerPaused = false;
timerCountdownDisplay.setText("00:00:00");
timerStart.setText("Start");
timerStart.setTextColor(0xFFFFFFFF);
timerProgressLabel.setText("Complete!");
timerProgressLabel.setTextColor(themeManager.getPrimaryColor(ClockActivity.this));
// Flash the display
timerCountdownDisplay.setTextColor(themeManager.getPrimaryColor(ClockActivity.this));
timerCountdownDisplay.animate()
.alpha(0.3f).setDuration(300)
.withEndAction(() -> timerCountdownDisplay.animate()
.alpha(1f).setDuration(300)
.withEndAction(() -> timerCountdownDisplay.animate()
.alpha(0.3f).setDuration(300)
.withEndAction(() -> timerCountdownDisplay.animate()
.alpha(1f).setDuration(300)
.withEndAction(() -> timerCountdownDisplay.setTextColor(0xFFFFFFFF))
.start())
.start())
.start())
.start();
// Vibrate
triggerTimerAlarm();
}
};
countDownTimer.start();
}
private void pauseTimer() {
if (countDownTimer != null) {
countDownTimer.cancel();
}
timerRunning = false;
timerPaused = true;
timerStart.setText("Resume");
timerStart.setTextColor(0xFFFFFFFF);
}
private void resetTimer() {
if (countDownTimer != null) {
countDownTimer.cancel();
}
timerRunning = false;
timerPaused = false;
timerRemainingMs = 0;
// Switch back to input display
timerInputArea.setVisibility(View.VISIBLE);
timerCountdownDisplay.setVisibility(View.GONE);
timerProgressLabel.setVisibility(View.GONE);
timerStart.setText("Start");
timerStart.setTextColor(0xFFFFFFFF);
timerProgressLabel.setTextColor(0x66FFFFFF);
timerCountdownDisplay.setTextColor(0xFFFFFFFF);
}
private void updateTimerDisplay(long ms) {
long totalSecs = (ms + 999) / 1000; // Round up to show correct second
long h = totalSecs / 3600;
long m = (totalSecs % 3600) / 60;
long s = totalSecs % 60;
timerCountdownDisplay.setText(
String.format(Locale.getDefault(), "%02d:%02d:%02d", h, m, s));
}
private void triggerTimerAlarm() {
// Vibrate pattern
try {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
if (vibrator != null && vibrator.hasVibrator()) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
long[] pattern = {0, 200, 100, 200, 100, 400};
vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1));
} else {
long[] pattern = {0, 200, 100, 200, 100, 400};
vibrator.vibrate(pattern, -1);
}
}
} catch (Exception ignored) {}
// Beep tone
try {
ToneGenerator toneGen = new ToneGenerator(AudioManager.STREAM_ALARM, 80);
toneGen.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 500);
// Release after a delay
new Handler(Looper.getMainLooper()).postDelayed(toneGen::release, 1000);
} catch (Exception ignored) {}
}
// ========================================================================
// UTILITY
// ========================================================================
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
// ========================================================================
// LIFECYCLE
// ========================================================================
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) hideSystemUI();
}
@Override
protected void onDestroy() {
super.onDestroy();
clockHandler.removeCallbacksAndMessages(null);
stopwatchHandler.removeCallbacks(stopwatchRunnable);
if (countDownTimer != null) {
countDownTimer.cancel();
}
}
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);
}
}
}

View file

@ -0,0 +1,354 @@
package com.aethex.os;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.GradientDrawable;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* DataAnalyzerWidget - Real-time system stats widget for desktop.
* Shows CPU, RAM, Battery, and Storage usage with animated graphs.
* Unlocked by installing "Data Analyzer" from Marketplace.
*/
public class DataAnalyzerWidget extends LinearLayout {
private Context context;
private ThemeManager themeManager;
private Handler handler;
private Runnable updateRunnable;
// Stats display
private TextView cpuValue;
private TextView ramValue;
private TextView batteryValue;
private TextView storageValue;
// Graph views
private GraphView cpuGraph;
private GraphView ramGraph;
// Update interval (ms)
private static final int UPDATE_INTERVAL = 2000;
// Graph history
private float[] cpuHistory = new float[20];
private float[] ramHistory = new float[20];
private int historyIndex = 0;
public DataAnalyzerWidget(Context context) {
super(context);
init(context);
}
public DataAnalyzerWidget(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
this.context = context;
this.themeManager = new ThemeManager(context);
this.handler = new Handler(Looper.getMainLooper());
setOrientation(VERTICAL);
setPadding(dpToPx(14), dpToPx(12), dpToPx(14), dpToPx(12));
// Widget background
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(dpToPx(12));
bg.setColor(Color.parseColor("#1A0F172A"));
int primary = themeManager.getPrimaryColor(context);
bg.setStroke(dpToPx(1), Color.argb(40, Color.red(primary), Color.green(primary), Color.blue(primary)));
setBackground(bg);
buildUI();
startUpdates();
}
private void buildUI() {
int primaryColor = themeManager.getPrimaryColor(context);
android.graphics.Typeface monoFont = themeManager.getMonoFont(context);
// Header
TextView header = new TextView(context);
header.setText("◉ SYSTEM ANALYZER");
header.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
header.setTextColor(primaryColor);
header.setTypeface(monoFont, android.graphics.Typeface.BOLD);
header.setLetterSpacing(0.15f);
addView(header);
// Divider
View divider = new View(context);
divider.setBackgroundColor(Color.parseColor("#1AFFFFFF"));
LinearLayout.LayoutParams divParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(1));
divParams.topMargin = dpToPx(8);
divParams.bottomMargin = dpToPx(10);
divider.setLayoutParams(divParams);
addView(divider);
// Stats grid (2x2)
LinearLayout grid = new LinearLayout(context);
grid.setOrientation(VERTICAL);
addView(grid);
// Row 1: CPU & RAM
LinearLayout row1 = new LinearLayout(context);
row1.setOrientation(HORIZONTAL);
row1.setWeightSum(2);
grid.addView(row1);
row1.addView(createStatItem("CPU", "0%", true));
row1.addView(createStatItem("RAM", "0%", false));
// Row 2: Battery & Storage
LinearLayout row2 = new LinearLayout(context);
row2.setOrientation(HORIZONTAL);
row2.setWeightSum(2);
LinearLayout.LayoutParams row2Params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
row2Params.topMargin = dpToPx(8);
row2.setLayoutParams(row2Params);
grid.addView(row2);
row2.addView(createStatItem("BATT", "0%", true));
row2.addView(createStatItem("DISK", "0%", false));
// Mini graphs
LinearLayout graphContainer = new LinearLayout(context);
graphContainer.setOrientation(HORIZONTAL);
LinearLayout.LayoutParams graphParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(40));
graphParams.topMargin = dpToPx(12);
graphContainer.setLayoutParams(graphParams);
addView(graphContainer);
cpuGraph = new GraphView(context, primaryColor, "CPU");
ramGraph = new GraphView(context, Color.parseColor("#A855F7"), "RAM");
LinearLayout.LayoutParams gp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f);
gp.setMarginEnd(dpToPx(6));
cpuGraph.setLayoutParams(gp);
LinearLayout.LayoutParams gp2 = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f);
gp2.setMarginStart(dpToPx(6));
ramGraph.setLayoutParams(gp2);
graphContainer.addView(cpuGraph);
graphContainer.addView(ramGraph);
}
private LinearLayout createStatItem(String label, String value, boolean isLeft) {
int primaryColor = themeManager.getPrimaryColor(context);
android.graphics.Typeface monoFont = themeManager.getMonoFont(context);
LinearLayout item = new LinearLayout(context);
item.setOrientation(VERTICAL);
LinearLayout.LayoutParams itemParams = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
if (isLeft) itemParams.setMarginEnd(dpToPx(8));
else itemParams.setMarginStart(dpToPx(8));
item.setLayoutParams(itemParams);
// Label
TextView labelView = new TextView(context);
labelView.setText(label);
labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
labelView.setTextColor(Color.parseColor("#66FFFFFF"));
labelView.setTypeface(monoFont);
labelView.setLetterSpacing(0.1f);
item.addView(labelView);
// Value
TextView valueView = new TextView(context);
valueView.setText(value);
valueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
valueView.setTextColor(Color.parseColor("#EEFFFFFF"));
valueView.setTypeface(monoFont, android.graphics.Typeface.BOLD);
item.addView(valueView);
// Store reference
if (label.equals("CPU")) cpuValue = valueView;
else if (label.equals("RAM")) ramValue = valueView;
else if (label.equals("BATT")) batteryValue = valueView;
else if (label.equals("DISK")) storageValue = valueView;
return item;
}
private void startUpdates() {
updateRunnable = new Runnable() {
@Override
public void run() {
updateStats();
handler.postDelayed(this, UPDATE_INTERVAL);
}
};
handler.post(updateRunnable);
}
public void stopUpdates() {
if (handler != null && updateRunnable != null) {
handler.removeCallbacks(updateRunnable);
}
}
private void updateStats() {
// CPU (simulated based on running processes)
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int runningApps = am.getRunningAppProcesses() != null ? am.getRunningAppProcesses().size() : 0;
int cpuPercent = Math.min(95, 15 + runningApps * 3 + (int)(Math.random() * 10));
cpuValue.setText(cpuPercent + "%");
setValueColor(cpuValue, cpuPercent);
// RAM
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
am.getMemoryInfo(memInfo);
int ramPercent = (int) (100 - (memInfo.availMem * 100 / memInfo.totalMem));
ramValue.setText(ramPercent + "%");
setValueColor(ramValue, ramPercent);
// Battery
BatteryManager bm = (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
int battery = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
batteryValue.setText(battery + "%");
setBatteryColor(batteryValue, battery);
// Storage
java.io.File path = android.os.Environment.getDataDirectory();
android.os.StatFs stat = new android.os.StatFs(path.getPath());
long total = stat.getTotalBytes();
long free = stat.getAvailableBytes();
int storagePercent = (int) (100 - (free * 100 / total));
storageValue.setText(storagePercent + "%");
setValueColor(storageValue, storagePercent);
// Update graphs
cpuHistory[historyIndex] = cpuPercent / 100f;
ramHistory[historyIndex] = ramPercent / 100f;
historyIndex = (historyIndex + 1) % cpuHistory.length;
cpuGraph.setData(cpuHistory, historyIndex);
ramGraph.setData(ramHistory, historyIndex);
}
private void setValueColor(TextView view, int percent) {
if (percent >= 80) view.setTextColor(Color.parseColor("#EF4444"));
else if (percent >= 60) view.setTextColor(Color.parseColor("#FBBF24"));
else view.setTextColor(Color.parseColor("#22C55E"));
}
private void setBatteryColor(TextView view, int percent) {
if (percent <= 20) view.setTextColor(Color.parseColor("#EF4444"));
else if (percent <= 40) view.setTextColor(Color.parseColor("#FBBF24"));
else view.setTextColor(Color.parseColor("#22C55E"));
}
private int dpToPx(int dp) {
return (int) (dp * context.getResources().getDisplayMetrics().density);
}
//
// Mini Graph View
//
private static class GraphView extends View {
private Paint linePaint;
private Paint fillPaint;
private Paint labelPaint;
private Path path;
private float[] data;
private int startIndex;
private String label;
public GraphView(Context context, int color, String label) {
super(context);
this.label = label;
linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(color);
linePaint.setStrokeWidth(2f);
linePaint.setStyle(Paint.Style.STROKE);
fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
fillPaint.setColor(Color.argb(40, Color.red(color), Color.green(color), Color.blue(color)));
fillPaint.setStyle(Paint.Style.FILL);
labelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
labelPaint.setColor(Color.argb(100, 255, 255, 255));
labelPaint.setTextSize(18f);
path = new Path();
data = new float[20];
// Background
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(8f);
bg.setColor(Color.parseColor("#0DFFFFFF"));
setBackground(bg);
}
public void setData(float[] history, int index) {
this.data = history.clone();
this.startIndex = index;
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int w = getWidth();
int h = getHeight();
if (w == 0 || h == 0 || data == null) return;
float segmentWidth = (float) w / (data.length - 1);
float padding = 4f;
// Build path
path.reset();
boolean started = false;
for (int i = 0; i < data.length; i++) {
int idx = (startIndex + i) % data.length;
float x = i * segmentWidth;
float y = h - padding - (data[idx] * (h - padding * 2));
if (!started) {
path.moveTo(x, y);
started = true;
} else {
path.lineTo(x, y);
}
}
// Fill
Path fillPath = new Path(path);
fillPath.lineTo(w, h);
fillPath.lineTo(0, h);
fillPath.close();
canvas.drawPath(fillPath, fillPaint);
// Line
canvas.drawPath(path, linePaint);
// Label
canvas.drawText(label, padding + 2, 14, labelPaint);
}
}
}

View file

@ -0,0 +1,352 @@
package com.aethex.os;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
public class FileManagerActivity extends AppCompatActivity {
private ThemeManager themeManager;
private RecyclerView recyclerView;
private TextView breadcrumb;
private FileAdapter adapter;
private final Stack<String> pathStack = new Stack<>();
private String currentPath = "/home";
// Virtual file system
private final Map<String, List<FileEntry>> fileSystem = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_manager);
hideSystemUI();
themeManager = new ThemeManager(this);
breadcrumb = findViewById(R.id.fm_breadcrumb);
recyclerView = findViewById(R.id.fm_recycler);
// Back button
findViewById(R.id.fm_back).setOnClickListener(v -> {
if (!navigateUp()) {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
}
});
// Storage bar - 42.3 / 128 = ~33%
View storageBar = findViewById(R.id.fm_storage_bar);
storageBar.post(() -> {
ViewGroup.LayoutParams params = storageBar.getLayoutParams();
int parentWidth = ((View) storageBar.getParent()).getWidth();
params.width = (int) (parentWidth * 0.33f);
storageBar.setLayoutParams(params);
});
// Build virtual file system
buildFileSystem();
// Setup RecyclerView
adapter = new FileAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
// Load root
navigateTo(currentPath);
// Entrance animation
View root = findViewById(R.id.file_manager_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
}
private void buildFileSystem() {
// /home - top-level folders
List<FileEntry> home = new ArrayList<>();
home.add(new FileEntry("Documents", true, "--", "2026-01-15"));
home.add(new FileEntry("Downloads", true, "--", "2026-02-10"));
home.add(new FileEntry("Pictures", true, "--", "2026-02-08"));
home.add(new FileEntry("Music", true, "--", "2026-01-22"));
home.add(new FileEntry("Videos", true, "--", "2026-02-01"));
home.add(new FileEntry("System", true, "--", "2025-12-01"));
home.add(new FileEntry("Programs", true, "--", "2026-01-30"));
fileSystem.put("/home", home);
// /home/Documents
List<FileEntry> docs = new ArrayList<>();
docs.add(new FileEntry("report.pdf", false, "2.4 MB", "2026-01-14"));
docs.add(new FileEntry("notes.txt", false, "12 KB", "2026-01-10"));
docs.add(new FileEntry("budget.xlsx", false, "890 KB", "2025-12-20"));
docs.add(new FileEntry("resume.docx", false, "156 KB", "2026-01-05"));
fileSystem.put("/home/Documents", docs);
// /home/Downloads
List<FileEntry> downloads = new ArrayList<>();
downloads.add(new FileEntry("setup.exe", false, "45.2 MB", "2026-02-09"));
downloads.add(new FileEntry("image_001.png", false, "3.1 MB", "2026-02-08"));
downloads.add(new FileEntry("song.mp3", false, "8.7 MB", "2026-02-05"));
downloads.add(new FileEntry("archive.zip", false, "22.0 MB", "2026-01-28"));
fileSystem.put("/home/Downloads", downloads);
// /home/Pictures
List<FileEntry> pictures = new ArrayList<>();
pictures.add(new FileEntry("vacation.jpg", false, "4.2 MB", "2026-02-07"));
pictures.add(new FileEntry("profile.png", false, "1.1 MB", "2026-01-18"));
pictures.add(new FileEntry("screenshot.png", false, "2.8 MB", "2026-02-03"));
pictures.add(new FileEntry("wallpaper.jpg", false, "5.6 MB", "2025-11-12"));
fileSystem.put("/home/Pictures", pictures);
// /home/Music
List<FileEntry> music = new ArrayList<>();
music.add(new FileEntry("track01.mp3", false, "7.2 MB", "2025-10-15"));
music.add(new FileEntry("album.flac", false, "42.1 MB", "2025-09-20"));
music.add(new FileEntry("podcast.mp3", false, "28.4 MB", "2026-01-22"));
fileSystem.put("/home/Music", music);
// /home/Videos
List<FileEntry> videos = new ArrayList<>();
videos.add(new FileEntry("tutorial.mp4", false, "156.0 MB", "2026-01-30"));
videos.add(new FileEntry("clip.mov", false, "23.4 MB", "2025-12-14"));
fileSystem.put("/home/Videos", videos);
// /home/System
List<FileEntry> system = new ArrayList<>();
system.add(new FileEntry("config.sys", false, "4 KB", "2025-12-01"));
system.add(new FileEntry("kernel.log", false, "128 KB", "2026-02-14"));
system.add(new FileEntry("boot.ini", false, "2 KB", "2025-11-01"));
fileSystem.put("/home/System", system);
// /home/Programs
List<FileEntry> programs = new ArrayList<>();
programs.add(new FileEntry("terminal.app", false, "12.0 MB", "2026-01-30"));
programs.add(new FileEntry("calculator.app", false, "8.4 MB", "2026-01-30"));
programs.add(new FileEntry("browser.app", false, "34.2 MB", "2026-01-25"));
fileSystem.put("/home/Programs", programs);
}
private void navigateTo(String path) {
currentPath = path;
breadcrumb.setText(currentPath);
List<FileEntry> entries = fileSystem.get(path);
if (entries == null) {
entries = new ArrayList<>();
}
// Sort folders first, then by name
List<FileEntry> sorted = new ArrayList<>(entries);
Collections.sort(sorted, (a, b) -> {
if (a.isFolder && !b.isFolder) return -1;
if (!a.isFolder && b.isFolder) return 1;
return a.name.compareToIgnoreCase(b.name);
});
adapter.setData(sorted);
}
private boolean navigateUp() {
if (!pathStack.isEmpty()) {
currentPath = pathStack.pop();
navigateTo(currentPath);
return true;
}
if (!currentPath.equals("/home")) {
int lastSlash = currentPath.lastIndexOf('/');
if (lastSlash > 0) {
currentPath = currentPath.substring(0, lastSlash);
navigateTo(currentPath);
return true;
}
}
return false;
}
@SuppressWarnings("deprecation")
@Override
public void onBackPressed() {
if (!navigateUp()) {
super.onBackPressed();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
}
}
// File entry model
private static class FileEntry {
final String name;
final boolean isFolder;
final String size;
final String date;
FileEntry(String name, boolean isFolder, String size, String date) {
this.name = name;
this.isFolder = isFolder;
this.size = size;
this.date = date;
}
}
// RecyclerView adapter
private class FileAdapter extends RecyclerView.Adapter<FileAdapter.FileViewHolder> {
private List<FileEntry> items = new ArrayList<>();
void setData(List<FileEntry> data) {
this.items = data;
notifyDataSetChanged();
}
@NonNull
@Override
public FileViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_file_entry, parent, false);
return new FileViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull FileViewHolder holder, int position) {
FileEntry entry = items.get(position);
holder.bind(entry);
}
@Override
public int getItemCount() {
return items.size();
}
class FileViewHolder extends RecyclerView.ViewHolder {
final TextView icon;
final TextView name;
final TextView size;
final TextView date;
FileViewHolder(@NonNull View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.file_icon);
name = itemView.findViewById(R.id.file_name);
size = itemView.findViewById(R.id.file_size);
date = itemView.findViewById(R.id.file_date);
}
void bind(FileEntry entry) {
if (entry.isFolder) {
icon.setText("\u25B8");
icon.setTextColor(themeManager.getPrimaryColor(FileManagerActivity.this));
name.setTextColor(themeManager.getPrimaryColor(FileManagerActivity.this));
} else {
// Determine icon based on file extension
String ext = getExtension(entry.name);
icon.setText(getFileIcon(ext));
icon.setTextColor(getResources().getColor(R.color.text_white_40));
name.setTextColor(getResources().getColor(R.color.text_white_60));
}
name.setText(entry.isFolder ? entry.name + "/" : entry.name);
size.setText(entry.size);
date.setText(entry.date);
itemView.setOnClickListener(v -> {
if (entry.isFolder) {
// Subtle press animation
v.animate().alpha(0.6f).setDuration(60).withEndAction(() ->
v.animate().alpha(1f).setDuration(60).withEndAction(() -> {
pathStack.push(currentPath);
navigateTo(currentPath + "/" + entry.name);
}).start()
).start();
}
});
}
private String getExtension(String filename) {
int dot = filename.lastIndexOf('.');
if (dot >= 0) {
return filename.substring(dot + 1).toLowerCase();
}
return "";
}
private String getFileIcon(String ext) {
switch (ext) {
case "pdf":
case "docx":
case "doc":
case "txt":
case "xlsx":
return "\u2637"; // document-like
case "png":
case "jpg":
case "jpeg":
case "bmp":
return "\u25A3"; // image-like square
case "mp3":
case "flac":
case "wav":
return "\u266B"; // music note
case "mp4":
case "mov":
case "avi":
return "\u25B6"; // play triangle
case "exe":
case "app":
return "\u2B22"; // hexagon
case "zip":
case "tar":
case "gz":
return "\u29C9"; // stacked squares
case "sys":
case "ini":
case "log":
case "cfg":
return "\u2699"; // gear
default:
return "\u25A1"; // empty square
}
}
}
}
// System UI
@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);
}
}
}

View file

@ -0,0 +1,536 @@
package com.aethex.os;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
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.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class MailActivity extends AppCompatActivity {
private ThemeManager themeManager;
private LinearLayout mailList;
private LinearLayout composePanel;
private FrameLayout detailOverlay;
private boolean composeOpen = false;
private Typeface monoFont;
private Typeface displayFont;
private static final String[][] EMAILS = {
{"System", "system@aethex.os", "Welcome to AeThex OS", "Welcome, Architect.\n\nYour AeThex OS installation is complete. All subsystems are online and operating within normal parameters.\n\nYour clearance level has been set to Foundation. You may change this at any time via Settings or the Start Menu.\n\nStay vigilant.\n\n— AeThex System", "just now"},
{"Security", "sec@aethex.os", "Security Scan Complete", "Daily security scan completed.\n\nThreats detected: 0\nVulnerabilities patched: 3\nFirewall status: ACTIVE\nEncryption: AES-256\n\nAll modules passed integrity checks. Next scan scheduled in 24 hours.", "5m ago"},
{"Updates", "updates@aethex.os", "Module Update Available", "A new update is available for the following modules:\n\n• Terminal v2.1 — New commands added\n• Browser v1.3 — Performance improvements\n• Snake v1.2 — Leaderboard support\n\nUpdates will be applied automatically on next reboot.", "12m ago"},
{"Projects", "projects@aethex.os", "Sprint Review Reminder", "Reminder: Sprint review for AeThex Mobile is scheduled for tomorrow at 14:00.\n\nPlease prepare your status updates and demo materials.\n\nCurrent sprint velocity: 42 points\nBurn-down: On track", "1h ago"},
{"Analytics", "analytics@aethex.os", "Weekly Report", "Weekly analytics summary:\n\n• Active sessions: 1,247\n• Uptime: 99.97%\n• CPU avg: 18%\n• Memory usage: 62%\n• Top app: Terminal (347 opens)\n\nFull report available in the Analytics module.", "3h ago"},
{"Marketplace", "market@aethex.os", "New Modules Available", "New modules have been published to the marketplace:\n\n1. Dark Matter Theme Pack — 500 credits\n2. Neural Network Toolkit — 1,200 credits\n3. Advanced Encryption Suite — 800 credits\n\nBrowse the marketplace to learn more.", "6h ago"},
{"HR", "hr@aethex.os", "Access Level Review", "Your access level is due for review.\n\nCurrent level: 5\nNext review date: 2025-03-01\n\nPlease ensure your Passport information is up to date before the review period.", "1d ago"},
{"Archive", "archive@aethex.os", "Backup Complete", "System backup completed successfully.\n\nBackup size: 2.4 GB\nDuration: 4m 32s\nStorage used: 34%\n\nNext automatic backup: 7 days.", "2d ago"},
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app);
hideSystemUI();
themeManager = new ThemeManager(this);
monoFont = themeManager.getMonoFont(this);
displayFont = themeManager.getDisplayFont(this);
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
TextView title = findViewById(R.id.app_title);
title.setText("Mail");
TextView nameDisplay = findViewById(R.id.app_name_display);
findViewById(R.id.app_back).setOnClickListener(v -> {
if (composeOpen) {
closeCompose();
} else {
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);
buildMailUI(content);
View root = findViewById(R.id.app_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
AeThexKeyboard.attach(this);
}
private void buildMailUI(LinearLayout parent) {
// Header bar with inbox count + compose button
LinearLayout header = new LinearLayout(this);
header.setOrientation(LinearLayout.HORIZONTAL);
header.setGravity(Gravity.CENTER_VERTICAL);
header.setPadding(dpToPx(16), dpToPx(12), dpToPx(16), dpToPx(12));
TextView inboxLabel = new TextView(this);
inboxLabel.setText("INBOX");
inboxLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
inboxLabel.setTextColor(Color.parseColor("#66FFFFFF"));
inboxLabel.setTypeface(monoFont);
inboxLabel.setLetterSpacing(0.15f);
header.addView(inboxLabel);
// Unread count badge
TextView unreadBadge = new TextView(this);
unreadBadge.setText(String.valueOf(EMAILS.length));
unreadBadge.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
unreadBadge.setTextColor(Color.WHITE);
unreadBadge.setTypeface(monoFont, Typeface.BOLD);
unreadBadge.setGravity(Gravity.CENTER);
GradientDrawable badgeBg = new GradientDrawable();
badgeBg.setShape(GradientDrawable.OVAL);
badgeBg.setColor(themeManager.getPrimaryColor(this));
unreadBadge.setBackground(badgeBg);
LinearLayout.LayoutParams badgeP = new LinearLayout.LayoutParams(dpToPx(20), dpToPx(20));
badgeP.setMarginStart(dpToPx(8));
unreadBadge.setLayoutParams(badgeP);
header.addView(unreadBadge);
// Spacer
View spacer = new View(this);
spacer.setLayoutParams(new LinearLayout.LayoutParams(0, 1, 1f));
header.addView(spacer);
// Compose button
TextView composeBtn = new TextView(this);
composeBtn.setText("+ Compose");
composeBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
composeBtn.setTextColor(themeManager.getPrimaryColor(this));
composeBtn.setTypeface(monoFont);
composeBtn.setPadding(dpToPx(12), dpToPx(6), dpToPx(12), dpToPx(6));
GradientDrawable composeBg = new GradientDrawable();
composeBg.setCornerRadius(dpToPx(8));
composeBg.setStroke(dpToPx(1), themeManager.getPrimaryColor(this));
composeBg.setColor(Color.TRANSPARENT);
composeBtn.setBackground(composeBg);
composeBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
showCompose();
});
header.addView(composeBtn);
parent.addView(header);
// Divider
View div = new View(this);
div.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(1)));
div.setBackgroundColor(Color.parseColor("#1AFFFFFF"));
parent.addView(div);
// Mail list in scroll
ScrollView scroll = new ScrollView(this);
scroll.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
scroll.setOverScrollMode(View.OVER_SCROLL_NEVER);
mailList = new LinearLayout(this);
mailList.setOrientation(LinearLayout.VERTICAL);
mailList.setPadding(dpToPx(8), dpToPx(4), dpToPx(8), dpToPx(8));
scroll.addView(mailList);
parent.addView(scroll);
populateMailList();
}
private void populateMailList() {
mailList.removeAllViews();
for (int i = 0; i < EMAILS.length; i++) {
final String[] email = EMAILS[i];
LinearLayout row = new LinearLayout(this);
row.setOrientation(LinearLayout.VERTICAL);
row.setPadding(dpToPx(14), dpToPx(12), dpToPx(14), dpToPx(12));
LinearLayout.LayoutParams rowP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rowP.bottomMargin = dpToPx(4);
row.setLayoutParams(rowP);
GradientDrawable rowBg = new GradientDrawable();
rowBg.setCornerRadius(dpToPx(10));
rowBg.setColor(Color.parseColor("#0DFFFFFF"));
row.setBackground(rowBg);
// Top: sender + time
LinearLayout topRow = new LinearLayout(this);
topRow.setOrientation(LinearLayout.HORIZONTAL);
topRow.setGravity(Gravity.CENTER_VERTICAL);
// Sender avatar dot
View dot = new View(this);
GradientDrawable dotBg = new GradientDrawable();
dotBg.setShape(GradientDrawable.OVAL);
int dotColor = i < 3 ? themeManager.getPrimaryColor(this) : Color.parseColor("#66FFFFFF");
dotBg.setColor(dotColor);
dot.setBackground(dotBg);
LinearLayout.LayoutParams dotP = new LinearLayout.LayoutParams(dpToPx(8), dpToPx(8));
dotP.setMarginEnd(dpToPx(8));
dot.setLayoutParams(dotP);
topRow.addView(dot);
TextView sender = new TextView(this);
sender.setText(email[0]);
sender.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
sender.setTextColor(i < 3 ? Color.WHITE : Color.parseColor("#99FFFFFF"));
sender.setTypeface(monoFont, i < 3 ? Typeface.BOLD : Typeface.NORMAL);
sender.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
topRow.addView(sender);
TextView time = new TextView(this);
time.setText(email[4]);
time.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
time.setTextColor(Color.parseColor("#4DFFFFFF"));
time.setTypeface(monoFont);
topRow.addView(time);
row.addView(topRow);
// Subject
TextView subject = new TextView(this);
subject.setText(email[2]);
subject.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
subject.setTextColor(Color.parseColor("#CCFFFFFF"));
subject.setTypeface(displayFont);
subject.setSingleLine(true);
LinearLayout.LayoutParams subP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
subP.topMargin = dpToPx(4);
subject.setLayoutParams(subP);
row.addView(subject);
// Preview
TextView preview = new TextView(this);
String previewText = email[3].replace("\n", " ");
if (previewText.length() > 80) previewText = previewText.substring(0, 80) + "...";
preview.setText(previewText);
preview.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
preview.setTextColor(Color.parseColor("#4DFFFFFF"));
preview.setTypeface(monoFont);
preview.setSingleLine(true);
LinearLayout.LayoutParams prevP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
prevP.topMargin = dpToPx(2);
preview.setLayoutParams(prevP);
row.addView(preview);
row.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
showEmailDetail(email);
});
mailList.addView(row);
}
}
private void showEmailDetail(String[] email) {
FrameLayout root = (FrameLayout) findViewById(R.id.app_root);
detailOverlay = new FrameLayout(this);
detailOverlay.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
detailOverlay.setBackgroundColor(Color.parseColor("#F2080810"));
LinearLayout detail = new LinearLayout(this);
detail.setOrientation(LinearLayout.VERTICAL);
detail.setPadding(dpToPx(20), dpToPx(20), dpToPx(20), dpToPx(20));
FrameLayout.LayoutParams dp = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
detail.setLayoutParams(dp);
// Close row
LinearLayout closeRow = new LinearLayout(this);
closeRow.setGravity(Gravity.CENTER_VERTICAL);
TextView backBtn = new TextView(this);
backBtn.setText("← Back");
backBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
backBtn.setTextColor(themeManager.getPrimaryColor(this));
backBtn.setTypeface(monoFont);
backBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
root.removeView(detailOverlay);
detailOverlay = null;
});
closeRow.addView(backBtn);
detail.addView(closeRow);
// Subject
TextView subject = new TextView(this);
subject.setText(email[2]);
subject.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
subject.setTextColor(Color.WHITE);
subject.setTypeface(displayFont);
LinearLayout.LayoutParams sP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
sP.topMargin = dpToPx(16);
subject.setLayoutParams(sP);
detail.addView(subject);
// From line
TextView from = new TextView(this);
from.setText("From: " + email[0] + " <" + email[1] + ">");
from.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
from.setTextColor(Color.parseColor("#66FFFFFF"));
from.setTypeface(monoFont);
LinearLayout.LayoutParams fP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
fP.topMargin = dpToPx(8);
from.setLayoutParams(fP);
detail.addView(from);
// Time
TextView time = new TextView(this);
time.setText(email[4]);
time.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
time.setTextColor(Color.parseColor("#4DFFFFFF"));
time.setTypeface(monoFont);
LinearLayout.LayoutParams tP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
tP.topMargin = dpToPx(2);
time.setLayoutParams(tP);
detail.addView(time);
// Divider
View div = new View(this);
LinearLayout.LayoutParams divP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(1));
divP.topMargin = dpToPx(16);
divP.bottomMargin = dpToPx(16);
div.setLayoutParams(divP);
div.setBackgroundColor(Color.parseColor("#1AFFFFFF"));
detail.addView(div);
// Body in scroll
ScrollView bodyScroll = new ScrollView(this);
bodyScroll.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
TextView body = new TextView(this);
body.setText(email[3]);
body.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
body.setTextColor(Color.parseColor("#B3FFFFFF"));
body.setTypeface(monoFont);
body.setLineSpacing(dpToPx(4), 1f);
bodyScroll.addView(body);
detail.addView(bodyScroll);
// Reply button
TextView replyBtn = new TextView(this);
replyBtn.setText("Reply");
replyBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
replyBtn.setTextColor(Color.WHITE);
replyBtn.setTypeface(monoFont);
replyBtn.setGravity(Gravity.CENTER);
replyBtn.setPadding(0, dpToPx(12), 0, dpToPx(12));
LinearLayout.LayoutParams rP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rP.topMargin = dpToPx(12);
replyBtn.setLayoutParams(rP);
GradientDrawable replyBg = new GradientDrawable();
replyBg.setCornerRadius(dpToPx(10));
replyBg.setColor(themeManager.getPrimaryColor(this));
replyBtn.setBackground(replyBg);
replyBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
root.removeView(detailOverlay);
detailOverlay = null;
showCompose();
});
detail.addView(replyBtn);
detailOverlay.addView(detail);
detailOverlay.setAlpha(0f);
root.addView(detailOverlay);
detailOverlay.animate().alpha(1f).setDuration(200).start();
}
private void showCompose() {
composeOpen = true;
FrameLayout root = (FrameLayout) findViewById(R.id.app_root);
composePanel = new LinearLayout(this);
composePanel.setOrientation(LinearLayout.VERTICAL);
composePanel.setTag("compose_panel");
composePanel.setBackgroundColor(Color.parseColor("#F2080810"));
composePanel.setPadding(dpToPx(20), dpToPx(20), dpToPx(20), dpToPx(20));
FrameLayout.LayoutParams cp = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
composePanel.setLayoutParams(cp);
// Header
LinearLayout hdr = new LinearLayout(this);
hdr.setGravity(Gravity.CENTER_VERTICAL);
TextView cancel = new TextView(this);
cancel.setText("Cancel");
cancel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
cancel.setTextColor(Color.parseColor("#66FFFFFF"));
cancel.setTypeface(monoFont);
cancel.setOnClickListener(v -> closeCompose());
hdr.addView(cancel);
View sp = new View(this);
sp.setLayoutParams(new LinearLayout.LayoutParams(0, 1, 1f));
hdr.addView(sp);
TextView sendBtn = new TextView(this);
sendBtn.setText("Send");
sendBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
sendBtn.setTextColor(themeManager.getPrimaryColor(this));
sendBtn.setTypeface(monoFont, Typeface.BOLD);
sendBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
closeCompose();
AeThexToast.show(this, "Message sent", AeThexToast.Type.SUCCESS);
});
hdr.addView(sendBtn);
composePanel.addView(hdr);
// Title
TextView compTitle = new TextView(this);
compTitle.setText("NEW MESSAGE");
compTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
compTitle.setTextColor(Color.parseColor("#66FFFFFF"));
compTitle.setTypeface(monoFont);
compTitle.setLetterSpacing(0.15f);
LinearLayout.LayoutParams ctP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ctP.topMargin = dpToPx(16);
ctP.bottomMargin = dpToPx(12);
compTitle.setLayoutParams(ctP);
composePanel.addView(compTitle);
addComposeField(composePanel, "To");
addComposeField(composePanel, "Subject");
// Body field
EditText bodyField = new EditText(this);
bodyField.setHint("Message body...");
bodyField.setHintTextColor(Color.parseColor("#33FFFFFF"));
bodyField.setTextColor(Color.parseColor("#CCFFFFFF"));
bodyField.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
bodyField.setTypeface(monoFont);
bodyField.setMinLines(8);
bodyField.setGravity(Gravity.TOP);
bodyField.setBackgroundColor(Color.TRANSPARENT);
bodyField.setPadding(dpToPx(14), dpToPx(12), dpToPx(14), dpToPx(12));
LinearLayout.LayoutParams bfP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f);
bfP.topMargin = dpToPx(8);
bodyField.setLayoutParams(bfP);
GradientDrawable fieldBg = new GradientDrawable();
fieldBg.setCornerRadius(dpToPx(10));
fieldBg.setColor(Color.parseColor("#0DFFFFFF"));
fieldBg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
bodyField.setBackground(fieldBg);
composePanel.addView(bodyField);
composePanel.setAlpha(0f);
composePanel.setTranslationY(dpToPx(40));
root.addView(composePanel);
composePanel.animate().alpha(1f).translationY(0f).setDuration(250).start();
}
private void addComposeField(LinearLayout parent, String hint) {
EditText field = new EditText(this);
field.setHint(hint);
field.setHintTextColor(Color.parseColor("#33FFFFFF"));
field.setTextColor(Color.parseColor("#CCFFFFFF"));
field.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
field.setTypeface(monoFont);
field.setSingleLine(true);
field.setPadding(dpToPx(14), dpToPx(10), dpToPx(14), dpToPx(10));
LinearLayout.LayoutParams p = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
p.bottomMargin = dpToPx(6);
field.setLayoutParams(p);
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(dpToPx(10));
bg.setColor(Color.parseColor("#0DFFFFFF"));
bg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
field.setBackground(bg);
parent.addView(field);
}
private void closeCompose() {
composeOpen = false;
if (composePanel != null) {
FrameLayout root = (FrameLayout) findViewById(R.id.app_root);
composePanel.animate().alpha(0f).translationY(dpToPx(40)).setDuration(200).withEndAction(() -> {
root.removeView(composePanel);
composePanel = null;
}).start();
}
AeThexKeyboard.dismissKeyboard(this);
}
@SuppressWarnings("deprecation")
@Override
public void onBackPressed() {
if (composeOpen) {
closeCompose();
} else if (detailOverlay != null) {
FrameLayout root = (FrameLayout) findViewById(R.id.app_root);
root.removeView(detailOverlay);
detailOverlay = null;
} else {
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);
}
}
}

View file

@ -1,9 +1,540 @@
package com.aethex.os; package com.aethex.os;
import com.getcapacitor.BridgeActivity; import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
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.WindowInsets;
import android.view.WindowInsetsController;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends BridgeActivity { /**
// Reverting to standard Capacitor implementation. * AeThexOS Boot Screen 5-phase cinematic boot sequence.
// The BridgeActivity handles the WebView creation and loading of assets *
// via the scheme configured in capacitor.config.ts (androidScheme: "https"). * Phase 1: Hardware scan (fast scrolling log lines)
* Phase 2: Security check (threat level assessment)
* Phase 3: Biometric simulation (fingerprint scan animation)
* Phase 4: System init (loading services)
* Phase 5: Desktop transition
*/
public class MainActivity extends AppCompatActivity {
// Track whether boot has already played this app session
private static boolean hasBooted = false;
private ProgressBar progressBar;
private TextView percentText;
private TextView statusText;
private TextView logText;
private ScrollView logScroll;
private View glowView;
private Handler handler;
private int progress = 0;
private ThemeManager themeManager;
// Phase 2: Threat level
private TextView threatLabel;
private View threatDot;
// Phase 3: Biometric
private FrameLayout biometricContainer;
private TextView biometricIcon;
private TextView biometricStatus;
private int currentPhase = 0;
// Phase 1: Hardware Scan messages
private static final String[] PHASE1_MSGS = {
"[KERN] Loading AeThex kernel modules...",
"[KERN] Memory check: 8192MB OK",
"[KERN] CPU cores initialized: 8",
"[KERN] Interrupt handlers registered",
"[SYS] Mounting filesystem /dev/aethex0...",
"[SYS] Filesystem mounted OK",
"[GPU] Graphics pipeline initialized",
"[GPU] Display resolution: adaptive",
};
// Phase 2: Security Check messages
private static final String[] PHASE2_MSGS = {
"[SEC] Initializing security protocols...",
"[SEC] Scanning network interfaces...",
"[SEC] Firewall rules loaded: 247 active",
"[SEC] Intrusion detection system: ARMED",
"[SEC] Encryption engine: AES-256-GCM",
"[SEC] Running threat assessment...",
};
// Phase 4: System Init messages
private static final String[] PHASE4_MSGS = {
"[NET] WiFi adapter detected",
"[NET] Establishing secure tunnel...",
"[SVC] Starting system services...",
"[SVC] Notification daemon started",
"[SVC] Window compositor ready",
"[APP] Loading application registry...",
"[APP] 20 applications found",
"[SYS] Loading user preferences...",
"[SYS] Theme engine initialized",
"[SYS] Desktop environment ready",
"[BOOT] All systems nominal",
"[BOOT] Welcome to AeThex OS v2.1.0"
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// If boot already played, skip straight to desktop (e.g. HOME button press)
if (hasBooted) {
Intent desktop = new Intent(this, SystemActivity.class);
desktop.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(desktop);
finish();
return;
}
setContentView(R.layout.activity_main);
hideSystemUI();
themeManager = new ThemeManager(this);
// Sync sound state
SoundManager.getInstance().setEnabled(themeManager.isSoundEnabled());
progressBar = findViewById(R.id.boot_progress);
percentText = findViewById(R.id.boot_percent);
statusText = findViewById(R.id.boot_status);
logText = findViewById(R.id.boot_log);
logScroll = findViewById(R.id.boot_log_scroll);
glowView = findViewById(R.id.boot_glow);
handler = new Handler(Looper.getMainLooper());
// Apply theme to boot screen elements
applyBootTheme();
// Create dynamic phase UI elements
createPhaseUI();
// Animate glow pulsing
ObjectAnimator pulseAnim = ObjectAnimator.ofFloat(glowView, "alpha", 0.3f, 0.7f);
pulseAnim.setDuration(2000);
pulseAnim.setRepeatCount(ObjectAnimator.INFINITE);
pulseAnim.setRepeatMode(ObjectAnimator.REVERSE);
pulseAnim.setInterpolator(new AccelerateDecelerateInterpolator());
pulseAnim.start();
// Fade in logo
View logoContainer = findViewById(R.id.boot_logo_container);
logoContainer.setAlpha(0f);
logoContainer.setScaleX(0.8f);
logoContainer.setScaleY(0.8f);
logoContainer.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(600).start();
// Fade in title
TextView title = findViewById(R.id.boot_title);
title.setAlpha(0f);
title.setTranslationY(20f);
title.animate().alpha(1f).translationY(0f).setStartDelay(300).setDuration(500).start();
// Start the 5-phase boot
handler.postDelayed(this::startPhase1, 800);
}
private void applyBootTheme() {
int bootTextColor = themeManager.getBootTextColor(this);
Typeface displayFont = themeManager.getDisplayFont(this);
Typeface monoFont = themeManager.getMonoFont(this);
glowView.setBackgroundResource(themeManager.getBootGlowDrawable());
FrameLayout logoContainer = findViewById(R.id.boot_logo_container);
if (logoContainer.getChildCount() > 0) {
logoContainer.getChildAt(0).setBackgroundResource(themeManager.getBootLogoDrawable());
}
TextView bootLogo = findViewById(R.id.boot_logo);
bootLogo.setTypeface(displayFont, Typeface.BOLD);
TextView bootTitle = findViewById(R.id.boot_title);
bootTitle.setTextColor(bootTextColor);
bootTitle.setTypeface(displayFont);
TextView bootVersion = findViewById(R.id.boot_version);
bootVersion.setTypeface(monoFont);
progressBar.setProgressDrawable(
getResources().getDrawable(themeManager.getBootProgressDrawable(), getTheme()));
statusText.setTypeface(monoFont);
percentText.setTypeface(monoFont);
logText.setTextColor(bootTextColor);
logText.setTypeface(monoFont);
}
/**
* Creates the threat level indicator and biometric scanner overlays programmatically.
*/
private void createPhaseUI() {
// Find the parent layout (the LinearLayout inside the FrameLayout root)
FrameLayout root = (FrameLayout) findViewById(R.id.boot_glow).getParent();
Typeface monoFont = themeManager.getMonoFont(this);
// Threat Level indicator (bottom-left)
LinearLayout threatRow = new LinearLayout(this);
threatRow.setOrientation(LinearLayout.HORIZONTAL);
threatRow.setGravity(Gravity.CENTER_VERTICAL);
FrameLayout.LayoutParams threatParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
threatParams.gravity = Gravity.BOTTOM | Gravity.START;
threatParams.bottomMargin = dpToPx(20);
threatParams.leftMargin = dpToPx(20);
threatRow.setLayoutParams(threatParams);
threatRow.setAlpha(0f);
threatRow.setTag("threat_row");
// Pulsing dot
threatDot = new View(this);
int dotSize = dpToPx(8);
LinearLayout.LayoutParams dotParams = new LinearLayout.LayoutParams(dotSize, dotSize);
dotParams.setMarginEnd(dpToPx(8));
threatDot.setLayoutParams(dotParams);
GradientDrawable dotBg = new GradientDrawable();
dotBg.setShape(GradientDrawable.OVAL);
dotBg.setColor(Color.parseColor("#22C55E")); // starts green
threatDot.setBackground(dotBg);
threatRow.addView(threatDot);
// Label
threatLabel = new TextView(this);
threatLabel.setText("THREAT LEVEL: SCANNING...");
threatLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
threatLabel.setTextColor(Color.parseColor("#66FFFFFF"));
threatLabel.setTypeface(monoFont);
threatLabel.setLetterSpacing(0.1f);
threatRow.addView(threatLabel);
root.addView(threatRow);
// Biometric Scanner (center overlay, hidden initially)
biometricContainer = new FrameLayout(this);
FrameLayout.LayoutParams bioParams = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
bioParams.gravity = Gravity.CENTER;
biometricContainer.setLayoutParams(bioParams);
biometricContainer.setAlpha(0f);
biometricContainer.setTag("biometric_container");
LinearLayout bioInner = new LinearLayout(this);
bioInner.setOrientation(LinearLayout.VERTICAL);
bioInner.setGravity(Gravity.CENTER);
// Fingerprint icon (using text)
biometricIcon = new TextView(this);
biometricIcon.setText("");
biometricIcon.setTextSize(TypedValue.COMPLEX_UNIT_SP, 56);
biometricIcon.setTextColor(themeManager.getBootTextColor(this));
biometricIcon.setGravity(Gravity.CENTER);
bioInner.addView(biometricIcon);
// Status text
biometricStatus = new TextView(this);
biometricStatus.setText("BIOMETRIC SCAN");
biometricStatus.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
biometricStatus.setTextColor(Color.parseColor("#66FFFFFF"));
biometricStatus.setTypeface(monoFont);
biometricStatus.setGravity(Gravity.CENTER);
biometricStatus.setLetterSpacing(0.15f);
LinearLayout.LayoutParams bioTextParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
bioTextParams.topMargin = dpToPx(8);
biometricStatus.setLayoutParams(bioTextParams);
bioInner.addView(biometricStatus);
biometricContainer.addView(bioInner);
root.addView(biometricContainer);
}
//
// Phase 1: Hardware Scan
//
private void startPhase1() {
currentPhase = 1;
statusText.setText("Hardware scan...");
advancePhase1(0);
}
private void advancePhase1(int index) {
if (index >= PHASE1_MSGS.length) {
startPhase2();
return;
}
logText.append(PHASE1_MSGS[index] + "\n");
logScroll.post(() -> logScroll.fullScroll(View.FOCUS_DOWN));
int totalMessages = PHASE1_MSGS.length + PHASE2_MSGS.length + 4 + PHASE4_MSGS.length;
int current = index + 1;
int percent = (int) ((float) current / totalMessages * 100);
progressBar.setProgress(Math.min(percent, 100));
percentText.setText(percent + "%");
statusText.setText(getPhase1Status(index));
SoundManager.getInstance().play(SoundManager.Sound.BOOT_BEEP);
int delay = 60 + (int) (Math.random() * 80);
handler.postDelayed(() -> advancePhase1(index + 1), delay);
}
private String getPhase1Status(int index) {
switch (index) {
case 0: return "Loading kernel...";
case 1: return "Checking memory...";
case 2: return "Initializing CPU...";
case 3: return "Registering handlers...";
case 4: return "Mounting filesystem...";
case 5: return "Filesystem OK";
case 6: return "GPU initialized";
case 7: return "Display configured";
default: return "Scanning...";
}
}
//
// Phase 2: Security Check + Threat Level
//
private void startPhase2() {
currentPhase = 2;
statusText.setText("Security check...");
// Fade in threat indicator
View threatRow = ((FrameLayout) glowView.getParent()).findViewWithTag("threat_row");
if (threatRow != null) {
threatRow.animate().alpha(1f).setDuration(300).start();
}
advancePhase2(0);
}
private void advancePhase2(int index) {
if (index >= PHASE2_MSGS.length) {
// Resolve threat level
resolveThreatLevel();
return;
}
logText.append(PHASE2_MSGS[index] + "\n");
logScroll.post(() -> logScroll.fullScroll(View.FOCUS_DOWN));
int totalMessages = PHASE1_MSGS.length + PHASE2_MSGS.length + 4 + PHASE4_MSGS.length;
int current = PHASE1_MSGS.length + index + 1;
int percent = (int) ((float) current / totalMessages * 100);
progressBar.setProgress(Math.min(percent, 100));
percentText.setText(percent + "%");
statusText.setText("Security scanning...");
// Pulse the threat dot yellow during scan
GradientDrawable dotBg = new GradientDrawable();
dotBg.setShape(GradientDrawable.OVAL);
dotBg.setColor(Color.parseColor("#FBBF24"));
threatDot.setBackground(dotBg);
threatLabel.setText("THREAT LEVEL: SCANNING...");
threatLabel.setTextColor(Color.parseColor("#FBBF24"));
int delay = 100 + (int) (Math.random() * 100);
handler.postDelayed(() -> advancePhase2(index + 1), delay);
}
private void resolveThreatLevel() {
// Always resolve to LOW for now
logText.append("[SEC] Threat assessment: LOW\n");
logText.append("[SEC] All clear - no threats detected\n");
logScroll.post(() -> logScroll.fullScroll(View.FOCUS_DOWN));
GradientDrawable dotBg = new GradientDrawable();
dotBg.setShape(GradientDrawable.OVAL);
dotBg.setColor(Color.parseColor("#22C55E"));
threatDot.setBackground(dotBg);
threatLabel.setText("THREAT LEVEL: LOW");
threatLabel.setTextColor(Color.parseColor("#22C55E"));
statusText.setText("Threat: LOW");
handler.postDelayed(this::startPhase3, 400);
}
//
// Phase 3: Biometric Simulation
//
private void startPhase3() {
currentPhase = 3;
statusText.setText("Biometric authentication...");
logText.append("[AUTH] Initializing biometric subsystem...\n");
logScroll.post(() -> logScroll.fullScroll(View.FOCUS_DOWN));
// Show biometric scanner
biometricContainer.animate().alpha(1f).setDuration(300).start();
// Pulse the icon 3 times
ObjectAnimator pulse = ObjectAnimator.ofFloat(biometricIcon, "alpha", 1f, 0.3f);
pulse.setDuration(400);
pulse.setRepeatCount(5);
pulse.setRepeatMode(ObjectAnimator.REVERSE);
pulse.setInterpolator(new LinearInterpolator());
pulse.start();
handler.postDelayed(() -> {
biometricStatus.setText("SCANNING...");
biometricStatus.setTextColor(Color.parseColor("#FBBF24"));
logText.append("[AUTH] Scanning biometric data...\n");
logScroll.post(() -> logScroll.fullScroll(View.FOCUS_DOWN));
}, 600);
handler.postDelayed(() -> {
biometricStatus.setText("AUTHENTICATED ✓");
biometricStatus.setTextColor(Color.parseColor("#22C55E"));
biometricIcon.setTextColor(Color.parseColor("#22C55E"));
logText.append("[AUTH] Biometric match: CONFIRMED\n");
logText.append("[AUTH] Clearance validated: " +
(themeManager.isFoundation() ? "FOUNDATION" : "CORP") + "\n");
logScroll.post(() -> logScroll.fullScroll(View.FOCUS_DOWN));
SoundManager.getInstance().play(SoundManager.Sound.NOTIFICATION);
}, 1400);
handler.postDelayed(() -> {
// Fade out biometric
biometricContainer.animate().alpha(0f).setDuration(200).start();
startPhase4();
}, 2000);
}
//
// Phase 4: System Init
//
private void startPhase4() {
currentPhase = 4;
statusText.setText("Initializing system...");
advancePhase4(0);
}
private void advancePhase4(int index) {
if (index >= PHASE4_MSGS.length) {
startPhase5();
return;
}
logText.append(PHASE4_MSGS[index] + "\n");
logScroll.post(() -> logScroll.fullScroll(View.FOCUS_DOWN));
int totalMessages = PHASE1_MSGS.length + PHASE2_MSGS.length + 4 + PHASE4_MSGS.length;
int current = PHASE1_MSGS.length + PHASE2_MSGS.length + 4 + index + 1;
int percent = (int) ((float) current / totalMessages * 100);
progressBar.setProgress(Math.min(percent, 100));
percentText.setText(percent + "%");
String[] phase4Statuses = {
"WiFi detected", "Connecting...", "Starting services...",
"Notifications ready", "Compositor ready", "Loading apps...",
"Apps found", "Loading prefs...", "Theme ready",
"Desktop ready", "Systems nominal", "Ready"
};
if (index < phase4Statuses.length) {
statusText.setText(phase4Statuses[index]);
}
SoundManager.getInstance().play(SoundManager.Sound.BOOT_BEEP);
int delay = 70 + (int) (Math.random() * 100);
if (index == 2 || index == 5) {
delay = 250 + (int) (Math.random() * 150);
}
handler.postDelayed(() -> advancePhase4(index + 1), delay);
}
//
// Phase 5: Transition to Desktop
//
private void startPhase5() {
currentPhase = 5;
progressBar.setProgress(100);
percentText.setText("100%");
statusText.setText("Launching desktop...");
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
hasBooted = true;
handler.postDelayed(() -> {
Intent intent = new Intent(MainActivity.this, SystemActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.scale_in, android.R.anim.fade_out);
finish();
}, 600);
}
//
// Lifecycle & Utility
//
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) hideSystemUI();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (handler != null) handler.removeCallbacksAndMessages(null);
}
private void hideSystemUI() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
final WindowInsetsController controller = getWindow().getInsetsController();
if (controller != null) {
controller.hide(WindowInsets.Type.systemBars());
controller.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);
}
}
} }

View file

@ -0,0 +1,720 @@
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.os.Handler;
import android.os.Looper;
import android.text.Editable;
import android.text.TextWatcher;
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.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Marketplace -- Full-featured module store with categories, search,
* install tracking, featured banner, and ALL / INSTALLED filter tabs.
*/
public class MarketplaceActivity extends AppCompatActivity {
private ThemeManager themeManager;
private SharedPreferences marketPrefs;
private final Handler handler = new Handler(Looper.getMainLooper());
// Filter state
private boolean showInstalledOnly = false;
private String searchQuery = "";
// UI references rebuilt on every filter pass
private LinearLayout contentContainer;
private TextView tabAll;
private TextView tabInstalled;
// Module catalogue
// {name, description, price, accentColor, category, installs, rating}
private static final String[][] ITEMS = {
// SECURITY
{"Cipher Module", "End-to-end encryption toolkit", "FREE", "#22C55E", "SECURITY", "12.4k", "4.9"},
{"Shield Guard", "Firewall & intrusion detection", "FREE", "#22C55E", "SECURITY", "8.7k", "4.7"},
{"Ghost Protocol", "Stealth networking & VPN tunnel", "500 Cr", "#EF4444", "SECURITY", "3.2k", "4.6"},
{"Vault Lock", "Biometric file encryption", "250 Cr", "#06B6D4", "SECURITY", "5.1k", "4.8"},
// THEMES
{"Neon Theme Pack", "3 premium animated wallpapers", "150 Cr", "#A855F7", "THEMES", "15.9k", "4.5"},
{"Retro CRT Skin", "Vintage CRT scanline overlay", "100 Cr", "#FBBF24", "THEMES", "6.3k", "4.3"},
{"Holo Glass UI", "Holographic glassmorphism theme", "200 Cr", "#06B6D4", "THEMES", "9.1k", "4.7"},
// TOOLS
{"Data Analyzer", "Real-time analytics dashboard", "300 Cr", "#06B6D4", "TOOLS", "7.8k", "4.6"},
{"Terminal Pro", "Advanced shell with autocomplete", "FREE", "#22C55E", "TOOLS", "21.0k", "4.9"},
{"Hex Editor", "Low-level binary file editor", "200 Cr", "#F97316", "TOOLS", "4.4k", "4.4"},
{"Net Scanner", "Network topology mapper", "350 Cr", "#A855F7", "TOOLS", "2.9k", "4.5"},
// ENTERTAINMENT
{"Sound Pack Pro", "12 custom notification tones", "100 Cr", "#F97316", "ENTERTAINMENT", "10.6k", "4.2"},
{"Pixel Arcade", "Retro game emulation layer", "FREE", "#22C55E", "ENTERTAINMENT", "18.3k", "4.8"},
{"Synth Studio", "Modular synthesizer workstation", "400 Cr", "#A855F7", "ENTERTAINMENT", "1.7k", "4.7"},
};
// Featured module index (Data Analyzer)
private static final int FEATURED_INDEX = 7;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app);
hideSystemUI();
themeManager = new ThemeManager(this);
marketPrefs = getSharedPreferences("aethex_marketplace", MODE_PRIVATE);
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
TextView title = findViewById(R.id.app_title);
title.setText("Marketplace");
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 | Gravity.CENTER_HORIZONTAL);
content.setPadding(0, 0, 0, 0);
// Wrap everything in a ScrollView
ScrollView scrollView = new ScrollView(this);
scrollView.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
scrollView.setFillViewport(true);
scrollView.setOverScrollMode(View.OVER_SCROLL_NEVER);
contentContainer = new LinearLayout(this);
contentContainer.setOrientation(LinearLayout.VERTICAL);
contentContainer.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
contentContainer.setPadding(dpToPx(16), dpToPx(12), dpToPx(16), dpToPx(24));
scrollView.addView(contentContainer);
content.addView(scrollView);
buildMarketplaceUI();
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_MARKETPLACE);
}
//
// Build the full marketplace UI into contentContainer
//
private void buildMarketplaceUI() {
contentContainer.removeAllViews();
Typeface displayFont = themeManager.getDisplayFont(this);
Typeface monoFont = themeManager.getMonoFont(this);
int primaryColor = themeManager.getPrimaryColor(this);
// Search bar
EditText searchInput = createSearchBar(monoFont);
contentContainer.addView(searchInput);
// Tab row: ALL / INSTALLED
LinearLayout tabRow = new LinearLayout(this);
tabRow.setOrientation(LinearLayout.HORIZONTAL);
tabRow.setGravity(Gravity.CENTER_VERTICAL);
LinearLayout.LayoutParams tabRowParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
tabRowParams.topMargin = dpToPx(12);
tabRowParams.bottomMargin = dpToPx(16);
tabRow.setLayoutParams(tabRowParams);
tabAll = createTabButton("ALL", monoFont, !showInstalledOnly, primaryColor);
tabInstalled = createTabButton("INSTALLED", monoFont, showInstalledOnly, primaryColor);
tabAll.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
showInstalledOnly = false;
buildMarketplaceUI();
});
tabInstalled.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
showInstalledOnly = true;
buildMarketplaceUI();
});
tabRow.addView(tabAll);
tabRow.addView(tabInstalled);
contentContainer.addView(tabRow);
// Collect visible items
List<String[]> visibleItems = new ArrayList<>();
for (String[] item : ITEMS) {
if (showInstalledOnly && !isInstalled(item[0])) continue;
if (!searchQuery.isEmpty() && !item[0].toLowerCase().contains(searchQuery.toLowerCase())) continue;
visibleItems.add(item);
}
// Featured section (only when showing ALL and no search)
if (!showInstalledOnly && searchQuery.isEmpty()) {
buildFeaturedCard(contentContainer, displayFont, monoFont, primaryColor);
}
// Group by category
Map<String, List<String[]>> categorised = new LinkedHashMap<>();
for (String[] item : visibleItems) {
String cat = item[4];
categorised.computeIfAbsent(cat, k -> new ArrayList<>()).add(item);
}
if (categorised.isEmpty()) {
TextView empty = new TextView(this);
empty.setText(showInstalledOnly ? "No installed modules yet" : "No modules found");
empty.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
empty.setTextColor(Color.parseColor("#66FFFFFF"));
empty.setTypeface(monoFont);
empty.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams emptyParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
emptyParams.topMargin = dpToPx(40);
empty.setLayoutParams(emptyParams);
contentContainer.addView(empty);
return;
}
for (Map.Entry<String, List<String[]>> entry : categorised.entrySet()) {
String category = entry.getKey();
List<String[]> items = entry.getValue();
// Category header
addCategoryLabel(contentContainer, category, monoFont, primaryColor);
// Item cards
for (String[] item : items) {
contentContainer.addView(buildItemCard(item, displayFont, monoFont, primaryColor));
}
}
}
//
// Search bar
//
private EditText createSearchBar(Typeface monoFont) {
EditText input = new EditText(this);
input.setHint("Search modules...");
input.setHintTextColor(Color.parseColor("#55FFFFFF"));
input.setTextColor(Color.parseColor("#CCFFFFFF"));
input.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
input.setTypeface(monoFont);
input.setSingleLine(true);
input.setPadding(dpToPx(14), dpToPx(10), dpToPx(14), dpToPx(10));
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(dpToPx(10));
bg.setColor(Color.parseColor("#0D0F172A"));
bg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
input.setBackground(bg);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
input.setLayoutParams(params);
// Suppress soft keyboard; use AeThexKeyboard instead
input.setShowSoftInputOnFocus(false);
input.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) {
AeThexKeyboard.attachToEditText(MarketplaceActivity.this, input);
}
});
input.setOnClickListener(v -> AeThexKeyboard.attachToEditText(MarketplaceActivity.this, input));
// Real-time filtering
input.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable s) {
searchQuery = s.toString().trim();
// Rebuild content below search bar (keep search bar text intact)
rebuildListContent();
}
});
// Pre-fill if there was a previous query (after tab switch etc.)
if (!searchQuery.isEmpty()) {
input.setText(searchQuery);
input.setSelection(searchQuery.length());
}
return input;
}
/**
* Rebuild only the catalogue list content (preserving search bar state).
* Removes everything after the tab row and rebuilds categories + cards.
*/
private void rebuildListContent() {
// Remove views after index 2 (search=0, tabRow=1, rest=categories/cards)
while (contentContainer.getChildCount() > 2) {
contentContainer.removeViewAt(2);
}
Typeface displayFont = themeManager.getDisplayFont(this);
Typeface monoFont = themeManager.getMonoFont(this);
int primaryColor = themeManager.getPrimaryColor(this);
List<String[]> visibleItems = new ArrayList<>();
for (String[] item : ITEMS) {
if (showInstalledOnly && !isInstalled(item[0])) continue;
if (!searchQuery.isEmpty() && !item[0].toLowerCase().contains(searchQuery.toLowerCase())) continue;
visibleItems.add(item);
}
// Featured section only when ALL + no search
if (!showInstalledOnly && searchQuery.isEmpty()) {
buildFeaturedCard(contentContainer, displayFont, monoFont, primaryColor);
}
Map<String, List<String[]>> categorised = new LinkedHashMap<>();
for (String[] item : visibleItems) {
String cat = item[4];
categorised.computeIfAbsent(cat, k -> new ArrayList<>()).add(item);
}
if (categorised.isEmpty()) {
TextView empty = new TextView(this);
empty.setText(showInstalledOnly ? "No installed modules yet" : "No modules found");
empty.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
empty.setTextColor(Color.parseColor("#66FFFFFF"));
empty.setTypeface(monoFont);
empty.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams emptyParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
emptyParams.topMargin = dpToPx(40);
empty.setLayoutParams(emptyParams);
contentContainer.addView(empty);
return;
}
for (Map.Entry<String, List<String[]>> entry : categorised.entrySet()) {
addCategoryLabel(contentContainer, entry.getKey(), monoFont, primaryColor);
for (String[] item : entry.getValue()) {
contentContainer.addView(buildItemCard(item, displayFont, monoFont, primaryColor));
}
}
}
//
// Tab button factory
//
private TextView createTabButton(String label, Typeface monoFont, boolean active, int primaryColor) {
TextView tab = new TextView(this);
tab.setText(label);
tab.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
tab.setTypeface(monoFont, Typeface.BOLD);
tab.setLetterSpacing(0.1f);
tab.setGravity(Gravity.CENTER);
tab.setPadding(dpToPx(16), dpToPx(8), dpToPx(16), dpToPx(8));
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(dpToPx(8));
if (active) {
int r = Color.red(primaryColor);
int g = Color.green(primaryColor);
int b = Color.blue(primaryColor);
bg.setColor(Color.argb(38, r, g, b));
bg.setStroke(dpToPx(1), primaryColor);
tab.setTextColor(primaryColor);
} else {
bg.setColor(Color.parseColor("#0DFFFFFF"));
bg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
tab.setTextColor(Color.parseColor("#66FFFFFF"));
}
tab.setBackground(bg);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMarginEnd(dpToPx(8));
tab.setLayoutParams(params);
return tab;
}
//
// Featured card
//
private void buildFeaturedCard(LinearLayout parent, Typeface displayFont,
Typeface monoFont, int primaryColor) {
String[] feat = ITEMS[FEATURED_INDEX];
// Label
TextView featLabel = new TextView(this);
featLabel.setText("FEATURED");
featLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
featLabel.setTextColor(primaryColor);
featLabel.setTypeface(monoFont, Typeface.BOLD);
featLabel.setLetterSpacing(0.2f);
LinearLayout.LayoutParams flParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
flParams.bottomMargin = dpToPx(8);
featLabel.setLayoutParams(flParams);
parent.addView(featLabel);
// Card container
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
cardParams.bottomMargin = dpToPx(20);
card.setLayoutParams(cardParams);
card.setPadding(dpToPx(18), dpToPx(18), dpToPx(18), dpToPx(18));
// Gradient accent border
GradientDrawable cardBg = new GradientDrawable();
cardBg.setCornerRadius(dpToPx(14));
cardBg.setColor(Color.parseColor("#0D0F172A"));
int r = Color.red(primaryColor);
int g = Color.green(primaryColor);
int b = Color.blue(primaryColor);
cardBg.setStroke(dpToPx(2), Color.argb(100, r, g, b));
card.setBackground(cardBg);
// Top row: category badge
TextView catBadge = new TextView(this);
catBadge.setText(feat[4]);
catBadge.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
catBadge.setTextColor(primaryColor);
catBadge.setTypeface(monoFont, Typeface.BOLD);
catBadge.setLetterSpacing(0.15f);
catBadge.setPadding(dpToPx(8), dpToPx(3), dpToPx(8), dpToPx(3));
GradientDrawable catBg = new GradientDrawable();
catBg.setCornerRadius(dpToPx(4));
catBg.setColor(Color.argb(25, r, g, b));
catBadge.setBackground(catBg);
LinearLayout.LayoutParams catParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
catParams.bottomMargin = dpToPx(10);
catBadge.setLayoutParams(catParams);
card.addView(catBadge);
// Name
TextView name = new TextView(this);
name.setText(feat[0]);
name.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
name.setTextColor(Color.parseColor("#EEFFFFFF"));
name.setTypeface(displayFont, Typeface.BOLD);
card.addView(name);
// Description
TextView desc = new TextView(this);
desc.setText(feat[1]);
desc.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
desc.setTextColor(Color.parseColor("#88FFFFFF"));
desc.setTypeface(monoFont);
LinearLayout.LayoutParams descParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
descParams.topMargin = dpToPx(4);
desc.setLayoutParams(descParams);
card.addView(desc);
// Stats row
TextView stats = new TextView(this);
stats.setText(feat[5] + " installs \u2605 " + feat[6]);
stats.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
stats.setTextColor(Color.parseColor("#55FFFFFF"));
stats.setTypeface(monoFont);
LinearLayout.LayoutParams stParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
stParams.topMargin = dpToPx(6);
stats.setLayoutParams(stParams);
card.addView(stats);
// Price / Install button row
LinearLayout btnRow = new LinearLayout(this);
btnRow.setOrientation(LinearLayout.HORIZONTAL);
btnRow.setGravity(Gravity.END | Gravity.CENTER_VERTICAL);
LinearLayout.LayoutParams btnRowParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
btnRowParams.topMargin = dpToPx(14);
btnRow.setLayoutParams(btnRowParams);
TextView priceBtn = createPriceButton(feat, monoFont, primaryColor);
btnRow.addView(priceBtn);
card.addView(btnRow);
card.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
handleInstall(feat, priceBtn, monoFont, primaryColor);
});
parent.addView(card);
}
//
// Category label
//
private void addCategoryLabel(LinearLayout parent, String category,
Typeface monoFont, int primaryColor) {
String colorForCategory;
switch (category) {
case "SECURITY": colorForCategory = "#EF4444"; break;
case "THEMES": colorForCategory = "#A855F7"; break;
case "TOOLS": colorForCategory = "#06B6D4"; break;
case "ENTERTAINMENT": colorForCategory = "#FBBF24"; break;
default: colorForCategory = "#66FFFFFF"; break;
}
LinearLayout row = new LinearLayout(this);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(Gravity.CENTER_VERTICAL);
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rowParams.topMargin = dpToPx(16);
rowParams.bottomMargin = dpToPx(8);
row.setLayoutParams(rowParams);
// Accent dot
View dot = new View(this);
int dotSize = dpToPx(6);
LinearLayout.LayoutParams dotParams = new LinearLayout.LayoutParams(dotSize, dotSize);
dotParams.setMarginEnd(dpToPx(8));
dot.setLayoutParams(dotParams);
GradientDrawable dotBg = new GradientDrawable();
dotBg.setShape(GradientDrawable.OVAL);
dotBg.setColor(Color.parseColor(colorForCategory));
dot.setBackground(dotBg);
row.addView(dot);
TextView label = new TextView(this);
label.setText(category);
label.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
label.setTextColor(Color.parseColor(colorForCategory));
label.setTypeface(monoFont, Typeface.BOLD);
label.setLetterSpacing(0.15f);
row.addView(label);
// Separator line
View line = new View(this);
LinearLayout.LayoutParams lineParams = new LinearLayout.LayoutParams(
0, dpToPx(1), 1f);
lineParams.setMarginStart(dpToPx(10));
line.setLayoutParams(lineParams);
line.setBackgroundColor(Color.parseColor("#1AFFFFFF"));
row.addView(line);
parent.addView(row);
}
//
// Regular item card
//
private LinearLayout buildItemCard(String[] item, Typeface displayFont,
Typeface monoFont, int primaryColor) {
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(16), 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);
// 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(item[0]);
name.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13);
name.setTextColor(Color.parseColor("#CCFFFFFF"));
name.setTypeface(displayFont);
info.addView(name);
TextView desc = new TextView(this);
desc.setText(item[1]);
desc.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
desc.setTextColor(Color.parseColor("#66FFFFFF"));
desc.setTypeface(monoFont);
LinearLayout.LayoutParams dParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
dParams.topMargin = dpToPx(2);
desc.setLayoutParams(dParams);
info.addView(desc);
// Stats line: installs + rating
TextView stats = new TextView(this);
stats.setText(item[5] + " installs \u2605 " + item[6]);
stats.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
stats.setTextColor(Color.parseColor("#44FFFFFF"));
stats.setTypeface(monoFont);
LinearLayout.LayoutParams sParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
sParams.topMargin = dpToPx(3);
stats.setLayoutParams(sParams);
info.addView(stats);
card.addView(info);
// Price / install button
TextView priceBtn = createPriceButton(item, monoFont, primaryColor);
card.addView(priceBtn);
card.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
handleInstall(item, priceBtn, monoFont, primaryColor);
});
return card;
}
//
// Price button
//
private TextView createPriceButton(String[] item, Typeface monoFont, int primaryColor) {
TextView price = new TextView(this);
price.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
price.setTypeface(monoFont, Typeface.BOLD);
price.setGravity(Gravity.CENTER);
price.setPadding(dpToPx(12), dpToPx(6), dpToPx(12), dpToPx(6));
boolean installed = isInstalled(item[0]);
String accentHex = item[3];
if (installed) {
price.setText("INSTALLED \u2713");
price.setTextColor(Color.parseColor("#22C55E"));
GradientDrawable pBg = new GradientDrawable();
pBg.setCornerRadius(dpToPx(6));
pBg.setColor(Color.parseColor("#1A22C55E"));
pBg.setStroke(dpToPx(1), Color.parseColor("#22C55E"));
price.setBackground(pBg);
} else {
price.setText(item[2]);
price.setTextColor(Color.parseColor(accentHex));
GradientDrawable pBg = new GradientDrawable();
pBg.setCornerRadius(dpToPx(6));
pBg.setColor(Color.parseColor("#1A000000"));
pBg.setStroke(dpToPx(1), Color.parseColor(accentHex));
price.setBackground(pBg);
}
return price;
}
//
// Install logic
//
private void handleInstall(String[] item, TextView priceBtn, Typeface monoFont, int primaryColor) {
if (isInstalled(item[0])) {
AeThexToast.show(this, item[0] + " is already installed", AeThexToast.Type.INFO);
return;
}
// Set to "Installing..." state
priceBtn.setText("Installing...");
priceBtn.setTextColor(Color.parseColor("#FBBF24"));
GradientDrawable loadingBg = new GradientDrawable();
loadingBg.setCornerRadius(dpToPx(6));
loadingBg.setColor(Color.parseColor("#1AFBBF24"));
loadingBg.setStroke(dpToPx(1), Color.parseColor("#FBBF24"));
priceBtn.setBackground(loadingBg);
// After 500ms, mark as installed
handler.postDelayed(() -> {
setInstalled(item[0], true);
priceBtn.setText("INSTALLED \u2713");
priceBtn.setTextColor(Color.parseColor("#22C55E"));
GradientDrawable doneBg = new GradientDrawable();
doneBg.setCornerRadius(dpToPx(6));
doneBg.setColor(Color.parseColor("#1A22C55E"));
doneBg.setStroke(dpToPx(1), Color.parseColor("#22C55E"));
priceBtn.setBackground(doneBg);
AeThexToast.show(this, item[0] + " installed successfully", AeThexToast.Type.SUCCESS);
}, 500);
}
//
// SharedPreferences helpers
//
private boolean isInstalled(String moduleName) {
return marketPrefs.getBoolean("installed_" + moduleName, false);
}
private void setInstalled(String moduleName, boolean installed) {
marketPrefs.edit().putBoolean("installed_" + moduleName, installed).apply();
}
//
// Utility
//
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);
}
}
}

View file

@ -0,0 +1,362 @@
package com.aethex.os;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.Random;
public class MinesweeperActivity extends AppCompatActivity {
private ThemeManager themeManager;
private static final int ROWS = 9;
private static final int COLS = 9;
private static final int MINE_COUNT = 10;
private int[][] board; // -1 = mine, 0-8 = adjacent mines
private boolean[][] revealed;
private boolean[][] flagged;
private boolean gameOver;
private boolean gameWon;
private boolean flagMode;
private int minesRemaining;
private int secondsElapsed;
private boolean timerRunning;
private MineFieldView mineFieldView;
private TextView mineCountText;
private TextView timerText;
private TextView flagToggle;
private Handler timerHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_minesweeper);
hideSystemUI();
themeManager = new ThemeManager(this);
mineCountText = findViewById(R.id.mine_count);
timerText = findViewById(R.id.mine_timer);
flagToggle = findViewById(R.id.mine_flag_toggle);
timerHandler = new Handler(Looper.getMainLooper());
findViewById(R.id.mine_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
findViewById(R.id.mine_new_game).setOnClickListener(v -> {
v.animate().scaleX(0.9f).scaleY(0.9f).setDuration(60).withEndAction(() -> {
v.animate().scaleX(1f).scaleY(1f).setDuration(60).start();
startNewGame();
}).start();
});
flagToggle.setOnClickListener(v -> {
flagMode = !flagMode;
flagToggle.setText(flagMode ? "MODE: FLAG" : "MODE: REVEAL");
flagToggle.setTextColor(flagMode ? 0xFFF87171 : 0xFFFFFFFF);
});
FrameLayout container = findViewById(R.id.mine_board_container);
mineFieldView = new MineFieldView(this);
container.addView(mineFieldView);
startNewGame();
// Entrance animation
View root = findViewById(R.id.minesweeper_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
}
private void startNewGame() {
board = new int[ROWS][COLS];
revealed = new boolean[ROWS][COLS];
flagged = new boolean[ROWS][COLS];
gameOver = false;
gameWon = false;
flagMode = false;
minesRemaining = MINE_COUNT;
secondsElapsed = 0;
timerRunning = false;
flagToggle.setText("MODE: REVEAL");
flagToggle.setTextColor(0xFFFFFFFF);
updateMineCount();
timerText.setText("0:00");
timerHandler.removeCallbacksAndMessages(null);
// Place mines
Random rand = new Random();
int placed = 0;
while (placed < MINE_COUNT) {
int r = rand.nextInt(ROWS);
int c = rand.nextInt(COLS);
if (board[r][c] != -1) {
board[r][c] = -1;
placed++;
}
}
// Calculate numbers
for (int r = 0; r < ROWS; r++) {
for (int c = 0; c < COLS; c++) {
if (board[r][c] == -1) continue;
int count = 0;
for (int dr = -1; dr <= 1; dr++) {
for (int dc = -1; dc <= 1; dc++) {
int nr = r + dr, nc = c + dc;
if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && board[nr][nc] == -1) {
count++;
}
}
}
board[r][c] = count;
}
}
if (mineFieldView != null) mineFieldView.invalidate();
}
private void startTimer() {
if (timerRunning) return;
timerRunning = true;
timerHandler.post(new Runnable() {
@Override
public void run() {
if (!timerRunning || gameOver || gameWon) return;
secondsElapsed++;
int mins = secondsElapsed / 60;
int secs = secondsElapsed % 60;
timerText.setText(String.format("%d:%02d", mins, secs));
timerHandler.postDelayed(this, 1000);
}
});
}
private void handleCellClick(int row, int col) {
if (gameOver || gameWon) return;
if (row < 0 || row >= ROWS || col < 0 || col >= COLS) return;
if (!timerRunning) startTimer();
if (flagMode) {
if (!revealed[row][col]) {
flagged[row][col] = !flagged[row][col];
minesRemaining += flagged[row][col] ? -1 : 1;
updateMineCount();
mineFieldView.invalidate();
}
return;
}
if (flagged[row][col] || revealed[row][col]) return;
if (board[row][col] == -1) {
// Hit a mine - game over
gameOver = true;
timerRunning = false;
revealAll();
mineFieldView.invalidate();
Toast.makeText(this, "Game Over! Tap NEW to restart.", Toast.LENGTH_SHORT).show();
return;
}
reveal(row, col);
checkWin();
mineFieldView.invalidate();
}
private void reveal(int row, int col) {
if (row < 0 || row >= ROWS || col < 0 || col >= COLS) return;
if (revealed[row][col] || flagged[row][col]) return;
revealed[row][col] = true;
if (board[row][col] == 0) {
for (int dr = -1; dr <= 1; dr++) {
for (int dc = -1; dc <= 1; dc++) {
reveal(row + dr, col + dc);
}
}
}
}
private void revealAll() {
for (int r = 0; r < ROWS; r++) {
for (int c = 0; c < COLS; c++) {
revealed[r][c] = true;
}
}
}
private void checkWin() {
int unrevealed = 0;
for (int r = 0; r < ROWS; r++) {
for (int c = 0; c < COLS; c++) {
if (!revealed[r][c]) unrevealed++;
}
}
if (unrevealed == MINE_COUNT) {
gameWon = true;
timerRunning = false;
Toast.makeText(this, "You Win! Time: " + timerText.getText(), Toast.LENGTH_LONG).show();
}
}
private void updateMineCount() {
mineCountText.setText("Mines: " + minesRemaining);
}
private class MineFieldView extends View {
private Paint cellPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Paint gridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private RectF cellRect = new RectF();
private int[] NUMBER_COLORS;
public MineFieldView(Context context) {
super(context);
int primary = themeManager.getPrimaryColor(context);
NUMBER_COLORS = new int[] {
0x00000000, // 0 - unused
primary, // 1 - themed primary
0xFF34D399, // 2 - green
0xFFF87171, // 3 - red
0xFFA855F7, // 4 - purple
0xFFFBBF24, // 5 - yellow
primary, // 6 - themed primary
0xFFFFFFFF, // 7 - white
0xFF94A3B8, // 8 - gray
};
gridPaint.setColor(0x1AFFFFFF);
gridPaint.setStyle(Paint.Style.STROKE);
gridPaint.setStrokeWidth(1f);
textPaint.setTextAlign(Paint.Align.CENTER);
textPaint.setTypeface(android.graphics.Typeface.create("sans-serif-medium", android.graphics.Typeface.BOLD));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (board == null) return;
float w = getWidth();
float h = getHeight();
float cellSize = Math.min(w / COLS, h / ROWS);
float offsetX = (w - cellSize * COLS) / 2;
float offsetY = (h - cellSize * ROWS) / 2;
float radius = cellSize * 0.15f;
textPaint.setTextSize(cellSize * 0.45f);
for (int r = 0; r < ROWS; r++) {
for (int c = 0; c < COLS; c++) {
float x = offsetX + c * cellSize;
float y = offsetY + r * cellSize;
cellRect.set(x + 2, y + 2, x + cellSize - 2, y + cellSize - 2);
if (revealed[r][c]) {
if (board[r][c] == -1) {
// Mine
cellPaint.setColor(gameOver ? 0x40F87171 : 0x1AFFFFFF);
canvas.drawRoundRect(cellRect, radius, radius, cellPaint);
textPaint.setColor(0xFFF87171);
canvas.drawText("\u25CF", cellRect.centerX(),
cellRect.centerY() + textPaint.getTextSize() / 3, textPaint);
} else if (board[r][c] > 0) {
// Number
cellPaint.setColor(0x0DFFFFFF);
canvas.drawRoundRect(cellRect, radius, radius, cellPaint);
textPaint.setColor(NUMBER_COLORS[board[r][c]]);
canvas.drawText(String.valueOf(board[r][c]), cellRect.centerX(),
cellRect.centerY() + textPaint.getTextSize() / 3, textPaint);
} else {
// Empty revealed
cellPaint.setColor(0x08FFFFFF);
canvas.drawRoundRect(cellRect, radius, radius, cellPaint);
}
} else if (flagged[r][c]) {
// Flagged
cellPaint.setColor(0x26F87171);
canvas.drawRoundRect(cellRect, radius, radius, cellPaint);
textPaint.setColor(0xFFF87171);
canvas.drawText("\u2691", cellRect.centerX(),
cellRect.centerY() + textPaint.getTextSize() / 3, textPaint);
} else {
// Unrevealed
cellPaint.setColor(0x1AFFFFFF);
canvas.drawRoundRect(cellRect, radius, radius, cellPaint);
}
// Grid outline
canvas.drawRoundRect(cellRect, radius, radius, gridPaint);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
float w = getWidth();
float h = getHeight();
float cellSize = Math.min(w / COLS, h / ROWS);
float offsetX = (w - cellSize * COLS) / 2;
float offsetY = (h - cellSize * ROWS) / 2;
int col = (int) ((event.getX() - offsetX) / cellSize);
int row = (int) ((event.getY() - offsetY) / cellSize);
handleCellClick(row, col);
return true;
}
return true;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
timerRunning = false;
timerHandler.removeCallbacksAndMessages(null);
}
@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);
}
}
}

View file

@ -0,0 +1,156 @@
package com.aethex.os;
import android.content.Context;
import android.content.SharedPreferences;
/**
* ModuleManager - Tracks installed marketplace modules and provides their functionality.
* Modules actually affect system behavior when installed.
*/
public class ModuleManager {
private static ModuleManager instance;
private SharedPreferences prefs;
// Module IDs (must match marketplace item names exactly)
public static final String CIPHER_MODULE = "Cipher Module";
public static final String SHIELD_GUARD = "Shield Guard";
public static final String GHOST_PROTOCOL = "Ghost Protocol";
public static final String VAULT_LOCK = "Vault Lock";
public static final String NEON_THEME_PACK = "Neon Theme Pack";
public static final String RETRO_CRT_SKIN = "Retro CRT Skin";
public static final String HOLO_GLASS_UI = "Holo Glass UI";
public static final String DATA_ANALYZER = "Data Analyzer";
public static final String TERMINAL_PRO = "Terminal Pro";
public static final String HEX_EDITOR = "Hex Editor";
public static final String NET_SCANNER = "Net Scanner";
public static final String SOUND_PACK_PRO = "Sound Pack Pro";
public static final String PIXEL_ARCADE = "Pixel Arcade";
public static final String SYNTH_STUDIO = "Synth Studio";
private ModuleManager(Context context) {
prefs = context.getSharedPreferences("aethex_marketplace", Context.MODE_PRIVATE);
}
public static ModuleManager getInstance(Context context) {
if (instance == null) {
instance = new ModuleManager(context.getApplicationContext());
}
return instance;
}
public boolean isInstalled(String moduleName) {
return prefs.getBoolean("installed_" + moduleName, false);
}
public void setInstalled(String moduleName, boolean installed) {
prefs.edit().putBoolean("installed_" + moduleName, installed).apply();
}
//
// Module feature checks
//
/** Cipher Module - Enables encrypted notes in Projects */
public boolean hasEncryption() {
return isInstalled(CIPHER_MODULE);
}
/** Shield Guard - Enables app lock / privacy features */
public boolean hasAppLock() {
return isInstalled(SHIELD_GUARD);
}
/** Ghost Protocol - Enables stealth mode (hide recent apps, etc.) */
public boolean hasStealthMode() {
return isInstalled(GHOST_PROTOCOL);
}
/** Vault Lock - Enables biometric file lock */
public boolean hasBiometricLock() {
return isInstalled(VAULT_LOCK);
}
/** Neon Theme Pack - Unlocks neon wallpapers */
public boolean hasNeonThemes() {
return isInstalled(NEON_THEME_PACK);
}
/** Retro CRT Skin - Enables CRT scanline overlay */
public boolean hasCRTOverlay() {
return isInstalled(RETRO_CRT_SKIN);
}
/** Holo Glass UI - Enables extra glassmorphism effects */
public boolean hasHoloGlass() {
return isInstalled(HOLO_GLASS_UI);
}
/** Data Analyzer - Enables system stats widget */
public boolean hasDataAnalyzer() {
return isInstalled(DATA_ANALYZER);
}
/** Terminal Pro - Enables advanced terminal features */
public boolean hasTerminalPro() {
return isInstalled(TERMINAL_PRO);
}
/** Hex Editor - Enables hex view in file browser */
public boolean hasHexEditor() {
return isInstalled(HEX_EDITOR);
}
/** Net Scanner - Enables network scanning tools */
public boolean hasNetScanner() {
return isInstalled(NET_SCANNER);
}
/** Sound Pack Pro - Unlocks extra sound effects */
public boolean hasSoundPackPro() {
return isInstalled(SOUND_PACK_PRO);
}
/** Pixel Arcade - Enables games section */
public boolean hasPixelArcade() {
return isInstalled(PIXEL_ARCADE);
}
/** Synth Studio - Enables synth app */
public boolean hasSynthStudio() {
return isInstalled(SYNTH_STUDIO);
}
//
// Module count helpers
//
public int getInstalledCount() {
int count = 0;
String[] modules = {
CIPHER_MODULE, SHIELD_GUARD, GHOST_PROTOCOL, VAULT_LOCK,
NEON_THEME_PACK, RETRO_CRT_SKIN, HOLO_GLASS_UI,
DATA_ANALYZER, TERMINAL_PRO, HEX_EDITOR, NET_SCANNER,
SOUND_PACK_PRO, PIXEL_ARCADE, SYNTH_STUDIO
};
for (String mod : modules) {
if (isInstalled(mod)) count++;
}
return count;
}
public int getTotalCreditsSpent() {
int total = 0;
if (isInstalled(GHOST_PROTOCOL)) total += 500;
if (isInstalled(VAULT_LOCK)) total += 250;
if (isInstalled(NEON_THEME_PACK)) total += 150;
if (isInstalled(RETRO_CRT_SKIN)) total += 100;
if (isInstalled(HOLO_GLASS_UI)) total += 200;
if (isInstalled(DATA_ANALYZER)) total += 300;
if (isInstalled(HEX_EDITOR)) total += 200;
if (isInstalled(NET_SCANNER)) total += 350;
if (isInstalled(SOUND_PACK_PRO)) total += 100;
if (isInstalled(SYNTH_STUDIO)) total += 400;
return total;
}
}

View file

@ -0,0 +1,452 @@
package com.aethex.os;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Gravity;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
public class MusicActivity extends AppCompatActivity {
private ThemeManager themeManager;
// Track data arrays
private final String[] trackTitles = {
"Neon Horizons", "Midnight Protocol", "Binary Sunset",
"Electric Dreams", "Data Stream"
};
private final String[] trackArtists = {
"Digital Pulse", "CyberWave", "The Algorithms",
"Neural Network", "Quantum Loop"
};
private final String[] trackAlbums = {
"Synthwave Collection", "Dark Net Sessions", "Algorithmic Beats",
"Neural Pathways", "Quantum Frequencies"
};
// Durations in seconds
private final int[] trackDurations = { 225, 252, 178, 323, 211 };
private int currentTrack = 0;
private boolean isPlaying = false;
private boolean shuffleOn = false;
private boolean repeatOn = false;
private int elapsedMillis = 0;
private TextView trackTitle;
private TextView trackArtist;
private TextView trackAlbum;
private TextView timeElapsed;
private TextView timeTotal;
private TextView playPauseBtn;
private TextView shuffleBtn;
private TextView repeatBtn;
private SeekBar seekBar;
private View albumArt;
private LinearLayout upNextContainer;
private final Handler handler = new Handler(Looper.getMainLooper());
private final Runnable playbackRunnable = new Runnable() {
@Override
public void run() {
if (!isPlaying) return;
elapsedMillis += 100;
int totalMillis = trackDurations[currentTrack] * 1000;
if (elapsedMillis >= totalMillis) {
// Track finished
if (repeatOn) {
elapsedMillis = 0;
} else {
nextTrack();
return;
}
}
updateSeekBar();
updateTimeLabels();
handler.postDelayed(this, 100);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music);
hideSystemUI();
themeManager = new ThemeManager(this);
// Find views
trackTitle = findViewById(R.id.music_track_title);
trackArtist = findViewById(R.id.music_track_artist);
trackAlbum = findViewById(R.id.music_track_album);
timeElapsed = findViewById(R.id.music_time_elapsed);
timeTotal = findViewById(R.id.music_time_total);
playPauseBtn = findViewById(R.id.music_play_pause);
shuffleBtn = findViewById(R.id.music_shuffle);
repeatBtn = findViewById(R.id.music_repeat);
seekBar = findViewById(R.id.music_seekbar);
albumArt = findViewById(R.id.music_album_art);
upNextContainer = findViewById(R.id.music_up_next_container);
// Back button
findViewById(R.id.music_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Setup album art gradient
setupAlbumArt();
// Setup play/pause button background (larger circle)
setupPlayPauseBackground();
// Display initial track
displayTrack(currentTrack);
// Build up-next list
buildUpNextList();
// --- Transport controls ---
playPauseBtn.setOnClickListener(v -> {
v.animate().scaleX(0.9f).scaleY(0.9f).setDuration(60).withEndAction(() ->
v.animate().scaleX(1f).scaleY(1f).setDuration(60).start()
).start();
togglePlayPause();
});
findViewById(R.id.music_next).setOnClickListener(v -> {
v.animate().scaleX(0.85f).scaleY(0.85f).setDuration(60).withEndAction(() ->
v.animate().scaleX(1f).scaleY(1f).setDuration(60).start()
).start();
nextTrack();
});
findViewById(R.id.music_prev).setOnClickListener(v -> {
v.animate().scaleX(0.85f).scaleY(0.85f).setDuration(60).withEndAction(() ->
v.animate().scaleX(1f).scaleY(1f).setDuration(60).start()
).start();
prevTrack();
});
shuffleBtn.setOnClickListener(v -> {
shuffleOn = !shuffleOn;
shuffleBtn.setTextColor(shuffleOn ? themeManager.getPrimaryColor(MusicActivity.this) : 0x66FFFFFF);
});
repeatBtn.setOnClickListener(v -> {
repeatOn = !repeatOn;
repeatBtn.setTextColor(repeatOn ? themeManager.getPrimaryColor(MusicActivity.this) : 0x66FFFFFF);
});
// SeekBar listener
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar sb, int progress, boolean fromUser) {
if (fromUser) {
int totalMillis = trackDurations[currentTrack] * 1000;
elapsedMillis = (int) ((progress / 1000f) * totalMillis);
updateTimeLabels();
}
}
@Override
public void onStartTrackingTouch(SeekBar sb) {}
@Override
public void onStopTrackingTouch(SeekBar sb) {}
});
// Entrance animation
View root = findViewById(R.id.music_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
}
private void setupAlbumArt() {
int primary = themeManager.getPrimaryColor(this);
GradientDrawable gradient = new GradientDrawable(
GradientDrawable.Orientation.TL_BR,
new int[] { primary, 0xFF7C3AED, 0xFFA855F7 }
);
gradient.setCornerRadius(dpToPx(16));
albumArt.setBackground(gradient);
}
private void setupPlayPauseBackground() {
GradientDrawable circle = new GradientDrawable();
circle.setShape(GradientDrawable.OVAL);
circle.setColor(0x33FFFFFF);
circle.setStroke(dpToPx(1), 0x4DFFFFFF);
playPauseBtn.setBackground(circle);
}
private void displayTrack(int index) {
trackTitle.setText(trackTitles[index]);
trackArtist.setText(trackArtists[index]);
trackAlbum.setText(trackAlbums[index]);
timeTotal.setText(formatTime(trackDurations[index]));
timeElapsed.setText("0:00");
seekBar.setProgress(0);
elapsedMillis = 0;
// Animate the album art with a subtle color shift per track
updateAlbumArtForTrack(index);
// Animate track title in
trackTitle.setAlpha(0f);
trackTitle.setTranslationY(10f);
trackTitle.animate().alpha(1f).translationY(0f).setDuration(200).start();
trackArtist.setAlpha(0f);
trackArtist.animate().alpha(1f).setStartDelay(50).setDuration(200).start();
trackAlbum.setAlpha(0f);
trackAlbum.animate().alpha(1f).setStartDelay(100).setDuration(200).start();
}
private void updateAlbumArtForTrack(int index) {
// Each track gets a slightly different gradient
int primary = themeManager.getPrimaryColor(this);
int[][] gradients = {
{ primary, 0xFF7C3AED, 0xFFA855F7 }, // Primary to purple
{ 0xFF1E40AF, 0xFF6D28D9, 0xFFDB2777 }, // Blue to pink
{ 0xFF059669, 0xFF0891B2, 0xFF6366F1 }, // Green to indigo
{ 0xFFF59E0B, 0xFFEF4444, 0xFF8B5CF6 }, // Amber to purple
{ primary, 0xFF3B82F6, 0xFF8B5CF6 }, // Primary to blue to violet
};
GradientDrawable gradient = new GradientDrawable(
GradientDrawable.Orientation.TL_BR,
gradients[index % gradients.length]
);
gradient.setCornerRadius(dpToPx(16));
albumArt.setBackground(gradient);
// Subtle scale animation
albumArt.setScaleX(0.95f);
albumArt.setScaleY(0.95f);
albumArt.animate().scaleX(1f).scaleY(1f).setDuration(300).start();
}
private void buildUpNextList() {
upNextContainer.removeAllViews();
// Show up to 4 tracks after the current one
int count = 0;
for (int i = 0; i < trackTitles.length && count < 4; i++) {
if (i == currentTrack) continue;
final int trackIndex = i;
LinearLayout row = new LinearLayout(this);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(Gravity.CENTER_VERTICAL);
row.setPadding(dpToPx(12), dpToPx(10), dpToPx(12), dpToPx(10));
// Track number indicator
TextView numView = new TextView(this);
numView.setText(String.valueOf(i + 1));
numView.setTextSize(12);
numView.setTextColor(0x66FFFFFF);
numView.setTypeface(themeManager.getMonoFont(MusicActivity.this));
numView.setWidth(dpToPx(24));
row.addView(numView);
// Track info column
LinearLayout infoCol = new LinearLayout(this);
infoCol.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams infoParams = new LinearLayout.LayoutParams(
0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f);
infoCol.setLayoutParams(infoParams);
TextView titleView = new TextView(this);
titleView.setText(trackTitles[i]);
titleView.setTextSize(13);
titleView.setTextColor(0xCCFFFFFF);
titleView.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
titleView.setSingleLine(true);
infoCol.addView(titleView);
TextView artistView = new TextView(this);
artistView.setText(trackArtists[i]);
artistView.setTextSize(11);
artistView.setTextColor(0x99FFFFFF);
artistView.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
artistView.setSingleLine(true);
infoCol.addView(artistView);
row.addView(infoCol);
// Duration
TextView durView = new TextView(this);
durView.setText(formatTime(trackDurations[i]));
durView.setTextSize(11);
durView.setTextColor(0x66FFFFFF);
durView.setTypeface(themeManager.getMonoFont(MusicActivity.this));
row.addView(durView);
// Tap to jump to track
row.setOnClickListener(v -> {
boolean wasPlaying = isPlaying;
if (isPlaying) {
isPlaying = false;
handler.removeCallbacks(playbackRunnable);
}
currentTrack = trackIndex;
displayTrack(currentTrack);
buildUpNextList();
if (wasPlaying) {
togglePlayPause();
}
});
upNextContainer.addView(row);
// Add divider between items (not after last)
count++;
if (count < 4 && (i + 1 < trackTitles.length || count < 3)) {
View divider = new View(this);
divider.setBackgroundColor(0x1AFFFFFF);
LinearLayout.LayoutParams divParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, dpToPx(1));
divParams.setMarginStart(dpToPx(36));
divParams.setMarginEnd(dpToPx(12));
divider.setLayoutParams(divParams);
upNextContainer.addView(divider);
}
}
}
private void togglePlayPause() {
isPlaying = !isPlaying;
if (isPlaying) {
playPauseBtn.setText("\u23F8"); // Pause symbol
handler.post(playbackRunnable);
} else {
playPauseBtn.setText("\u25B6"); // Play symbol
handler.removeCallbacks(playbackRunnable);
}
}
private void nextTrack() {
boolean wasPlaying = isPlaying;
isPlaying = false;
handler.removeCallbacks(playbackRunnable);
if (shuffleOn) {
int next;
do {
next = (int) (Math.random() * trackTitles.length);
} while (next == currentTrack && trackTitles.length > 1);
currentTrack = next;
} else {
currentTrack = (currentTrack + 1) % trackTitles.length;
}
displayTrack(currentTrack);
buildUpNextList();
if (wasPlaying) {
togglePlayPause();
}
}
private void prevTrack() {
boolean wasPlaying = isPlaying;
isPlaying = false;
handler.removeCallbacks(playbackRunnable);
// If more than 3 seconds in, restart current track; else go to previous
if (elapsedMillis > 3000) {
elapsedMillis = 0;
updateSeekBar();
updateTimeLabels();
if (wasPlaying) {
togglePlayPause();
}
return;
}
if (shuffleOn) {
int prev;
do {
prev = (int) (Math.random() * trackTitles.length);
} while (prev == currentTrack && trackTitles.length > 1);
currentTrack = prev;
} else {
currentTrack = (currentTrack - 1 + trackTitles.length) % trackTitles.length;
}
displayTrack(currentTrack);
buildUpNextList();
if (wasPlaying) {
togglePlayPause();
}
}
private void updateSeekBar() {
int totalMillis = trackDurations[currentTrack] * 1000;
if (totalMillis > 0) {
int progress = (int) ((elapsedMillis / (float) totalMillis) * 1000);
seekBar.setProgress(Math.min(progress, 1000));
}
}
private void updateTimeLabels() {
int elapsedSec = elapsedMillis / 1000;
timeElapsed.setText(formatTime(elapsedSec));
}
private String formatTime(int totalSeconds) {
int minutes = totalSeconds / 60;
int seconds = totalSeconds % 60;
return minutes + ":" + String.format("%02d", seconds);
}
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacks(playbackRunnable);
}
@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);
}
}
}

View file

@ -0,0 +1,340 @@
package com.aethex.os;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class NotesActivity extends AppCompatActivity {
private ThemeManager themeManager;
private static final String PREFS_NAME = "aethex_notes";
private static final String KEY_NOTES = "notes_json";
private RecyclerView recyclerView;
private View listContainer;
private View editContainer;
private View emptyState;
private View fab;
private EditText editTitle;
private EditText editBody;
private TextView editTitleLabel;
private List<NoteItem> notes = new ArrayList<>();
private NotesAdapter adapter;
private int editingIndex = -1; // -1 = new note
// Lifecycle
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notes);
hideSystemUI();
themeManager = new ThemeManager(this);
listContainer = findViewById(R.id.notes_list_container);
editContainer = findViewById(R.id.notes_edit_container);
emptyState = findViewById(R.id.notes_empty);
fab = findViewById(R.id.notes_fab);
recyclerView = findViewById(R.id.notes_recycler);
editTitle = findViewById(R.id.notes_edit_title);
editBody = findViewById(R.id.notes_edit_body);
editTitleLabel = findViewById(R.id.notes_edit_title_label);
// Apply Source Code Pro font (keeping amber/yellow aesthetic)
editBody.setTypeface(themeManager.getMonoFont(this));
// Load persisted notes
loadNotes();
// RecyclerView setup
adapter = new NotesAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
// Swipe-to-delete
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(@NonNull RecyclerView rv,
@NonNull RecyclerView.ViewHolder vh,
@NonNull RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder vh, int direction) {
int pos = vh.getAdapterPosition();
if (pos >= 0 && pos < notes.size()) {
notes.remove(pos);
adapter.notifyItemRemoved(pos);
saveNotes();
updateEmptyState();
}
}
}).attachToRecyclerView(recyclerView);
// Back button (list view)
findViewById(R.id.notes_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// FAB: new note
fab.setOnClickListener(v -> openEditor(-1));
// Edit view: back (discard)
findViewById(R.id.notes_edit_back).setOnClickListener(v -> closeEditor());
// Edit view: save
findViewById(R.id.notes_save_btn).setOnClickListener(v -> saveCurrentNote());
// Fade-in animation
View root = findViewById(R.id.notes_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
updateEmptyState();
// Custom themed keyboard
AeThexKeyboard.attach(this);
}
@SuppressWarnings("deprecation")
@Override
public void onBackPressed() {
if (editContainer.getVisibility() == View.VISIBLE) {
closeEditor();
} else {
super.onBackPressed();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
}
}
// Editor logic
private void openEditor(int index) {
editingIndex = index;
if (index >= 0 && index < notes.size()) {
NoteItem note = notes.get(index);
editTitle.setText(note.title);
editBody.setText(note.body);
editTitleLabel.setText("Edit Note");
} else {
editTitle.setText("");
editBody.setText("");
editTitleLabel.setText("New Note");
}
listContainer.setVisibility(View.GONE);
fab.setVisibility(View.GONE);
editContainer.setVisibility(View.VISIBLE);
editContainer.setAlpha(0f);
editContainer.animate().alpha(1f).setDuration(200).start();
editTitle.requestFocus();
}
private void closeEditor() {
editContainer.setVisibility(View.GONE);
listContainer.setVisibility(View.VISIBLE);
fab.setVisibility(View.VISIBLE);
}
private void saveCurrentNote() {
String title = editTitle.getText().toString().trim();
String body = editBody.getText().toString().trim();
// Don't save completely empty notes
if (title.isEmpty() && body.isEmpty()) {
closeEditor();
return;
}
// If title is empty, use the first line of the body
if (title.isEmpty()) {
String[] lines = body.split("\n");
title = lines[0].length() > 50 ? lines[0].substring(0, 50) : lines[0];
}
String now = new SimpleDateFormat("MMM dd, yyyy HH:mm", Locale.getDefault())
.format(new Date());
if (editingIndex >= 0 && editingIndex < notes.size()) {
// Update existing
NoteItem note = notes.get(editingIndex);
note.title = title;
note.body = body;
note.date = now;
adapter.notifyItemChanged(editingIndex);
} else {
// Create new (insert at beginning)
NoteItem note = new NoteItem(title, body, now);
notes.add(0, note);
adapter.notifyItemInserted(0);
recyclerView.scrollToPosition(0);
}
saveNotes();
updateEmptyState();
closeEditor();
}
// Persistence (SharedPreferences + JSON)
private void loadNotes() {
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
String json = prefs.getString(KEY_NOTES, "[]");
notes.clear();
try {
JSONArray arr = new JSONArray(json);
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = arr.getJSONObject(i);
notes.add(new NoteItem(
obj.optString("title", ""),
obj.optString("body", ""),
obj.optString("date", "")
));
}
} catch (JSONException e) {
e.printStackTrace();
}
}
private void saveNotes() {
JSONArray arr = new JSONArray();
try {
for (NoteItem n : notes) {
JSONObject obj = new JSONObject();
obj.put("title", n.title);
obj.put("body", n.body);
obj.put("date", n.date);
arr.put(obj);
}
} catch (JSONException e) {
e.printStackTrace();
}
getSharedPreferences(PREFS_NAME, MODE_PRIVATE)
.edit()
.putString(KEY_NOTES, arr.toString())
.apply();
}
// Empty state
private void updateEmptyState() {
if (notes.isEmpty()) {
emptyState.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyState.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
}
// Data model
private static class NoteItem {
String title;
String body;
String date;
NoteItem(String title, String body, String date) {
this.title = title;
this.body = body;
this.date = date;
}
}
// RecyclerView Adapter
private class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.VH> {
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_note, parent, false);
return new VH(v);
}
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
NoteItem note = notes.get(position);
holder.title.setText(note.title);
holder.body.setText(note.body);
holder.date.setText(note.date);
// Hide body if empty
holder.body.setVisibility(
note.body.isEmpty() ? View.GONE : View.VISIBLE);
holder.itemView.setOnClickListener(v -> openEditor(holder.getAdapterPosition()));
}
@Override
public int getItemCount() {
return notes.size();
}
class VH extends RecyclerView.ViewHolder {
TextView title, body, date;
VH(@NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.note_item_title);
body = itemView.findViewById(R.id.note_item_body);
date = itemView.findViewById(R.id.note_item_date);
}
}
}
// Immersive mode
@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);
}
}
}

View file

@ -0,0 +1,681 @@
package com.aethex.os;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
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.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
/**
* Passport User identity card with edit, share, and QR code.
*/
public class PassportActivity extends AppCompatActivity {
private ThemeManager themeManager;
private Typeface displayFont;
private Typeface monoFont;
private SharedPreferences prefs;
private static final String PREFS_NAME = "aethex_passport";
// Editable fields
private TextView usernameView;
private TextView bioView;
private boolean editMode = false;
// Content parent for rebuilding
private LinearLayout contentParent;
@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("Passport");
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);
});
contentParent = (LinearLayout) nameDisplay.getParent();
contentParent.removeAllViews();
contentParent.setGravity(Gravity.TOP);
contentParent.setPadding(0, 0, 0, 0);
buildPassportUI(contentParent);
View root = findViewById(R.id.app_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
}
private void buildPassportUI(LinearLayout parent) {
boolean isFoundation = themeManager.isFoundation();
int primaryColor = themeManager.getPrimaryColor(this);
ScrollView scroll = new ScrollView(this);
scroll.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
scroll.setOverScrollMode(View.OVER_SCROLL_NEVER);
LinearLayout wrapper = new LinearLayout(this);
wrapper.setOrientation(LinearLayout.VERTICAL);
wrapper.setPadding(dpToPx(16), dpToPx(12), dpToPx(16), dpToPx(24));
// Action buttons row
LinearLayout actionsRow = new LinearLayout(this);
actionsRow.setOrientation(LinearLayout.HORIZONTAL);
actionsRow.setGravity(Gravity.END | Gravity.CENTER_VERTICAL);
LinearLayout.LayoutParams arP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
arP.bottomMargin = dpToPx(12);
actionsRow.setLayoutParams(arP);
// Edit button
TextView editBtn = new TextView(this);
editBtn.setText("EDIT");
editBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
editBtn.setTextColor(primaryColor);
editBtn.setTypeface(monoFont, Typeface.BOLD);
editBtn.setLetterSpacing(0.1f);
editBtn.setPadding(dpToPx(14), dpToPx(6), dpToPx(14), dpToPx(6));
GradientDrawable editBg = new GradientDrawable();
editBg.setCornerRadius(dpToPx(6));
editBg.setStroke(dpToPx(1), primaryColor);
editBg.setColor(Color.parseColor("#0D000000"));
editBtn.setBackground(editBg);
editBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
toggleEditMode(editBtn);
});
actionsRow.addView(editBtn);
// Share button
TextView shareBtn = new TextView(this);
shareBtn.setText("SHARE");
shareBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
shareBtn.setTextColor(Color.parseColor("#99FFFFFF"));
shareBtn.setTypeface(monoFont, Typeface.BOLD);
shareBtn.setLetterSpacing(0.1f);
shareBtn.setPadding(dpToPx(14), dpToPx(6), dpToPx(14), dpToPx(6));
GradientDrawable shareBg = new GradientDrawable();
shareBg.setCornerRadius(dpToPx(6));
shareBg.setStroke(dpToPx(1), Color.parseColor("#33FFFFFF"));
shareBg.setColor(Color.parseColor("#0D000000"));
shareBtn.setBackground(shareBg);
LinearLayout.LayoutParams sbP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
sbP.setMarginStart(dpToPx(8));
shareBtn.setLayoutParams(sbP);
shareBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
sharePassport();
});
actionsRow.addView(shareBtn);
wrapper.addView(actionsRow);
// Passport Card
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.VERTICAL);
card.setGravity(Gravity.CENTER_HORIZONTAL);
card.setPadding(dpToPx(24), dpToPx(28), dpToPx(24), dpToPx(28));
GradientDrawable cardBg = new GradientDrawable();
cardBg.setCornerRadius(dpToPx(16));
cardBg.setStroke(dpToPx(1), Color.parseColor("#33FFFFFF"));
cardBg.setColor(Color.parseColor("#1A0F172A"));
card.setBackground(cardBg);
// Avatar circle
FrameLayout avatar = new FrameLayout(this);
int avatarSize = dpToPx(72);
LinearLayout.LayoutParams avatarParams = new LinearLayout.LayoutParams(avatarSize, avatarSize);
avatarParams.gravity = Gravity.CENTER_HORIZONTAL;
avatar.setLayoutParams(avatarParams);
avatar.setBackgroundResource(themeManager.getBootLogoDrawable());
TextView avatarText = new TextView(this);
avatarText.setText("Æ");
avatarText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 32);
avatarText.setTextColor(Color.WHITE);
avatarText.setTypeface(displayFont, Typeface.BOLD);
avatarText.setGravity(Gravity.CENTER);
avatar.addView(avatarText, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
card.addView(avatar);
// Username (editable)
String savedName = prefs.getString("username", "ARCHITECT");
usernameView = new TextView(this);
usernameView.setText(savedName);
usernameView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 22);
usernameView.setTextColor(Color.WHITE);
usernameView.setTypeface(displayFont);
usernameView.setGravity(Gravity.CENTER);
usernameView.setLetterSpacing(0.1f);
LinearLayout.LayoutParams uParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
uParams.topMargin = dpToPx(16);
usernameView.setLayoutParams(uParams);
card.addView(usernameView);
// Bio (editable)
String savedBio = prefs.getString("bio", "System Architect · AeThex OS");
bioView = new TextView(this);
bioView.setText(savedBio);
bioView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
bioView.setTextColor(Color.parseColor("#66FFFFFF"));
bioView.setTypeface(monoFont);
bioView.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams bioP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
bioP.topMargin = dpToPx(4);
bioView.setLayoutParams(bioP);
card.addView(bioView);
// Clearance badge
TextView badge = new TextView(this);
badge.setText(isFoundation ? "FOUNDATION CLEARANCE" : "CORP CLEARANCE");
badge.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
badge.setTextColor(themeManager.getClearanceBadgeTextColor());
badge.setTypeface(monoFont);
badge.setLetterSpacing(0.15f);
badge.setGravity(Gravity.CENTER);
GradientDrawable badgeBg = new GradientDrawable();
badgeBg.setCornerRadius(dpToPx(4));
badgeBg.setColor(themeManager.getClearanceBadgeBackground());
badge.setBackground(badgeBg);
badge.setPadding(dpToPx(12), dpToPx(4), dpToPx(12), dpToPx(4));
LinearLayout.LayoutParams bParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
bParams.topMargin = dpToPx(10);
bParams.gravity = Gravity.CENTER_HORIZONTAL;
badge.setLayoutParams(bParams);
card.addView(badge);
// Divider
View divider = new View(this);
LinearLayout.LayoutParams dParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(1));
dParams.topMargin = dpToPx(20);
dParams.bottomMargin = dpToPx(16);
divider.setLayoutParams(dParams);
divider.setBackgroundColor(Color.parseColor("#1AFFFFFF"));
card.addView(divider);
// Info rows
addInfoRow(card, "ID", "AX-2847-ARCH");
addInfoRow(card, "STATUS", "ACTIVE");
addInfoRow(card, "JOINED", "2024-01-15");
addInfoRow(card, "ACCESS", "Level 5");
addInfoRow(card, "REGION", "Sector 7-G");
addInfoRow(card, "MODULES", "12 active");
wrapper.addView(card);
// QR Code Section
TextView qrLabel = new TextView(this);
qrLabel.setText("IDENTITY QR");
qrLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
qrLabel.setTextColor(Color.parseColor("#66FFFFFF"));
qrLabel.setTypeface(monoFont);
qrLabel.setLetterSpacing(0.15f);
qrLabel.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams qlP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
qlP.topMargin = dpToPx(24);
qlP.bottomMargin = dpToPx(8);
qrLabel.setLayoutParams(qlP);
wrapper.addView(qrLabel);
// QR code (simulated with a grid pattern)
LinearLayout qrCard = new LinearLayout(this);
qrCard.setOrientation(LinearLayout.VERTICAL);
qrCard.setGravity(Gravity.CENTER);
qrCard.setPadding(dpToPx(20), dpToPx(20), dpToPx(20), dpToPx(20));
LinearLayout.LayoutParams qcP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
qrCard.setLayoutParams(qcP);
GradientDrawable qrCardBg = new GradientDrawable();
qrCardBg.setCornerRadius(dpToPx(12));
qrCardBg.setColor(Color.parseColor("#0D0F172A"));
qrCardBg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
qrCard.setBackground(qrCardBg);
// Generate a deterministic pseudo-QR grid
ImageView qrImage = new ImageView(this);
int qrSize = dpToPx(140);
LinearLayout.LayoutParams qrIp = new LinearLayout.LayoutParams(qrSize, qrSize);
qrIp.gravity = Gravity.CENTER_HORIZONTAL;
qrImage.setLayoutParams(qrIp);
qrImage.setImageBitmap(generateQrBitmap(savedName, primaryColor));
qrCard.addView(qrImage);
// QR text below
TextView qrText = new TextView(this);
qrText.setText("Scan to verify identity");
qrText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
qrText.setTextColor(Color.parseColor("#4DFFFFFF"));
qrText.setTypeface(monoFont);
qrText.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams qtP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
qtP.topMargin = dpToPx(10);
qrText.setLayoutParams(qtP);
qrCard.addView(qrText);
// Tap QR to copy ID
qrCard.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboard.setPrimaryClip(ClipData.newPlainText("AeThex ID", "AX-2847-ARCH"));
AeThexToast.show(this, "ID copied to clipboard", AeThexToast.Type.SUCCESS);
});
wrapper.addView(qrCard);
// Stats Section
TextView statsLabel = new TextView(this);
statsLabel.setText("ACTIVITY");
statsLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
statsLabel.setTextColor(Color.parseColor("#66FFFFFF"));
statsLabel.setTypeface(monoFont);
statsLabel.setLetterSpacing(0.15f);
LinearLayout.LayoutParams slP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
slP.topMargin = dpToPx(24);
slP.bottomMargin = dpToPx(8);
statsLabel.setLayoutParams(slP);
wrapper.addView(statsLabel);
// Stats grid
LinearLayout statsRow = new LinearLayout(this);
statsRow.setOrientation(LinearLayout.HORIZONTAL);
statsRow.setLayoutParams(new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
addStatTile(statsRow, "42", "Sessions", "#06B6D4");
addStatTile(statsRow, "187", "Commands", "#22C55E");
addStatTile(statsRow, "12", "Projects", "#A855F7");
addStatTile(statsRow, "5", "Badges", "#F97316");
wrapper.addView(statsRow);
scroll.addView(wrapper);
parent.addView(scroll);
}
private void addInfoRow(LinearLayout parent, String label, String value) {
LinearLayout row = new LinearLayout(this);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(Gravity.CENTER_VERTICAL);
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rowParams.bottomMargin = dpToPx(8);
row.setLayoutParams(rowParams);
TextView labelView = new TextView(this);
labelView.setText(label);
labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
labelView.setTextColor(Color.parseColor("#66FFFFFF"));
labelView.setTypeface(monoFont);
labelView.setLetterSpacing(0.1f);
labelView.setLayoutParams(new LinearLayout.LayoutParams(dpToPx(80), ViewGroup.LayoutParams.WRAP_CONTENT));
row.addView(labelView);
TextView valueView = new TextView(this);
valueView.setText(value);
valueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
valueView.setTextColor(Color.parseColor("#CCFFFFFF"));
valueView.setTypeface(monoFont);
row.addView(valueView);
parent.addView(row);
}
private void addStatTile(LinearLayout parent, String value, String label, String colorHex) {
LinearLayout tile = new LinearLayout(this);
tile.setOrientation(LinearLayout.VERTICAL);
tile.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams tp = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
tp.setMarginEnd(dpToPx(6));
tile.setLayoutParams(tp);
tile.setPadding(dpToPx(8), dpToPx(12), dpToPx(8), dpToPx(12));
GradientDrawable tileBg = new GradientDrawable();
tileBg.setCornerRadius(dpToPx(10));
tileBg.setColor(Color.parseColor("#0D0F172A"));
tileBg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
tile.setBackground(tileBg);
TextView valView = new TextView(this);
valView.setText(value);
valView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
valView.setTextColor(Color.parseColor(colorHex));
valView.setTypeface(displayFont);
valView.setGravity(Gravity.CENTER);
tile.addView(valView);
TextView labView = new TextView(this);
labView.setText(label);
labView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
labView.setTextColor(Color.parseColor("#66FFFFFF"));
labView.setTypeface(monoFont);
labView.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp.topMargin = dpToPx(4);
labView.setLayoutParams(lp);
tile.addView(labView);
parent.addView(tile);
}
private android.graphics.Bitmap generateQrBitmap(String seed, int accentColor) {
int gridSize = 21; // QR-like grid
int cellPx = dpToPx(140) / gridSize;
int bmpSize = cellPx * gridSize;
android.graphics.Bitmap bmp = android.graphics.Bitmap.createBitmap(
bmpSize, bmpSize, android.graphics.Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
canvas.drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setAntiAlias(false);
// Deterministic pseudo-random from seed
int hash = seed.hashCode();
java.util.Random rand = new java.util.Random(hash);
// Draw finder patterns (3 corners)
drawFinderPattern(canvas, paint, 0, 0, cellPx, accentColor);
drawFinderPattern(canvas, paint, (gridSize - 7) * cellPx, 0, cellPx, accentColor);
drawFinderPattern(canvas, paint, 0, (gridSize - 7) * cellPx, cellPx, accentColor);
// Fill data area with pseudo-random pattern
paint.setColor(Color.parseColor("#1A1A2E"));
for (int y = 0; y < gridSize; y++) {
for (int x = 0; x < gridSize; x++) {
// Skip finder pattern areas
if ((x < 8 && y < 8) || (x >= gridSize - 8 && y < 8) || (x < 8 && y >= gridSize - 8))
continue;
if (rand.nextFloat() < 0.45f) {
canvas.drawRect(x * cellPx, y * cellPx,
(x + 1) * cellPx, (y + 1) * cellPx, paint);
}
}
}
return bmp;
}
private void drawFinderPattern(Canvas canvas, Paint paint, int x, int y, int cell, int color) {
// Outer ring
paint.setColor(Color.parseColor("#1A1A2E"));
canvas.drawRect(x, y, x + 7 * cell, y + 7 * cell, paint);
// White inner
paint.setColor(Color.WHITE);
canvas.drawRect(x + cell, y + cell, x + 6 * cell, y + 6 * cell, paint);
// Center block with accent
paint.setColor(color);
canvas.drawRect(x + 2 * cell, y + 2 * cell, x + 5 * cell, y + 5 * cell, paint);
}
private void toggleEditMode(TextView editBtn) {
if (!editMode) {
// Enter edit mode show edit overlay
editMode = true;
editBtn.setText("DONE");
showEditOverlay();
} else {
// Save and exit edit mode
editMode = false;
editBtn.setText("EDIT");
dismissEditOverlay();
}
}
private void showEditOverlay() {
FrameLayout root = findViewById(R.id.app_root);
FrameLayout scrim = new FrameLayout(this);
scrim.setTag("passport_edit_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);
int panelWidth = dpToPx(300);
FrameLayout.LayoutParams pp = new FrameLayout.LayoutParams(
panelWidth, 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("EDIT PASSPORT");
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);
// Username field
TextView nameLabel = new TextView(this);
nameLabel.setText("CALLSIGN");
nameLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
nameLabel.setTextColor(Color.parseColor("#66FFFFFF"));
nameLabel.setTypeface(monoFont);
panel.addView(nameLabel);
EditText nameInput = new EditText(this);
nameInput.setText(prefs.getString("username", "ARCHITECT"));
nameInput.setTextColor(Color.WHITE);
nameInput.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
nameInput.setTypeface(monoFont);
nameInput.setSingleLine(true);
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);
// Bio field
TextView bioLabel = new TextView(this);
bioLabel.setText("BIO");
bioLabel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
bioLabel.setTextColor(Color.parseColor("#66FFFFFF"));
bioLabel.setTypeface(monoFont);
panel.addView(bioLabel);
EditText bioInput = new EditText(this);
bioInput.setText(prefs.getString("bio", "System Architect · AeThex OS"));
bioInput.setTextColor(Color.WHITE);
bioInput.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
bioInput.setTypeface(monoFont);
bioInput.setMaxLines(3);
bioInput.setPadding(dpToPx(12), dpToPx(10), dpToPx(12), dpToPx(10));
bioInput.setShowSoftInputOnFocus(false);
GradientDrawable biBg = new GradientDrawable();
biBg.setCornerRadius(dpToPx(8));
biBg.setColor(Color.parseColor("#0DFFFFFF"));
biBg.setStroke(dpToPx(1), Color.parseColor("#1AFFFFFF"));
bioInput.setBackground(biBg);
LinearLayout.LayoutParams biP = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
biP.topMargin = dpToPx(4);
biP.bottomMargin = dpToPx(20);
bioInput.setLayoutParams(biP);
bioInput.setOnFocusChangeListener((v, hasFocus) -> {
if (hasFocus) AeThexKeyboard.attachToEditText(this, bioInput);
});
panel.addView(bioInput);
// Save button
int primaryColor = themeManager.getPrimaryColor(this);
TextView saveBtn = new TextView(this);
saveBtn.setText("SAVE CHANGES");
saveBtn.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
saveBtn.setTextColor(Color.WHITE);
saveBtn.setTypeface(monoFont, Typeface.BOLD);
saveBtn.setGravity(Gravity.CENTER);
saveBtn.setPadding(dpToPx(16), dpToPx(12), dpToPx(16), dpToPx(12));
GradientDrawable saveBg = new GradientDrawable();
saveBg.setCornerRadius(dpToPx(8));
saveBg.setColor(primaryColor);
saveBtn.setBackground(saveBg);
saveBtn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
String newName = nameInput.getText().toString().trim();
String newBio = bioInput.getText().toString().trim();
if (!newName.isEmpty()) {
prefs.edit()
.putString("username", newName.toUpperCase())
.putString("bio", newBio)
.apply();
usernameView.setText(newName.toUpperCase());
bioView.setText(newBio);
}
AeThexKeyboard.dismissKeyboard(this);
editMode = false;
dismissEditOverlay();
AeThexToast.show(this, "Passport updated", AeThexToast.Type.SUCCESS);
});
panel.addView(saveBtn);
scrim.addView(panel);
scrim.setAlpha(0f);
root.addView(scrim);
scrim.animate().alpha(1f).setDuration(200).start();
// Focus name input
nameInput.requestFocus();
nameInput.postDelayed(() -> AeThexKeyboard.attachToEditText(this, nameInput), 200);
}
private void dismissEditOverlay() {
AeThexKeyboard.dismissKeyboard(this);
FrameLayout root = findViewById(R.id.app_root);
View overlay = root.findViewWithTag("passport_edit_overlay");
if (overlay != null) {
overlay.animate().alpha(0f).setDuration(150).withEndAction(() -> {
root.removeView(overlay);
}).start();
}
}
private void sharePassport() {
String name = prefs.getString("username", "ARCHITECT");
String bio = prefs.getString("bio", "System Architect · AeThex OS");
String clearance = themeManager.isFoundation() ? "Foundation" : "Corp";
String shareText = "AeThex Passport\n" +
"Callsign: " + name + "\n" +
"Bio: " + bio + "\n" +
"ID: AX-2847-ARCH\n" +
"Clearance: " + clearance + "\n" +
"Status: ACTIVE";
android.content.Intent shareIntent = new android.content.Intent(android.content.Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareText);
startActivity(android.content.Intent.createChooser(shareIntent, "Share Passport"));
}
@SuppressWarnings("deprecation")
@Override
public void onBackPressed() {
if (editMode) {
editMode = false;
AeThexKeyboard.dismissKeyboard(this);
dismissEditOverlay();
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);
}
}
}

View file

@ -0,0 +1,482 @@
package com.aethex.os;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import java.util.ArrayList;
import java.util.List;
public class PhotosActivity extends AppCompatActivity {
private ThemeManager themeManager;
private FrameLayout viewerOverlay;
private View viewerPhoto;
private TextView viewerFilename;
private TextView viewerSize;
private TextView viewerDate;
private float viewerStartY;
private List<PhotoItem> photos;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photos);
hideSystemUI();
themeManager = new ThemeManager(this);
// Back button
findViewById(R.id.photos_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Filter tabs
findViewById(R.id.tab_favorites).setOnClickListener(v ->
Toast.makeText(this, "Favorites - Coming soon", Toast.LENGTH_SHORT).show());
findViewById(R.id.tab_recent).setOnClickListener(v ->
Toast.makeText(this, "Recent - Coming soon", Toast.LENGTH_SHORT).show());
// Generate photo data
photos = generatePhotos();
// Setup RecyclerView grid
RecyclerView grid = findViewById(R.id.photos_grid);
GridLayoutManager layoutManager = new GridLayoutManager(this, 3);
grid.setLayoutManager(layoutManager);
grid.addItemDecoration(new GridSpacingDecoration(3, dpToPx(4)));
grid.setAdapter(new PhotoAdapter());
// Fullscreen viewer setup
viewerOverlay = findViewById(R.id.photo_viewer_overlay);
viewerPhoto = findViewById(R.id.viewer_photo);
viewerFilename = findViewById(R.id.viewer_filename);
viewerSize = findViewById(R.id.viewer_size);
viewerDate = findViewById(R.id.viewer_date);
// Tap to dismiss viewer
viewerOverlay.setOnClickListener(v -> dismissViewer());
// Swipe down to dismiss
viewerOverlay.setOnTouchListener((v, event) -> {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
viewerStartY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float dy = event.getY() - viewerStartY;
if (dy > 0) {
viewerOverlay.setTranslationY(dy * 0.5f);
float alpha = Math.max(0f, 1f - (dy / 800f));
viewerOverlay.setAlpha(alpha);
}
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
float finalDy = event.getY() - viewerStartY;
if (finalDy > 150) {
dismissViewer();
} else {
viewerOverlay.animate()
.translationY(0f)
.alpha(1f)
.setDuration(200)
.start();
}
return true;
}
return false;
});
// Entrance animation
View root = findViewById(R.id.photos_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
}
private List<PhotoItem> generatePhotos() {
List<PhotoItem> list = new ArrayList<>();
// 24 unique gradient pairs
int[][] gradients = {
{0xFF06B6D4, 0xFF7C3AED}, // cyan -> violet
{0xFFEF4444, 0xFFF97316}, // red -> orange
{0xFF10B981, 0xFF14B8A6}, // emerald -> teal
{0xFF3B82F6, 0xFF6366F1}, // blue -> indigo
{0xFFEC4899, 0xFFF43F5E}, // pink -> rose
{0xFFEAB308, 0xFFF59E0B}, // yellow -> amber
{0xFF8B5CF6, 0xFFD946EF}, // violet -> fuchsia
{0xFF06B6D4, 0xFF22D3EE}, // cyan -> sky
{0xFFEF4444, 0xFFEC4899}, // red -> pink
{0xFF10B981, 0xFF06B6D4}, // emerald -> cyan
{0xFF6366F1, 0xFFA855F7}, // indigo -> purple
{0xFFF97316, 0xFFEAB308}, // orange -> yellow
{0xFFF43F5E, 0xFF8B5CF6}, // rose -> violet
{0xFF14B8A6, 0xFF3B82F6}, // teal -> blue
{0xFFD946EF, 0xFFEF4444}, // fuchsia -> red
{0xFF22D3EE, 0xFF10B981}, // sky -> emerald
{0xFFA855F7, 0xFF06B6D4}, // purple -> cyan
{0xFFF59E0B, 0xFFEF4444}, // amber -> red
{0xFF7C3AED, 0xFF3B82F6}, // violet -> blue
{0xFFEC4899, 0xFFF59E0B}, // pink -> amber
{0xFF3B82F6, 0xFF10B981}, // blue -> emerald
{0xFFEAB308, 0xFF10B981}, // yellow -> emerald
{0xFF6366F1, 0xFFEC4899}, // indigo -> pink
{0xFFF43F5E, 0xFFF97316}, // rose -> orange
};
String[] months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
String[] resolutions = {
"4032 x 3024", "3840 x 2160", "3024 x 4032", "2560 x 1440",
"4000 x 3000", "3264 x 2448", "4032 x 3024", "1920 x 1080"
};
for (int i = 0; i < 24; i++) {
int num = 1001 + i;
String filename = "IMG_" + num + ".jpg";
// Distribute dates across recent months
int monthIdx = 11 - (i / 4); // Dec, Nov, Oct...
if (monthIdx < 0) monthIdx = 0;
int day = (i % 28) + 1;
int year = monthIdx >= 6 ? 2025 : 2025;
String date = months[monthIdx] + " " + day + ", " + year;
String resolution = resolutions[i % resolutions.length];
list.add(new PhotoItem(filename, date, resolution,
gradients[i][0], gradients[i][1], i));
}
return list;
}
private void showViewer(PhotoItem photo) {
// Set photo gradient as fullscreen background
GradientDrawable bg = new GradientDrawable(
GradientDrawable.Orientation.TL_BR,
new int[]{photo.colorStart, photo.colorEnd}
);
viewerPhoto.setBackground(bg);
viewerFilename.setText(photo.filename);
viewerSize.setText(photo.resolution);
viewerDate.setText(photo.date);
viewerOverlay.setTranslationY(0f);
viewerOverlay.setAlpha(0f);
viewerOverlay.setVisibility(View.VISIBLE);
viewerOverlay.animate()
.alpha(1f)
.setDuration(250)
.start();
}
private void dismissViewer() {
viewerOverlay.animate()
.alpha(0f)
.translationY(100f)
.setDuration(200)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
viewerOverlay.setVisibility(View.GONE);
viewerOverlay.setTranslationY(0f);
viewerOverlay.animate().setListener(null);
}
})
.start();
}
private int dpToPx(int dp) {
return (int) (dp * getResources().getDisplayMetrics().density);
}
// ---- Inner data class ----
private static class PhotoItem {
String filename;
String date;
String resolution;
int colorStart;
int colorEnd;
int index;
PhotoItem(String filename, String date, String resolution,
int colorStart, int colorEnd, int index) {
this.filename = filename;
this.date = date;
this.resolution = resolution;
this.colorStart = colorStart;
this.colorEnd = colorEnd;
this.index = index;
}
}
// ---- RecyclerView Adapter ----
private class PhotoAdapter extends RecyclerView.Adapter<PhotoAdapter.PhotoViewHolder> {
@NonNull
@Override
public PhotoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// Create item view programmatically: a FrameLayout containing
// - the gradient View (fills the cell)
// - a subtle overlay pattern
FrameLayout container = new FrameLayout(parent.getContext());
container.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
return new PhotoViewHolder(container);
}
@Override
public void onBindViewHolder(@NonNull PhotoViewHolder holder, int position) {
PhotoItem photo = photos.get(position);
FrameLayout container = (FrameLayout) holder.itemView;
container.removeAllViews();
// Make it square
container.post(() -> {
int width = container.getWidth();
if (width > 0) {
ViewGroup.LayoutParams lp = container.getLayoutParams();
lp.height = width;
container.setLayoutParams(lp);
}
});
// Main gradient background with rounded corners
GradientDrawable gradient = new GradientDrawable(
getGradientOrientation(position),
new int[]{photo.colorStart, photo.colorEnd}
);
gradient.setCornerRadius(dpToPx(8));
container.setBackground(gradient);
// Add a semi-transparent overlay pattern for depth
View overlay = new View(container.getContext());
overlay.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
GradientDrawable overlayGrad = new GradientDrawable(
GradientDrawable.Orientation.BOTTOM_TOP,
new int[]{0x40000000, 0x00000000, 0x00000000, 0x20000000}
);
overlayGrad.setCornerRadius(dpToPx(8));
overlay.setBackground(overlayGrad);
container.addView(overlay);
// Add diagonal accent line for visual interest
if (position % 3 == 0) {
View accent = new View(container.getContext());
FrameLayout.LayoutParams accentLp = new FrameLayout.LayoutParams(
dpToPx(40), dpToPx(2));
accentLp.topMargin = dpToPx(16);
accentLp.leftMargin = dpToPx(12);
accent.setLayoutParams(accentLp);
accent.setAlpha(0.3f);
GradientDrawable accentBg = new GradientDrawable();
accentBg.setColor(0xFFFFFFFF);
accentBg.setCornerRadius(dpToPx(1));
accent.setBackground(accentBg);
container.addView(accent);
}
// Add small dot pattern for texture on some photos
if (position % 4 == 1) {
for (int r = 0; r < 3; r++) {
for (int c = 0; c < 3; c++) {
View dot = new View(container.getContext());
int dotSize = dpToPx(3);
FrameLayout.LayoutParams dotLp = new FrameLayout.LayoutParams(
dotSize, dotSize);
dotLp.topMargin = dpToPx(14 + r * 8);
dotLp.leftMargin = dpToPx(14 + c * 8);
dot.setLayoutParams(dotLp);
dot.setAlpha(0.2f);
GradientDrawable dotBg = new GradientDrawable();
dotBg.setColor(0xFFFFFFFF);
dotBg.setShape(GradientDrawable.OVAL);
dot.setBackground(dotBg);
container.addView(dot);
}
}
}
// Add ring overlay on some photos
if (position % 5 == 2) {
View ring = new View(container.getContext());
int ringSize = dpToPx(32);
FrameLayout.LayoutParams ringLp = new FrameLayout.LayoutParams(
ringSize, ringSize);
ringLp.topMargin = dpToPx(20);
ringLp.leftMargin = dpToPx(20);
ring.setLayoutParams(ringLp);
ring.setAlpha(0.2f);
GradientDrawable ringBg = new GradientDrawable();
ringBg.setShape(GradientDrawable.OVAL);
ringBg.setColor(0x00000000);
ringBg.setStroke(dpToPx(2), 0xFFFFFFFF);
ring.setBackground(ringBg);
container.addView(ring);
}
// Add triangle-like shape on some photos
if (position % 6 == 3) {
View tri1 = new View(container.getContext());
FrameLayout.LayoutParams triLp = new FrameLayout.LayoutParams(
dpToPx(24), dpToPx(1));
triLp.topMargin = dpToPx(24);
triLp.leftMargin = dpToPx(16);
tri1.setLayoutParams(triLp);
tri1.setAlpha(0.25f);
tri1.setBackgroundColor(0xFFFFFFFF);
container.addView(tri1);
View tri2 = new View(container.getContext());
FrameLayout.LayoutParams tri2Lp = new FrameLayout.LayoutParams(
dpToPx(1), dpToPx(20));
tri2Lp.topMargin = dpToPx(24);
tri2Lp.leftMargin = dpToPx(16);
tri2.setLayoutParams(tri2Lp);
tri2.setAlpha(0.25f);
tri2.setBackgroundColor(0xFFFFFFFF);
container.addView(tri2);
}
// Clip to rounded corners
container.setClipToOutline(true);
container.setOutlineProvider(new android.view.ViewOutlineProvider() {
@Override
public void getOutline(View view, android.graphics.Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), dpToPx(8));
}
});
// Click handler to open viewer
container.setOnClickListener(v -> {
v.animate().scaleX(0.93f).scaleY(0.93f).setDuration(80).withEndAction(() -> {
v.animate().scaleX(1f).scaleY(1f).setDuration(80).start();
showViewer(photo);
}).start();
});
// Stagger fade-in animation
container.setAlpha(0f);
container.setTranslationY(20f);
container.animate()
.alpha(1f)
.translationY(0f)
.setDuration(300)
.setStartDelay(position * 30L)
.start();
}
@Override
public int getItemCount() {
return photos.size();
}
private GradientDrawable.Orientation getGradientOrientation(int position) {
// Vary gradient direction for visual variety
switch (position % 6) {
case 0: return GradientDrawable.Orientation.TL_BR;
case 1: return GradientDrawable.Orientation.TOP_BOTTOM;
case 2: return GradientDrawable.Orientation.TR_BL;
case 3: return GradientDrawable.Orientation.LEFT_RIGHT;
case 4: return GradientDrawable.Orientation.BL_TR;
case 5: return GradientDrawable.Orientation.BOTTOM_TOP;
default: return GradientDrawable.Orientation.TL_BR;
}
}
class PhotoViewHolder extends RecyclerView.ViewHolder {
PhotoViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
// ---- Grid spacing item decoration ----
private static class GridSpacingDecoration extends RecyclerView.ItemDecoration {
private final int spanCount;
private final int spacing;
GridSpacingDecoration(int spanCount, int spacing) {
this.spanCount = spanCount;
this.spacing = spacing;
}
@Override
public void getItemOffsets(@NonNull android.graphics.Rect outRect,
@NonNull View view,
@NonNull RecyclerView parent,
@NonNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
int column = position % spanCount;
outRect.left = spacing - column * spacing / spanCount;
outRect.right = (column + 1) * spacing / spanCount;
if (position >= spanCount) {
outRect.top = spacing;
}
}
}
// ---- System UI ----
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) hideSystemUI();
}
@SuppressWarnings("deprecation")
@Override
public void onBackPressed() {
if (viewerOverlay.getVisibility() == View.VISIBLE) {
dismissViewer();
} else {
super.onBackPressed();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
}
}
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);
}
}
}

View file

@ -0,0 +1,824 @@
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<ProjectItem> 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);
}
}
}

View file

@ -0,0 +1,253 @@
package com.aethex.os;
import android.os.Handler;
import android.os.Looper;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* RootShell - Execute commands with root privileges on AeThexOS.
* Provides both synchronous and asynchronous execution methods.
*/
public class RootShell {
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
private static Boolean rootAvailable = null;
public interface CommandCallback {
void onOutput(String output);
void onError(String error);
void onComplete(int exitCode);
}
/**
* Check if root access is available
*/
public static boolean isRootAvailable() {
if (rootAvailable != null) {
return rootAvailable;
}
try {
Process process = Runtime.getRuntime().exec("su -c id");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = reader.readLine();
process.waitFor();
reader.close();
rootAvailable = (line != null && line.contains("uid=0"));
return rootAvailable;
} catch (Exception e) {
rootAvailable = false;
return false;
}
}
/**
* Execute a command as root synchronously
* @return Command output or error message
*/
public static String execute(String command) {
StringBuilder output = new StringBuilder();
StringBuilder error = new StringBuilder();
try {
Process process = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
os.writeBytes(command + "\n");
os.writeBytes("exit\n");
os.flush();
String line;
while ((line = stdout.readLine()) != null) {
output.append(line).append("\n");
}
while ((line = stderr.readLine()) != null) {
error.append(line).append("\n");
}
process.waitFor();
os.close();
stdout.close();
stderr.close();
if (error.length() > 0 && output.length() == 0) {
return "Error: " + error.toString().trim();
}
return output.toString().trim();
} catch (Exception e) {
return "Error: " + e.getMessage();
}
}
/**
* Execute a command without root (normal shell)
*/
public static String executeNonRoot(String command) {
StringBuilder output = new StringBuilder();
try {
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
while ((line = stdout.readLine()) != null) {
output.append(line).append("\n");
}
while ((line = stderr.readLine()) != null) {
output.append(line).append("\n");
}
process.waitFor();
stdout.close();
stderr.close();
return output.toString().trim();
} catch (Exception e) {
return "Error: " + e.getMessage();
}
}
/**
* Execute a command asynchronously with callbacks
*/
public static void executeAsync(String command, boolean useRoot, CommandCallback callback) {
executor.execute(() -> {
try {
Process process;
DataOutputStream os = null;
if (useRoot) {
process = Runtime.getRuntime().exec("su");
os = new DataOutputStream(process.getOutputStream());
os.writeBytes(command + "\n");
os.writeBytes("exit\n");
os.flush();
} else {
process = Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
}
BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
// Read output line by line and post to main thread
String line;
while ((line = stdout.readLine()) != null) {
final String outputLine = line;
mainHandler.post(() -> callback.onOutput(outputLine));
}
while ((line = stderr.readLine()) != null) {
final String errorLine = line;
mainHandler.post(() -> callback.onError(errorLine));
}
int exitCode = process.waitFor();
if (os != null) os.close();
stdout.close();
stderr.close();
mainHandler.post(() -> callback.onComplete(exitCode));
} catch (Exception e) {
mainHandler.post(() -> callback.onError("Exception: " + e.getMessage()));
mainHandler.post(() -> callback.onComplete(-1));
}
});
}
/**
* Get device CPU info from /proc/cpuinfo
*/
public static String getCpuInfo() {
return executeNonRoot("cat /proc/cpuinfo | head -20");
}
/**
* Get memory info
*/
public static String getMemInfo() {
return executeNonRoot("cat /proc/meminfo | head -10");
}
/**
* Get kernel version
*/
public static String getKernelVersion() {
return executeNonRoot("uname -a");
}
/**
* Get list of running processes (root)
*/
public static String getProcessList() {
if (isRootAvailable()) {
return execute("ps -A | head -30");
}
return executeNonRoot("ps | head -20");
}
/**
* Get SELinux status
*/
public static String getSelinuxStatus() {
return executeNonRoot("getenforce 2>/dev/null || echo 'Unknown'");
}
/**
* Get mounted filesystems
*/
public static String getMounts() {
return executeNonRoot("mount | head -15");
}
/**
* Get network interfaces
*/
public static String getNetworkInterfaces() {
return executeNonRoot("ip addr 2>/dev/null || ifconfig 2>/dev/null || echo 'Network info unavailable'");
}
/**
* Get disk usage
*/
public static String getDiskUsage() {
return executeNonRoot("df -h 2>/dev/null || df 2>/dev/null");
}
/**
* Remount system as read-write (requires root)
*/
public static String remountSystemRW() {
if (!isRootAvailable()) {
return "Error: Root access required";
}
return execute("mount -o rw,remount /system 2>&1 || mount -o rw,remount / 2>&1");
}
/**
* Get prop value
*/
public static String getProp(String prop) {
return executeNonRoot("getprop " + prop);
}
/**
* Set prop value (requires root)
*/
public static String setProp(String prop, String value) {
if (!isRootAvailable()) {
return "Error: Root access required";
}
return execute("setprop " + prop + " " + value);
}
}

View file

@ -0,0 +1,164 @@
package com.aethex.os;
import android.content.Context;
import android.content.SharedPreferences;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
/**
* Tracks app launch counts and screen time per app.
* Used for auto-categorization and analytics.
*/
public class ScreenTimeTracker {
private static final String PREFS_NAME = "aethex_screen_time";
private static final String KEY_LAUNCH_COUNTS = "launch_counts";
private static final String KEY_PINNED_APPS = "pinned_apps";
private static final String KEY_HIDDEN_APPS = "hidden_apps";
private final SharedPreferences prefs;
public ScreenTimeTracker(Context context) {
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
}
/**
* Record an app launch.
*/
public void recordLaunch(String appId) {
try {
String json = prefs.getString(KEY_LAUNCH_COUNTS, "{}");
JSONObject counts = new JSONObject(json);
int current = counts.optInt(appId, 0);
counts.put(appId, current + 1);
prefs.edit().putString(KEY_LAUNCH_COUNTS, counts.toString()).apply();
} catch (Exception ignored) {}
}
/**
* Get launch count for an app.
*/
public int getLaunchCount(String appId) {
try {
String json = prefs.getString(KEY_LAUNCH_COUNTS, "{}");
JSONObject counts = new JSONObject(json);
return counts.optInt(appId, 0);
} catch (Exception e) {
return 0;
}
}
/**
* Get total launches across all apps.
*/
public int getTotalLaunches() {
try {
String json = prefs.getString(KEY_LAUNCH_COUNTS, "{}");
JSONObject counts = new JSONObject(json);
int total = 0;
Iterator<String> keys = counts.keys();
while (keys.hasNext()) {
total += counts.optInt(keys.next(), 0);
}
return total;
} catch (Exception e) {
return 0;
}
}
/**
* Get the top N most used app IDs.
*/
public List<String> getMostUsed(int limit) {
try {
String json = prefs.getString(KEY_LAUNCH_COUNTS, "{}");
JSONObject counts = new JSONObject(json);
List<String[]> entries = new ArrayList<>();
Iterator<String> keys = counts.keys();
while (keys.hasNext()) {
String key = keys.next();
entries.add(new String[]{key, String.valueOf(counts.optInt(key, 0))});
}
Collections.sort(entries, (a, b) -> Integer.parseInt(b[1]) - Integer.parseInt(a[1]));
List<String> result = new ArrayList<>();
for (int i = 0; i < Math.min(limit, entries.size()); i++) {
result.add(entries.get(i)[0]);
}
return result;
} catch (Exception e) {
return new ArrayList<>();
}
}
/**
* Categorize an app as: "pinned", "most_used", "essential", or "other".
*/
public String categorize(String appId) {
if (isPinned(appId)) return "pinned";
int count = getLaunchCount(appId);
if (count >= 10) return "most_used";
// Essential = built-in AeThexOS core apps
if (isEssential(appId)) return "essential";
return "other";
}
private boolean isEssential(String appId) {
switch (appId) {
case "terminal":
case "browser":
case "settings":
case "files":
case "notes":
case "chat":
case "mail":
case "camera":
case "photos":
return true;
default:
return false;
}
}
// Pinning
public boolean isPinned(String appId) {
return prefs.getStringSet(KEY_PINNED_APPS, Collections.emptySet()).contains(appId);
}
public void setPinned(String appId, boolean pinned) {
java.util.Set<String> set = new java.util.HashSet<>(
prefs.getStringSet(KEY_PINNED_APPS, Collections.emptySet()));
if (pinned) set.add(appId);
else set.remove(appId);
prefs.edit().putStringSet(KEY_PINNED_APPS, set).apply();
}
// Hiding
public boolean isHidden(String appId) {
return prefs.getStringSet(KEY_HIDDEN_APPS, Collections.emptySet()).contains(appId);
}
public void setHidden(String appId, boolean hidden) {
java.util.Set<String> set = new java.util.HashSet<>(
prefs.getStringSet(KEY_HIDDEN_APPS, Collections.emptySet()));
if (hidden) set.add(appId);
else set.remove(appId);
prefs.edit().putStringSet(KEY_HIDDEN_APPS, set).apply();
}
/**
* Get all hidden app IDs.
*/
public java.util.Set<String> getHiddenApps() {
return prefs.getStringSet(KEY_HIDDEN_APPS, Collections.emptySet());
}
}

View file

@ -0,0 +1,537 @@
package com.aethex.os;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.provider.Settings;
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.FrameLayout;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.res.ResourcesCompat;
import java.util.Set;
public class SettingsActivity extends AppCompatActivity {
private ThemeManager themeManager;
private TextView currentModeLabel;
private LinearLayout accentColorRow;
private LinearLayout wallpaperGrid;
private SeekBar transparencySlider;
private TextView transparencyValue;
private TextView soundToggle;
private TextView accentReset;
private LinearLayout aboutCard;
private TextView aboutKonamiHint;
private int konamiTapCount = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
hideSystemUI();
themeManager = new ThemeManager(this);
// Sync SoundManager with persisted setting
SoundManager.getInstance().setEnabled(themeManager.isSoundEnabled());
// Bind views
currentModeLabel = findViewById(R.id.current_mode_label);
accentColorRow = findViewById(R.id.accent_color_row);
wallpaperGrid = findViewById(R.id.wallpaper_grid);
transparencySlider = findViewById(R.id.transparency_slider);
transparencyValue = findViewById(R.id.transparency_value);
soundToggle = findViewById(R.id.sound_toggle);
accentReset = findViewById(R.id.accent_reset);
aboutCard = findViewById(R.id.about_card);
aboutKonamiHint = findViewById(R.id.about_konami_hint);
// Back button
findViewById(R.id.settings_back).setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLOSE);
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Clearance mode buttons
setupClearanceButtons();
// Accent colors
setupAccentColors();
// Wallpaper picker
setupWallpaperPicker();
// Transparency slider
setupTransparencySlider();
// Sound toggle
setupSoundToggle();
// Device Settings & Hidden Apps
setupDeviceSettings();
setupHiddenApps();
// Konami easter egg
setupKonamiEasterEgg();
// Update all display states
updateModeLabel();
highlightSelection();
updateSoundToggle();
// Entrance animation
View content = findViewById(R.id.settings_root);
content.setAlpha(0f);
content.animate().alpha(1f).setDuration(300).start();
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
// Add bottom navigation bar
BottomNavBar.attach(this, (ViewGroup) content, BottomNavBar.TAB_SETTINGS);
}
//
// Clearance Mode
//
private void setupClearanceButtons() {
LinearLayout btnFoundation = findViewById(R.id.btn_foundation);
LinearLayout btnCorp = findViewById(R.id.btn_corp);
btnFoundation.setOnClickListener(v -> {
if (themeManager.isFoundation()) return; // Already selected
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
v.animate().scaleX(0.95f).scaleY(0.95f).setDuration(80).withEndAction(() -> {
v.animate().scaleX(1f).scaleY(1f).setDuration(80).start();
switchClearance(ThemeManager.CLEARANCE_FOUNDATION);
}).start();
});
btnCorp.setOnClickListener(v -> {
if (!themeManager.isFoundation()) return; // Already selected
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
v.animate().scaleX(0.95f).scaleY(0.95f).setDuration(80).withEndAction(() -> {
v.animate().scaleX(1f).scaleY(1f).setDuration(80).start();
switchClearance(ThemeManager.CLEARANCE_CORP);
}).start();
});
}
private void switchClearance(String targetMode) {
ClearanceSwitchOverlay.show(this, targetMode, () -> {
themeManager.setClearanceMode(targetMode);
updateModeLabel();
highlightSelection();
refreshWallpaperPicker();
AeThexToast.show(this,
ThemeManager.CLEARANCE_FOUNDATION.equals(targetMode)
? "Foundation clearance activated"
: "Corp clearance activated",
AeThexToast.Type.SUCCESS);
});
}
private void updateModeLabel() {
String mode = themeManager.isFoundation() ? "Foundation" : "Corp";
currentModeLabel.setText("Current: " + mode);
}
private void highlightSelection() {
LinearLayout btnFoundation = findViewById(R.id.btn_foundation);
LinearLayout btnCorp = findViewById(R.id.btn_corp);
float density = getResources().getDisplayMetrics().density;
int cornerRadius = (int) (16 * density);
if (themeManager.isFoundation()) {
GradientDrawable foundBg = new GradientDrawable();
foundBg.setColor(0x33DC2626);
foundBg.setCornerRadius(cornerRadius);
foundBg.setStroke((int) (2 * density), 0xFFDC2626);
btnFoundation.setBackground(foundBg);
GradientDrawable corpBg = new GradientDrawable();
corpBg.setColor(0x1AFFFFFF);
corpBg.setCornerRadius(cornerRadius);
corpBg.setStroke((int) (1 * density), 0x1AFFFFFF);
btnCorp.setBackground(corpBg);
} else {
GradientDrawable foundBg = new GradientDrawable();
foundBg.setColor(0x1AFFFFFF);
foundBg.setCornerRadius(cornerRadius);
foundBg.setStroke((int) (1 * density), 0x1AFFFFFF);
btnFoundation.setBackground(foundBg);
GradientDrawable corpBg = new GradientDrawable();
corpBg.setColor(0x332563EB);
corpBg.setCornerRadius(cornerRadius);
corpBg.setStroke((int) (2 * density), 0xFF2563EB);
btnCorp.setBackground(corpBg);
}
}
//
// Accent Colors
//
private void setupAccentColors() {
accentColorRow.removeAllViews();
String currentAccent = themeManager.getCustomAccentColorHex();
for (String[] accentDef : ThemeManager.ACCENT_COLORS) {
String name = accentDef[0];
String hex = accentDef[1];
FrameLayout swatch = new FrameLayout(this);
int size = dpToPx(36);
LinearLayout.LayoutParams swatchParams = new LinearLayout.LayoutParams(size, size);
swatchParams.setMarginEnd(dpToPx(10));
swatch.setLayoutParams(swatchParams);
boolean isSelected = hex.equalsIgnoreCase(currentAccent);
GradientDrawable swatchBg = new GradientDrawable();
swatchBg.setShape(GradientDrawable.OVAL);
swatchBg.setColor(Color.parseColor(hex));
if (isSelected) {
swatchBg.setStroke(dpToPx(2), Color.WHITE);
}
swatch.setBackground(swatchBg);
// Checkmark for selected
if (isSelected) {
TextView check = new TextView(this);
check.setText("");
check.setTextColor(Color.WHITE);
check.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
check.setGravity(Gravity.CENTER);
swatch.addView(check, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
swatch.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
themeManager.setCustomAccentColor(hex);
setupAccentColors(); // Rebuild to update selection
AeThexToast.show(this, "Accent: " + name, AeThexToast.Type.INFO);
});
accentColorRow.addView(swatch);
}
accentReset.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
themeManager.setCustomAccentColor(null);
setupAccentColors();
AeThexToast.show(this, "Accent reset to default", AeThexToast.Type.INFO);
});
}
//
// Wallpaper Picker
//
private void setupWallpaperPicker() {
refreshWallpaperPicker();
}
private void refreshWallpaperPicker() {
wallpaperGrid.removeAllViews();
String currentWallpaper = themeManager.getWallpaperId();
boolean konamiUnlocked = themeManager.isKonamiUnlocked();
Typeface monoFont = themeManager.getMonoFont(this);
// Build rows of 2 wallpaper cards
LinearLayout currentRow = null;
int count = 0;
for (String[] wallDef : ThemeManager.WALLPAPERS) {
String id = wallDef[0];
String name = wallDef[1];
boolean isSecret = "true".equals(wallDef[2]);
// Skip secret wallpapers if not unlocked
if (isSecret && !konamiUnlocked) continue;
if (count % 2 == 0) {
currentRow = new LinearLayout(this);
currentRow.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.LayoutParams rowParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
rowParams.bottomMargin = dpToPx(10);
currentRow.setLayoutParams(rowParams);
wallpaperGrid.addView(currentRow);
}
boolean isSelected = id.equals(currentWallpaper);
View card = createWallpaperCard(id, name, isSelected, isSecret, monoFont);
currentRow.addView(card);
count++;
}
}
private View createWallpaperCard(String id, String name, boolean isSelected,
boolean isSecret, Typeface monoFont) {
LinearLayout card = new LinearLayout(this);
card.setOrientation(LinearLayout.VERTICAL);
card.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f);
cardParams.setMarginEnd(dpToPx(8));
card.setLayoutParams(cardParams);
card.setPadding(dpToPx(8), dpToPx(8), dpToPx(8), dpToPx(8));
// Card background
GradientDrawable cardBg = new GradientDrawable();
cardBg.setCornerRadius(dpToPx(12));
if (isSelected) {
cardBg.setStroke(dpToPx(2), Color.WHITE);
cardBg.setColor(Color.parseColor("#1AFFFFFF"));
} else {
cardBg.setStroke(dpToPx(1), Color.parseColor("#33FFFFFF"));
cardBg.setColor(Color.parseColor("#0DFFFFFF"));
}
card.setBackground(cardBg);
// Preview gradient
View preview = new View(this);
LinearLayout.LayoutParams previewParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, dpToPx(60));
preview.setLayoutParams(previewParams);
GradientDrawable previewGd;
if (ThemeManager.WALLPAPER_DEFAULT.equals(id)) {
// Show current theme default wallpaper
previewGd = themeManager.createWallpaperDrawable(this);
} else {
previewGd = ThemeManager.createWallpaperPreview(id);
}
previewGd.setCornerRadius(dpToPx(8));
preview.setBackground(previewGd);
card.addView(preview);
// Name label
TextView label = new TextView(this);
label.setText(name);
label.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
label.setTextColor(isSelected ? Color.WHITE : Color.parseColor("#99FFFFFF"));
label.setTypeface(monoFont);
label.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams labelParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
labelParams.topMargin = dpToPx(6);
label.setLayoutParams(labelParams);
card.addView(label);
// Selected indicator
if (isSelected) {
TextView check = new TextView(this);
check.setText("● Active");
check.setTextSize(TypedValue.COMPLEX_UNIT_SP, 9);
check.setTextColor(Color.parseColor("#22C55E"));
check.setTypeface(monoFont);
check.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams checkParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
checkParams.topMargin = dpToPx(2);
check.setLayoutParams(checkParams);
card.addView(check);
}
card.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
themeManager.setWallpaperId(id);
refreshWallpaperPicker();
AeThexToast.show(this, "Wallpaper: " + name, AeThexToast.Type.SUCCESS);
});
return card;
}
//
// Transparency Slider
//
private void setupTransparencySlider() {
int currentTransparency = themeManager.getTransparency();
// SeekBar max=50, representing 50-100 range
transparencySlider.setProgress(currentTransparency - 50);
transparencyValue.setText("Current: " + currentTransparency + "%");
transparencySlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
int value = progress + 50; // Map 0-50 to 50-100
transparencyValue.setText("Current: " + value + "%");
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int value = seekBar.getProgress() + 50;
themeManager.setTransparency(value);
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
AeThexToast.show(SettingsActivity.this,
"Panel transparency: " + value + "%", AeThexToast.Type.INFO);
}
});
}
//
// Sound Toggle
//
private void setupSoundToggle() {
soundToggle.setOnClickListener(v -> {
boolean newState = !themeManager.isSoundEnabled();
themeManager.setSoundEnabled(newState);
SoundManager.getInstance().setEnabled(newState);
updateSoundToggle();
if (newState) {
SoundManager.getInstance().play(SoundManager.Sound.NOTIFICATION);
}
AeThexToast.show(this, "Sound " + (newState ? "enabled" : "disabled"),
AeThexToast.Type.INFO);
});
}
private void updateSoundToggle() {
boolean enabled = themeManager.isSoundEnabled();
soundToggle.setText(enabled ? "ON" : "OFF");
GradientDrawable bg = new GradientDrawable();
bg.setCornerRadius(dpToPx(8));
if (enabled) {
bg.setColor(Color.parseColor("#1A22C55E"));
bg.setStroke(dpToPx(1), Color.parseColor("#22C55E"));
soundToggle.setTextColor(Color.parseColor("#22C55E"));
} else {
bg.setColor(Color.parseColor("#1AEF4444"));
bg.setStroke(dpToPx(1), Color.parseColor("#EF4444"));
soundToggle.setTextColor(Color.parseColor("#EF4444"));
}
soundToggle.setBackground(bg);
}
//
// Konami Easter Egg
//
private void setupKonamiEasterEgg() {
if (themeManager.isKonamiUnlocked()) {
aboutKonamiHint.setText("✓ Secret wallpapers unlocked");
aboutKonamiHint.setTextColor(Color.parseColor("#22C55E"));
}
aboutCard.setOnClickListener(v -> {
konamiTapCount++;
if (konamiTapCount >= 7 && !themeManager.isKonamiUnlocked()) {
themeManager.setKonamiUnlocked(true);
SoundManager.getInstance().play(SoundManager.Sound.NOTIFICATION);
aboutKonamiHint.setText("✓ Secret wallpapers unlocked");
aboutKonamiHint.setTextColor(Color.parseColor("#22C55E"));
refreshWallpaperPicker();
AeThexToast.show(this, "🔓 Secret wallpapers unlocked!", AeThexToast.Type.SUCCESS);
konamiTapCount = 0;
} else if (konamiTapCount >= 4 && !themeManager.isKonamiUnlocked()) {
int remaining = 7 - konamiTapCount;
aboutKonamiHint.setText(remaining + " more taps...");
}
});
}
//
// Device Settings
//
private void setupDeviceSettings() {
LinearLayout btn = findViewById(R.id.device_settings_btn);
btn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
v.animate().scaleX(0.97f).scaleY(0.97f).setDuration(80).withEndAction(() -> {
v.animate().scaleX(1f).scaleY(1f).setDuration(80).start();
startActivity(new Intent(Settings.ACTION_SETTINGS));
}).start();
});
}
//
// Hidden Apps
//
private void setupHiddenApps() {
LinearLayout btn = findViewById(R.id.hidden_apps_btn);
TextView countLabel = findViewById(R.id.hidden_apps_count);
ScreenTimeTracker tracker = new ScreenTimeTracker(this);
Set<String> hidden = tracker.getHiddenApps();
int count = hidden.size();
countLabel.setText(count > 0 ? count + " hidden app" + (count != 1 ? "s" : "") : "No hidden apps");
btn.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
Set<String> currentHidden = tracker.getHiddenApps();
if (currentHidden.isEmpty()) {
AeThexToast.show(this, "No hidden apps to restore", AeThexToast.Type.INFO);
return;
}
// Unhide all
for (String appId : currentHidden) {
tracker.setHidden(appId, false);
}
countLabel.setText("No hidden apps");
AeThexToast.show(this, currentHidden.size() + " app(s) restored", AeThexToast.Type.SUCCESS);
});
}
//
// Utility
//
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);
}
}
}

View file

@ -0,0 +1,511 @@
package com.aethex.os;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;
public class SnakeActivity extends AppCompatActivity {
private ThemeManager themeManager;
private SnakeView snakeView;
private TextView scoreText;
private TextView highScoreText;
private TextView statusText;
private FrameLayout gameOverOverlay;
private TextView finalScoreText;
private TextView finalHighScoreText;
private int score = 0;
private int highScore = 0;
private static final String PREFS_NAME = "snake_prefs";
private static final String KEY_HIGH_SCORE = "high_score";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_snake);
hideSystemUI();
themeManager = new ThemeManager(this);
// Load high score
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
highScore = prefs.getInt(KEY_HIGH_SCORE, 0);
// Bind views
scoreText = findViewById(R.id.snake_score);
highScoreText = findViewById(R.id.snake_high_score);
statusText = findViewById(R.id.snake_status);
gameOverOverlay = findViewById(R.id.snake_game_over_overlay);
finalScoreText = findViewById(R.id.snake_final_score);
finalHighScoreText = findViewById(R.id.snake_final_high_score);
highScoreText.setText("HIGH: " + highScore);
// Back button
findViewById(R.id.snake_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Create and add SnakeView
FrameLayout container = findViewById(R.id.snake_board_container);
snakeView = new SnakeView(this);
container.addView(snakeView, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
// Game over tap to restart
gameOverOverlay.setOnClickListener(v -> {
gameOverOverlay.setVisibility(View.GONE);
snakeView.resetGame();
});
}
private void updateScore(int newScore) {
score = newScore;
scoreText.setText("SCORE: " + score);
}
private void showGameOver() {
if (score > highScore) {
highScore = score;
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
prefs.edit().putInt(KEY_HIGH_SCORE, highScore).apply();
highScoreText.setText("HIGH: " + highScore);
}
finalScoreText.setText("Score: " + score);
finalHighScoreText.setText("High Score: " + highScore);
statusText.setText("Game over");
gameOverOverlay.setVisibility(View.VISIBLE);
}
@Override
protected void onPause() {
super.onPause();
if (snakeView != null) {
snakeView.pause();
}
}
@Override
protected void onResume() {
super.onResume();
if (snakeView != null && snakeView.isRunning) {
snakeView.resume();
}
}
@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);
}
}
// =========================================================================
// Inner class: SnakeView - Custom View that renders the snake game
// =========================================================================
public class SnakeView extends View {
private static final int GRID_SIZE = 20;
private static final int INITIAL_DELAY = 200; // ms between moves
private static final int MIN_DELAY = 80; // fastest speed
private static final int SPEED_INCREMENT = 3; // ms faster per food eaten
// Direction constants
private static final int DIR_UP = 0;
private static final int DIR_RIGHT = 1;
private static final int DIR_DOWN = 2;
private static final int DIR_LEFT = 3;
// Game state
private LinkedList<int[]> snake; // head is first element
private int[] food;
private int direction;
private int nextDirection;
private boolean gameStarted = false;
boolean isRunning = false;
private boolean isPaused = false;
private int currentDelay;
// Drawing
private float cellSize;
private float boardOffsetX;
private float boardOffsetY;
private float boardSize;
private Paint snakePaint;
private Paint snakeHeadPaint;
private Paint foodPaint;
private Paint foodGlowPaint;
private Paint gridPaint;
private Paint borderPaint;
private Paint bgPaint;
private RectF cellRect;
// Game loop
private final Handler handler = new Handler(Looper.getMainLooper());
private final Runnable gameLoop = new Runnable() {
@Override
public void run() {
if (isRunning && !isPaused) {
update();
invalidate();
handler.postDelayed(this, currentDelay);
}
}
};
// Gesture detection
private GestureDetector gestureDetector;
private Random random = new Random();
public SnakeView(Context context) {
super(context);
init();
}
private void init() {
// Snake body paint - emerald green
snakePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
snakePaint.setColor(Color.parseColor("#34D399"));
snakePaint.setStyle(Paint.Style.FILL);
// Snake head paint - slightly brighter
snakeHeadPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
snakeHeadPaint.setColor(Color.parseColor("#6EE7B7"));
snakeHeadPaint.setStyle(Paint.Style.FILL);
// Food paint - red
foodPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
foodPaint.setColor(Color.parseColor("#F87171"));
foodPaint.setStyle(Paint.Style.FILL);
// Food glow effect
foodGlowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
foodGlowPaint.setColor(Color.parseColor("#33F87171"));
foodGlowPaint.setStyle(Paint.Style.FILL);
// Grid lines - very subtle
gridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
gridPaint.setColor(Color.parseColor("#0D34D399"));
gridPaint.setStyle(Paint.Style.STROKE);
gridPaint.setStrokeWidth(1f);
// Board border
borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
borderPaint.setColor(Color.parseColor("#1A34D399"));
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(2f);
// Board background
bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
bgPaint.setColor(Color.parseColor("#050A0D"));
bgPaint.setStyle(Paint.Style.FILL);
cellRect = new RectF();
// Setup gesture detector for swipe
gestureDetector = new GestureDetector(getContext(),
new GestureDetector.SimpleOnGestureListener() {
private static final int SWIPE_THRESHOLD = 30;
private static final int SWIPE_VELOCITY_THRESHOLD = 30;
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {
if (e1 == null || e2 == null) return false;
float diffX = e2.getX() - e1.getX();
float diffY = e2.getY() - e1.getY();
if (Math.abs(diffX) > Math.abs(diffY)) {
// Horizontal swipe
if (Math.abs(diffX) > SWIPE_THRESHOLD &&
Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
if (diffX > 0) {
setDirection(DIR_RIGHT);
} else {
setDirection(DIR_LEFT);
}
return true;
}
} else {
// Vertical swipe
if (Math.abs(diffY) > SWIPE_THRESHOLD &&
Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) {
if (diffY > 0) {
setDirection(DIR_DOWN);
} else {
setDirection(DIR_UP);
}
return true;
}
}
return false;
}
});
resetGame();
}
private void setDirection(int newDir) {
// Start game on first swipe
if (!gameStarted) {
gameStarted = true;
isRunning = true;
((SnakeActivity) getContext()).statusText.setText("Playing");
handler.post(gameLoop);
}
// Prevent 180-degree turns
if ((newDir == DIR_UP && direction != DIR_DOWN) ||
(newDir == DIR_DOWN && direction != DIR_UP) ||
(newDir == DIR_LEFT && direction != DIR_RIGHT) ||
(newDir == DIR_RIGHT && direction != DIR_LEFT)) {
nextDirection = newDir;
}
}
void resetGame() {
handler.removeCallbacks(gameLoop);
snake = new LinkedList<>();
// Start in center, 3 segments, moving right
int centerX = GRID_SIZE / 2;
int centerY = GRID_SIZE / 2;
snake.add(new int[]{centerX, centerY});
snake.add(new int[]{centerX - 1, centerY});
snake.add(new int[]{centerX - 2, centerY});
direction = DIR_RIGHT;
nextDirection = DIR_RIGHT;
currentDelay = INITIAL_DELAY;
gameStarted = false;
isRunning = false;
isPaused = false;
spawnFood();
((SnakeActivity) getContext()).updateScore(0);
((SnakeActivity) getContext()).statusText.setText("Swipe to start");
invalidate();
}
private void spawnFood() {
// Build list of all empty cells
ArrayList<int[]> emptyCells = new ArrayList<>();
for (int x = 0; x < GRID_SIZE; x++) {
for (int y = 0; y < GRID_SIZE; y++) {
boolean occupied = false;
for (int[] segment : snake) {
if (segment[0] == x && segment[1] == y) {
occupied = true;
break;
}
}
if (!occupied) {
emptyCells.add(new int[]{x, y});
}
}
}
if (!emptyCells.isEmpty()) {
food = emptyCells.get(random.nextInt(emptyCells.size()));
}
}
private void update() {
direction = nextDirection;
// Calculate new head position
int[] head = snake.getFirst();
int newX = head[0];
int newY = head[1];
switch (direction) {
case DIR_UP: newY--; break;
case DIR_DOWN: newY++; break;
case DIR_LEFT: newX--; break;
case DIR_RIGHT: newX++; break;
}
// Check wall collision
if (newX < 0 || newX >= GRID_SIZE || newY < 0 || newY >= GRID_SIZE) {
gameOver();
return;
}
// Check self collision
for (int[] segment : snake) {
if (segment[0] == newX && segment[1] == newY) {
gameOver();
return;
}
}
// Add new head
snake.addFirst(new int[]{newX, newY});
// Check food collision
if (food != null && newX == food[0] && newY == food[1]) {
// Ate food - don't remove tail (snake grows)
int newScore = ((SnakeActivity) getContext()).score + 10;
((SnakeActivity) getContext()).updateScore(newScore);
// Speed up
currentDelay = Math.max(MIN_DELAY, currentDelay - SPEED_INCREMENT);
// Spawn new food
spawnFood();
} else {
// Remove tail (snake moves)
snake.removeLast();
}
}
private void gameOver() {
isRunning = false;
gameStarted = false;
handler.removeCallbacks(gameLoop);
((SnakeActivity) getContext()).showGameOver();
}
void pause() {
isPaused = true;
handler.removeCallbacks(gameLoop);
}
void resume() {
if (isRunning) {
isPaused = false;
handler.post(gameLoop);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Calculate board dimensions to be square and centered
boardSize = Math.min(w, h);
cellSize = boardSize / GRID_SIZE;
boardOffsetX = (w - boardSize) / 2f;
boardOffsetY = (h - boardSize) / 2f;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw board background
canvas.drawRect(boardOffsetX, boardOffsetY,
boardOffsetX + boardSize, boardOffsetY + boardSize, bgPaint);
// Draw grid lines
for (int i = 0; i <= GRID_SIZE; i++) {
float x = boardOffsetX + i * cellSize;
float y = boardOffsetY + i * cellSize;
// Vertical line
canvas.drawLine(x, boardOffsetY, x, boardOffsetY + boardSize, gridPaint);
// Horizontal line
canvas.drawLine(boardOffsetX, y, boardOffsetX + boardSize, y, gridPaint);
}
// Draw board border
canvas.drawRect(boardOffsetX, boardOffsetY,
boardOffsetX + boardSize, boardOffsetY + boardSize, borderPaint);
float padding = cellSize * 0.08f;
float cornerRadius = cellSize * 0.2f;
// Draw food with glow effect
if (food != null) {
float foodCX = boardOffsetX + food[0] * cellSize + cellSize / 2f;
float foodCY = boardOffsetY + food[1] * cellSize + cellSize / 2f;
// Outer glow
canvas.drawCircle(foodCX, foodCY, cellSize * 0.7f, foodGlowPaint);
// Food cell
cellRect.set(
boardOffsetX + food[0] * cellSize + padding,
boardOffsetY + food[1] * cellSize + padding,
boardOffsetX + (food[0] + 1) * cellSize - padding,
boardOffsetY + (food[1] + 1) * cellSize - padding);
canvas.drawRoundRect(cellRect, cornerRadius, cornerRadius, foodPaint);
}
// Draw snake
for (int i = 0; i < snake.size(); i++) {
int[] segment = snake.get(i);
cellRect.set(
boardOffsetX + segment[0] * cellSize + padding,
boardOffsetY + segment[1] * cellSize + padding,
boardOffsetX + (segment[0] + 1) * cellSize - padding,
boardOffsetY + (segment[1] + 1) * cellSize - padding);
if (i == 0) {
// Head - brighter color
canvas.drawRoundRect(cellRect, cornerRadius, cornerRadius, snakeHeadPaint);
} else {
// Body - with subtle alpha gradient toward tail
int alpha = Math.max(120, 255 - (i * 4));
snakePaint.setAlpha(alpha);
canvas.drawRoundRect(cellRect, cornerRadius, cornerRadius, snakePaint);
}
}
// Reset alpha
snakePaint.setAlpha(255);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event);
}
}
}

View file

@ -0,0 +1,324 @@
package com.aethex.os;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* SnakeGame - Classic snake game for Pixel Arcade module.
* Swipe to control direction. Eat food to grow. Don't hit walls or yourself!
*/
public class SnakeGame extends View {
private static final int GRID_SIZE = 20;
private static final int GAME_SPEED = 150; // ms per frame
private int gridWidth, gridHeight;
private float cellSize;
private List<int[]> snake = new ArrayList<>();
private int[] food = new int[2];
private int direction = 0; // 0=right, 1=down, 2=left, 3=up
private int nextDirection = 0;
private boolean gameOver = false;
private boolean gameStarted = false;
private int score = 0;
private int highScore = 0;
private Paint snakePaint;
private Paint headPaint;
private Paint foodPaint;
private Paint gridPaint;
private Paint textPaint;
private Paint overlayPaint;
private Handler handler;
private Runnable gameLoop;
private Random random = new Random();
private float touchStartX, touchStartY;
private int primaryColor;
public SnakeGame(Context context) {
super(context);
init(context);
}
public SnakeGame(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
ThemeManager tm = new ThemeManager(context);
primaryColor = tm.getPrimaryColor(context);
snakePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
snakePaint.setColor(primaryColor);
headPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
headPaint.setColor(Color.WHITE);
foodPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
foodPaint.setColor(Color.parseColor("#EF4444"));
gridPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
gridPaint.setColor(Color.parseColor("#1AFFFFFF"));
gridPaint.setStyle(Paint.Style.STROKE);
gridPaint.setStrokeWidth(1);
textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setColor(Color.WHITE);
textPaint.setTextAlign(Paint.Align.CENTER);
overlayPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
overlayPaint.setColor(Color.parseColor("#CC000000"));
handler = new Handler(Looper.getMainLooper());
gameLoop = new Runnable() {
@Override
public void run() {
if (!gameOver && gameStarted) {
update();
invalidate();
handler.postDelayed(this, GAME_SPEED);
}
}
};
// Load high score
highScore = context.getSharedPreferences("aethex_arcade", Context.MODE_PRIVATE)
.getInt("snake_highscore", 0);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
cellSize = Math.min(w, h) / (float) GRID_SIZE;
gridWidth = (int) (w / cellSize);
gridHeight = (int) (h / cellSize);
resetGame();
}
private void resetGame() {
snake.clear();
// Start in center
int startX = gridWidth / 2;
int startY = gridHeight / 2;
snake.add(new int[]{startX, startY});
snake.add(new int[]{startX - 1, startY});
snake.add(new int[]{startX - 2, startY});
direction = 0;
nextDirection = 0;
gameOver = false;
gameStarted = false;
score = 0;
spawnFood();
invalidate();
}
private void spawnFood() {
boolean valid;
do {
food[0] = random.nextInt(gridWidth);
food[1] = random.nextInt(gridHeight);
valid = true;
for (int[] seg : snake) {
if (seg[0] == food[0] && seg[1] == food[1]) {
valid = false;
break;
}
}
} while (!valid);
}
private void update() {
direction = nextDirection;
int[] head = snake.get(0);
int newX = head[0];
int newY = head[1];
switch (direction) {
case 0: newX++; break; // right
case 1: newY++; break; // down
case 2: newX--; break; // left
case 3: newY--; break; // up
}
// Wall collision
if (newX < 0 || newX >= gridWidth || newY < 0 || newY >= gridHeight) {
endGame();
return;
}
// Self collision
for (int[] seg : snake) {
if (seg[0] == newX && seg[1] == newY) {
endGame();
return;
}
}
// Move snake
snake.add(0, new int[]{newX, newY});
// Check food
if (newX == food[0] && newY == food[1]) {
score += 10;
spawnFood();
} else {
snake.remove(snake.size() - 1);
}
}
private void endGame() {
gameOver = true;
if (score > highScore) {
highScore = score;
getContext().getSharedPreferences("aethex_arcade", Context.MODE_PRIVATE)
.edit().putInt("snake_highscore", highScore).apply();
}
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Background
canvas.drawColor(Color.parseColor("#0A0C14"));
// Grid
for (int x = 0; x <= gridWidth; x++) {
canvas.drawLine(x * cellSize, 0, x * cellSize, gridHeight * cellSize, gridPaint);
}
for (int y = 0; y <= gridHeight; y++) {
canvas.drawLine(0, y * cellSize, gridWidth * cellSize, y * cellSize, gridPaint);
}
// Food with glow
float fx = food[0] * cellSize + cellSize / 2;
float fy = food[1] * cellSize + cellSize / 2;
foodPaint.setAlpha(60);
canvas.drawCircle(fx, fy, cellSize * 0.6f, foodPaint);
foodPaint.setAlpha(255);
canvas.drawCircle(fx, fy, cellSize * 0.35f, foodPaint);
// Snake
float cornerRadius = cellSize * 0.3f;
for (int i = 0; i < snake.size(); i++) {
int[] seg = snake.get(i);
RectF rect = new RectF(
seg[0] * cellSize + 2,
seg[1] * cellSize + 2,
(seg[0] + 1) * cellSize - 2,
(seg[1] + 1) * cellSize - 2
);
if (i == 0) {
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, headPaint);
} else {
int alpha = 255 - (i * 8);
snakePaint.setAlpha(Math.max(100, alpha));
canvas.drawRoundRect(rect, cornerRadius, cornerRadius, snakePaint);
}
}
snakePaint.setAlpha(255);
// Score
textPaint.setTextSize(cellSize * 0.8f);
textPaint.setColor(Color.parseColor("#66FFFFFF"));
canvas.drawText("SCORE: " + score, getWidth() / 2f, cellSize * 1.2f, textPaint);
// High score
textPaint.setTextSize(cellSize * 0.5f);
canvas.drawText("HI: " + highScore, getWidth() / 2f, cellSize * 1.9f, textPaint);
// Game over / Start overlay
if (gameOver || !gameStarted) {
canvas.drawRect(0, 0, getWidth(), getHeight(), overlayPaint);
textPaint.setColor(Color.WHITE);
if (gameOver) {
textPaint.setTextSize(cellSize * 1.5f);
canvas.drawText("GAME OVER", getWidth() / 2f, getHeight() / 2f - cellSize, textPaint);
textPaint.setTextSize(cellSize);
canvas.drawText("Score: " + score, getWidth() / 2f, getHeight() / 2f + cellSize * 0.5f, textPaint);
if (score >= highScore && score > 0) {
textPaint.setColor(Color.parseColor("#FBBF24"));
canvas.drawText("NEW HIGH SCORE!", getWidth() / 2f, getHeight() / 2f + cellSize * 1.8f, textPaint);
}
} else {
textPaint.setTextSize(cellSize * 1.5f);
textPaint.setColor(primaryColor);
canvas.drawText("SNAKE", getWidth() / 2f, getHeight() / 2f - cellSize * 2, textPaint);
textPaint.setTextSize(cellSize * 0.7f);
textPaint.setColor(Color.parseColor("#88FFFFFF"));
canvas.drawText("Swipe to control", getWidth() / 2f, getHeight() / 2f, textPaint);
}
textPaint.setTextSize(cellSize * 0.6f);
textPaint.setColor(Color.parseColor("#66FFFFFF"));
canvas.drawText("TAP TO " + (gameOver ? "RESTART" : "START"), getWidth() / 2f, getHeight() - cellSize * 2, textPaint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStartX = event.getX();
touchStartY = event.getY();
return true;
case MotionEvent.ACTION_UP:
float dx = event.getX() - touchStartX;
float dy = event.getY() - touchStartY;
if (Math.abs(dx) < 30 && Math.abs(dy) < 30) {
// Tap
if (gameOver) {
resetGame();
} else if (!gameStarted) {
gameStarted = true;
handler.post(gameLoop);
}
} else if (gameStarted && !gameOver) {
// Swipe
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal swipe
if (dx > 0 && direction != 2) nextDirection = 0;
else if (dx < 0 && direction != 0) nextDirection = 2;
} else {
// Vertical swipe
if (dy > 0 && direction != 3) nextDirection = 1;
else if (dy < 0 && direction != 1) nextDirection = 3;
}
}
return true;
}
return super.onTouchEvent(event);
}
public void pause() {
handler.removeCallbacks(gameLoop);
}
public void resume() {
if (gameStarted && !gameOver) {
handler.post(gameLoop);
}
}
}

View file

@ -0,0 +1,162 @@
package com.aethex.os;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioTrack;
import java.util.EnumMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Singleton sound manager for AeThexOS Android.
* Generates tones programmatically using AudioTrack - no sound asset files needed.
*/
public class SoundManager {
private static final int SAMPLE_RATE = 44100;
private static final float VOLUME = 0.3f;
public enum Sound {
OPEN(523, 0.1, WaveType.SINE),
CLOSE(392, 0.1, WaveType.SINE),
CLICK(800, 0.03, WaveType.SQUARE),
NOTIFICATION(880, 0.15, WaveType.SINE),
SWITCH(440, 0.2, WaveType.SAWTOOTH),
BOOT_BEEP(660, 0.05, WaveType.SINE);
final int frequency;
final double duration;
final WaveType waveType;
Sound(int frequency, double duration, WaveType waveType) {
this.frequency = frequency;
this.duration = duration;
this.waveType = waveType;
}
}
private enum WaveType {
SINE, SQUARE, SAWTOOTH
}
private static volatile SoundManager instance;
private final Map<Sound, byte[]> bufferCache = new EnumMap<>(Sound.class);
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private volatile boolean enabled = true;
private SoundManager() {
}
public static SoundManager getInstance() {
if (instance == null) {
synchronized (SoundManager.class) {
if (instance == null) {
instance = new SoundManager();
}
}
}
return instance;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return enabled;
}
/**
* Play a sound on a background thread. Non-blocking.
*/
public void play(Sound sound) {
if (!enabled) return;
executor.execute(() -> {
try {
byte[] pcm = getOrGenerateBuffer(sound);
int bufferSize = pcm.length;
AudioAttributes attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
AudioFormat format = new AudioFormat.Builder()
.setSampleRate(SAMPLE_RATE)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build();
AudioTrack track = new AudioTrack.Builder()
.setAudioAttributes(attributes)
.setAudioFormat(format)
.setBufferSizeInBytes(bufferSize)
.setTransferMode(AudioTrack.MODE_STATIC)
.build();
track.write(pcm, 0, pcm.length);
track.setVolume(VOLUME);
track.play();
// Wait for playback to complete, then release
long durationMs = (long) (sound.duration * 1000) + 50;
Thread.sleep(durationMs);
track.stop();
track.release();
} catch (Exception e) {
// Silently ignore sound playback failures
}
});
}
private synchronized byte[] getOrGenerateBuffer(Sound sound) {
byte[] cached = bufferCache.get(sound);
if (cached != null) return cached;
byte[] buffer = generatePcmBuffer(sound.frequency, sound.duration, sound.waveType);
bufferCache.put(sound, buffer);
return buffer;
}
private byte[] generatePcmBuffer(int frequency, double duration, WaveType waveType) {
int numSamples = (int) (SAMPLE_RATE * duration);
byte[] pcm = new byte[numSamples * 2]; // 16-bit mono = 2 bytes per sample
for (int i = 0; i < numSamples; i++) {
double t = (double) i / SAMPLE_RATE;
double sample;
switch (waveType) {
case SQUARE:
sample = Math.signum(Math.sin(2.0 * Math.PI * frequency * t));
break;
case SAWTOOTH:
sample = 2.0 * (t * frequency - Math.floor(0.5 + t * frequency));
break;
case SINE:
default:
sample = Math.sin(2.0 * Math.PI * frequency * t);
break;
}
// Apply a short fade-in/fade-out envelope to avoid clicks
int fadeLength = Math.min(numSamples / 10, SAMPLE_RATE / 200);
if (i < fadeLength) {
sample *= (double) i / fadeLength;
} else if (i > numSamples - fadeLength) {
sample *= (double) (numSamples - i) / fadeLength;
}
short pcmValue = (short) (sample * Short.MAX_VALUE);
pcm[i * 2] = (byte) (pcmValue & 0xFF);
pcm[i * 2 + 1] = (byte) ((pcmValue >> 8) & 0xFF);
}
return pcm;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,559 @@
package com.aethex.os;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* AeThexOS Terminal - Real root shell with full system access.
* Execute actual Linux commands on the device.
*/
public class TerminalActivity extends AppCompatActivity {
private ThemeManager themeManager;
private TextView outputView;
private EditText inputView;
private ScrollView scrollView;
private Handler mainHandler;
// Current working directory (simulated)
private String currentDir = "/sdcard";
// Command history
private List<String> commandHistory = new ArrayList<>();
private int historyIndex = -1;
// Root status
private boolean hasRoot = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_terminal);
hideSystemUI();
themeManager = new ThemeManager(this);
mainHandler = new Handler(Looper.getMainLooper());
outputView = findViewById(R.id.terminal_output);
inputView = findViewById(R.id.terminal_input);
scrollView = findViewById(R.id.terminal_scroll);
// Apply Source Code Pro font
outputView.setTypeface(themeManager.getMonoFont(this));
inputView.setTypeface(themeManager.getMonoFont(this));
findViewById(R.id.terminal_back).setOnClickListener(v -> {
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Check root status
new Thread(() -> {
hasRoot = RootShell.isRootAvailable();
mainHandler.post(this::showWelcome);
}).start();
// Handle enter key
inputView.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEND ||
(event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER
&& event.getAction() == KeyEvent.ACTION_DOWN)) {
String cmd = inputView.getText().toString().trim();
if (!cmd.isEmpty()) {
commandHistory.add(cmd);
historyIndex = commandHistory.size();
processCommand(cmd);
inputView.setText("");
}
return true;
}
return false;
});
inputView.requestFocus();
AeThexKeyboard.attach(this);
}
private void showWelcome() {
appendColored("╔══════════════════════════════════════════╗", "#06B6D4");
appendColored("║ AeThexOS Terminal v2.1.0 [ROOT] ║", "#06B6D4");
appendColored("╚══════════════════════════════════════════╝", "#06B6D4");
appendOutput("");
if (hasRoot) {
appendColored(" ✓ ROOT ACCESS GRANTED", "#22C55E");
appendColored(" ✓ Full system privileges enabled", "#22C55E");
} else {
appendColored(" ⚠ Root access unavailable", "#FBBF24");
appendColored(" ⚠ Running in restricted mode", "#FBBF24");
}
// Check Terminal Pro
ModuleManager modules = ModuleManager.getInstance(this);
if (modules.hasTerminalPro()) {
appendColored(" ★ Terminal Pro features enabled", "#A855F7");
}
appendOutput("");
appendColored("Type 'help' for commands, or run any Linux command.", "#66FFFFFF");
appendColored("Prefix with 'su' for root execution.\n", "#66FFFFFF");
}
private void processCommand(String cmd) {
String prompt = hasRoot ? "root@aethex:~# " : "user@aethex:~$ ";
appendColored(prompt + cmd, "#06B6D4");
String[] parts = cmd.split("\\s+");
String command = parts[0].toLowerCase();
// Terminal Pro aliases
ModuleManager modules = ModuleManager.getInstance(this);
if (modules.hasTerminalPro()) {
// Expand aliases
switch (command) {
case "ll": cmd = "ls -la"; parts = cmd.split("\\s+"); command = "ls"; break;
case "la": cmd = "ls -a"; parts = cmd.split("\\s+"); command = "ls"; break;
case "l": cmd = "ls -l"; parts = cmd.split("\\s+"); command = "ls"; break;
case "cls": outputView.setText(""); return;
case "..": cmd = "cd .."; parts = cmd.split("\\s+"); command = "cd"; break;
case "c": cmd = "clear"; command = "clear"; break;
case "h": cmd = "history"; command = "history"; break;
}
}
// Built-in commands
switch (command) {
case "help":
showHelp();
return;
case "clear":
case "cls":
outputView.setText("");
return;
case "exit":
case "quit":
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
return;
case "neofetch":
showNeofetch();
return;
case "rootcheck":
checkRoot();
return;
case "sysinfo":
showSysInfo();
return;
case "processes":
case "ps":
showProcesses();
return;
case "netinfo":
showNetInfo();
return;
case "cpuinfo":
showCpuInfo();
return;
case "meminfo":
showMemInfo();
return;
case "diskinfo":
case "df":
showDiskInfo();
return;
case "mounts":
showMounts();
return;
case "selinux":
showSelinux();
return;
case "getprop":
if (parts.length > 1) {
executeAsync(cmd, false);
} else {
appendColored("Usage: getprop <property>", "#FBBF24");
}
return;
case "setprop":
if (parts.length > 2) {
executeAsync(cmd, true);
} else {
appendColored("Usage: setprop <property> <value>", "#FBBF24");
}
return;
case "remount":
remountSystem();
return;
case "reboot":
rebootDevice(parts);
return;
case "dmesg":
executeAsync("dmesg | tail -50", true);
return;
case "logcat":
executeAsync("logcat -d -t 30", false);
return;
case "su":
if (parts.length > 1) {
// Execute rest of command as root
String rootCmd = cmd.substring(3).trim();
executeAsync(rootCmd, true);
} else {
if (hasRoot) {
appendColored("Root shell active. All commands run as root.", "#22C55E");
} else {
appendColored("Root access denied.", "#EF4444");
}
}
return;
case "shell":
case "sh":
if (parts.length > 1) {
String shellCmd = cmd.substring(command.length()).trim();
executeAsync(shellCmd, false);
} else {
appendColored("Usage: sh <command>", "#FBBF24");
}
return;
case "history":
showHistory();
return;
case "modules":
showModules();
return;
}
// Execute as real shell command
executeAsync(cmd, hasRoot);
}
private void showHistory() {
ModuleManager modules = ModuleManager.getInstance(this);
if (!modules.hasTerminalPro()) {
appendColored("⚠ Terminal Pro required for command history", "#FBBF24");
appendColored(" Install from Marketplace to unlock", "#66FFFFFF");
return;
}
appendColored("\n═══ Command History ═══", "#A855F7");
if (commandHistory.isEmpty()) {
appendOutput(" (no commands yet)");
} else {
int start = Math.max(0, commandHistory.size() - 20);
for (int i = start; i < commandHistory.size(); i++) {
appendOutput(" " + (i + 1) + " " + commandHistory.get(i));
}
}
appendOutput("");
}
private void showModules() {
ModuleManager mm = ModuleManager.getInstance(this);
appendColored("\n═══ Installed Modules ═══", "#A855F7");
appendOutput(" " + mm.getInstalledCount() + " modules installed");
appendOutput(" " + mm.getTotalCreditsSpent() + " Cr spent");
appendOutput("");
if (mm.hasTerminalPro()) appendColored(" ✓ Terminal Pro", "#22C55E");
if (mm.hasDataAnalyzer()) appendColored(" ✓ Data Analyzer", "#22C55E");
if (mm.hasNetScanner()) appendColored(" ✓ Net Scanner", "#22C55E");
if (mm.hasHexEditor()) appendColored(" ✓ Hex Editor", "#22C55E");
if (mm.hasEncryption()) appendColored(" ✓ Cipher Module", "#22C55E");
if (mm.hasAppLock()) appendColored(" ✓ Shield Guard", "#22C55E");
if (mm.hasStealthMode()) appendColored(" ✓ Ghost Protocol", "#22C55E");
if (mm.hasBiometricLock()) appendColored(" ✓ Vault Lock", "#22C55E");
if (mm.hasNeonThemes()) appendColored(" ✓ Neon Theme Pack", "#22C55E");
if (mm.hasCRTOverlay()) appendColored(" ✓ Retro CRT Skin", "#22C55E");
if (mm.hasHoloGlass()) appendColored(" ✓ Holo Glass UI", "#22C55E");
if (mm.hasSoundPackPro()) appendColored(" ✓ Sound Pack Pro", "#22C55E");
if (mm.hasPixelArcade()) appendColored(" ✓ Pixel Arcade", "#22C55E");
if (mm.hasSynthStudio()) appendColored(" ✓ Synth Studio", "#22C55E");
appendOutput("");
}
private void showHelp() {
ModuleManager modules = ModuleManager.getInstance(this);
boolean hasPro = modules.hasTerminalPro();
appendColored("\n═══ AeThexOS Terminal Commands ═══", "#06B6D4");
appendOutput("");
appendColored("BUILT-IN:", "#FBBF24");
appendOutput(" help Show this help");
appendOutput(" clear Clear terminal");
appendOutput(" exit Close terminal");
appendOutput(" neofetch System overview");
appendOutput(" modules Show installed modules");
appendOutput("");
appendColored("SYSTEM INFO:", "#FBBF24");
appendOutput(" sysinfo Full system information");
appendOutput(" cpuinfo CPU details");
appendOutput(" meminfo Memory statistics");
appendOutput(" diskinfo Storage usage");
appendOutput(" processes Running processes");
appendOutput(" netinfo Network interfaces");
appendOutput(" mounts Mounted filesystems");
appendOutput(" selinux SELinux status");
appendOutput(" rootcheck Check root access");
appendOutput("");
appendColored("ROOT COMMANDS:", "#EF4444");
appendOutput(" su <cmd> Execute as root");
appendOutput(" remount Remount /system r/w");
appendOutput(" reboot Reboot device");
appendOutput(" reboot recovery");
appendOutput(" reboot bootloader");
appendOutput(" dmesg Kernel messages");
appendOutput(" getprop Get system property");
appendOutput(" setprop Set system property");
appendOutput("");
if (hasPro) {
appendColored("TERMINAL PRO:", "#A855F7");
appendOutput(" history Command history");
appendOutput(" ll Alias for ls -la");
appendOutput(" la Alias for ls -a");
appendOutput(" l Alias for ls -l");
appendOutput(" c Alias for clear");
appendOutput(" h Alias for history");
appendOutput(" .. Alias for cd ..");
appendOutput("");
}
appendColored("SHELL:", "#22C55E");
appendOutput(" Any Linux command will be executed");
appendOutput(" Examples: ls, cat, grep, find, etc.");
appendOutput("");
}
private void showNeofetch() {
ThemeManager tm = new ThemeManager(this);
String clearance = tm.isFoundation() ? "Foundation" : "Corp";
String rootStatus = hasRoot ? "Enabled" : "Disabled";
String selinux = RootShell.getSelinuxStatus();
appendColored("", "#06B6D4");
appendColored(" ▄▀▀▀▀▄ " + "root@aethex", "#06B6D4");
appendColored(" █ ▄▄▄▄ █ " + "─────────────────────", "#06B6D4");
appendColored(" █ █ █ █ " + "OS: AeThex OS v2.1.0", "#FFFFFF");
appendColored(" █ ▀▀▀▀ █ " + "Host: " + android.os.Build.MANUFACTURER + " " + android.os.Build.MODEL, "#FFFFFF");
appendColored(" ▀▄▄▄▄▀ " + "Kernel: " + RootShell.executeNonRoot("uname -r"), "#FFFFFF");
appendColored(" ▄█████▄ " + "Uptime: " + getUptimeString(), "#FFFFFF");
appendColored(" ▀███████▀ " + "Shell: AeThex Terminal", "#FFFFFF");
appendColored(" █ █ █ " + "Root: " + rootStatus, hasRoot ? "#22C55E" : "#EF4444");
appendColored(" ▀▀▀▀▀▀▀ " + "SELinux: " + selinux, "#FFFFFF");
appendColored(" " + "Android: " + android.os.Build.VERSION.RELEASE, "#FFFFFF");
appendColored(" " + "Clearance: " + clearance, tm.isFoundation() ? "#06B6D4" : "#A855F7");
appendOutput("");
}
private void checkRoot() {
appendColored("\n═══ Root Status Check ═══", "#06B6D4");
// Check su binary
appendOutput("Checking su binary...");
boolean suExists = new File("/system/bin/su").exists() ||
new File("/system/xbin/su").exists() ||
new File("/sbin/su").exists();
if (suExists) {
appendColored(" ✓ su binary found", "#22C55E");
} else {
appendColored(" ✗ su binary not found", "#EF4444");
}
// Check root access
appendOutput("Testing root access...");
if (hasRoot) {
appendColored(" ✓ Root access confirmed (uid=0)", "#22C55E");
String whoami = RootShell.execute("whoami");
appendOutput(" Running as: " + whoami);
} else {
appendColored(" ✗ Root access denied", "#EF4444");
}
// Check busybox
appendOutput("Checking busybox...");
String bbCheck = RootShell.executeNonRoot("which busybox 2>/dev/null");
if (!bbCheck.isEmpty() && !bbCheck.startsWith("Error")) {
appendColored(" ✓ Busybox found: " + bbCheck, "#22C55E");
} else {
appendColored(" - Busybox not installed", "#FBBF24");
}
appendOutput("");
}
private void showSysInfo() {
appendColored("\n═══ System Information ═══", "#06B6D4");
appendOutput("");
appendOutput("Device: " + android.os.Build.MANUFACTURER + " " + android.os.Build.MODEL);
appendOutput("Product: " + android.os.Build.PRODUCT);
appendOutput("Board: " + android.os.Build.BOARD);
appendOutput("Hardware: " + android.os.Build.HARDWARE);
appendOutput("Android: " + android.os.Build.VERSION.RELEASE + " (SDK " + android.os.Build.VERSION.SDK_INT + ")");
appendOutput("Build: " + android.os.Build.DISPLAY);
appendOutput("Fingerprint: " + android.os.Build.FINGERPRINT);
appendOutput("Kernel: " + RootShell.executeNonRoot("uname -r"));
appendOutput("Architecture: " + android.os.Build.SUPPORTED_ABIS[0]);
appendOutput("Root: " + (hasRoot ? "Yes" : "No"));
appendOutput("SELinux: " + RootShell.getSelinuxStatus());
appendOutput("");
}
private void showProcesses() {
appendColored("\n═══ Running Processes ═══", "#06B6D4");
executeAsync("ps -A 2>/dev/null | head -25 || ps | head -20", hasRoot);
}
private void showNetInfo() {
appendColored("\n═══ Network Interfaces ═══", "#06B6D4");
executeAsync("ip addr 2>/dev/null || ifconfig 2>/dev/null", false);
}
private void showCpuInfo() {
appendColored("\n═══ CPU Information ═══", "#06B6D4");
executeAsync("cat /proc/cpuinfo | head -25", false);
}
private void showMemInfo() {
appendColored("\n═══ Memory Information ═══", "#06B6D4");
executeAsync("cat /proc/meminfo | head -15", false);
}
private void showDiskInfo() {
appendColored("\n═══ Disk Usage ═══", "#06B6D4");
executeAsync("df -h 2>/dev/null || df", false);
}
private void showMounts() {
appendColored("\n═══ Mounted Filesystems ═══", "#06B6D4");
executeAsync("mount | head -20", false);
}
private void showSelinux() {
appendColored("\n═══ SELinux Status ═══", "#06B6D4");
appendOutput("Status: " + RootShell.getSelinuxStatus());
if (hasRoot) {
executeAsync("cat /sys/fs/selinux/enforce 2>/dev/null && echo '(1=Enforcing, 0=Permissive)'", true);
}
appendOutput("");
}
private void remountSystem() {
if (!hasRoot) {
appendColored("Error: Root access required", "#EF4444");
return;
}
appendColored("Remounting /system as read-write...", "#FBBF24");
executeAsync("mount -o rw,remount /system 2>&1", true);
}
private void rebootDevice(String[] parts) {
if (!hasRoot) {
appendColored("Error: Root access required", "#EF4444");
return;
}
String mode = parts.length > 1 ? parts[1].toLowerCase() : "normal";
String cmd;
switch (mode) {
case "recovery":
appendColored("Rebooting to recovery...", "#FBBF24");
cmd = "reboot recovery";
break;
case "bootloader":
case "fastboot":
appendColored("Rebooting to bootloader...", "#FBBF24");
cmd = "reboot bootloader";
break;
case "download":
appendColored("Rebooting to download mode...", "#FBBF24");
cmd = "reboot download";
break;
default:
appendColored("Rebooting device...", "#FBBF24");
cmd = "reboot";
break;
}
executeAsync(cmd, true);
}
private void executeAsync(String command, boolean useRoot) {
RootShell.executeAsync(command, useRoot, new RootShell.CommandCallback() {
@Override
public void onOutput(String output) {
mainHandler.post(() -> appendOutput(output));
}
@Override
public void onError(String error) {
mainHandler.post(() -> appendColored(error, "#EF4444"));
}
@Override
public void onComplete(int exitCode) {
mainHandler.post(() -> {
if (exitCode != 0 && exitCode != -1) {
appendColored("Exit code: " + exitCode, "#FBBF24");
}
appendOutput("");
});
}
});
}
private String getUptimeString() {
long uptimeMs = android.os.SystemClock.elapsedRealtime();
long hours = uptimeMs / 3600000;
long mins = (uptimeMs % 3600000) / 60000;
return hours + "h " + mins + "m";
}
private void appendOutput(String text) {
outputView.append(text + "\n");
scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));
}
private void appendColored(String text, String hexColor) {
int start = outputView.getText().length();
outputView.append(text + "\n");
// Note: For full color support, would need SpannableString
// This is simplified - the text color is set by the view
scrollView.post(() -> scrollView.fullScroll(View.FOCUS_DOWN));
}
@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);
}
}
}

View file

@ -0,0 +1,510 @@
package com.aethex.os;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.GradientDrawable;
import androidx.core.content.res.ResourcesCompat;
public class ThemeManager {
private static final String PREFS_NAME = "aethex_prefs";
private static final String KEY_CLEARANCE = "clearance_mode";
private static final String KEY_ACCENT_COLOR = "accent_color";
private static final String KEY_WALLPAPER = "wallpaper_id";
private static final String KEY_TRANSPARENCY = "transparency";
private static final String KEY_KONAMI_UNLOCKED = "konami_unlocked";
private static final String KEY_SOUND_ENABLED = "sound_enabled";
public static final String CLEARANCE_FOUNDATION = "foundation";
public static final String CLEARANCE_CORP = "corp";
// Wallpaper IDs
public static final String WALLPAPER_DEFAULT = "default";
public static final String WALLPAPER_CYBER_GRID = "cyber_grid";
public static final String WALLPAPER_MATRIX = "matrix";
public static final String WALLPAPER_NEON_SUNSET = "neon_sunset";
public static final String WALLPAPER_DEEP_OCEAN = "deep_ocean";
public static final String WALLPAPER_VAPORWAVE = "vaporwave";
public static final String WALLPAPER_BLOOD_MOON = "blood_moon";
public static final String WALLPAPER_GALAXY = "galaxy";
// Accent color options
public static final String[][] ACCENT_COLORS = {
{"Cyan", "#06B6D4"},
{"Purple", "#A855F7"},
{"Green", "#22C55E"},
{"Orange", "#F97316"},
{"Pink", "#EC4899"},
{"Red", "#EF4444"},
};
// Wallpaper definitions: {id, name, secret}
public static final String[][] WALLPAPERS = {
{WALLPAPER_DEFAULT, "Default", "false"},
{WALLPAPER_CYBER_GRID, "Cyber Grid", "false"},
{WALLPAPER_MATRIX, "Matrix", "false"},
{WALLPAPER_NEON_SUNSET, "Neon Sunset", "false"},
{WALLPAPER_DEEP_OCEAN, "Deep Ocean", "false"},
{WALLPAPER_VAPORWAVE, "⚡ Vaporwave", "true"},
{WALLPAPER_BLOOD_MOON, "🔥 Blood Moon", "true"},
{WALLPAPER_GALAXY, "🌌 Galaxy", "true"},
};
private final SharedPreferences prefs;
// Cached typefaces
private static Typeface cachedDisplayFont;
private static Typeface cachedMonoFont;
public ThemeManager(Context context) {
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
}
// Clearance mode
public String getClearanceMode() {
return prefs.getString(KEY_CLEARANCE, CLEARANCE_FOUNDATION);
}
public void setClearanceMode(String mode) {
prefs.edit().putString(KEY_CLEARANCE, mode).apply();
}
public boolean isFoundation() {
return CLEARANCE_FOUNDATION.equals(getClearanceMode());
}
// Accent color
/**
* Returns the user-chosen custom accent color hex string, or null if using theme default.
*/
public String getCustomAccentColorHex() {
return prefs.getString(KEY_ACCENT_COLOR, null);
}
/**
* Sets a custom accent color override. Pass null to reset to theme default.
*/
public void setCustomAccentColor(String hex) {
if (hex == null) {
prefs.edit().remove(KEY_ACCENT_COLOR).apply();
} else {
prefs.edit().putString(KEY_ACCENT_COLOR, hex).apply();
}
}
/**
* Returns the effective accent color custom override if set, otherwise theme accent.
*/
public int getEffectiveAccentColor(Context context) {
String custom = getCustomAccentColorHex();
if (custom != null) {
try {
return Color.parseColor(custom);
} catch (Exception e) {
// Fall through to default
}
}
return getAccentColor(context);
}
// Wallpaper
public String getWallpaperId() {
return prefs.getString(KEY_WALLPAPER, WALLPAPER_DEFAULT);
}
public void setWallpaperId(String id) {
prefs.edit().putString(KEY_WALLPAPER, id).apply();
}
// Transparency
/**
* Returns panel transparency percentage (50-100). Default 80.
* 50 = very transparent, 100 = fully opaque.
*/
public int getTransparency() {
return prefs.getInt(KEY_TRANSPARENCY, 80);
}
public void setTransparency(int percent) {
prefs.edit().putInt(KEY_TRANSPARENCY, Math.max(50, Math.min(100, percent))).apply();
}
// Konami code
public boolean isKonamiUnlocked() {
return prefs.getBoolean(KEY_KONAMI_UNLOCKED, false);
}
public void setKonamiUnlocked(boolean unlocked) {
prefs.edit().putBoolean(KEY_KONAMI_UNLOCKED, unlocked).apply();
}
// Sound
public boolean isSoundEnabled() {
return prefs.getBoolean(KEY_SOUND_ENABLED, true);
}
public void setSoundEnabled(boolean enabled) {
prefs.edit().putBoolean(KEY_SOUND_ENABLED, enabled).apply();
}
// Color accessors
public int getPrimaryColor(Context context) {
if (isFoundation()) {
return context.getResources().getColor(R.color.foundation_primary, context.getTheme());
}
return context.getResources().getColor(R.color.corp_primary, context.getTheme());
}
public int getAccentColor(Context context) {
if (isFoundation()) {
return context.getResources().getColor(R.color.foundation_accent, context.getTheme());
}
return context.getResources().getColor(R.color.corp_accent, context.getTheme());
}
public int getBackgroundColor(Context context) {
if (isFoundation()) {
return context.getResources().getColor(R.color.foundation_background, context.getTheme());
}
return context.getResources().getColor(R.color.corp_background, context.getTheme());
}
public int getSurfaceColor(Context context) {
if (isFoundation()) {
return context.getResources().getColor(R.color.foundation_surface, context.getTheme());
}
return context.getResources().getColor(R.color.corp_surface, context.getTheme());
}
public int getTextColor(Context context) {
if (isFoundation()) {
return context.getResources().getColor(R.color.foundation_text, context.getTheme());
}
return context.getResources().getColor(R.color.corp_text, context.getTheme());
}
public int getSecondaryTextColor(Context context) {
if (isFoundation()) {
return context.getResources().getColor(R.color.foundation_text_secondary, context.getTheme());
}
return context.getResources().getColor(R.color.corp_text_secondary, context.getTheme());
}
public int getTaskbarColor(Context context) {
if (isFoundation()) {
return context.getResources().getColor(R.color.foundation_taskbar, context.getTheme());
}
return context.getResources().getColor(R.color.corp_taskbar, context.getTheme());
}
public int getBorderColor(Context context) {
if (isFoundation()) {
return context.getResources().getColor(R.color.foundation_border, context.getTheme());
}
return context.getResources().getColor(R.color.corp_border, context.getTheme());
}
public int getBootTextColor(Context context) {
if (isFoundation()) {
return context.getResources().getColor(R.color.boot_text_foundation, context.getTheme());
}
return context.getResources().getColor(R.color.boot_text_corp, context.getTheme());
}
// Drawable resource accessors
public int getGlassPanelDrawable() {
return isFoundation() ? R.drawable.bg_glass_panel_foundation : R.drawable.bg_glass_panel_corp;
}
public int getGlassPanelStrongDrawable() {
return isFoundation() ? R.drawable.bg_glass_panel_strong_foundation : R.drawable.bg_glass_panel_strong_corp;
}
public int getCardSurfaceDrawable() {
return isFoundation() ? R.drawable.bg_card_surface_foundation : R.drawable.bg_card_surface_corp;
}
public int getTaskbarDrawable() {
return isFoundation() ? R.drawable.bg_taskbar_foundation : R.drawable.bg_taskbar_corp;
}
public int getBootGlowDrawable() {
return isFoundation() ? R.drawable.bg_boot_glow_foundation : R.drawable.bg_boot_glow_corp;
}
public int getBootProgressDrawable() {
return isFoundation() ? R.drawable.bg_progress_foundation : R.drawable.bg_progress_corp;
}
public int getBootLogoDrawable() {
return isFoundation() ? R.drawable.bg_boot_logo_foundation : R.drawable.bg_boot_logo_corp;
}
public int getSearchInputDrawable() {
return isFoundation() ? R.drawable.bg_search_input_foundation : R.drawable.bg_search_input_corp;
}
// Wallpaper gradient (radial, built programmatically)
/**
* Creates a wallpaper drawable based on user selection or theme default.
* If a custom wallpaper is selected, creates that gradient.
* Otherwise falls back to the clearance-themed radial gradient.
*/
public GradientDrawable createWallpaperDrawable(Context context) {
String wallpaperId = getWallpaperId();
if (!WALLPAPER_DEFAULT.equals(wallpaperId)) {
return createNamedWallpaper(wallpaperId);
}
// Default: clearance-themed radial gradient
return createDefaultWallpaper(context);
}
private GradientDrawable createDefaultWallpaper(Context context) {
int[] colors;
if (isFoundation()) {
colors = new int[]{
context.getResources().getColor(R.color.foundation_wallpaper_start, context.getTheme()),
context.getResources().getColor(R.color.foundation_wallpaper_mid, context.getTheme()),
context.getResources().getColor(R.color.foundation_wallpaper_end, context.getTheme())
};
} else {
colors = new int[]{
context.getResources().getColor(R.color.corp_wallpaper_start, context.getTheme()),
context.getResources().getColor(R.color.corp_wallpaper_mid, context.getTheme()),
context.getResources().getColor(R.color.corp_wallpaper_end, context.getTheme())
};
}
GradientDrawable wallpaper = new GradientDrawable(
GradientDrawable.Orientation.TL_BR, colors);
wallpaper.setGradientType(GradientDrawable.RADIAL_GRADIENT);
wallpaper.setGradientRadius(1200f);
if (isFoundation()) {
wallpaper.setGradientCenter(0.3f, 0.2f);
} else {
wallpaper.setGradientCenter(0.7f, 0.8f);
}
return wallpaper;
}
private GradientDrawable createNamedWallpaper(String id) {
int[] colors;
GradientDrawable.Orientation orientation;
boolean radial = false;
switch (id) {
case WALLPAPER_CYBER_GRID:
colors = new int[]{
Color.parseColor("#0f172a"),
Color.parseColor("#1e1b4b"),
Color.parseColor("#0f172a")
};
orientation = GradientDrawable.Orientation.BL_TR;
break;
case WALLPAPER_MATRIX:
colors = new int[]{
Color.parseColor("#001100"),
Color.parseColor("#002200"),
Color.parseColor("#001100")
};
orientation = GradientDrawable.Orientation.TOP_BOTTOM;
break;
case WALLPAPER_NEON_SUNSET:
colors = new int[]{
Color.parseColor("#1a0533"),
Color.parseColor("#4a1942"),
Color.parseColor("#0f172a")
};
orientation = GradientDrawable.Orientation.TOP_BOTTOM;
break;
case WALLPAPER_DEEP_OCEAN:
colors = new int[]{
Color.parseColor("#0a1628"),
Color.parseColor("#0d3b66"),
Color.parseColor("#0a1628")
};
orientation = GradientDrawable.Orientation.TOP_BOTTOM;
break;
case WALLPAPER_VAPORWAVE:
colors = new int[]{
Color.parseColor("#ff71ce"),
Color.parseColor("#01cdfe"),
Color.parseColor("#05ffa1"),
Color.parseColor("#b967ff")
};
orientation = GradientDrawable.Orientation.TL_BR;
break;
case WALLPAPER_BLOOD_MOON:
colors = new int[]{
Color.parseColor("#1a0000"),
Color.parseColor("#4a0000"),
Color.parseColor("#1a0000")
};
orientation = GradientDrawable.Orientation.TOP_BOTTOM;
break;
case WALLPAPER_GALAXY:
colors = new int[]{
Color.parseColor("#1b2735"),
Color.parseColor("#090a0f")
};
orientation = GradientDrawable.Orientation.TL_BR;
radial = true;
break;
default:
// Unknown wallpaper, fall back to dark
colors = new int[]{Color.BLACK, Color.BLACK};
orientation = GradientDrawable.Orientation.TOP_BOTTOM;
break;
}
GradientDrawable gd = new GradientDrawable(orientation, colors);
if (radial) {
gd.setGradientType(GradientDrawable.RADIAL_GRADIENT);
gd.setGradientRadius(1200f);
gd.setGradientCenter(0.5f, 0.5f);
}
return gd;
}
/**
* Creates a wallpaper preview drawable for the settings picker.
*/
public static GradientDrawable createWallpaperPreview(String wallpaperId) {
switch (wallpaperId) {
case WALLPAPER_CYBER_GRID:
return new GradientDrawable(GradientDrawable.Orientation.BL_TR,
new int[]{Color.parseColor("#0f172a"), Color.parseColor("#1e1b4b"), Color.parseColor("#0f172a")});
case WALLPAPER_MATRIX:
return new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{Color.parseColor("#001100"), Color.parseColor("#002200"), Color.parseColor("#001100")});
case WALLPAPER_NEON_SUNSET:
return new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{Color.parseColor("#1a0533"), Color.parseColor("#4a1942"), Color.parseColor("#0f172a")});
case WALLPAPER_DEEP_OCEAN:
return new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{Color.parseColor("#0a1628"), Color.parseColor("#0d3b66"), Color.parseColor("#0a1628")});
case WALLPAPER_VAPORWAVE:
return new GradientDrawable(GradientDrawable.Orientation.TL_BR,
new int[]{Color.parseColor("#ff71ce"), Color.parseColor("#01cdfe"), Color.parseColor("#05ffa1"), Color.parseColor("#b967ff")});
case WALLPAPER_BLOOD_MOON:
return new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{Color.parseColor("#1a0000"), Color.parseColor("#4a0000"), Color.parseColor("#1a0000")});
case WALLPAPER_GALAXY:
GradientDrawable gd = new GradientDrawable(GradientDrawable.Orientation.TL_BR,
new int[]{Color.parseColor("#1b2735"), Color.parseColor("#090a0f")});
gd.setGradientType(GradientDrawable.RADIAL_GRADIENT);
gd.setGradientRadius(400f);
gd.setGradientCenter(0.5f, 0.5f);
return gd;
default:
return new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{Color.parseColor("#0A0202"), Color.parseColor("#0A0202")});
}
}
// Start button gradient
public GradientDrawable createStartButtonDrawable() {
int[] colors;
if (isFoundation()) {
colors = new int[]{ Color.parseColor("#DC2626"), Color.parseColor("#D4AF37") };
} else {
colors = new int[]{ Color.parseColor("#3B82F6"), Color.parseColor("#06B6D4") };
}
GradientDrawable gd = new GradientDrawable(
GradientDrawable.Orientation.LEFT_RIGHT, colors);
gd.setCornerRadius(dpToPx(10));
return gd;
}
// Clearance badge colors
public int getClearanceBadgeBackground() {
return isFoundation()
? Color.parseColor("#33DC2626")
: Color.parseColor("#333B82F6");
}
public int getClearanceBadgeTextColor() {
return isFoundation()
? Color.parseColor("#D4AF37")
: Color.parseColor("#C0C0C0");
}
public String getClearanceLabel() {
return isFoundation() ? "FOUNDATION CLEARANCE" : "CORP CLEARANCE";
}
// Clearance switching animation colors
public static int getSwitchOverlayBackground(String targetMode) {
return CLEARANCE_CORP.equals(targetMode)
? Color.parseColor("#0F172A")
: Color.parseColor("#1a0505");
}
public static int getSwitchSpinnerColor(String targetMode) {
return CLEARANCE_CORP.equals(targetMode)
? Color.parseColor("#3B82F6")
: Color.parseColor("#D4AF37");
}
public static String getSwitchLabel(String targetMode) {
return CLEARANCE_CORP.equals(targetMode)
? "ENTERING CORP"
: "ENTERING FOUNDATION";
}
// Font accessors
public Typeface getDisplayFont(Context context) {
if (cachedDisplayFont == null) {
try {
cachedDisplayFont = ResourcesCompat.getFont(context, R.font.electrolize);
} catch (Exception e) {
cachedDisplayFont = Typeface.SANS_SERIF;
}
}
return cachedDisplayFont;
}
public Typeface getMonoFont(Context context) {
if (cachedMonoFont == null) {
try {
cachedMonoFont = ResourcesCompat.getFont(context, R.font.source_code_pro);
} catch (Exception e) {
cachedMonoFont = Typeface.MONOSPACE;
}
}
return cachedMonoFont;
}
// Utility
public static float dpToPx(int dp) {
return dp * android.content.res.Resources.getSystem().getDisplayMetrics().density;
}
}

View file

@ -0,0 +1,482 @@
package com.aethex.os;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.Random;
public class WeatherActivity extends AppCompatActivity {
private ThemeManager themeManager;
private static final String[] CONDITIONS = {
"Clear Sky", "Partly Cloudy", "Overcast", "Light Rain",
"Scattered Clouds", "Foggy", "Windy", "Thunderstorms"
};
// Current weather state (stored for F/C toggling and refresh)
private int currentTempF;
private int currentHiF;
private int currentLoF;
private String currentCondition;
private boolean isCelsius = false;
// Hourly temps stored for F/C toggling
private int[] hourlyTempsF = new int[12];
private String[] hourlyLabels = new String[12];
private String[] hourlyEmojis = new String[12];
// Forecast data stored for F/C toggling and tap info
private int[] forecastHiF = new int[5];
private int[] forecastLoF = new int[5];
private String[] forecastConditions = new String[5];
private String[] forecastDayNames = new String[5];
private String[] forecastEmojis = new String[5];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather);
hideSystemUI();
themeManager = new ThemeManager(this);
// Play open sound
SoundManager.getInstance().play(SoundManager.Sound.OPEN);
// Track achievement stat
AchievementsActivity.incrementStat(this, "weather_checks");
// Back button
findViewById(R.id.weather_back).setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLOSE);
finish();
overridePendingTransition(R.anim.scale_in, R.anim.slide_down_out);
});
// Refresh button
findViewById(R.id.weather_refresh).setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
refreshAllData();
AeThexToast.show(WeatherActivity.this, "Weather updated", AeThexToast.Type.INFO);
});
// Temperature tap to toggle F/C
findViewById(R.id.weather_temp).setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
isCelsius = !isCelsius;
updateTemperatureDisplay();
updateHourlyDisplay();
updateForecastDisplay();
String unit = isCelsius ? "Celsius" : "Fahrenheit";
AeThexToast.show(WeatherActivity.this, "Switched to " + unit, AeThexToast.Type.INFO);
});
generateWeatherData();
buildHourlyForecast();
buildForecast();
// Entrance animation
View root = findViewById(R.id.weather_root);
root.setAlpha(0f);
root.animate().alpha(1f).setDuration(300).start();
}
/**
* Refresh all weather data with new random values.
*/
private void refreshAllData() {
generateWeatherData();
// Rebuild hourly forecast
LinearLayout hourlyContainer = findViewById(R.id.hourly_container);
hourlyContainer.removeAllViews();
buildHourlyForecast();
// Rebuild 5-day forecast
LinearLayout forecastContainer = findViewById(R.id.forecast_container);
forecastContainer.removeAllViews();
buildForecast();
}
/**
* Returns the emoji for a given weather condition string.
*/
private String getConditionEmoji(String condition) {
switch (condition) {
case "Clear Sky": return "\u2600\uFE0F"; // sun
case "Partly Cloudy": return "\u26C5"; // sun behind cloud
case "Overcast": return "\u2601\uFE0F"; // cloud
case "Light Rain": return "\uD83C\uDF27\uFE0F"; // cloud with rain
case "Scattered Clouds": return "\uD83C\uDF24\uFE0F"; // sun behind small cloud
case "Foggy": return "\uD83C\uDF2B\uFE0F"; // fog
case "Windy": return "\uD83C\uDF2C\uFE0F"; // wind face
case "Thunderstorms": return "\u26C8\uFE0F"; // cloud with lightning and rain
default: return "\uD83C\uDF21\uFE0F"; // thermometer
}
}
/**
* Convert Fahrenheit to Celsius.
*/
private int toCelsius(int fahrenheit) {
return Math.round((fahrenheit - 32) * 5f / 9f);
}
/**
* Format temperature with current unit suffix.
*/
private String formatTemp(int fahrenheit) {
if (isCelsius) {
return toCelsius(fahrenheit) + "\u00B0C";
}
return fahrenheit + "\u00B0F";
}
/**
* Format temperature for the big display (no unit letter, just degree symbol).
*/
private String formatTempBig(int fahrenheit) {
if (isCelsius) {
return toCelsius(fahrenheit) + "\u00B0";
}
return fahrenheit + "\u00B0";
}
private void generateWeatherData() {
Random rand = new Random();
currentTempF = 60 + rand.nextInt(30); // 60-89
currentHiF = currentTempF + rand.nextInt(8) + 2;
currentLoF = currentTempF - rand.nextInt(8) - 2;
currentCondition = CONDITIONS[rand.nextInt(CONDITIONS.length)];
int wind = 3 + rand.nextInt(20);
int humidity = 30 + rand.nextInt(50);
float pressure = 29.5f + rand.nextFloat() * 1.5f;
int uv = rand.nextInt(11);
String uvLabel;
if (uv <= 2) uvLabel = uv + " Low";
else if (uv <= 5) uvLabel = uv + " Moderate";
else if (uv <= 7) uvLabel = uv + " High";
else uvLabel = uv + " Very High";
// Generate sunrise/sunset times
int sunriseHour = 5 + rand.nextInt(2); // 5 or 6
int sunriseMin = rand.nextInt(60);
int sunsetHour = 17 + rand.nextInt(3); // 5-7 PM (17-19)
int sunsetMin = rand.nextInt(60);
String sunrise = String.format(Locale.US, "%d:%02d AM", sunriseHour, sunriseMin);
String sunset = String.format(Locale.US, "%d:%02d PM", sunsetHour - 12, sunsetMin);
// Set location with current date
SimpleDateFormat sdf = new SimpleDateFormat("EEEE, MMM d", Locale.getDefault());
TextView location = findViewById(R.id.weather_location);
location.setText("AeThex HQ \u2022 " + sdf.format(Calendar.getInstance().getTime()));
location.setTypeface(themeManager.getMonoFont(this));
// Weather emoji
String emoji = getConditionEmoji(currentCondition);
((TextView) findViewById(R.id.weather_emoji)).setText(emoji);
// Big temperature
TextView tempView = findViewById(R.id.weather_temp);
tempView.setText(formatTempBig(currentTempF));
tempView.setTypeface(themeManager.getDisplayFont(this));
// Condition with emoji
TextView condView = findViewById(R.id.weather_condition);
condView.setText(emoji + " " + currentCondition);
condView.setTypeface(themeManager.getDisplayFont(this));
condView.setTextColor(themeManager.getPrimaryColor(this));
// Hi / Lo
TextView hiloView = findViewById(R.id.weather_hilo);
hiloView.setTypeface(themeManager.getMonoFont(this));
updateHiLoText(hiloView);
// Detail stats
TextView windView = findViewById(R.id.weather_wind);
windView.setText(wind + " mph");
windView.setTypeface(themeManager.getDisplayFont(this));
TextView humidityView = findViewById(R.id.weather_humidity);
humidityView.setText(humidity + "%");
humidityView.setTypeface(themeManager.getDisplayFont(this));
TextView pressureView = findViewById(R.id.weather_pressure);
pressureView.setText(String.format(Locale.US, "%.1f in", pressure));
pressureView.setTypeface(themeManager.getDisplayFont(this));
TextView uvView = findViewById(R.id.weather_uv);
uvView.setText(uvLabel);
uvView.setTypeface(themeManager.getDisplayFont(this));
// Sunrise / Sunset
TextView sunriseView = findViewById(R.id.weather_sunrise);
sunriseView.setText("\u2600\uFE0F " + sunrise);
sunriseView.setTypeface(themeManager.getDisplayFont(this));
TextView sunsetView = findViewById(R.id.weather_sunset);
sunsetView.setText("\uD83C\uDF05 " + sunset);
sunsetView.setTypeface(themeManager.getDisplayFont(this));
}
private void updateHiLoText(TextView hiloView) {
if (isCelsius) {
hiloView.setText("H: " + toCelsius(currentHiF) + "\u00B0C L: " + toCelsius(currentLoF) + "\u00B0C");
} else {
hiloView.setText("H: " + currentHiF + "\u00B0F L: " + currentLoF + "\u00B0F");
}
}
/**
* Update temperature display when toggling F/C (main temp + hi/lo).
*/
private void updateTemperatureDisplay() {
TextView tempView = findViewById(R.id.weather_temp);
tempView.setText(formatTempBig(currentTempF));
TextView hiloView = findViewById(R.id.weather_hilo);
updateHiLoText(hiloView);
}
/**
* Build the hourly forecast (next 12 hours).
*/
private void buildHourlyForecast() {
LinearLayout container = findViewById(R.id.hourly_container);
Random rand = new Random();
Calendar cal = Calendar.getInstance();
for (int i = 0; i < 12; i++) {
cal.add(Calendar.HOUR_OF_DAY, 1);
int hour = cal.get(Calendar.HOUR_OF_DAY);
String amPm = hour >= 12 ? "PM" : "AM";
int displayHour = hour % 12;
if (displayHour == 0) displayHour = 12;
hourlyLabels[i] = displayHour + amPm;
// Hourly temp varies around current temp
hourlyTempsF[i] = currentTempF + rand.nextInt(11) - 5; // +/- 5 degrees
String hourCondition = CONDITIONS[rand.nextInt(CONDITIONS.length)];
hourlyEmojis[i] = getConditionEmoji(hourCondition);
container.addView(createHourlyItem(i));
// Spacer between items
if (i < 11) {
View spacer = new View(this);
spacer.setMinimumWidth(dp(2));
container.addView(spacer);
}
}
}
/**
* Create a single hourly forecast item view.
*/
private LinearLayout createHourlyItem(int index) {
LinearLayout item = new LinearLayout(this);
item.setOrientation(LinearLayout.VERTICAL);
item.setGravity(Gravity.CENTER);
item.setPadding(dp(12), dp(8), dp(12), dp(8));
// Hour label
TextView hourText = new TextView(this);
hourText.setText(hourlyLabels[index]);
hourText.setTextSize(11);
hourText.setTextColor(0x99FFFFFF);
hourText.setTypeface(themeManager.getMonoFont(this));
hourText.setGravity(Gravity.CENTER);
item.addView(hourText);
// Emoji
TextView emojiText = new TextView(this);
emojiText.setText(hourlyEmojis[index]);
emojiText.setTextSize(20);
emojiText.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams emojiParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
emojiParams.topMargin = dp(4);
emojiParams.bottomMargin = dp(4);
emojiText.setLayoutParams(emojiParams);
item.addView(emojiText);
// Temperature
TextView tempText = new TextView(this);
tempText.setTag("hourly_temp_" + index);
tempText.setText(formatTemp(hourlyTempsF[index]));
tempText.setTextSize(13);
tempText.setTextColor(0xFFFFFFFF);
tempText.setTypeface(themeManager.getDisplayFont(this));
tempText.setGravity(Gravity.CENTER);
item.addView(tempText);
return item;
}
/**
* Update all hourly forecast temp labels when toggling F/C.
*/
private void updateHourlyDisplay() {
LinearLayout container = findViewById(R.id.hourly_container);
for (int i = 0; i < 12; i++) {
TextView tv = container.findViewWithTag("hourly_temp_" + i);
if (tv != null) {
tv.setText(formatTemp(hourlyTempsF[i]));
}
}
}
private void buildForecast() {
LinearLayout container = findViewById(R.id.forecast_container);
Random rand = new Random();
Calendar cal = Calendar.getInstance();
for (int i = 0; i < 5; i++) {
cal.add(Calendar.DAY_OF_YEAR, 1);
forecastDayNames[i] = new SimpleDateFormat("EEE", Locale.getDefault()).format(cal.getTime()).toUpperCase();
forecastHiF[i] = 65 + rand.nextInt(25);
forecastLoF[i] = forecastHiF[i] - 5 - rand.nextInt(10);
forecastConditions[i] = CONDITIONS[rand.nextInt(CONDITIONS.length)];
forecastEmojis[i] = getConditionEmoji(forecastConditions[i]);
container.addView(createForecastRow(i));
// Add divider between rows (not after last)
if (i < 4) {
View divider = new View(this);
divider.setBackgroundColor(0x1AFFFFFF);
LinearLayout.LayoutParams divParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, 1);
divParams.setMarginStart(dp(16));
divParams.setMarginEnd(dp(16));
divider.setLayoutParams(divParams);
container.addView(divider);
}
}
}
/**
* Create a single forecast row.
*/
private LinearLayout createForecastRow(int index) {
LinearLayout row = new LinearLayout(this);
row.setOrientation(LinearLayout.HORIZONTAL);
row.setGravity(Gravity.CENTER_VERTICAL);
row.setPadding(dp(16), dp(14), dp(16), dp(14));
row.setTag("forecast_row_" + index);
// Day name
TextView dayText = new TextView(this);
dayText.setText(forecastDayNames[index]);
dayText.setTextSize(13);
dayText.setTextColor(0xCCFFFFFF);
dayText.setTypeface(themeManager.getMonoFont(this));
dayText.setWidth(dp(50));
row.addView(dayText);
// Emoji
TextView emojiText = new TextView(this);
emojiText.setText(forecastEmojis[index]);
emojiText.setTextSize(16);
emojiText.setWidth(dp(30));
row.addView(emojiText);
// Condition
TextView condText = new TextView(this);
condText.setTag("forecast_cond_" + index);
condText.setText(forecastConditions[index]);
condText.setTextSize(12);
condText.setTextColor(0x99FFFFFF);
condText.setTypeface(Typeface.create("sans-serif-light", Typeface.NORMAL));
LinearLayout.LayoutParams condParams = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f);
condText.setLayoutParams(condParams);
row.addView(condText);
// Temps
TextView tempText = new TextView(this);
tempText.setTag("forecast_temp_" + index);
tempText.setText(formatForecastTemp(index));
tempText.setTextSize(14);
tempText.setTextColor(0xFFFFFFFF);
tempText.setTypeface(themeManager.getDisplayFont(this));
row.addView(tempText);
// Tap handler: show full day info toast
final int idx = index;
row.setOnClickListener(v -> {
SoundManager.getInstance().play(SoundManager.Sound.CLICK);
String info = forecastDayNames[idx] + ": " + forecastEmojis[idx] + " " + forecastConditions[idx]
+ " | High " + formatTemp(forecastHiF[idx])
+ " / Low " + formatTemp(forecastLoF[idx]);
AeThexToast.show(WeatherActivity.this, info, AeThexToast.Type.INFO);
});
// Make it look tappable
row.setClickable(true);
row.setFocusable(true);
return row;
}
/**
* Format forecast temp string for a given index.
*/
private String formatForecastTemp(int index) {
if (isCelsius) {
return toCelsius(forecastLoF[index]) + "\u00B0 / " + toCelsius(forecastHiF[index]) + "\u00B0C";
}
return forecastLoF[index] + "\u00B0 / " + forecastHiF[index] + "\u00B0F";
}
/**
* Update all forecast temp labels when toggling F/C.
*/
private void updateForecastDisplay() {
LinearLayout container = findViewById(R.id.forecast_container);
for (int i = 0; i < 5; i++) {
TextView tv = container.findViewWithTag("forecast_temp_" + i);
if (tv != null) {
tv.setText(formatForecastTemp(i));
}
}
}
private int dp(int value) {
return (int) (value * 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);
}
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="200"
android:interpolator="@android:interpolator/decelerate_quad" />

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="200"
android:interpolator="@android:interpolator/accelerate_quad" />

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<scale
android:fromXScale="0.92"
android:toXScale="1.0"
android:fromYScale="0.92"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="250" />
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="200" />
</set>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:fromXScale="1.0"
android:toXScale="0.92"
android:fromYScale="1.0"
android:toYScale="0.92"
android:pivotX="50%"
android:pivotY="50%"
android:duration="200" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="180" />
</set>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<translate
android:fromYDelta="0%"
android:toYDelta="5%"
android:duration="200" />
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="180" />
</set>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
android:fromYDelta="8%"
android:toYDelta="0%"
android:duration="250" />
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="200" />
</set>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp" />
<solid android:color="#1AFFFFFF" />
<stroke
android:width="1dp"
android:color="#0DFFFFFF" />
</shape>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#33FFFFFF" />
<corners android:radius="10dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#1AFFFFFF" />
<corners android:radius="10dp" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#EF4444" />
</shape>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<gradient
android:type="radial"
android:gradientRadius="120dp"
android:centerColor="#4006B6D4"
android:endColor="#00000000" />
</shape>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<gradient
android:type="radial"
android:gradientRadius="200dp"
android:centerX="0.5"
android:centerY="0.5"
android:startColor="#3306B6D4"
android:endColor="#00000000" />
</shape>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<gradient
android:type="radial"
android:gradientRadius="200dp"
android:centerX="0.5"
android:centerY="0.5"
android:startColor="#33DC2626"
android:endColor="#00000000" />
</shape>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#06B6D4"
android:endColor="#A855F7"
android:angle="135" />
<corners android:radius="24dp" />
</shape>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#2563EB"
android:endColor="#06B6D4"
android:angle="135" />
<corners android:radius="24dp" />
</shape>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#DC2626"
android:endColor="#F59E0B"
android:angle="135" />
<corners android:radius="24dp" />
</shape>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<solid android:color="#00000000" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape android:shape="rectangle">
<solid android:color="#06B6D4" />
</shape>
</clip>
</item>
</layer-list>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#33FFFFFF" />
<corners android:radius="14dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#1AFFFFFF" />
<corners android:radius="14dp" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<gradient
android:startColor="#0891B2"
android:endColor="#7C3AED"
android:angle="135" />
<corners android:radius="14dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<gradient
android:startColor="#06B6D4"
android:endColor="#A855F7"
android:angle="135" />
<corners android:radius="14dp" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#33FF7043" />
<corners android:radius="14dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#26FFFFFF" />
<corners android:radius="14dp" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#4006B6D4" />
<corners android:radius="14dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#2606B6D4" />
<corners android:radius="14dp" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#1AFFFFFF" />
<corners android:radius="16dp" />
<stroke android:width="1dp" android:color="#0DFFFFFF" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#1A3B82F6" />
<corners android:radius="16dp" />
<stroke android:width="1dp" android:color="#2694A3B8" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#1ADC2626" />
<corners android:radius="16dp" />
<stroke android:width="1dp" android:color="#33CA8A04" />
</shape>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#06B6D4" />
<size android:width="36dp" android:height="36dp" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#06B6D4" />
<stroke android:width="2dp" android:color="#FFFFFF" />
<size android:width="36dp" android:height="36dp" />
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#80FFFFFF" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#CC0F172A" />
<corners android:radius="16dp" />
<stroke android:width="1dp" android:color="#1AFFFFFF" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#CC0A0A1A" />
<corners android:radius="16dp" />
<stroke android:width="1dp" android:color="#4D94A3B8" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#CC1A0A0A" />
<corners android:radius="16dp" />
<stroke android:width="1dp" android:color="#66CA8A04" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E60F172A" />
<corners android:radius="12dp" />
<stroke android:width="1dp" android:color="#1AFFFFFF" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E60A0A1A" />
<corners android:radius="12dp" />
<stroke android:width="1dp" android:color="#4D94A3B8" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E61A0A0A" />
<corners android:radius="12dp" />
<stroke android:width="1dp" android:color="#66CA8A04" />
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#00000000" />
</shape>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#0DFFFFFF" />
<corners android:radius="12dp" />
</shape>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<solid android:color="#1A3B82F6" />
<corners android:radius="4dp" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape android:shape="rectangle">
<gradient
android:startColor="#3B82F6"
android:endColor="#06B6D4"
android:angle="0" />
<corners android:radius="4dp" />
</shape>
</clip>
</item>
</layer-list>

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<solid android:color="#1ADC2626" />
<corners android:radius="4dp" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape android:shape="rectangle">
<gradient
android:startColor="#DC2626"
android:endColor="#D4AF37"
android:angle="0" />
<corners android:radius="4dp" />
</shape>
</clip>
</item>
</layer-list>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="#1A18181B" />
<corners android:radius="8dp" />
<stroke android:width="1dp" android:color="#1AFFFFFF" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<gradient
android:startColor="#06B6D4"
android:endColor="#A855F7"
android:angle="0" />
<corners android:radius="8dp" />
</shape>
</clip>
</item>
</layer-list>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- CRT scanline overlay - applied programmatically via custom View for best effect -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#08000000" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#0DFFFFFF" />
<corners android:radius="10dp" />
<stroke android:width="1dp" android:color="#1AFFFFFF" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#0DFFFFFF" />
<corners android:radius="10dp" />
<stroke android:width="1dp" android:color="#333B82F6" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#0DFFFFFF" />
<corners android:radius="10dp" />
<stroke android:width="1dp" android:color="#33D4AF37" />
</shape>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<shape android:shape="rectangle">
<solid android:color="#332563EB" />
<corners android:radius="16dp" />
<stroke android:width="2dp" android:color="#802563EB" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#1AFFFFFF" />
<corners android:radius="16dp" />
<stroke android:width="1dp" android:color="#0DFFFFFF" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<shape android:shape="rectangle">
<solid android:color="#33DC2626" />
<corners android:radius="16dp" />
<stroke android:width="2dp" android:color="#80DC2626" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#1AFFFFFF" />
<corners android:radius="16dp" />
<stroke android:width="1dp" android:color="#0DFFFFFF" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#33FFFFFF" />
<corners android:radius="8dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#0DFFFFFF" />
<corners android:radius="8dp" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#06B6D4"
android:endColor="#A855F7"
android:angle="135" />
<corners android:radius="12dp" />
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E60F172A" />
</shape>

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#3306B6D4" />
<corners android:radius="10dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#00000000" />
<corners android:radius="10dp" />
</shape>
</item>
</selector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E60F172A" />
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E61A0505" />
</shape>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#F5030712"
android:endColor="#F5030712"
android:angle="270" />
<corners android:radius="0dp" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#1A00FF00" />
<corners android:topLeftRadius="0dp" android:topRightRadius="0dp" />
<stroke android:width="0dp" android:color="#00000000" />
</shape>

View file

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

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E60F172A" />
<corners android:radius="8dp" />
<stroke android:width="1dp" android:color="#1AFFFFFF" />
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#00000000" />
<corners android:radius="12dp" />
<stroke android:width="1dp" android:color="#33FFFFFF" />
</shape>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#FA0F172A"
android:endColor="#F50F172A"
android:angle="270" />
<corners android:radius="12dp" />
<stroke android:width="1dp" android:color="#1AFFFFFF" />
</shape>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<solid android:color="@color/boot_progress_track" />
<corners android:radius="3dp" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/boot_progress" />
<corners android:radius="3dp" />
</shape>
</clip>
</item>
</layer-list>

Some files were not shown because too many files have changed in this diff Show more