501 lines
11 KiB
Markdown
501 lines
11 KiB
Markdown
# 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
|
|
|
|
1. **Browse Apps**
|
|
- Featured apps carousel
|
|
- Categories (Games, Utilities, Social)
|
|
- Search functionality
|
|
|
|
2. **App Details**
|
|
- Name, icon, description
|
|
- Screenshots
|
|
- Permissions list
|
|
- Install/Update button
|
|
|
|
3. **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`)
|
|
|
|
```html
|
|
<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
|
|
|
|
```cpp
|
|
// 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
|
|
|
|
1. **Viewfinder**
|
|
- Live camera preview (from ICamera)
|
|
- Capture button
|
|
- Switch camera (front/back)
|
|
- Flash toggle
|
|
|
|
2. **Capture**
|
|
- Take photo
|
|
- Save to gallery
|
|
- Share option
|
|
|
|
3. **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`)
|
|
|
|
```html
|
|
<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
|
|
|
|
```lua
|
|
-- 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
|
|
|
|
1. **Library**
|
|
- Songs list
|
|
- Albums
|
|
- Artists
|
|
- Playlists
|
|
|
|
2. **Player**
|
|
- Play/pause
|
|
- Next/previous
|
|
- Seek bar
|
|
- Volume control
|
|
- Shuffle/repeat
|
|
|
|
3. **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**:
|
|
|
|
1. **JSON Files** (Simple)
|
|
```
|
|
/data/contacts.json
|
|
/data/messages.json
|
|
/data/settings.json
|
|
```
|
|
|
|
2. **SQLite** (Robust)
|
|
```
|
|
/data/mosis.db
|
|
- contacts table
|
|
- messages table
|
|
- settings table
|
|
```
|
|
|
|
### Contact Storage
|
|
|
|
```cpp
|
|
// 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
|
|
|
|
```cpp
|
|
// 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
|