Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
5499b10b2a
commit
fe7ab135af
@ -553,7 +553,6 @@ SelfieCam uses the following architectural patterns:
|
|||||||
- Skin smoothing
|
- Skin smoothing
|
||||||
- Center Stage
|
- Center Stage
|
||||||
- Extended timers (5s, 10s)
|
- Extended timers (5s, 10s)
|
||||||
- Video and Boomerang capture modes
|
|
||||||
|
|
||||||
|
|
||||||
## Settings & iCloud Sync
|
## Settings & iCloud Sync
|
||||||
@ -621,7 +620,6 @@ var flashMode: CameraFlashMode // .off, .on, .auto
|
|||||||
- Front/back camera switching
|
- Front/back camera switching
|
||||||
- Pinch-to-zoom
|
- Pinch-to-zoom
|
||||||
- Photo capture with quality settings
|
- Photo capture with quality settings
|
||||||
- Video recording (premium)
|
|
||||||
- HDR mode (premium)
|
- HDR mode (premium)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -75,7 +75,6 @@ Features/
|
|||||||
- Post-capture preview with share functionality
|
- Post-capture preview with share functionality
|
||||||
- Auto-save option to Photo Library
|
- Auto-save option to Photo Library
|
||||||
- Front flash using screen brightness
|
- Front flash using screen brightness
|
||||||
- Support for photo, video, and boomerang modes
|
|
||||||
|
|
||||||
### 4. Freemium Model
|
### 4. Freemium Model
|
||||||
- Built with **RevenueCat** for subscription management
|
- Built with **RevenueCat** for subscription management
|
||||||
@ -128,7 +127,6 @@ var isMirrorFlipped: Bool {
|
|||||||
| Skin smoothing | Off | Configurable |
|
| Skin smoothing | Off | Configurable |
|
||||||
| Flash sync | Off | Configurable |
|
| Flash sync | Off | Configurable |
|
||||||
| Center stage | Off | Configurable |
|
| Center stage | Off | Configurable |
|
||||||
| Capture modes | Photo | Photo, Video, Boomerang |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
11
README.md
11
README.md
@ -15,10 +15,8 @@ Perfect for low-light selfies, content creation, video calls, makeup application
|
|||||||
- **Front Flash**: Uses screen brightness for front camera flash effect
|
- **Front Flash**: Uses screen brightness for front camera flash effect
|
||||||
- Real-time camera preview with smooth performance
|
- Real-time camera preview with smooth performance
|
||||||
|
|
||||||
### Capture Modes
|
### Capture
|
||||||
- **Photo capture** with high-quality output
|
- **Photo capture** with high-quality output
|
||||||
- **Video recording** (Premium)
|
|
||||||
- **Boomerang mode** for looping short videos (Premium)
|
|
||||||
- Self-timer with 3-second (free), 5-second, and 10-second (Premium) options
|
- Self-timer with 3-second (free), 5-second, and 10-second (Premium) options
|
||||||
- Pinch-to-zoom gesture support
|
- Pinch-to-zoom gesture support
|
||||||
- Rule-of-thirds grid overlay (toggleable)
|
- Rule-of-thirds grid overlay (toggleable)
|
||||||
@ -34,7 +32,6 @@ Perfect for low-light selfies, content creation, video calls, makeup application
|
|||||||
- **Skin Smoothing**: Real-time subtle skin smoothing filter
|
- **Skin Smoothing**: Real-time subtle skin smoothing filter
|
||||||
- **Center Stage**: Automatic subject tracking/centering
|
- **Center Stage**: Automatic subject tracking/centering
|
||||||
- **Extended Timers**: 5-second and 10-second self-timer options
|
- **Extended Timers**: 5-second and 10-second self-timer options
|
||||||
- **Video & Boomerang**: Video recording and looping video capture
|
|
||||||
- Ad-free experience
|
- Ad-free experience
|
||||||
|
|
||||||
### iCloud Sync
|
### iCloud Sync
|
||||||
@ -139,15 +136,15 @@ Add `REVENUECAT_API_KEY` as a secret in your Xcode Cloud workflow.
|
|||||||
|
|
||||||
## Privacy
|
## Privacy
|
||||||
- Camera access required for preview and capture
|
- Camera access required for preview and capture
|
||||||
- Photo Library access required to save photos/videos
|
- Photo Library access required to save photos
|
||||||
- Microphone access required for video recording
|
- Microphone access may be requested by the camera framework (not actively used)
|
||||||
- iCloud access for settings synchronization (optional)
|
- iCloud access for settings synchronization (optional)
|
||||||
- No data collection, no analytics, no tracking
|
- No data collection, no analytics, no tracking
|
||||||
|
|
||||||
## Monetization
|
## Monetization
|
||||||
Freemium model with optional "Pro" subscription:
|
Freemium model with optional "Pro" subscription:
|
||||||
- **Free**: Basic ring light, standard colors (Pure White, Warm Cream), photo capture, 3s timer, grid, zoom
|
- **Free**: Basic ring light, standard colors (Pure White, Warm Cream), photo capture, 3s timer, grid, zoom
|
||||||
- **Pro**: Full color palette, custom colors, HDR, high quality, flash sync, true mirror, skin smoothing, center stage, extended timers, video, boomerang
|
- **Pro**: Full color palette, custom colors, HDR, high quality, flash sync, true mirror, skin smoothing, center stage, extended timers
|
||||||
|
|
||||||
Implemented with RevenueCat for reliable subscription management.
|
Implemented with RevenueCat for reliable subscription management.
|
||||||
|
|
||||||
|
|||||||
@ -422,9 +422,9 @@
|
|||||||
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to display your live selfie preview, apply real-time filters and ring light effects, capture high-quality photos and videos, and enable advanced features like Center Stage auto-framing.";
|
INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to display your live selfie preview, apply real-time filters and ring light effects, capture high-quality photos, and enable advanced features like Center Stage auto-framing.";
|
||||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "SelfieCam needs microphone access to record clear audio when capturing videos, ensuring your voiceovers and ambient sounds are captured along with your video content.";
|
INFOPLIST_KEY_NSMicrophoneUsageDescription = "SelfieCam needs microphone access for the camera framework to initialize properly. Audio is not recorded.";
|
||||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "SelfieCam needs photo library access to automatically save your captured photos and videos to your device, making them available in the Photos app and other compatible applications.";
|
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "SelfieCam needs photo library access to automatically save your captured photos to your device, making them available in the Photos app and other compatible applications.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_BackgroundColor = LaunchBackground;
|
INFOPLIST_KEY_UILaunchScreen_BackgroundColor = LaunchBackground;
|
||||||
@ -459,9 +459,9 @@
|
|||||||
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to display your live selfie preview, apply real-time filters and ring light effects, capture high-quality photos and videos, and enable advanced features like Center Stage auto-framing.";
|
INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to display your live selfie preview, apply real-time filters and ring light effects, capture high-quality photos, and enable advanced features like Center Stage auto-framing.";
|
||||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "SelfieCam needs microphone access to record clear audio when capturing videos, ensuring your voiceovers and ambient sounds are captured along with your video content.";
|
INFOPLIST_KEY_NSMicrophoneUsageDescription = "SelfieCam needs microphone access for the camera framework to initialize properly. Audio is not recorded.";
|
||||||
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "SelfieCam needs photo library access to automatically save your captured photos and videos to your device, making them available in the Photos app and other compatible applications.";
|
INFOPLIST_KEY_NSPhotoLibraryAddUsageDescription = "SelfieCam needs photo library access to automatically save your captured photos to your device, making them available in the Photos app and other compatible applications.";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_BackgroundColor = LaunchBackground;
|
INFOPLIST_KEY_UILaunchScreen_BackgroundColor = LaunchBackground;
|
||||||
|
|||||||
@ -39,7 +39,7 @@ struct ContentView: View {
|
|||||||
.ignoresSafeArea() // Only camera ignores safe area to fill screen
|
.ignoresSafeArea() // Only camera ignores safe area to fill screen
|
||||||
}
|
}
|
||||||
|
|
||||||
// Photo review overlay
|
// Photo review overlay - handles its own safe area
|
||||||
if showPhotoReview, let photo = capturedPhoto {
|
if showPhotoReview, let photo = capturedPhoto {
|
||||||
PhotoReviewView(
|
PhotoReviewView(
|
||||||
photo: photo,
|
photo: photo,
|
||||||
@ -53,24 +53,25 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
.ignoresSafeArea() // Photo review also fills screen
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Settings button overlay - respects safe area naturally
|
// Settings button overlay - only show when NOT in photo review mode
|
||||||
.overlay(alignment: .topTrailing) {
|
.overlay(alignment: .topTrailing) {
|
||||||
Button {
|
if !showPhotoReview {
|
||||||
showSettings = true
|
Button {
|
||||||
} label: {
|
showSettings = true
|
||||||
Image(systemName: "gearshape.fill")
|
} label: {
|
||||||
.font(.title3)
|
Image(systemName: "gearshape.fill")
|
||||||
.foregroundStyle(.white)
|
.font(.title3)
|
||||||
.padding(Design.Spacing.medium)
|
.foregroundStyle(.white)
|
||||||
.background(.ultraThinMaterial, in: Circle())
|
.padding(Design.Spacing.medium)
|
||||||
.shadow(radius: Design.Shadow.radiusSmall)
|
.background(.ultraThinMaterial, in: Circle())
|
||||||
|
.shadow(radius: Design.Shadow.radiusSmall)
|
||||||
|
}
|
||||||
|
.accessibilityLabel("Settings")
|
||||||
|
.padding(.horizontal, Design.Spacing.large)
|
||||||
|
.padding(.top, Design.Spacing.small)
|
||||||
}
|
}
|
||||||
.accessibilityLabel("Settings")
|
|
||||||
.padding(.horizontal, Design.Spacing.large)
|
|
||||||
.padding(.top, Design.Spacing.small)
|
|
||||||
}
|
}
|
||||||
.animation(.easeInOut(duration: Design.Animation.quick), value: showPhotoReview)
|
.animation(.easeInOut(duration: Design.Animation.quick), value: showPhotoReview)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// PhotoReviewView.swift
|
// PhotoReviewView.swift
|
||||||
// CameraTester
|
// SelfieCam
|
||||||
//
|
//
|
||||||
// Created by Matt Bruce on 1/2/26.
|
// Created by Matt Bruce on 1/2/26.
|
||||||
//
|
//
|
||||||
@ -16,58 +16,81 @@ struct PhotoReviewView: View {
|
|||||||
let saveError: String?
|
let saveError: String?
|
||||||
let onRetake: () -> Void
|
let onRetake: () -> Void
|
||||||
let onSave: () -> Void
|
let onSave: () -> Void
|
||||||
|
|
||||||
|
// Layout constants
|
||||||
|
private let toolbarHeight: CGFloat = 100
|
||||||
|
private let topButtonSize: CGFloat = 44 // Retake, Close (smaller, out of the way)
|
||||||
|
private let bottomButtonSize: CGFloat = 64 // Share, Save (larger, main actions)
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
// Photo display
|
// Black background
|
||||||
Color.black
|
Color.black
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
|
|
||||||
|
// Photo - centered in available space
|
||||||
Image(uiImage: photo.image)
|
Image(uiImage: photo.image)
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.ignoresSafeArea()
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
// Top toolbar
|
// Top toolbar with gradient background
|
||||||
VStack {
|
.overlay(alignment: .top) {
|
||||||
|
ZStack(alignment: .bottom) {
|
||||||
|
// Gradient background for visibility
|
||||||
|
LinearGradient(
|
||||||
|
colors: [Color.black.opacity(0.7), Color.black.opacity(0)],
|
||||||
|
startPoint: .top,
|
||||||
|
endPoint: .bottom
|
||||||
|
)
|
||||||
|
.frame(height: toolbarHeight)
|
||||||
|
.ignoresSafeArea(edges: .top)
|
||||||
|
|
||||||
|
// Buttons
|
||||||
HStack {
|
HStack {
|
||||||
// Retake button
|
// Retake button
|
||||||
Button(action: onRetake) {
|
Button(action: onRetake) {
|
||||||
Image(systemName: "arrow.triangle.2.circlepath")
|
Image(systemName: "arrow.triangle.2.circlepath")
|
||||||
.font(.system(size: 20, weight: .medium))
|
.font(.system(size: 18, weight: .semibold))
|
||||||
.foregroundColor(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: 44, height: 44)
|
.frame(width: topButtonSize, height: topButtonSize)
|
||||||
.background(
|
.background(.ultraThinMaterial, in: Circle())
|
||||||
Circle()
|
|
||||||
.fill(Color.black.opacity(0.6))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
.accessibilityLabel("Retake photo")
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
// Close button
|
// Close button
|
||||||
Button(action: onRetake) {
|
Button(action: onRetake) {
|
||||||
Image(systemName: "xmark")
|
Image(systemName: "xmark")
|
||||||
.font(.system(size: 20, weight: .medium))
|
.font(.system(size: 18, weight: .semibold))
|
||||||
.foregroundColor(.white)
|
.foregroundStyle(.white)
|
||||||
.frame(width: 44, height: 44)
|
.frame(width: topButtonSize, height: topButtonSize)
|
||||||
.background(
|
.background(.ultraThinMaterial, in: Circle())
|
||||||
Circle()
|
|
||||||
.fill(Color.black.opacity(0.6))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
.accessibilityLabel("Close")
|
||||||
}
|
}
|
||||||
.padding(.horizontal, Design.Spacing.large)
|
.padding(.horizontal, Design.Spacing.large)
|
||||||
.padding(.top, Design.Spacing.large)
|
.padding(.bottom, Design.Spacing.medium)
|
||||||
|
}
|
||||||
Spacer()
|
}
|
||||||
|
// Bottom toolbar with gradient background
|
||||||
// Bottom action bar
|
.overlay(alignment: .bottom) {
|
||||||
|
ZStack(alignment: .top) {
|
||||||
|
// Gradient background for visibility
|
||||||
|
LinearGradient(
|
||||||
|
colors: [Color.black.opacity(0), Color.black.opacity(0.85)],
|
||||||
|
startPoint: .top,
|
||||||
|
endPoint: .bottom
|
||||||
|
)
|
||||||
|
.frame(height: 160)
|
||||||
|
.ignoresSafeArea(edges: .bottom)
|
||||||
|
|
||||||
VStack(spacing: Design.Spacing.medium) {
|
VStack(spacing: Design.Spacing.medium) {
|
||||||
// Save status or error
|
// Save status or error
|
||||||
if let error = saveError {
|
if let error = saveError {
|
||||||
Text(error)
|
Text(error)
|
||||||
.foregroundColor(.red)
|
.foregroundStyle(.red)
|
||||||
.font(.system(size: 14, weight: .medium))
|
.font(.system(size: 14, weight: .medium))
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.padding(.vertical, Design.Spacing.small)
|
.padding(.vertical, Design.Spacing.small)
|
||||||
@ -81,38 +104,37 @@ struct PhotoReviewView: View {
|
|||||||
ProgressView()
|
ProgressView()
|
||||||
.tint(.white)
|
.tint(.white)
|
||||||
Text("Saving...")
|
Text("Saving...")
|
||||||
.foregroundColor(.white)
|
.foregroundStyle(.white)
|
||||||
.font(.system(size: 16, weight: .medium))
|
.font(.system(size: 16, weight: .medium))
|
||||||
}
|
}
|
||||||
.padding(.vertical, Design.Spacing.small)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action buttons
|
// Action buttons - same size, different styling
|
||||||
HStack(spacing: Design.Spacing.xLarge) {
|
HStack(spacing: Design.Spacing.xLarge * 2) {
|
||||||
// Share button
|
// Share button (frosted glass = secondary)
|
||||||
ShareButton(photo: photo.image)
|
ShareButton(photo: photo.image)
|
||||||
|
.frame(width: bottomButtonSize, height: bottomButtonSize)
|
||||||
|
.background(.ultraThinMaterial, in: Circle())
|
||||||
|
|
||||||
// Save button
|
// Save button (solid white = primary)
|
||||||
Button(action: onSave) {
|
Button(action: onSave) {
|
||||||
ZStack {
|
Image(systemName: "checkmark")
|
||||||
Circle()
|
.font(.system(size: 26, weight: .bold))
|
||||||
.fill(Color.white)
|
.foregroundStyle(.black)
|
||||||
.frame(width: 80, height: 80)
|
.frame(width: bottomButtonSize, height: bottomButtonSize)
|
||||||
|
.background(.white, in: Circle())
|
||||||
Image(systemName: "checkmark")
|
.shadow(color: .black.opacity(0.3), radius: Design.Shadow.radiusMedium)
|
||||||
.font(.system(size: 24, weight: .bold))
|
|
||||||
.foregroundColor(.black)
|
|
||||||
}
|
|
||||||
.shadow(radius: 5)
|
|
||||||
}
|
}
|
||||||
.disabled(isSaving)
|
.disabled(isSaving)
|
||||||
|
.accessibilityLabel("Save photo")
|
||||||
|
.accessibilityHint("Saves the photo to your library")
|
||||||
}
|
}
|
||||||
.padding(.bottom, Design.Spacing.large)
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, Design.Spacing.large)
|
.padding(.top, Design.Spacing.xLarge)
|
||||||
|
.padding(.bottom, Design.Spacing.large)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.accessibilityElement(children: .contain)
|
||||||
.accessibilityLabel("Photo review")
|
.accessibilityLabel("Photo review")
|
||||||
.accessibilityHint("Use the buttons at the bottom to save or share your photo")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,20 +15,15 @@ struct ShareButton: View {
|
|||||||
@State private var isShareSheetPresented = false
|
@State private var isShareSheetPresented = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Button(action: {
|
Button {
|
||||||
isShareSheetPresented = true
|
isShareSheetPresented = true
|
||||||
}) {
|
} label: {
|
||||||
ZStack {
|
Image(systemName: "square.and.arrow.up")
|
||||||
Circle()
|
.font(.system(size: 20, weight: .medium))
|
||||||
.fill(Color.black.opacity(0.6))
|
.foregroundStyle(.white)
|
||||||
.frame(width: 80, height: 80)
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
|
||||||
Image(systemName: "square.and.arrow.up")
|
|
||||||
.font(.system(size: 24, weight: .medium))
|
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
.shadow(radius: 5)
|
|
||||||
}
|
}
|
||||||
|
.accessibilityLabel("Share photo")
|
||||||
.sheet(isPresented: $isShareSheetPresented) {
|
.sheet(isPresented: $isShareSheetPresented) {
|
||||||
ShareSheet(activityItems: [photo])
|
ShareSheet(activityItems: [photo])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -507,6 +507,7 @@
|
|||||||
},
|
},
|
||||||
"Boomerang" : {
|
"Boomerang" : {
|
||||||
"comment" : "Display name for the \"Boomerang\" capture mode.",
|
"comment" : "Display name for the \"Boomerang\" capture mode.",
|
||||||
|
"extractionState" : "stale",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"es-MX" : {
|
"es-MX" : {
|
||||||
@ -769,6 +770,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Close" : {
|
||||||
|
"comment" : "A button label that closes the view.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Close preview" : {
|
"Close preview" : {
|
||||||
"comment" : "A button label that closes the preview screen.",
|
"comment" : "A button label that closes the preview screen.",
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
@ -1691,6 +1696,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Photo" : {
|
"Photo" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"es-MX" : {
|
"es-MX" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -1957,6 +1963,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Retake photo" : {
|
||||||
|
"comment" : "A button that, when tapped, allows the user to retake a photo.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Ring Light" : {
|
"Ring Light" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"es-MX" : {
|
"es-MX" : {
|
||||||
@ -2148,6 +2158,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Save photo" : {
|
||||||
|
"comment" : "A button that saves the currently displayed photo to the user's library.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Saved to Photos" : {
|
"Saved to Photos" : {
|
||||||
"comment" : "Text shown as a toast message when a photo is successfully saved to Photos.",
|
"comment" : "Text shown as a toast message when a photo is successfully saved to Photos.",
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
@ -2173,6 +2187,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Saves the photo to your library" : {
|
||||||
|
"comment" : "An accessibility hint for the save button in the photo review view.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Saving..." : {
|
"Saving..." : {
|
||||||
"comment" : "A text that appears while a photo is being saved.",
|
"comment" : "A text that appears while a photo is being saved.",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
@ -2390,6 +2408,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Share photo" : {
|
||||||
|
"comment" : "An accessibility label for the share button.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Show colored light ring around camera preview" : {
|
"Show colored light ring around camera preview" : {
|
||||||
"comment" : "Subtitle for the \"Enable Ring Light\" toggle in the Settings view.",
|
"comment" : "Subtitle for the \"Enable Ring Light\" toggle in the Settings view.",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
@ -3016,6 +3038,7 @@
|
|||||||
},
|
},
|
||||||
"Use the buttons at the bottom to save or share your photo" : {
|
"Use the buttons at the bottom to save or share your photo" : {
|
||||||
"comment" : "An accessibility hint for the photo review view, instructing the user on how to interact with the view.",
|
"comment" : "An accessibility hint for the photo review view, instructing the user on how to interact with the view.",
|
||||||
|
"extractionState" : "stale",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"es-MX" : {
|
"es-MX" : {
|
||||||
@ -3065,6 +3088,7 @@
|
|||||||
},
|
},
|
||||||
"Video" : {
|
"Video" : {
|
||||||
"comment" : "Display name for the \"Video\" capture mode.",
|
"comment" : "Display name for the \"Video\" capture mode.",
|
||||||
|
"extractionState" : "stale",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"es-MX" : {
|
"es-MX" : {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user