diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 3953f290..18602482 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -139,6 +139,9 @@
+
+
+
diff --git a/android/app/src/main/kotlin/eu/twonly/MainActivity.kt b/android/app/src/main/kotlin/eu/twonly/MainActivity.kt
index 945bb0db..207cfdc2 100644
--- a/android/app/src/main/kotlin/eu/twonly/MainActivity.kt
+++ b/android/app/src/main/kotlin/eu/twonly/MainActivity.kt
@@ -20,12 +20,16 @@ import io.flutter.plugin.common.MethodChannel
import eu.twonly.notifications.NotificationTapChannel
import eu.twonly.webxdc.WebxdcChannel
import eu.twonly.widget.WidgetRuntimeChannel
+import android.Manifest
+import android.content.pm.PackageManager
class MainActivity : FlutterFragmentActivity() {
private val CHANNEL = "eu.twonly/photo_picker"
private var pendingResult: MethodChannel.Result? = null
+ private var pendingLocationPermissionResult: MethodChannel.Result? = null
private lateinit var pickMultipleMedia: ActivityResultLauncher
+ private lateinit var requestLocationPermissions: ActivityResultLauncher>
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
@@ -43,6 +47,14 @@ class MainActivity : FlutterFragmentActivity() {
}
pendingResult = null
}
+ requestLocationPermissions = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions(),
+ ) {
+ pendingLocationPermissionResult?.success(
+ checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED,
+ )
+ pendingLocationPermissionResult = null
+ }
super.onCreate(savedInstanceState)
}
@@ -112,6 +124,28 @@ class MainActivity : FlutterFragmentActivity() {
else -> result.notImplemented()
}
}
+ MethodChannel(
+ flutterEngine.dartExecutor.binaryMessenger,
+ "eu.twonly/location_metadata",
+ ).setMethodCallHandler { call, result ->
+ if (call.method != "requestPrecisePermission") {
+ result.notImplemented()
+ return@setMethodCallHandler
+ }
+ if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
+ result.success(true)
+ } else if (pendingLocationPermissionResult != null) {
+ result.error("request_in_progress", "A location permission request is already open.", null)
+ } else {
+ pendingLocationPermissionResult = result
+ requestLocationPermissions.launch(
+ arrayOf(
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ ),
+ )
+ }
+ }
}
override fun cleanUpFlutterEngine(flutterEngine: FlutterEngine) {
diff --git a/android/app/src/main/kotlin/eu/twonly/location/NativeLocation.kt b/android/app/src/main/kotlin/eu/twonly/location/NativeLocation.kt
new file mode 100644
index 00000000..cf1e5310
--- /dev/null
+++ b/android/app/src/main/kotlin/eu/twonly/location/NativeLocation.kt
@@ -0,0 +1,60 @@
+package eu.twonly.location
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.pm.PackageManager
+import android.location.Location
+import android.location.LocationListener
+import android.location.LocationManager
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import eu.twonly.MyApplication
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicReference
+
+/** One-shot precise location lookup called synchronously by Rust/JNI. */
+object NativeLocation {
+ private const val TARGET_ACCURACY_METERS = 50f
+
+ @JvmStatic
+ @SuppressLint("MissingPermission")
+ fun current(timeoutMillis: Long): String? {
+ val context = MyApplication.instance
+ if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ return null
+ }
+ val manager = context.getSystemService(Context.LOCATION_SERVICE) as? LocationManager ?: return null
+ val finished = CountDownLatch(1)
+ val best = AtomicReference(null)
+ val listener = object : LocationListener {
+ override fun onLocationChanged(location: Location) {
+ if (location.accuracy < 0) return
+ val previous = best.get()
+ if (previous == null || location.accuracy < previous.accuracy) best.set(location)
+ if (location.accuracy <= TARGET_ACCURACY_METERS) finished.countDown()
+ }
+
+ @Deprecated("Deprecated in Android")
+ override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) = Unit
+ }
+ val main = Handler(Looper.getMainLooper())
+ main.post {
+ var requested = false
+ for (provider in listOf(LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER)) {
+ if (!runCatching { manager.isProviderEnabled(provider) }.getOrDefault(false)) continue
+ runCatching {
+ manager.requestLocationUpdates(provider, 0L, 0f, listener, Looper.getMainLooper())
+ requested = true
+ }
+ }
+ if (!requested) finished.countDown()
+ }
+ finished.await(timeoutMillis.coerceAtLeast(0), TimeUnit.MILLISECONDS)
+ main.post { runCatching { manager.removeUpdates(listener) } }
+ val location = best.get() ?: return null
+ return "[${location.latitude},${location.longitude},${location.accuracy}]"
+ }
+}
diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html
index e633b03e..e2b0d3ee 100644
--- a/android/build/reports/problems/problems-report.html
+++ b/android/build/reports/problems/problems-report.html
@@ -650,7 +650,7 @@ code + .copy-button {
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index c704c950..6204f0c3 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -31,6 +31,7 @@
D3A100032F70000100D1A002 /* NativeImageCodec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A100042F70000100D1A002 /* NativeImageCodec.swift */; };
D3A100052F70000100D1A003 /* NativeVideoCodec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A100062F70000100D1A003 /* NativeVideoCodec.swift */; };
D3A100072F70000100D1A004 /* NativeGallery.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A100082F70000100D1A004 /* NativeGallery.swift */; };
+ D3A100092F70000100D1A005 /* NativeLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A1000A2F70000100D1A005 /* NativeLocation.swift */; };
D3A1000B2F70000100D1A0FE /* WebxdcHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3A1000C2F70000100D1A0FE /* WebxdcHost.swift */; };
F3C66D726A2EB28484DF0B10 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 16FBC6F5B58E1C6646F5D447 /* GoogleService-Info.plist */; };
/* End PBXBuildFile section */
@@ -135,6 +136,7 @@
D3A100042F70000100D1A002 /* NativeImageCodec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeImageCodec.swift; sourceTree = ""; };
D3A100062F70000100D1A003 /* NativeVideoCodec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeVideoCodec.swift; sourceTree = ""; };
D3A100082F70000100D1A004 /* NativeGallery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeGallery.swift; sourceTree = ""; };
+ D3A1000A2F70000100D1A005 /* NativeLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeLocation.swift; sourceTree = ""; };
D3A1000C2F70000100D1A0FE /* WebxdcHost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebxdcHost.swift; sourceTree = ""; };
DC1EE71614E1B4F84D6FDC2D /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E190E82D9973B318A389650B /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -305,6 +307,7 @@
D3A100042F70000100D1A002 /* NativeImageCodec.swift */,
D3A100062F70000100D1A003 /* NativeVideoCodec.swift */,
D3A100082F70000100D1A004 /* NativeGallery.swift */,
+ D3A1000A2F70000100D1A005 /* NativeLocation.swift */,
D3A1000C2F70000100D1A0FE /* WebxdcHost.swift */,
D24E27CC2F38ABC10055D9D1 /* RunnerRelease.entitlements */,
D25D4D802EFF437F0029F805 /* RunnerDebug.entitlements */,
@@ -739,6 +742,7 @@
D3A100032F70000100D1A002 /* NativeImageCodec.swift in Sources */,
D3A100052F70000100D1A003 /* NativeVideoCodec.swift in Sources */,
D3A100072F70000100D1A004 /* NativeGallery.swift in Sources */,
+ D3A100092F70000100D1A005 /* NativeLocation.swift in Sources */,
D3A1000B2F70000100D1A0FE /* WebxdcHost.swift in Sources */,
D4A100022F81000100A10002 /* TwonlyWidgetShared.swift in Sources */,
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 3a5a88ff..864d7d77 100644
--- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
- "revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
- "version" : "11.2.0"
+ "revision" : "3e33dd27dd4c69bd81c7c81fe61d8ccf58846902",
+ "version" : "11.3.1"
}
},
{
@@ -23,17 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk",
"state" : {
- "revision" : "8d5b4189f1f482df8d5c58c9985ea70491ef5382",
- "version" : "12.14.0"
- }
- },
- {
- "identity" : "flutterfire",
- "kind" : "remoteSourceControl",
- "location" : "https://github.com/firebase/flutterfire",
- "state" : {
- "revision" : "a10a4148e769fadb01b1ff8d6bb76e9137f35b81",
- "version" : "4.6.0-firebase-core-swift"
+ "revision" : "346daa9f46316aa372b35b317e18224acc2e9063",
+ "version" : "12.18.0"
}
},
{
@@ -41,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk",
"state" : {
- "revision" : "9bfcc6cf435b2e7c5562c1900b8680c594fa9a64",
- "version" : "3.6.0"
+ "revision" : "dc39082d8881109d35b94b1c122164c0e8d08a55",
+ "version" : "3.6.1"
}
},
{
@@ -50,8 +41,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
- "revision" : "219e564a8510e983e675c94f77f7f7c50049f22d",
- "version" : "12.14.0"
+ "revision" : "f04760d460296cc0fa430935a7be212e5bd67fc5",
+ "version" : "12.18.0"
}
},
{
@@ -59,8 +50,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
- "revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
- "version" : "10.1.0"
+ "revision" : "ba3358d3c3dbae8ef230b58a46b97ad65e84e974",
+ "version" : "10.1.1"
}
},
{
@@ -68,8 +59,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
- "revision" : "60da361632d0de02786f709bdc0c4df340f7613e",
- "version" : "8.1.0"
+ "revision" : "92c8f6dc3ac375d6febdfcb3db68bc3d10633db3",
+ "version" : "8.1.3"
}
},
{
@@ -86,8 +77,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
- "revision" : "c0ac7575d70050c2973ba2318bd5af47f8e8153a",
- "version" : "5.3.0"
+ "revision" : "724a52eea6329b7e12d3ad8300d76ca9f3895fcc",
+ "version" : "5.3.1"
}
},
{
@@ -113,8 +104,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
- "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
- "version" : "2.30910.0"
+ "revision" : "3851d94a41890dea16dc3db34caf60e585cb4163",
+ "version" : "2.30910.1"
}
},
{
@@ -122,8 +113,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
- "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
- "version" : "2.4.0"
+ "revision" : "f4a19a3c313dc2616c70bb49d29a799fb16be837",
+ "version" : "2.4.1"
}
},
{
@@ -131,8 +122,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/getsentry/sentry-cocoa",
"state" : {
- "revision" : "16cd512711375fa73f25ae5e373f596bdf4251ae",
- "version" : "8.58.0"
+ "revision" : "f196cbb98e0388381d57908f2f5a9bdc385cde68",
+ "version" : "8.58.4"
}
}
],
diff --git a/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
index e6556149..864d7d77 100644
--- a/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -27,15 +27,6 @@
"version" : "12.18.0"
}
},
- {
- "identity" : "flutterfire",
- "kind" : "remoteSourceControl",
- "location" : "https://github.com/firebase/flutterfire",
- "state" : {
- "revision" : "a10a4148e769fadb01b1ff8d6bb76e9137f35b81",
- "version" : "4.6.0-firebase-core-swift"
- }
- },
{
"identity" : "google-ads-on-device-conversion-ios-sdk",
"kind" : "remoteSourceControl",
@@ -131,8 +122,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/getsentry/sentry-cocoa",
"state" : {
- "revision" : "16cd512711375fa73f25ae5e373f596bdf4251ae",
- "version" : "8.58.0"
+ "revision" : "f196cbb98e0388381d57908f2f5a9bdc385cde68",
+ "version" : "8.58.4"
}
}
],
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index c6097586..84152847 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -64,6 +64,7 @@ import flutter_sharing_intent
RuntimeStorageChannel.register(with: engineBridge.pluginRegistry)
NativeNotificationChannel.register(with: engineBridge.pluginRegistry)
WebxdcHostChannel.register(with: engineBridge.pluginRegistry)
+ NativeLocationPermission.register(with: engineBridge.pluginRegistry)
}
override func userNotificationCenter(
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 4d8b0fe0..36c68595 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -62,7 +62,12 @@
NSPhotoLibraryAddUsageDescription
twonly will save photos or videos to your library.
NSLocationWhenInUseUsageDescription
- This app does not use or store your location information.
+ If you enable location metadata, twonly uses your precise location to add it to Memories stored on this device and photos you export to your library.
+ NSLocationTemporaryUsageDescriptionDictionary
+
+ MemoryLocation
+ Your precise location is needed to attach an accurate capture location to this Memory.
+
UIApplicationSceneManifest
UIApplicationSupportsMultipleScenes
diff --git a/ios/Runner/NativeLocation.swift b/ios/Runner/NativeLocation.swift
new file mode 100644
index 00000000..98c05806
--- /dev/null
+++ b/ios/Runner/NativeLocation.swift
@@ -0,0 +1,164 @@
+import CoreLocation
+import Flutter
+import Foundation
+
+private let memoryLocationPurpose = "MemoryLocation"
+
+/// Permission is requested only from the settings switch through this channel.
+final class NativeLocationPermission: NSObject, CLLocationManagerDelegate {
+ private static var active: NativeLocationPermission?
+ private let manager = CLLocationManager()
+ private let completion: FlutterResult
+
+ init(completion: @escaping FlutterResult) {
+ self.completion = completion
+ super.init()
+ manager.delegate = self
+ }
+
+ static func register(with registry: FlutterPluginRegistry) {
+ guard let registrar = registry.registrar(forPlugin: "TwonlyLocationMetadata") else { return }
+ let channel = FlutterMethodChannel(
+ name: "eu.twonly/location_metadata",
+ binaryMessenger: registrar.messenger()
+ )
+ channel.setMethodCallHandler { call, result in
+ guard call.method == "requestPrecisePermission" else {
+ result(FlutterMethodNotImplemented)
+ return
+ }
+ DispatchQueue.main.async {
+ guard active == nil else {
+ result(false)
+ return
+ }
+ let request = NativeLocationPermission(completion: result)
+ active = request
+ request.begin()
+ }
+ }
+ }
+
+ private func begin() {
+ switch manager.authorizationStatus {
+ case .notDetermined:
+ manager.requestWhenInUseAuthorization()
+ case .authorizedAlways, .authorizedWhenInUse:
+ ensureFullAccuracy()
+ default:
+ finish(false)
+ }
+ }
+
+ func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
+ switch manager.authorizationStatus {
+ case .authorizedAlways, .authorizedWhenInUse: ensureFullAccuracy()
+ case .denied, .restricted: finish(false)
+ default: break
+ }
+ }
+
+ private func ensureFullAccuracy() {
+ guard manager.accuracyAuthorization != .fullAccuracy else {
+ finish(true)
+ return
+ }
+ manager.requestTemporaryFullAccuracyAuthorization(withPurposeKey: memoryLocationPurpose) {
+ [weak self] _ in
+ guard let self else { return }
+ self.finish(self.manager.accuracyAuthorization == .fullAccuracy)
+ }
+ }
+
+ private func finish(_ granted: Bool) {
+ guard Self.active === self else { return }
+ Self.active = nil
+ completion(granted)
+ }
+}
+
+private final class NativeLocationRequest: NSObject, CLLocationManagerDelegate {
+ private let manager = CLLocationManager()
+ private let completion: (CLLocation?) -> Void
+ private(set) var best: CLLocation?
+ private var finished = false
+
+ init(completion: @escaping (CLLocation?) -> Void) {
+ self.completion = completion
+ super.init()
+ manager.delegate = self
+ manager.desiredAccuracy = kCLLocationAccuracyBest
+ manager.distanceFilter = kCLDistanceFilterNone
+ }
+
+ func start() {
+ guard CLLocationManager.locationServicesEnabled(),
+ [.authorizedAlways, .authorizedWhenInUse].contains(manager.authorizationStatus),
+ manager.accuracyAuthorization == .fullAccuracy
+ else {
+ stop(returning: nil)
+ return
+ }
+ manager.startUpdatingLocation()
+ }
+
+ func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
+ for location in locations where location.horizontalAccuracy >= 0 {
+ if best == nil || location.horizontalAccuracy < best!.horizontalAccuracy {
+ best = location
+ }
+ }
+ if let best, best.horizontalAccuracy <= 50 {
+ stop(returning: best)
+ }
+ }
+
+ func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
+ if (error as? CLError)?.code != .locationUnknown {
+ stop(returning: best)
+ }
+ }
+
+ func stop(returning location: CLLocation?) {
+ guard !finished else { return }
+ finished = true
+ manager.stopUpdatingLocation()
+ completion(location)
+ }
+}
+
+private var activeNativeLocationRequest: NativeLocationRequest?
+
+/// Synchronous C ABI used from Rust's blocking location worker.
+@_cdecl("twonly_current_location")
+func twonlyCurrentLocation(
+ _ timeoutMillis: Int64,
+ _ latitude: UnsafeMutablePointer?,
+ _ longitude: UnsafeMutablePointer?,
+ _ accuracy: UnsafeMutablePointer?
+) -> Bool {
+ guard let latitude, let longitude, let accuracy else { return false }
+ let finished = DispatchSemaphore(value: 0)
+ var found: CLLocation?
+ DispatchQueue.main.async {
+ let request = NativeLocationRequest { location in
+ found = location
+ activeNativeLocationRequest = nil
+ finished.signal()
+ }
+ activeNativeLocationRequest = request
+ request.start()
+ }
+ if finished.wait(timeout: .now() + .milliseconds(Int(max(0, timeoutMillis)))) == .timedOut {
+ DispatchQueue.main.sync {
+ found = activeNativeLocationRequest?.best
+ activeNativeLocationRequest?.stop(returning: found)
+ activeNativeLocationRequest = nil
+ }
+ }
+ guard let found else { return false }
+ latitude.pointee = found.coordinate.latitude
+ longitude.pointee = found.coordinate.longitude
+ accuracy.pointee = found.horizontalAccuracy
+ return true
+}
diff --git a/lib/core/bridge/api.dart b/lib/core/bridge/api.dart
index 4676f9f7..56a38342 100644
--- a/lib/core/bridge/api.dart
+++ b/lib/core/bridge/api.dart
@@ -471,6 +471,13 @@ class RustApi {
isDraftMedia: isDraftMedia,
);
+ /// Starts the optional location window for a Memory row created by the
+ /// local save button rather than the outgoing-media initializer.
+ static Future initializeMemoryLocation({required String mediaId}) =>
+ RustLib.instance.api.crateBridgeApiRustApiInitializeMemoryLocation(
+ mediaId: mediaId,
+ );
+
static Future insertAndSendAdditionalData({
required String groupId,
required String messageType,
@@ -568,6 +575,11 @@ class RustApi {
.api
.crateBridgeApiRustApiPrerenderMedia(mediaId: mediaId);
+ /// Begins the optional precise-location lookup when the capture camera is
+ /// opened, so a later photo usually has a fix immediately available.
+ static Future prewarmMemoryLocation() =>
+ RustLib.instance.api.crateBridgeApiRustApiPrewarmMemoryLocation();
+
/// Deletes temporary media whose messages are finished with it.
static Future purgeMediaTempFolder() =>
RustLib.instance.api.crateBridgeApiRustApiPurgeMediaTempFolder();
diff --git a/lib/core/frb_generated.dart b/lib/core/frb_generated.dart
index 315bd0c8..357ec571 100644
--- a/lib/core/frb_generated.dart
+++ b/lib/core/frb_generated.dart
@@ -87,7 +87,7 @@ class RustLib extends BaseEntrypoint {
String get codegenVersion => '2.12.0';
@override
- int get rustContentHash => -1591152700;
+ int get rustContentHash => 101877534;
static const kDefaultExternalLibraryLoaderConfig =
ExternalLibraryLoaderConfig(
@@ -333,6 +333,10 @@ abstract class RustLibApi extends BaseApi {
required bool isDraftMedia,
});
+ Future crateBridgeApiRustApiInitializeMemoryLocation({
+ required String mediaId,
+ });
+
Future crateBridgeApiRustApiInsertAndSendAdditionalData({
required String groupId,
required String messageType,
@@ -385,6 +389,8 @@ abstract class RustLibApi extends BaseApi {
Future crateBridgeApiRustApiPrerenderMedia({required String mediaId});
+ Future crateBridgeApiRustApiPrewarmMemoryLocation();
+
Future crateBridgeApiRustApiPurgeMediaTempFolder();
Future crateBridgeApiRustApiPurgeWidgetMedia();
@@ -3061,6 +3067,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
argNames: ["mediaType", "displayLimitInMilliseconds", "isDraftMedia"],
);
+ @override
+ Future crateBridgeApiRustApiInitializeMemoryLocation({
+ required String mediaId,
+ }) {
+ return handler.executeNormal(
+ NormalTask(
+ callFfi: (port_) {
+ final serializer = SseSerializer(generalizedFrbRustBinding);
+ sse_encode_String(mediaId, serializer);
+ pdeCallFfi(
+ generalizedFrbRustBinding,
+ serializer,
+ funcId: 70,
+ port: port_,
+ );
+ },
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_unit,
+ decodeErrorData: sse_decode_AnyhowException,
+ ),
+ constMeta: kCrateBridgeApiRustApiInitializeMemoryLocationConstMeta,
+ argValues: [mediaId],
+ apiImpl: this,
+ ),
+ );
+ }
+
+ TaskConstMeta get kCrateBridgeApiRustApiInitializeMemoryLocationConstMeta =>
+ const TaskConstMeta(
+ debugName: "rust_api_initialize_memory_location",
+ argNames: ["mediaId"],
+ );
+
@override
Future crateBridgeApiRustApiInsertAndSendAdditionalData({
required String groupId,
@@ -3079,7 +3118,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 70,
+ funcId: 71,
port: port_,
);
},
@@ -3115,7 +3154,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 71,
+ funcId: 72,
port: port_,
);
},
@@ -3150,7 +3189,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 72,
+ funcId: 73,
port: port_,
);
},
@@ -3192,7 +3231,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 73,
+ funcId: 74,
port: port_,
);
},
@@ -3234,7 +3273,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 74,
+ funcId: 75,
port: port_,
);
},
@@ -3264,7 +3303,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 75,
+ funcId: 76,
port: port_,
);
},
@@ -3297,7 +3336,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 76,
+ funcId: 77,
port: port_,
);
},
@@ -3332,7 +3371,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 77,
+ funcId: 78,
port: port_,
);
},
@@ -3362,7 +3401,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 78,
+ funcId: 79,
port: port_,
);
},
@@ -3397,7 +3436,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 79,
+ funcId: 80,
port: port_,
);
},
@@ -3427,7 +3466,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 80,
+ funcId: 81,
port: port_,
);
},
@@ -3460,7 +3499,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 81,
+ funcId: 82,
port: port_,
);
},
@@ -3481,6 +3520,36 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
argNames: ["mediaId"],
);
+ @override
+ Future crateBridgeApiRustApiPrewarmMemoryLocation() {
+ return handler.executeNormal(
+ NormalTask(
+ callFfi: (port_) {
+ final serializer = SseSerializer(generalizedFrbRustBinding);
+ pdeCallFfi(
+ generalizedFrbRustBinding,
+ serializer,
+ funcId: 83,
+ port: port_,
+ );
+ },
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_unit,
+ decodeErrorData: sse_decode_AnyhowException,
+ ),
+ constMeta: kCrateBridgeApiRustApiPrewarmMemoryLocationConstMeta,
+ argValues: [],
+ apiImpl: this,
+ ),
+ );
+ }
+
+ TaskConstMeta get kCrateBridgeApiRustApiPrewarmMemoryLocationConstMeta =>
+ const TaskConstMeta(
+ debugName: "rust_api_prewarm_memory_location",
+ argNames: [],
+ );
+
@override
Future crateBridgeApiRustApiPurgeMediaTempFolder() {
return handler.executeNormal(
@@ -3490,7 +3559,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 82,
+ funcId: 84,
port: port_,
);
},
@@ -3520,7 +3589,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 83,
+ funcId: 85,
port: port_,
);
},
@@ -3550,7 +3619,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 84,
+ funcId: 86,
port: port_,
);
},
@@ -3589,7 +3658,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 85,
+ funcId: 87,
port: port_,
);
},
@@ -3624,7 +3693,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 86,
+ funcId: 88,
port: port_,
);
},
@@ -3663,7 +3732,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 87,
+ funcId: 89,
port: port_,
);
},
@@ -3705,7 +3774,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 88,
+ funcId: 90,
port: port_,
);
},
@@ -3736,7 +3805,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 89,
+ funcId: 91,
port: port_,
);
},
@@ -3769,7 +3838,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 90,
+ funcId: 92,
port: port_,
);
},
@@ -3802,7 +3871,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 91,
+ funcId: 93,
port: port_,
);
},
@@ -3837,7 +3906,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 92,
+ funcId: 94,
port: port_,
);
},
@@ -3870,7 +3939,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 93,
+ funcId: 95,
port: port_,
);
},
@@ -3903,7 +3972,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 94,
+ funcId: 96,
port: port_,
);
},
@@ -3936,7 +4005,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 95,
+ funcId: 97,
port: port_,
);
},
@@ -3973,7 +4042,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 96,
+ funcId: 98,
port: port_,
);
},
@@ -4003,7 +4072,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 97,
+ funcId: 99,
port: port_,
);
},
@@ -4033,7 +4102,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 98,
+ funcId: 100,
port: port_,
);
},
@@ -4063,7 +4132,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 99,
+ funcId: 101,
port: port_,
);
},
@@ -4096,7 +4165,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 100,
+ funcId: 102,
port: port_,
);
},
@@ -4127,7 +4196,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 101,
+ funcId: 103,
port: port_,
);
},
@@ -4160,7 +4229,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 102,
+ funcId: 104,
port: port_,
);
},
@@ -4206,7 +4275,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 103,
+ funcId: 105,
port: port_,
);
},
@@ -4262,7 +4331,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 104,
+ funcId: 106,
port: port_,
);
},
@@ -4310,7 +4379,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 105,
+ funcId: 107,
port: port_,
);
},
@@ -4348,7 +4417,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 106,
+ funcId: 108,
port: port_,
);
},
@@ -4383,7 +4452,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 107,
+ funcId: 109,
port: port_,
);
},
@@ -4418,7 +4487,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 108,
+ funcId: 110,
port: port_,
);
},
@@ -4451,7 +4520,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 109,
+ funcId: 111,
port: port_,
);
},
@@ -4488,7 +4557,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 110,
+ funcId: 112,
port: port_,
);
},
@@ -4519,7 +4588,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 111,
+ funcId: 113,
port: port_,
);
},
@@ -4557,7 +4626,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 112,
+ funcId: 114,
port: port_,
);
},
@@ -4592,7 +4661,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 113,
+ funcId: 115,
port: port_,
);
},
@@ -4631,7 +4700,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 114,
+ funcId: 116,
port: port_,
);
},
@@ -4664,7 +4733,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 115,
+ funcId: 117,
port: port_,
);
},
@@ -4695,7 +4764,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 116,
+ funcId: 118,
port: port_,
);
},
@@ -4730,7 +4799,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 117,
+ funcId: 119,
port: port_,
);
},
@@ -4760,7 +4829,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 118,
+ funcId: 120,
port: port_,
);
},
@@ -4793,7 +4862,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 119,
+ funcId: 121,
port: port_,
);
},
@@ -4828,7 +4897,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 120,
+ funcId: 122,
port: port_,
);
},
@@ -4861,7 +4930,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 121,
+ funcId: 123,
port: port_,
);
},
@@ -4892,7 +4961,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 122,
+ funcId: 124,
port: port_,
);
},
@@ -4929,7 +4998,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 123,
+ funcId: 125,
port: port_,
);
},
@@ -4981,7 +5050,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 124,
+ funcId: 126,
port: port_,
);
},
@@ -5034,7 +5103,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 125,
+ funcId: 127,
port: port_,
);
},
@@ -5074,7 +5143,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 126,
+ funcId: 128,
port: port_,
);
},
@@ -5107,7 +5176,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 127,
+ funcId: 129,
port: port_,
);
},
@@ -5140,7 +5209,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 128,
+ funcId: 130,
port: port_,
);
},
@@ -5177,7 +5246,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 129,
+ funcId: 131,
port: port_,
);
},
@@ -5209,7 +5278,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 130,
+ funcId: 132,
port: port_,
);
},
@@ -5242,7 +5311,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 131,
+ funcId: 133,
port: port_,
);
},
@@ -5277,7 +5346,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 132,
+ funcId: 134,
port: port_,
);
},
@@ -5309,7 +5378,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 133,
+ funcId: 135,
port: port_,
);
},
@@ -5347,7 +5416,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 134,
+ funcId: 136,
port: port_,
);
},
@@ -5380,7 +5449,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 135,
+ funcId: 137,
port: port_,
);
},
@@ -5418,7 +5487,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 136,
+ funcId: 138,
port: port_,
);
},
@@ -5455,7 +5524,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 137,
+ funcId: 139,
port: port_,
);
},
@@ -5492,7 +5561,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 138,
+ funcId: 140,
port: port_,
);
},
@@ -5530,7 +5599,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 139,
+ funcId: 141,
port: port_,
);
},
@@ -5568,7 +5637,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 140,
+ funcId: 142,
port: port_,
);
},
@@ -5601,7 +5670,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 141,
+ funcId: 143,
port: port_,
);
},
@@ -5633,7 +5702,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 142,
+ funcId: 144,
port: port_,
);
},
@@ -5668,7 +5737,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 143,
+ funcId: 145,
port: port_,
);
},
@@ -5705,7 +5774,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 144,
+ funcId: 146,
port: port_,
);
},
@@ -5738,7 +5807,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 145,
+ funcId: 147,
port: port_,
);
},
@@ -5770,7 +5839,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 146,
+ funcId: 148,
port: port_,
);
},
@@ -5805,7 +5874,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 147,
+ funcId: 149,
port: port_,
);
},
@@ -5844,7 +5913,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 148,
+ funcId: 150,
port: port_,
);
},
@@ -5881,7 +5950,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 149,
+ funcId: 151,
port: port_,
);
},
@@ -5911,7 +5980,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 150,
+ funcId: 152,
port: port_,
);
},
@@ -5943,7 +6012,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 151,
+ funcId: 153,
port: port_,
);
},
@@ -5978,7 +6047,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 152,
+ funcId: 154,
port: port_,
);
},
@@ -6010,7 +6079,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 153,
+ funcId: 155,
port: port_,
);
},
@@ -6048,7 +6117,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 154,
+ funcId: 156,
port: port_,
);
},
@@ -6087,7 +6156,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 155,
+ funcId: 157,
port: port_,
);
},
@@ -6122,7 +6191,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 156,
+ funcId: 158,
port: port_,
);
},
@@ -6167,7 +6236,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 157,
+ funcId: 159,
port: port_,
);
},
@@ -6210,7 +6279,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 158,
+ funcId: 160,
port: port_,
);
},
@@ -6244,7 +6313,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 159,
+ funcId: 161,
port: port_,
);
},
@@ -6279,7 +6348,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 160,
+ funcId: 162,
port: port_,
);
},
@@ -6314,7 +6383,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 161,
+ funcId: 163,
port: port_,
);
},
@@ -6347,7 +6416,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 162,
+ funcId: 164,
)!;
},
codec: SseCodec(
@@ -6387,7 +6456,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 163,
+ funcId: 165,
port: port_,
);
},
@@ -6432,7 +6501,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 164,
+ funcId: 166,
port: port_,
);
},
@@ -6462,7 +6531,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 165,
+ funcId: 167,
port: port_,
);
},
@@ -6495,7 +6564,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 166,
+ funcId: 168,
port: port_,
);
},
@@ -6530,7 +6599,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 167,
+ funcId: 169,
port: port_,
);
},
@@ -6569,7 +6638,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 168,
+ funcId: 170,
)!;
},
codec: SseCodec(
@@ -7577,8 +7646,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
UserConfig dco_decode_user_config(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List;
- if (arr.length != 61)
- throw Exception('unexpected arr length: expect 61 but see ${arr.length}');
+ if (arr.length != 62)
+ throw Exception('unexpected arr length: expect 62 but see ${arr.length}');
return UserConfig(
userId: dco_decode_i_64(arr[0]),
username: dco_decode_String(arr[1]),
@@ -7608,44 +7677,45 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
preSelectedEmojies: dco_decode_opt_list_String(arr[25]),
autoDownloadOptions: dco_decode_opt_Map_String_list_String_None(arr[26]),
storeMediaFilesInGallery: dco_decode_bool(arr[27]),
- autoStoreAllSendUnlimitedMediaFiles: dco_decode_bool(arr[28]),
- typingIndicators: dco_decode_bool(arr[29]),
- showRestoreFlame: dco_decode_bool(arr[30]),
- myBestFriendGroupId: dco_decode_opt_String(arr[31]),
+ storeLocationInMemories: dco_decode_bool(arr[28]),
+ autoStoreAllSendUnlimitedMediaFiles: dco_decode_bool(arr[29]),
+ typingIndicators: dco_decode_bool(arr[30]),
+ showRestoreFlame: dco_decode_bool(arr[31]),
+ myBestFriendGroupId: dco_decode_opt_String(arr[32]),
signalLastSignedPreKeyUpdated: dco_decode_opt_box_autoadd_Chrono_Utc(
- arr[32],
- ),
- signalLastPqcPreKeysUploaded: dco_decode_opt_box_autoadd_Chrono_Utc(
arr[33],
),
- allowErrorTrackingViaSentry: dco_decode_bool(arr[34]),
- screenLockEnabled: dco_decode_bool(arr[35]),
- isCloudBackupEnabled: dco_decode_bool(arr[36]),
- isUserDiscoveryEnabled: dco_decode_bool(arr[37]),
- requiredSendImages: dco_decode_i_64(arr[38]),
- userDiscoveryThreshold: dco_decode_u_8(arr[39]),
- userDiscoveryRequiresManualApproval: dco_decode_bool(arr[40]),
- userDiscoverySharePromotion: dco_decode_bool(arr[41]),
- userDiscoveryInitializationError: dco_decode_bool(arr[42]),
- askForFriendPromotions: dco_decode_bool(arr[43]),
- currentPreKeyIndexStart: dco_decode_i_64(arr[44]),
- currentSignedPreKeyIndexStart: dco_decode_i_64(arr[45]),
- lastChangeLogHash: dco_decode_opt_list_prim_u_8_strict(arr[46]),
- hideChangeLog: dco_decode_bool(arr[47]),
- hideMemoriesBackupPromo: dco_decode_bool(arr[48]),
- hideWidgetShareExplainer: dco_decode_bool(arr[49]),
- updateFcmToken: dco_decode_bool(arr[50]),
- canUseLoginTokenForAuth: dco_decode_bool(arr[51]),
- twonlySafeBackup: dco_decode_opt_box_autoadd_twonly_safe_backup(arr[52]),
- isBackupEnabled: dco_decode_bool(arr[53]),
+ signalLastPqcPreKeysUploaded: dco_decode_opt_box_autoadd_Chrono_Utc(
+ arr[34],
+ ),
+ allowErrorTrackingViaSentry: dco_decode_bool(arr[35]),
+ screenLockEnabled: dco_decode_bool(arr[36]),
+ isCloudBackupEnabled: dco_decode_bool(arr[37]),
+ isUserDiscoveryEnabled: dco_decode_bool(arr[38]),
+ requiredSendImages: dco_decode_i_64(arr[39]),
+ userDiscoveryThreshold: dco_decode_u_8(arr[40]),
+ userDiscoveryRequiresManualApproval: dco_decode_bool(arr[41]),
+ userDiscoverySharePromotion: dco_decode_bool(arr[42]),
+ userDiscoveryInitializationError: dco_decode_bool(arr[43]),
+ askForFriendPromotions: dco_decode_bool(arr[44]),
+ currentPreKeyIndexStart: dco_decode_i_64(arr[45]),
+ currentSignedPreKeyIndexStart: dco_decode_i_64(arr[46]),
+ lastChangeLogHash: dco_decode_opt_list_prim_u_8_strict(arr[47]),
+ hideChangeLog: dco_decode_bool(arr[48]),
+ hideMemoriesBackupPromo: dco_decode_bool(arr[49]),
+ hideWidgetShareExplainer: dco_decode_bool(arr[50]),
+ updateFcmToken: dco_decode_bool(arr[51]),
+ canUseLoginTokenForAuth: dco_decode_bool(arr[52]),
+ twonlySafeBackup: dco_decode_opt_box_autoadd_twonly_safe_backup(arr[53]),
+ isBackupEnabled: dco_decode_bool(arr[54]),
passwordLessRecovery:
- dco_decode_opt_box_autoadd_passwordless_recovery_config(arr[54]),
- fcmToken: dco_decode_opt_String(arr[55]),
- lastFcmWakeupAt: dco_decode_opt_box_autoadd_i_64(arr[56]),
- lastServerMessageAt: dco_decode_opt_box_autoadd_i_64(arr[57]),
- currentSetupPage: dco_decode_opt_String(arr[58]),
- skipSetupPages: dco_decode_bool(arr[59]),
- hasZoomed: dco_decode_bool(arr[60]),
+ dco_decode_opt_box_autoadd_passwordless_recovery_config(arr[55]),
+ fcmToken: dco_decode_opt_String(arr[56]),
+ lastFcmWakeupAt: dco_decode_opt_box_autoadd_i_64(arr[57]),
+ lastServerMessageAt: dco_decode_opt_box_autoadd_i_64(arr[58]),
+ currentSetupPage: dco_decode_opt_String(arr[59]),
+ skipSetupPages: dco_decode_bool(arr[60]),
+ hasZoomed: dco_decode_bool(arr[61]),
);
}
@@ -8880,6 +8950,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
deserializer,
);
var var_storeMediaFilesInGallery = sse_decode_bool(deserializer);
+ var var_storeLocationInMemories = sse_decode_bool(deserializer);
var var_autoStoreAllSendUnlimitedMediaFiles = sse_decode_bool(deserializer);
var var_typingIndicators = sse_decode_bool(deserializer);
var var_showRestoreFlame = sse_decode_bool(deserializer);
@@ -8950,6 +9021,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
preSelectedEmojies: var_preSelectedEmojies,
autoDownloadOptions: var_autoDownloadOptions,
storeMediaFilesInGallery: var_storeMediaFilesInGallery,
+ storeLocationInMemories: var_storeLocationInMemories,
autoStoreAllSendUnlimitedMediaFiles:
var_autoStoreAllSendUnlimitedMediaFiles,
typingIndicators: var_typingIndicators,
@@ -10198,6 +10270,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
serializer,
);
sse_encode_bool(self.storeMediaFilesInGallery, serializer);
+ sse_encode_bool(self.storeLocationInMemories, serializer);
sse_encode_bool(self.autoStoreAllSendUnlimitedMediaFiles, serializer);
sse_encode_bool(self.typingIndicators, serializer);
sse_encode_bool(self.showRestoreFlame, serializer);
diff --git a/lib/core/user_config.dart b/lib/core/user_config.dart
index 6af0319f..8a0b14c3 100644
--- a/lib/core/user_config.dart
+++ b/lib/core/user_config.dart
@@ -131,6 +131,10 @@ class UserConfig {
List? preSelectedEmojies;
Map>? autoDownloadOptions;
bool storeMediaFilesInGallery;
+
+ /// Adds the capture location to locally stored Memories and gallery
+ /// exports. Disabled by default and never added to outgoing media.
+ bool storeLocationInMemories;
bool autoStoreAllSendUnlimitedMediaFiles;
bool typingIndicators;
bool showRestoreFlame;
@@ -202,6 +206,7 @@ class UserConfig {
this.preSelectedEmojies,
this.autoDownloadOptions,
required this.storeMediaFilesInGallery,
+ required this.storeLocationInMemories,
required this.autoStoreAllSendUnlimitedMediaFiles,
required this.typingIndicators,
required this.showRestoreFlame,
@@ -267,6 +272,7 @@ class UserConfig {
preSelectedEmojies.hashCode ^
autoDownloadOptions.hashCode ^
storeMediaFilesInGallery.hashCode ^
+ storeLocationInMemories.hashCode ^
autoStoreAllSendUnlimitedMediaFiles.hashCode ^
typingIndicators.hashCode ^
showRestoreFlame.hashCode ^
@@ -336,6 +342,7 @@ class UserConfig {
preSelectedEmojies == other.preSelectedEmojies &&
autoDownloadOptions == other.autoDownloadOptions &&
storeMediaFilesInGallery == other.storeMediaFilesInGallery &&
+ storeLocationInMemories == other.storeLocationInMemories &&
autoStoreAllSendUnlimitedMediaFiles ==
other.autoStoreAllSendUnlimitedMediaFiles &&
typingIndicators == other.typingIndicators &&
diff --git a/lib/src/database/tables/mediafiles.table.dart b/lib/src/database/tables/mediafiles.table.dart
index f4e212a7..5fe0f731 100644
--- a/lib/src/database/tables/mediafiles.table.dart
+++ b/lib/src/database/tables/mediafiles.table.dart
@@ -91,6 +91,14 @@ class MediaFiles extends Table {
IntColumn get sizeInBytes => integer().nullable()();
+ RealColumn get locationLatitude => real().nullable()();
+ RealColumn get locationLongitude => real().nullable()();
+ RealColumn get locationAccuracy => real().nullable()();
+ TextColumn get locationStatus => text().nullable()();
+ IntColumn get locationDeadlineAt => integer().nullable()();
+ BoolColumn get galleryExportPending =>
+ boolean().withDefault(const Constant(false))();
+
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
TextColumn get createdAtMonth => text().nullable()();
diff --git a/lib/src/database/twonly.db.g.dart b/lib/src/database/twonly.db.g.dart
index 48d99d8f..23366fed 100644
--- a/lib/src/database/twonly.db.g.dart
+++ b/lib/src/database/twonly.db.g.dart
@@ -3536,6 +3536,75 @@ class $MediaFilesTable extends MediaFiles
type: DriftSqlType.int,
requiredDuringInsert: false,
);
+ static const VerificationMeta _locationLatitudeMeta = const VerificationMeta(
+ 'locationLatitude',
+ );
+ @override
+ late final GeneratedColumn locationLatitude = GeneratedColumn(
+ 'location_latitude',
+ aliasedName,
+ true,
+ type: DriftSqlType.double,
+ requiredDuringInsert: false,
+ );
+ static const VerificationMeta _locationLongitudeMeta = const VerificationMeta(
+ 'locationLongitude',
+ );
+ @override
+ late final GeneratedColumn locationLongitude =
+ GeneratedColumn(
+ 'location_longitude',
+ aliasedName,
+ true,
+ type: DriftSqlType.double,
+ requiredDuringInsert: false,
+ );
+ static const VerificationMeta _locationAccuracyMeta = const VerificationMeta(
+ 'locationAccuracy',
+ );
+ @override
+ late final GeneratedColumn locationAccuracy = GeneratedColumn(
+ 'location_accuracy',
+ aliasedName,
+ true,
+ type: DriftSqlType.double,
+ requiredDuringInsert: false,
+ );
+ static const VerificationMeta _locationStatusMeta = const VerificationMeta(
+ 'locationStatus',
+ );
+ @override
+ late final GeneratedColumn locationStatus = GeneratedColumn(
+ 'location_status',
+ aliasedName,
+ true,
+ type: DriftSqlType.string,
+ requiredDuringInsert: false,
+ );
+ static const VerificationMeta _locationDeadlineAtMeta =
+ const VerificationMeta('locationDeadlineAt');
+ @override
+ late final GeneratedColumn locationDeadlineAt = GeneratedColumn(
+ 'location_deadline_at',
+ aliasedName,
+ true,
+ type: DriftSqlType.int,
+ requiredDuringInsert: false,
+ );
+ static const VerificationMeta _galleryExportPendingMeta =
+ const VerificationMeta('galleryExportPending');
+ @override
+ late final GeneratedColumn galleryExportPending = GeneratedColumn(
+ 'gallery_export_pending',
+ aliasedName,
+ false,
+ type: DriftSqlType.bool,
+ requiredDuringInsert: false,
+ defaultConstraints: GeneratedColumn.constraintIsAlways(
+ 'CHECK ("gallery_export_pending" IN (0, 1))',
+ ),
+ defaultValue: const Constant(false),
+ );
static const VerificationMeta _createdAtMeta = const VerificationMeta(
'createdAt',
);
@@ -3586,6 +3655,12 @@ class $MediaFilesTable extends MediaFiles
storedFileHash,
hasThumbnail,
sizeInBytes,
+ locationLatitude,
+ locationLongitude,
+ locationAccuracy,
+ locationStatus,
+ locationDeadlineAt,
+ galleryExportPending,
createdAt,
createdAtMonth,
];
@@ -3768,6 +3843,60 @@ class $MediaFilesTable extends MediaFiles
),
);
}
+ if (data.containsKey('location_latitude')) {
+ context.handle(
+ _locationLatitudeMeta,
+ locationLatitude.isAcceptableOrUnknown(
+ data['location_latitude']!,
+ _locationLatitudeMeta,
+ ),
+ );
+ }
+ if (data.containsKey('location_longitude')) {
+ context.handle(
+ _locationLongitudeMeta,
+ locationLongitude.isAcceptableOrUnknown(
+ data['location_longitude']!,
+ _locationLongitudeMeta,
+ ),
+ );
+ }
+ if (data.containsKey('location_accuracy')) {
+ context.handle(
+ _locationAccuracyMeta,
+ locationAccuracy.isAcceptableOrUnknown(
+ data['location_accuracy']!,
+ _locationAccuracyMeta,
+ ),
+ );
+ }
+ if (data.containsKey('location_status')) {
+ context.handle(
+ _locationStatusMeta,
+ locationStatus.isAcceptableOrUnknown(
+ data['location_status']!,
+ _locationStatusMeta,
+ ),
+ );
+ }
+ if (data.containsKey('location_deadline_at')) {
+ context.handle(
+ _locationDeadlineAtMeta,
+ locationDeadlineAt.isAcceptableOrUnknown(
+ data['location_deadline_at']!,
+ _locationDeadlineAtMeta,
+ ),
+ );
+ }
+ if (data.containsKey('gallery_export_pending')) {
+ context.handle(
+ _galleryExportPendingMeta,
+ galleryExportPending.isAcceptableOrUnknown(
+ data['gallery_export_pending']!,
+ _galleryExportPendingMeta,
+ ),
+ );
+ }
if (data.containsKey('created_at')) {
context.handle(
_createdAtMeta,
@@ -3903,6 +4032,30 @@ class $MediaFilesTable extends MediaFiles
DriftSqlType.int,
data['${effectivePrefix}size_in_bytes'],
),
+ locationLatitude: attachedDatabase.typeMapping.read(
+ DriftSqlType.double,
+ data['${effectivePrefix}location_latitude'],
+ ),
+ locationLongitude: attachedDatabase.typeMapping.read(
+ DriftSqlType.double,
+ data['${effectivePrefix}location_longitude'],
+ ),
+ locationAccuracy: attachedDatabase.typeMapping.read(
+ DriftSqlType.double,
+ data['${effectivePrefix}location_accuracy'],
+ ),
+ locationStatus: attachedDatabase.typeMapping.read(
+ DriftSqlType.string,
+ data['${effectivePrefix}location_status'],
+ ),
+ locationDeadlineAt: attachedDatabase.typeMapping.read(
+ DriftSqlType.int,
+ data['${effectivePrefix}location_deadline_at'],
+ ),
+ galleryExportPending: attachedDatabase.typeMapping.read(
+ DriftSqlType.bool,
+ data['${effectivePrefix}gallery_export_pending'],
+ )!,
createdAt: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
@@ -3972,6 +4125,12 @@ class MediaFile extends DataClass implements Insertable {
final Uint8List? storedFileHash;
final bool hasThumbnail;
final int? sizeInBytes;
+ final double? locationLatitude;
+ final double? locationLongitude;
+ final double? locationAccuracy;
+ final String? locationStatus;
+ final int? locationDeadlineAt;
+ final bool galleryExportPending;
final DateTime createdAt;
final String? createdAtMonth;
const MediaFile({
@@ -4000,6 +4159,12 @@ class MediaFile extends DataClass implements Insertable {
this.storedFileHash,
required this.hasThumbnail,
this.sizeInBytes,
+ this.locationLatitude,
+ this.locationLongitude,
+ this.locationAccuracy,
+ this.locationStatus,
+ this.locationDeadlineAt,
+ required this.galleryExportPending,
required this.createdAt,
this.createdAtMonth,
});
@@ -4079,6 +4244,22 @@ class MediaFile extends DataClass implements Insertable {
if (!nullToAbsent || sizeInBytes != null) {
map['size_in_bytes'] = Variable(sizeInBytes);
}
+ if (!nullToAbsent || locationLatitude != null) {
+ map['location_latitude'] = Variable(locationLatitude);
+ }
+ if (!nullToAbsent || locationLongitude != null) {
+ map['location_longitude'] = Variable(locationLongitude);
+ }
+ if (!nullToAbsent || locationAccuracy != null) {
+ map['location_accuracy'] = Variable(locationAccuracy);
+ }
+ if (!nullToAbsent || locationStatus != null) {
+ map['location_status'] = Variable(locationStatus);
+ }
+ if (!nullToAbsent || locationDeadlineAt != null) {
+ map['location_deadline_at'] = Variable(locationDeadlineAt);
+ }
+ map['gallery_export_pending'] = Variable(galleryExportPending);
map['created_at'] = Variable(createdAt);
if (!nullToAbsent || createdAtMonth != null) {
map['created_at_month'] = Variable(createdAtMonth);
@@ -4144,6 +4325,22 @@ class MediaFile extends DataClass implements Insertable {
sizeInBytes: sizeInBytes == null && nullToAbsent
? const Value.absent()
: Value(sizeInBytes),
+ locationLatitude: locationLatitude == null && nullToAbsent
+ ? const Value.absent()
+ : Value(locationLatitude),
+ locationLongitude: locationLongitude == null && nullToAbsent
+ ? const Value.absent()
+ : Value(locationLongitude),
+ locationAccuracy: locationAccuracy == null && nullToAbsent
+ ? const Value.absent()
+ : Value(locationAccuracy),
+ locationStatus: locationStatus == null && nullToAbsent
+ ? const Value.absent()
+ : Value(locationStatus),
+ locationDeadlineAt: locationDeadlineAt == null && nullToAbsent
+ ? const Value.absent()
+ : Value(locationDeadlineAt),
+ galleryExportPending: Value(galleryExportPending),
createdAt: Value(createdAt),
createdAtMonth: createdAtMonth == null && nullToAbsent
? const Value.absent()
@@ -4198,6 +4395,16 @@ class MediaFile extends DataClass implements Insertable {
storedFileHash: serializer.fromJson(json['storedFileHash']),
hasThumbnail: serializer.fromJson(json['hasThumbnail']),
sizeInBytes: serializer.fromJson(json['sizeInBytes']),
+ locationLatitude: serializer.fromJson(json['locationLatitude']),
+ locationLongitude: serializer.fromJson(
+ json['locationLongitude'],
+ ),
+ locationAccuracy: serializer.fromJson(json['locationAccuracy']),
+ locationStatus: serializer.fromJson(json['locationStatus']),
+ locationDeadlineAt: serializer.fromJson(json['locationDeadlineAt']),
+ galleryExportPending: serializer.fromJson(
+ json['galleryExportPending'],
+ ),
createdAt: serializer.fromJson(json['createdAt']),
createdAtMonth: serializer.fromJson(json['createdAtMonth']),
);
@@ -4241,6 +4448,12 @@ class MediaFile extends DataClass implements Insertable {
'storedFileHash': serializer.toJson(storedFileHash),
'hasThumbnail': serializer.toJson(hasThumbnail),
'sizeInBytes': serializer.toJson(sizeInBytes),
+ 'locationLatitude': serializer.toJson(locationLatitude),
+ 'locationLongitude': serializer.toJson(locationLongitude),
+ 'locationAccuracy': serializer.toJson(locationAccuracy),
+ 'locationStatus': serializer.toJson(locationStatus),
+ 'locationDeadlineAt': serializer.toJson(locationDeadlineAt),
+ 'galleryExportPending': serializer.toJson(galleryExportPending),
'createdAt': serializer.toJson(createdAt),
'createdAtMonth': serializer.toJson(createdAtMonth),
};
@@ -4272,6 +4485,12 @@ class MediaFile extends DataClass implements Insertable {
Value storedFileHash = const Value.absent(),
bool? hasThumbnail,
Value sizeInBytes = const Value.absent(),
+ Value locationLatitude = const Value.absent(),
+ Value locationLongitude = const Value.absent(),
+ Value locationAccuracy = const Value.absent(),
+ Value locationStatus = const Value.absent(),
+ Value locationDeadlineAt = const Value.absent(),
+ bool? galleryExportPending,
DateTime? createdAt,
Value createdAtMonth = const Value.absent(),
}) => MediaFile(
@@ -4319,6 +4538,22 @@ class MediaFile extends DataClass implements Insertable {
: this.storedFileHash,
hasThumbnail: hasThumbnail ?? this.hasThumbnail,
sizeInBytes: sizeInBytes.present ? sizeInBytes.value : this.sizeInBytes,
+ locationLatitude: locationLatitude.present
+ ? locationLatitude.value
+ : this.locationLatitude,
+ locationLongitude: locationLongitude.present
+ ? locationLongitude.value
+ : this.locationLongitude,
+ locationAccuracy: locationAccuracy.present
+ ? locationAccuracy.value
+ : this.locationAccuracy,
+ locationStatus: locationStatus.present
+ ? locationStatus.value
+ : this.locationStatus,
+ locationDeadlineAt: locationDeadlineAt.present
+ ? locationDeadlineAt.value
+ : this.locationDeadlineAt,
+ galleryExportPending: galleryExportPending ?? this.galleryExportPending,
createdAt: createdAt ?? this.createdAt,
createdAtMonth: createdAtMonth.present
? createdAtMonth.value
@@ -4391,6 +4626,24 @@ class MediaFile extends DataClass implements Insertable {
sizeInBytes: data.sizeInBytes.present
? data.sizeInBytes.value
: this.sizeInBytes,
+ locationLatitude: data.locationLatitude.present
+ ? data.locationLatitude.value
+ : this.locationLatitude,
+ locationLongitude: data.locationLongitude.present
+ ? data.locationLongitude.value
+ : this.locationLongitude,
+ locationAccuracy: data.locationAccuracy.present
+ ? data.locationAccuracy.value
+ : this.locationAccuracy,
+ locationStatus: data.locationStatus.present
+ ? data.locationStatus.value
+ : this.locationStatus,
+ locationDeadlineAt: data.locationDeadlineAt.present
+ ? data.locationDeadlineAt.value
+ : this.locationDeadlineAt,
+ galleryExportPending: data.galleryExportPending.present
+ ? data.galleryExportPending.value
+ : this.galleryExportPending,
createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
createdAtMonth: data.createdAtMonth.present
? data.createdAtMonth.value
@@ -4426,6 +4679,12 @@ class MediaFile extends DataClass implements Insertable {
..write('storedFileHash: $storedFileHash, ')
..write('hasThumbnail: $hasThumbnail, ')
..write('sizeInBytes: $sizeInBytes, ')
+ ..write('locationLatitude: $locationLatitude, ')
+ ..write('locationLongitude: $locationLongitude, ')
+ ..write('locationAccuracy: $locationAccuracy, ')
+ ..write('locationStatus: $locationStatus, ')
+ ..write('locationDeadlineAt: $locationDeadlineAt, ')
+ ..write('galleryExportPending: $galleryExportPending, ')
..write('createdAt: $createdAt, ')
..write('createdAtMonth: $createdAtMonth')
..write(')'))
@@ -4459,6 +4718,12 @@ class MediaFile extends DataClass implements Insertable {
$driftBlobEquality.hash(storedFileHash),
hasThumbnail,
sizeInBytes,
+ locationLatitude,
+ locationLongitude,
+ locationAccuracy,
+ locationStatus,
+ locationDeadlineAt,
+ galleryExportPending,
createdAt,
createdAtMonth,
]);
@@ -4497,6 +4762,12 @@ class MediaFile extends DataClass implements Insertable {
) &&
other.hasThumbnail == this.hasThumbnail &&
other.sizeInBytes == this.sizeInBytes &&
+ other.locationLatitude == this.locationLatitude &&
+ other.locationLongitude == this.locationLongitude &&
+ other.locationAccuracy == this.locationAccuracy &&
+ other.locationStatus == this.locationStatus &&
+ other.locationDeadlineAt == this.locationDeadlineAt &&
+ other.galleryExportPending == this.galleryExportPending &&
other.createdAt == this.createdAt &&
other.createdAtMonth == this.createdAtMonth);
}
@@ -4527,6 +4798,12 @@ class MediaFilesCompanion extends UpdateCompanion {
final Value storedFileHash;
final Value hasThumbnail;
final Value sizeInBytes;
+ final Value locationLatitude;
+ final Value locationLongitude;
+ final Value locationAccuracy;
+ final Value locationStatus;
+ final Value locationDeadlineAt;
+ final Value galleryExportPending;
final Value createdAt;
final Value createdAtMonth;
final Value rowid;
@@ -4556,6 +4833,12 @@ class MediaFilesCompanion extends UpdateCompanion {
this.storedFileHash = const Value.absent(),
this.hasThumbnail = const Value.absent(),
this.sizeInBytes = const Value.absent(),
+ this.locationLatitude = const Value.absent(),
+ this.locationLongitude = const Value.absent(),
+ this.locationAccuracy = const Value.absent(),
+ this.locationStatus = const Value.absent(),
+ this.locationDeadlineAt = const Value.absent(),
+ this.galleryExportPending = const Value.absent(),
this.createdAt = const Value.absent(),
this.createdAtMonth = const Value.absent(),
this.rowid = const Value.absent(),
@@ -4586,6 +4869,12 @@ class MediaFilesCompanion extends UpdateCompanion {
this.storedFileHash = const Value.absent(),
this.hasThumbnail = const Value.absent(),
this.sizeInBytes = const Value.absent(),
+ this.locationLatitude = const Value.absent(),
+ this.locationLongitude = const Value.absent(),
+ this.locationAccuracy = const Value.absent(),
+ this.locationStatus = const Value.absent(),
+ this.locationDeadlineAt = const Value.absent(),
+ this.galleryExportPending = const Value.absent(),
this.createdAt = const Value.absent(),
this.createdAtMonth = const Value.absent(),
this.rowid = const Value.absent(),
@@ -4617,6 +4906,12 @@ class MediaFilesCompanion extends UpdateCompanion {
Expression? storedFileHash,
Expression? hasThumbnail,
Expression? sizeInBytes,
+ Expression? locationLatitude,
+ Expression? locationLongitude,
+ Expression? locationAccuracy,
+ Expression? locationStatus,
+ Expression? locationDeadlineAt,
+ Expression? galleryExportPending,
Expression? createdAt,
Expression? createdAtMonth,
Expression? rowid,
@@ -4651,6 +4946,14 @@ class MediaFilesCompanion extends UpdateCompanion {
if (storedFileHash != null) 'stored_file_hash': storedFileHash,
if (hasThumbnail != null) 'has_thumbnail': hasThumbnail,
if (sizeInBytes != null) 'size_in_bytes': sizeInBytes,
+ if (locationLatitude != null) 'location_latitude': locationLatitude,
+ if (locationLongitude != null) 'location_longitude': locationLongitude,
+ if (locationAccuracy != null) 'location_accuracy': locationAccuracy,
+ if (locationStatus != null) 'location_status': locationStatus,
+ if (locationDeadlineAt != null)
+ 'location_deadline_at': locationDeadlineAt,
+ if (galleryExportPending != null)
+ 'gallery_export_pending': galleryExportPending,
if (createdAt != null) 'created_at': createdAt,
if (createdAtMonth != null) 'created_at_month': createdAtMonth,
if (rowid != null) 'rowid': rowid,
@@ -4683,6 +4986,12 @@ class MediaFilesCompanion extends UpdateCompanion {
Value? storedFileHash,
Value? hasThumbnail,
Value? sizeInBytes,
+ Value? locationLatitude,
+ Value? locationLongitude,
+ Value? locationAccuracy,
+ Value? locationStatus,
+ Value? locationDeadlineAt,
+ Value? galleryExportPending,
Value? createdAt,
Value? createdAtMonth,
Value? rowid,
@@ -4716,6 +5025,12 @@ class MediaFilesCompanion extends UpdateCompanion {
storedFileHash: storedFileHash ?? this.storedFileHash,
hasThumbnail: hasThumbnail ?? this.hasThumbnail,
sizeInBytes: sizeInBytes ?? this.sizeInBytes,
+ locationLatitude: locationLatitude ?? this.locationLatitude,
+ locationLongitude: locationLongitude ?? this.locationLongitude,
+ locationAccuracy: locationAccuracy ?? this.locationAccuracy,
+ locationStatus: locationStatus ?? this.locationStatus,
+ locationDeadlineAt: locationDeadlineAt ?? this.locationDeadlineAt,
+ galleryExportPending: galleryExportPending ?? this.galleryExportPending,
createdAt: createdAt ?? this.createdAt,
createdAtMonth: createdAtMonth ?? this.createdAtMonth,
rowid: rowid ?? this.rowid,
@@ -4818,6 +5133,26 @@ class MediaFilesCompanion extends UpdateCompanion {
if (sizeInBytes.present) {
map['size_in_bytes'] = Variable(sizeInBytes.value);
}
+ if (locationLatitude.present) {
+ map['location_latitude'] = Variable(locationLatitude.value);
+ }
+ if (locationLongitude.present) {
+ map['location_longitude'] = Variable(locationLongitude.value);
+ }
+ if (locationAccuracy.present) {
+ map['location_accuracy'] = Variable(locationAccuracy.value);
+ }
+ if (locationStatus.present) {
+ map['location_status'] = Variable(locationStatus.value);
+ }
+ if (locationDeadlineAt.present) {
+ map['location_deadline_at'] = Variable(locationDeadlineAt.value);
+ }
+ if (galleryExportPending.present) {
+ map['gallery_export_pending'] = Variable(
+ galleryExportPending.value,
+ );
+ }
if (createdAt.present) {
map['created_at'] = Variable(createdAt.value);
}
@@ -4858,6 +5193,12 @@ class MediaFilesCompanion extends UpdateCompanion {
..write('storedFileHash: $storedFileHash, ')
..write('hasThumbnail: $hasThumbnail, ')
..write('sizeInBytes: $sizeInBytes, ')
+ ..write('locationLatitude: $locationLatitude, ')
+ ..write('locationLongitude: $locationLongitude, ')
+ ..write('locationAccuracy: $locationAccuracy, ')
+ ..write('locationStatus: $locationStatus, ')
+ ..write('locationDeadlineAt: $locationDeadlineAt, ')
+ ..write('galleryExportPending: $galleryExportPending, ')
..write('createdAt: $createdAt, ')
..write('createdAtMonth: $createdAtMonth, ')
..write('rowid: $rowid')
@@ -17839,6 +18180,12 @@ typedef $$MediaFilesTableCreateCompanionBuilder =
Value storedFileHash,
Value hasThumbnail,
Value sizeInBytes,
+ Value locationLatitude,
+ Value locationLongitude,
+ Value locationAccuracy,
+ Value locationStatus,
+ Value locationDeadlineAt,
+ Value galleryExportPending,
Value createdAt,
Value createdAtMonth,
Value rowid,
@@ -17870,6 +18217,12 @@ typedef $$MediaFilesTableUpdateCompanionBuilder =
Value storedFileHash,
Value hasThumbnail,
Value sizeInBytes,
+ Value locationLatitude,
+ Value locationLongitude,
+ Value locationAccuracy,
+ Value locationStatus,
+ Value locationDeadlineAt,
+ Value galleryExportPending,
Value createdAt,
Value createdAtMonth,
Value rowid,
@@ -18037,6 +18390,36 @@ class $$MediaFilesTableFilterComposer
builder: (column) => ColumnFilters(column),
);
+ ColumnFilters get locationLatitude => $composableBuilder(
+ column: $table.locationLatitude,
+ builder: (column) => ColumnFilters(column),
+ );
+
+ ColumnFilters get locationLongitude => $composableBuilder(
+ column: $table.locationLongitude,
+ builder: (column) => ColumnFilters(column),
+ );
+
+ ColumnFilters get locationAccuracy => $composableBuilder(
+ column: $table.locationAccuracy,
+ builder: (column) => ColumnFilters(column),
+ );
+
+ ColumnFilters get locationStatus => $composableBuilder(
+ column: $table.locationStatus,
+ builder: (column) => ColumnFilters(column),
+ );
+
+ ColumnFilters get locationDeadlineAt => $composableBuilder(
+ column: $table.locationDeadlineAt,
+ builder: (column) => ColumnFilters(column),
+ );
+
+ ColumnFilters get galleryExportPending => $composableBuilder(
+ column: $table.galleryExportPending,
+ builder: (column) => ColumnFilters(column),
+ );
+
ColumnFilters get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnFilters(column),
@@ -18207,6 +18590,36 @@ class $$MediaFilesTableOrderingComposer
builder: (column) => ColumnOrderings(column),
);
+ ColumnOrderings get locationLatitude => $composableBuilder(
+ column: $table.locationLatitude,
+ builder: (column) => ColumnOrderings(column),
+ );
+
+ ColumnOrderings get locationLongitude => $composableBuilder(
+ column: $table.locationLongitude,
+ builder: (column) => ColumnOrderings(column),
+ );
+
+ ColumnOrderings get locationAccuracy => $composableBuilder(
+ column: $table.locationAccuracy,
+ builder: (column) => ColumnOrderings(column),
+ );
+
+ ColumnOrderings get locationStatus => $composableBuilder(
+ column: $table.locationStatus,
+ builder: (column) => ColumnOrderings(column),
+ );
+
+ ColumnOrderings get locationDeadlineAt => $composableBuilder(
+ column: $table.locationDeadlineAt,
+ builder: (column) => ColumnOrderings(column),
+ );
+
+ ColumnOrderings get galleryExportPending => $composableBuilder(
+ column: $table.galleryExportPending,
+ builder: (column) => ColumnOrderings(column),
+ );
+
ColumnOrderings get createdAt => $composableBuilder(
column: $table.createdAt,
builder: (column) => ColumnOrderings(column),
@@ -18346,6 +18759,36 @@ class $$MediaFilesTableAnnotationComposer
builder: (column) => column,
);
+ GeneratedColumn get locationLatitude => $composableBuilder(
+ column: $table.locationLatitude,
+ builder: (column) => column,
+ );
+
+ GeneratedColumn get locationLongitude => $composableBuilder(
+ column: $table.locationLongitude,
+ builder: (column) => column,
+ );
+
+ GeneratedColumn get locationAccuracy => $composableBuilder(
+ column: $table.locationAccuracy,
+ builder: (column) => column,
+ );
+
+ GeneratedColumn get locationStatus => $composableBuilder(
+ column: $table.locationStatus,
+ builder: (column) => column,
+ );
+
+ GeneratedColumn get locationDeadlineAt => $composableBuilder(
+ column: $table.locationDeadlineAt,
+ builder: (column) => column,
+ );
+
+ GeneratedColumn get galleryExportPending => $composableBuilder(
+ column: $table.galleryExportPending,
+ builder: (column) => column,
+ );
+
GeneratedColumn get createdAt =>
$composableBuilder(column: $table.createdAt, builder: (column) => column);
@@ -18433,6 +18876,12 @@ class $$MediaFilesTableTableManager
Value storedFileHash = const Value.absent(),
Value hasThumbnail = const Value.absent(),
Value sizeInBytes = const Value.absent(),
+ Value locationLatitude = const Value.absent(),
+ Value locationLongitude = const Value.absent(),
+ Value locationAccuracy = const Value.absent(),
+ Value locationStatus = const Value.absent(),
+ Value locationDeadlineAt = const Value.absent(),
+ Value galleryExportPending = const Value.absent(),
Value createdAt = const Value.absent(),
Value createdAtMonth = const Value.absent(),
Value rowid = const Value.absent(),
@@ -18462,6 +18911,12 @@ class $$MediaFilesTableTableManager
storedFileHash: storedFileHash,
hasThumbnail: hasThumbnail,
sizeInBytes: sizeInBytes,
+ locationLatitude: locationLatitude,
+ locationLongitude: locationLongitude,
+ locationAccuracy: locationAccuracy,
+ locationStatus: locationStatus,
+ locationDeadlineAt: locationDeadlineAt,
+ galleryExportPending: galleryExportPending,
createdAt: createdAt,
createdAtMonth: createdAtMonth,
rowid: rowid,
@@ -18493,6 +18948,12 @@ class $$MediaFilesTableTableManager
Value storedFileHash = const Value.absent(),
Value hasThumbnail = const Value.absent(),
Value sizeInBytes = const Value.absent(),
+ Value locationLatitude = const Value.absent(),
+ Value locationLongitude = const Value.absent(),
+ Value locationAccuracy = const Value.absent(),
+ Value locationStatus = const Value.absent(),
+ Value locationDeadlineAt = const Value.absent(),
+ Value galleryExportPending = const Value.absent(),
Value createdAt = const Value.absent(),
Value createdAtMonth = const Value.absent(),
Value rowid = const Value.absent(),
@@ -18522,6 +18983,12 @@ class $$MediaFilesTableTableManager
storedFileHash: storedFileHash,
hasThumbnail: hasThumbnail,
sizeInBytes: sizeInBytes,
+ locationLatitude: locationLatitude,
+ locationLongitude: locationLongitude,
+ locationAccuracy: locationAccuracy,
+ locationStatus: locationStatus,
+ locationDeadlineAt: locationDeadlineAt,
+ galleryExportPending: galleryExportPending,
createdAt: createdAt,
createdAtMonth: createdAtMonth,
rowid: rowid,
diff --git a/lib/src/localization/generated/app_localizations.dart b/lib/src/localization/generated/app_localizations.dart
index b4ab479f..453a9384 100644
--- a/lib/src/localization/generated/app_localizations.dart
+++ b/lib/src/localization/generated/app_localizations.dart
@@ -521,9 +521,27 @@ abstract class AppLocalizations {
/// No description provided for @settingsStorageData.
///
/// In en, this message translates to:
- /// **'Data and storage'**
+ /// **'Memories & Storage'**
String get settingsStorageData;
+ /// No description provided for @memoryLocationTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Store location in Memories'**
+ String get memoryLocationTitle;
+
+ /// No description provided for @memoryLocationSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Location for Memories and gallery exports. Never sent to contacts.'**
+ String get memoryLocationSubtitle;
+
+ /// No description provided for @memoryLocationPermissionDenied.
+ ///
+ /// In en, this message translates to:
+ /// **'Precise location access was not granted. The setting remains disabled.'**
+ String get memoryLocationPermissionDenied;
+
/// No description provided for @settingsStorageDataStoreInGTitle.
///
/// In en, this message translates to:
diff --git a/lib/src/localization/generated/app_localizations_de.dart b/lib/src/localization/generated/app_localizations_de.dart
index cca01180..e7e94630 100644
--- a/lib/src/localization/generated/app_localizations_de.dart
+++ b/lib/src/localization/generated/app_localizations_de.dart
@@ -231,7 +231,18 @@ class AppLocalizationsDe extends AppLocalizations {
String get settingsProfile => 'Profil';
@override
- String get settingsStorageData => 'Daten und Speicher';
+ String get settingsStorageData => 'Memories & Speicher';
+
+ @override
+ String get memoryLocationTitle => 'Standort in Memories speichern';
+
+ @override
+ String get memoryLocationSubtitle =>
+ 'Standort für Memories und Galerie-Exporte. Wird nicht an Kontakte gesendet.';
+
+ @override
+ String get memoryLocationPermissionDenied =>
+ 'Der genaue Standortzugriff wurde nicht erlaubt. Die Einstellung bleibt deaktiviert.';
@override
String get settingsStorageDataStoreInGTitle => 'In der Galerie speichern';
diff --git a/lib/src/localization/generated/app_localizations_en.dart b/lib/src/localization/generated/app_localizations_en.dart
index fe195e8e..d1fa98b5 100644
--- a/lib/src/localization/generated/app_localizations_en.dart
+++ b/lib/src/localization/generated/app_localizations_en.dart
@@ -229,7 +229,18 @@ class AppLocalizationsEn extends AppLocalizations {
String get settingsProfile => 'Profile';
@override
- String get settingsStorageData => 'Data and storage';
+ String get settingsStorageData => 'Memories & Storage';
+
+ @override
+ String get memoryLocationTitle => 'Store location in Memories';
+
+ @override
+ String get memoryLocationSubtitle =>
+ 'Location for Memories and gallery exports. Never sent to contacts.';
+
+ @override
+ String get memoryLocationPermissionDenied =>
+ 'Precise location access was not granted. The setting remains disabled.';
@override
String get settingsStorageDataStoreInGTitle => 'Store in Gallery';
diff --git a/lib/src/localization/translations/de.arb b/lib/src/localization/translations/de.arb
index 3b3f5b85..f0751d51 100644
--- a/lib/src/localization/translations/de.arb
+++ b/lib/src/localization/translations/de.arb
@@ -74,7 +74,10 @@
"imageEditorDrawOk": "Zeichnung machen",
"settingsTitle": "Einstellungen",
"settingsChats": "Chats",
- "settingsStorageData": "Daten und Speicher",
+ "settingsStorageData": "Memories & Speicher",
+ "memoryLocationTitle": "Standort in Memories speichern",
+ "memoryLocationSubtitle": "Standort für Memories und Galerie-Exporte. Wird nicht an Kontakte gesendet.",
+ "memoryLocationPermissionDenied": "Der genaue Standortzugriff wurde nicht erlaubt. Die Einstellung bleibt deaktiviert.",
"settingsStorageDataStoreInGTitle": "In der Galerie speichern",
"settingsStorageDataMediaAutoDownload": "Automatischer Mediendownload",
"settingsStorageDataAutoDownMobile": "Bei Nutzung mobiler Daten",
diff --git a/lib/src/localization/translations/en.arb b/lib/src/localization/translations/en.arb
index 517e8ec3..3b07dcbd 100644
--- a/lib/src/localization/translations/en.arb
+++ b/lib/src/localization/translations/en.arb
@@ -70,7 +70,10 @@
"settingsAutomaticallyMarkEqualMediaFilesAsOpenedSubtitle": "If you receive the same media in multiple chats, opening one marks all others as opened.",
"settingsPreSelectedReactionsError": "A maximum of 12 reactions can be selected.",
"settingsProfile": "Profile",
- "settingsStorageData": "Data and storage",
+ "settingsStorageData": "Memories & Storage",
+ "memoryLocationTitle": "Store location in Memories",
+ "memoryLocationSubtitle": "Location for Memories and gallery exports. Never sent to contacts.",
+ "memoryLocationPermissionDenied": "Precise location access was not granted. The setting remains disabled.",
"settingsStorageDataStoreInGTitle": "Store in Gallery",
"settingsStorageDataMediaAutoDownload": "Media auto-download",
"settingsStorageDataAutoDownMobile": "When using mobile data",
diff --git a/lib/src/providers/purchases.provider.dart b/lib/src/providers/purchases.provider.dart
index 706aae68..c3a36e31 100644
--- a/lib/src/providers/purchases.provider.dart
+++ b/lib/src/providers/purchases.provider.dart
@@ -108,11 +108,14 @@ class PurchasesProvider with ChangeNotifier, DiagnosticableTreeMixin {
isPayingUser(
planFromString(userService.currentUser.subscriptionPlan),
)) {
- globalForceIpaCheck = Timer(const Duration(seconds: 5), () async {
+ globalForceIpaCheck = Timer(const Duration(seconds: 5), () {
Log.info(
'Force Ipa check was not stopped. Requesting forced check...',
);
- await RustApi.forceIpaCheck();
+ unawaitedRustCall(
+ RustApi.forceIpaCheck(),
+ 'Forced IAP check',
+ );
});
}
diff --git a/lib/src/services/location_metadata.service.dart b/lib/src/services/location_metadata.service.dart
new file mode 100644
index 00000000..97eb7b87
--- /dev/null
+++ b/lib/src/services/location_metadata.service.dart
@@ -0,0 +1,13 @@
+import 'package:flutter/services.dart';
+
+class LocationMetadataService {
+ static const _channel = MethodChannel('eu.twonly/location_metadata');
+
+ /// Opens the native permission sheet only after the user enables the switch.
+ /// Approximate/reduced access is rejected because this feature promises a
+ /// precise capture location.
+ static Future requestPrecisePermission() async {
+ return await _channel.invokeMethod('requestPrecisePermission') ??
+ false;
+ }
+}
diff --git a/lib/src/services/memories/memories.service.dart b/lib/src/services/memories/memories.service.dart
index 22131068..4398fd5e 100644
--- a/lib/src/services/memories/memories.service.dart
+++ b/lib/src/services/memories/memories.service.dart
@@ -384,6 +384,7 @@ class MemoriesService {
final mediaFile = item.mediaService.mediaFile;
if (!mediaFile.hasThumbnail &&
mediaFile.type != MediaType.audio &&
+ item.mediaService.storedPath.existsSync() &&
_thumbnailRequests.add(mediaFile.mediaId)) {
unawaited(item.mediaService.createThumbnail());
}
diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart
index ef1e41c8..65d12e7c 100644
--- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart
+++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart
@@ -169,6 +169,7 @@ class _CameraPreviewViewState extends State {
initVolumeControl();
initAsync();
_checkAndInitCamera();
+ _prewarmMemoryLocation();
}
@override
@@ -178,12 +179,24 @@ class _CameraPreviewViewState extends State {
if (widget.isVisible) {
initVolumeControl();
_checkAndInitCamera();
+ _prewarmMemoryLocation();
} else {
_deInitVolumeControl();
}
}
}
+ void _prewarmMemoryLocation() {
+ // QR scanning deliberately does not touch location. A normal camera view
+ // has controllers and can create a Memory, so start looking as soon as it
+ // becomes visible instead of waiting for the shutter.
+ if (!widget.isVisible || widget.hideControllers) return;
+ unawaitedRustCall(
+ RustApi.prewarmMemoryLocation(),
+ 'prewarmMemoryLocation',
+ );
+ }
+
void _checkAndInitCamera() {
if (widget.isVisible &&
mc.cameraController == null &&
diff --git a/lib/src/visual/views/camera/camera_preview_components/save_to_gallery.dart b/lib/src/visual/views/camera/camera_preview_components/save_to_gallery.dart
index e4b4916d..4a251ea2 100644
--- a/lib/src/visual/views/camera/camera_preview_components/save_to_gallery.dart
+++ b/lib/src/visual/views/camera/camera_preview_components/save_to_gallery.dart
@@ -52,13 +52,19 @@ class SaveToGalleryButtonState extends State {
MediaFilesCompanion(
type: Value(widget.mediaService.mediaFile.type),
createdAt: Value(clock.now()),
- stored: const Value(true),
+ // Rust publishes the Memory as stored only after the
+ // plaintext and its thumbnail have been written.
+ stored: const Value(false),
),
);
if (newMediaFile != null) {
final newService = MediaFileService(newMediaFile);
+ await RustApi.initializeMemoryLocation(
+ mediaId: newMediaFile.mediaId,
+ );
+
if (widget.mediaService.tempPath.existsSync()) {
widget.mediaService.tempPath.copySync(
newService.tempPath.path,
diff --git a/lib/src/visual/views/memories/components/memory_thumbnail.comp.dart b/lib/src/visual/views/memories/components/memory_thumbnail.comp.dart
index 0f3fde6e..c703ba40 100644
--- a/lib/src/visual/views/memories/components/memory_thumbnail.comp.dart
+++ b/lib/src/visual/views/memories/components/memory_thumbnail.comp.dart
@@ -142,8 +142,13 @@ class _MemoriesThumbnailCompState extends State {
@override
void didUpdateWidget(covariant MemoriesThumbnailComp oldWidget) {
super.didUpdateWidget(oldWidget);
- if (oldWidget.galleryItem.mediaService.mediaFile.mediaId !=
- widget.galleryItem.mediaService.mediaFile.mediaId) {
+ final oldMedia = oldWidget.galleryItem.mediaService.mediaFile;
+ final media = widget.galleryItem.mediaService.mediaFile;
+ final changedMedia = oldMedia.mediaId != media.mediaId;
+ final sourceBecameAvailable =
+ !oldMedia.stored && media.stored ||
+ !oldMedia.hasThumbnail && media.hasThumbnail;
+ if (changedMedia || sourceBecameAvailable) {
_imageStream?.removeListener(_listener);
_imageStream = null;
_imageProvider = null;
diff --git a/lib/src/visual/views/settings/data_and_storage.view.dart b/lib/src/visual/views/settings/data_and_storage.view.dart
index 5259e59c..d1e4aa51 100644
--- a/lib/src/visual/views/settings/data_and_storage.view.dart
+++ b/lib/src/visual/views/settings/data_and_storage.view.dart
@@ -5,8 +5,10 @@ import 'package:go_router/go_router.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
+import 'package:twonly/src/services/location_metadata.service.dart';
import 'package:twonly/src/services/mediafiles/media_download_policy.dart';
import 'package:twonly/src/services/user.service.dart';
+import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
class DataAndStorageView extends StatefulWidget {
@@ -18,6 +20,7 @@ class DataAndStorageView extends StatefulWidget {
class _DataAndStorageViewState extends State {
late Future