apps ui
This commit is contained in:
475
ROADMAP.md
Normal file
475
ROADMAP.md
Normal file
@@ -0,0 +1,475 @@
|
||||
# Mosis Virtual Smartphone Platform - Roadmap
|
||||
|
||||
## Vision
|
||||
|
||||
Mosis is a **virtual smartphone OS** for VR games and applications. It provides a phone-like device that users interact with inside VR environments, running virtual apps with real smartphone functionality.
|
||||
|
||||
### Key Use Cases
|
||||
|
||||
1. **In-Game Phone**: Players use a virtual phone in VR games for communication, utilities, entertainment
|
||||
2. **Cross-Game Communication**: Phone identity persists across different games (unified contacts, messages)
|
||||
3. **Real-World Bridge**: Connect to real smartphones via WebRTC for mixed-reality communication
|
||||
4. **Virtual Hardware**: Game engine provides camera textures, audio, and other "hardware" to the phone OS
|
||||
5. **Store Ecosystem**: Downloadable apps from a store, self-contained packages with isolation
|
||||
|
||||
### Integration Points
|
||||
|
||||
| Platform | Location | Status |
|
||||
|----------|----------|--------|
|
||||
| Unity Plugin | `D:\Dev\Mosis\Mosis Unity` | Binder client, raycast, viewport |
|
||||
| Unreal Plugin | `D:\Dev\Mosis\Mosis Unreal` | WIP |
|
||||
| Desktop Designer | `MosisService/designer/` | Complete |
|
||||
| Designer Tests | `MosisService/designer-test/` | Mostly complete |
|
||||
|
||||
---
|
||||
|
||||
## Milestones Overview
|
||||
|
||||
| # | Milestone | Status | Description |
|
||||
|---|-----------|--------|-------------|
|
||||
| 1 | Cross-Platform Kernel | ✅ Complete | Desktop designer with shared kernel code |
|
||||
| 2 | Testing Framework | 🔶 80% | Automated UI testing and inspection |
|
||||
| 3 | Virtual Hardware | ❌ Not started | Camera, mic, speaker, filesystem APIs |
|
||||
| 4 | App Sandboxing | ❌ Not started | Lua/WASM runtime, package format |
|
||||
| 5 | WebRTC Bridge | ❌ Not started | Phone-to-phone communication |
|
||||
| 6 | System Apps | 🔶 75% | Core phone apps |
|
||||
| 7 | Game Integration | ❌ Not started | Unity/Unreal plugin polish |
|
||||
|
||||
---
|
||||
|
||||
## Milestone 1: Cross-Platform Kernel ✅ COMPLETE
|
||||
|
||||
**Goal**: Desktop designer with shared kernel code for rapid UI iteration.
|
||||
|
||||
### Completed Tasks
|
||||
|
||||
- [x] Platform abstraction layer (`src/main/kernel/include/`)
|
||||
- `platform.h` - IPlatform, IGraphicsContext, IRenderTarget
|
||||
- `service_interface.h` - IKernel, IServiceListener
|
||||
- `file_interface.h` - IFileInterface extending Rml::FileInterface
|
||||
- [x] Desktop designer project (`designer/`)
|
||||
- GLFW + OpenGL 3.3 context
|
||||
- Hot-reload on RML/RCSS/Lua changes
|
||||
- Mouse input maps to touch events
|
||||
- [x] UI designs imported from MosisDesigner
|
||||
- Theme and component stylesheets
|
||||
- Navigation system (Lua)
|
||||
- Fonts and icons
|
||||
- [x] Android build unchanged and working
|
||||
|
||||
---
|
||||
|
||||
## Milestone 2: Testing Automation 🔶 80% COMPLETE
|
||||
|
||||
**Goal**: Automated UI testing for rapid iteration and AI agent verification.
|
||||
|
||||
### Completed Tasks
|
||||
|
||||
- [x] UI hierarchy dumping to JSON
|
||||
- [x] PNG screenshot capture (F12 key)
|
||||
- [x] Agent-compatible JSON test results
|
||||
- [x] `designer-test` executable
|
||||
- WindowController (Windows SendInput API)
|
||||
- HierarchyReader (element lookup by ID/class)
|
||||
- LogParser (navigation event verification)
|
||||
- [x] All 5 navigation tests passing
|
||||
- [x] Android event injection via ADB broadcast
|
||||
|
||||
### Remaining Tasks
|
||||
|
||||
- [ ] **Action Recording**
|
||||
- Capture tap, swipe, long_press to JSON
|
||||
- Record timestamps for replay timing
|
||||
- Save to `test-recordings/*.json`
|
||||
|
||||
- [ ] **Action Playback**
|
||||
- Load recorded JSON
|
||||
- Replay with timing
|
||||
- Capture results at each step
|
||||
|
||||
- [ ] **Screenshot Diff**
|
||||
- Compare two PNG screenshots
|
||||
- Highlight pixel differences
|
||||
- Report diff percentage
|
||||
|
||||
### Test Recording Format
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Navigate to contacts and back",
|
||||
"recorded_at": "2024-01-16T12:00:00Z",
|
||||
"resolution": {"width": 677, "height": 1202},
|
||||
"actions": [
|
||||
{"type": "tap", "x": 413, "y": 1174, "timestamp": 0},
|
||||
{"type": "wait", "duration": 1000, "timestamp": 100},
|
||||
{"type": "tap", "x": 40, "y": 28, "timestamp": 1100},
|
||||
{"type": "wait", "duration": 500, "timestamp": 1200}
|
||||
],
|
||||
"assertions": [
|
||||
{"after_action": 0, "type": "screen_is", "expected": "contacts"},
|
||||
{"after_action": 2, "type": "screen_is", "expected": "home"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Milestone 3: Virtual Hardware ❌ NOT STARTED
|
||||
|
||||
**Goal**: Hardware-like APIs backed by game engine or real devices.
|
||||
|
||||
### 3.1 Camera Interface
|
||||
|
||||
```cpp
|
||||
class ICamera {
|
||||
virtual void RequestFrame(FrameCallback callback) = 0;
|
||||
virtual void SetResolution(int width, int height) = 0;
|
||||
virtual bool IsAvailable() const = 0;
|
||||
};
|
||||
```
|
||||
|
||||
**Implementations**:
|
||||
- **Game Mode**: Receives texture from Unity/Unreal
|
||||
- **Desktop Mode**: System webcam (optional)
|
||||
- **Android Test Mode**: SharedTexture from MainActivity
|
||||
- **Mock Mode**: Test patterns
|
||||
|
||||
### 3.2 Microphone Interface
|
||||
|
||||
```cpp
|
||||
class IMicrophone {
|
||||
virtual void StartCapture(AudioCallback callback) = 0;
|
||||
virtual void StopCapture() = 0;
|
||||
virtual bool IsAvailable() const = 0;
|
||||
};
|
||||
```
|
||||
|
||||
### 3.3 Speaker Interface
|
||||
|
||||
```cpp
|
||||
class ISpeaker {
|
||||
virtual void PlayAudio(AudioBuffer buffer) = 0;
|
||||
virtual void SetVolume(float volume) = 0;
|
||||
};
|
||||
```
|
||||
|
||||
### 3.4 Filesystem Interface
|
||||
|
||||
```cpp
|
||||
class IFileSystem {
|
||||
virtual FileHandle Open(const std::string& path, Mode mode) = 0;
|
||||
virtual std::vector<std::string> List(const std::string& dir) = 0;
|
||||
virtual bool CreateDirectory(const std::string& path) = 0;
|
||||
virtual bool Delete(const std::string& path) = 0;
|
||||
};
|
||||
```
|
||||
|
||||
### 3.5 Network Interface
|
||||
|
||||
```cpp
|
||||
class INetwork {
|
||||
virtual HttpResponse Fetch(const HttpRequest& request) = 0;
|
||||
virtual WebSocket Connect(const std::string& url) = 0;
|
||||
virtual bool IsOnline() const = 0;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Milestone 4: App Sandboxing ❌ NOT STARTED
|
||||
|
||||
**Goal**: Secure app runtime with defined package format.
|
||||
|
||||
### 4.1 Runtime Decision
|
||||
|
||||
| Aspect | Lua | WASM |
|
||||
|--------|-----|------|
|
||||
| Isolation | Weak | Strong |
|
||||
| Performance | Good for UI | Near-native |
|
||||
| RmlUi Integration | Native | Needs bridge |
|
||||
| Ecosystem | Small | Large |
|
||||
|
||||
**Recommendation**: Hybrid approach
|
||||
- Lua for UI scripting (RmlUi integration)
|
||||
- WASM for app logic needing isolation (future)
|
||||
|
||||
### 4.2 Package Format (.mpkg)
|
||||
|
||||
```
|
||||
myapp.mpkg/
|
||||
├── manifest.json # Metadata, permissions, entry
|
||||
├── ui/
|
||||
│ ├── main.rml
|
||||
│ ├── styles.rcss
|
||||
│ └── scripts/
|
||||
├── assets/
|
||||
│ ├── icons/
|
||||
│ └── images/
|
||||
└── data/
|
||||
```
|
||||
|
||||
### 4.3 Manifest Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "com.example.myapp",
|
||||
"name": "My App",
|
||||
"version": "1.0.0",
|
||||
"permissions": ["camera", "network", "storage"],
|
||||
"entry": "ui/main.rml",
|
||||
"icon": "assets/icons/app.png"
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 App Lifecycle
|
||||
|
||||
```
|
||||
INSTALLED → LAUNCHING → RUNNING → PAUSED → STOPPED → UNINSTALLED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Milestone 5: WebRTC Bridge ❌ NOT STARTED
|
||||
|
||||
**Goal**: Cross-device communication via WebRTC.
|
||||
|
||||
### 5.1 Dependencies
|
||||
|
||||
- libdatachannel (add to vcpkg)
|
||||
|
||||
### 5.2 Communication Protocol
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "call" | "message" | "file" | "screen_share",
|
||||
"from": "phone_id",
|
||||
"to": "phone_id",
|
||||
"payload": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 Use Cases
|
||||
|
||||
- Voice/video calls between virtual phones
|
||||
- Text messaging across games
|
||||
- File sharing
|
||||
- Screen sharing
|
||||
|
||||
### 5.4 Components
|
||||
|
||||
- **WebRTCBridge** class for data channels
|
||||
- **Signaling Server** (WebSocket-based)
|
||||
- **Real Smartphone Bridge** (companion app)
|
||||
|
||||
---
|
||||
|
||||
## Milestone 6: System Apps 🔶 75% COMPLETE
|
||||
|
||||
### Completed Apps
|
||||
|
||||
| App | Features | Status |
|
||||
|-----|----------|--------|
|
||||
| Home | App grid, dock, navigation | ✅ Complete |
|
||||
| Dialer | Keypad, call UI (mock) | ✅ Complete |
|
||||
| Messages | Conversation list, chat view | ✅ Complete |
|
||||
| Contacts | Contact list, search, detail | ✅ Complete |
|
||||
| Settings | Display, sound, about | ✅ Complete |
|
||||
| Browser | URL bar, placeholder | ✅ Complete |
|
||||
|
||||
### Remaining Apps
|
||||
|
||||
| App | Features | Status |
|
||||
|-----|----------|--------|
|
||||
| Store | Browse apps, install packages | ❌ UI only |
|
||||
| Camera | Viewfinder, capture, gallery | ❌ UI only |
|
||||
| Music/Audio | Playback, playlists | ❌ Outline only |
|
||||
|
||||
### App Data Persistence (Future)
|
||||
|
||||
- Contacts: JSON or SQLite storage
|
||||
- Messages: Conversation history
|
||||
- Settings: Preferences file
|
||||
- Photos: Virtual filesystem
|
||||
|
||||
---
|
||||
|
||||
## Milestone 7: Game Integration ❌ NOT STARTED
|
||||
|
||||
**Goal**: Production-ready game engine plugins.
|
||||
|
||||
### 7.1 Unity Package
|
||||
|
||||
- [ ] Improved raycast interaction
|
||||
- [ ] Hardware button simulation (volume, power)
|
||||
- [ ] Virtual camera provider (RenderTexture → phone)
|
||||
- [ ] Virtual microphone provider
|
||||
- [ ] Virtual speaker provider
|
||||
|
||||
### 7.2 Unreal Plugin
|
||||
|
||||
- [ ] Full Binder client implementation
|
||||
- [ ] Blueprint integration
|
||||
- [ ] Same features as Unity
|
||||
|
||||
### 7.3 Documentation
|
||||
|
||||
- [ ] How to embed phone in game scene
|
||||
- [ ] How to provide virtual hardware
|
||||
- [ ] How to handle phone events
|
||||
|
||||
---
|
||||
|
||||
## Extended Features (Future)
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| Notifications | System notification center, per-app channels |
|
||||
| Widgets | Home screen widgets, live data |
|
||||
| Gestures | Swipe up for home, swipe down for notifications |
|
||||
| Themes | Dark/light mode, custom colors |
|
||||
| Accessibility | Font size, high contrast, TTS |
|
||||
| Multi-Window | Split-screen, picture-in-picture |
|
||||
| Clipboard | Copy/paste between apps and game |
|
||||
| Voice Assistant | Wake word, voice commands |
|
||||
| Virtual Location | GPS from game, map integration |
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ VR Game (Unity/Unreal) │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
|
||||
│ │ Phone Render │ │ Touch/Button │ │ Virtual Hardware Provider │ │
|
||||
│ │ Viewport │ │ Input │ │ (Camera, Mic, Speakers) │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └────────────┬─────────────┘ │
|
||||
└─────────┼─────────────────┼────────────────────────┼────────────────┘
|
||||
│ Binder IPC │ Binder IPC │ Binder IPC
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Mosis Service │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Kernel │ │
|
||||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
|
||||
│ │ │ RmlUi │ │ App Runtime │ │ Virtual Hardware │ │ │
|
||||
│ │ │ Renderer │ │ (Lua) │ │ Abstraction │ │ │
|
||||
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
|
||||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
|
||||
│ │ │ App │ │ IPC │ │ libdatachannel │ │ │
|
||||
│ │ │ Manager │ │ Router │ │ (WebRTC Bridge) │ │ │
|
||||
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ WebRTC (libdatachannel)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Real Smartphone / Other Devices │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Folder Structure
|
||||
|
||||
```
|
||||
MosisService/
|
||||
├── src/main/
|
||||
│ ├── kernel/ # Shared cross-platform kernel
|
||||
│ │ ├── include/ # Platform abstractions
|
||||
│ │ └── src/ # Shared implementation
|
||||
│ ├── cpp/ # Android-specific native code
|
||||
│ ├── java/ # Kotlin/Java code
|
||||
│ └── assets/ # Shared UI assets
|
||||
│ ├── apps/ # System apps
|
||||
│ │ ├── home/
|
||||
│ │ ├── dialer/
|
||||
│ │ ├── messages/
|
||||
│ │ ├── contacts/
|
||||
│ │ ├── settings/
|
||||
│ │ ├── browser/
|
||||
│ │ ├── store/ # TODO
|
||||
│ │ └── camera/ # TODO
|
||||
│ ├── ui/ # Shared stylesheets
|
||||
│ ├── scripts/ # Lua scripts
|
||||
│ ├── icons/ # TGA icons
|
||||
│ └── fonts/ # TTF fonts
|
||||
│
|
||||
├── designer/ # Desktop designer
|
||||
│ ├── src/
|
||||
│ │ ├── testing/ # UI inspection, capture
|
||||
│ │ └── ...
|
||||
│ └── build/
|
||||
│
|
||||
├── designer-test/ # Automated UI tests
|
||||
│ ├── src/
|
||||
│ └── build/
|
||||
│
|
||||
└── docs/ # Documentation (future)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build Commands
|
||||
|
||||
```bash
|
||||
# Android
|
||||
./gradlew assembleDebug
|
||||
./gradlew installDebug
|
||||
|
||||
# Desktop Designer
|
||||
cd designer
|
||||
cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake
|
||||
cmake --build build --config Debug
|
||||
./build/Debug/mosis-designer.exe ../src/main/assets/apps/home/home.rml
|
||||
|
||||
# Designer Tests
|
||||
cd designer-test
|
||||
cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake
|
||||
cmake --build build --config Debug
|
||||
./build/Debug/designer-test.exe
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Current Sprint: Complete Partial Tasks
|
||||
|
||||
### Priority 1: Testing Framework Completion
|
||||
- [ ] Action recording (capture interactions to JSON)
|
||||
- [ ] Action playback (replay with timing)
|
||||
- [ ] Screenshot diff (visual regression)
|
||||
|
||||
### Priority 2: Remaining System Apps
|
||||
- [ ] Store app (UI only - browse, install)
|
||||
- [ ] Camera app (UI + shared texture from MainActivity)
|
||||
- [ ] Music app (UI outline only)
|
||||
|
||||
### Priority 3: App Data Persistence
|
||||
- [ ] JSON/SQLite storage layer
|
||||
- [ ] Contact CRUD operations
|
||||
- [ ] Message history
|
||||
- [ ] Settings persistence
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
### Material Design
|
||||
|
||||
- **Icons**: `D:\Dev\Mosis\MosisDesigner\material-design-icons`
|
||||
- 20 categories, SVG/PNG/Font formats
|
||||
- Browse at https://fonts.google.com/icons
|
||||
|
||||
- **Components**: `D:\Dev\Mosis\MosisDesigner\material-design-lite`
|
||||
- Reference implementation for design patterns
|
||||
|
||||
### Documentation
|
||||
|
||||
- `CLAUDE.md` - Development guide
|
||||
- `TESTING.md` - Testing framework documentation
|
||||
- `ROADMAP.md` - This file
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2024-01-16*
|
||||
385
src/main/assets/apps/camera/camera.rml
Normal file
385
src/main/assets/apps/camera/camera.rml
Normal file
@@ -0,0 +1,385 @@
|
||||
<rml>
|
||||
<head>
|
||||
<link type="text/rcss" href="../../ui/html.rcss"/>
|
||||
<link type="text/rcss" href="../../ui/theme.rcss"/>
|
||||
<link type="text/rcss" href="../../ui/components.rcss"/>
|
||||
<script src="../../scripts/navigation.lua"></script>
|
||||
<title>Camera</title>
|
||||
<style>
|
||||
.camera-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Top Controls */
|
||||
.camera-top-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.camera-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 22px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.camera-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.camera-btn img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* Viewfinder */
|
||||
.viewfinder-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.viewfinder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #1a1a1a;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Placeholder for camera feed - replace with shared texture */
|
||||
.viewfinder-placeholder {
|
||||
color: #666666;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.viewfinder-placeholder-icon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 16px;
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
/* Grid Overlay */
|
||||
.grid-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.grid-line-h, .grid-line-v {
|
||||
position: absolute;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.grid-line-h {
|
||||
height: 1px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.grid-line-h.h1 { top: 33.33%; }
|
||||
.grid-line-h.h2 { top: 66.66%; }
|
||||
|
||||
.grid-line-v {
|
||||
width: 1px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.grid-line-v.v1 { left: 33.33%; }
|
||||
.grid-line-v.v2 { left: 66.66%; }
|
||||
|
||||
/* Focus indicator */
|
||||
.focus-indicator {
|
||||
position: absolute;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 2px solid #FFFFFF;
|
||||
border-radius: 8px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin-top: -40px;
|
||||
margin-left: -40px;
|
||||
pointer-events: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Camera Modes */
|
||||
.camera-modes {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 16px 0;
|
||||
gap: 24px;
|
||||
}
|
||||
|
||||
.camera-mode {
|
||||
font-size: 14px;
|
||||
color: #B3B3B3;
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.camera-mode.active {
|
||||
color: #FFD700;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.camera-mode:hover {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* Bottom Controls */
|
||||
.camera-bottom-bar {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
padding: 24px 32px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.gallery-preview {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 8px;
|
||||
background-color: #333333;
|
||||
border: 2px solid #FFFFFF;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gallery-preview img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.capture-btn {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 36px;
|
||||
background-color: #FFFFFF;
|
||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.capture-btn:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.capture-btn:active {
|
||||
transform: scale(0.95);
|
||||
background-color: #E0E0E0;
|
||||
}
|
||||
|
||||
.capture-btn-inner {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 30px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.capture-btn.video .capture-btn-inner {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 6px;
|
||||
background-color: #F44336;
|
||||
}
|
||||
|
||||
.switch-camera-btn {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 24px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.switch-camera-btn:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.switch-camera-btn img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* Settings Overlay */
|
||||
.settings-value {
|
||||
position: absolute;
|
||||
bottom: 200px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* Flash modes */
|
||||
.flash-indicator {
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
left: 16px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
padding: 6px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* Timer indicator */
|
||||
.timer-indicator {
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
right: 16px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
padding: 6px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* Zoom slider */
|
||||
.zoom-control {
|
||||
position: absolute;
|
||||
bottom: 180px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.zoom-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 16px;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 18px;
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.zoom-level {
|
||||
font-size: 14px;
|
||||
color: #FFD700;
|
||||
font-weight: 600;
|
||||
min-width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="camera-screen">
|
||||
<!-- Top Bar -->
|
||||
<div class="camera-top-bar">
|
||||
<div class="camera-btn" onclick="goBack()">
|
||||
<img src="../../icons/close.tga"/>
|
||||
</div>
|
||||
<div style="display: flex; gap: 12px;">
|
||||
<div class="camera-btn">
|
||||
<img src="../../icons/flash.tga"/>
|
||||
</div>
|
||||
<div class="camera-btn">
|
||||
<img src="../../icons/timer.tga"/>
|
||||
</div>
|
||||
<div class="camera-btn">
|
||||
<img src="../../icons/settings.tga"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Viewfinder Area -->
|
||||
<div class="viewfinder-container">
|
||||
<div class="viewfinder" id="camera-viewfinder">
|
||||
<!-- This is where the shared camera texture would be rendered -->
|
||||
<div class="viewfinder-placeholder">
|
||||
<div class="viewfinder-placeholder-icon">C</div>
|
||||
<div>Camera Preview</div>
|
||||
<div style="font-size: 12px; margin-top: 8px; color: #555555;">
|
||||
Tap to focus
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Grid Overlay -->
|
||||
<div class="grid-overlay">
|
||||
<div class="grid-line-h h1"></div>
|
||||
<div class="grid-line-h h2"></div>
|
||||
<div class="grid-line-v v1"></div>
|
||||
<div class="grid-line-v v2"></div>
|
||||
</div>
|
||||
|
||||
<!-- Focus Indicator -->
|
||||
<div class="focus-indicator"></div>
|
||||
</div>
|
||||
|
||||
<!-- Flash Indicator -->
|
||||
<div class="flash-indicator">Flash: Auto</div>
|
||||
|
||||
<!-- Timer Indicator -->
|
||||
<div class="timer-indicator">Timer: Off</div>
|
||||
|
||||
<!-- Zoom Control -->
|
||||
<div class="zoom-control">
|
||||
<div class="zoom-btn">-</div>
|
||||
<span class="zoom-level">1.0x</span>
|
||||
<div class="zoom-btn">+</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Camera Modes -->
|
||||
<div class="camera-modes">
|
||||
<span class="camera-mode">Night</span>
|
||||
<span class="camera-mode">Portrait</span>
|
||||
<span class="camera-mode active">Photo</span>
|
||||
<span class="camera-mode">Video</span>
|
||||
<span class="camera-mode">More</span>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Bar -->
|
||||
<div class="camera-bottom-bar">
|
||||
<div class="gallery-preview">
|
||||
<img src="../../icons/gallery.tga"/>
|
||||
</div>
|
||||
<div class="capture-btn" id="capture-button">
|
||||
<div class="capture-btn-inner"></div>
|
||||
</div>
|
||||
<div class="switch-camera-btn">
|
||||
<img src="../../icons/switch-camera.tga"/>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</rml>
|
||||
@@ -73,16 +73,16 @@
|
||||
<div class="app-icon-image" style="background-color: #9C27B0;"><img src="../../icons/gallery.tga"/></div>
|
||||
<span class="app-icon-label">Gallery</span>
|
||||
</div>
|
||||
<div class="app-icon">
|
||||
<div class="app-icon-image" style="background-color: #00BCD4;"><img src="../../icons/camera.tga"/></div>
|
||||
<div id="app-camera" class="app-icon">
|
||||
<div class="app-icon-image" style="background-color: #00BCD4;" onclick="navigateTo('camera')"><img src="../../icons/camera.tga"/></div>
|
||||
<span class="app-icon-label">Camera</span>
|
||||
</div>
|
||||
<div id="app-settings" class="app-icon">
|
||||
<div class="app-icon-image" style="background-color: #607D8B;" onclick="navigateTo('settings')"><img src="../../icons/settings.tga"/></div>
|
||||
<span class="app-icon-label">Settings</span>
|
||||
</div>
|
||||
<div class="app-icon">
|
||||
<div class="app-icon-image" style="background-color: #E91E63;"><img src="../../icons/music.tga"/></div>
|
||||
<div id="app-music" class="app-icon">
|
||||
<div class="app-icon-image" style="background-color: #E91E63;" onclick="navigateTo('music')"><img src="../../icons/music.tga"/></div>
|
||||
<span class="app-icon-label">Music</span>
|
||||
</div>
|
||||
|
||||
@@ -105,8 +105,8 @@
|
||||
</div>
|
||||
|
||||
<!-- Row 4 -->
|
||||
<div class="app-icon">
|
||||
<div class="app-icon-image" style="background-color: #8BC34A;"><img src="../../icons/store.tga"/></div>
|
||||
<div id="app-store" class="app-icon">
|
||||
<div class="app-icon-image" style="background-color: #8BC34A;" onclick="navigateTo('store')"><img src="../../icons/store.tga"/></div>
|
||||
<span class="app-icon-label">Store</span>
|
||||
</div>
|
||||
<div class="app-icon">
|
||||
|
||||
410
src/main/assets/apps/music/music.rml
Normal file
410
src/main/assets/apps/music/music.rml
Normal file
@@ -0,0 +1,410 @@
|
||||
<rml>
|
||||
<head>
|
||||
<link type="text/rcss" href="../../ui/html.rcss"/>
|
||||
<link type="text/rcss" href="../../ui/theme.rcss"/>
|
||||
<link type="text/rcss" href="../../ui/components.rcss"/>
|
||||
<script src="../../scripts/navigation.lua"></script>
|
||||
<title>Music</title>
|
||||
<style>
|
||||
.music-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #121212;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.music-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Now Playing Mini Bar */
|
||||
.mini-player {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
background-color: #282828;
|
||||
border-top: 1px solid #333333;
|
||||
}
|
||||
|
||||
.mini-player-art {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.mini-player-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.mini-player-title {
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.mini-player-artist {
|
||||
font-size: 12px;
|
||||
color: #B3B3B3;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.mini-player-controls {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mini-control-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mini-control-btn img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
/* Section Headers */
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24px 16px 12px 16px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.section-action {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #B3B3B3;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Recently Played Row */
|
||||
.recent-row {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
padding: 0 16px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.recent-item {
|
||||
min-width: 130px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.recent-art {
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 40px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.recent-title {
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.recent-subtitle {
|
||||
font-size: 12px;
|
||||
color: #B3B3B3;
|
||||
}
|
||||
|
||||
/* Quick Access Cards */
|
||||
.quick-access {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.quick-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: #282828;
|
||||
border-radius: 4px;
|
||||
padding: 0;
|
||||
height: 56px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.quick-card:hover {
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
.quick-card-art {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.quick-card-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
/* Playlist Row */
|
||||
.playlist-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.playlist-item:hover {
|
||||
background-color: #1E1E1E;
|
||||
}
|
||||
|
||||
.playlist-art {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 4px;
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.playlist-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.playlist-title {
|
||||
font-size: 16px;
|
||||
color: #FFFFFF;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.playlist-meta {
|
||||
font-size: 13px;
|
||||
color: #B3B3B3;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* Bottom Navigation */
|
||||
.music-bottom-nav {
|
||||
display: flex;
|
||||
height: 56px;
|
||||
background-color: #1E1E1E;
|
||||
border-top: 1px solid #282828;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #B3B3B3;
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.nav-item img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.nav-item span {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
/* Color palette for album arts */
|
||||
.bg-gradient-1 { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
|
||||
.bg-gradient-2 { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
|
||||
.bg-gradient-3 { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
|
||||
.bg-gradient-4 { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
|
||||
.bg-gradient-5 { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); }
|
||||
.bg-gradient-6 { background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); }
|
||||
.bg-solid-purple { background-color: #7c3aed; }
|
||||
.bg-solid-red { background-color: #dc2626; }
|
||||
.bg-solid-green { background-color: #16a34a; }
|
||||
.bg-solid-blue { background-color: #2563eb; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="music-screen">
|
||||
<!-- App Bar -->
|
||||
<div class="app-bar">
|
||||
<div class="app-bar-nav btn-icon" onclick="goBack()">
|
||||
<img src="../../icons/back.tga" style="width: 24px; height: 24px;"/>
|
||||
</div>
|
||||
<span class="app-bar-title">Music</span>
|
||||
<div class="btn-icon">
|
||||
<img src="../../icons/search.tga" style="width: 24px; height: 24px;"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="music-content">
|
||||
<!-- Good Morning Section -->
|
||||
<div class="section-header">
|
||||
<span class="section-title">Good afternoon</span>
|
||||
</div>
|
||||
|
||||
<!-- Quick Access Grid -->
|
||||
<div class="quick-access">
|
||||
<div class="quick-card">
|
||||
<div class="quick-card-art bg-solid-red">L</div>
|
||||
<span class="quick-card-title">Liked Songs</span>
|
||||
</div>
|
||||
<div class="quick-card">
|
||||
<div class="quick-card-art bg-gradient-1">D</div>
|
||||
<span class="quick-card-title">Daily Mix 1</span>
|
||||
</div>
|
||||
<div class="quick-card">
|
||||
<div class="quick-card-art bg-solid-green">R</div>
|
||||
<span class="quick-card-title">Release Radar</span>
|
||||
</div>
|
||||
<div class="quick-card">
|
||||
<div class="quick-card-art bg-gradient-2">C</div>
|
||||
<span class="quick-card-title">Chill Vibes</span>
|
||||
</div>
|
||||
<div class="quick-card">
|
||||
<div class="quick-card-art bg-solid-blue">W</div>
|
||||
<span class="quick-card-title">Workout Mix</span>
|
||||
</div>
|
||||
<div class="quick-card">
|
||||
<div class="quick-card-art bg-gradient-3">F</div>
|
||||
<span class="quick-card-title">Focus Flow</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recently Played -->
|
||||
<div class="section-header">
|
||||
<span class="section-title">Recently Played</span>
|
||||
<span class="section-action">SEE ALL</span>
|
||||
</div>
|
||||
|
||||
<div class="recent-row">
|
||||
<div class="recent-item">
|
||||
<div class="recent-art bg-gradient-4">P</div>
|
||||
<div class="recent-title">Pop Hits</div>
|
||||
<div class="recent-subtitle">Playlist</div>
|
||||
</div>
|
||||
<div class="recent-item">
|
||||
<div class="recent-art bg-gradient-5">E</div>
|
||||
<div class="recent-title">Electronic</div>
|
||||
<div class="recent-subtitle">Playlist</div>
|
||||
</div>
|
||||
<div class="recent-item">
|
||||
<div class="recent-art bg-gradient-1">J</div>
|
||||
<div class="recent-title">Jazz Classics</div>
|
||||
<div class="recent-subtitle">Playlist</div>
|
||||
</div>
|
||||
<div class="recent-item">
|
||||
<div class="recent-art bg-gradient-2">R</div>
|
||||
<div class="recent-title">Rock Legends</div>
|
||||
<div class="recent-subtitle">Playlist</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Made For You -->
|
||||
<div class="section-header">
|
||||
<span class="section-title">Made For You</span>
|
||||
<span class="section-action">SEE ALL</span>
|
||||
</div>
|
||||
|
||||
<div class="playlist-item">
|
||||
<div class="playlist-art bg-gradient-3">1</div>
|
||||
<div class="playlist-info">
|
||||
<div class="playlist-title">Daily Mix 1</div>
|
||||
<div class="playlist-meta">Based on your listening</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="playlist-item">
|
||||
<div class="playlist-art bg-gradient-4">2</div>
|
||||
<div class="playlist-info">
|
||||
<div class="playlist-title">Daily Mix 2</div>
|
||||
<div class="playlist-meta">Electronic, Ambient, Chill</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="playlist-item">
|
||||
<div class="playlist-art bg-gradient-5">D</div>
|
||||
<div class="playlist-info">
|
||||
<div class="playlist-title">Discover Weekly</div>
|
||||
<div class="playlist-meta">Your weekly mixtape</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="playlist-item">
|
||||
<div class="playlist-art bg-solid-green">R</div>
|
||||
<div class="playlist-info">
|
||||
<div class="playlist-title">Release Radar</div>
|
||||
<div class="playlist-meta">New music from artists you follow</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mini Player -->
|
||||
<div class="mini-player">
|
||||
<div class="mini-player-art">M</div>
|
||||
<div class="mini-player-info">
|
||||
<div class="mini-player-title">Midnight City</div>
|
||||
<div class="mini-player-artist">M83</div>
|
||||
</div>
|
||||
<div class="mini-player-controls">
|
||||
<div class="mini-control-btn">
|
||||
<img src="../../icons/heart.tga"/>
|
||||
</div>
|
||||
<div class="mini-control-btn">
|
||||
<img src="../../icons/play.tga"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Navigation -->
|
||||
<div class="music-bottom-nav">
|
||||
<div class="nav-item active">
|
||||
<img src="../../icons/home.tga"/>
|
||||
<span>Home</span>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<img src="../../icons/search.tga"/>
|
||||
<span>Search</span>
|
||||
</div>
|
||||
<div class="nav-item">
|
||||
<img src="../../icons/library.tga"/>
|
||||
<span>Library</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</rml>
|
||||
488
src/main/assets/apps/store/store.rml
Normal file
488
src/main/assets/apps/store/store.rml
Normal file
@@ -0,0 +1,488 @@
|
||||
<rml>
|
||||
<head>
|
||||
<link type="text/rcss" href="../../ui/html.rcss"/>
|
||||
<link type="text/rcss" href="../../ui/theme.rcss"/>
|
||||
<link type="text/rcss" href="../../ui/components.rcss"/>
|
||||
<script src="../../scripts/navigation.lua"></script>
|
||||
<title>Store</title>
|
||||
<style>
|
||||
.store-screen {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #121212;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.store-content {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Search Bar */
|
||||
.store-search {
|
||||
margin: 16px;
|
||||
background-color: #2D2D2D;
|
||||
border-radius: 24px;
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.store-search img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 12px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.store-search-text {
|
||||
font-size: 16px;
|
||||
color: #B3B3B3;
|
||||
}
|
||||
|
||||
/* Section Headers */
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 16px 8px 16px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.section-action {
|
||||
font-size: 14px;
|
||||
color: #BB86FC;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Featured Banner */
|
||||
.featured-banner {
|
||||
margin: 0 16px 16px 16px;
|
||||
height: 160px;
|
||||
background: linear-gradient(135deg, #BB86FC 0%, #6200EE 100%);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.featured-tag {
|
||||
font-size: 12px;
|
||||
color: rgba(255,255,255,0.7);
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.featured-title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.featured-subtitle {
|
||||
font-size: 14px;
|
||||
color: rgba(255,255,255,0.8);
|
||||
}
|
||||
|
||||
/* App Cards Row */
|
||||
.app-cards-row {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
padding: 0 16px;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.app-card {
|
||||
min-width: 140px;
|
||||
background-color: #1E1E1E;
|
||||
border-radius: 12px;
|
||||
padding: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.app-card:hover {
|
||||
background-color: #252525;
|
||||
}
|
||||
|
||||
.app-card-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 14px;
|
||||
margin-bottom: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.app-card-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #FFFFFF;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.app-card-category {
|
||||
font-size: 12px;
|
||||
color: #B3B3B3;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.app-card-rating {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #B3B3B3;
|
||||
}
|
||||
|
||||
.app-card-rating img {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* App List Items */
|
||||
.app-list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.app-list-item:hover {
|
||||
background-color: #1E1E1E;
|
||||
}
|
||||
|
||||
.app-list-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 12px;
|
||||
margin-right: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.app-list-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.app-list-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.app-list-meta {
|
||||
font-size: 13px;
|
||||
color: #B3B3B3;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.app-list-rating {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.app-list-rating img {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.app-list-rating span {
|
||||
font-size: 13px;
|
||||
color: #B3B3B3;
|
||||
}
|
||||
|
||||
.install-btn {
|
||||
background-color: #BB86FC;
|
||||
color: #000000;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
padding: 8px 20px;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.install-btn:hover {
|
||||
background-color: #D4A5FF;
|
||||
}
|
||||
|
||||
.install-btn.installed {
|
||||
background-color: transparent;
|
||||
color: #BB86FC;
|
||||
border: 1px solid #BB86FC;
|
||||
}
|
||||
|
||||
/* Category Chips */
|
||||
.category-chips {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
padding: 0 16px;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.category-chip {
|
||||
background-color: #2D2D2D;
|
||||
color: #FFFFFF;
|
||||
font-size: 14px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 20px;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.category-chip:hover {
|
||||
background-color: #3D3D3D;
|
||||
}
|
||||
|
||||
.category-chip.active {
|
||||
background-color: #BB86FC;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
/* Bottom Nav */
|
||||
.store-bottom-nav {
|
||||
display: flex;
|
||||
height: 56px;
|
||||
background-color: #1E1E1E;
|
||||
}
|
||||
|
||||
.store-nav-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
color: #B3B3B3;
|
||||
}
|
||||
|
||||
.store-nav-item.active {
|
||||
color: #BB86FC;
|
||||
}
|
||||
|
||||
.store-nav-item img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.store-nav-item span {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Color palette for app icons */
|
||||
.bg-purple { background-color: #BB86FC; }
|
||||
.bg-teal { background-color: #03DAC6; }
|
||||
.bg-orange { background-color: #FF9800; }
|
||||
.bg-blue { background-color: #2196F3; }
|
||||
.bg-green { background-color: #4CAF50; }
|
||||
.bg-red { background-color: #F44336; }
|
||||
.bg-pink { background-color: #E91E63; }
|
||||
.bg-indigo { background-color: #3F51B5; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="store-screen">
|
||||
<!-- App Bar -->
|
||||
<div class="app-bar">
|
||||
<div class="app-bar-nav btn-icon" onclick="goBack()">
|
||||
<img src="../../icons/back.tga" style="width: 24px; height: 24px;"/>
|
||||
</div>
|
||||
<span class="app-bar-title">Mosis Store</span>
|
||||
<div class="btn-icon">
|
||||
<img src="../../icons/account.tga" style="width: 24px; height: 24px;"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Store Content -->
|
||||
<div class="store-content">
|
||||
<!-- Search Bar -->
|
||||
<div class="store-search">
|
||||
<img src="../../icons/search.tga"/>
|
||||
<span class="store-search-text">Search apps & games</span>
|
||||
</div>
|
||||
|
||||
<!-- Featured Banner -->
|
||||
<div class="featured-banner">
|
||||
<span class="featured-tag">Featured</span>
|
||||
<span class="featured-title">Weather Pro</span>
|
||||
<span class="featured-subtitle">Beautiful forecasts for your virtual world</span>
|
||||
</div>
|
||||
|
||||
<!-- Category Chips -->
|
||||
<div class="category-chips">
|
||||
<div class="category-chip active">For You</div>
|
||||
<div class="category-chip">Games</div>
|
||||
<div class="category-chip">Social</div>
|
||||
<div class="category-chip">Productivity</div>
|
||||
<div class="category-chip">Entertainment</div>
|
||||
<div class="category-chip">Tools</div>
|
||||
</div>
|
||||
|
||||
<!-- Recommended Section -->
|
||||
<div class="section-header">
|
||||
<span class="section-title">Recommended for You</span>
|
||||
<span class="section-action">See all</span>
|
||||
</div>
|
||||
|
||||
<div class="app-cards-row">
|
||||
<div class="app-card">
|
||||
<div class="app-card-icon bg-teal">N</div>
|
||||
<div class="app-card-name">Notes</div>
|
||||
<div class="app-card-category">Productivity</div>
|
||||
<div class="app-card-rating">4.7</div>
|
||||
</div>
|
||||
<div class="app-card">
|
||||
<div class="app-card-icon bg-orange">C</div>
|
||||
<div class="app-card-name">Calculator</div>
|
||||
<div class="app-card-category">Tools</div>
|
||||
<div class="app-card-rating">4.5</div>
|
||||
</div>
|
||||
<div class="app-card">
|
||||
<div class="app-card-icon bg-blue">W</div>
|
||||
<div class="app-card-name">Weather</div>
|
||||
<div class="app-card-category">Weather</div>
|
||||
<div class="app-card-rating">4.8</div>
|
||||
</div>
|
||||
<div class="app-card">
|
||||
<div class="app-card-icon bg-green">M</div>
|
||||
<div class="app-card-name">Maps</div>
|
||||
<div class="app-card-category">Navigation</div>
|
||||
<div class="app-card-rating">4.6</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Top Apps Section -->
|
||||
<div class="section-header">
|
||||
<span class="section-title">Top Free Apps</span>
|
||||
<span class="section-action">See all</span>
|
||||
</div>
|
||||
|
||||
<!-- App List -->
|
||||
<div class="app-list-item">
|
||||
<div class="app-list-icon bg-purple">S</div>
|
||||
<div class="app-list-info">
|
||||
<div class="app-list-name">Social Hub</div>
|
||||
<div class="app-list-meta">Social • 12 MB</div>
|
||||
<div class="app-list-rating">
|
||||
<span>4.9 • 1.2M downloads</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-btn">Install</div>
|
||||
</div>
|
||||
|
||||
<div class="app-list-item">
|
||||
<div class="app-list-icon bg-red">G</div>
|
||||
<div class="app-list-info">
|
||||
<div class="app-list-name">Games Center</div>
|
||||
<div class="app-list-meta">Games • 45 MB</div>
|
||||
<div class="app-list-rating">
|
||||
<span>4.7 • 890K downloads</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-btn">Install</div>
|
||||
</div>
|
||||
|
||||
<div class="app-list-item">
|
||||
<div class="app-list-icon bg-indigo">F</div>
|
||||
<div class="app-list-info">
|
||||
<div class="app-list-name">File Manager</div>
|
||||
<div class="app-list-meta">Tools • 8 MB</div>
|
||||
<div class="app-list-rating">
|
||||
<span>4.6 • 650K downloads</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-btn installed">Open</div>
|
||||
</div>
|
||||
|
||||
<div class="app-list-item">
|
||||
<div class="app-list-icon bg-pink">M</div>
|
||||
<div class="app-list-info">
|
||||
<div class="app-list-name">Music Player</div>
|
||||
<div class="app-list-meta">Music • 18 MB</div>
|
||||
<div class="app-list-rating">
|
||||
<span>4.5 • 520K downloads</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-btn">Install</div>
|
||||
</div>
|
||||
|
||||
<div class="app-list-item">
|
||||
<div class="app-list-icon bg-teal">P</div>
|
||||
<div class="app-list-info">
|
||||
<div class="app-list-name">Photo Editor</div>
|
||||
<div class="app-list-meta">Photography • 32 MB</div>
|
||||
<div class="app-list-rating">
|
||||
<span>4.4 • 410K downloads</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="install-btn">Install</div>
|
||||
</div>
|
||||
|
||||
<!-- New Games Section -->
|
||||
<div class="section-header">
|
||||
<span class="section-title">New Games</span>
|
||||
<span class="section-action">See all</span>
|
||||
</div>
|
||||
|
||||
<div class="app-cards-row">
|
||||
<div class="app-card">
|
||||
<div class="app-card-icon bg-red">P</div>
|
||||
<div class="app-card-name">Puzzle Quest</div>
|
||||
<div class="app-card-category">Puzzle</div>
|
||||
<div class="app-card-rating">4.8</div>
|
||||
</div>
|
||||
<div class="app-card">
|
||||
<div class="app-card-icon bg-green">R</div>
|
||||
<div class="app-card-name">Racing VR</div>
|
||||
<div class="app-card-category">Racing</div>
|
||||
<div class="app-card-rating">4.6</div>
|
||||
</div>
|
||||
<div class="app-card">
|
||||
<div class="app-card-icon bg-blue">S</div>
|
||||
<div class="app-card-name">Space Explorer</div>
|
||||
<div class="app-card-category">Adventure</div>
|
||||
<div class="app-card-rating">4.7</div>
|
||||
</div>
|
||||
<div class="app-card">
|
||||
<div class="app-card-icon bg-orange">C</div>
|
||||
<div class="app-card-name">Card Master</div>
|
||||
<div class="app-card-category">Card</div>
|
||||
<div class="app-card-rating">4.5</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bottom Navigation -->
|
||||
<div class="store-bottom-nav">
|
||||
<div class="store-nav-item active">
|
||||
<img src="../../icons/home.tga"/>
|
||||
<span>Apps</span>
|
||||
</div>
|
||||
<div class="store-nav-item">
|
||||
<img src="../../icons/game.tga"/>
|
||||
<span>Games</span>
|
||||
</div>
|
||||
<div class="store-nav-item">
|
||||
<img src="../../icons/download.tga"/>
|
||||
<span>Updates</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</rml>
|
||||
BIN
src/main/assets/icons/account.tga
LFS
Normal file
BIN
src/main/assets/icons/account.tga
LFS
Normal file
Binary file not shown.
BIN
src/main/assets/icons/download.tga
LFS
Normal file
BIN
src/main/assets/icons/download.tga
LFS
Normal file
Binary file not shown.
BIN
src/main/assets/icons/flash.tga
LFS
Normal file
BIN
src/main/assets/icons/flash.tga
LFS
Normal file
Binary file not shown.
BIN
src/main/assets/icons/game.tga
LFS
Normal file
BIN
src/main/assets/icons/game.tga
LFS
Normal file
Binary file not shown.
BIN
src/main/assets/icons/heart.tga
LFS
Normal file
BIN
src/main/assets/icons/heart.tga
LFS
Normal file
Binary file not shown.
BIN
src/main/assets/icons/library.tga
LFS
Normal file
BIN
src/main/assets/icons/library.tga
LFS
Normal file
Binary file not shown.
BIN
src/main/assets/icons/play.tga
LFS
Normal file
BIN
src/main/assets/icons/play.tga
LFS
Normal file
Binary file not shown.
BIN
src/main/assets/icons/switch-camera.tga
LFS
Normal file
BIN
src/main/assets/icons/switch-camera.tga
LFS
Normal file
Binary file not shown.
BIN
src/main/assets/icons/timer.tga
LFS
Normal file
BIN
src/main/assets/icons/timer.tga
LFS
Normal file
Binary file not shown.
@@ -12,7 +12,10 @@ local screens = {
|
||||
messages = "apps/messages/messages.rml",
|
||||
chat = "apps/messages/chat.rml",
|
||||
settings = "apps/settings/settings.rml",
|
||||
browser = "apps/browser/browser.rml"
|
||||
browser = "apps/browser/browser.rml",
|
||||
store = "apps/store/store.rml",
|
||||
camera = "apps/camera/camera.rml",
|
||||
music = "apps/music/music.rml"
|
||||
}
|
||||
|
||||
-- Use global state to persist across document loads
|
||||
|
||||
Reference in New Issue
Block a user