Fix workspace background feature for Android

- Add missing filesystem permissions in capabilities
  - fs:allow-applocaldata-read-recursive
  - fs:allow-applocaldata-write-recursive
  - fs:allow-write-file
  - fs:allow-mkdir
  - fs:allow-exists
  - fs:allow-remove

- Fix Android photo picker URI handling
  - Detect file type from binary signature (PNG, JPEG, WebP)
  - Use manual path construction to avoid path joining issues
  - Works with Android photo picker content:// URIs

- Improve error handling with detailed toast messages
  - Show specific error at each step (read, mkdir, write, db)
  - Better debugging on Android where console is unavailable

- Fix window activation behavior
  - Restore minimized windows when activated

- Remove unused imports in launcher component
This commit is contained in:
2025-11-03 02:03:34 +01:00
parent 3a4f482021
commit c71b8468df
5 changed files with 118 additions and 26 deletions

View File

@ -18,8 +18,14 @@
"fs:allow-appconfig-write-recursive", "fs:allow-appconfig-write-recursive",
"fs:allow-appdata-read-recursive", "fs:allow-appdata-read-recursive",
"fs:allow-appdata-write-recursive", "fs:allow-appdata-write-recursive",
"fs:allow-applocaldata-read-recursive",
"fs:allow-applocaldata-write-recursive",
"fs:allow-read-file", "fs:allow-read-file",
"fs:allow-write-file",
"fs:allow-read-dir", "fs:allow-read-dir",
"fs:allow-mkdir",
"fs:allow-exists",
"fs:allow-remove",
"fs:allow-resource-read-recursive", "fs:allow-resource-read-recursive",
"fs:allow-resource-write-recursive", "fs:allow-resource-write-recursive",
"fs:allow-download-read-recursive", "fs:allow-download-read-recursive",

View File

@ -1 +1 @@
{"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main"],"permissions":["core:default","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-webview-show","core:webview:default","core:window:allow-create","core:window:allow-get-all-windows","core:window:allow-show","core:window:default","dialog:default","fs:allow-appconfig-read-recursive","fs:allow-appconfig-write-recursive","fs:allow-appdata-read-recursive","fs:allow-appdata-write-recursive","fs:allow-read-file","fs:allow-read-dir","fs:allow-resource-read-recursive","fs:allow-resource-write-recursive","fs:allow-download-read-recursive","fs:allow-download-write-recursive","fs:default",{"identifier":"fs:scope","allow":[{"path":"**"}]},"http:allow-fetch-send","http:allow-fetch","http:default","notification:allow-create-channel","notification:allow-list-channels","notification:allow-notify","notification:allow-is-permission-granted","notification:default","opener:allow-open-url","opener:default","os:allow-hostname","os:default","store:default"]}} {"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main"],"permissions":["core:default","core:webview:allow-create-webview-window","core:webview:allow-create-webview","core:webview:allow-webview-show","core:webview:default","core:window:allow-create","core:window:allow-get-all-windows","core:window:allow-show","core:window:default","dialog:default","fs:allow-appconfig-read-recursive","fs:allow-appconfig-write-recursive","fs:allow-appdata-read-recursive","fs:allow-appdata-write-recursive","fs:allow-applocaldata-read-recursive","fs:allow-applocaldata-write-recursive","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-mkdir","fs:allow-exists","fs:allow-remove","fs:allow-resource-read-recursive","fs:allow-resource-write-recursive","fs:allow-download-read-recursive","fs:allow-download-write-recursive","fs:default",{"identifier":"fs:scope","allow":[{"path":"**"}]},"http:allow-fetch-send","http:allow-fetch","http:default","notification:allow-create-channel","notification:allow-list-channels","notification:allow-notify","notification:allow-is-permission-granted","notification:default","opener:allow-open-url","opener:default","os:allow-hostname","os:default","store:default"]}}

View File

@ -43,7 +43,6 @@
draggable="true" draggable="true"
@click="openItem(item)" @click="openItem(item)"
@dragstart="handleDragStart($event, item)" @dragstart="handleDragStart($event, item)"
@dragend="handleDragEnd"
/> />
</UContextMenu> </UContextMenu>

View File

@ -47,7 +47,7 @@
/> />
</div> </div>
<div class="h-full"/> <div class="h-full" />
</div> </div>
</div> </div>
</template> </template>
@ -55,8 +55,14 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Locale } from 'vue-i18n' import type { Locale } from 'vue-i18n'
import { open } from '@tauri-apps/plugin-dialog' import { open } from '@tauri-apps/plugin-dialog'
import { readFile, writeFile, mkdir, exists, remove } from '@tauri-apps/plugin-fs' import {
import { appLocalDataDir, join } from '@tauri-apps/api/path' readFile,
writeFile,
mkdir,
exists,
remove,
} from '@tauri-apps/plugin-fs'
import { appLocalDataDir } from '@tauri-apps/api/path'
const { t, setLocale } = useI18n() const { t, setLocale } = useI18n()
@ -120,38 +126,115 @@ const selectBackgroundImage = async () => {
try { try {
const selected = await open({ const selected = await open({
multiple: false, multiple: false,
filters: [{ filters: [
{
name: 'Images', name: 'Images',
extensions: ['png', 'jpg', 'jpeg', 'webp'] extensions: ['png', 'jpg', 'jpeg', 'webp'],
}] },
],
}) })
if (selected && typeof selected === 'string') { if (!selected || typeof selected !== 'string') {
const fileData = await readFile(selected) return
// Create files directory if it doesn't exist
const appDataPath = await appLocalDataDir()
const filesDir = await join(appDataPath, 'files')
if (!await exists(filesDir)) {
await mkdir(filesDir, { recursive: true })
} }
// Generate unique filename for the background image // Read the selected file (works with Android photo picker URIs)
const ext = selected.split('.').pop()?.toLowerCase() || 'png' let fileData: Uint8Array
const fileName = `workspace-${currentWorkspace.value.id}-background.${ext}` try {
const targetPath = await join(filesDir, fileName) fileData = await readFile(selected)
} catch (readError) {
add({
description: `Fehler beim Lesen: ${readError instanceof Error ? readError.message : String(readError)}`,
color: 'error',
})
return
}
// Copy file to app data directory // Detect file type from file signature
let ext = 'jpg' // default
if (fileData.length > 4) {
// PNG signature: 89 50 4E 47
if (
fileData[0] === 0x89 &&
fileData[1] === 0x50 &&
fileData[2] === 0x4e &&
fileData[3] === 0x47
) {
ext = 'png'
}
// JPEG signature: FF D8 FF
else if (
fileData[0] === 0xff &&
fileData[1] === 0xd8 &&
fileData[2] === 0xff
) {
ext = 'jpg'
}
// WebP signature: RIFF xxxx WEBP
else if (
fileData[0] === 0x52 &&
fileData[1] === 0x49 &&
fileData[2] === 0x46 &&
fileData[3] === 0x46
) {
ext = 'webp'
}
}
// Get app local data directory
const appDataPath = await appLocalDataDir()
// Construct target path manually to avoid path joining issues
const fileName = `workspace-${currentWorkspace.value.id}-background.${ext}`
const targetPath = `${appDataPath}/files/${fileName}`
// Create parent directory if it doesn't exist
const parentDir = `${appDataPath}/files`
try {
if (!(await exists(parentDir))) {
await mkdir(parentDir, { recursive: true })
}
} catch (mkdirError) {
add({
description: `Fehler beim Erstellen des Ordners: ${mkdirError instanceof Error ? mkdirError.message : String(mkdirError)}`,
color: 'error',
})
return
}
// Write file to app data directory
try {
await writeFile(targetPath, fileData) await writeFile(targetPath, fileData)
} catch (writeError) {
add({
description: `Fehler beim Schreiben: ${writeError instanceof Error ? writeError.message : String(writeError)}`,
color: 'error',
})
return
}
// Store the absolute file path in database // Store the absolute file path in database
await updateWorkspaceBackgroundAsync(currentWorkspace.value.id, targetPath) try {
add({ description: t('workspaceBackground.update.success'), color: 'success' }) await updateWorkspaceBackgroundAsync(
currentWorkspace.value.id,
targetPath,
)
add({
description: t('workspaceBackground.update.success'),
color: 'success',
})
} catch (dbError) {
add({
description: `Fehler beim DB-Update: ${dbError instanceof Error ? dbError.message : String(dbError)}`,
color: 'error',
})
} }
} catch (error) { } catch (error) {
console.error('Error selecting background:', error) console.error('Error selecting background:', error)
add({ description: t('workspaceBackground.update.error'), color: 'error' }) add({
description: `${t('workspaceBackground.update.error')}: ${error instanceof Error ? error.message : String(error)}`,
color: 'error',
})
} }
} }
@ -173,7 +256,10 @@ const removeBackgroundImage = async () => {
} }
await updateWorkspaceBackgroundAsync(currentWorkspace.value.id, null) await updateWorkspaceBackgroundAsync(currentWorkspace.value.id, null)
add({ description: t('workspaceBackground.remove.success'), color: 'success' }) add({
description: t('workspaceBackground.remove.success'),
color: 'success',
})
} catch (error) { } catch (error) {
console.error('Error removing background:', error) console.error('Error removing background:', error)
add({ description: t('workspaceBackground.remove.error'), color: 'error' }) add({ description: t('workspaceBackground.remove.error'), color: 'error' })

View File

@ -300,6 +300,7 @@ export const useWindowManagerStore = defineStore('windowManager', () => {
const window = windows.value.find((w) => w.id === windowId) const window = windows.value.find((w) => w.id === windowId)
if (window) { if (window) {
window.zIndex = nextZIndex.value++ window.zIndex = nextZIndex.value++
window.isMinimized = false
activeWindowId.value = windowId activeWindowId.value = windowId
} }
} }