11 KiB
11 KiB
Milestone 6: System Apps
Status: 75% Complete Goal: Core smartphone apps with full functionality.
Overview
System apps provide the essential smartphone experience:
- Home launcher
- Phone/Dialer
- Messages
- Contacts
- Settings
- Browser
- Store (TODO)
- Camera (TODO)
- Music (TODO)
App Status
Completed Apps
| App | Location | Features | Status |
|---|---|---|---|
| Home | apps/home/ |
App grid, dock, navigation | Complete |
| Dialer | apps/dialer/ |
Keypad, call UI (mock) | Complete |
| Messages | apps/messages/ |
Conversation list, chat | Complete |
| Contacts | apps/contacts/ |
List, search, detail | Complete |
| Settings | apps/settings/ |
Display, sound, about | Complete |
| Browser | apps/browser/ |
URL bar, placeholder | Complete |
Remaining Apps
| App | Priority | Description |
|---|---|---|
| Store | High | Browse and install apps |
| Camera | Medium | Viewfinder, capture photos |
| Music | Low | Audio playback |
App: Store
Location: src/main/assets/apps/store/
Features
-
Browse Apps
- Featured apps carousel
- Categories (Games, Utilities, Social)
- Search functionality
-
App Details
- Name, icon, description
- Screenshots
- Permissions list
- Install/Update button
-
My Apps
- Installed apps list
- Update available indicator
- Uninstall option
UI Screens
store/
├── store.rml # Main store screen
├── store.rcss # Store styles
├── category.rml # Category listing
├── detail.rml # App detail page
└── scripts/
└── store.lua # Store logic
Main Screen (store.rml)
<rml>
<head>
<link type="text/rcss" href="store.rcss"/>
<link type="text/rcss" href="../../ui/theme.rcss"/>
</head>
<body class="store-screen">
<div class="app-bar">
<div class="app-bar-nav btn-icon" onclick="goBack()">
<img src="../../icons/back.tga"/>
</div>
<span class="app-bar-title">Store</span>
<div class="app-bar-action btn-icon" onclick="openSearch()">
<img src="../../icons/search.tga"/>
</div>
</div>
<div class="store-content">
<!-- Featured carousel -->
<div class="featured-section">
<h2>Featured</h2>
<div class="featured-carousel" data-for="app : featured_apps">
<div class="featured-card" data-event-click="showDetail(app.id)">
<img class="featured-banner" data-attr-src="app.banner"/>
<span class="featured-name" data-text="app.name"/>
</div>
</div>
</div>
<!-- Categories -->
<div class="category-section">
<h2>Categories</h2>
<div class="category-grid">
<div class="category-item" onclick="showCategory('games')">
<img src="../../icons/games.tga"/>
<span>Games</span>
</div>
<div class="category-item" onclick="showCategory('utilities')">
<img src="../../icons/tools.tga"/>
<span>Utilities</span>
</div>
</div>
</div>
<!-- Top Apps -->
<div class="top-apps-section">
<h2>Top Apps</h2>
<div class="app-list" data-for="app : top_apps">
<div class="app-list-item" data-event-click="showDetail(app.id)">
<img class="app-icon" data-attr-src="app.icon"/>
<div class="app-info">
<span class="app-name" data-text="app.name"/>
<span class="app-category" data-text="app.category"/>
</div>
<button class="install-btn" data-event-click="install(app.id)">
Install
</button>
</div>
</div>
</div>
</div>
</body>
</rml>
Data Model
// In data_models.cpp
struct StoreApp {
std::string id;
std::string name;
std::string icon;
std::string banner;
std::string category;
std::string description;
std::string version;
std::vector<std::string> screenshots;
std::vector<std::string> permissions;
bool installed;
};
void setupStoreDataModel(Rml::Context* context) {
auto model = context->CreateDataModel("store");
model.Bind("featured_apps", &g_featured_apps);
model.Bind("top_apps", &g_top_apps);
model.Bind("categories", &g_categories);
model.Bind("current_app", &g_current_app);
model.BindEventCallback("install", [](auto& event, auto& args) {
// Install app
});
}
App: Camera
Location: src/main/assets/apps/camera/
Features
-
Viewfinder
- Live camera preview (from ICamera)
- Capture button
- Switch camera (front/back)
- Flash toggle
-
Capture
- Take photo
- Save to gallery
- Share option
-
Gallery
- View captured photos
- Delete photos
- Share photos
UI Screens
camera/
├── camera.rml # Viewfinder
├── camera.rcss # Camera styles
├── gallery.rml # Photo gallery
└── scripts/
└── camera.lua # Camera logic
Viewfinder (camera.rml)
<rml>
<head>
<link type="text/rcss" href="camera.rcss"/>
</head>
<body class="camera-screen">
<!-- Camera preview (texture from ICamera) -->
<div id="camera-preview">
<img id="preview-frame" data-attr-src="camera_frame"/>
</div>
<!-- Top controls -->
<div class="camera-top-bar">
<div class="btn-icon" onclick="goBack()">
<img src="../../icons/close.tga"/>
</div>
<div class="btn-icon" onclick="toggleFlash()">
<img id="flash-icon" src="../../icons/flash_off.tga"/>
</div>
</div>
<!-- Bottom controls -->
<div class="camera-bottom-bar">
<div class="btn-icon" onclick="openGallery()">
<img id="last-photo" data-attr-src="last_photo_thumb"/>
</div>
<div id="capture-btn" onclick="capture()">
<div class="capture-ring"/>
</div>
<div class="btn-icon" onclick="switchCamera()">
<img src="../../icons/flip_camera.tga"/>
</div>
</div>
</body>
</rml>
Camera Lua Script
-- camera.lua
local camera = mosis.platform.getCamera()
local capture = mosis.testing.VisualCapture(540, 960)
local is_front_camera = false
local flash_on = false
function onAppCreate()
if camera:isAvailable() then
camera:startCapture(function(frame)
-- Update preview texture
document:GetElementById("preview-frame"):SetAttribute("src", frame.texture_url)
end)
else
-- Show "no camera" message
end
end
function capture()
local path = mosis.filesystem:getSharedMediaPath() .. "/photos/" .. os.time() .. ".png"
capture:CaptureScreenshot(path)
-- Show capture animation
playSound("shutter")
flashScreen()
-- Update last photo thumbnail
document:GetElementById("last-photo"):SetAttribute("src", path)
end
function switchCamera()
is_front_camera = not is_front_camera
-- camera:setFacing(is_front_camera and "front" or "back")
end
function toggleFlash()
flash_on = not flash_on
local icon = flash_on and "flash_on" or "flash_off"
document:GetElementById("flash-icon"):SetAttribute("src", "../../icons/" .. icon .. ".tga")
end
function openGallery()
navigateTo("camera/gallery")
end
App: Music
Location: src/main/assets/apps/music/
Features
-
Library
- Songs list
- Albums
- Artists
- Playlists
-
Player
- Play/pause
- Next/previous
- Seek bar
- Volume control
- Shuffle/repeat
-
Now Playing
- Album art
- Song info
- Progress bar
UI Screens
music/
├── music.rml # Library view
├── music.rcss # Music styles
├── player.rml # Now playing
├── playlist.rml # Playlist view
└── scripts/
└── music.lua # Player logic
Data Persistence
Storage Layer
Apps need persistent storage for:
- Contacts
- Messages
- Settings
- Photos
Implementation Options:
-
JSON Files (Simple)
/data/contacts.json /data/messages.json /data/settings.json -
SQLite (Robust)
/data/mosis.db - contacts table - messages table - settings table
Contact Storage
// contact_storage.h
struct Contact {
std::string id;
std::string name;
std::string phone;
std::string email;
std::string avatar;
};
class ContactStorage {
public:
std::vector<Contact> GetAll();
std::optional<Contact> GetById(const std::string& id);
bool Save(const Contact& contact);
bool Delete(const std::string& id);
std::vector<Contact> Search(const std::string& query);
};
Message Storage
// message_storage.h
struct Message {
std::string id;
std::string conversation_id;
std::string sender;
std::string text;
int64_t timestamp;
bool read;
};
struct Conversation {
std::string id;
std::string contact_id;
std::string last_message;
int64_t last_timestamp;
int unread_count;
};
class MessageStorage {
public:
std::vector<Conversation> GetConversations();
std::vector<Message> GetMessages(const std::string& conversation_id);
bool SaveMessage(const Message& message);
bool MarkAsRead(const std::string& conversation_id);
};
Implementation Plan
Phase 1: Store App UI
- Main store screen layout
- Category browsing
- App detail page
- Mock data for testing
Phase 2: Camera App
- Viewfinder UI
- Capture to file
- Gallery view
- Integration with ICamera
Phase 3: Music App
- Library UI
- Player UI
- Audio playback (stub)
Phase 4: Data Persistence
- JSON storage layer
- Contact CRUD
- Message storage
- Settings persistence
Phase 5: Real Functionality
- Store: Install real .mpkg files
- Camera: Real camera frames
- Music: Audio playback
Testing
Test IDs for Store
| ID | Element |
|---|---|
store-search |
Search button |
store-featured |
Featured carousel |
store-categories |
Category grid |
app-install-btn |
Install button on detail |
Test IDs for Camera
| ID | Element |
|---|---|
camera-preview |
Preview area |
capture-btn |
Capture button |
gallery-btn |
Gallery button |
switch-camera-btn |
Switch camera |
Acceptance Criteria
Store
- Browse featured and top apps
- View app details
- See permission requirements
- Install apps (mock or real)
Camera
- Display camera preview
- Capture photos
- View in gallery
- Share photos
Music
- Display music library
- Play/pause audio
- Show now playing
Persistence
- Contacts persist across sessions
- Messages persist across sessions
- Settings persist across sessions