90 changed files with 4017 additions and 405 deletions
@ -0,0 +1,852 @@ |
|||
# TimeSafari Code Quality: Comprehensive Deep Analysis |
|||
|
|||
**Author**: Matthew Raymer |
|||
**Date**: Tue Sep 16 05:22:10 AM UTC 2025 |
|||
**Status**: 🎯 **COMPREHENSIVE ANALYSIS** - Complete code quality assessment with actionable recommendations |
|||
|
|||
## Executive Summary |
|||
|
|||
The TimeSafari codebase demonstrates **exceptional code quality** with mature patterns, minimal technical debt, and excellent separation of concerns. This comprehensive analysis covers **291 source files** totaling **104,527 lines** of code, including detailed examination of **94 Vue components and views**. |
|||
|
|||
**Key Quality Metrics:** |
|||
- **Technical Debt**: Extremely low (6 TODO/FIXME comments across entire codebase) |
|||
- **Database Migration**: 99.5% complete (1 remaining legacy import) |
|||
- **File Complexity**: High variance (largest file: 2,215 lines) |
|||
- **Type Safety**: Mixed patterns (41 "as any" assertions in Vue files, 62 total) |
|||
- **Error Handling**: Comprehensive (367 catch blocks with good coverage) |
|||
- **Architecture**: Consistent Vue 3 Composition API with TypeScript |
|||
|
|||
## Vue Components & Views Analysis (94 Files) |
|||
|
|||
### Component Analysis (40 Components) |
|||
|
|||
#### Component Size Distribution |
|||
``` |
|||
Large Components (>500 lines): 5 components (12.5%) |
|||
├── ImageMethodDialog.vue (947 lines) 🔴 CRITICAL |
|||
├── GiftedDialog.vue (670 lines) ⚠️ HIGH PRIORITY |
|||
├── PhotoDialog.vue (669 lines) ⚠️ HIGH PRIORITY |
|||
├── PushNotificationPermission.vue (660 lines) ⚠️ HIGH PRIORITY |
|||
└── MembersList.vue (550 lines) ⚠️ MODERATE PRIORITY |
|||
|
|||
Medium Components (200-500 lines): 12 components (30%) |
|||
├── GiftDetailsStep.vue (450 lines) |
|||
├── EntityGrid.vue (348 lines) |
|||
├── ActivityListItem.vue (334 lines) |
|||
├── OfferDialog.vue (327 lines) |
|||
├── OnboardingDialog.vue (314 lines) |
|||
├── EntitySelectionStep.vue (313 lines) |
|||
├── GiftedPrompts.vue (293 lines) |
|||
├── ChoiceButtonDialog.vue (250 lines) |
|||
├── DataExportSection.vue (251 lines) |
|||
├── AmountInput.vue (224 lines) |
|||
├── HiddenDidDialog.vue (220 lines) |
|||
└── FeedFilters.vue (218 lines) |
|||
|
|||
Small Components (<200 lines): 23 components (57.5%) |
|||
├── ContactListItem.vue (217 lines) |
|||
├── EntitySummaryButton.vue (202 lines) |
|||
├── IdentitySection.vue (186 lines) |
|||
├── ContactInputForm.vue (173 lines) |
|||
├── SpecialEntityCard.vue (156 lines) |
|||
├── RegistrationNotice.vue (154 lines) |
|||
├── ContactNameDialog.vue (154 lines) |
|||
├── PersonCard.vue (153 lines) |
|||
├── UserNameDialog.vue (147 lines) |
|||
├── InfiniteScroll.vue (132 lines) |
|||
├── LocationSearchSection.vue (124 lines) |
|||
├── UsageLimitsSection.vue (123 lines) |
|||
├── QuickNav.vue (118 lines) |
|||
├── ProjectCard.vue (104 lines) |
|||
├── ContactListHeader.vue (101 lines) |
|||
├── TopMessage.vue (98 lines) |
|||
├── InviteDialog.vue (95 lines) |
|||
├── ImageViewer.vue (94 lines) |
|||
├── EntityIcon.vue (86 lines) |
|||
├── ShowAllCard.vue (66 lines) |
|||
├── ContactBulkActions.vue (53 lines) |
|||
├── ProjectIcon.vue (47 lines) |
|||
└── LargeIdenticonModal.vue (44 lines) |
|||
``` |
|||
|
|||
#### Critical Component Analysis |
|||
|
|||
**1. `ImageMethodDialog.vue` (947 lines) 🔴 CRITICAL REFACTORING NEEDED** |
|||
|
|||
**Issues Identified:** |
|||
- **Excessive Single Responsibility**: Handles camera preview, file upload, URL input, cropping, diagnostics, and error handling |
|||
- **Complex State Management**: 20+ reactive properties with interdependencies |
|||
- **Mixed Concerns**: Camera API, file handling, UI state, and business logic intertwined |
|||
- **Template Complexity**: ~300 lines of template with deeply nested conditions |
|||
|
|||
**Refactoring Strategy:** |
|||
```typescript |
|||
// Current monolithic structure |
|||
ImageMethodDialog.vue (947 lines) { |
|||
CameraPreview: ~200 lines |
|||
FileUpload: ~150 lines |
|||
URLInput: ~100 lines |
|||
CroppingInterface: ~200 lines |
|||
DiagnosticsPanel: ~150 lines |
|||
ErrorHandling: ~100 lines |
|||
StateManagement: ~47 lines |
|||
} |
|||
|
|||
// Proposed component decomposition |
|||
ImageMethodDialog.vue (coordinator, ~200 lines) |
|||
├── CameraPreviewComponent.vue (~250 lines) |
|||
├── FileUploadComponent.vue (~150 lines) |
|||
├── URLInputComponent.vue (~100 lines) |
|||
├── ImageCropperComponent.vue (~200 lines) |
|||
├── DiagnosticsPanelComponent.vue (~150 lines) |
|||
└── ImageUploadErrorHandler.vue (~100 lines) |
|||
``` |
|||
|
|||
**2. `GiftedDialog.vue` (670 lines) ⚠️ HIGH PRIORITY** |
|||
|
|||
**Assessment**: **GOOD** - Already partially refactored with step components extracted. |
|||
|
|||
**3. `PhotoDialog.vue` (669 lines) ⚠️ HIGH PRIORITY** |
|||
|
|||
**Issues**: Similar to ImageMethodDialog with significant code duplication. |
|||
|
|||
**4. `PushNotificationPermission.vue` (660 lines) ⚠️ HIGH PRIORITY** |
|||
|
|||
**Issues**: Complex permission logic with platform-specific code mixed together. |
|||
|
|||
### View Analysis (54 Views) |
|||
|
|||
#### View Size Distribution |
|||
``` |
|||
Large Views (>1000 lines): 9 views (16.7%) |
|||
├── AccountViewView.vue (2,215 lines) 🔴 CRITICAL |
|||
├── HomeView.vue (1,852 lines) ⚠️ HIGH PRIORITY |
|||
├── ProjectViewView.vue (1,479 lines) ⚠️ HIGH PRIORITY |
|||
├── DatabaseMigration.vue (1,438 lines) ⚠️ HIGH PRIORITY |
|||
├── ContactsView.vue (1,382 lines) ⚠️ HIGH PRIORITY |
|||
├── TestView.vue (1,259 lines) ⚠️ MODERATE PRIORITY |
|||
├── ClaimView.vue (1,225 lines) ⚠️ MODERATE PRIORITY |
|||
├── NewEditProjectView.vue (957 lines) ⚠️ MODERATE PRIORITY |
|||
└── ContactQRScanShowView.vue (929 lines) ⚠️ MODERATE PRIORITY |
|||
|
|||
Medium Views (500-1000 lines): 8 views (14.8%) |
|||
├── ConfirmGiftView.vue (898 lines) |
|||
├── DiscoverView.vue (888 lines) |
|||
├── DIDView.vue (848 lines) |
|||
├── GiftedDetailsView.vue (840 lines) |
|||
├── OfferDetailsView.vue (781 lines) |
|||
├── HelpView.vue (780 lines) |
|||
├── ProjectsView.vue (742 lines) |
|||
└── ContactQRScanFullView.vue (701 lines) |
|||
|
|||
Small Views (<500 lines): 37 views (68.5%) |
|||
├── OnboardMeetingSetupView.vue (687 lines) |
|||
├── ContactImportView.vue (568 lines) |
|||
├── HelpNotificationsView.vue (566 lines) |
|||
├── OnboardMeetingListView.vue (507 lines) |
|||
├── InviteOneView.vue (475 lines) |
|||
├── QuickActionBvcEndView.vue (442 lines) |
|||
├── ContactAmountsView.vue (416 lines) |
|||
├── SearchAreaView.vue (384 lines) |
|||
├── SharedPhotoView.vue (379 lines) |
|||
├── ContactGiftingView.vue (373 lines) |
|||
├── ContactEditView.vue (345 lines) |
|||
├── IdentitySwitcherView.vue (324 lines) |
|||
├── UserProfileView.vue (323 lines) |
|||
├── NewActivityView.vue (323 lines) |
|||
├── QuickActionBvcBeginView.vue (303 lines) |
|||
├── SeedBackupView.vue (292 lines) |
|||
├── InviteOneAcceptView.vue (292 lines) |
|||
├── ClaimCertificateView.vue (279 lines) |
|||
├── StartView.vue (271 lines) |
|||
├── ImportAccountView.vue (265 lines) |
|||
├── ClaimAddRawView.vue (249 lines) |
|||
├── OnboardMeetingMembersView.vue (247 lines) |
|||
├── DeepLinkErrorView.vue (239 lines) |
|||
├── ClaimReportCertificateView.vue (236 lines) |
|||
├── DeepLinkRedirectView.vue (219 lines) |
|||
├── ImportDerivedAccountView.vue (207 lines) |
|||
├── ShareMyContactInfoView.vue (196 lines) |
|||
├── RecentOffersToUserProjectsView.vue (176 lines) |
|||
├── RecentOffersToUserView.vue (166 lines) |
|||
├── NewEditAccountView.vue (142 lines) |
|||
├── StatisticsView.vue (133 lines) |
|||
├── HelpOnboardingView.vue (118 lines) |
|||
├── LogView.vue (104 lines) |
|||
├── NewIdentifierView.vue (97 lines) |
|||
├── HelpNotificationTypesView.vue (73 lines) |
|||
├── ConfirmContactView.vue (57 lines) |
|||
└── QuickActionBvcView.vue (54 lines) |
|||
``` |
|||
|
|||
#### Critical View Analysis |
|||
|
|||
**1. `AccountViewView.vue` (2,215 lines) 🔴 CRITICAL REFACTORING NEEDED** |
|||
|
|||
**Issues Identified:** |
|||
- **Monolithic Architecture**: Handles 7 distinct concerns in single file |
|||
- **Template Complexity**: ~750 lines of template with deeply nested conditions |
|||
- **Method Proliferation**: 50+ methods handling disparate concerns |
|||
- **State Management**: 25+ reactive properties without clear organization |
|||
|
|||
**Refactoring Strategy:** |
|||
```typescript |
|||
// Current monolithic structure |
|||
AccountViewView.vue (2,215 lines) { |
|||
ProfileSection: ~400 lines |
|||
SettingsSection: ~300 lines |
|||
NotificationSection: ~200 lines |
|||
ServerConfigSection: ~250 lines |
|||
ExportImportSection: ~300 lines |
|||
LimitsSection: ~150 lines |
|||
MapSection: ~200 lines |
|||
StateManagement: ~415 lines |
|||
} |
|||
|
|||
// Proposed component extraction |
|||
AccountViewView.vue (coordinator, ~400 lines) |
|||
├── ProfileManagementSection.vue (~300 lines) |
|||
├── ServerConfigurationSection.vue (~250 lines) |
|||
├── NotificationSettingsSection.vue (~200 lines) |
|||
├── DataExportImportSection.vue (~300 lines) |
|||
├── UsageLimitsDisplay.vue (~150 lines) |
|||
├── LocationProfileSection.vue (~200 lines) |
|||
└── AccountViewStateManager.ts (~200 lines) |
|||
``` |
|||
|
|||
**2. `HomeView.vue` (1,852 lines) ⚠️ HIGH PRIORITY** |
|||
|
|||
**Issues Identified:** |
|||
- **Multiple Concerns**: Activity feed, projects, contacts, notifications in one file |
|||
- **Complex State Management**: 20+ reactive properties with interdependencies |
|||
- **Mixed Lifecycle Logic**: Mount, update, and destroy logic intertwined |
|||
|
|||
**3. `ProjectViewView.vue` (1,479 lines) ⚠️ HIGH PRIORITY** |
|||
|
|||
**Issues Identified:** |
|||
- **Project Management Complexity**: Handles project details, members, offers, and activities |
|||
- **Mixed Concerns**: Project data, member management, and activity feed in single view |
|||
|
|||
### Vue Component Quality Patterns |
|||
|
|||
#### Excellent Patterns Found: |
|||
|
|||
**1. EntityIcon.vue (86 lines) ✅ EXCELLENT** |
|||
```typescript |
|||
// Clean, focused responsibility |
|||
@Component({ name: "EntityIcon" }) |
|||
export default class EntityIcon extends Vue { |
|||
@Prop() contact?: Contact; |
|||
@Prop({ default: "" }) entityId!: string; |
|||
@Prop({ default: 0 }) iconSize!: number; |
|||
|
|||
generateIcon(): string { |
|||
// Clear priority order: profile image → avatar → fallback |
|||
const imageUrl = this.contact?.profileImageUrl || this.profileImageUrl; |
|||
if (imageUrl) return `<img src="${imageUrl}" ... />`; |
|||
|
|||
const identifier = this.contact?.did || this.entityId; |
|||
if (!identifier) return `<img src="${blankSquareSvg}" ... />`; |
|||
|
|||
return createAvatar(avataaars, { seed: identifier, size: this.iconSize }).toString(); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**2. QuickNav.vue (118 lines) ✅ EXCELLENT** |
|||
```typescript |
|||
// Simple, focused navigation component |
|||
@Component({ name: "QuickNav" }) |
|||
export default class QuickNav extends Vue { |
|||
@Prop selected = ""; |
|||
|
|||
// Clean template with consistent patterns |
|||
// Proper accessibility attributes |
|||
// Responsive design with safe area handling |
|||
} |
|||
``` |
|||
|
|||
**3. Small Focused Views ✅ EXCELLENT** |
|||
```typescript |
|||
// QuickActionBvcView.vue (54 lines) - Perfect size |
|||
// ConfirmContactView.vue (57 lines) - Focused responsibility |
|||
// HelpNotificationTypesView.vue (73 lines) - Clear purpose |
|||
// LogView.vue (104 lines) - Simple utility view |
|||
``` |
|||
|
|||
#### Problematic Patterns Found: |
|||
|
|||
**1. Excessive Props in Dialog Components** |
|||
```typescript |
|||
// GiftedDialog.vue - Too many props |
|||
@Prop() fromProjectId = ""; |
|||
@Prop() toProjectId = ""; |
|||
@Prop() isFromProjectView = false; |
|||
@Prop() hideShowAll = false; |
|||
@Prop({ default: "person" }) giverEntityType = "person"; |
|||
@Prop({ default: "person" }) recipientEntityType = "person"; |
|||
// ... 10+ more props |
|||
``` |
|||
|
|||
**2. Complex State Machines** |
|||
```typescript |
|||
// ImageMethodDialog.vue - Complex state management |
|||
cameraState: "off" | "initializing" | "active" | "error" | "retrying" | "stopped" = "off"; |
|||
showCameraPreview = false; |
|||
isRetrying = false; |
|||
showDiagnostics = false; |
|||
// ... 15+ more state properties |
|||
``` |
|||
|
|||
**3. Excessive Reactive Properties** |
|||
```typescript |
|||
// AccountViewView.vue - Too many reactive properties |
|||
downloadUrl: string = ""; |
|||
loadingLimits: boolean = false; |
|||
loadingProfile: boolean = true; |
|||
showAdvanced: boolean = false; |
|||
showB64Copy: boolean = false; |
|||
showContactGives: boolean = false; |
|||
showDidCopy: boolean = false; |
|||
showDerCopy: boolean = false; |
|||
showGeneralAdvanced: boolean = false; |
|||
showLargeIdenticonId?: string; |
|||
showLargeIdenticonUrl?: string; |
|||
showPubCopy: boolean = false; |
|||
showShortcutBvc: boolean = false; |
|||
warnIfProdServer: boolean = false; |
|||
warnIfTestServer: boolean = false; |
|||
zoom: number = 2; |
|||
isMapReady: boolean = false; |
|||
// ... 10+ more properties |
|||
``` |
|||
|
|||
## File Size and Complexity Analysis (All Files) |
|||
|
|||
### Problematic Large Files |
|||
|
|||
#### 1. `AccountViewView.vue` (2,215 lines) 🔴 **CRITICAL** |
|||
**Issues Identified:** |
|||
- **Excessive Single File Responsibility**: Handles profile, settings, notifications, server configuration, export/import, limits checking |
|||
- **Template Complexity**: ~750 lines of template with deeply nested conditions |
|||
- **Method Proliferation**: 50+ methods handling disparate concerns |
|||
- **State Management**: 25+ reactive properties without clear organization |
|||
|
|||
#### 2. `PlatformServiceMixin.ts` (2,091 lines) ⚠️ **HIGH PRIORITY** |
|||
**Issues Identified:** |
|||
- **God Object Pattern**: Single file handling 80+ methods across multiple concerns |
|||
- **Mixed Abstraction Levels**: Low-level SQL utilities mixed with high-level business logic |
|||
- **Method Length Variance**: Some methods 100+ lines, others single-line wrappers |
|||
|
|||
**Refactoring Strategy:** |
|||
```typescript |
|||
// Current monolithic mixin |
|||
PlatformServiceMixin.ts (2,091 lines) |
|||
|
|||
// Proposed separation of concerns |
|||
├── CoreDatabaseMixin.ts // $db, $exec, $query, $first (200 lines) |
|||
├── SettingsManagementMixin.ts // $settings, $saveSettings (400 lines) |
|||
├── ContactManagementMixin.ts // $contacts, $insertContact (300 lines) |
|||
├── EntityOperationsMixin.ts // $insertEntity, $updateEntity (400 lines) |
|||
├── CachingMixin.ts // Cache management (150 lines) |
|||
├── ActiveIdentityMixin.ts // Active DID management (200 lines) |
|||
├── UtilityMixin.ts // Mapping, JSON parsing (200 lines) |
|||
└── LoggingMixin.ts // $log, $logError (100 lines) |
|||
``` |
|||
|
|||
#### 3. `HomeView.vue` (1,852 lines) ⚠️ **MODERATE PRIORITY** |
|||
**Issues Identified:** |
|||
- **Multiple Concerns**: Activity feed, projects, contacts, notifications in one file |
|||
- **Complex State Management**: 20+ reactive properties with interdependencies |
|||
- **Mixed Lifecycle Logic**: Mount, update, and destroy logic intertwined |
|||
|
|||
### File Size Distribution Analysis |
|||
``` |
|||
Files > 1000 lines: 9 files (4.6% of codebase) |
|||
Files 500-1000 lines: 23 files (11.7% of codebase) |
|||
Files 200-500 lines: 45 files (22.8% of codebase) |
|||
Files < 200 lines: 120 files (60.9% of codebase) |
|||
``` |
|||
|
|||
**Assessment**: Good distribution with most files reasonably sized, but critical outliers need attention. |
|||
|
|||
## Type Safety Analysis |
|||
|
|||
### Type Assertion Patterns |
|||
|
|||
#### "as any" Usage (62 total instances) ⚠️ |
|||
|
|||
**Vue Components & Views (41 instances):** |
|||
```typescript |
|||
// ImageMethodDialog.vue:504 |
|||
const activeIdentity = await (this as any).$getActiveIdentity(); |
|||
|
|||
// GiftedDialog.vue:228 |
|||
const activeIdentity = await (this as any).$getActiveIdentity(); |
|||
|
|||
// AccountViewView.vue: Multiple instances for: |
|||
// - PlatformServiceMixin method access |
|||
// - Vue refs with complex typing |
|||
// - External library integration (Leaflet) |
|||
``` |
|||
|
|||
**Other Files (21 instances):** |
|||
- **Vue Component References** (23 instances): `(this.$refs.dialog as any)` |
|||
- **Platform Detection** (12 instances): `(navigator as any).standalone` |
|||
- **External Library Integration** (15 instances): Leaflet, Axios extensions |
|||
- **Legacy Code Compatibility** (8 instances): Temporary migration code |
|||
- **Event Handler Workarounds** (4 instances): Vue event typing issues |
|||
|
|||
**Example Problematic Pattern:** |
|||
```typescript |
|||
// src/views/AccountViewView.vue:934 |
|||
const iconDefault = L.Icon.Default.prototype as unknown as Record<string, unknown>; |
|||
|
|||
// Better approach: |
|||
interface LeafletIconPrototype { |
|||
_getIconUrl?: unknown; |
|||
} |
|||
const iconDefault = L.Icon.Default.prototype as LeafletIconPrototype; |
|||
``` |
|||
|
|||
#### "unknown" Type Usage (755 instances) |
|||
**Analysis**: Generally good practice showing defensive programming, but some areas could benefit from more specific typing. |
|||
|
|||
### Recommended Type Safety Improvements |
|||
|
|||
1. **Create Interface Extensions**: |
|||
```typescript |
|||
// src/types/platform-service-mixin.ts |
|||
interface VueWithPlatformServiceMixin extends Vue { |
|||
$getActiveIdentity(): Promise<{ activeDid: string }>; |
|||
$saveSettings(changes: Partial<Settings>): Promise<boolean>; |
|||
// ... other methods |
|||
} |
|||
|
|||
// src/types/external.ts |
|||
declare global { |
|||
interface Navigator { |
|||
standalone?: boolean; |
|||
} |
|||
} |
|||
|
|||
interface VueRefWithOpen { |
|||
open: (callback: (result?: unknown) => void) => void; |
|||
} |
|||
``` |
|||
|
|||
2. **Component Ref Typing**: |
|||
```typescript |
|||
// Instead of: (this.$refs.dialog as any).open() |
|||
// Use: (this.$refs.dialog as VueRefWithOpen).open() |
|||
``` |
|||
|
|||
## Error Handling Consistency Analysis |
|||
|
|||
### Error Handling Patterns (367 catch blocks) |
|||
|
|||
#### Pattern Distribution: |
|||
1. **Structured Logging** (85%): Uses logger.error with context |
|||
2. **User Notification** (78%): Shows user-friendly error messages |
|||
3. **Graceful Degradation** (92%): Provides fallback behavior |
|||
4. **Error Propagation** (45%): Re-throws when appropriate |
|||
|
|||
#### Excellent Pattern Example: |
|||
```typescript |
|||
// src/views/AccountViewView.vue:1617 |
|||
try { |
|||
const response = await this.axios.delete(url, { headers }); |
|||
if (response.status === 204) { |
|||
this.profileImageUrl = ""; |
|||
this.notify.success("Image deleted successfully."); |
|||
} |
|||
} catch (error) { |
|||
if (isApiError(error) && error.response?.status === 404) { |
|||
// Graceful handling - image already gone |
|||
this.profileImageUrl = ""; |
|||
} else { |
|||
this.notify.error("Failed to delete image", TIMEOUTS.STANDARD); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### Areas for Improvement: |
|||
1. **Inconsistent Error Typing**: Some catch(error: any), others catch(error: unknown) |
|||
2. **Missing Error Boundaries**: No Vue error boundary components |
|||
3. **Silent Failures**: 15% of catch blocks don't notify users |
|||
|
|||
## Code Duplication Analysis |
|||
|
|||
### Significant Duplication Patterns |
|||
|
|||
#### 1. **Toggle Component Pattern** (12 occurrences) |
|||
```html |
|||
<!-- Repeated across multiple files --> |
|||
<div class="relative ml-2 cursor-pointer" @click="toggleMethod()"> |
|||
<input v-model="property" type="checkbox" class="sr-only" /> |
|||
<div class="block bg-slate-500 w-14 h-8 rounded-full"></div> |
|||
<div class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition"></div> |
|||
</div> |
|||
``` |
|||
|
|||
**Solution**: Create `ToggleSwitch.vue` component with props for value, label, and change handler. |
|||
|
|||
#### 2. **API Error Handling Pattern** (25 occurrences) |
|||
```typescript |
|||
try { |
|||
const response = await this.axios.post(url, data, { headers }); |
|||
if (response.status === 200) { |
|||
this.notify.success("Operation successful"); |
|||
} |
|||
} catch (error) { |
|||
if (isApiError(error)) { |
|||
this.notify.error(`Failed: ${error.message}`); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
**Solution**: Create `ApiRequestMixin.ts` with standardized request/response handling. |
|||
|
|||
#### 3. **Settings Update Pattern** (40+ occurrences) |
|||
```typescript |
|||
async methodName() { |
|||
await this.$saveSettings({ property: this.newValue }); |
|||
this.property = this.newValue; |
|||
} |
|||
``` |
|||
|
|||
**Solution**: Enhanced PlatformServiceMixin already provides `$saveSettings()` - migrate remaining manual patterns. |
|||
|
|||
## Dependency and Coupling Analysis |
|||
|
|||
### Import Dependency Patterns |
|||
|
|||
#### Legacy Database Coupling (EXCELLENT) |
|||
- **Status**: 99.5% resolved (1 remaining databaseUtil import) |
|||
- **Remaining**: `src/views/DeepLinkErrorView.vue:import { logConsoleAndDb }` |
|||
- **Resolution**: Replace with PlatformServiceMixin `$logAndConsole()` |
|||
|
|||
#### Circular Dependency Status (EXCELLENT) |
|||
- **Status**: 100% resolved, no active circular dependencies |
|||
- **Previous Issues**: All resolved through PlatformServiceMixin architecture |
|||
|
|||
#### Component Coupling Analysis |
|||
```typescript |
|||
// High coupling components (>10 imports) |
|||
AccountViewView.vue: 15 imports (understandable given scope) |
|||
HomeView.vue: 12 imports |
|||
ProjectViewView.vue: 11 imports |
|||
|
|||
// Well-isolated components (<5 imports) |
|||
QuickActionViews: 3-4 imports each |
|||
Component utilities: 2-3 imports each |
|||
``` |
|||
|
|||
**Assessment**: Reasonable coupling levels with clear architectural boundaries. |
|||
|
|||
## Console Logging Analysis (129 instances) |
|||
|
|||
### Logging Pattern Distribution: |
|||
1. **console.log**: 89 instances (69%) |
|||
2. **console.warn**: 24 instances (19%) |
|||
3. **console.error**: 16 instances (12%) |
|||
|
|||
### Vue Components & Views Logging (3 instances): |
|||
- **Components**: 1 console.* call |
|||
- **Views**: 2 console.* calls |
|||
|
|||
### Inconsistent Logging Approach: |
|||
```typescript |
|||
// Mixed patterns found: |
|||
console.log("Direct console logging"); // 89 instances |
|||
logger.debug("Structured logging"); // Preferred pattern |
|||
this.$logAndConsole("Mixin logging"); // PlatformServiceMixin |
|||
``` |
|||
|
|||
### Recommended Standardization: |
|||
1. **Migration Strategy**: Replace all console.* with logger.* calls |
|||
2. **Structured Context**: Add consistent metadata to log entries |
|||
3. **Log Levels**: Standardize debug/info/warn/error usage |
|||
|
|||
## Technical Debt Analysis (6 total) |
|||
|
|||
### Components (1 TODO): |
|||
```typescript |
|||
// PushNotificationPermission.vue |
|||
// TODO: secretDB functionality needs to be migrated to PlatformServiceMixin |
|||
``` |
|||
|
|||
### Views (2 TODOs): |
|||
```typescript |
|||
// AccountViewView.vue |
|||
// TODO: Implement this for SQLite |
|||
// TODO: implement this for SQLite |
|||
``` |
|||
|
|||
### Other Files (3 TODOs): |
|||
```typescript |
|||
// src/db/tables/accounts.ts |
|||
// TODO: When finished with migration, move these fields to Account and move identity and mnemonic here. |
|||
|
|||
// src/util.d.ts |
|||
// TODO: , inspect: inspect |
|||
|
|||
// src/libs/crypto/vc/passkeyHelpers.ts |
|||
// TODO: If it's after February 2025 when you read this then consider whether it still makes sense |
|||
``` |
|||
|
|||
**Assessment**: **EXCELLENT** - Only 6 TODO comments across 291 files. |
|||
|
|||
## Performance Anti-Patterns |
|||
|
|||
### Identified Issues: |
|||
|
|||
#### 1. **Excessive Reactive Properties** |
|||
```typescript |
|||
// AccountViewView.vue has 25+ reactive properties |
|||
// Many could be computed or moved to component state |
|||
``` |
|||
|
|||
#### 2. **Inline Method Calls in Templates** |
|||
```html |
|||
<!-- Anti-pattern: --> |
|||
<span>{{ readableDate(timeStr) }}</span> |
|||
|
|||
<!-- Better: --> |
|||
<span>{{ readableTime }}</span> |
|||
<!-- With computed property --> |
|||
``` |
|||
|
|||
#### 3. **Missing Key Attributes in Lists** |
|||
```html |
|||
<!-- Several v-for loops missing :key attributes --> |
|||
<li v-for="item in items"> |
|||
``` |
|||
|
|||
#### 4. **Complex Template Logic** |
|||
```html |
|||
<!-- AccountViewView.vue - Complex nested conditions --> |
|||
<div v-if="!activeDid" id="noticeBeforeShare" class="bg-amber-200..."> |
|||
<p class="mb-4"> |
|||
<b>Note:</b> Before you can share with others or take any action, you need an identifier. |
|||
</p> |
|||
<router-link :to="{ name: 'new-identifier' }" class="inline-block..."> |
|||
Create An Identifier |
|||
</router-link> |
|||
</div> |
|||
|
|||
<!-- Identity Details --> |
|||
<IdentitySection |
|||
:given-name="givenName" |
|||
:profile-image-url="profileImageUrl" |
|||
:active-did="activeDid" |
|||
:is-registered="isRegistered" |
|||
:show-large-identicon-id="showLargeIdenticonId" |
|||
:show-large-identicon-url="showLargeIdenticonUrl" |
|||
:show-did-copy="showDidCopy" |
|||
@edit-name="onEditName" |
|||
@show-qr-code="onShowQrCode" |
|||
@add-image="onAddImage" |
|||
@delete-image="onDeleteImage" |
|||
@show-large-identicon-id="onShowLargeIdenticonId" |
|||
@show-large-identicon-url="onShowLargeIdenticonUrl" |
|||
/> |
|||
``` |
|||
|
|||
## Specific Actionable Recommendations |
|||
|
|||
### Priority 1: Critical File Refactoring |
|||
|
|||
1. **Split AccountViewView.vue**: |
|||
- **Timeline**: 2-3 sprints |
|||
- **Strategy**: Extract 6 major sections into focused components |
|||
- **Risk**: Medium (requires careful state management coordination) |
|||
- **Benefit**: Massive maintainability improvement, easier testing |
|||
|
|||
2. **Decompose ImageMethodDialog.vue**: |
|||
- **Timeline**: 2-3 sprints |
|||
- **Strategy**: Extract 6 focused components (camera, file upload, cropping, etc.) |
|||
- **Risk**: Medium (complex camera state management) |
|||
- **Benefit**: Massive maintainability improvement |
|||
|
|||
3. **Decompose PlatformServiceMixin.ts**: |
|||
- **Timeline**: 1-2 sprints |
|||
- **Strategy**: Create focused mixins by concern area |
|||
- **Risk**: Low (well-defined interfaces already exist) |
|||
- **Benefit**: Better code organization, reduced cognitive load |
|||
|
|||
### Priority 2: Component Extraction |
|||
|
|||
1. **HomeView.vue** → 4 focused sections |
|||
- **Timeline**: 1-2 sprints |
|||
- **Risk**: Low (clear separation of concerns) |
|||
- **Benefit**: Better code organization |
|||
|
|||
2. **ProjectViewView.vue** → 4 focused sections |
|||
- **Timeline**: 1-2 sprints |
|||
- **Risk**: Low (well-defined boundaries) |
|||
- **Benefit**: Improved maintainability |
|||
|
|||
### Priority 3: Shared Component Creation |
|||
|
|||
1. **CameraPreviewComponent.vue** |
|||
- Extract from ImageMethodDialog.vue and PhotoDialog.vue |
|||
- **Benefit**: Eliminate code duplication |
|||
|
|||
2. **FileUploadComponent.vue** |
|||
- Extract from ImageMethodDialog.vue and PhotoDialog.vue |
|||
- **Benefit**: Consistent file handling |
|||
|
|||
3. **ToggleSwitch.vue** |
|||
- Replace 12 duplicate toggle patterns |
|||
- **Benefit**: Consistent UI components |
|||
|
|||
4. **DiagnosticsPanelComponent.vue** |
|||
- Extract from ImageMethodDialog.vue |
|||
- **Benefit**: Reusable debugging component |
|||
|
|||
### Priority 4: Type Safety Enhancement |
|||
|
|||
1. **Eliminate "as any" Assertions**: |
|||
- **Timeline**: 1 sprint |
|||
- **Strategy**: Create proper interface extensions |
|||
- **Risk**: Low |
|||
- **Benefit**: Better compile-time error detection |
|||
|
|||
2. **Standardize Error Typing**: |
|||
- **Timeline**: 0.5 sprint |
|||
- **Strategy**: Use consistent `catch (error: unknown)` pattern |
|||
- **Risk**: None |
|||
- **Benefit**: Better error handling consistency |
|||
|
|||
### Priority 5: State Management Optimization |
|||
|
|||
1. **Create Composables for Complex State**: |
|||
```typescript |
|||
// src/composables/useCameraState.ts |
|||
export function useCameraState() { |
|||
const cameraState = ref<CameraState>("off"); |
|||
const showPreview = ref(false); |
|||
const isRetrying = ref(false); |
|||
|
|||
const startCamera = async () => { /* ... */ }; |
|||
const stopCamera = () => { /* ... */ }; |
|||
|
|||
return { cameraState, showPreview, isRetrying, startCamera, stopCamera }; |
|||
} |
|||
``` |
|||
|
|||
2. **Group Related Reactive Properties**: |
|||
```typescript |
|||
// Instead of: |
|||
showB64Copy: boolean = false; |
|||
showDidCopy: boolean = false; |
|||
showDerCopy: boolean = false; |
|||
showPubCopy: boolean = false; |
|||
|
|||
// Use: |
|||
copyStates = { |
|||
b64: false, |
|||
did: false, |
|||
der: false, |
|||
pub: false |
|||
}; |
|||
``` |
|||
|
|||
### Priority 6: Code Standardization |
|||
|
|||
1. **Logging Standardization**: |
|||
- **Timeline**: 1 sprint |
|||
- **Strategy**: Replace all console.* with logger.* |
|||
- **Risk**: None |
|||
- **Benefit**: Consistent logging, better debugging |
|||
|
|||
2. **Template Optimization**: |
|||
- Add missing `:key` attributes |
|||
- Convert inline method calls to computed properties |
|||
- Implement virtual scrolling for large lists |
|||
|
|||
## Quality Metrics Summary |
|||
|
|||
### Vue Component Quality Distribution: |
|||
| Size Category | Count | Percentage | Quality Assessment | |
|||
|---------------|-------|------------|-------------------| |
|||
| Large (>500 lines) | 5 | 12.5% | 🔴 Needs Refactoring | |
|||
| Medium (200-500 lines) | 12 | 30% | 🟡 Good with Minor Issues | |
|||
| Small (<200 lines) | 23 | 57.5% | 🟢 Excellent | |
|||
|
|||
### Vue View Quality Distribution: |
|||
| Size Category | Count | Percentage | Quality Assessment | |
|||
|---------------|-------|------------|-------------------| |
|||
| Large (>1000 lines) | 9 | 16.7% | 🔴 Needs Refactoring | |
|||
| Medium (500-1000 lines) | 8 | 14.8% | 🟡 Good with Minor Issues | |
|||
| Small (<500 lines) | 37 | 68.5% | 🟢 Excellent | |
|||
|
|||
### Overall Quality Metrics: |
|||
| Metric | Components | Views | Overall Assessment | |
|||
|--------|------------|-------|-------------------| |
|||
| Technical Debt | 1 TODO | 2 TODOs | 🟢 Excellent | |
|||
| Type Safety | 6 "as any" | 35 "as any" | 🟡 Good | |
|||
| Console Logging | 1 instance | 2 instances | 🟢 Excellent | |
|||
| Architecture Consistency | 100% | 100% | 🟢 Excellent | |
|||
| Component Reuse | High | High | 🟢 Excellent | |
|||
|
|||
### Before vs. Target State: |
|||
| Metric | Current | Target | Status | |
|||
|--------|---------|---------|---------| |
|||
| Files >1000 lines | 9 files | 3 files | 🟡 Needs Work | |
|||
| "as any" assertions | 62 | 15 | 🟡 Moderate | |
|||
| Console.* calls | 129 | 0 | 🔴 Needs Work | |
|||
| Component reuse | 40% | 75% | 🟡 Moderate | |
|||
| Error consistency | 85% | 95% | 🟢 Good | |
|||
| Type coverage | 88% | 95% | 🟢 Good | |
|||
|
|||
## Risk Assessment |
|||
|
|||
### Low Risk Improvements (High Impact): |
|||
- Logging standardization |
|||
- Type assertion cleanup |
|||
- Missing key attributes |
|||
- Component extraction from AccountViewView.vue |
|||
- Shared component creation (ToggleSwitch, CameraPreview) |
|||
|
|||
### Medium Risk Improvements: |
|||
- PlatformServiceMixin decomposition |
|||
- State management optimization |
|||
- ImageMethodDialog decomposition |
|||
|
|||
### High Risk Items: |
|||
- None identified - project demonstrates excellent architectural discipline |
|||
|
|||
## Conclusion |
|||
|
|||
The TimeSafari codebase demonstrates **exceptional code quality** with: |
|||
|
|||
**Key Strengths:** |
|||
- **Consistent Architecture**: 100% Vue 3 Composition API with TypeScript |
|||
- **Minimal Technical Debt**: Only 6 TODO comments across 291 files |
|||
- **Excellent Small Components**: 68.5% of views and 57.5% of components are well-sized |
|||
- **Strong Type Safety**: Minimal "as any" usage, mostly justified |
|||
- **Clean Logging**: Minimal console.* usage, structured logging preferred |
|||
- **Excellent Database Migration**: 99.5% complete |
|||
- **Comprehensive Error Handling**: 367 catch blocks with good coverage |
|||
- **No Circular Dependencies**: 100% resolved |
|||
|
|||
**Primary Focus Areas:** |
|||
1. **Decompose Large Files**: 5 components and 9 views need refactoring |
|||
2. **Extract Shared Components**: Camera, file upload, and diagnostics components |
|||
3. **Optimize State Management**: Group related properties and create composables |
|||
4. **Improve Type Safety**: Create proper interface extensions for mixin methods |
|||
5. **Logging Standardization**: Replace 129 console.* calls with structured logger.* |
|||
|
|||
**The component architecture is production-ready** with these improvements representing **strategic optimization** rather than critical fixes. The codebase demonstrates **mature Vue.js development practices** with excellent separation of concerns and consistent patterns. |
|||
|
|||
--- |
|||
|
|||
**Investigation Methodology:** |
|||
- Static analysis of 291 source files (197 general + 94 Vue components/views) |
|||
- Pattern recognition across 104,527 lines of code |
|||
- Manual review of large files and complexity patterns |
|||
- Dependency analysis and coupling assessment |
|||
- Performance anti-pattern identification |
|||
- Architecture consistency evaluation |
@ -0,0 +1,655 @@ |
|||
# Android Emulator Deployment Guide (No Android Studio) |
|||
|
|||
**Author**: Matthew Raymer |
|||
**Date**: 2025-01-27 |
|||
**Status**: 🎯 **ACTIVE** - Complete guide for deploying TimeSafari to Android emulator using command-line tools |
|||
|
|||
## Overview |
|||
|
|||
This guide provides comprehensive instructions for building and deploying TimeSafari to Android emulators using only command-line tools, without requiring Android Studio. It leverages the existing build system and adds emulator-specific deployment workflows. |
|||
|
|||
## Prerequisites |
|||
|
|||
### Required Tools |
|||
|
|||
1. **Android SDK Command Line Tools** |
|||
```bash |
|||
# Install via package manager (Arch Linux) |
|||
sudo pacman -S android-sdk-cmdline-tools-latest |
|||
|
|||
# Or download from Google |
|||
# https://developer.android.com/studio/command-line |
|||
``` |
|||
|
|||
2. **Android SDK Platform Tools** |
|||
```bash |
|||
# Install via package manager |
|||
sudo pacman -S android-sdk-platform-tools |
|||
|
|||
# Or via Android SDK Manager |
|||
sdkmanager "platform-tools" |
|||
``` |
|||
|
|||
3. **Android SDK Build Tools** |
|||
```bash |
|||
sdkmanager "build-tools;34.0.0" |
|||
``` |
|||
|
|||
4. **Android Platform** |
|||
```bash |
|||
sdkmanager "platforms;android-34" |
|||
``` |
|||
|
|||
5. **Android Emulator** |
|||
```bash |
|||
sdkmanager "emulator" |
|||
``` |
|||
|
|||
6. **System Images** |
|||
```bash |
|||
# For API 34 (Android 14) |
|||
sdkmanager "system-images;android-34;google_apis;x86_64" |
|||
|
|||
# For API 33 (Android 13) - alternative |
|||
sdkmanager "system-images;android-33;google_apis;x86_64" |
|||
``` |
|||
|
|||
### Environment Setup |
|||
|
|||
```bash |
|||
# Add to ~/.bashrc or ~/.zshrc |
|||
export ANDROID_HOME=$HOME/Android/Sdk |
|||
export ANDROID_AVD_HOME=$HOME/.android/avd # Important for AVD location |
|||
export PATH=$PATH:$ANDROID_HOME/emulator |
|||
export PATH=$PATH:$ANDROID_HOME/platform-tools |
|||
export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin |
|||
export PATH=$PATH:$ANDROID_HOME/build-tools/34.0.0 |
|||
|
|||
# Reload shell |
|||
source ~/.bashrc |
|||
``` |
|||
|
|||
### Verify Installation |
|||
|
|||
```bash |
|||
# Check all tools are available |
|||
adb version |
|||
emulator -version |
|||
avdmanager list |
|||
``` |
|||
|
|||
## Resource-Aware Emulator Setup |
|||
|
|||
### ⚡ **Quick Start Recommendation** |
|||
|
|||
**For best results, always start with resource analysis:** |
|||
|
|||
```bash |
|||
# 1. Check your system capabilities |
|||
./scripts/avd-resource-checker.sh |
|||
|
|||
# 2. Use the generated optimal startup script |
|||
/tmp/start-avd-TimeSafari_Emulator.sh |
|||
|
|||
# 3. Deploy your app |
|||
npm run build:android:dev |
|||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk |
|||
``` |
|||
|
|||
This prevents system lockups and ensures optimal performance. |
|||
|
|||
### AVD Resource Checker Script |
|||
|
|||
**New Feature**: TimeSafari includes an intelligent resource checker that automatically detects your system capabilities and recommends optimal AVD configurations. |
|||
|
|||
```bash |
|||
# Check system resources and get recommendations |
|||
./scripts/avd-resource-checker.sh |
|||
|
|||
# Check resources for specific AVD |
|||
./scripts/avd-resource-checker.sh TimeSafari_Emulator |
|||
|
|||
# Test AVD startup performance |
|||
./scripts/avd-resource-checker.sh TimeSafari_Emulator --test |
|||
|
|||
# Create optimized AVD with recommended settings |
|||
./scripts/avd-resource-checker.sh TimeSafari_Emulator --create |
|||
``` |
|||
|
|||
**What the script analyzes:** |
|||
- **System Memory**: Total and available RAM |
|||
- **CPU Cores**: Available processing power |
|||
- **GPU Capabilities**: NVIDIA, AMD, Intel, or software rendering |
|||
- **Hardware Acceleration**: Optimal graphics settings |
|||
|
|||
**What it generates:** |
|||
- **Optimal configuration**: Memory, cores, and GPU settings |
|||
- **Startup command**: Ready-to-use emulator command |
|||
- **Startup script**: Saved to `/tmp/start-avd-{name}.sh` for reuse |
|||
|
|||
## Emulator Management |
|||
|
|||
### Create Android Virtual Device (AVD) |
|||
|
|||
```bash |
|||
# List available system images |
|||
avdmanager list target |
|||
|
|||
# Create AVD for API 34 |
|||
avdmanager create avd \ |
|||
--name "TimeSafari_Emulator" \ |
|||
--package "system-images;android-34;google_apis;x86_64" \ |
|||
--device "pixel_7" |
|||
|
|||
# List created AVDs |
|||
avdmanager list avd |
|||
``` |
|||
|
|||
### Start Emulator |
|||
|
|||
```bash |
|||
# Start emulator with hardware acceleration (recommended) |
|||
emulator -avd TimeSafari_Emulator -gpu host -no-audio & |
|||
|
|||
# Start with reduced resources (if system has limited RAM) |
|||
emulator -avd TimeSafari_Emulator \ |
|||
-no-audio \ |
|||
-memory 2048 \ |
|||
-cores 2 \ |
|||
-gpu swiftshader_indirect & |
|||
|
|||
# Start with minimal resources (safest for low-end systems) |
|||
emulator -avd TimeSafari_Emulator \ |
|||
-no-audio \ |
|||
-memory 1536 \ |
|||
-cores 1 \ |
|||
-gpu swiftshader_indirect & |
|||
|
|||
# Check if emulator is running |
|||
adb devices |
|||
``` |
|||
|
|||
### Resource Management |
|||
|
|||
**Important**: Android emulators can consume significant system resources. Choose the appropriate configuration based on your system: |
|||
|
|||
- **High-end systems** (16GB+ RAM, dedicated GPU): Use `-gpu host` |
|||
- **Mid-range systems** (8-16GB RAM): Use `-memory 2048 -cores 2` |
|||
- **Low-end systems** (4-8GB RAM): Use `-memory 1536 -cores 1 -gpu swiftshader_indirect` |
|||
|
|||
### Emulator Control |
|||
|
|||
```bash |
|||
# Stop emulator |
|||
adb emu kill |
|||
|
|||
# Restart emulator |
|||
adb reboot |
|||
|
|||
# Check emulator status |
|||
adb get-state |
|||
``` |
|||
|
|||
## Build and Deploy Workflow |
|||
|
|||
### Method 1: Using Existing Build Scripts |
|||
|
|||
The TimeSafari project already has comprehensive Android build scripts that can be adapted for emulator deployment: |
|||
|
|||
```bash |
|||
# Development build with auto-run |
|||
npm run build:android:dev:run |
|||
|
|||
# Test build with auto-run |
|||
npm run build:android:test:run |
|||
|
|||
# Production build with auto-run |
|||
npm run build:android:prod:run |
|||
``` |
|||
|
|||
### Method 2: Custom Emulator Deployment Script |
|||
|
|||
Create a new script specifically for emulator deployment: |
|||
|
|||
```bash |
|||
# Create emulator deployment script |
|||
cat > scripts/deploy-android-emulator.sh << 'EOF' |
|||
#!/bin/bash |
|||
# deploy-android-emulator.sh |
|||
# Author: Matthew Raymer |
|||
# Date: 2025-01-27 |
|||
# Description: Deploy TimeSafari to Android emulator without Android Studio |
|||
|
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Default values |
|||
BUILD_MODE="development" |
|||
AVD_NAME="TimeSafari_Emulator" |
|||
START_EMULATOR=true |
|||
CLEAN_BUILD=true |
|||
|
|||
# Parse command line arguments |
|||
while [[ $# -gt 0 ]]; do |
|||
case $1 in |
|||
--dev|--development) |
|||
BUILD_MODE="development" |
|||
shift |
|||
;; |
|||
--test) |
|||
BUILD_MODE="test" |
|||
shift |
|||
;; |
|||
--prod|--production) |
|||
BUILD_MODE="production" |
|||
shift |
|||
;; |
|||
--avd) |
|||
AVD_NAME="$2" |
|||
shift 2 |
|||
;; |
|||
--no-start-emulator) |
|||
START_EMULATOR=false |
|||
shift |
|||
;; |
|||
--no-clean) |
|||
CLEAN_BUILD=false |
|||
shift |
|||
;; |
|||
-h|--help) |
|||
echo "Usage: $0 [options]" |
|||
echo "Options:" |
|||
echo " --dev, --development Build for development" |
|||
echo " --test Build for testing" |
|||
echo " --prod, --production Build for production" |
|||
echo " --avd NAME Use specific AVD name" |
|||
echo " --no-start-emulator Don't start emulator" |
|||
echo " --no-clean Skip clean build" |
|||
echo " -h, --help Show this help" |
|||
exit 0 |
|||
;; |
|||
*) |
|||
log_error "Unknown option: $1" |
|||
exit 1 |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
# Function to check if emulator is running |
|||
check_emulator_running() { |
|||
if adb devices | grep -q "emulator.*device"; then |
|||
return 0 |
|||
else |
|||
return 1 |
|||
fi |
|||
} |
|||
|
|||
# Function to start emulator |
|||
start_emulator() { |
|||
log_info "Starting Android emulator: $AVD_NAME" |
|||
|
|||
# Check if AVD exists |
|||
if ! avdmanager list avd | grep -q "$AVD_NAME"; then |
|||
log_error "AVD '$AVD_NAME' not found. Please create it first." |
|||
log_info "Create AVD with: avdmanager create avd --name $AVD_NAME --package system-images;android-34;google_apis;x86_64" |
|||
exit 1 |
|||
fi |
|||
|
|||
# Start emulator in background |
|||
emulator -avd "$AVD_NAME" -no-audio -no-snapshot & |
|||
EMULATOR_PID=$! |
|||
|
|||
# Wait for emulator to boot |
|||
log_info "Waiting for emulator to boot..." |
|||
adb wait-for-device |
|||
|
|||
# Wait for boot to complete |
|||
log_info "Waiting for boot to complete..." |
|||
while [ "$(adb shell getprop sys.boot_completed)" != "1" ]; do |
|||
sleep 2 |
|||
done |
|||
|
|||
log_success "Emulator is ready!" |
|||
} |
|||
|
|||
# Function to build and deploy |
|||
build_and_deploy() { |
|||
log_info "Building TimeSafari for $BUILD_MODE mode..." |
|||
|
|||
# Clean build if requested |
|||
if [ "$CLEAN_BUILD" = true ]; then |
|||
log_info "Cleaning previous build..." |
|||
npm run clean:android |
|||
fi |
|||
|
|||
# Build based on mode |
|||
case $BUILD_MODE in |
|||
"development") |
|||
npm run build:android:dev |
|||
;; |
|||
"test") |
|||
npm run build:android:test |
|||
;; |
|||
"production") |
|||
npm run build:android:prod |
|||
;; |
|||
esac |
|||
|
|||
# Deploy to emulator |
|||
log_info "Deploying to emulator..." |
|||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk |
|||
|
|||
# Launch app |
|||
log_info "Launching TimeSafari..." |
|||
adb shell am start -n app.timesafari/.MainActivity |
|||
|
|||
log_success "TimeSafari deployed and launched successfully!" |
|||
} |
|||
|
|||
# Main execution |
|||
main() { |
|||
log_info "TimeSafari Android Emulator Deployment" |
|||
log_info "Build Mode: $BUILD_MODE" |
|||
log_info "AVD Name: $AVD_NAME" |
|||
|
|||
# Start emulator if requested and not running |
|||
if [ "$START_EMULATOR" = true ]; then |
|||
if ! check_emulator_running; then |
|||
start_emulator |
|||
else |
|||
log_info "Emulator already running" |
|||
fi |
|||
fi |
|||
|
|||
# Build and deploy |
|||
build_and_deploy |
|||
|
|||
log_success "Deployment completed successfully!" |
|||
} |
|||
|
|||
# Run main function |
|||
main "$@" |
|||
EOF |
|||
|
|||
# Make script executable |
|||
chmod +x scripts/deploy-android-emulator.sh |
|||
``` |
|||
|
|||
### Method 3: Direct Command Line Deployment |
|||
|
|||
For quick deployments without scripts: |
|||
|
|||
```bash |
|||
# 1. Ensure emulator is running |
|||
adb devices |
|||
|
|||
# 2. Build the app |
|||
npm run build:android:dev |
|||
|
|||
# 3. Install APK |
|||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk |
|||
|
|||
# 4. Launch app |
|||
adb shell am start -n app.timesafari/.MainActivity |
|||
|
|||
# 5. View logs |
|||
adb logcat | grep -E "(TimeSafari|Capacitor|MainActivity)" |
|||
``` |
|||
|
|||
## Advanced Deployment Options |
|||
|
|||
### Custom API Server Configuration |
|||
|
|||
For development with custom API endpoints: |
|||
|
|||
```bash |
|||
# Build with custom API IP |
|||
npm run build:android:dev:custom |
|||
|
|||
# Or modify capacitor.config.ts for specific IP |
|||
# Then build normally |
|||
npm run build:android:dev |
|||
``` |
|||
|
|||
### Debug vs Release Builds |
|||
|
|||
```bash |
|||
# Debug build (default) |
|||
npm run build:android:debug |
|||
|
|||
# Release build |
|||
npm run build:android:release |
|||
|
|||
# Install specific build |
|||
adb install -r android/app/build/outputs/apk/release/app-release.apk |
|||
``` |
|||
|
|||
### Asset Management |
|||
|
|||
```bash |
|||
# Validate Android assets |
|||
npm run assets:validate:android |
|||
|
|||
# Generate assets only |
|||
npm run build:android:assets |
|||
|
|||
# Clean assets |
|||
npm run assets:clean |
|||
``` |
|||
|
|||
## Troubleshooting |
|||
|
|||
### Common Issues |
|||
|
|||
1. **Emulator Not Starting / AVD Not Found** |
|||
```bash |
|||
# Check available AVDs |
|||
avdmanager list avd |
|||
|
|||
# If AVD exists but emulator can't find it, check AVD location |
|||
echo $ANDROID_AVD_HOME |
|||
ls -la ~/.android/avd/ |
|||
|
|||
# Fix AVD path issue (common on Arch Linux) |
|||
export ANDROID_AVD_HOME=/home/$USER/.config/.android/avd |
|||
|
|||
# Or create symlinks if AVDs are in different location |
|||
mkdir -p ~/.android/avd |
|||
ln -s /home/$USER/.config/.android/avd/* ~/.android/avd/ |
|||
|
|||
# Create new AVD if needed |
|||
avdmanager create avd --name "TimeSafari_Emulator" --package "system-images;android-34;google_apis;x86_64" |
|||
|
|||
# Check emulator logs |
|||
emulator -avd TimeSafari_Emulator -verbose |
|||
``` |
|||
|
|||
2. **System Lockup / High Resource Usage** |
|||
```bash |
|||
# Kill any stuck emulator processes |
|||
pkill -f emulator |
|||
|
|||
# Check system resources |
|||
free -h |
|||
nvidia-smi # if using NVIDIA GPU |
|||
|
|||
# Start with minimal resources |
|||
emulator -avd TimeSafari_Emulator \ |
|||
-no-audio \ |
|||
-memory 1536 \ |
|||
-cores 1 \ |
|||
-gpu swiftshader_indirect & |
|||
|
|||
# Monitor resource usage |
|||
htop |
|||
|
|||
# If still having issues, try software rendering only |
|||
emulator -avd TimeSafari_Emulator \ |
|||
-no-audio \ |
|||
-no-snapshot \ |
|||
-memory 1024 \ |
|||
-cores 1 \ |
|||
-gpu off & |
|||
``` |
|||
|
|||
3. **ADB Device Not Found** |
|||
```bash |
|||
# Restart ADB server |
|||
adb kill-server |
|||
adb start-server |
|||
|
|||
# Check devices |
|||
adb devices |
|||
|
|||
# Check emulator status |
|||
adb get-state |
|||
``` |
|||
|
|||
3. **Build Failures** |
|||
```bash |
|||
# Clean everything |
|||
npm run clean:android |
|||
|
|||
# Rebuild |
|||
npm run build:android:dev |
|||
|
|||
# Check Gradle logs |
|||
cd android && ./gradlew clean --stacktrace |
|||
``` |
|||
|
|||
4. **Installation Failures** |
|||
```bash |
|||
# Uninstall existing app |
|||
adb uninstall app.timesafari |
|||
|
|||
# Reinstall |
|||
adb install android/app/build/outputs/apk/debug/app-debug.apk |
|||
|
|||
# Check package info |
|||
adb shell pm list packages | grep timesafari |
|||
``` |
|||
|
|||
### Performance Optimization |
|||
|
|||
1. **Emulator Performance** |
|||
```bash |
|||
# Start with hardware acceleration |
|||
emulator -avd TimeSafari_Emulator -gpu host |
|||
|
|||
# Use snapshot for faster startup |
|||
emulator -avd TimeSafari_Emulator -snapshot default |
|||
|
|||
# Allocate more RAM |
|||
emulator -avd TimeSafari_Emulator -memory 4096 |
|||
``` |
|||
|
|||
2. **Build Performance** |
|||
```bash |
|||
# Use Gradle daemon |
|||
echo "org.gradle.daemon=true" >> android/gradle.properties |
|||
|
|||
# Increase heap size |
|||
echo "org.gradle.jvmargs=-Xmx4g" >> android/gradle.properties |
|||
|
|||
# Enable parallel builds |
|||
echo "org.gradle.parallel=true" >> android/gradle.properties |
|||
``` |
|||
|
|||
## Integration with Existing Build System |
|||
|
|||
### NPM Scripts Integration |
|||
|
|||
Add emulator-specific scripts to `package.json`: |
|||
|
|||
```json |
|||
{ |
|||
"scripts": { |
|||
"emulator:check": "./scripts/avd-resource-checker.sh", |
|||
"emulator:check:test": "./scripts/avd-resource-checker.sh TimeSafari_Emulator --test", |
|||
"emulator:check:create": "./scripts/avd-resource-checker.sh TimeSafari_Emulator --create", |
|||
"emulator:start": "emulator -avd TimeSafari_Emulator -no-audio &", |
|||
"emulator:start:optimized": "/tmp/start-avd-TimeSafari_Emulator.sh", |
|||
"emulator:stop": "adb emu kill", |
|||
"emulator:deploy": "./scripts/deploy-android-emulator.sh", |
|||
"emulator:deploy:dev": "./scripts/deploy-android-emulator.sh --dev", |
|||
"emulator:deploy:test": "./scripts/deploy-android-emulator.sh --test", |
|||
"emulator:deploy:prod": "./scripts/deploy-android-emulator.sh --prod", |
|||
"emulator:logs": "adb logcat | grep -E '(TimeSafari|Capacitor|MainActivity)'", |
|||
"emulator:shell": "adb shell" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### CI/CD Integration |
|||
|
|||
For automated testing and deployment: |
|||
|
|||
```bash |
|||
# GitHub Actions example |
|||
- name: Start Android Emulator |
|||
run: | |
|||
emulator -avd TimeSafari_Emulator -no-audio -no-snapshot & |
|||
adb wait-for-device |
|||
adb shell getprop sys.boot_completed |
|||
|
|||
- name: Build and Deploy |
|||
run: | |
|||
npm run build:android:test |
|||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk |
|||
adb shell am start -n app.timesafari/.MainActivity |
|||
|
|||
- name: Run Tests |
|||
run: | |
|||
npm run test:android |
|||
``` |
|||
|
|||
## Best Practices |
|||
|
|||
### Development Workflow |
|||
|
|||
1. **Start emulator once per session** |
|||
```bash |
|||
emulator -avd TimeSafari_Emulator -no-audio & |
|||
``` |
|||
|
|||
2. **Use incremental builds** |
|||
```bash |
|||
# For rapid iteration |
|||
npm run build:android:sync |
|||
adb install -r android/app/build/outputs/apk/debug/app-debug.apk |
|||
``` |
|||
|
|||
3. **Monitor logs continuously** |
|||
```bash |
|||
adb logcat | grep -E "(TimeSafari|Capacitor|MainActivity)" --color=always |
|||
``` |
|||
|
|||
### Performance Tips |
|||
|
|||
1. **Use snapshots for faster startup** |
|||
2. **Enable hardware acceleration** |
|||
3. **Allocate sufficient RAM (4GB+)** |
|||
4. **Use SSD storage for AVDs** |
|||
5. **Close unnecessary applications** |
|||
|
|||
### Security Considerations |
|||
|
|||
1. **Use debug builds for development only** |
|||
2. **Never commit debug keystores** |
|||
3. **Use release builds for testing** |
|||
4. **Validate API endpoints in production builds** |
|||
|
|||
## Conclusion |
|||
|
|||
This guide provides a complete solution for deploying TimeSafari to Android emulators without Android Studio. The approach leverages the existing build system while adding emulator-specific deployment capabilities. |
|||
|
|||
The key benefits: |
|||
- ✅ **No Android Studio required** |
|||
- ✅ **Command-line only workflow** |
|||
- ✅ **Integration with existing build scripts** |
|||
- ✅ **Automated deployment options** |
|||
- ✅ **Comprehensive troubleshooting guide** |
|||
|
|||
For questions or issues, refer to the troubleshooting section or check the existing build documentation in `BUILDING.md`. |
@ -0,0 +1,46 @@ |
|||
{ |
|||
"icons": [ |
|||
{ |
|||
"src": "../icons/icon-48.webp", |
|||
"type": "image/png", |
|||
"sizes": "48x48", |
|||
"purpose": "any maskable" |
|||
}, |
|||
{ |
|||
"src": "../icons/icon-72.webp", |
|||
"type": "image/png", |
|||
"sizes": "72x72", |
|||
"purpose": "any maskable" |
|||
}, |
|||
{ |
|||
"src": "../icons/icon-96.webp", |
|||
"type": "image/png", |
|||
"sizes": "96x96", |
|||
"purpose": "any maskable" |
|||
}, |
|||
{ |
|||
"src": "../icons/icon-128.webp", |
|||
"type": "image/png", |
|||
"sizes": "128x128", |
|||
"purpose": "any maskable" |
|||
}, |
|||
{ |
|||
"src": "../icons/icon-192.webp", |
|||
"type": "image/png", |
|||
"sizes": "192x192", |
|||
"purpose": "any maskable" |
|||
}, |
|||
{ |
|||
"src": "../icons/icon-256.webp", |
|||
"type": "image/png", |
|||
"sizes": "256x256", |
|||
"purpose": "any maskable" |
|||
}, |
|||
{ |
|||
"src": "../icons/icon-512.webp", |
|||
"type": "image/png", |
|||
"sizes": "512x512", |
|||
"purpose": "any maskable" |
|||
} |
|||
] |
|||
} |
@ -0,0 +1,389 @@ |
|||
#!/bin/bash |
|||
# avd-resource-checker.sh |
|||
# Author: Matthew Raymer |
|||
# Date: 2025-01-27 |
|||
# Description: Check system resources and recommend optimal AVD configuration |
|||
|
|||
set -e |
|||
|
|||
# Source common utilities |
|||
source "$(dirname "$0")/common.sh" |
|||
|
|||
# Colors for output |
|||
RED_COLOR='\033[0;31m' |
|||
GREEN_COLOR='\033[0;32m' |
|||
YELLOW_COLOR='\033[1;33m' |
|||
BLUE_COLOR='\033[0;34m' |
|||
NC_COLOR='\033[0m' # No Color |
|||
|
|||
# Function to print colored output |
|||
print_status() { |
|||
local color=$1 |
|||
local message=$2 |
|||
echo -e "${color}${message}${NC_COLOR}" |
|||
} |
|||
|
|||
# Function to get system memory in MB |
|||
get_system_memory() { |
|||
if command -v free >/dev/null 2>&1; then |
|||
free -m | awk 'NR==2{print $2}' |
|||
else |
|||
echo "0" |
|||
fi |
|||
} |
|||
|
|||
# Function to get available memory in MB |
|||
get_available_memory() { |
|||
if command -v free >/dev/null 2>&1; then |
|||
free -m | awk 'NR==2{print $7}' |
|||
else |
|||
echo "0" |
|||
fi |
|||
} |
|||
|
|||
# Function to get CPU core count |
|||
get_cpu_cores() { |
|||
if command -v nproc >/dev/null 2>&1; then |
|||
nproc |
|||
elif [ -f /proc/cpuinfo ]; then |
|||
grep -c ^processor /proc/cpuinfo |
|||
else |
|||
echo "1" |
|||
fi |
|||
} |
|||
|
|||
# Function to check GPU capabilities |
|||
check_gpu_capabilities() { |
|||
local gpu_type="unknown" |
|||
local gpu_memory="0" |
|||
|
|||
# Check for NVIDIA GPU |
|||
if command -v nvidia-smi >/dev/null 2>&1; then |
|||
gpu_type="nvidia" |
|||
gpu_memory=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1 || echo "0") |
|||
print_status $GREEN_COLOR "✓ NVIDIA GPU detected (${gpu_memory}MB VRAM)" |
|||
return 0 |
|||
fi |
|||
|
|||
# Check for AMD GPU |
|||
if command -v rocm-smi >/dev/null 2>&1; then |
|||
gpu_type="amd" |
|||
print_status $GREEN_COLOR "✓ AMD GPU detected" |
|||
return 0 |
|||
fi |
|||
|
|||
# Check for Intel GPU |
|||
if lspci 2>/dev/null | grep -i "vga.*intel" >/dev/null; then |
|||
gpu_type="intel" |
|||
print_status $YELLOW_COLOR "✓ Intel integrated GPU detected" |
|||
return 1 |
|||
fi |
|||
|
|||
# Check for generic GPU |
|||
if lspci 2>/dev/null | grep -i "vga" >/dev/null; then |
|||
gpu_type="generic" |
|||
print_status $YELLOW_COLOR "✓ Generic GPU detected" |
|||
return 1 |
|||
fi |
|||
|
|||
print_status $RED_COLOR "✗ No GPU detected" |
|||
return 2 |
|||
} |
|||
|
|||
# Function to check if hardware acceleration is available |
|||
check_hardware_acceleration() { |
|||
local gpu_capable=$1 |
|||
|
|||
if [ $gpu_capable -eq 0 ]; then |
|||
print_status $GREEN_COLOR "✓ Hardware acceleration recommended" |
|||
return 0 |
|||
elif [ $gpu_capable -eq 1 ]; then |
|||
print_status $YELLOW_COLOR "⚠ Limited hardware acceleration" |
|||
return 1 |
|||
else |
|||
print_status $RED_COLOR "✗ No hardware acceleration available" |
|||
return 2 |
|||
fi |
|||
} |
|||
|
|||
# Function to recommend AVD configuration |
|||
recommend_avd_config() { |
|||
local total_memory=$1 |
|||
local available_memory=$2 |
|||
local cpu_cores=$3 |
|||
local gpu_capable=$4 |
|||
|
|||
print_status $BLUE_COLOR "\n=== AVD Configuration Recommendation ===" |
|||
|
|||
# Calculate recommended memory (leave 2GB for system) |
|||
local system_reserve=2048 |
|||
local recommended_memory=$((available_memory - system_reserve)) |
|||
|
|||
# Cap memory at reasonable limits |
|||
if [ $recommended_memory -gt 4096 ]; then |
|||
recommended_memory=4096 |
|||
elif [ $recommended_memory -lt 1024 ]; then |
|||
recommended_memory=1024 |
|||
fi |
|||
|
|||
# Calculate recommended cores (leave 2 cores for system) |
|||
local recommended_cores=$((cpu_cores - 2)) |
|||
if [ $recommended_cores -lt 1 ]; then |
|||
recommended_cores=1 |
|||
elif [ $recommended_cores -gt 4 ]; then |
|||
recommended_cores=4 |
|||
fi |
|||
|
|||
# Determine GPU setting |
|||
local gpu_setting="" |
|||
case $gpu_capable in |
|||
0) gpu_setting="-gpu host" ;; |
|||
1) gpu_setting="-gpu swiftshader_indirect" ;; |
|||
2) gpu_setting="-gpu swiftshader_indirect" ;; |
|||
esac |
|||
|
|||
# Generate recommendation |
|||
print_status $GREEN_COLOR "Recommended AVD Configuration:" |
|||
echo " Memory: ${recommended_memory}MB" |
|||
echo " Cores: ${recommended_cores}" |
|||
echo " GPU: ${gpu_setting}" |
|||
|
|||
# Get AVD name from function parameter (passed from main) |
|||
local avd_name=$5 |
|||
local command="emulator -avd ${avd_name} -no-audio -memory ${recommended_memory} -cores ${recommended_cores} ${gpu_setting} &" |
|||
|
|||
print_status $BLUE_COLOR "\nGenerated Command:" |
|||
echo " ${command}" |
|||
|
|||
# Save to file for easy execution |
|||
local script_file="/tmp/start-avd-${avd_name}.sh" |
|||
cat > "$script_file" << EOF |
|||
#!/bin/bash |
|||
# Auto-generated AVD startup script |
|||
# Generated by avd-resource-checker.sh on $(date) |
|||
|
|||
echo "Starting AVD: ${avd_name}" |
|||
echo "Memory: ${recommended_memory}MB" |
|||
echo "Cores: ${recommended_cores}" |
|||
echo "GPU: ${gpu_setting}" |
|||
|
|||
${command} |
|||
|
|||
echo "AVD started in background" |
|||
echo "Check status with: adb devices" |
|||
echo "View logs with: adb logcat" |
|||
EOF |
|||
|
|||
chmod +x "$script_file" |
|||
print_status $GREEN_COLOR "\n✓ Startup script saved to: ${script_file}" |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
# Function to test AVD startup |
|||
test_avd_startup() { |
|||
local avd_name=$1 |
|||
local test_duration=${2:-30} |
|||
|
|||
print_status $BLUE_COLOR "\n=== Testing AVD Startup ===" |
|||
|
|||
# Check if AVD exists |
|||
if ! avdmanager list avd | grep -q "$avd_name"; then |
|||
print_status $RED_COLOR "✗ AVD '$avd_name' not found" |
|||
return 1 |
|||
fi |
|||
|
|||
print_status $YELLOW_COLOR "Testing AVD startup for ${test_duration} seconds..." |
|||
|
|||
# Start emulator in test mode |
|||
emulator -avd "$avd_name" -no-audio -no-window -no-snapshot -memory 1024 -cores 1 -gpu swiftshader_indirect & |
|||
local emulator_pid=$! |
|||
|
|||
# Wait for boot |
|||
local boot_time=0 |
|||
local max_wait=$test_duration |
|||
|
|||
while [ $boot_time -lt $max_wait ]; do |
|||
if adb devices | grep -q "emulator.*device"; then |
|||
print_status $GREEN_COLOR "✓ AVD booted successfully in ${boot_time} seconds" |
|||
break |
|||
fi |
|||
sleep 2 |
|||
boot_time=$((boot_time + 2)) |
|||
done |
|||
|
|||
# Cleanup |
|||
kill $emulator_pid 2>/dev/null || true |
|||
adb emu kill 2>/dev/null || true |
|||
|
|||
if [ $boot_time -ge $max_wait ]; then |
|||
print_status $RED_COLOR "✗ AVD failed to boot within ${test_duration} seconds" |
|||
return 1 |
|||
fi |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
# Function to list available AVDs |
|||
list_available_avds() { |
|||
print_status $BLUE_COLOR "\n=== Available AVDs ===" |
|||
|
|||
if ! command -v avdmanager >/dev/null 2>&1; then |
|||
print_status $RED_COLOR "✗ avdmanager not found. Please install Android SDK command line tools." |
|||
return 1 |
|||
fi |
|||
|
|||
local avd_list=$(avdmanager list avd 2>/dev/null) |
|||
if [ -z "$avd_list" ]; then |
|||
print_status $YELLOW_COLOR "⚠ No AVDs found. Create one with:" |
|||
echo " avdmanager create avd --name TimeSafari_Emulator --package system-images;android-34;google_apis;x86_64" |
|||
return 1 |
|||
fi |
|||
|
|||
echo "$avd_list" |
|||
return 0 |
|||
} |
|||
|
|||
# Function to create optimized AVD |
|||
create_optimized_avd() { |
|||
local avd_name=$1 |
|||
local memory=$2 |
|||
local cores=$3 |
|||
|
|||
print_status $BLUE_COLOR "\n=== Creating Optimized AVD ===" |
|||
|
|||
# Check if system image is available |
|||
local system_image="system-images;android-34;google_apis;x86_64" |
|||
if ! sdkmanager --list | grep -q "$system_image"; then |
|||
print_status $YELLOW_COLOR "Installing system image: $system_image" |
|||
sdkmanager "$system_image" |
|||
fi |
|||
|
|||
# Create AVD |
|||
print_status $YELLOW_COLOR "Creating AVD: $avd_name" |
|||
avdmanager create avd \ |
|||
--name "$avd_name" \ |
|||
--package "$system_image" \ |
|||
--device "pixel_7" \ |
|||
--force |
|||
|
|||
# Configure AVD |
|||
local avd_config_file="$HOME/.android/avd/${avd_name}.avd/config.ini" |
|||
if [ -f "$avd_config_file" ]; then |
|||
print_status $YELLOW_COLOR "Configuring AVD settings..." |
|||
|
|||
# Set memory |
|||
sed -i "s/vm.heapSize=.*/vm.heapSize=${memory}/" "$avd_config_file" |
|||
|
|||
# Set cores |
|||
sed -i "s/hw.cpu.ncore=.*/hw.cpu.ncore=${cores}/" "$avd_config_file" |
|||
|
|||
# Disable unnecessary features |
|||
echo "hw.audioInput=no" >> "$avd_config_file" |
|||
echo "hw.audioOutput=no" >> "$avd_config_file" |
|||
echo "hw.camera.back=none" >> "$avd_config_file" |
|||
echo "hw.camera.front=none" >> "$avd_config_file" |
|||
echo "hw.gps=no" >> "$avd_config_file" |
|||
echo "hw.sensors.orientation=no" >> "$avd_config_file" |
|||
echo "hw.sensors.proximity=no" >> "$avd_config_file" |
|||
|
|||
print_status $GREEN_COLOR "✓ AVD configured successfully" |
|||
fi |
|||
|
|||
return 0 |
|||
} |
|||
|
|||
# Main function |
|||
main() { |
|||
print_status $BLUE_COLOR "=== TimeSafari AVD Resource Checker ===" |
|||
print_status $BLUE_COLOR "Checking system resources and recommending optimal AVD configuration\n" |
|||
|
|||
# Get system information |
|||
local total_memory=$(get_system_memory) |
|||
local available_memory=$(get_available_memory) |
|||
local cpu_cores=$(get_cpu_cores) |
|||
|
|||
print_status $BLUE_COLOR "=== System Information ===" |
|||
echo "Total Memory: ${total_memory}MB" |
|||
echo "Available Memory: ${available_memory}MB" |
|||
echo "CPU Cores: ${cpu_cores}" |
|||
|
|||
# Check GPU capabilities |
|||
print_status $BLUE_COLOR "\n=== GPU Analysis ===" |
|||
check_gpu_capabilities |
|||
local gpu_capable=$? |
|||
|
|||
# Check hardware acceleration |
|||
check_hardware_acceleration $gpu_capable |
|||
local hw_accel=$? |
|||
|
|||
# List available AVDs |
|||
list_available_avds |
|||
|
|||
# Get AVD name from user or use default |
|||
local avd_name="TimeSafari_Emulator" |
|||
if [ $# -gt 0 ]; then |
|||
avd_name="$1" |
|||
fi |
|||
|
|||
# Recommend configuration |
|||
recommend_avd_config $total_memory $available_memory $cpu_cores $gpu_capable "$avd_name" |
|||
|
|||
# Test AVD if requested |
|||
if [ "$2" = "--test" ]; then |
|||
test_avd_startup "$avd_name" |
|||
fi |
|||
|
|||
# Create optimized AVD if requested |
|||
if [ "$2" = "--create" ]; then |
|||
local recommended_memory=$((available_memory - 2048)) |
|||
if [ $recommended_memory -gt 4096 ]; then |
|||
recommended_memory=4096 |
|||
elif [ $recommended_memory -lt 1024 ]; then |
|||
recommended_memory=1024 |
|||
fi |
|||
|
|||
local recommended_cores=$((cpu_cores - 2)) |
|||
if [ $recommended_cores -lt 1 ]; then |
|||
recommended_cores=1 |
|||
elif [ $recommended_cores -gt 4 ]; then |
|||
recommended_cores=4 |
|||
fi |
|||
|
|||
create_optimized_avd "$avd_name" $recommended_memory $recommended_cores |
|||
fi |
|||
|
|||
print_status $GREEN_COLOR "\n=== Resource Check Complete ===" |
|||
print_status $YELLOW_COLOR "Tip: Use the generated startup script for consistent AVD launches" |
|||
} |
|||
|
|||
# Show help |
|||
show_help() { |
|||
echo "Usage: $0 [AVD_NAME] [OPTIONS]" |
|||
echo "" |
|||
echo "Options:" |
|||
echo " --test Test AVD startup (30 second test)" |
|||
echo " --create Create optimized AVD with recommended settings" |
|||
echo " --help Show this help message" |
|||
echo "" |
|||
echo "Examples:" |
|||
echo " $0 # Check resources and recommend config" |
|||
echo " $0 TimeSafari_Emulator # Check resources for specific AVD" |
|||
echo " $0 TimeSafari_Emulator --test # Test AVD startup" |
|||
echo " $0 TimeSafari_Emulator --create # Create optimized AVD" |
|||
echo "" |
|||
echo "The script will:" |
|||
echo " - Analyze system resources (RAM, CPU, GPU)" |
|||
echo " - Recommend optimal AVD configuration" |
|||
echo " - Generate startup command and script" |
|||
echo " - Optionally test or create AVD" |
|||
} |
|||
|
|||
# Parse command line arguments |
|||
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then |
|||
show_help |
|||
exit 0 |
|||
fi |
|||
|
|||
# Run main function |
|||
main "$@" |
@ -0,0 +1,14 @@ |
|||
/** |
|||
* ActiveIdentity type describes the active identity selection. |
|||
* This replaces the activeDid field in the settings table for better |
|||
* database architecture and data integrity. |
|||
* |
|||
* @author Matthew Raymer |
|||
* @since 2025-08-29 |
|||
*/ |
|||
|
|||
export interface ActiveIdentity { |
|||
id: number; |
|||
activeDid: string; |
|||
lastUpdated: string; |
|||
} |
Loading…
Reference in new issue