From 97a6c6d7a0eff92af98a83b3bee8d9e188376da3 Mon Sep 17 00:00:00 2001 From: mertalev <101130780+mertalev@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:25:25 +0300 Subject: [PATCH] cleanup restore xcode settings formatting restore xcode settings fix rebase --- mobile/ios/Runner.xcodeproj/project.pbxproj | 55 +- mobile/ios/Runner/Images/Thumbhash.swift | 649 ------------------ mobile/ios/Runner/Info.plist | 364 +++++----- mobile/ios/Runner/Runner.entitlements | 4 +- mobile/ios/Runner/RunnerProfile.entitlements | 4 +- .../ShareExtension.entitlements | 4 +- .../WidgetExtension.entitlements | 4 +- .../repositories/asset_media.repository.dart | 3 +- mobile/lib/main.dart | 3 +- .../widgets/album/album_selector.widget.dart | 5 +- .../asset_viewer/asset_viewer.page.dart | 5 +- .../asset_viewer/asset_viewer.state.dart | 2 +- .../widgets/images/local_image_provider.dart | 10 +- .../widgets/images/thumbnail.widget.dart | 13 +- .../widgets/images/thumbnail_tile.widget.dart | 7 +- .../widgets/memory/memory_lane.widget.dart | 10 +- .../widgets/timeline/fixed/segment.model.dart | 18 +- .../lib/widgets/common/immich_thumbnail.dart | 3 +- 18 files changed, 255 insertions(+), 908 deletions(-) delete mode 100644 mobile/ios/Runner/Images/Thumbhash.swift diff --git a/mobile/ios/Runner.xcodeproj/project.pbxproj b/mobile/ios/Runner.xcodeproj/project.pbxproj index 01ca6c4b2c..4557c8b706 100644 --- a/mobile/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/ios/Runner.xcodeproj/project.pbxproj @@ -24,7 +24,6 @@ FAC6F89B2D287C890078CB2F /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = FAC6F8902D287C890078CB2F /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; FAC6F8B72D287F120078CB2F /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC6F8B52D287F120078CB2F /* ShareViewController.swift */; }; FAC6F8B92D287F120078CB2F /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FAC6F8B32D287F120078CB2F /* MainInterface.storyboard */; }; - FE1BEAC92E264F8400D7F138 /* Thumbhash.swift in Resources */ = {isa = PBXBuildFile; fileRef = FE1BEAC82E264F8400D7F138 /* Thumbhash.swift */; }; FED3B1962E253E9B0030FD97 /* ThumbnailsImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED3B1942E253E9B0030FD97 /* ThumbnailsImpl.swift */; }; FED3B1972E253E9B0030FD97 /* Thumbnails.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = FED3B1932E253E9B0030FD97 /* Thumbnails.g.swift */; }; /* End PBXBuildFile section */ @@ -47,6 +46,16 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; FAC6F89A2D287C890078CB2F /* Embed Foundation Extensions */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -95,8 +104,6 @@ FAC6F8B42D287F120078CB2F /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = ""; }; FAC6F8B52D287F120078CB2F /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; FAC7416727DB9F5500C668D8 /* RunnerProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerProfile.entitlements; sourceTree = ""; }; - FE1BEAC82E264F8400D7F138 /* Thumbhash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbhash.swift; sourceTree = ""; }; - FED3B1472E253B110030FD97 /* Flutter.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:S8QB4VV633:FLUTTER.IO LLC"; lastKnownFileType = wrapper.xcframework; name = Flutter.xcframework; path = ../../../Flutter/bin/cache/artifacts/engine/ios/Flutter.xcframework; sourceTree = ""; }; FED3B1932E253E9B0030FD97 /* Thumbnails.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbnails.g.swift; sourceTree = ""; }; FED3B1942E253E9B0030FD97 /* ThumbnailsImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailsImpl.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -114,6 +121,8 @@ /* Begin PBXFileSystemSynchronizedRootGroup section */ B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = { isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + ); path = Sync; sourceTree = ""; }; @@ -172,7 +181,6 @@ 1754452DD81DA6620E279E51 /* Frameworks */ = { isa = PBXGroup; children = ( - FED3B1472E253B110030FD97 /* Flutter.xcframework */, 886774DBDDE6B35BF2B4F2CD /* Pods_Runner.framework */, 357FC57E54FD0F51795CF28A /* Pods_ShareExtension.framework */, F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */, @@ -258,7 +266,6 @@ FED3B1952E253E9B0030FD97 /* Images */ = { isa = PBXGroup; children = ( - FE1BEAC82E264F8400D7F138 /* Thumbhash.swift */, FED3B1932E253E9B0030FD97 /* Thumbnails.g.swift */, FED3B1942E253E9B0030FD97 /* ThumbnailsImpl.swift */, ); @@ -277,6 +284,7 @@ 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, FAC6F89A2D287C890078CB2F /* Embed Foundation Extensions */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, D218A34AEE62BC1EF119F5B0 /* [CP] Embed Pods Frameworks */, @@ -385,7 +393,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - FE1BEAC92E264F8400D7F138 /* Thumbhash.swift in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, @@ -660,7 +667,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; - DEVELOPMENT_TEAM = 33MF3D8ZGA; + DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -669,7 +676,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.121.0; - PRODUCT_BUNDLE_IDENTIFIER = app.mertalev.immich.profile; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile; PRODUCT_NAME = "Immich-Profile"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -804,7 +811,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; - DEVELOPMENT_TEAM = 33MF3D8ZGA; + DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -813,7 +820,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.121.0; - PRODUCT_BUNDLE_IDENTIFIER = app.mertalev.immich.vdebug; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug; PRODUCT_NAME = "Immich-Debug"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -834,7 +841,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; - DEVELOPMENT_TEAM = 33MF3D8ZGA; + DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 14.0; @@ -843,7 +850,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.121.0; - PRODUCT_BUNDLE_IDENTIFIER = app.mertalev.immich; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich; PRODUCT_NAME = Immich; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -867,7 +874,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 213; - DEVELOPMENT_TEAM = 33MF3D8ZGA; + DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -884,7 +891,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.mertalev.immich.vdebug.Widget; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.Widget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; @@ -910,7 +917,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 213; - DEVELOPMENT_TEAM = 33MF3D8ZGA; + DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -926,7 +933,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.mertalev.immich.Widget; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.Widget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -950,7 +957,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 213; - DEVELOPMENT_TEAM = 33MF3D8ZGA; + DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -966,7 +973,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.mertalev.immich.profile.Widget; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.Widget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -990,7 +997,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; - DEVELOPMENT_TEAM = 33MF3D8ZGA; + DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1007,7 +1014,7 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.mertalev.immich.vdebug.ShareExtension; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.vdebug.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -1034,7 +1041,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; - DEVELOPMENT_TEAM = 33MF3D8ZGA; + DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1050,7 +1057,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.mertalev.immich.ShareExtension; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; @@ -1075,7 +1082,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 213; CUSTOM_GROUP_ID = group.app.immich.share; - DEVELOPMENT_TEAM = 33MF3D8ZGA; + DEVELOPMENT_TEAM = 2F67MQ8R79; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -1091,7 +1098,7 @@ LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = app.mertalev.immich.profile.ShareExtension; + PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich.profile.ShareExtension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; diff --git a/mobile/ios/Runner/Images/Thumbhash.swift b/mobile/ios/Runner/Images/Thumbhash.swift deleted file mode 100644 index 8e461f1710..0000000000 --- a/mobile/ios/Runner/Images/Thumbhash.swift +++ /dev/null @@ -1,649 +0,0 @@ -import Foundation - -// NOTE: Swift has an exponential-time type checker and compiling very simple -// expressions can easily take many seconds, especially when expressions involve -// numeric type constructors. -// -// This file deliberately breaks compound expressions up into separate variables -// to improve compile time even though this comes at the expense of readability. -// This is a known workaround for this deficiency in the Swift compiler. -// -// The following command is helpful when debugging Swift compile time issues: -// -// swiftc ThumbHash.swift -Xfrontend -debug-time-function-bodies -// -// These optimizations brought the compile time for this file from around 2.5 -// seconds to around 250ms (10x faster). - -// NOTE: Swift's debug-build performance of for-in loops over numeric ranges is -// really awful. Debug builds compile a very generic indexing iterator thing -// that makes many nested calls for every iteration, which makes debug-build -// performance crawl. -// -// This file deliberately avoids for-in loops that loop for more than a few -// times to improve debug-build run time even though this comes at the expense -// of readability. Similarly unsafe pointers are used instead of array getters -// to avoid unnecessary bounds checks, which have extra overhead in debug builds. -// -// These optimizations brought the run time to encode and decode 10 ThumbHashes -// in debug mode from 700ms to 70ms (10x faster). - -func rgbaToThumbHash(w: Int, h: Int, rgba: Data) -> Data { - // Encoding an image larger than 100x100 is slow with no benefit - assert(w <= 100 && h <= 100) - assert(rgba.count == w * h * 4) - - // Determine the average color - var avg_r: Float32 = 0 - var avg_g: Float32 = 0 - var avg_b: Float32 = 0 - var avg_a: Float32 = 0 - rgba.withUnsafeBytes { rgba in - var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count) - let n = w * h - var i = 0 - while i < n { - let alpha = Float32(rgba[3]) / 255 - avg_r += alpha / 255 * Float32(rgba[0]) - avg_g += alpha / 255 * Float32(rgba[1]) - avg_b += alpha / 255 * Float32(rgba[2]) - avg_a += alpha - rgba = rgba.advanced(by: 4) - i += 1 - } - } - if avg_a > 0 { - avg_r /= avg_a - avg_g /= avg_a - avg_b /= avg_a - } - - let hasAlpha = avg_a < Float32(w * h) - let l_limit = hasAlpha ? 5 : 7 // Use fewer luminance bits if there's alpha - let imax_wh = max(w, h) - let iwl_limit = l_limit * w - let ihl_limit = l_limit * h - let fmax_wh = Float32(imax_wh) - let fwl_limit = Float32(iwl_limit) - let fhl_limit = Float32(ihl_limit) - let flx = round(fwl_limit / fmax_wh) - let fly = round(fhl_limit / fmax_wh) - var lx = Int(flx) - var ly = Int(fly) - lx = max(1, lx) - ly = max(1, ly) - var lpqa = [Float32](repeating: 0, count: w * h * 4) - - // Convert the image from RGBA to LPQA (composite atop the average color) - rgba.withUnsafeBytes { rgba in - lpqa.withUnsafeMutableBytes { lpqa in - var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count) - var lpqa = lpqa.baseAddress!.bindMemory(to: Float32.self, capacity: lpqa.count) - let n = w * h - var i = 0 - while i < n { - let alpha = Float32(rgba[3]) / 255 - let r = avg_r * (1 - alpha) + alpha / 255 * Float32(rgba[0]) - let g = avg_g * (1 - alpha) + alpha / 255 * Float32(rgba[1]) - let b = avg_b * (1 - alpha) + alpha / 255 * Float32(rgba[2]) - lpqa[0] = (r + g + b) / 3 - lpqa[1] = (r + g) / 2 - b - lpqa[2] = r - g - lpqa[3] = alpha - rgba = rgba.advanced(by: 4) - lpqa = lpqa.advanced(by: 4) - i += 1 - } - } - } - - // Encode using the DCT into DC (constant) and normalized AC (varying) terms - let encodeChannel = { (channel: UnsafePointer, nx: Int, ny: Int) -> (Float32, [Float32], Float32) in - var dc: Float32 = 0 - var ac: [Float32] = [] - var scale: Float32 = 0 - var fx = [Float32](repeating: 0, count: w) - fx.withUnsafeMutableBytes { fx in - let fx = fx.baseAddress!.bindMemory(to: Float32.self, capacity: fx.count) - var cy = 0 - while cy < ny { - var cx = 0 - while cx * ny < nx * (ny - cy) { - var ptr = channel - var f: Float32 = 0 - var x = 0 - while x < w { - let fw = Float32(w) - let fxx = Float32(x) - let fcx = Float32(cx) - fx[x] = cos(Float32.pi / fw * fcx * (fxx + 0.5)) - x += 1 - } - var y = 0 - while y < h { - let fh = Float32(h) - let fyy = Float32(y) - let fcy = Float32(cy) - let fy = cos(Float32.pi / fh * fcy * (fyy + 0.5)) - var x = 0 - while x < w { - f += ptr.pointee * fx[x] * fy - x += 1 - ptr = ptr.advanced(by: 4) - } - y += 1 - } - f /= Float32(w * h) - if cx > 0 || cy > 0 { - ac.append(f) - scale = max(scale, abs(f)) - } else { - dc = f - } - cx += 1 - } - cy += 1 - } - } - if scale > 0 { - let n = ac.count - var i = 0 - while i < n { - ac[i] = 0.5 + 0.5 / scale * ac[i] - i += 1 - } - } - return (dc, ac, scale) - } - let ( - (l_dc, l_ac, l_scale), - (p_dc, p_ac, p_scale), - (q_dc, q_ac, q_scale), - (a_dc, a_ac, a_scale) - ) = lpqa.withUnsafeBytes { lpqa in - let lpqa = lpqa.baseAddress!.bindMemory(to: Float32.self, capacity: lpqa.count) - return ( - encodeChannel(lpqa, max(3, lx), max(3, ly)), - encodeChannel(lpqa.advanced(by: 1), 3, 3), - encodeChannel(lpqa.advanced(by: 2), 3, 3), - hasAlpha ? encodeChannel(lpqa.advanced(by: 3), 5, 5) : (1, [], 1) - ) - } - - // Write the constants - let isLandscape = w > h - let fl_dc = round(63.0 * l_dc) - let fp_dc = round(31.5 + 31.5 * p_dc) - let fq_dc = round(31.5 + 31.5 * q_dc) - let fl_scale = round(31.0 * l_scale) - let il_dc = UInt32(fl_dc) - let ip_dc = UInt32(fp_dc) - let iq_dc = UInt32(fq_dc) - let il_scale = UInt32(fl_scale) - let ihasAlpha = UInt32(hasAlpha ? 1 : 0) - let header24 = il_dc | (ip_dc << 6) | (iq_dc << 12) | (il_scale << 18) | (ihasAlpha << 23) - let fp_scale = round(63.0 * p_scale) - let fq_scale = round(63.0 * q_scale) - let ilxy = UInt16(isLandscape ? ly : lx) - let ip_scale = UInt16(fp_scale) - let iq_scale = UInt16(fq_scale) - let iisLandscape = UInt16(isLandscape ? 1 : 0) - let header16 = ilxy | (ip_scale << 3) | (iq_scale << 9) | (iisLandscape << 15) - var hash = Data(capacity: 25) - hash.append(UInt8(header24 & 255)) - hash.append(UInt8((header24 >> 8) & 255)) - hash.append(UInt8(header24 >> 16)) - hash.append(UInt8(header16 & 255)) - hash.append(UInt8(header16 >> 8)) - var isOdd = false - if hasAlpha { - let fa_dc = round(15.0 * a_dc) - let fa_scale = round(15.0 * a_scale) - let ia_dc = UInt8(fa_dc) - let ia_scale = UInt8(fa_scale) - hash.append(ia_dc | (ia_scale << 4)) - } - - // Write the varying factors - for ac in [l_ac, p_ac, q_ac] { - for f in ac { - let f15 = round(15.0 * f) - let i15 = UInt8(f15) - if isOdd { - hash[hash.count - 1] |= i15 << 4 - } else { - hash.append(i15) - } - isOdd = !isOdd - } - } - if hasAlpha { - for f in a_ac { - let f15 = round(15.0 * f) - let i15 = UInt8(f15) - if isOdd { - hash[hash.count - 1] |= i15 << 4 - } else { - hash.append(i15) - } - isOdd = !isOdd - } - } - return hash -} - -func thumbHashToRGBA(hash: Data) -> (Int, Int, Data) { - // Read the constants - let h0 = UInt32(hash[0]) - let h1 = UInt32(hash[1]) - let h2 = UInt32(hash[2]) - let h3 = UInt16(hash[3]) - let h4 = UInt16(hash[4]) - let header24 = h0 | (h1 << 8) | (h2 << 16) - let header16 = h3 | (h4 << 8) - let il_dc = header24 & 63 - let ip_dc = (header24 >> 6) & 63 - let iq_dc = (header24 >> 12) & 63 - var l_dc = Float32(il_dc) - var p_dc = Float32(ip_dc) - var q_dc = Float32(iq_dc) - l_dc = l_dc / 63 - p_dc = p_dc / 31.5 - 1 - q_dc = q_dc / 31.5 - 1 - let il_scale = (header24 >> 18) & 31 - var l_scale = Float32(il_scale) - l_scale = l_scale / 31 - let hasAlpha = (header24 >> 23) != 0 - let ip_scale = (header16 >> 3) & 63 - let iq_scale = (header16 >> 9) & 63 - var p_scale = Float32(ip_scale) - var q_scale = Float32(iq_scale) - p_scale = p_scale / 63 - q_scale = q_scale / 63 - let isLandscape = (header16 >> 15) != 0 - let lx16 = max(3, isLandscape ? hasAlpha ? 5 : 7 : header16 & 7) - let ly16 = max(3, isLandscape ? header16 & 7 : hasAlpha ? 5 : 7) - let lx = Int(lx16) - let ly = Int(ly16) - var a_dc = Float32(1) - var a_scale = Float32(1) - if hasAlpha { - let ia_dc = hash[5] & 15 - let ia_scale = hash[5] >> 4 - a_dc = Float32(ia_dc) - a_scale = Float32(ia_scale) - a_dc /= 15 - a_scale /= 15 - } - - // Read the varying factors (boost saturation by 1.25x to compensate for quantization) - let ac_start = hasAlpha ? 6 : 5 - var ac_index = 0 - let decodeChannel = { (nx: Int, ny: Int, scale: Float32) -> [Float32] in - var ac: [Float32] = [] - for cy in 0 ..< ny { - var cx = cy > 0 ? 0 : 1 - while cx * ny < nx * (ny - cy) { - let iac = (hash[ac_start + (ac_index >> 1)] >> ((ac_index & 1) << 2)) & 15; - var fac = Float32(iac) - fac = (fac / 7.5 - 1) * scale - ac.append(fac) - ac_index += 1 - cx += 1 - } - } - return ac - } - let l_ac = decodeChannel(lx, ly, l_scale) - let p_ac = decodeChannel(3, 3, p_scale * 1.25) - let q_ac = decodeChannel(3, 3, q_scale * 1.25) - let a_ac = hasAlpha ? decodeChannel(5, 5, a_scale) : [] - - // Decode using the DCT into RGB - let ratio = thumbHashToApproximateAspectRatio(hash: hash) - let fw = round(ratio > 1 ? 32 : 32 * ratio) - let fh = round(ratio > 1 ? 32 / ratio : 32) - let w = Int(fw) - let h = Int(fh) - var rgba = Data(count: w * h * 4) - let cx_stop = max(lx, hasAlpha ? 5 : 3) - let cy_stop = max(ly, hasAlpha ? 5 : 3) - var fx = [Float32](repeating: 0, count: cx_stop) - var fy = [Float32](repeating: 0, count: cy_stop) - fx.withUnsafeMutableBytes { fx in - let fx = fx.baseAddress!.bindMemory(to: Float32.self, capacity: fx.count) - fy.withUnsafeMutableBytes { fy in - let fy = fy.baseAddress!.bindMemory(to: Float32.self, capacity: fy.count) - rgba.withUnsafeMutableBytes { rgba in - var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count) - var y = 0 - while y < h { - var x = 0 - while x < w { - var l = l_dc - var p = p_dc - var q = q_dc - var a = a_dc - - // Precompute the coefficients - var cx = 0 - while cx < cx_stop { - let fw = Float32(w) - let fxx = Float32(x) - let fcx = Float32(cx) - fx[cx] = cos(Float32.pi / fw * (fxx + 0.5) * fcx) - cx += 1 - } - var cy = 0 - while cy < cy_stop { - let fh = Float32(h) - let fyy = Float32(y) - let fcy = Float32(cy) - fy[cy] = cos(Float32.pi / fh * (fyy + 0.5) * fcy) - cy += 1 - } - - // Decode L - var j = 0 - cy = 0 - while cy < ly { - var cx = cy > 0 ? 0 : 1 - let fy2 = fy[cy] * 2 - while cx * ly < lx * (ly - cy) { - l += l_ac[j] * fx[cx] * fy2 - j += 1 - cx += 1 - } - cy += 1 - } - - // Decode P and Q - j = 0 - cy = 0 - while cy < 3 { - var cx = cy > 0 ? 0 : 1 - let fy2 = fy[cy] * 2 - while cx < 3 - cy { - let f = fx[cx] * fy2 - p += p_ac[j] * f - q += q_ac[j] * f - j += 1 - cx += 1 - } - cy += 1 - } - - // Decode A - if hasAlpha { - j = 0 - cy = 0 - while cy < 5 { - var cx = cy > 0 ? 0 : 1 - let fy2 = fy[cy] * 2 - while cx < 5 - cy { - a += a_ac[j] * fx[cx] * fy2 - j += 1 - cx += 1 - } - cy += 1 - } - } - - // Convert to RGB - var b = l - 2 / 3 * p - var r = (3 * l - b + q) / 2 - var g = r - q - r = max(0, 255 * min(1, r)) - g = max(0, 255 * min(1, g)) - b = max(0, 255 * min(1, b)) - a = max(0, 255 * min(1, a)) - rgba[0] = UInt8(r) - rgba[1] = UInt8(g) - rgba[2] = UInt8(b) - rgba[3] = UInt8(a) - rgba = rgba.advanced(by: 4) - x += 1 - } - y += 1 - } - } - } - } - return (w, h, rgba) -} - -func thumbHashToAverageRGBA(hash: Data) -> (Float32, Float32, Float32, Float32) { - let h0 = UInt32(hash[0]) - let h1 = UInt32(hash[1]) - let h2 = UInt32(hash[2]) - let header = h0 | (h1 << 8) | (h2 << 16) - let il = header & 63 - let ip = (header >> 6) & 63 - let iq = (header >> 12) & 63 - var l = Float32(il) - var p = Float32(ip) - var q = Float32(iq) - l = l / 63 - p = p / 31.5 - 1 - q = q / 31.5 - 1 - let hasAlpha = (header >> 23) != 0 - var a = Float32(1) - if hasAlpha { - let ia = hash[5] & 15 - a = Float32(ia) - a = a / 15 - } - let b = l - 2 / 3 * p - let r = (3 * l - b + q) / 2 - let g = r - q - return ( - max(0, min(1, r)), - max(0, min(1, g)), - max(0, min(1, b)), - a - ) -} - -func thumbHashToApproximateAspectRatio(hash: Data) -> Float32 { - let header = hash[3] - let hasAlpha = (hash[2] & 0x80) != 0 - let isLandscape = (hash[4] & 0x80) != 0 - let lx = isLandscape ? hasAlpha ? 5 : 7 : header & 7 - let ly = isLandscape ? header & 7 : hasAlpha ? 5 : 7 - return Float32(lx) / Float32(ly) -} - -#if os(macOS) -import Cocoa - -func imageToThumbHash(image: NSImage) -> Data { - let size = image.size - let fw = round(100 * size.width / max(size.width, size.height)) - let fh = round(100 * size.height / max(size.width, size.height)) - let w = Int(fw) - let h = Int(fh) - var rgba = Data(count: w * h * 4) - rgba.withUnsafeMutableBytes { rgba in - var rect = NSRect(x: 0, y: 0, width: w, height: h) - if - let cgImage = image.cgImage(forProposedRect: &rect, context: nil, hints: nil), - let space = (image.representations[0] as? NSBitmapImageRep)?.colorSpace.cgColorSpace, - let context = CGContext( - data: rgba.baseAddress, - width: w, - height: h, - bitsPerComponent: 8, - bytesPerRow: w * 4, - space: space, - bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue - ) - { - context.draw(cgImage, in: rect) - - // Convert from premultiplied alpha to unpremultiplied alpha - var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count) - let n = w * h - var i = 0 - while i < n { - let a = UInt16(rgba[3]) - if a > 0 && a < 255 { - var r = UInt16(rgba[0]) - var g = UInt16(rgba[1]) - var b = UInt16(rgba[2]) - r = min(255, r * 255 / a) - g = min(255, g * 255 / a) - b = min(255, b * 255 / a) - rgba[0] = UInt8(r) - rgba[1] = UInt8(g) - rgba[2] = UInt8(b) - } - rgba = rgba.advanced(by: 4) - i += 1 - } - } - } - return rgbaToThumbHash(w: w, h: h, rgba: rgba) -} - -func thumbHashToImage(hash: Data) -> NSImage { - let (w, h, rgba) = thumbHashToRGBA(hash: hash) - let bitmap = NSBitmapImageRep( - bitmapDataPlanes: nil, - pixelsWide: w, - pixelsHigh: h, - bitsPerSample: 8, - samplesPerPixel: 4, - hasAlpha: true, - isPlanar: false, - colorSpaceName: .deviceRGB, - bytesPerRow: w * 4, - bitsPerPixel: 32 - )! - rgba.withUnsafeBytes { rgba in - // Convert from unpremultiplied alpha to premultiplied alpha - var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count) - var to = bitmap.bitmapData! - let n = w * h - var i = 0 - while i < n { - let a = rgba[3] - if a == 255 { - to[0] = rgba[0] - to[1] = rgba[1] - to[2] = rgba[2] - } else { - var r = UInt16(rgba[0]) - var g = UInt16(rgba[1]) - var b = UInt16(rgba[2]) - let a = UInt16(a) - r = min(255, r * a / 255) - g = min(255, g * a / 255) - b = min(255, b * a / 255) - to[0] = UInt8(r) - to[1] = UInt8(g) - to[2] = UInt8(b) - } - to[3] = a - rgba = rgba.advanced(by: 4) - to = to.advanced(by: 4) - i += 1 - } - } - let image = NSImage(size: NSSize(width: w, height: h)) - image.addRepresentation(bitmap) - return image -} -#endif - -#if os(iOS) -import UIKit - -func imageToThumbHash(image: UIImage) -> Data { - let size = image.size - let w = Int(round(100 * size.width / max(size.width, size.height))) - let h = Int(round(100 * size.height / max(size.width, size.height))) - var rgba = Data(count: w * h * 4) - rgba.withUnsafeMutableBytes { rgba in - if - let space = image.cgImage?.colorSpace, - let context = CGContext( - data: rgba.baseAddress, - width: w, - height: h, - bitsPerComponent: 8, - bytesPerRow: w * 4, - space: space, - bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue - ) - { - // EXIF orientation only works if you draw the UIImage, not the CGImage - context.concatenate(CGAffineTransform(1, 0, 0, -1, 0, CGFloat(h))) - UIGraphicsPushContext(context) - image.draw(in: CGRect(x: 0, y: 0, width: w, height: h)) - UIGraphicsPopContext() - - // Convert from premultiplied alpha to unpremultiplied alpha - var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count) - let n = w * h - var i = 0 - while i < n { - let a = UInt16(rgba[3]) - if a > 0 && a < 255 { - var r = UInt16(rgba[0]) - var g = UInt16(rgba[1]) - var b = UInt16(rgba[2]) - r = min(255, r * 255 / a) - g = min(255, g * 255 / a) - b = min(255, b * 255 / a) - rgba[0] = UInt8(r) - rgba[1] = UInt8(g) - rgba[2] = UInt8(b) - } - rgba = rgba.advanced(by: 4) - i += 1 - } - } - } - return rgbaToThumbHash(w: w, h: h, rgba: rgba) -} - -func thumbHashToImage(hash: Data) -> UIImage { - var (w, h, rgba) = thumbHashToRGBA(hash: hash) - rgba.withUnsafeMutableBytes { rgba in - // Convert from unpremultiplied alpha to premultiplied alpha - var rgba = rgba.baseAddress!.bindMemory(to: UInt8.self, capacity: rgba.count) - let n = w * h - var i = 0 - while i < n { - let a = UInt16(rgba[3]) - if a < 255 { - var r = UInt16(rgba[0]) - var g = UInt16(rgba[1]) - var b = UInt16(rgba[2]) - r = min(255, r * a / 255) - g = min(255, g * a / 255) - b = min(255, b * a / 255) - rgba[0] = UInt8(r) - rgba[1] = UInt8(g) - rgba[2] = UInt8(b) - } - rgba = rgba.advanced(by: 4) - i += 1 - } - } - let image = CGImage( - width: w, - height: h, - bitsPerComponent: 8, - bitsPerPixel: 32, - bytesPerRow: w * 4, - space: CGColorSpaceCreateDeviceRGB(), - bitmapInfo: CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue), - provider: CGDataProvider(data: rgba as CFData)!, - decode: nil, - shouldInterpolate: true, - intent: .perceptual - ) - return UIImage(cgImage: image!) -} -#endif diff --git a/mobile/ios/Runner/Info.plist b/mobile/ios/Runner/Info.plist index b10b0fa08c..4b93dc820f 100644 --- a/mobile/ios/Runner/Info.plist +++ b/mobile/ios/Runner/Info.plist @@ -1,187 +1,187 @@ - - AppGroupId - $(CUSTOM_GROUP_ID) - BGTaskSchedulerPermittedIdentifiers - - app.alextran.immich.backgroundFetch - app.alextran.immich.backgroundProcessing - - CADisableMinimumFrameDurationOnPhone - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleDocumentTypes - - - CFBundleTypeName - ShareHandler - LSHandlerRank - Alternate - LSItemContentTypes - - public.file-url - public.image - public.text - public.movie - public.url - public.data - - - - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleLocalizations - - en - ar - ca - cs - da - de - es - fi - fr - he - hi - hu - it - ja - ko - lv - mn - nb - nl - pl - pt - ro - ru - sk - sl - sr - sv - th - uk - vi - zh - - CFBundleName - immich_mobile - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.137.2 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleTypeRole - Editor - CFBundleURLName - Share Extension - CFBundleURLSchemes - - ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) - - - - CFBundleTypeRole - Editor - CFBundleURLName - Deep Link - CFBundleURLSchemes - - immich - - - - CFBundleVersion - 213 - FLTEnableImpeller - - ITSAppUsesNonExemptEncryption - - LSApplicationQueriesSchemes - - https - - LSRequiresIPhoneOS - - LSSupportsOpeningDocumentsInPlace - No - MGLMapboxMetricsEnabledSettingShownInApp - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - NSBonjourServices - - _googlecast._tcp - _CC1AD845._googlecast._tcp - - NSCameraUsageDescription - We need to access the camera to let you take beautiful video using this app - NSFaceIDUsageDescription - We need to use FaceID to allow access to your locked folder - NSLocalNetworkUsageDescription - We need local network permission to connect to the local server using IP address and + + AppGroupId + $(CUSTOM_GROUP_ID) + BGTaskSchedulerPermittedIdentifiers + + app.alextran.immich.backgroundFetch + app.alextran.immich.backgroundProcessing + + CADisableMinimumFrameDurationOnPhone + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleDocumentTypes + + + CFBundleTypeName + ShareHandler + LSHandlerRank + Alternate + LSItemContentTypes + + public.file-url + public.image + public.text + public.movie + public.url + public.data + + + + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLocalizations + + en + ar + ca + cs + da + de + es + fi + fr + he + hi + hu + it + ja + ko + lv + mn + nb + nl + pl + pt + ro + ru + sk + sl + sr + sv + th + uk + vi + zh + + CFBundleName + immich_mobile + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.137.2 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + Share Extension + CFBundleURLSchemes + + ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) + + + + CFBundleTypeRole + Editor + CFBundleURLName + Deep Link + CFBundleURLSchemes + + immich + + + + CFBundleVersion + 213 + FLTEnableImpeller + + ITSAppUsesNonExemptEncryption + + LSApplicationQueriesSchemes + + https + + LSRequiresIPhoneOS + + LSSupportsOpeningDocumentsInPlace + No + MGLMapboxMetricsEnabledSettingShownInApp + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSBonjourServices + + _googlecast._tcp + _CC1AD845._googlecast._tcp + + NSCameraUsageDescription + We need to access the camera to let you take beautiful video using this app + NSFaceIDUsageDescription + We need to use FaceID to allow access to your locked folder + NSLocationAlwaysAndWhenInUseUsageDescription + We require this permission to access the local WiFi name for background upload mechanism + NSLocationUsageDescription + We require this permission to access the local WiFi name + NSLocationWhenInUseUsageDescription + We require this permission to access the local WiFi name + NSMicrophoneUsageDescription + We need to access the microphone to let you take beautiful video using this app + NSPhotoLibraryAddUsageDescription + We need to manage backup your photos album + NSPhotoLibraryUsageDescription + We need to manage backup your photos album + NSUserActivityTypes + + INSendMessageIntent + + UIApplicationSupportsIndirectInputEvents + + UIBackgroundModes + + fetch + processing + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIStatusBarHidden + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + io.flutter.embedded_views_preview + + NSLocalNetworkUsageDescription + We need local network permission to connect to the local server using IP address and allow the casting feature to work - NSLocationAlwaysAndWhenInUseUsageDescription - We require this permission to access the local WiFi name for background upload mechanism - NSLocationUsageDescription - We require this permission to access the local WiFi name - NSLocationWhenInUseUsageDescription - We require this permission to access the local WiFi name - NSMicrophoneUsageDescription - We need to access the microphone to let you take beautiful video using this app - NSPhotoLibraryAddUsageDescription - We need to manage backup your photos album - NSPhotoLibraryUsageDescription - We need to manage backup your photos album - NSUserActivityTypes - - INSendMessageIntent - - UIApplicationSupportsIndirectInputEvents - - UIBackgroundModes - - fetch - processing - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIStatusBarHidden - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - io.flutter.embedded_views_preview - - + diff --git a/mobile/ios/Runner/Runner.entitlements b/mobile/ios/Runner/Runner.entitlements index cfbada638d..e5862cb213 100644 --- a/mobile/ios/Runner/Runner.entitlements +++ b/mobile/ios/Runner/Runner.entitlements @@ -9,6 +9,8 @@ com.apple.developer.networking.wifi-info com.apple.security.application-groups - + + group.app.immich.share + diff --git a/mobile/ios/Runner/RunnerProfile.entitlements b/mobile/ios/Runner/RunnerProfile.entitlements index 6a397cfd54..6a5c086baf 100644 --- a/mobile/ios/Runner/RunnerProfile.entitlements +++ b/mobile/ios/Runner/RunnerProfile.entitlements @@ -11,6 +11,8 @@ com.apple.developer.networking.wifi-info com.apple.security.application-groups - + + group.app.immich.share + diff --git a/mobile/ios/ShareExtension/ShareExtension.entitlements b/mobile/ios/ShareExtension/ShareExtension.entitlements index 2eb7e333a6..4ad1a257d8 100644 --- a/mobile/ios/ShareExtension/ShareExtension.entitlements +++ b/mobile/ios/ShareExtension/ShareExtension.entitlements @@ -3,6 +3,8 @@ com.apple.security.application-groups - + + group.app.immich.share + diff --git a/mobile/ios/WidgetExtension/WidgetExtension.entitlements b/mobile/ios/WidgetExtension/WidgetExtension.entitlements index 2eb7e333a6..4ad1a257d8 100644 --- a/mobile/ios/WidgetExtension/WidgetExtension.entitlements +++ b/mobile/ios/WidgetExtension/WidgetExtension.entitlements @@ -3,6 +3,8 @@ com.apple.security.application-groups - + + group.app.immich.share + diff --git a/mobile/lib/infrastructure/repositories/asset_media.repository.dart b/mobile/lib/infrastructure/repositories/asset_media.repository.dart index d640d47910..7c3c3fddb6 100644 --- a/mobile/lib/infrastructure/repositories/asset_media.repository.dart +++ b/mobile/lib/infrastructure/repositories/asset_media.repository.dart @@ -23,8 +23,7 @@ class AssetMediaRepository { final actualSize = actualWidth * actualHeight * 4; try { - final buffer = - await ImmutableBuffer.fromUint8List(pointer.asTypedList(actualSize)); + final buffer = await ImmutableBuffer.fromUint8List(pointer.asTypedList(actualSize)); final descriptor = ui.ImageDescriptor.raw( buffer, width: actualWidth, diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 7b6a4f8f1f..56f0593604 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -71,8 +71,7 @@ Future initApp() async { } } - PaintingBinding.instance.imageCache.maximumSizeBytes = - kTimelineImageCacheMemory; + PaintingBinding.instance.imageCache.maximumSizeBytes = kTimelineImageCacheMemory; await DynamicTheme.fetchSystemPalette(); diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index a97ca736d1..b003180e00 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -12,6 +12,7 @@ import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/albums/album_search.model.dart'; import 'package:immich_mobile/pages/common/large_leading_tile.dart'; +import 'package:immich_mobile/presentation/widgets/images/remote_image_provider.dart'; import 'package:immich_mobile/presentation/widgets/images/thumbnail.widget.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; @@ -443,7 +444,7 @@ class _AlbumList extends ConsumerWidget { leading: album.thumbnailAssetId != null ? ClipRRect( borderRadius: const BorderRadius.all(Radius.circular(15)), - child: SizedBox(width: 80, height: 80, child: Thumbnail(remoteId: album.thumbnailAssetId)), + child: SizedBox(width: 80, height: 80, child: Thumbnail(imageProvider: RemoteThumbProvider(assetId: album.thumbnailAssetId!))), ) : SizedBox( width: 80, @@ -529,7 +530,7 @@ class _GridAlbumCard extends ConsumerWidget { child: SizedBox( width: double.infinity, child: album.thumbnailAssetId != null - ? Thumbnail(remoteId: album.thumbnailAssetId) + ? Thumbnail(imageProvider: RemoteThumbProvider(assetId: album.thumbnailAssetId!)) : Container( color: context.colorScheme.surfaceContainerHighest, child: const Icon(Icons.photo_album_rounded, size: 40, color: Colors.grey), diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart index c70c904750..f38f4552a6 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.page.dart @@ -577,10 +577,7 @@ class _AssetViewerState extends ConsumerState { // Using multiple selectors to avoid unnecessary rebuilds for other state changes ref.watch( assetViewerProvider.select( - (s) => - s.showingBottomSheet.hashCode ^ - s.backgroundOpacity.hashCode ^ - s.stackIndex.hashCode, + (s) => s.showingBottomSheet.hashCode ^ s.backgroundOpacity.hashCode ^ s.stackIndex.hashCode, ), ); ref.watch(isPlayingMotionVideoProvider); diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart index 096d5b1782..e6e7464962 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_viewer.state.dart @@ -77,7 +77,7 @@ class AssetViewerStateNotifier extends AutoDisposeNotifier { void setAsset(BaseAsset? asset) { if (asset != state.currentAsset) { state = state.copyWith(currentAsset: asset, stackIndex: 0); - } + } } void setOpacity(int opacity) { diff --git a/mobile/lib/presentation/widgets/images/local_image_provider.dart b/mobile/lib/presentation/widgets/images/local_image_provider.dart index feb89ec228..8b9cd5ea01 100644 --- a/mobile/lib/presentation/widgets/images/local_image_provider.dart +++ b/mobile/lib/presentation/widgets/images/local_image_provider.dart @@ -39,8 +39,7 @@ class LocalThumbProvider extends ImageProvider { } Future _codec(LocalThumbProvider key) async { - final codec = - await _assetMediaRepository.getLocalThumbnail(key.id, key.size); + final codec = await _assetMediaRepository.getLocalThumbnail(key.id, key.size); return ImageInfo(image: (await codec.getNextFrame()).image, scale: 1.0); } @@ -48,9 +47,7 @@ class LocalThumbProvider extends ImageProvider { bool operator ==(Object other) { if (identical(this, other)) return true; if (other is LocalThumbProvider) { - return id == other.id && - size == other.size && - updatedAt == other.updatedAt; + return id == other.id && size == other.size && updatedAt == other.updatedAt; } return false; } @@ -81,8 +78,7 @@ class LocalFullImageProvider extends ImageProvider { } Future _codec(LocalFullImageProvider key) async { - final devicePixelRatio = - PlatformDispatcher.instance.views.first.devicePixelRatio; + final devicePixelRatio = PlatformDispatcher.instance.views.first.devicePixelRatio; final codec = await _assetMediaRepository.getLocalThumbnail( key.id, Size(size.width * devicePixelRatio, size.height * devicePixelRatio), diff --git a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart index b46b022508..ae20ab52d9 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail.widget.dart @@ -106,8 +106,7 @@ class _ThumbnailState extends State { super.didUpdateWidget(oldWidget); if (oldWidget.imageProvider != widget.imageProvider || oldWidget.blurhash != widget.blurhash || - (oldWidget.thumbhashMode == ThumbhashMode.disabled && - oldWidget.thumbhashMode != ThumbhashMode.disabled)) { + (oldWidget.thumbhashMode == ThumbhashMode.disabled && oldWidget.thumbhashMode != ThumbhashMode.disabled)) { _loadImage(); } } @@ -120,13 +119,11 @@ class _ThumbnailState extends State { void _loadImage() { _stopListeningToStream(); - if (widget.thumbhashMode != ThumbhashMode.disabled && - widget.blurhash != null) { + if (widget.thumbhashMode != ThumbhashMode.disabled && widget.blurhash != null) { _decodeThumbhash(); } - if (widget.thumbhashMode != ThumbhashMode.only && - widget.imageProvider != null) { + if (widget.thumbhashMode != ThumbhashMode.only && widget.imageProvider != null) { _loadFromProvider(); } } @@ -298,9 +295,7 @@ class _ThumbnailRenderBox extends RenderBox { if (_fadeStartTime != null) { final elapsed = DateTime.now().difference(_fadeStartTime!); - _crossFadeProgress = - (elapsed.inMilliseconds / _fadeDuration.inMilliseconds) - .clamp(0.0, 1.0); + _crossFadeProgress = (elapsed.inMilliseconds / _fadeDuration.inMilliseconds).clamp(0.0, 1.0); if (_crossFadeProgress < 1.0) { SchedulerBinding.instance.scheduleFrameCallback((_) { diff --git a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart index e65cc0fd27..171b9a40f1 100644 --- a/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart +++ b/mobile/lib/presentation/widgets/images/thumbnail_tile.widget.dart @@ -42,8 +42,7 @@ class ThumbnailTile extends ConsumerWidget { final isSelected = ref.watch( multiSelectProvider.select((multiselect) => multiselect.selectedAssets.contains(asset)), ); - final isScrubbing = - ref.watch(timelineStateProvider.select((state) => state.isScrubbing)); + final isScrubbing = ref.watch(timelineStateProvider.select((state) => state.isScrubbing)); final borderStyle = lockSelection ? BoxDecoration( @@ -79,9 +78,7 @@ class ThumbnailTile extends ConsumerWidget { tag: '${asset?.heroTag ?? ''}_$heroIndex', child: Thumbnail.fromBaseAsset( asset: asset, - thumbhashMode: isScrubbing - ? ThumbhashMode.only - : ThumbhashMode.enabled, + thumbhashMode: isScrubbing ? ThumbhashMode.only : ThumbhashMode.enabled, ), ), ), diff --git a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart index 48d4f25873..7704adfa2c 100644 --- a/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart +++ b/mobile/lib/presentation/widgets/memory/memory_lane.widget.dart @@ -61,11 +61,11 @@ class DriftMemoryCard extends ConsumerWidget { Colors.black.withValues(alpha: 0.2), BlendMode.darken, ), - child: SizedBox( - width: 205, - height: 200, - child: Thumbnail.fromBaseAsset(asset: memory.assets[0]), - ), + child: SizedBox( + width: 205, + height: 200, + child: Thumbnail.fromBaseAsset(asset: memory.assets[0]), + ), ), Positioned( bottom: 16, diff --git a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart index 8b4a2e3b1e..46b528fe15 100644 --- a/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart +++ b/mobile/lib/presentation/widgets/timeline/fixed/segment.model.dart @@ -131,8 +131,8 @@ class _FixedSegmentRow extends ConsumerWidget { future: timelineService.loadAssets(assetIndex, assetCount), builder: (context, snapshot) { return _buildAssetRow(context, snapshot.data, timelineService); - }, - ); + }, + ); } } @@ -217,14 +217,12 @@ class _AssetTileWidget extends ConsumerWidget { return GestureDetector( onTap: () => lockSelection || asset == null ? null : _handleOnTap(context, ref, assetIndex, asset, heroOffset), - onLongPress: () => lockSelection || asset == null - ? null - : _handleOnLongPress(ref, asset), - child: ThumbnailTile( - asset, - lockSelection: lockSelection, - showStorageIndicator: showStorageIndicator, - heroOffset: heroOffset, + onLongPress: () => lockSelection || asset == null ? null : _handleOnLongPress(ref, asset), + child: ThumbnailTile( + asset, + lockSelection: lockSelection, + showStorageIndicator: showStorageIndicator, + heroOffset: heroOffset, ), ); } diff --git a/mobile/lib/widgets/common/immich_thumbnail.dart b/mobile/lib/widgets/common/immich_thumbnail.dart index 101ac107e0..746b12614e 100644 --- a/mobile/lib/widgets/common/immich_thumbnail.dart +++ b/mobile/lib/widgets/common/immich_thumbnail.dart @@ -68,8 +68,7 @@ class ImmichThumbnail extends HookConsumerWidget { fadeInDuration: Duration.zero, fadeOutDuration: const Duration(milliseconds: 100), octoSet: OctoSet( - placeholderBuilder: - blurHashPlaceholderBuilder(asset?.thumbhash, fit: fit), + placeholderBuilder: blurHashPlaceholderBuilder(asset?.thumbhash, fit: fit), errorBuilder: customErrorBuilder, ), image: thumbnailProviderInstance,