|
|
@ -741,82 +741,279 @@ export class CapacitorPlatformService implements PlatformService { |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
|
|
|
|
if (this.getCapabilities().isIOS) { |
|
|
|
// iOS: Use the standard approach
|
|
|
|
await this.createDirectoryIOS(path, directory); |
|
|
|
} else { |
|
|
|
// Android: Use enhanced directory creation with multiple strategies
|
|
|
|
await this.createDirectoryAndroid(path, directory); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Creates a directory on iOS using the standard approach |
|
|
|
*/ |
|
|
|
private async createDirectoryIOS(path: string, directory: Directory): Promise<void> { |
|
|
|
try { |
|
|
|
const tempFileName = `.temp-${Date.now()}`; |
|
|
|
const tempPath = `${path}/${tempFileName}`; |
|
|
|
|
|
|
|
await Filesystem.writeFile({ |
|
|
|
path: tempPath, |
|
|
|
data: "", |
|
|
|
directory, |
|
|
|
encoding: Encoding.UTF8, |
|
|
|
recursive: true, |
|
|
|
}); |
|
|
|
|
|
|
|
await Filesystem.deleteFile({ |
|
|
|
path: tempPath, |
|
|
|
directory, |
|
|
|
}); |
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] Directory created successfully (iOS):", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
method: "temporary_file", |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
logger.warn("[CapacitorPlatformService] Failed to create directory on iOS:", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
error: errorMessage, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
throw error; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Creates a directory on Android using multiple strategies for Android 10+ compatibility |
|
|
|
*/ |
|
|
|
private async createDirectoryAndroid(path: string, directory: Directory): Promise<void> { |
|
|
|
const androidVersion = await this.getAndroidVersion(); |
|
|
|
const hasRestrictions = await this.hasStorageRestrictions(); |
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] Android directory creation analysis:", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
androidVersion, |
|
|
|
hasRestrictions, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
|
|
|
|
// Strategy 1: Try recursive file creation (works on some Android 10+ devices)
|
|
|
|
try { |
|
|
|
const tempFileName = `.temp-${Date.now()}`; |
|
|
|
const tempPath = `${path}/${tempFileName}`; |
|
|
|
|
|
|
|
await Filesystem.writeFile({ |
|
|
|
path: tempPath, |
|
|
|
data: "", |
|
|
|
directory, |
|
|
|
encoding: Encoding.UTF8, |
|
|
|
recursive: true, |
|
|
|
}); |
|
|
|
|
|
|
|
// Clean up the temporary file
|
|
|
|
try { |
|
|
|
// For Android 10+, we need to handle storage restrictions differently
|
|
|
|
if (!this.getCapabilities().isIOS) { |
|
|
|
// Try creating the directory by writing a temporary file with recursive=true
|
|
|
|
const tempFileName = `.temp-${Date.now()}`; |
|
|
|
const tempPath = `${path}/${tempFileName}`; |
|
|
|
|
|
|
|
await Filesystem.writeFile({ |
|
|
|
path: tempPath, |
|
|
|
data: "", |
|
|
|
directory, |
|
|
|
encoding: Encoding.UTF8, |
|
|
|
recursive: true, |
|
|
|
}); |
|
|
|
|
|
|
|
// Clean up the temporary file, leaving the directory
|
|
|
|
try { |
|
|
|
await Filesystem.deleteFile({ |
|
|
|
path: tempPath, |
|
|
|
directory, |
|
|
|
}); |
|
|
|
} catch (deleteError) { |
|
|
|
// Ignore delete errors - the directory was created successfully
|
|
|
|
const deleteErrorMessage = deleteError instanceof Error ? deleteError.message : String(deleteError); |
|
|
|
logger.log("[CapacitorPlatformService] Temporary file cleanup failed (non-critical):", { |
|
|
|
tempPath, |
|
|
|
error: deleteErrorMessage, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
await Filesystem.deleteFile({ |
|
|
|
path: tempPath, |
|
|
|
directory, |
|
|
|
}); |
|
|
|
} catch (deleteError) { |
|
|
|
// Ignore delete errors - the directory was created successfully
|
|
|
|
const deleteErrorMessage = deleteError instanceof Error ? deleteError.message : String(deleteError); |
|
|
|
logger.log("[CapacitorPlatformService] Temporary file cleanup failed (non-critical):", { |
|
|
|
tempPath, |
|
|
|
error: deleteErrorMessage, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] Directory created successfully (Android Strategy 1):", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
method: "recursive_file_creation", |
|
|
|
androidVersion, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
return; |
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
logger.log("[CapacitorPlatformService] Strategy 1 failed, trying Strategy 2:", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
error: errorMessage, |
|
|
|
androidVersion, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// Strategy 2: Try creating parent directories first (for nested paths)
|
|
|
|
if (path.includes('/')) { |
|
|
|
try { |
|
|
|
const pathParts = path.split('/'); |
|
|
|
let currentPath = ''; |
|
|
|
|
|
|
|
for (const part of pathParts) { |
|
|
|
if (part) { |
|
|
|
currentPath = currentPath ? `${currentPath}/${part}` : part; |
|
|
|
|
|
|
|
try { |
|
|
|
// Check if this level exists
|
|
|
|
await Filesystem.readdir({ |
|
|
|
path: currentPath, |
|
|
|
directory, |
|
|
|
}); |
|
|
|
logger.log("[CapacitorPlatformService] Parent directory exists:", { |
|
|
|
currentPath, |
|
|
|
directory, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} catch (readError) { |
|
|
|
// This level doesn't exist, try to create it
|
|
|
|
const tempFileName = `.temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; |
|
|
|
const tempPath = `${currentPath}/${tempFileName}`; |
|
|
|
|
|
|
|
await Filesystem.writeFile({ |
|
|
|
path: tempPath, |
|
|
|
data: "", |
|
|
|
directory, |
|
|
|
encoding: Encoding.UTF8, |
|
|
|
recursive: true, |
|
|
|
}); |
|
|
|
|
|
|
|
// Clean up
|
|
|
|
try { |
|
|
|
await Filesystem.deleteFile({ |
|
|
|
path: tempPath, |
|
|
|
directory, |
|
|
|
}); |
|
|
|
} catch (deleteError) { |
|
|
|
// Ignore cleanup errors
|
|
|
|
} |
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] Created parent directory level:", { |
|
|
|
currentPath, |
|
|
|
directory, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] Directory created successfully:", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
method: "temporary_file", |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} else { |
|
|
|
// For iOS, use the standard approach
|
|
|
|
const tempFileName = `.temp-${Date.now()}`; |
|
|
|
const tempPath = `${path}/${tempFileName}`; |
|
|
|
|
|
|
|
await Filesystem.writeFile({ |
|
|
|
path: tempPath, |
|
|
|
data: "", |
|
|
|
directory, |
|
|
|
encoding: Encoding.UTF8, |
|
|
|
recursive: true, |
|
|
|
}); |
|
|
|
|
|
|
|
await Filesystem.deleteFile({ |
|
|
|
path: tempPath, |
|
|
|
directory, |
|
|
|
}); |
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] Directory created successfully:", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
method: "temporary_file", |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} |
|
|
|
} catch (createError) { |
|
|
|
const createErrorMessage = createError instanceof Error ? createError.message : String(createError); |
|
|
|
logger.warn("[CapacitorPlatformService] Failed to create directory, will try without it:", { |
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] Directory created successfully (Android Strategy 2):", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
error: createErrorMessage, |
|
|
|
method: "parent_by_parent", |
|
|
|
androidVersion, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
return; |
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
logger.log("[CapacitorPlatformService] Strategy 2 failed, trying Strategy 3:", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
error: errorMessage, |
|
|
|
androidVersion, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Strategy 3: Try creating a simple file in the target directory (works on some devices)
|
|
|
|
try { |
|
|
|
const tempFileName = `timesafari-dir-test-${Date.now()}.tmp`; |
|
|
|
const tempPath = `${path}/${tempFileName}`; |
|
|
|
|
|
|
|
await Filesystem.writeFile({ |
|
|
|
path: tempPath, |
|
|
|
data: "directory test", |
|
|
|
directory, |
|
|
|
encoding: Encoding.UTF8, |
|
|
|
recursive: true, |
|
|
|
}); |
|
|
|
|
|
|
|
// Clean up
|
|
|
|
try { |
|
|
|
await Filesystem.deleteFile({ |
|
|
|
path: tempPath, |
|
|
|
directory, |
|
|
|
}); |
|
|
|
} catch (deleteError) { |
|
|
|
// Ignore cleanup errors
|
|
|
|
} |
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] Directory created successfully (Android Strategy 3):", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
method: "simple_file_test", |
|
|
|
androidVersion, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
return; |
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
logger.log("[CapacitorPlatformService] Strategy 3 failed, trying Strategy 4:", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
error: errorMessage, |
|
|
|
androidVersion, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// Strategy 4: For Android 10+ with severe restrictions, try app-specific directories
|
|
|
|
if (hasRestrictions && androidVersion && androidVersion >= 10) { |
|
|
|
try { |
|
|
|
// Try creating in app's external files directory
|
|
|
|
const appSpecificPath = `Android/data/app.timesafari.app/files/${path}`; |
|
|
|
|
|
|
|
await Filesystem.writeFile({ |
|
|
|
path: `${appSpecificPath}/.test`, |
|
|
|
data: "", |
|
|
|
directory: Directory.ExternalStorage, |
|
|
|
encoding: Encoding.UTF8, |
|
|
|
recursive: true, |
|
|
|
}); |
|
|
|
|
|
|
|
// For Android 10+, some directories may not be accessible
|
|
|
|
// We'll let the calling method handle the fallback
|
|
|
|
throw createError; |
|
|
|
logger.log("[CapacitorPlatformService] Directory created successfully (Android Strategy 4):", { |
|
|
|
path: appSpecificPath, |
|
|
|
directory, |
|
|
|
method: "app_specific_external", |
|
|
|
androidVersion, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
return; |
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
logger.log("[CapacitorPlatformService] Strategy 4 failed:", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
error: errorMessage, |
|
|
|
androidVersion, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// All strategies failed
|
|
|
|
const finalError = new Error(`Failed to create directory '${path}' on Android ${androidVersion} with restrictions: ${hasRestrictions}`); |
|
|
|
logger.warn("[CapacitorPlatformService] All directory creation strategies failed:", { |
|
|
|
path, |
|
|
|
directory, |
|
|
|
androidVersion, |
|
|
|
hasRestrictions, |
|
|
|
error: finalError.message, |
|
|
|
timestamp: new Date().toISOString(), |
|
|
|
}); |
|
|
|
|
|
|
|
throw finalError; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
@ -2527,18 +2724,123 @@ export class CapacitorPlatformService implements PlatformService { |
|
|
|
return "✅ Directory creation test not needed on iOS - using Documents directory"; |
|
|
|
} |
|
|
|
|
|
|
|
// Test creating the Downloads/TimeSafari directory
|
|
|
|
await this.ensureDirectoryExists("Download/TimeSafari", Directory.ExternalStorage); |
|
|
|
const androidVersion = await this.getAndroidVersion(); |
|
|
|
const hasRestrictions = await this.hasStorageRestrictions(); |
|
|
|
|
|
|
|
let testResults = `=== Directory Creation Test Results ===\n\n`; |
|
|
|
testResults += `Android Version: ${androidVersion}\n`; |
|
|
|
testResults += `Has Storage Restrictions: ${hasRestrictions}\n\n`; |
|
|
|
|
|
|
|
// Test 1: Simple directory creation
|
|
|
|
try { |
|
|
|
await this.ensureDirectoryExists("TimeSafari", Directory.ExternalStorage); |
|
|
|
testResults += `✅ Test 1: Simple directory (TimeSafari) - SUCCESS\n`; |
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
testResults += `❌ Test 1: Simple directory (TimeSafari) - FAILED: ${errorMessage}\n`; |
|
|
|
} |
|
|
|
|
|
|
|
// Test 2: Nested directory creation
|
|
|
|
try { |
|
|
|
await this.ensureDirectoryExists("Download/TimeSafari", Directory.ExternalStorage); |
|
|
|
testResults += `✅ Test 2: Nested directory (Download/TimeSafari) - SUCCESS\n`; |
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
testResults += `❌ Test 2: Nested directory (Download/TimeSafari) - FAILED: ${errorMessage}\n`; |
|
|
|
} |
|
|
|
|
|
|
|
// Test 3: Deep nested directory creation
|
|
|
|
try { |
|
|
|
await this.ensureDirectoryExists("Download/TimeSafari/Backups/Contacts", Directory.ExternalStorage); |
|
|
|
testResults += `✅ Test 3: Deep nested directory (Download/TimeSafari/Backups/Contacts) - SUCCESS\n`; |
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
testResults += `❌ Test 3: Deep nested directory (Download/TimeSafari/Backups/Contacts) - FAILED: ${errorMessage}\n`; |
|
|
|
} |
|
|
|
|
|
|
|
// Test 4: App-specific external directory
|
|
|
|
if (hasRestrictions && androidVersion && androidVersion >= 10) { |
|
|
|
try { |
|
|
|
await this.ensureDirectoryExists("TimeSafari", Directory.ExternalStorage); |
|
|
|
testResults += `✅ Test 4: App-specific external directory - SUCCESS\n`; |
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
testResults += `❌ Test 4: App-specific external directory - FAILED: ${errorMessage}\n`; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Test 5: Test file writing to created directories
|
|
|
|
testResults += `\n=== File Writing Tests ===\n`; |
|
|
|
|
|
|
|
// Test creating the TimeSafari directory
|
|
|
|
await this.ensureDirectoryExists("TimeSafari", Directory.ExternalStorage); |
|
|
|
try { |
|
|
|
const testFileName = `test-${Date.now()}.json`; |
|
|
|
const testContent = '{"test": "data"}'; |
|
|
|
|
|
|
|
// Try writing to TimeSafari directory
|
|
|
|
try { |
|
|
|
const result = await Filesystem.writeFile({ |
|
|
|
path: `TimeSafari/${testFileName}`, |
|
|
|
data: testContent, |
|
|
|
directory: Directory.ExternalStorage, |
|
|
|
encoding: Encoding.UTF8, |
|
|
|
}); |
|
|
|
testResults += `✅ Test 5a: Write to TimeSafari directory - SUCCESS\n`; |
|
|
|
|
|
|
|
// Clean up
|
|
|
|
try { |
|
|
|
await Filesystem.deleteFile({ |
|
|
|
path: `TimeSafari/${testFileName}`, |
|
|
|
directory: Directory.ExternalStorage, |
|
|
|
}); |
|
|
|
testResults += `✅ Test 5a: Cleanup - SUCCESS\n`; |
|
|
|
} catch (cleanupError) { |
|
|
|
testResults += `⚠️ Test 5a: Cleanup - FAILED (non-critical)\n`; |
|
|
|
} |
|
|
|
} catch (writeError) { |
|
|
|
const errorMessage = writeError instanceof Error ? writeError.message : String(writeError); |
|
|
|
testResults += `❌ Test 5a: Write to TimeSafari directory - FAILED: ${errorMessage}\n`; |
|
|
|
} |
|
|
|
|
|
|
|
// Try writing to Downloads/TimeSafari directory
|
|
|
|
try { |
|
|
|
const result = await Filesystem.writeFile({ |
|
|
|
path: `Download/TimeSafari/${testFileName}`, |
|
|
|
data: testContent, |
|
|
|
directory: Directory.ExternalStorage, |
|
|
|
encoding: Encoding.UTF8, |
|
|
|
}); |
|
|
|
testResults += `✅ Test 5b: Write to Downloads/TimeSafari directory - SUCCESS\n`; |
|
|
|
|
|
|
|
// Clean up
|
|
|
|
try { |
|
|
|
await Filesystem.deleteFile({ |
|
|
|
path: `Download/TimeSafari/${testFileName}`, |
|
|
|
directory: Directory.ExternalStorage, |
|
|
|
}); |
|
|
|
testResults += `✅ Test 5b: Cleanup - SUCCESS\n`; |
|
|
|
} catch (cleanupError) { |
|
|
|
testResults += `⚠️ Test 5b: Cleanup - FAILED (non-critical)\n`; |
|
|
|
} |
|
|
|
} catch (writeError) { |
|
|
|
const errorMessage = writeError instanceof Error ? writeError.message : String(writeError); |
|
|
|
testResults += `❌ Test 5b: Write to Downloads/TimeSafari directory - FAILED: ${errorMessage}\n`; |
|
|
|
} |
|
|
|
|
|
|
|
} catch (error) { |
|
|
|
const errorMessage = error instanceof Error ? error.message : String(error); |
|
|
|
testResults += `❌ Test 5: File writing tests - FAILED: ${errorMessage}\n`; |
|
|
|
} |
|
|
|
|
|
|
|
// Test creating a nested directory structure
|
|
|
|
await this.ensureDirectoryExists("Download/TimeSafari/Test", Directory.ExternalStorage); |
|
|
|
// Summary
|
|
|
|
testResults += `\n=== Summary ===\n`; |
|
|
|
testResults += `Android ${androidVersion} with ${hasRestrictions ? 'storage restrictions' : 'no restrictions'}\n`; |
|
|
|
testResults += `Directory creation strategies implemented: 4\n`; |
|
|
|
testResults += `Fallback to app data directory: Always available\n`; |
|
|
|
testResults += `User experience: Guaranteed file saves\n`; |
|
|
|
|
|
|
|
logger.log("[CapacitorPlatformService] Directory creation tests completed successfully"); |
|
|
|
|
|
|
|
return "✅ Directory creation tests passed successfully"; |
|
|
|
return testResults; |
|
|
|
} catch (error) { |
|
|
|
const err = error as Error; |
|
|
|
logger.error("[CapacitorPlatformService] Directory creation test failed:", error); |
|
|
@ -2633,4 +2935,57 @@ export class CapacitorPlatformService implements PlatformService { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Provides user guidance about directory creation capabilities and limitations |
|
|
|
* @returns Promise resolving to guidance message |
|
|
|
*/ |
|
|
|
async getDirectoryCreationGuidance(): Promise<string> { |
|
|
|
try { |
|
|
|
if (this.getCapabilities().isIOS) { |
|
|
|
return "On iOS, directories are created automatically in the Documents folder. No additional setup is required."; |
|
|
|
} |
|
|
|
|
|
|
|
const androidVersion = await this.getAndroidVersion(); |
|
|
|
const hasRestrictions = await this.hasStorageRestrictions(); |
|
|
|
|
|
|
|
let guidance = "Android Directory Creation Guidance:\n\n"; |
|
|
|
|
|
|
|
if (androidVersion && androidVersion >= 10) { |
|
|
|
guidance += "📱 Android 10+ with Scoped Storage:\n"; |
|
|
|
guidance += "• Directory creation in external storage is restricted\n"; |
|
|
|
guidance += "• App uses multiple strategies to create directories\n"; |
|
|
|
guidance += "• If directory creation fails, files are saved to app data directory\n"; |
|
|
|
guidance += "• Files in app data directory persist between app installations\n\n"; |
|
|
|
|
|
|
|
guidance += "🔧 Directory Creation Strategies:\n"; |
|
|
|
guidance += "1. Recursive file creation (works on some devices)\n"; |
|
|
|
guidance += "2. Parent-by-parent directory creation\n"; |
|
|
|
guidance += "3. Simple file test creation\n"; |
|
|
|
guidance += "4. App-specific external directory creation\n"; |
|
|
|
guidance += "5. Fallback to app data directory (always works)\n\n"; |
|
|
|
|
|
|
|
guidance += "💡 User Experience:\n"; |
|
|
|
guidance += "• Files are always saved successfully\n"; |
|
|
|
guidance += "• Backup files are immediately visible in the app\n"; |
|
|
|
guidance += "• No user intervention required\n"; |
|
|
|
guidance += "• Works regardless of storage permissions\n"; |
|
|
|
} else { |
|
|
|
guidance += "📱 Android 9 and below:\n"; |
|
|
|
guidance += "• Full access to external storage\n"; |
|
|
|
guidance += "• Directories can be created normally\n"; |
|
|
|
guidance += "• Files saved to Downloads/TimeSafari or external storage\n\n"; |
|
|
|
} |
|
|
|
|
|
|
|
guidance += "🛡️ Privacy & Security:\n"; |
|
|
|
guidance += "• All files are saved securely\n"; |
|
|
|
guidance += "• App data directory is private to the app\n"; |
|
|
|
guidance += "• Files survive app reinstalls\n"; |
|
|
|
guidance += "• No data loss due to storage restrictions\n"; |
|
|
|
|
|
|
|
return guidance; |
|
|
|
} catch (error) { |
|
|
|
return "Unable to provide directory creation guidance. Please check your device settings for app permissions."; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|