From 0b619b4a88ea61ca66689d38909086171229163f Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 18 Jul 2024 14:02:14 -0700 Subject: [PATCH 01/22] plat --- .../shared_preferences_android/CHANGELOG.md | 5 + .../android/build.gradle | 20 + ...=> DeprecatedSharedPreferencesPlugin.java} | 12 +- .../plugins/sharedpreferences/Messages.java | 20 +- .../sharedpreferences/MessagesAsync.g.kt | 453 ++++++++++ .../SharedPreferencesPlugin.kt | 274 ++++++ ...a => DeprecatedSharedPreferencesTest.java} | 39 +- .../SharedPreferencesTest.kt | 174 ++++ .../example/android/app/build.gradle | 3 + .../flutter/plugins/DartIntegrationTest.java | 2 +- .../example/android/build.gradle | 4 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- .../shared_preferences_test.dart | 204 ++++- .../example/lib/main.dart | 33 +- .../example/pubspec.yaml | 2 +- .../legacy_shared_preferences_android.dart | 102 +++ .../lib/shared_preferences_android.dart | 209 +++-- .../lib/src/messages.g.dart | 307 ++++--- .../lib/src/messages_async.g.dart | 424 +++++++++ .../pigeons/messages_async.dart | 118 +++ .../shared_preferences_android/pubspec.yaml | 6 +- ...gacy_shared_preferences_android_test.dart} | 14 +- .../test/shared_preferences_test.dart | 300 +++++++ .../CHANGELOG.md | 5 + .../darwin/Tests/RunnerTests.swift | 133 ++- .../SharedPreferencesPlugin.swift | 135 ++- .../messages.g.swift | 250 +++++- .../shared_preferences_test.dart | 197 +++++ .../example/lib/main.dart | 35 +- .../example/pubspec.yaml | 2 +- .../legacy_shared_preferences_foundation.dart | 108 +++ .../lib/messages.g.dart | 446 +++++++--- .../lib/shared_preferences_foundation.dart | 270 ++++-- .../pigeons/messages.dart | 46 +- .../pubspec.yaml | 6 +- ...cy_shared_preferences_foundation_test.dart | 347 ++++++++ .../shared_preferences_foundation_test.dart | 483 +++++------ .../test/test_api.g.dart | 422 +++++++-- .../shared_preferences_linux/CHANGELOG.md | 3 +- .../shared_preferences_test.dart | 196 +++++ .../example/lib/main.dart | 27 +- .../example/pubspec.yaml | 2 +- .../lib/shared_preferences_linux.dart | 358 ++++++-- .../shared_preferences_linux/pubspec.yaml | 4 +- .../test/fake_path_provider_linux.dart | 29 + ...legacy_shared_preferences_linux_test.dart} | 28 +- .../shared_preferences_linux_async_test.dart | 203 +++++ .../shared_preferences_web/CHANGELOG.md | 4 + .../shared_preferences_web_test.dart | 816 +++++++++++------- .../example/pubspec.yaml | 2 +- .../lib/shared_preferences_web.dart | 193 ++++- .../shared_preferences_web/pubspec.yaml | 4 +- .../shared_preferences_windows/CHANGELOG.md | 3 +- .../shared_preferences_test.dart | 184 ++++ .../example/lib/main.dart | 27 +- .../example/pubspec.yaml | 2 +- .../lib/shared_preferences_windows.dart | 373 ++++++-- .../shared_preferences_windows/pubspec.yaml | 4 +- .../test/fake_path_provider_windows.dart | 36 + ...gacy_shared_preferences_windows_test.dart} | 34 +- ...shared_preferences_windows_async_test.dart | 197 +++++ 61 files changed, 6986 insertions(+), 1357 deletions(-) rename packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/{SharedPreferencesPlugin.java => DeprecatedSharedPreferencesPlugin.java} (95%) create mode 100644 packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/MessagesAsync.g.kt create mode 100644 packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.kt rename packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/{SharedPreferencesTest.java => DeprecatedSharedPreferencesTest.java} (90%) create mode 100644 packages/shared_preferences/shared_preferences_android/android/src/test/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.kt create mode 100644 packages/shared_preferences/shared_preferences_android/lib/legacy_shared_preferences_android.dart create mode 100644 packages/shared_preferences/shared_preferences_android/lib/src/messages_async.g.dart create mode 100644 packages/shared_preferences/shared_preferences_android/pigeons/messages_async.dart rename packages/shared_preferences/shared_preferences_android/test/{shared_preferences_android_test.dart => legacy_shared_preferences_android_test.dart} (96%) create mode 100755 packages/shared_preferences/shared_preferences_android/test/shared_preferences_test.dart create mode 100644 packages/shared_preferences/shared_preferences_foundation/lib/legacy_shared_preferences_foundation.dart create mode 100644 packages/shared_preferences/shared_preferences_foundation/test/legacy_shared_preferences_foundation_test.dart create mode 100644 packages/shared_preferences/shared_preferences_linux/test/fake_path_provider_linux.dart rename packages/shared_preferences/shared_preferences_linux/test/{shared_preferences_linux_test.dart => legacy_shared_preferences_linux_test.dart} (90%) create mode 100755 packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_async_test.dart create mode 100644 packages/shared_preferences/shared_preferences_windows/test/fake_path_provider_windows.dart rename packages/shared_preferences/shared_preferences_windows/test/{shared_preferences_windows_test.dart => legacy_shared_preferences_windows_test.dart} (89%) create mode 100755 packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_async_test.dart diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index aa4279ede83..6448b606605 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.0.0 + +* Renames `SharedPreferencesAndroid` to `LegacySharedPreferencesAndroid`. +* Creates new `SharedPreferencesAndroid` that extends `SharedPreferencesAsyncPlatform`. + ## 2.2.3 * Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 291be962565..4985674f2b4 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -2,6 +2,7 @@ group 'io.flutter.plugins.sharedpreferences' version '1.0-SNAPSHOT' buildscript { + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() @@ -9,6 +10,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.2.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -28,6 +30,7 @@ allprojects { } apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { // Conditional for compatibility with AGP <4.2. @@ -41,6 +44,14 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + defaultConfig { minSdkVersion 19 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -52,7 +63,11 @@ android { } dependencies { testImplementation 'junit:junit:4.13.2' + testImplementation 'androidx.test:core-ktx:1.5.0' + testImplementation 'androidx.test.ext:junit-ktx:1.1.5' + testImplementation 'org.robolectric:robolectric:4.12.2' testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation 'io.mockk:mockk:1.13.9' } @@ -68,3 +83,8 @@ android { } } } + +dependencies { + implementation 'androidx.datastore:datastore:1.0.0' + implementation 'androidx.datastore:datastore-preferences:1.0.0' +} \ No newline at end of file diff --git a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesPlugin.java similarity index 95% rename from packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java rename to packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesPlugin.java index 6bfa4e285a6..1c972dbdc15 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java +++ b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesPlugin.java @@ -27,8 +27,8 @@ import java.util.Map; import java.util.Set; -/** SharedPreferencesPlugin */ -public class SharedPreferencesPlugin implements FlutterPlugin, SharedPreferencesApi { +/** DeprecatedSharedPreferencesPlugin */ +public class DeprecatedSharedPreferencesPlugin implements FlutterPlugin, SharedPreferencesApi { private static final String TAG = "SharedPreferencesPlugin"; private static final String SHARED_PREFERENCES_NAME = "FlutterSharedPreferences"; private static final String LIST_IDENTIFIER = "VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu"; @@ -38,19 +38,19 @@ public class SharedPreferencesPlugin implements FlutterPlugin, SharedPreferences private SharedPreferences preferences; private SharedPreferencesListEncoder listEncoder; - public SharedPreferencesPlugin() { + public DeprecatedSharedPreferencesPlugin() { this(new ListEncoder()); } @VisibleForTesting - SharedPreferencesPlugin(@NonNull SharedPreferencesListEncoder listEncoder) { + DeprecatedSharedPreferencesPlugin(@NonNull SharedPreferencesListEncoder listEncoder) { this.listEncoder = listEncoder; } private void setUp(@NonNull BinaryMessenger messenger, @NonNull Context context) { preferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); try { - SharedPreferencesApi.setup(messenger, this); + SharedPreferencesApi.setUp(messenger, this); } catch (Exception ex) { Log.e(TAG, "Received exception while setting up SharedPreferencesPlugin", ex); } @@ -63,7 +63,7 @@ public void onAttachedToEngine(@NonNull FlutterPlugin.FlutterPluginBinding bindi @Override public void onDetachedFromEngine(@NonNull FlutterPlugin.FlutterPluginBinding binding) { - SharedPreferencesApi.setup(binding.getBinaryMessenger(), null); + SharedPreferencesApi.setUp(binding.getBinaryMessenger(), null); } @Override diff --git a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java index 26fb7958fa9..4041ad9aa6f 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java +++ b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.sharedpreferences; @@ -88,14 +88,14 @@ public interface SharedPreferencesApi { * Sets up an instance of `SharedPreferencesApi` to handle messages through the * `binaryMessenger`. */ - static void setup( + static void setUp( @NonNull BinaryMessenger binaryMessenger, @Nullable SharedPreferencesApi api) { { BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.remove", + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.remove", getCodec(), taskQueue); if (api != null) { @@ -122,7 +122,7 @@ static void setup( BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.setBool", + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setBool", getCodec(), taskQueue); if (api != null) { @@ -150,7 +150,7 @@ static void setup( BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.setString", + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setString", getCodec(), taskQueue); if (api != null) { @@ -178,7 +178,7 @@ static void setup( BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.setInt", + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setInt", getCodec(), taskQueue); if (api != null) { @@ -207,7 +207,7 @@ static void setup( BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.setDouble", + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setDouble", getCodec(), taskQueue); if (api != null) { @@ -235,7 +235,7 @@ static void setup( BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.setStringList", + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setStringList", getCodec(), taskQueue); if (api != null) { @@ -263,7 +263,7 @@ static void setup( BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.clear", + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.clear", getCodec(), taskQueue); if (api != null) { @@ -291,7 +291,7 @@ static void setup( BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.getAll", + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.getAll", getCodec(), taskQueue); if (api != null) { diff --git a/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/MessagesAsync.g.kt b/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/MessagesAsync.g.kt new file mode 100644 index 00000000000..159253f4280 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/MessagesAsync.g.kt @@ -0,0 +1,453 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v16.0.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package io.flutter.plugins.sharedpreferences + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +private fun wrapResult(result: Any?): List { + return listOf(result) +} + +private fun wrapError(exception: Throwable): List { + if (exception is SharedPreferencesError) { + return listOf(exception.code, exception.message, exception.details) + } else { + return listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) + } +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class SharedPreferencesError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +/** Generated class from Pigeon that represents data sent in messages. */ +data class SharedPreferencesPigeonOptions(val fileKey: String? = null) { + + companion object { + @Suppress("UNCHECKED_CAST") + fun fromList(list: List): SharedPreferencesPigeonOptions { + val fileKey = list[0] as String? + return SharedPreferencesPigeonOptions(fileKey) + } + } + + fun toList(): List { + return listOf( + fileKey, + ) + } +} + +@Suppress("UNCHECKED_CAST") +private object SharedPreferencesAsyncApiCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 128.toByte() -> { + return (readValue(buffer) as? List)?.let { + SharedPreferencesPigeonOptions.fromList(it) + } + } + else -> super.readValueOfType(type, buffer) + } + } + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is SharedPreferencesPigeonOptions -> { + stream.write(128) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface SharedPreferencesAsyncApi { + /** Adds property to shared preferences data set of type bool. */ + fun setBool(key: String, value: Boolean, options: SharedPreferencesPigeonOptions) + /** Adds property to shared preferences data set of type String. */ + fun setString(key: String, value: String, options: SharedPreferencesPigeonOptions) + /** Adds property to shared preferences data set of type int. */ + fun setInt(key: String, value: Long, options: SharedPreferencesPigeonOptions) + /** Adds property to shared preferences data set of type double. */ + fun setDouble(key: String, value: Double, options: SharedPreferencesPigeonOptions) + /** Adds property to shared preferences data set of type List. */ + fun setStringList(key: String, value: List, options: SharedPreferencesPigeonOptions) + /** Gets individual String value stored with [key], if any. */ + fun getString(key: String, options: SharedPreferencesPigeonOptions): String? + /** Gets individual void value stored with [key], if any. */ + fun getBool(key: String, options: SharedPreferencesPigeonOptions): Boolean? + /** Gets individual double value stored with [key], if any. */ + fun getDouble(key: String, options: SharedPreferencesPigeonOptions): Double? + /** Gets individual int value stored with [key], if any. */ + fun getInt(key: String, options: SharedPreferencesPigeonOptions): Long? + /** Gets individual List value stored with [key], if any. */ + fun getStringList(key: String, options: SharedPreferencesPigeonOptions): List? + /** Removes all properties from shared preferences data set with matching prefix. */ + fun clear(allowList: List?, options: SharedPreferencesPigeonOptions) + /** Gets all properties from shared preferences data set with matching prefix. */ + fun getAll(allowList: List?, options: SharedPreferencesPigeonOptions): Map + /** Gets all properties from shared preferences data set with matching prefix. */ + fun getKeys(allowList: List?, options: SharedPreferencesPigeonOptions): List + + companion object { + /** The codec used by SharedPreferencesAsyncApi. */ + val codec: MessageCodec by lazy { SharedPreferencesAsyncApiCodec } + /** + * Sets up an instance of `SharedPreferencesAsyncApi` to handle messages through the + * `binaryMessenger`. + */ + @Suppress("UNCHECKED_CAST") + fun setUp(binaryMessenger: BinaryMessenger, api: SharedPreferencesAsyncApi?) { + run { + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setBool", + codec, + taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val valueArg = args[1] as Boolean + val optionsArg = args[2] as SharedPreferencesPigeonOptions + var wrapped: List + try { + api.setBool(keyArg, valueArg, optionsArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setString", + codec, + taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val valueArg = args[1] as String + val optionsArg = args[2] as SharedPreferencesPigeonOptions + var wrapped: List + try { + api.setString(keyArg, valueArg, optionsArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setInt", + codec, + taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val valueArg = args[1].let { if (it is Int) it.toLong() else it as Long } + val optionsArg = args[2] as SharedPreferencesPigeonOptions + var wrapped: List + try { + api.setInt(keyArg, valueArg, optionsArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setDouble", + codec, + taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val valueArg = args[1] as Double + val optionsArg = args[2] as SharedPreferencesPigeonOptions + var wrapped: List + try { + api.setDouble(keyArg, valueArg, optionsArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setStringList", + codec, + taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val valueArg = args[1] as List + val optionsArg = args[2] as SharedPreferencesPigeonOptions + var wrapped: List + try { + api.setStringList(keyArg, valueArg, optionsArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getString", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val optionsArg = args[1] as SharedPreferencesPigeonOptions + var wrapped: List + try { + wrapped = listOf(api.getString(keyArg, optionsArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getBool", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val optionsArg = args[1] as SharedPreferencesPigeonOptions + var wrapped: List + try { + wrapped = listOf(api.getBool(keyArg, optionsArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getDouble", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val optionsArg = args[1] as SharedPreferencesPigeonOptions + var wrapped: List + try { + wrapped = listOf(api.getDouble(keyArg, optionsArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getInt", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val optionsArg = args[1] as SharedPreferencesPigeonOptions + var wrapped: List + try { + wrapped = listOf(api.getInt(keyArg, optionsArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getStringList", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val keyArg = args[0] as String + val optionsArg = args[1] as SharedPreferencesPigeonOptions + var wrapped: List + try { + wrapped = listOf(api.getStringList(keyArg, optionsArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.clear", + codec, + taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val allowListArg = args[0] as List? + val optionsArg = args[1] as SharedPreferencesPigeonOptions + var wrapped: List + try { + api.clear(allowListArg, optionsArg) + wrapped = listOf(null) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getAll", + codec, + taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val allowListArg = args[0] as List? + val optionsArg = args[1] as SharedPreferencesPigeonOptions + var wrapped: List + try { + wrapped = listOf(api.getAll(allowListArg, optionsArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val taskQueue = binaryMessenger.makeBackgroundTaskQueue() + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getKeys", + codec, + taskQueue) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val allowListArg = args[0] as List? + val optionsArg = args[1] as SharedPreferencesPigeonOptions + var wrapped: List + try { + wrapped = listOf(api.getKeys(allowListArg, optionsArg)) + } catch (exception: Throwable) { + wrapped = wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.kt b/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.kt new file mode 100644 index 00000000000..f524b222129 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.kt @@ -0,0 +1,274 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.sharedpreferences + +import android.content.Context +import android.util.Base64 +import android.util.Log +import androidx.annotation.VisibleForTesting +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.doublePreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.longPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.BinaryMessenger +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.ObjectInputStream +import java.io.ObjectOutputStream +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.runBlocking + +const val TAG = "SharedPreferencesPlugin" +const val SHARED_PREFERENCES_NAME = "FlutterSharedPreferences" +const val LIST_PREFIX = "VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu" + +private val Context.sharedPreferencesDataStore: DataStore by + preferencesDataStore(SHARED_PREFERENCES_NAME) + +/// SharedPreferencesPlugin +class SharedPreferencesPlugin() : FlutterPlugin, SharedPreferencesAsyncApi { + private lateinit var context: Context + + private var listEncoder = ListEncoder() as SharedPreferencesListEncoder + + @VisibleForTesting + constructor(listEncoder: SharedPreferencesListEncoder) : this() { + this.listEncoder = listEncoder + } + + private fun setUp(messenger: BinaryMessenger, context: Context) { + this.context = context + try { + SharedPreferencesAsyncApi.setUp(messenger, this) + } catch (ex: Exception) { + Log.e(TAG, "Received exception while setting up SharedPreferencesPlugin", ex) + } + } + + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + setUp(binding.binaryMessenger, binding.applicationContext) + DeprecatedSharedPreferencesPlugin().onAttachedToEngine(binding) + } + + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + SharedPreferencesAsyncApi.setUp(binding.binaryMessenger, null) + } + + /** Adds property to data store of type bool. */ + override fun setBool(key: String, value: Boolean, options: SharedPreferencesPigeonOptions) { + return runBlocking { + val boolKey = booleanPreferencesKey(key) + context.sharedPreferencesDataStore.edit { preferences -> preferences[boolKey] = value } + } + } + + /** Adds property to data store of type String. */ + override fun setString(key: String, value: String, options: SharedPreferencesPigeonOptions) { + return runBlocking { dataStoreSetString(key, value) } + } + + private suspend fun dataStoreSetString(key: String, value: String) { + val stringKey = stringPreferencesKey(key) + context.sharedPreferencesDataStore.edit { preferences -> preferences[stringKey] = value } + } + + /** Adds property to data store of type int. Converted to Long by pigeon, and saved as such. */ + override fun setInt(key: String, value: Long, options: SharedPreferencesPigeonOptions) { + return runBlocking { + val intKey = longPreferencesKey(key) + context.sharedPreferencesDataStore.edit { preferences -> preferences[intKey] = value } + } + } + + /** Adds property to data store of type double. */ + override fun setDouble(key: String, value: Double, options: SharedPreferencesPigeonOptions) { + return runBlocking { + val doubleKey = doublePreferencesKey(key) + context.sharedPreferencesDataStore.edit { preferences -> preferences[doubleKey] = value } + } + } + + /** Adds property to data store of type List. */ + override fun setStringList( + key: String, + value: List, + options: SharedPreferencesPigeonOptions + ) { + val valueString = LIST_PREFIX + listEncoder.encode(value) + return runBlocking { dataStoreSetString(key, valueString) } + } + + /** Removes all properties from data store. */ + override fun clear(allowList: List?, options: SharedPreferencesPigeonOptions) { + runBlocking { + context.sharedPreferencesDataStore.edit { preferences -> + allowList?.let { list -> + list.forEach { key -> + val preferencesKey = booleanPreferencesKey(key) + preferences.remove(preferencesKey) + } + } ?: preferences.clear() + } + } + } + + /** Gets all properties from data store. */ + override fun getAll( + allowList: List?, + options: SharedPreferencesPigeonOptions + ): Map { + return runBlocking { getPrefs(allowList) } + } + + /** Gets int (as long) at [key] from data store. */ + override fun getInt(key: String, options: SharedPreferencesPigeonOptions): Long? { + val value: Long? + runBlocking { + val preferencesKey = longPreferencesKey(key) + val preferenceFlow: Flow = + context.sharedPreferencesDataStore.data.map { preferences -> preferences[preferencesKey] } + + value = preferenceFlow.firstOrNull() + } + return value + } + + /** Gets bool at [key] from data store. */ + override fun getBool(key: String, options: SharedPreferencesPigeonOptions): Boolean? { + val value: Boolean? + runBlocking { + val preferencesKey = booleanPreferencesKey(key) + val preferenceFlow: Flow = + context.sharedPreferencesDataStore.data.map { preferences -> preferences[preferencesKey] } + + value = preferenceFlow.firstOrNull() + } + return value + } + /** Gets double at [key] from data store. */ + override fun getDouble(key: String, options: SharedPreferencesPigeonOptions): Double? { + val value: Double? + runBlocking { + val preferencesKey = stringPreferencesKey(key) + val preferenceFlow: Flow = + context.sharedPreferencesDataStore.data.map { preferences -> + transformPref(preferences[preferencesKey] as Any?) as Double? + } + + value = preferenceFlow.firstOrNull() + } + return value + } + + /** Gets String at [key] from data store. */ + override fun getString(key: String, options: SharedPreferencesPigeonOptions): String? { + val value: String? + runBlocking { + val preferencesKey = stringPreferencesKey(key) + val preferenceFlow: Flow = + context.sharedPreferencesDataStore.data.map { preferences -> preferences[preferencesKey] } + + value = preferenceFlow.firstOrNull() + } + return value + } + + /** Gets StringList at [key] from data store. */ + override fun getStringList(key: String, options: SharedPreferencesPigeonOptions): List? { + return (transformPref(getString(key, options) as Any?) as List<*>?)?.filterIsInstance() + } + + /** Gets all properties from data store. */ + override fun getKeys( + allowList: List?, + options: SharedPreferencesPigeonOptions + ): List { + val prefs = runBlocking { getPrefs(allowList) } + return prefs.keys.toList() + } + + private suspend fun getPrefs(allowList: List?): Map { + val allowSet = allowList?.toSet() + val filteredMap = mutableMapOf() + + val keys = readAllKeys() + keys?.forEach() { key -> + val value = getValueByKey(key) + if (preferencesFilter(key.toString(), value, allowSet)) { + val transformedValue = transformPref(value) + if (transformedValue != null) { + filteredMap[key.toString()] = transformedValue + } + } + } + return filteredMap + } + + private suspend fun readAllKeys(): Set>? { + val keys = context.sharedPreferencesDataStore.data.map { it.asMap().keys } + return keys.firstOrNull() + } + + private suspend fun getValueByKey(key: Preferences.Key<*>): Any? { + val value = context.sharedPreferencesDataStore.data.map { it[key] } + return value.firstOrNull() + } + + /** + * Returns false for any preferences that are not included in [allowList]. + * + * If no [allowList] is provided, instead returns false for any preferences that are not supported + * by shared_preferences. + */ + private fun preferencesFilter(key: String, value: Any?, allowList: Set?): Boolean { + if (allowList == null) { + return value is Boolean || value is Long || value is String || value is Double + } + + return allowList.contains(key) + } + + /** Transforms preferences that are stored as Strings back to original type. */ + private fun transformPref(value: Any?): Any? { + if (value is String) { + if (value.startsWith(LIST_PREFIX)) { + return listEncoder.decode(value.substring(LIST_PREFIX.length)) + } + } + return value + } + + /** Class that provides tools for encoding and decoding List to String and back. */ + class ListEncoder : SharedPreferencesListEncoder { + override fun encode(list: List): String { + try { + val byteStream = ByteArrayOutputStream() + val stream = ObjectOutputStream(byteStream) + stream.writeObject(list) + stream.flush() + return Base64.encodeToString(byteStream.toByteArray(), 0) + } catch (e: RuntimeException) { + throw RuntimeException(e) + } + } + + override fun decode(listString: String): List { + try { + val byteArray = Base64.decode(listString, 0) + val stream = ObjectInputStream(ByteArrayInputStream(byteArray)) + return (stream.readObject() as List<*>).filterIsInstance() + } catch (e: RuntimeException) { + throw RuntimeException(e) + } + } + } +} diff --git a/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.java b/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesTest.java similarity index 90% rename from packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.java rename to packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesTest.java index 19debfeff78..26dc6fee0f2 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.java +++ b/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesTest.java @@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyString; @@ -15,6 +16,7 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -24,9 +26,9 @@ import org.mockito.Mock; import org.mockito.Mockito; -public class SharedPreferencesTest { +public class DeprecatedSharedPreferencesTest { - SharedPreferencesPlugin plugin; + DeprecatedSharedPreferencesPlugin plugin; @Mock BinaryMessenger mockMessenger; @Mock FlutterPlugin.FlutterPluginBinding flutterPluginBinding; @@ -42,7 +44,7 @@ public void before() { Mockito.when(flutterPluginBinding.getApplicationContext()).thenReturn(context); Mockito.when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs); - plugin = new SharedPreferencesPlugin(new ListEncoder()); + plugin = new DeprecatedSharedPreferencesPlugin(new ListEncoder()); plugin.onAttachedToEngine(flutterPluginBinding); } @@ -92,24 +94,24 @@ public void allowList() { addData(); - final List allowList = Arrays.asList("flutter.Language"); + final List allowList = Collections.singletonList("flutter.Language"); Map allData = plugin.getAll("flutter.", allowList); assertEquals(allData.size(), 1); assertEquals(allData.get("flutter.Language"), "Java"); - assertEquals(allData.get("flutter.Counter"), null); + assertNull(allData.get("flutter.Counter")); allData = plugin.getAll("", allowList); assertEquals(allData.size(), 1); assertEquals(allData.get("flutter.Language"), "Java"); - assertEquals(allData.get("flutter.Counter"), null); + assertNull(allData.get("flutter.Counter")); allData = plugin.getAll("prefix.", allowList); assertEquals(allData.size(), 0); - assertEquals(allData.get("flutter.Language"), null); + assertNull(allData.get("flutter.Language")); } @Test @@ -174,7 +176,7 @@ public void clearWithAllowList() { assertEquals(plugin.getAll("", null).size(), 15); - plugin.clear("flutter.", Arrays.asList("flutter.Language")); + plugin.clear("flutter.", Collections.singletonList("flutter.Language")); assertEquals(plugin.getAll("", null).size(), 14); } @@ -240,26 +242,25 @@ public static class FakeSharedPreferencesEditor implements SharedPreferences.Edi } @Override - public @NonNull SharedPreferences.Editor putBoolean( - @NonNull String key, @NonNull boolean value) { + public @NonNull SharedPreferences.Editor putBoolean(@NonNull String key, boolean value) { sharedPrefData.put(key, value); return this; } @Override - public @NonNull SharedPreferences.Editor putInt(@NonNull String key, @NonNull int value) { + public @NonNull SharedPreferences.Editor putInt(@NonNull String key, int value) { sharedPrefData.put(key, value); return this; } @Override - public @NonNull SharedPreferences.Editor putLong(@NonNull String key, @NonNull long value) { + public @NonNull SharedPreferences.Editor putLong(@NonNull String key, long value) { sharedPrefData.put(key, value); return this; } @Override - public @NonNull SharedPreferences.Editor putFloat(@NonNull String key, @NonNull float value) { + public @NonNull SharedPreferences.Editor putFloat(@NonNull String key, float value) { sharedPrefData.put(key, value); return this; } @@ -271,7 +272,7 @@ public static class FakeSharedPreferencesEditor implements SharedPreferences.Edi } @Override - public @NonNull boolean commit() { + public boolean commit() { return true; } @@ -303,27 +304,27 @@ private static class FakeSharedPreferences implements SharedPreferences { // All methods below are not implemented. @Override - public @NonNull boolean contains(@NonNull String key) { + public boolean contains(@NonNull String key) { throw new UnsupportedOperationException("This method is not implemented for testing"); } @Override - public @NonNull boolean getBoolean(@NonNull String key, @NonNull boolean defValue) { + public boolean getBoolean(@NonNull String key, boolean defValue) { throw new UnsupportedOperationException("This method is not implemented for testing"); } @Override - public @NonNull float getFloat(@NonNull String key, @NonNull float defValue) { + public float getFloat(@NonNull String key, float defValue) { throw new UnsupportedOperationException("This method is not implemented for testing"); } @Override - public @NonNull int getInt(@NonNull String key, @NonNull int defValue) { + public int getInt(@NonNull String key, int defValue) { throw new UnsupportedOperationException("This method is not implemented for testing"); } @Override - public @NonNull long getLong(@NonNull String key, @NonNull long defValue) { + public long getLong(@NonNull String key, long defValue) { throw new UnsupportedOperationException("This method is not implemented for testing"); } diff --git a/packages/shared_preferences/shared_preferences_android/android/src/test/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.kt b/packages/shared_preferences/shared_preferences_android/android/src/test/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.kt new file mode 100644 index 00000000000..64c08a1c4df --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/android/src/test/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.kt @@ -0,0 +1,174 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.sharedpreferences + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.plugin.common.BinaryMessenger +import io.mockk.every +import io.mockk.mockk +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +internal class SharedPreferencesTest { + private val stringKey = "testString" + + private val boolKey = "testBool" + + private val intKey = "testInt" + + private val doubleKey = "testDouble" + + private val listKey = "testList" + + private val testString = "hello world" + + private val testBool = true + + private val testInt = 42L + + private val testDouble = 3.14159 + + private val testList = listOf("foo", "bar") + + private val emptyOptions = SharedPreferencesPigeonOptions() + + private fun pluginSetup(): SharedPreferencesPlugin { + val testContext: Context = ApplicationProvider.getApplicationContext() + + val plugin = SharedPreferencesPlugin() + val binaryMessenger = mockk() + val flutterPluginBinding = mockk() + every { flutterPluginBinding.binaryMessenger } returns binaryMessenger + every { flutterPluginBinding.applicationContext } returns testContext + plugin.onAttachedToEngine(flutterPluginBinding) + return plugin + } + + @Test + fun testSetAndGetBool() { + val plugin = pluginSetup() + plugin.setBool(boolKey, testBool, emptyOptions) + Assert.assertEquals(plugin.getBool(boolKey, emptyOptions), testBool) + } + + @Test + fun testSetAndGetString() { + val plugin = pluginSetup() + plugin.setString(stringKey, testString, emptyOptions) + Assert.assertEquals(plugin.getString(stringKey, emptyOptions), testString) + } + + @Test + fun testSetAndGetInt() { + val plugin = pluginSetup() + plugin.setInt(intKey, testInt, emptyOptions) + Assert.assertEquals(plugin.getInt(intKey, emptyOptions), testInt) + } + + @Test + fun testSetAndGetDouble() { + val plugin = pluginSetup() + plugin.setDouble(doubleKey, testDouble, emptyOptions) + Assert.assertEquals(plugin.getDouble(doubleKey, emptyOptions), testDouble) + } + + @Test + fun testSetAndGetStringList() { + val plugin = pluginSetup() + plugin.setStringList(listKey, testList, emptyOptions) + Assert.assertEquals(plugin.getStringList(listKey, emptyOptions), testList) + } + + @Test + fun testGetKeys() { + val plugin = pluginSetup() + plugin.setBool(boolKey, testBool, emptyOptions) + plugin.setString(stringKey, testString, emptyOptions) + plugin.setInt(intKey, testInt, emptyOptions) + plugin.setDouble(doubleKey, testDouble, emptyOptions) + plugin.setStringList(listKey, testList, emptyOptions) + val keyList = plugin.getKeys(listOf(boolKey, stringKey), emptyOptions) + Assert.assertEquals(keyList.size, 2) + Assert.assertTrue(keyList.contains(stringKey)) + Assert.assertTrue(keyList.contains(boolKey)) + } + + @Test + fun testClear() { + val plugin = pluginSetup() + plugin.setBool(boolKey, testBool, emptyOptions) + plugin.setString(stringKey, testString, emptyOptions) + plugin.setInt(intKey, testInt, emptyOptions) + plugin.setDouble(doubleKey, testDouble, emptyOptions) + plugin.setStringList(listKey, testList, emptyOptions) + + plugin.clear(null, emptyOptions) + + Assert.assertNull(plugin.getBool(boolKey, emptyOptions)) + Assert.assertNull(plugin.getBool(stringKey, emptyOptions)) + Assert.assertNull(plugin.getBool(intKey, emptyOptions)) + Assert.assertNull(plugin.getBool(doubleKey, emptyOptions)) + Assert.assertNull(plugin.getBool(listKey, emptyOptions)) + } + + @Test + fun testGetAll() { + val plugin = pluginSetup() + plugin.setBool(boolKey, testBool, emptyOptions) + plugin.setString(stringKey, testString, emptyOptions) + plugin.setInt(intKey, testInt, emptyOptions) + plugin.setDouble(doubleKey, testDouble, emptyOptions) + plugin.setStringList(listKey, testList, emptyOptions) + + val all = plugin.getAll(null, emptyOptions) + + Assert.assertEquals(all[boolKey], testBool) + Assert.assertEquals(all[stringKey], testString) + Assert.assertEquals(all[intKey], testInt) + Assert.assertEquals(all[doubleKey], testDouble) + Assert.assertEquals(all[listKey], testList) + } + + @Test + fun testClearWithAllowList() { + val plugin = pluginSetup() + plugin.setBool(boolKey, testBool, emptyOptions) + plugin.setString(stringKey, testString, emptyOptions) + plugin.setInt(intKey, testInt, emptyOptions) + plugin.setDouble(doubleKey, testDouble, emptyOptions) + plugin.setStringList(listKey, testList, emptyOptions) + + plugin.clear(listOf(boolKey, stringKey), emptyOptions) + + Assert.assertNull(plugin.getBool(boolKey, emptyOptions)) + Assert.assertNull(plugin.getBool(stringKey, emptyOptions)) + Assert.assertNotNull(plugin.getBool(intKey, emptyOptions)) + Assert.assertNotNull(plugin.getBool(doubleKey, emptyOptions)) + Assert.assertNotNull(plugin.getBool(listKey, emptyOptions)) + } + + @Test + fun testGetAllWithAllowList() { + val plugin = pluginSetup() + plugin.setBool(boolKey, testBool, emptyOptions) + plugin.setString(stringKey, testString, emptyOptions) + plugin.setInt(intKey, testInt, emptyOptions) + plugin.setDouble(doubleKey, testDouble, emptyOptions) + plugin.setStringList(listKey, testList, emptyOptions) + + val all = plugin.getAll(listOf(boolKey, stringKey), emptyOptions) + + Assert.assertEquals(all[boolKey], testBool) + Assert.assertEquals(all[stringKey], testString) + Assert.assertNull(all[intKey]) + Assert.assertNull(all[doubleKey]) + Assert.assertNull(all[listKey]) + } +} diff --git a/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle b/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle index 237a211a6cc..9e46ede2d7c 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle @@ -57,4 +57,7 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "androidx.datastore:datastore-preferences:1.0.0" } + +//implementation 'androidx.datastore:datastore:1.0.0' \ No newline at end of file diff --git a/packages/shared_preferences/shared_preferences_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java b/packages/shared_preferences/shared_preferences_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java index 21923b54698..6f454ccfb97 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java +++ b/packages/shared_preferences/shared_preferences_android/example/android/app/src/androidTest/java/io/flutter/plugins/DartIntegrationTest.java @@ -14,7 +14,7 @@ * a native java unit test or a java class with a dart integration. * * See: https://github.com/flutter/flutter/blob/master/docs/ecosystem/testing/Plugin-Tests.md#enabling-android-ui-tests - * for more infomation. + * for more information. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) diff --git a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle index 4dec761be7b..f57c64e8ad3 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.2.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/packages/shared_preferences/shared_preferences_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/shared_preferences/shared_preferences_android/example/android/gradle/wrapper/gradle-wrapper.properties index aeaff6f869f..9087d16a204 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/shared_preferences/shared_preferences_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Jun 23 08:50:38 CEST 2017 +#Wed Jan 17 19:21:34 PST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart index 2bb16133840..d98d6419475 100644 --- a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart @@ -5,6 +5,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:shared_preferences_android/shared_preferences_android.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; @@ -462,9 +464,8 @@ void main() { expect(values['Int'], writeCount); }); - testWidgets('string clash with lists, big integers and doubles', - (WidgetTester _) async { - const String key = 'akey'; + testWidgets('string clash with lists and doubles', (WidgetTester _) async { + const String key = 'aKey'; const String value = 'a string value'; await preferences.clearWithParameters( ClearParameters( @@ -477,8 +478,6 @@ void main() { const List specialPrefixes = [ // Prefix for lists: 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu', - // Prefix for big integers: - 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBCaWdJbnRlZ2Vy', // Prefix for doubles: 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBEb3VibGUu', ]; @@ -495,4 +494,199 @@ void main() { } }); }); + + group('shared_preferences_async', () { + const SharedPreferencesAndroidOptions emptyOptions = + SharedPreferencesAndroidOptions(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + Future getPreferences() async { + final SharedPreferencesAsyncPlatform preferences = + SharedPreferencesAsyncPlatform.instance!; + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + return preferences; + } + + testWidgets('set and get String', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); + }); + + testWidgets('set and get bool', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); + }); + + testWidgets('set and get int', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + }); + + testWidgets('set and get double', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + testWidgets('set and get StringList', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + testWidgets('getPreferences', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); + }); + + testWidgets('getPreferences with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + testWidgets('getKeys', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + testWidgets('getKeys with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + }); + + testWidgets('clear', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + testWidgets('clear with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + }); } diff --git a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart index 69abfbd506d..7f2313f7e23 100644 --- a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart @@ -5,7 +5,8 @@ // ignore_for_file: public_member_api_docs import 'package:flutter/material.dart'; -import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_android/shared_preferences_android.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; void main() { runApp(const MyApp()); @@ -31,32 +32,36 @@ class SharedPreferencesDemo extends StatefulWidget { } class SharedPreferencesDemoState extends State { - final SharedPreferencesStorePlatform _prefs = - SharedPreferencesStorePlatform.instance; + final SharedPreferencesAsyncPlatform _prefs = + SharedPreferencesAsyncPlatform.instance!; + final SharedPreferencesAndroidOptions options = + const SharedPreferencesAndroidOptions(); + static const String _counterKey = 'counter'; late Future _counter; - // Includes the prefix because this is using the platform interface directly, - // but the prefix (which the native code assumes is present) is added by the - // app-facing package. - static const String _prefKey = 'flutter.counter'; - Future _incrementCounter() async { - final Map values = await _prefs.getAll(); - final int counter = ((values[_prefKey] as int?) ?? 0) + 1; + final int? value = await _prefs.getInt(_counterKey, options); + final int counter = (value ?? 0) + 1; setState(() { - _counter = _prefs.setValue('Int', _prefKey, counter).then((bool success) { + _counter = _prefs.setInt(_counterKey, counter, options).then((_) { return counter; }); }); } + Future _getAndSetCounter() async { + setState(() { + _counter = _prefs.getInt(_counterKey, options).then((int? counter) { + return counter ?? 0; + }); + }); + } + @override void initState() { super.initState(); - _counter = _prefs.getAll().then((Map values) { - return (values[_prefKey] as int?) ?? 0; - }); + _getAndSetCounter(); } @override diff --git a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml index 0bdf6908628..632f949762f 100644 --- a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_android/lib/legacy_shared_preferences_android.dart b/packages/shared_preferences/shared_preferences_android/lib/legacy_shared_preferences_android.dart new file mode 100644 index 00000000000..942f81223a0 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/lib/legacy_shared_preferences_android.dart @@ -0,0 +1,102 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; + +import 'src/messages.g.dart'; + +/// The Android implementation of [SharedPreferencesStorePlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for Android. +class LegacySharedPreferencesAndroid extends SharedPreferencesStorePlatform { + /// Creates a new plugin implementation instance. + LegacySharedPreferencesAndroid({ + @visibleForTesting SharedPreferencesApi? api, + }) : _api = api ?? SharedPreferencesApi(); + + final SharedPreferencesApi _api; + + /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. + static void registerWith() { + SharedPreferencesStorePlatform.instance = LegacySharedPreferencesAndroid(); + } + + static const String _defaultPrefix = 'flutter.'; + + @override + Future remove(String key) async { + return _api.remove(key); + } + + @override + Future setValue(String valueType, String key, Object value) async { + switch (valueType) { + case 'String': + return _api.setString(key, value as String); + case 'Bool': + return _api.setBool(key, value as bool); + case 'Int': + return _api.setInt(key, value as int); + case 'Double': + return _api.setDouble(key, value as double); + case 'StringList': + return _api.setStringList(key, value as List); + } + // TODO(tarrinneal): change to ArgumentError across all platforms. + throw PlatformException( + code: 'InvalidOperation', + message: '"$valueType" is not a supported type.'); + } + + @override + Future clear() async { + return clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); + } + + @override + Future clearWithPrefix(String prefix) async { + return clearWithParameters( + ClearParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future clearWithParameters(ClearParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + return _api.clear( + filter.prefix, + filter.allowList?.toList(), + ); + } + + @override + Future> getAll() async { + return getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); + } + + @override + Future> getAllWithPrefix(String prefix) async { + return getAllWithParameters( + GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future> getAllWithParameters( + GetAllParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + final Map data = + await _api.getAll(filter.prefix, filter.allowList?.toList()); + return data.cast(); + } +} diff --git a/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart index 352d36484e2..5a17433ef8a 100644 --- a/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart +++ b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart @@ -4,99 +4,194 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; -import 'src/messages.g.dart'; +import 'legacy_shared_preferences_android.dart'; +import 'src/messages_async.g.dart'; -/// The Android implementation of [SharedPreferencesStorePlatform]. +const String _listPrefix = 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu'; + +/// The Android implementation of [SharedPreferencesAsyncPlatform]. /// /// This class implements the `package:shared_preferences` functionality for Android. -class SharedPreferencesAndroid extends SharedPreferencesStorePlatform { +base class SharedPreferencesAndroid extends SharedPreferencesAsyncPlatform { /// Creates a new plugin implementation instance. SharedPreferencesAndroid({ - @visibleForTesting SharedPreferencesApi? api, - }) : _api = api ?? SharedPreferencesApi(); + @visibleForTesting SharedPreferencesAsyncApi? api, + }) : _api = api ?? SharedPreferencesAsyncApi(); - final SharedPreferencesApi _api; + final SharedPreferencesAsyncApi _api; - /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. + /// Registers this class as the default instance of [SharedPreferencesAsyncPlatform]. static void registerWith() { - SharedPreferencesStorePlatform.instance = SharedPreferencesAndroid(); + SharedPreferencesAsyncPlatform.instance = SharedPreferencesAndroid(); + // A temporary work-around for having two plugins contained in a single package. + LegacySharedPreferencesAndroid.registerWith(); } - static const String _defaultPrefix = 'flutter.'; + /// Returns a SharedPreferencesPigeonOptions for sending to platform. + SharedPreferencesPigeonOptions _convertOptionsToPigeonOptions( + SharedPreferencesOptions options) { + return SharedPreferencesPigeonOptions(); + } @override - Future remove(String key) async { - return _api.remove(key); + Future> getKeys( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 + // is fixed. In practice, the values will never be null, and the native implementation assumes that. + return (await _api.getKeys( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + )) + .cast() + .toSet(); } @override - Future setValue(String valueType, String key, Object value) async { - switch (valueType) { - case 'String': - return _api.setString(key, value as String); - case 'Bool': - return _api.setBool(key, value as bool); - case 'Int': - return _api.setInt(key, value as int); - case 'Double': - return _api.setDouble(key, value as double); - case 'StringList': - return _api.setStringList(key, value as List); + Future setString( + String key, + String value, + SharedPreferencesOptions options, + ) async { + if (value.startsWith(_listPrefix)) { + throw ArgumentError( + 'StorageError: This string cannot be stored as it clashes with special identifier prefixes'); } - // TODO(tarrinneal): change to ArgumentError across all platforms. - throw PlatformException( - code: 'InvalidOperation', - message: '"$valueType" is not a supported type.'); + + return _api.setString(key, value, _convertOptionsToPigeonOptions(options)); } @override - Future clear() async { - return clearWithParameters( - ClearParameters( - filter: PreferencesFilter(prefix: _defaultPrefix), - ), - ); + Future setInt( + String key, + int value, + SharedPreferencesOptions options, + ) async { + return _api.setInt(key, value, _convertOptionsToPigeonOptions(options)); } @override - Future clearWithPrefix(String prefix) async { - return clearWithParameters( - ClearParameters(filter: PreferencesFilter(prefix: prefix))); + Future setDouble( + String key, + double value, + SharedPreferencesOptions options, + ) async { + return _api.setDouble(key, value, _convertOptionsToPigeonOptions(options)); } @override - Future clearWithParameters(ClearParameters parameters) async { - final PreferencesFilter filter = parameters.filter; - return _api.clear( - filter.prefix, - filter.allowList?.toList(), - ); + Future setBool( + String key, + bool value, + SharedPreferencesOptions options, + ) async { + return _api.setBool(key, value, _convertOptionsToPigeonOptions(options)); } @override - Future> getAll() async { - return getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: _defaultPrefix), - ), - ); + Future setStringList( + String key, + List value, + SharedPreferencesOptions options, + ) async { + return _api.setStringList( + key, value, _convertOptionsToPigeonOptions(options)); + } + + @override + Future getString( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => + _api.getString(key, _convertOptionsToPigeonOptions(options))); + } + + @override + Future getBool( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions( + () async => _api.getBool(key, _convertOptionsToPigeonOptions(options))); + } + + @override + Future getDouble( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => + _api.getDouble(key, _convertOptionsToPigeonOptions(options))); } @override - Future> getAllWithPrefix(String prefix) async { - return getAllWithParameters( - GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + Future getInt( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions( + () async => _api.getInt(key, _convertOptionsToPigeonOptions(options))); } @override - Future> getAllWithParameters( - GetAllParameters parameters) async { - final PreferencesFilter filter = parameters.filter; - final Map data = - await _api.getAll(filter.prefix, filter.allowList?.toList()); + Future?> getStringList( + String key, + SharedPreferencesOptions options, + ) async { + // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 + // is fixed. In practice, the values will never be null, and the native implementation assumes that. + return _convertKnownExceptions>(() async => + (await _api.getStringList(key, _convertOptionsToPigeonOptions(options))) + ?.cast()); + } + + Future _convertKnownExceptions(Future Function() method) async { + try { + final T? value = await method(); + return value; + } on PlatformException catch (e) { + if (e.code == 'ClassCastException') { + throw TypeError(); + } else { + rethrow; + } + } + } + + @override + Future clear( + ClearPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + return _api.clear( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + ); + } + + @override + Future> getPreferences( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + final Map data = await _api.getAll( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + ); return data.cast(); } } + +/// Options for the Android specific SharedPreferences plugin. +class SharedPreferencesAndroidOptions extends SharedPreferencesOptions { + /// Constructor for SharedPreferencesAndroidOptions. + const SharedPreferencesAndroidOptions(); +} diff --git a/packages/shared_preferences/shared_preferences_android/lib/src/messages.g.dart b/packages/shared_preferences/shared_preferences_android/lib/src/messages.g.dart index 1e843098973..3a0034796d3 100644 --- a/packages/shared_preferences/shared_preferences_android/lib/src/messages.g.dart +++ b/packages/shared_preferences/shared_preferences_android/lib/src/messages.g.dart @@ -1,9 +1,9 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.5), do not edit directly. +// Autogenerated from Pigeon (v16.0.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -11,238 +11,263 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + class SharedPreferencesApi { /// Constructor for [SharedPreferencesApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. SharedPreferencesApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; - static const MessageCodec codec = StandardMessageCodec(); + static const MessageCodec pigeonChannelCodec = + StandardMessageCodec(); /// Removes property from shared preferences data set. - Future remove(String arg_key) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.remove', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key]) as List?; - if (replyList == null) { + Future remove(String key) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.remove'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as bool?)!; + return (__pigeon_replyList[0] as bool?)!; } } /// Adds property to shared preferences data set of type bool. - Future setBool(String arg_key, bool arg_value) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.setBool', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key, arg_value]) as List?; - if (replyList == null) { + Future setBool(String key, bool value) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, value]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as bool?)!; + return (__pigeon_replyList[0] as bool?)!; } } /// Adds property to shared preferences data set of type String. - Future setString(String arg_key, String arg_value) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.setString', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key, arg_value]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + Future setString(String key, String value) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, value]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as bool?)!; + return (__pigeon_replyList[0] as bool?)!; } } /// Adds property to shared preferences data set of type int. - Future setInt(String arg_key, int arg_value) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.setInt', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key, arg_value]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + Future setInt(String key, int value) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setInt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, value]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as bool?)!; + return (__pigeon_replyList[0] as bool?)!; } } /// Adds property to shared preferences data set of type double. - Future setDouble(String arg_key, double arg_value) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.setDouble', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key, arg_value]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + Future setDouble(String key, double value) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, value]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as bool?)!; + return (__pigeon_replyList[0] as bool?)!; } } /// Adds property to shared preferences data set of type List. - Future setStringList(String arg_key, List arg_value) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.setStringList', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key, arg_value]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + Future setStringList(String key, List value) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.setStringList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, value]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as bool?)!; + return (__pigeon_replyList[0] as bool?)!; } } /// Removes all properties from shared preferences data set with matching prefix. - Future clear(String arg_prefix, List? arg_allowList) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.clear', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_prefix, arg_allowList]) as List?; - if (replyList == null) { + Future clear(String prefix, List? allowList) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.clear'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([prefix, allowList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { - throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], - ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as bool?)!; + return (__pigeon_replyList[0] as bool?)!; } } /// Gets all properties from shared preferences data set with matching prefix. Future> getAll( - String arg_prefix, List? arg_allowList) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.getAll', codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_prefix, arg_allowList]) as List?; - if (replyList == null) { - throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', - ); - } else if (replyList.length > 1) { + String prefix, List? allowList) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesApi.getAll'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([prefix, allowList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as Map?)!.cast(); + return (__pigeon_replyList[0] as Map?)! + .cast(); } } } diff --git a/packages/shared_preferences/shared_preferences_android/lib/src/messages_async.g.dart b/packages/shared_preferences/shared_preferences_android/lib/src/messages_async.g.dart new file mode 100644 index 00000000000..d355cfd916d --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/lib/src/messages_async.g.dart @@ -0,0 +1,424 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Autogenerated from Pigeon (v16.0.5), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +class SharedPreferencesPigeonOptions { + SharedPreferencesPigeonOptions({ + this.fileKey, + }); + + String? fileKey; + + Object encode() { + return [ + fileKey, + ]; + } + + static SharedPreferencesPigeonOptions decode(Object result) { + result as List; + return SharedPreferencesPigeonOptions( + fileKey: result[0] as String?, + ); + } +} + +class _SharedPreferencesAsyncApiCodec extends StandardMessageCodec { + const _SharedPreferencesAsyncApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is SharedPreferencesPigeonOptions) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return SharedPreferencesPigeonOptions.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class SharedPreferencesAsyncApi { + /// Constructor for [SharedPreferencesAsyncApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + SharedPreferencesAsyncApi({BinaryMessenger? binaryMessenger}) + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = + _SharedPreferencesAsyncApiCodec(); + + /// Adds property to shared preferences data set of type bool. + Future setBool( + String key, bool value, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([key, value, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Adds property to shared preferences data set of type String. + Future setString( + String key, String value, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([key, value, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Adds property to shared preferences data set of type int. + Future setInt( + String key, int value, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setInt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([key, value, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Adds property to shared preferences data set of type double. + Future setDouble( + String key, double value, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([key, value, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Adds property to shared preferences data set of type List. + Future setStringList(String key, List value, + SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.setStringList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([key, value, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Gets individual String value stored with [key], if any. + Future getString( + String key, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getString'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as String?); + } + } + + /// Gets individual void value stored with [key], if any. + Future getBool( + String key, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as bool?); + } + } + + /// Gets individual double value stored with [key], if any. + Future getDouble( + String key, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as double?); + } + } + + /// Gets individual int value stored with [key], if any. + Future getInt( + String key, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getInt'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as int?); + } + } + + /// Gets individual List value stored with [key], if any. + Future?> getStringList( + String key, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getStringList'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return (__pigeon_replyList[0] as List?)?.cast(); + } + } + + /// Removes all properties from shared preferences data set with matching prefix. + Future clear( + List? allowList, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.clear'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([allowList, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else { + return; + } + } + + /// Gets all properties from shared preferences data set with matching prefix. + Future> getAll( + List? allowList, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getAll'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([allowList, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as Map?)! + .cast(); + } + } + + /// Gets all properties from shared preferences data set with matching prefix. + Future> getKeys( + List? allowList, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_android.SharedPreferencesAsyncApi.getKeys'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([allowList, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as List?)!.cast(); + } + } +} diff --git a/packages/shared_preferences/shared_preferences_android/pigeons/messages_async.dart b/packages/shared_preferences/shared_preferences_android/pigeons/messages_async.dart new file mode 100644 index 00000000000..5334cc042f5 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/pigeons/messages_async.dart @@ -0,0 +1,118 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + input: 'pigeons/messages_async.dart', + kotlinOut: + 'android/src/main/kotlin/io/flutter/plugins/sharedpreferences/MessagesAsync.g.kt', + kotlinOptions: KotlinOptions( + package: 'io.flutter.plugins.sharedpreferences', + errorClassName: 'SharedPreferencesError', + ), + dartOut: 'lib/src/messages_async.g.dart', + copyrightHeader: 'pigeons/copyright.txt', +)) +class SharedPreferencesPigeonOptions { + SharedPreferencesPigeonOptions({ + this.fileKey, + }); + String? fileKey; +} + +@HostApi(dartHostTestHandler: 'TestSharedPreferencesAsyncApi') +abstract class SharedPreferencesAsyncApi { + /// Adds property to shared preferences data set of type bool. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + void setBool(String key, bool value, SharedPreferencesPigeonOptions options); + + /// Adds property to shared preferences data set of type String. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + void setString( + String key, + String value, + SharedPreferencesPigeonOptions options, + ); + + /// Adds property to shared preferences data set of type int. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + void setInt( + String key, + int value, + SharedPreferencesPigeonOptions options, + ); + + /// Adds property to shared preferences data set of type double. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + void setDouble( + String key, + double value, + SharedPreferencesPigeonOptions options, + ); + + /// Adds property to shared preferences data set of type List. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + void setStringList( + String key, + List value, + SharedPreferencesPigeonOptions options, + ); + + /// Gets individual String value stored with [key], if any. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + String? getString( + String key, + SharedPreferencesPigeonOptions options, + ); + + /// Gets individual void value stored with [key], if any. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + bool? getBool( + String key, + SharedPreferencesPigeonOptions options, + ); + + /// Gets individual double value stored with [key], if any. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + double? getDouble( + String key, + SharedPreferencesPigeonOptions options, + ); + + /// Gets individual int value stored with [key], if any. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + int? getInt( + String key, + SharedPreferencesPigeonOptions options, + ); + + /// Gets individual List value stored with [key], if any. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + List? getStringList( + String key, + SharedPreferencesPigeonOptions options, + ); + + /// Removes all properties from shared preferences data set with matching prefix. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + void clear( + List? allowList, + SharedPreferencesPigeonOptions options, + ); + + /// Gets all properties from shared preferences data set with matching prefix. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + Map getAll( + List? allowList, + SharedPreferencesPigeonOptions options, + ); + + /// Gets all properties from shared preferences data set with matching prefix. + @TaskQueue(type: TaskQueueType.serialBackgroundThread) + List getKeys( + List? allowList, + SharedPreferencesPigeonOptions options, + ); +} diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 201f30884c9..bd1adb8cb9c 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.2.3 +version: 2.3.0 environment: sdk: ^3.4.0 @@ -20,12 +20,12 @@ flutter: dependencies: flutter: sdk: flutter - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 dev_dependencies: flutter_test: sdk: flutter - pigeon: ^9.2.3 + pigeon: ^16.0.4 topics: - persistence diff --git a/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart b/packages/shared_preferences/shared_preferences_android/test/legacy_shared_preferences_android_test.dart similarity index 96% rename from packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart rename to packages/shared_preferences/shared_preferences_android/test/legacy_shared_preferences_android_test.dart index 421ccfe7ea8..241f11cc099 100644 --- a/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart +++ b/packages/shared_preferences/shared_preferences_android/test/legacy_shared_preferences_android_test.dart @@ -4,7 +4,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:shared_preferences_android/shared_preferences_android.dart'; +import 'package:shared_preferences_android/legacy_shared_preferences_android.dart'; import 'package:shared_preferences_android/src/messages.g.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; @@ -12,7 +12,7 @@ import 'package:shared_preferences_platform_interface/types.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); late _FakeSharedPreferencesApi api; - late SharedPreferencesAndroid plugin; + late LegacySharedPreferencesAndroid plugin; const Map flutterTestValues = { 'flutter.String': 'hello world', @@ -46,13 +46,13 @@ void main() { setUp(() { api = _FakeSharedPreferencesApi(); - plugin = SharedPreferencesAndroid(api: api); + plugin = LegacySharedPreferencesAndroid(api: api); }); - test('registerWith', () { - SharedPreferencesAndroid.registerWith(); + test('registerWith', () async { + LegacySharedPreferencesAndroid.registerWith(); expect(SharedPreferencesStorePlatform.instance, - isA()); + isA()); }); test('remove', () async { @@ -211,7 +211,7 @@ void main() { expect(api.items['flutter.StringList'], ['hi']); }); - test('setValue with unsupported type', () { + test('setValue with unsupported type', () async { expect(() async { await plugin.setValue('Map', 'flutter.key', {}); }, throwsA(isA())); diff --git a/packages/shared_preferences/shared_preferences_android/test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_test.dart new file mode 100755 index 00000000000..6951e8d9dd4 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_test.dart @@ -0,0 +1,300 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences_android/shared_preferences_android.dart'; +import 'package:shared_preferences_android/src/messages_async.g.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + const SharedPreferencesAndroidOptions emptyOptions = + SharedPreferencesAndroidOptions(); + + SharedPreferencesAndroid getPreferences() { + final _FakeSharedPreferencesApi api = _FakeSharedPreferencesApi(); + final SharedPreferencesAndroid preferences = + SharedPreferencesAndroid(api: api); + + return preferences; + } + + test('set and get String', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); + }); + + test('set and get bool', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); + }); + + test('set and get int', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + }); + + test('set and get double', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + test('set and get StringList', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + test('getPreferences', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); + }); + + test('getPreferences with filter', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: + PreferencesFilters(allowList: {stringKey, boolKey})), + emptyOptions); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + test('getKeys', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + test('getKeys with filter', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + }); + + test('clear', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + test('clear with filter', () async { + final SharedPreferencesAndroid preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); +} + +class _FakeSharedPreferencesApi implements SharedPreferencesAsyncApi { + final Map items = {}; + + @override + Future clear( + List? allowList, SharedPreferencesPigeonOptions options) async { + if (allowList != null) { + items.removeWhere((String key, _) => allowList.contains(key)); + } else { + items.clear(); + } + + return true; + } + + @override + Future> getAll( + List? allowList, SharedPreferencesPigeonOptions options) async { + final Map filteredItems = {...items}; + if (allowList != null) { + filteredItems.removeWhere((String key, _) => !allowList.contains(key)); + } + return filteredItems; + } + + @override + Future getBool( + String key, SharedPreferencesPigeonOptions options) async { + return items[key] as bool?; + } + + @override + Future getDouble( + String key, SharedPreferencesPigeonOptions options) async { + return items[key] as double?; + } + + @override + Future getInt( + String key, SharedPreferencesPigeonOptions options) async { + return items[key] as int?; + } + + @override + Future> getKeys( + List? allowList, SharedPreferencesPigeonOptions options) async { + final List filteredItems = items.keys.toList(); + if (allowList != null) { + filteredItems.removeWhere((String key) => !allowList.contains(key)); + } + return filteredItems; + } + + @override + Future getString( + String key, SharedPreferencesPigeonOptions options) async { + return items[key] as String?; + } + + @override + Future?> getStringList( + String key, SharedPreferencesPigeonOptions options) async { + return items[key] as List?; + } + + @override + Future setBool( + String key, bool value, SharedPreferencesPigeonOptions options) async { + items[key] = value; + return true; + } + + @override + Future setDouble( + String key, double value, SharedPreferencesPigeonOptions options) async { + items[key] = value; + return true; + } + + @override + Future setInt( + String key, int value, SharedPreferencesPigeonOptions options) async { + items[key] = value; + return true; + } + + @override + Future setString( + String key, String value, SharedPreferencesPigeonOptions options) async { + items[key] = value; + return true; + } + + @override + Future setStringList(String key, List value, + SharedPreferencesPigeonOptions options) async { + items[key] = value; + return true; + } +} diff --git a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md index e487278c498..38723c28c6c 100644 --- a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.5.0 + +* Renames `SharedPreferencesFoundation` to `LegacySharedPreferencesFoundation`. +* Creates new `SharedPreferencesFoundation` that extends `SharedPreferencesAsyncPlatform`. + ## 2.4.0 * Adds Swift Package Manager compatibility. diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift index 42a1d00d835..2021dca9d41 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift @@ -13,11 +13,17 @@ import XCTest #endif class RunnerTests: XCTestCase { + let testKey = "foo" + let testKeyTwo = "baz" + let testValue = "bar" + + // Deprecated system tests. + let prefixes: [String] = ["aPrefix", ""] func testSetAndGet() throws { for aPrefix in prefixes { - let plugin = SharedPreferencesPlugin() + let plugin = DeprecatedSharedPreferencesPlugin() plugin.setBool(key: "\(aPrefix)aBool", value: true) plugin.setDouble(key: "\(aPrefix)aDouble", value: 3.14) @@ -36,7 +42,7 @@ class RunnerTests: XCTestCase { func testGetWithAllowList() throws { for aPrefix in prefixes { - let plugin = SharedPreferencesPlugin() + let plugin = DeprecatedSharedPreferencesPlugin() plugin.setBool(key: "\(aPrefix)aBool", value: true) plugin.setDouble(key: "\(aPrefix)aDouble", value: 3.14) @@ -55,8 +61,8 @@ class RunnerTests: XCTestCase { func testRemove() throws { for aPrefix in prefixes { - let plugin = SharedPreferencesPlugin() - let testKey = "\(aPrefix)foo" + let plugin = DeprecatedSharedPreferencesPlugin() + let testKey = "\(aPrefix)\(testKey)" plugin.setValue(key: testKey, value: 42) // Make sure there is something to remove, so the test can't pass due to a set failure. @@ -73,8 +79,8 @@ class RunnerTests: XCTestCase { func testClearWithNoAllowlist() throws { for aPrefix in prefixes { - let plugin = SharedPreferencesPlugin() - let testKey = "\(aPrefix)foo" + let plugin = DeprecatedSharedPreferencesPlugin() + let testKey = "\(aPrefix)\(testKey)" plugin.setValue(key: testKey, value: 42) // Make sure there is something to clear, so the test can't pass due to a set failure. @@ -91,19 +97,128 @@ class RunnerTests: XCTestCase { func testClearWithAllowlist() throws { for aPrefix in prefixes { - let plugin = SharedPreferencesPlugin() - let testKey = "\(aPrefix)foo" + let plugin = DeprecatedSharedPreferencesPlugin() + let testKey = "\(aPrefix)\(testKey)" plugin.setValue(key: testKey, value: 42) // Make sure there is something to clear, so the test can't pass due to a set failure. let preRemovalValues = plugin.getAll(prefix: aPrefix, allowList: nil) XCTAssertEqual(preRemovalValues[testKey] as? Int, 42) - plugin.clear(prefix: aPrefix, allowList: ["\(aPrefix)notfoo"]) + plugin.clear(prefix: aPrefix, allowList: ["\(aPrefix)\(testKeyTwo)"]) let finalValues = plugin.getAll(prefix: aPrefix, allowList: nil) XCTAssertEqual(finalValues[testKey] as? Int, 42) } } + // Async system tests. + + let emptyOptions = SharedPreferencesPigeonOptions() + + func testAsyncSetAndGet() throws { + let plugin = SharedPreferencesPlugin() + + try plugin.set(key: "aBool", value: true, options: emptyOptions) + try plugin.set(key: "aDouble", value: 3.14, options: emptyOptions) + try plugin.set(key: "anInt", value: 42, options: emptyOptions) + try plugin.set(key: "aString", value: "hello world", options: emptyOptions) + try plugin.set(key: "aStringList", value: ["hello", "world"], options: emptyOptions) + + XCTAssertEqual(((try plugin.getValue(key: "aBool", options: emptyOptions)) != nil), true) + XCTAssertEqual( + try plugin.getValue(key: "aDouble", options: emptyOptions) as! Double, 3.14, accuracy: 0.0001) + XCTAssertEqual(try plugin.getValue(key: "anInt", options: emptyOptions) as! Int, 42) + XCTAssertEqual( + try plugin.getValue(key: "aString", options: emptyOptions) as! String, "hello world") + XCTAssertEqual( + try plugin.getValue(key: "aStringList", options: emptyOptions) as! [String], + ["hello", "world"]) + } + + func testAsyncGetAll() throws { + let plugin = SharedPreferencesPlugin() + + try plugin.set(key: "aBool", value: true, options: emptyOptions) + try plugin.set(key: "aDouble", value: 3.14, options: emptyOptions) + try plugin.set(key: "anInt", value: 42, options: emptyOptions) + try plugin.set(key: "aString", value: "hello world", options: emptyOptions) + try plugin.set(key: "aStringList", value: ["hello", "world"], options: emptyOptions) + + let storedValues = try plugin.getAll(allowList: nil, options: emptyOptions) + XCTAssertEqual(storedValues["aBool"] as? Bool, true) + XCTAssertEqual(storedValues["aDouble"] as! Double, 3.14, accuracy: 0.0001) + XCTAssertEqual(storedValues["anInt"] as? Int, 42) + XCTAssertEqual(storedValues["aString"] as? String, "hello world") + XCTAssertEqual(storedValues["aStringList"] as? [String], ["hello", "world"]) + + } + + func testAsyncGetAllWithAllowList() throws { + let plugin = SharedPreferencesPlugin() + + try plugin.set(key: "aBool", value: true, options: emptyOptions) + try plugin.set(key: "aDouble", value: 3.14, options: emptyOptions) + try plugin.set(key: "anInt", value: 42, options: emptyOptions) + try plugin.set(key: "aString", value: "hello world", options: emptyOptions) + try plugin.set(key: "aStringList", value: ["hello", "world"], options: emptyOptions) + + let storedValues = try plugin.getAll(allowList: ["aBool"], options: emptyOptions) + XCTAssertEqual(storedValues["aBool"] as? Bool, true) + XCTAssertNil(storedValues["aDouble"] ?? nil) + XCTAssertNil(storedValues["anInt"] ?? nil) + XCTAssertNil(storedValues["aString"] ?? nil) + XCTAssertNil(storedValues["aStringList"] ?? nil) + + } + + func testAsyncRemove() throws { + let plugin = SharedPreferencesPlugin() + try plugin.set(key: testKey, value: testValue, options: emptyOptions) + + // Make sure there is something to remove, so the test can't pass due to a set failure. + let preRemovalValue = try plugin.getValue(key: testKey, options: emptyOptions) as! String + XCTAssertEqual(preRemovalValue, testValue) + + // Then verify that removing it works. + try plugin.remove(key: testKey, options: emptyOptions) + + let finalValue = try plugin.getValue(key: testKey, options: emptyOptions) + XCTAssertNil(finalValue) + + } + + func testAsyncClearWithNoAllowlist() throws { + let plugin = SharedPreferencesPlugin() + try plugin.set(key: testKey, value: testValue, options: emptyOptions) + + // Make sure there is something to remove, so the test can't pass due to a set failure. + let preRemovalValue = try plugin.getValue(key: testKey, options: emptyOptions) as! String + XCTAssertEqual(preRemovalValue, testValue) + + // Then verify that clearing works. + try plugin.clear(allowList: nil, options: emptyOptions) + + let finalValue = try plugin.getValue(key: testKey, options: emptyOptions) + XCTAssertNil(finalValue) + + } + + func testAsyncClearWithAllowlist() throws { + let plugin = SharedPreferencesPlugin() + + try plugin.set(key: testKey, value: testValue, options: emptyOptions) + try plugin.set(key: testKeyTwo, value: testValue, options: emptyOptions) + + // Make sure there is something to clear, so the test can't pass due to a set failure. + let preRemovalValue = try plugin.getValue(key: testKey, options: emptyOptions) as! String + XCTAssertEqual(preRemovalValue, testValue) + + try plugin.clear(allowList: [testKey], options: emptyOptions) + + let finalValueNil = try plugin.getValue(key: testKey, options: emptyOptions) + XCTAssertNil(finalValueNil) + let finalValueNotNil = try plugin.getValue(key: testKeyTwo, options: emptyOptions) as! String + XCTAssertEqual(finalValueNotNil, testValue) + } } diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift index b88562e8398..52b3caa8e8e 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift @@ -10,16 +10,19 @@ import Foundation import FlutterMacOS #endif -public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { +let argumentError: String = "Argument Error" + +public class DeprecatedSharedPreferencesPlugin: NSObject, FlutterPlugin, DeprecatedUserDefaultsApi { + public static func register(with registrar: FlutterPluginRegistrar) { - let instance = SharedPreferencesPlugin() + let instance = DeprecatedSharedPreferencesPlugin() // Workaround for https://github.com/flutter/flutter/issues/118103. #if os(iOS) let messenger = registrar.messenger() #else let messenger = registrar.messenger #endif - UserDefaultsApiSetup.setUp(binaryMessenger: messenger, api: instance) + DeprecatedUserDefaultsApiSetup.setUp(binaryMessenger: messenger, api: instance) } func getAll(prefix: String, allowList: [String]?) -> [String?: Any?] { @@ -54,18 +57,130 @@ public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { /// If [allowList] is included, only items included will be returned. func getAllPrefs(prefix: String, allowList: [String]?) -> [String: Any] { var filteredPrefs: [String: Any] = [:] - var allowSet: Set? + + let prefs = try! SharedPreferencesPlugin.getAllPrefs( + allowList: allowList, options: SharedPreferencesPigeonOptions()) + + for (key, value) in prefs where (key.hasPrefix(prefix)) { + filteredPrefs[key] = value + } + + return filteredPrefs + } + +} + +public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = SharedPreferencesPlugin() + // Workaround for https://github.com/flutter/flutter/issues/118103. + #if os(iOS) + let messenger = registrar.messenger() + #else + let messenger = registrar.messenger + #endif + UserDefaultsApiSetup.setUp(binaryMessenger: messenger, api: instance) + DeprecatedSharedPreferencesPlugin.register(with: registrar) + } + + static private func getUserDefaults(options: SharedPreferencesPigeonOptions) throws + -> UserDefaults + { + #if os(iOS) + if !(options.suiteName?.starts(with: "group.") ?? true) { + throw FlutterError( + code: argumentError, + message: + "The provided Suite Name '\(options.suiteName!)' does not follow the predefined requirements", + details: "") as! Error + } + #endif + let prefs = UserDefaults(suiteName: options.suiteName) + + if prefs == nil { + throw FlutterError( + code: argumentError, + message: "The provided Suite Name '\(options.suiteName!)' does not exist", + details: "") as! Error + } + return prefs! + } + + func getKeys(allowList: [String]?, options: SharedPreferencesPigeonOptions) throws -> [String] { + return Array(try getAll(allowList: allowList, options: options).keys) + } + + func getAll(allowList: [String]?, options: SharedPreferencesPigeonOptions) throws -> [String: Any] + { + return try SharedPreferencesPlugin.getAllPrefs(allowList: allowList, options: options) + } + + func set(key: String, value: Any, options: SharedPreferencesPigeonOptions) throws { + try SharedPreferencesPlugin.getUserDefaults(options: options).set(value, forKey: key) + } + + func getValue(key: String, options: SharedPreferencesPigeonOptions) throws -> Any? { + return try SharedPreferencesPlugin.getUserDefaults(options: options).object(forKey: key) + } + + func remove(key: String, options: SharedPreferencesPigeonOptions) throws { + try SharedPreferencesPlugin.getUserDefaults(options: options).removeObject(forKey: key) + } + + func clear(allowList: [String]?, options: SharedPreferencesPigeonOptions) throws { + let defaults = try SharedPreferencesPlugin.getUserDefaults(options: options) if let allowList = allowList { - allowSet = Set(allowList) + for (key) in allowList { + defaults.removeObject(forKey: key) + } + } else { + for key in defaults.dictionaryRepresentation().keys { + defaults.removeObject(forKey: key) + } } + } + + /// Returns all preferences stored with specified prefix. + /// If [allowList] is included, only items included will be returned. + /// If no [allowList], returns supported types only. + static func getAllPrefs(allowList: [String]?, options: SharedPreferencesPigeonOptions) throws + -> [String: Any] + { + var filteredPrefs: [String: Any] = [:] + var compatiblePrefs: [String: Any] = [:] + let allowSet = allowList.map { Set($0) } if let appDomain = Bundle.main.bundleIdentifier, - let prefs = UserDefaults.standard.persistentDomain(forName: appDomain) + let prefs = try getUserDefaults(options: options).persistentDomain(forName: appDomain) { - for (key, value) in prefs - where (key.hasPrefix(prefix) && (allowSet == nil || allowSet!.contains(key))) { - filteredPrefs[key] = value + if let allowSet = allowSet { + filteredPrefs = prefs.filter { allowSet.contains($0.key) } + } else { + filteredPrefs = prefs } + compatiblePrefs = filteredPrefs.filter { isTypeCompatible(value: $0.value) } } - return filteredPrefs + return compatiblePrefs } + + static func isTypeCompatible(value: Any) -> Bool { + switch value { + case is Bool: + return true + case is Double: + return true + case is String: + return true + case is Int: + return true + case is [Any]: + if let value = value as? [Any] { + return value.allSatisfy(isTypeCompatible) + } + default: + return false + } + return false + } + } diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/messages.g.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/messages.g.swift index cb247f9c8cb..65327a40513 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/messages.g.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/messages.g.swift @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v10.1.6), do not edit directly. +// Autogenerated from Pigeon (v16.0.5), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -33,12 +33,71 @@ private func wrapError(_ error: Any) -> [Any?] { ] } +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } return value as! T? } + +/// Generated class from Pigeon that represents data sent in messages. +struct SharedPreferencesPigeonOptions { + var suiteName: String? = nil + + static func fromList(_ list: [Any?]) -> SharedPreferencesPigeonOptions? { + let suiteName: String? = nilOrValue(list[0]) + + return SharedPreferencesPigeonOptions( + suiteName: suiteName + ) + } + func toList() -> [Any?] { + return [ + suiteName + ] + } +} +private class DeprecatedUserDefaultsApiCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 128: + return SharedPreferencesPigeonOptions.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class DeprecatedUserDefaultsApiCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? SharedPreferencesPigeonOptions { + super.writeByte(128) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class DeprecatedUserDefaultsApiCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return DeprecatedUserDefaultsApiCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return DeprecatedUserDefaultsApiCodecWriter(data: data) + } +} + +class DeprecatedUserDefaultsApiCodec: FlutterStandardMessageCodec { + static let shared = DeprecatedUserDefaultsApiCodec( + readerWriter: DeprecatedUserDefaultsApiCodecReaderWriter()) +} + /// Generated protocol from Pigeon that represents a handler of messages from Flutter. -protocol UserDefaultsApi { +protocol DeprecatedUserDefaultsApi { func remove(key: String) throws func setBool(key: String, value: Bool) throws func setDouble(key: String, value: Double) throws @@ -48,13 +107,14 @@ protocol UserDefaultsApi { } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. -class UserDefaultsApiSetup { - /// The codec used by UserDefaultsApi. - /// Sets up an instance of `UserDefaultsApi` to handle messages through the `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: UserDefaultsApi?) { +class DeprecatedUserDefaultsApiSetup { + /// The codec used by DeprecatedUserDefaultsApi. + static var codec: FlutterStandardMessageCodec { DeprecatedUserDefaultsApiCodec.shared } + /// Sets up an instance of `DeprecatedUserDefaultsApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: DeprecatedUserDefaultsApi?) { let removeChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.remove", - binaryMessenger: binaryMessenger) + name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove", + binaryMessenger: binaryMessenger, codec: codec) if let api = api { removeChannel.setMessageHandler { message, reply in let args = message as! [Any?] @@ -70,8 +130,8 @@ class UserDefaultsApiSetup { removeChannel.setMessageHandler(nil) } let setBoolChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setBool", - binaryMessenger: binaryMessenger) + name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool", + binaryMessenger: binaryMessenger, codec: codec) if let api = api { setBoolChannel.setMessageHandler { message, reply in let args = message as! [Any?] @@ -88,8 +148,8 @@ class UserDefaultsApiSetup { setBoolChannel.setMessageHandler(nil) } let setDoubleChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setDouble", - binaryMessenger: binaryMessenger) + name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble", + binaryMessenger: binaryMessenger, codec: codec) if let api = api { setDoubleChannel.setMessageHandler { message, reply in let args = message as! [Any?] @@ -106,8 +166,8 @@ class UserDefaultsApiSetup { setDoubleChannel.setMessageHandler(nil) } let setValueChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setValue", - binaryMessenger: binaryMessenger) + name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue", + binaryMessenger: binaryMessenger, codec: codec) if let api = api { setValueChannel.setMessageHandler { message, reply in let args = message as! [Any?] @@ -124,8 +184,8 @@ class UserDefaultsApiSetup { setValueChannel.setMessageHandler(nil) } let getAllChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll", - binaryMessenger: binaryMessenger) + name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll", + binaryMessenger: binaryMessenger, codec: codec) if let api = api { getAllChannel.setMessageHandler { message, reply in let args = message as! [Any?] @@ -142,8 +202,8 @@ class UserDefaultsApiSetup { getAllChannel.setMessageHandler(nil) } let clearChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear", - binaryMessenger: binaryMessenger) + name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear", + binaryMessenger: binaryMessenger, codec: codec) if let api = api { clearChannel.setMessageHandler { message, reply in let args = message as! [Any?] @@ -161,3 +221,157 @@ class UserDefaultsApiSetup { } } } +private class UserDefaultsApiCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 128: + return SharedPreferencesPigeonOptions.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class UserDefaultsApiCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? SharedPreferencesPigeonOptions { + super.writeByte(128) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class UserDefaultsApiCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return UserDefaultsApiCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return UserDefaultsApiCodecWriter(data: data) + } +} + +class UserDefaultsApiCodec: FlutterStandardMessageCodec { + static let shared = UserDefaultsApiCodec(readerWriter: UserDefaultsApiCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol UserDefaultsApi { + /// Adds property to shared preferences data set of type String. + func set(key: String, value: Any, options: SharedPreferencesPigeonOptions) throws + /// Removes all properties from shared preferences data set with matching prefix. + func clear(allowList: [String]?, options: SharedPreferencesPigeonOptions) throws + /// Gets all properties from shared preferences data set with matching prefix. + func getAll(allowList: [String]?, options: SharedPreferencesPigeonOptions) throws -> [String: Any] + /// Gets individual value stored with [key], if any. + func getValue(key: String, options: SharedPreferencesPigeonOptions) throws -> Any? + /// Gets all properties from shared preferences data set with matching prefix. + func getKeys(allowList: [String]?, options: SharedPreferencesPigeonOptions) throws -> [String] +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class UserDefaultsApiSetup { + /// The codec used by UserDefaultsApi. + static var codec: FlutterStandardMessageCodec { UserDefaultsApiCodec.shared } + /// Sets up an instance of `UserDefaultsApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: UserDefaultsApi?) { + /// Adds property to shared preferences data set of type String. + let setChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.set", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let keyArg = args[0] as! String + let valueArg = args[1]! + let optionsArg = args[2] as! SharedPreferencesPigeonOptions + do { + try api.set(key: keyArg, value: valueArg, options: optionsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setChannel.setMessageHandler(nil) + } + /// Removes all properties from shared preferences data set with matching prefix. + let clearChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + clearChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let allowListArg: [String]? = nilOrValue(args[0]) + let optionsArg = args[1] as! SharedPreferencesPigeonOptions + do { + try api.clear(allowList: allowListArg, options: optionsArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + clearChannel.setMessageHandler(nil) + } + /// Gets all properties from shared preferences data set with matching prefix. + let getAllChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getAllChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let allowListArg: [String]? = nilOrValue(args[0]) + let optionsArg = args[1] as! SharedPreferencesPigeonOptions + do { + let result = try api.getAll(allowList: allowListArg, options: optionsArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getAllChannel.setMessageHandler(nil) + } + /// Gets individual value stored with [key], if any. + let getValueChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getValue", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getValueChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let keyArg = args[0] as! String + let optionsArg = args[1] as! SharedPreferencesPigeonOptions + do { + let result = try api.getValue(key: keyArg, options: optionsArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getValueChannel.setMessageHandler(nil) + } + /// Gets all properties from shared preferences data set with matching prefix. + let getKeysChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getKeys", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getKeysChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let allowListArg: [String]? = nilOrValue(args[0]) + let optionsArg = args[1] as! SharedPreferencesPigeonOptions + do { + let result = try api.getKeys(allowList: allowListArg, options: optionsArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getKeysChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart index 49892ec18aa..3bc0f156607 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart @@ -4,6 +4,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:shared_preferences_foundation/shared_preferences_foundation.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; @@ -481,4 +483,199 @@ void main() { expect(values['Int'], writeCount); }); }); + + group('shared_preferences_async', () { + final SharedPreferencesFoundationOptions emptyOptions = + SharedPreferencesFoundationOptions(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + Future getPreferences() async { + final SharedPreferencesAsyncPlatform preferences = + SharedPreferencesAsyncPlatform.instance!; + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + return preferences; + } + + testWidgets('set and get String', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); + }); + + testWidgets('set and get bool', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); + }); + + testWidgets('set and get int', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + }); + + testWidgets('set and get double', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + testWidgets('set and get StringList', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + testWidgets('getPreferences', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); + }); + + testWidgets('getPreferences with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + testWidgets('getKeys', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + testWidgets('getKeys with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + }); + + testWidgets('clear', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + testWidgets('clear with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + }); } diff --git a/packages/shared_preferences/shared_preferences_foundation/example/lib/main.dart b/packages/shared_preferences/shared_preferences_foundation/example/lib/main.dart index e7cb57b7eb6..c58b785d551 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_foundation/example/lib/main.dart @@ -4,10 +4,9 @@ // ignore_for_file: public_member_api_docs -import 'dart:async'; - import 'package:flutter/material.dart'; -import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_foundation/shared_preferences_foundation.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; void main() { runApp(const MyApp()); @@ -33,32 +32,36 @@ class SharedPreferencesDemo extends StatefulWidget { } class SharedPreferencesDemoState extends State { - final SharedPreferencesStorePlatform _prefs = - SharedPreferencesStorePlatform.instance; + final SharedPreferencesAsyncPlatform? _prefs = + SharedPreferencesAsyncPlatform.instance; + SharedPreferencesFoundationOptions options = + SharedPreferencesFoundationOptions(); + static const String _counterKey = 'counter'; late Future _counter; - // Includes the prefix because this is using the platform interface directly, - // but the prefix (which the native code assumes is present) is added by the - // app-facing package. - static const String _prefKey = 'flutter.counter'; - Future _incrementCounter() async { - final Map values = await _prefs.getAll(); - final int counter = ((values[_prefKey] as int?) ?? 0) + 1; + final int? value = await _prefs!.getInt(_counterKey, options); + final int counter = (value ?? 0) + 1; setState(() { - _counter = _prefs.setValue('Int', _prefKey, counter).then((bool success) { + _counter = _prefs.setInt(_counterKey, counter, options).then((_) { return counter; }); }); } + Future _getAndSetCounter() async { + setState(() { + _counter = _prefs!.getInt(_counterKey, options).then((int? counter) { + return counter ?? 0; + }); + }); + } + @override void initState() { super.initState(); - _counter = _prefs.getAll().then((Map values) { - return (values[_prefKey] as int?) ?? 0; - }); + _getAndSetCounter(); } @override diff --git a/packages/shared_preferences/shared_preferences_foundation/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_foundation/example/pubspec.yaml index 39df1eb0f46..dfee764e631 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_foundation/example/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/legacy_shared_preferences_foundation.dart b/packages/shared_preferences/shared_preferences_foundation/lib/legacy_shared_preferences_foundation.dart new file mode 100644 index 00000000000..d8e0cb277bc --- /dev/null +++ b/packages/shared_preferences/shared_preferences_foundation/lib/legacy_shared_preferences_foundation.dart @@ -0,0 +1,108 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; +import 'messages.g.dart'; + +typedef _Setter = Future Function(String key, Object value); + +/// iOS and macOS implementation of shared_preferences. +class LegacySharedPreferencesFoundation extends SharedPreferencesStorePlatform { + final DeprecatedUserDefaultsApi _api = DeprecatedUserDefaultsApi(); + + static const String _defaultPrefix = 'flutter.'; + + late final Map _setters = { + 'Bool': (String key, Object value) { + return _api.setBool(key, value as bool); + }, + 'Double': (String key, Object value) { + return _api.setDouble(key, value as double); + }, + 'Int': (String key, Object value) { + return _api.setValue(key, value as int); + }, + 'String': (String key, Object value) { + return _api.setValue(key, value as String); + }, + 'StringList': (String key, Object value) { + return _api.setValue(key, value as List); + }, + }; + + /// Registers this class as the default instance of + /// [SharedPreferencesStorePlatform]. + static void registerWith() { + SharedPreferencesStorePlatform.instance = + LegacySharedPreferencesFoundation(); + } + + @override + Future clear() async { + return clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); + } + + @override + Future clearWithPrefix(String prefix) async { + return clearWithParameters( + ClearParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future clearWithParameters(ClearParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + return _api.clear( + filter.prefix, + filter.allowList?.toList(), + ); + } + + @override + Future> getAll() async { + return getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); + } + + @override + Future> getAllWithPrefix(String prefix) async { + return getAllWithParameters( + GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future> getAllWithParameters( + GetAllParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + final Map data = + await _api.getAll(filter.prefix, filter.allowList?.toList()); + return data.cast(); + } + + @override + Future remove(String key) async { + await _api.remove(key); + return true; + } + + @override + Future setValue(String valueType, String key, Object value) async { + final _Setter? setter = _setters[valueType]; + if (setter == null) { + throw PlatformException( + code: 'InvalidOperation', + message: '"$valueType" is not a supported type.'); + } + await setter(key, value); + return true; + } +} diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart b/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart index 132d92ac224..d0f34b2a503 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart @@ -1,9 +1,9 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v10.1.6), do not edit directly. +// Autogenerated from Pigeon (v16.0.5), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -11,162 +11,408 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; -class UserDefaultsApi { - /// Constructor for [UserDefaultsApi]. The [binaryMessenger] named argument is +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} + +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + +class SharedPreferencesPigeonOptions { + SharedPreferencesPigeonOptions({ + this.suiteName, + }); + + String? suiteName; + + Object encode() { + return [ + suiteName, + ]; + } + + static SharedPreferencesPigeonOptions decode(Object result) { + result as List; + return SharedPreferencesPigeonOptions( + suiteName: result[0] as String?, + ); + } +} + +class _DeprecatedUserDefaultsApiCodec extends StandardMessageCodec { + const _DeprecatedUserDefaultsApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is SharedPreferencesPigeonOptions) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return SharedPreferencesPigeonOptions.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class DeprecatedUserDefaultsApi { + /// Constructor for [DeprecatedUserDefaultsApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - UserDefaultsApi({BinaryMessenger? binaryMessenger}) - : _binaryMessenger = binaryMessenger; - final BinaryMessenger? _binaryMessenger; - - static const MessageCodec codec = StandardMessageCodec(); - - Future remove(String arg_key) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.remove', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key]) as List?; - if (replyList == null) { + DeprecatedUserDefaultsApi({BinaryMessenger? binaryMessenger}) + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = + _DeprecatedUserDefaultsApiCodec(); + + Future remove(String key) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { + } else { + return; + } + } + + Future setBool(String key, bool value) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, value]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future setBool(String arg_key, bool arg_value) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setBool', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key, arg_value]) as List?; - if (replyList == null) { + Future setDouble(String key, double value) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, value]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { + } else { + return; + } + } + + Future setValue(String key, Object value) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, value]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future setDouble(String arg_key, double arg_value) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setDouble', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key, arg_value]) as List?; - if (replyList == null) { + Future> getAll( + String prefix, List? allowList) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([prefix, allowList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', ); } else { - return; + return (__pigeon_replyList[0] as Map?)! + .cast(); } } - Future setValue(String arg_key, Object arg_value) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setValue', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_key, arg_value]) as List?; - if (replyList == null) { + Future clear(String prefix, List? allowList) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([prefix, allowList]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as bool?)!; + } + } +} + +class _UserDefaultsApiCodec extends StandardMessageCodec { + const _UserDefaultsApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is SharedPreferencesPigeonOptions) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return SharedPreferencesPigeonOptions.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class UserDefaultsApi { + /// Constructor for [UserDefaultsApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + UserDefaultsApi({BinaryMessenger? binaryMessenger}) + : __pigeon_binaryMessenger = binaryMessenger; + final BinaryMessenger? __pigeon_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = + _UserDefaultsApiCodec(); + + /// Adds property to shared preferences data set of type String. + Future set( + String key, Object value, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.set'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([key, value, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); } else { return; } } - Future> getAll( - String arg_prefix, List? arg_allowList) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_prefix, arg_allowList]) as List?; - if (replyList == null) { + /// Removes all properties from shared preferences data set with matching prefix. + Future clear( + List? allowList, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([allowList, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { + } else { + return; + } + } + + /// Gets all properties from shared preferences data set with matching prefix. + Future> getAll( + List? allowList, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([allowList, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as Map?)!.cast(); + return (__pigeon_replyList[0] as Map?)! + .cast(); } } - Future clear(String arg_prefix, List? arg_allowList) async { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear', - codec, - binaryMessenger: _binaryMessenger); - final List? replyList = await channel - .send([arg_prefix, arg_allowList]) as List?; - if (replyList == null) { + /// Gets individual value stored with [key], if any. + Future getValue( + String key, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getValue'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send([key, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel.', + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList.length > 1) { + } else { + return __pigeon_replyList[0]; + } + } + + /// Gets all properties from shared preferences data set with matching prefix. + Future> getKeys( + List? allowList, SharedPreferencesPigeonOptions options) async { + const String __pigeon_channelName = + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getKeys'; + final BasicMessageChannel __pigeon_channel = + BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = await __pigeon_channel + .send([allowList, options]) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { throw PlatformException( - code: replyList[0]! as String, - message: replyList[1] as String?, - details: replyList[2], + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], ); - } else if (replyList[0] == null) { + } else if (__pigeon_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (replyList[0] as bool?)!; + return (__pigeon_replyList[0] as List?)!.cast(); } } } diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart b/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart index 92699b7fbfa..a3149582712 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart @@ -2,106 +2,236 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; -import 'messages.g.dart'; -typedef _Setter = Future Function(String key, Object value); +import './messages.g.dart'; +import 'legacy_shared_preferences_foundation.dart'; + +const String _argumentErrorCode = 'Argument Error'; /// iOS and macOS implementation of shared_preferences. -class SharedPreferencesFoundation extends SharedPreferencesStorePlatform { - final UserDefaultsApi _api = UserDefaultsApi(); - - static const String _defaultPrefix = 'flutter.'; - - late final Map _setters = { - 'Bool': (String key, Object value) { - return _api.setBool(key, value as bool); - }, - 'Double': (String key, Object value) { - return _api.setDouble(key, value as double); - }, - 'Int': (String key, Object value) { - return _api.setValue(key, value as int); - }, - 'String': (String key, Object value) { - return _api.setValue(key, value as String); - }, - 'StringList': (String key, Object value) { - return _api.setValue(key, value as List); - }, - }; - - /// Registers this class as the default instance of - /// [SharedPreferencesStorePlatform]. +base class SharedPreferencesFoundation extends SharedPreferencesAsyncPlatform { + /// Creates a new plugin implementation instance. + SharedPreferencesFoundation({ + @visibleForTesting UserDefaultsApi? api, + }) : _api = api ?? UserDefaultsApi(); + + final UserDefaultsApi _api; + + /// Registers this class as the default instance of [SharedPreferencesAsyncPlatform]. static void registerWith() { - SharedPreferencesStorePlatform.instance = SharedPreferencesFoundation(); + SharedPreferencesAsyncPlatform.instance = SharedPreferencesFoundation(); + // A temporary work-around for having two plugins contained in a single package. + LegacySharedPreferencesFoundation.registerWith(); + } + + /// Returns a SharedPreferencesPigeonOptions for sending to platform. + SharedPreferencesPigeonOptions _convertOptionsToPigeonOptions( + SharedPreferencesOptions options) { + if (options is SharedPreferencesFoundationOptions) { + final String? suiteName = options.suiteName; + return SharedPreferencesPigeonOptions( + suiteName: suiteName, + ); + } + return SharedPreferencesPigeonOptions(); + } + + @override + Future> getKeys( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 + // is fixed. In practice, the values will never be null, and the native implementation assumes that. + return (await _convertKnownExceptions>( + () async => (await _api.getKeys( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + )) + .cast()))! + .toSet(); + } + + Future _setValue( + String key, + Object value, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => + _api.set(key, value, _convertOptionsToPigeonOptions(options))); + } + + @override + Future setString( + String key, + String value, + SharedPreferencesOptions options, + ) async { + await _setValue(key, value, options); + } + + @override + Future setInt( + String key, + int value, + SharedPreferencesOptions options, + ) async { + await _setValue(key, value, options); } @override - Future clear() async { - return clearWithParameters( - ClearParameters( - filter: PreferencesFilter(prefix: _defaultPrefix), - ), - ); + Future setStringList( + String key, + List value, + SharedPreferencesOptions options, + ) async { + await _setValue(key, value, options); } @override - Future clearWithPrefix(String prefix) async { - return clearWithParameters( - ClearParameters(filter: PreferencesFilter(prefix: prefix))); + Future setBool( + String key, + bool value, + SharedPreferencesOptions options, + ) async { + await _api.set(key, value, _convertOptionsToPigeonOptions(options)); } @override - Future clearWithParameters(ClearParameters parameters) async { - final PreferencesFilter filter = parameters.filter; - return _api.clear( - filter.prefix, - filter.allowList?.toList(), - ); + Future setDouble( + String key, + double value, + SharedPreferencesOptions options, + ) async { + await _api.set(key, value, _convertOptionsToPigeonOptions(options)); } @override - Future> getAll() async { - return getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: _defaultPrefix), - ), - ); + Future getString( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => (await _api.getValue( + key, _convertOptionsToPigeonOptions(options))) as String?); } @override - Future> getAllWithPrefix(String prefix) async { - return getAllWithParameters( - GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + Future getBool( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => await _api.getValue( + key, _convertOptionsToPigeonOptions(options)) as bool?); } @override - Future> getAllWithParameters( - GetAllParameters parameters) async { - final PreferencesFilter filter = parameters.filter; - final Map data = - await _api.getAll(filter.prefix, filter.allowList?.toList()); - return data.cast(); + Future getDouble( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => await _api.getValue( + key, _convertOptionsToPigeonOptions(options)) as double?); } @override - Future remove(String key) async { - await _api.remove(key); - return true; + Future getInt( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => await _api.getValue( + key, _convertOptionsToPigeonOptions(options)) as int?); } @override - Future setValue(String valueType, String key, Object value) async { - final _Setter? setter = _setters[valueType]; - if (setter == null) { - throw PlatformException( - code: 'InvalidOperation', - message: '"$valueType" is not a supported type.'); + Future?> getStringList( + String key, + SharedPreferencesOptions options, + ) async { + // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 + // is fixed. In practice, the values will never be null, and the native implementation assumes that. + return _convertKnownExceptions>(() async => + ((await _api.getValue(key, _convertOptionsToPigeonOptions(options))) + as List?) + ?.cast()); + } + + @override + Future clear( + ClearPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + return _convertKnownExceptions(() async => _api.clear( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + )); + } + + @override + Future> getPreferences( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + final Map? data = + await _convertKnownExceptions>( + () async => _api.getAll( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + )); + + return data!.cast(); + } + + Future _convertKnownExceptions(Future Function() method) async { + try { + final T? value = await method(); + return value; + } on PlatformException catch (e) { + if (e.code == _argumentErrorCode) { + throw ArgumentError( + 'shared_preferences_foundation argument error ${e.message ?? ''}'); + } else { + rethrow; + } + } + } +} + +/// Options for the Foundation specific SharedPreferences plugin. +@immutable +class SharedPreferencesFoundationOptions extends SharedPreferencesOptions { + /// Creates a new instance with the given options. + SharedPreferencesFoundationOptions({ + this.suiteName, + }) { + if (Platform.isIOS && !(suiteName?.startsWith('group.') ?? true)) { + throw ArgumentError('iOS suite name must begin with "group."'); + } + } + + /// Name of Foundation suite to get/set to. + /// + /// On iOS this represents a container ID which must begin with `group.` + /// followed by a custom string in reverse DNS notation. + /// + /// If this option is not set, the default NSUserDefaults will be used. + final String? suiteName; + + /// Returns a new instance of [SharedPreferencesFoundationOptions] from an existing + /// [SharedPreferencesOptions]. + static SharedPreferencesFoundationOptions fromSharedPreferencesOptions( + SharedPreferencesOptions options) { + if (options is SharedPreferencesFoundationOptions) { + return options; } - await setter(key, value); - return true; + return SharedPreferencesFoundationOptions(); } } diff --git a/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart b/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart index ad8060e3474..464e6a4599d 100644 --- a/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart +++ b/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart @@ -12,10 +12,8 @@ import 'package:pigeon/pigeon.dart'; copyrightHeader: 'pigeons/copyright_header.txt', )) @HostApi(dartHostTestHandler: 'TestUserDefaultsApi') -abstract class UserDefaultsApi { +abstract class DeprecatedUserDefaultsApi { void remove(String key); - // TODO(stuartmorgan): Give these setters better Swift signatures (_,forKey:) - // once https://github.com/flutter/flutter/issues/105932 is fixed. void setBool(String key, bool value); void setDouble(String key, double value); void setValue(String key, Object value); @@ -24,3 +22,45 @@ abstract class UserDefaultsApi { Map getAll(String prefix, List? allowList); bool clear(String prefix, List? allowList); } + +class SharedPreferencesPigeonOptions { + SharedPreferencesPigeonOptions({ + this.suiteName, + }); + String? suiteName; +} + +@HostApi(dartHostTestHandler: 'TestSharedPreferencesAsyncApi') +abstract class UserDefaultsApi { + /// Adds property to shared preferences data set of type String. + @SwiftFunction('set(key:value:options:)') + void set( + String key, + Object value, + SharedPreferencesPigeonOptions options, + ); + + /// Removes all properties from shared preferences data set with matching prefix. + void clear( + List? allowList, + SharedPreferencesPigeonOptions options, + ); + + /// Gets all properties from shared preferences data set with matching prefix. + Map getAll( + List? allowList, + SharedPreferencesPigeonOptions options, + ); + + /// Gets individual value stored with [key], if any. + Object? getValue( + String key, + SharedPreferencesPigeonOptions options, + ); + + /// Gets all properties from shared preferences data set with matching prefix. + List getKeys( + List? allowList, + SharedPreferencesPigeonOptions options, + ); +} diff --git a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml index 2e5d728e142..53dfa7294e2 100644 --- a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_foundation description: iOS and macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_foundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.4.0 +version: 2.5.0 environment: sdk: ^3.2.3 @@ -24,12 +24,12 @@ flutter: dependencies: flutter: sdk: flutter - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 dev_dependencies: flutter_test: sdk: flutter - pigeon: ^10.1.6 + pigeon: ^16.0.4 topics: - persistence diff --git a/packages/shared_preferences/shared_preferences_foundation/test/legacy_shared_preferences_foundation_test.dart b/packages/shared_preferences/shared_preferences_foundation/test/legacy_shared_preferences_foundation_test.dart new file mode 100644 index 00000000000..6c0a4ef2782 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_foundation/test/legacy_shared_preferences_foundation_test.dart @@ -0,0 +1,347 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences_foundation/legacy_shared_preferences_foundation.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; + +import 'test_api.g.dart'; + +class _MockSharedPreferencesApi implements TestUserDefaultsApi { + final Map items = {}; + + @override + Map getAll( + String prefix, + List? allowList, + ) { + Set? allowSet; + if (allowList != null) { + allowSet = Set.from(allowList); + } + return { + for (final String key in items.keys) + if (key.startsWith(prefix) && + (allowSet == null || allowSet.contains(key))) + key: items[key] + }; + } + + @override + void remove(String key) { + items.remove(key); + } + + @override + void setBool(String key, bool value) { + items[key] = value; + } + + @override + void setDouble(String key, double value) { + items[key] = value; + } + + @override + void setValue(String key, Object value) { + items[key] = value; + } + + @override + bool clear(String prefix, List? allowList) { + items.keys.toList().forEach((String key) { + if (key.startsWith(prefix) && + (allowList == null || allowList.contains(key))) { + items.remove(key); + } + }); + return true; + } +} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + late _MockSharedPreferencesApi api; + + const Map flutterTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + + const Map prefixTestValues = { + 'prefix.String': 'hello world', + 'prefix.Bool': true, + 'prefix.Int': 42, + 'prefix.Double': 3.14159, + 'prefix.StringList': ['foo', 'bar'], + }; + + const Map nonPrefixTestValues = { + 'String': 'hello world', + 'Bool': true, + 'Int': 42, + 'Double': 3.14159, + 'StringList': ['foo', 'bar'], + }; + + final Map allTestValues = {}; + + allTestValues.addAll(flutterTestValues); + allTestValues.addAll(prefixTestValues); + allTestValues.addAll(nonPrefixTestValues); + + setUp(() { + api = _MockSharedPreferencesApi(); + TestUserDefaultsApi.setup(api); + }); + + test('registerWith', () async { + LegacySharedPreferencesFoundation.registerWith(); + expect(SharedPreferencesStorePlatform.instance, + isA()); + }); + + test('remove', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + api.items['flutter.hi'] = 'world'; + expect(await plugin.remove('flutter.hi'), isTrue); + expect(api.items.containsKey('flutter.hi'), isFalse); + }); + + test('clear', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + api.items['flutter.hi'] = 'world'; + expect(await plugin.clear(), isTrue); + expect(api.items.containsKey('flutter.hi'), isFalse); + }); + + test('clearWithPrefix', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithPrefix('prefix.'); + expect(all.length, 5); + await plugin.clearWithPrefix('prefix.'); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithPrefix('prefix.'); + expect(all.length, 0); + }); + + test('clearWithParameters', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 0); + }); + + test('clearWithParameters with allow list', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 4); + }); + + test('getAll', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in flutterTestValues.keys) { + api.items[key] = flutterTestValues[key]!; + } + final Map all = await plugin.getAll(); + expect(all.length, 5); + expect(all, flutterTestValues); + }); + + test('getAllWithPrefix', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithPrefix('prefix.'); + expect(all.length, 5); + expect(all, prefixTestValues); + }); + + test('getAllWithParameters', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + expect(all, prefixTestValues); + }); + + test('getAllWithParameters with allow list', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.Bool'}, + ), + ), + ); + expect(all.length, 1); + expect(all['prefix.Bool'], prefixTestValues['prefix.Bool']); + }); + + test('setValue', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + expect(await plugin.setValue('Bool', 'flutter.Bool', true), isTrue); + expect(api.items['flutter.Bool'], true); + expect(await plugin.setValue('Double', 'flutter.Double', 1.5), isTrue); + expect(api.items['flutter.Double'], 1.5); + expect(await plugin.setValue('Int', 'flutter.Int', 12), isTrue); + expect(api.items['flutter.Int'], 12); + expect(await plugin.setValue('String', 'flutter.String', 'hi'), isTrue); + expect(api.items['flutter.String'], 'hi'); + expect( + await plugin + .setValue('StringList', 'flutter.StringList', ['hi']), + isTrue); + expect(api.items['flutter.StringList'], ['hi']); + }); + + test('setValue with unsupported type', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + expect(() async { + await plugin.setValue('Map', 'flutter.key', {}); + }, throwsA(isA())); + }); + + test('getAllWithNoPrefix', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithPrefix(''); + expect(all.length, 15); + expect(all, allTestValues); + }); + + test('clearWithNoPrefix', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithPrefix(''); + expect(all.length, 15); + await plugin.clearWithPrefix(''); + all = await plugin.getAllWithPrefix(''); + expect(all.length, 0); + }); + + test('getAllWithNoPrefix with param', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(all.length, 15); + expect(all, allTestValues); + }); + + test('clearWithNoPrefix with param', () async { + final LegacySharedPreferencesFoundation plugin = + LegacySharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(all.length, 15); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(all.length, 0); + }); +} diff --git a/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart index 496cdd9b0f7..a82a3ad27fc 100644 --- a/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart @@ -2,331 +2,258 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences_foundation/messages.g.dart'; import 'package:shared_preferences_foundation/shared_preferences_foundation.dart'; -import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; -import 'test_api.g.dart'; +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); -class _MockSharedPreferencesApi implements TestUserDefaultsApi { - final Map items = {}; + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; - @override - Map getAll( - String prefix, - List? allowList, - ) { - Set? allowSet; - if (allowList != null) { - allowSet = Set.from(allowList); - } - return { - for (final String key in items.keys) - if (key.startsWith(prefix) && - (allowSet == null || allowSet.contains(key))) - key: items[key] - }; - } + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; - @override - void remove(String key) { - items.remove(key); - } + final SharedPreferencesFoundationOptions emptyOptions = + SharedPreferencesFoundationOptions(); - @override - void setBool(String key, bool value) { - items[key] = value; - } + SharedPreferencesFoundation getPreferences() { + final _FakeSharedPreferencesApi api = _FakeSharedPreferencesApi(); + final SharedPreferencesFoundation preferences = + SharedPreferencesFoundation(api: api); - @override - void setDouble(String key, double value) { - items[key] = value; + return preferences; } - @override - void setValue(String key, Object value) { - items[key] = value; - } + test('set and get String', () async { + final SharedPreferencesFoundation preferences = getPreferences(); - @override - bool clear(String prefix, List? allowList) { - items.keys.toList().forEach((String key) { - if (key.startsWith(prefix) && - (allowList == null || allowList.contains(key))) { - items.remove(key); - } - }); - return true; - } -} - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - late _MockSharedPreferencesApi api; - - const Map flutterTestValues = { - 'flutter.String': 'hello world', - 'flutter.Bool': true, - 'flutter.Int': 42, - 'flutter.Double': 3.14159, - 'flutter.StringList': ['foo', 'bar'], - }; - - const Map prefixTestValues = { - 'prefix.String': 'hello world', - 'prefix.Bool': true, - 'prefix.Int': 42, - 'prefix.Double': 3.14159, - 'prefix.StringList': ['foo', 'bar'], - }; - - const Map nonPrefixTestValues = { - 'String': 'hello world', - 'Bool': true, - 'Int': 42, - 'Double': 3.14159, - 'StringList': ['foo', 'bar'], - }; - - final Map allTestValues = {}; - - allTestValues.addAll(flutterTestValues); - allTestValues.addAll(prefixTestValues); - allTestValues.addAll(nonPrefixTestValues); - - setUp(() { - api = _MockSharedPreferencesApi(); - TestUserDefaultsApi.setup(api); - }); - - test('registerWith', () { - SharedPreferencesFoundation.registerWith(); - expect(SharedPreferencesStorePlatform.instance, - isA()); + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); }); - test('remove', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - api.items['flutter.hi'] = 'world'; - expect(await plugin.remove('flutter.hi'), isTrue); - expect(api.items.containsKey('flutter.hi'), isFalse); - }); + test('set and get bool', () async { + final SharedPreferencesFoundation preferences = getPreferences(); - test('clear', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - api.items['flutter.hi'] = 'world'; - expect(await plugin.clear(), isTrue); - expect(api.items.containsKey('flutter.hi'), isFalse); + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); }); - test('clearWithPrefix', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } + test('set and get int', () async { + final SharedPreferencesFoundation preferences = getPreferences(); - Map all = await plugin.getAllWithPrefix('prefix.'); - expect(all.length, 5); - await plugin.clearWithPrefix('prefix.'); - all = await plugin.getAll(); - expect(all.length, 5); - all = await plugin.getAllWithPrefix('prefix.'); - expect(all.length, 0); + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); }); - test('clearWithParameters', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } + test('set and get double', () async { + final SharedPreferencesFoundation preferences = getPreferences(); - Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(all.length, 5); - await plugin.clearWithParameters( - ClearParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - all = await plugin.getAll(); - expect(all.length, 5); - all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(all.length, 0); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); }); - test('clearWithParameters with allow list', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } + test('set and get StringList', () async { + final SharedPreferencesFoundation preferences = getPreferences(); - Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(all.length, 5); - await plugin.clearWithParameters( - ClearParameters( - filter: PreferencesFilter( - prefix: 'prefix.', - allowList: {'prefix.String'}, - ), - ), - ); - all = await plugin.getAll(); - expect(all.length, 5); - all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(all.length, 4); + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); }); - test('getAll', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in flutterTestValues.keys) { - api.items[key] = flutterTestValues[key]!; - } - final Map all = await plugin.getAll(); - expect(all.length, 5); - expect(all, flutterTestValues); + test('getPreferences', () async { + final SharedPreferencesFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); }); - test('getAllWithPrefix', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - final Map all = await plugin.getAllWithPrefix('prefix.'); - expect(all.length, 5); - expect(all, prefixTestValues); + test('getPreferences with filter', () async { + final SharedPreferencesFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: + PreferencesFilters(allowList: {stringKey, boolKey})), + emptyOptions); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); }); - test('getAllWithParameters', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - final Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), + test('getKeys', () async { + final SharedPreferencesFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, ); - expect(all.length, 5); - expect(all, prefixTestValues); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); }); - test('getAllWithParameters with allow list', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - final Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter( - prefix: 'prefix.', - allowList: {'prefix.Bool'}, - ), + test('getKeys with filter', () async { + final SharedPreferencesFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), ), + emptyOptions, ); - expect(all.length, 1); - expect(all['prefix.Bool'], prefixTestValues['prefix.Bool']); - }); - test('setValue', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - expect(await plugin.setValue('Bool', 'flutter.Bool', true), isTrue); - expect(api.items['flutter.Bool'], true); - expect(await plugin.setValue('Double', 'flutter.Double', 1.5), isTrue); - expect(api.items['flutter.Double'], 1.5); - expect(await plugin.setValue('Int', 'flutter.Int', 12), isTrue); - expect(api.items['flutter.Int'], 12); - expect(await plugin.setValue('String', 'flutter.String', 'hi'), isTrue); - expect(api.items['flutter.String'], 'hi'); - expect( - await plugin - .setValue('StringList', 'flutter.StringList', ['hi']), - isTrue); - expect(api.items['flutter.StringList'], ['hi']); + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); }); - test('setValue with unsupported type', () { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - expect(() async { - await plugin.setValue('Map', 'flutter.key', {}); - }, throwsA(isA())); + test('clear', () async { + final SharedPreferencesFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); }); - test('getAllWithNoPrefix', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - final Map all = await plugin.getAllWithPrefix(''); - expect(all.length, 15); - expect(all, allTestValues); + test('clear with filter', () async { + final SharedPreferencesFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); }); +} + +class _FakeSharedPreferencesApi implements UserDefaultsApi { + final Map items = {}; - test('clearWithNoPrefix', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; + @override + Future clear( + List? allowList, SharedPreferencesPigeonOptions options) async { + if (allowList != null) { + items.removeWhere((String key, _) => allowList.contains(key)); + } else { + items.clear(); } - Map all = await plugin.getAllWithPrefix(''); - expect(all.length, 15); - await plugin.clearWithPrefix(''); - all = await plugin.getAllWithPrefix(''); - expect(all.length, 0); - }); + return true; + } - test('getAllWithNoPrefix with param', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; + @override + Future> getAll( + List? allowList, SharedPreferencesPigeonOptions options) async { + final Map filteredItems = {...items}; + if (allowList != null) { + filteredItems.removeWhere((String key, _) => !allowList.contains(key)); } - final Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - expect(all.length, 15); - expect(all, allTestValues); - }); + return filteredItems; + } - test('clearWithNoPrefix with param', () async { - final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; + @override + Future> getKeys( + List? allowList, SharedPreferencesPigeonOptions options) async { + final List filteredItems = items.keys.toList(); + if (allowList != null) { + filteredItems.removeWhere((String key) => !allowList.contains(key)); } + return filteredItems; + } - Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - expect(all.length, 15); - await plugin.clearWithParameters( - ClearParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - expect(all.length, 0); - }); + @override + Future getString( + String key, SharedPreferencesPigeonOptions options) async { + return items[key] as String?; + } + + @override + Future?> getStringList( + String key, SharedPreferencesPigeonOptions options) async { + return items[key] as List?; + } + + @override + Future set( + String key, Object value, SharedPreferencesPigeonOptions options) async { + items[key] = value; + } + + @override + Future getValue( + String key, SharedPreferencesPigeonOptions options) async { + return items[key]; + } } diff --git a/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart b/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart index 39f8b4fede5..5d995154f5b 100644 --- a/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart +++ b/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart @@ -1,9 +1,9 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v10.1.6), do not edit directly. +// Autogenerated from Pigeon (v16.0.5), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import, no_leading_underscores_for_local_identifiers // ignore_for_file: avoid_relative_lib_imports import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -13,10 +13,34 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences_foundation/messages.g.dart'; +class _TestUserDefaultsApiCodec extends StandardMessageCodec { + const _TestUserDefaultsApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is SharedPreferencesPigeonOptions) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return SharedPreferencesPigeonOptions.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + abstract class TestUserDefaultsApi { static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance; - static const MessageCodec codec = StandardMessageCodec(); + static const MessageCodec pigeonChannelCodec = + _TestUserDefaultsApiCodec(); void remove(String key); @@ -33,154 +57,430 @@ abstract class TestUserDefaultsApi { static void setup(TestUserDefaultsApi? api, {BinaryMessenger? binaryMessenger}) { { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.remove', - codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.remove was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove was null.'); final List args = (message as List?)!; final String? arg_key = (args[0] as String?); assert(arg_key != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.remove was null, expected non-null String.'); - api.remove(arg_key!); - return []; + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove was null, expected non-null String.'); + try { + api.remove(arg_key!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setBool', - codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setBool was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool was null.'); final List args = (message as List?)!; final String? arg_key = (args[0] as String?); assert(arg_key != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setBool was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool was null, expected non-null String.'); final bool? arg_value = (args[1] as bool?); assert(arg_value != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setBool was null, expected non-null bool.'); - api.setBool(arg_key!, arg_value!); - return []; + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool was null, expected non-null bool.'); + try { + api.setBool(arg_key!, arg_value!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setDouble', - codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setDouble was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble was null.'); final List args = (message as List?)!; final String? arg_key = (args[0] as String?); assert(arg_key != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setDouble was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble was null, expected non-null String.'); final double? arg_value = (args[1] as double?); assert(arg_value != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setDouble was null, expected non-null double.'); - api.setDouble(arg_key!, arg_value!); - return []; + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble was null, expected non-null double.'); + try { + api.setDouble(arg_key!, arg_value!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setValue', - codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setValue was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue was null.'); final List args = (message as List?)!; final String? arg_key = (args[0] as String?); assert(arg_key != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setValue was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue was null, expected non-null String.'); final Object? arg_value = (args[1] as Object?); assert(arg_value != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.setValue was null, expected non-null Object.'); - api.setValue(arg_key!, arg_value!); - return []; + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue was null, expected non-null Object.'); + try { + api.setValue(arg_key!, arg_value!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll', - codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll was null.'); final List args = (message as List?)!; final String? arg_prefix = (args[0] as String?); assert(arg_prefix != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll was null, expected non-null String.'); final List? arg_allowList = (args[1] as List?)?.cast(); - final Map output = - api.getAll(arg_prefix!, arg_allowList); - return [output]; + try { + final Map output = + api.getAll(arg_prefix!, arg_allowList); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear', - codec, + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, null); + .setMockDecodedMessageHandler(__pigeon_channel, null); } else { _testBinaryMessengerBinding!.defaultBinaryMessenger - .setMockDecodedMessageHandler(channel, + .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear was null.'); final List args = (message as List?)!; final String? arg_prefix = (args[0] as String?); assert(arg_prefix != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear was null, expected non-null String.'); final List? arg_allowList = (args[1] as List?)?.cast(); - final bool output = api.clear(arg_prefix!, arg_allowList); - return [output]; + try { + final bool output = api.clear(arg_prefix!, arg_allowList); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} + +class _TestSharedPreferencesAsyncApiCodec extends StandardMessageCodec { + const _TestSharedPreferencesAsyncApiCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is SharedPreferencesPigeonOptions) { + buffer.putUint8(128); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return SharedPreferencesPigeonOptions.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +abstract class TestSharedPreferencesAsyncApi { + static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => + TestDefaultBinaryMessengerBinding.instance; + static const MessageCodec pigeonChannelCodec = + _TestSharedPreferencesAsyncApiCodec(); + + /// Adds property to shared preferences data set of type String. + void set(String key, Object value, SharedPreferencesPigeonOptions options); + + /// Removes all properties from shared preferences data set with matching prefix. + void clear(List? allowList, SharedPreferencesPigeonOptions options); + + /// Gets all properties from shared preferences data set with matching prefix. + Map getAll( + List? allowList, SharedPreferencesPigeonOptions options); + + /// Gets individual value stored with [key], if any. + Object? getValue(String key, SharedPreferencesPigeonOptions options); + + /// Gets all properties from shared preferences data set with matching prefix. + List getKeys( + List? allowList, SharedPreferencesPigeonOptions options); + + static void setup(TestSharedPreferencesAsyncApi? api, + {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.set', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.set was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.set was null, expected non-null String.'); + final Object? arg_value = (args[1] as Object?); + assert(arg_value != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.set was null, expected non-null Object.'); + final SharedPreferencesPigeonOptions? arg_options = + (args[2] as SharedPreferencesPigeonOptions?); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.set was null, expected non-null SharedPreferencesPigeonOptions.'); + try { + api.set(arg_key!, arg_value!, arg_options!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear was null.'); + final List args = (message as List?)!; + final List? arg_allowList = + (args[0] as List?)?.cast(); + final SharedPreferencesPigeonOptions? arg_options = + (args[1] as SharedPreferencesPigeonOptions?); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.clear was null, expected non-null SharedPreferencesPigeonOptions.'); + try { + api.clear(arg_allowList, arg_options!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll was null.'); + final List args = (message as List?)!; + final List? arg_allowList = + (args[0] as List?)?.cast(); + final SharedPreferencesPigeonOptions? arg_options = + (args[1] as SharedPreferencesPigeonOptions?); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getAll was null, expected non-null SharedPreferencesPigeonOptions.'); + try { + final Map output = + api.getAll(arg_allowList, arg_options!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getValue', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getValue was null.'); + final List args = (message as List?)!; + final String? arg_key = (args[0] as String?); + assert(arg_key != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getValue was null, expected non-null String.'); + final SharedPreferencesPigeonOptions? arg_options = + (args[1] as SharedPreferencesPigeonOptions?); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getValue was null, expected non-null SharedPreferencesPigeonOptions.'); + try { + final Object? output = api.getValue(arg_key!, arg_options!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getKeys', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(__pigeon_channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getKeys was null.'); + final List args = (message as List?)!; + final List? arg_allowList = + (args[0] as List?)?.cast(); + final SharedPreferencesPigeonOptions? arg_options = + (args[1] as SharedPreferencesPigeonOptions?); + assert(arg_options != null, + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.UserDefaultsApi.getKeys was null, expected non-null SharedPreferencesPigeonOptions.'); + try { + final List output = + api.getKeys(arg_allowList, arg_options!); + return [output]; + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } }); } } diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index cabbb80908e..0c831984b90 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.4.0 +* Adds `SharedPreferencesAsyncLinux` API. * Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. ## 2.3.2 diff --git a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart index 5f06c87c317..370839f0e9c 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; void main() { @@ -336,4 +337,199 @@ void main() { }); }); }); + + group('shared_preferences_async', () { + const SharedPreferencesLinuxOptions emptyOptions = + SharedPreferencesLinuxOptions(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + Future getPreferences() async { + final SharedPreferencesAsyncPlatform preferences = + SharedPreferencesAsyncPlatform.instance!; + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + return preferences; + } + + testWidgets('set and get String', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); + }); + + testWidgets('set and get bool', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); + }); + + testWidgets('set and get int', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + }); + + testWidgets('set and get double', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + testWidgets('set and get StringList', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + testWidgets('getPreferences', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); + }); + + testWidgets('getPreferences with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + testWidgets('getKeys', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + testWidgets('getKeys with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + }); + + testWidgets('clear', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + testWidgets('clear with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + }); } diff --git a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart index 050d2e50f64..b333716cb15 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart @@ -4,10 +4,9 @@ // ignore_for_file: public_member_api_docs -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; void main() { runApp(const MyApp()); @@ -33,26 +32,36 @@ class SharedPreferencesDemo extends StatefulWidget { } class SharedPreferencesDemoState extends State { - final SharedPreferencesLinux prefs = SharedPreferencesLinux(); + final SharedPreferencesAsyncPlatform? _prefs = + SharedPreferencesAsyncPlatform.instance; + final SharedPreferencesLinuxOptions options = + const SharedPreferencesLinuxOptions(); + static const String _counterKey = 'counter'; late Future _counter; Future _incrementCounter() async { - final Map values = await prefs.getAll(); - final int counter = (values['counter'] as int? ?? 0) + 1; + final int? value = await _prefs!.getInt(_counterKey, options); + final int counter = (value ?? 0) + 1; setState(() { - _counter = prefs.setValue('Int', 'counter', counter).then((bool success) { + _counter = _prefs.setInt(_counterKey, counter, options).then((_) { return counter; }); }); } + Future _getAndSetCounter() async { + setState(() { + _counter = _prefs!.getInt(_counterKey, options).then((int? counter) { + return counter ?? 0; + }); + }); + } + @override void initState() { super.initState(); - _counter = prefs.getAll().then((Map values) { - return values['counter'] as int? ?? 0; - }); + _getAndSetCounter(); } @override diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml index 060ea7da5f4..f3704c92ac5 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart index e9f93489b6c..4c7e5268ce1 100644 --- a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart +++ b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart @@ -10,9 +10,14 @@ import 'package:file/local.dart'; import 'package:flutter/foundation.dart' show debugPrint, visibleForTesting; import 'package:path/path.dart' as path; import 'package:path_provider_linux/path_provider_linux.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; +const String _defaultFileName = 'shared_preferences'; + +const String _defaultPrefix = 'flutter.'; + /// The Linux implementation of [SharedPreferencesStorePlatform]. /// /// This class implements the `package:shared_preferences` functionality for Linux. @@ -22,11 +27,11 @@ class SharedPreferencesLinux extends SharedPreferencesStorePlatform { @Deprecated('Use `SharedPreferencesStorePlatform.instance` instead.') static SharedPreferencesLinux instance = SharedPreferencesLinux(); - static const String _defaultPrefix = 'flutter.'; - /// Registers the Linux implementation. static void registerWith() { SharedPreferencesStorePlatform.instance = SharedPreferencesLinux(); + // A temporary work-around for having two plugins contained in a single package. + SharedPreferencesAsyncLinux.registerWith(); } /// Local copy of preferences @@ -40,57 +45,15 @@ class SharedPreferencesLinux extends SharedPreferencesStorePlatform { @visibleForTesting PathProviderLinux pathProvider = PathProviderLinux(); - /// Gets the file where the preferences are stored. - Future _getLocalDataFile() async { - final String? directory = await pathProvider.getApplicationSupportPath(); - if (directory == null) { - return null; - } - return fs.file(path.join(directory, 'shared_preferences.json')); - } - - /// Gets the preferences from the stored file and saves them in cache. - Future> _reload() async { - Map preferences = {}; - final File? localDataFile = await _getLocalDataFile(); - if (localDataFile != null && localDataFile.existsSync()) { - final String stringMap = localDataFile.readAsStringSync(); - if (stringMap.isNotEmpty) { - final Object? data = json.decode(stringMap); - if (data is Map) { - preferences = data.cast(); - } - } - } - _cachedPreferences = preferences; - return preferences; - } - /// Checks for cached preferences and returns them or loads preferences from /// file and returns and caches them. Future> _readPreferences() async { - return _cachedPreferences ?? await _reload(); - } - - /// Writes the cached preferences to disk. Returns [true] if the operation - /// succeeded. - Future _writePreferences(Map preferences) async { - try { - final File? localDataFile = await _getLocalDataFile(); - if (localDataFile == null) { - debugPrint('Unable to determine where to write preferences.'); - return false; - } - if (!localDataFile.existsSync()) { - localDataFile.createSync(recursive: true); - } - final String stringMap = json.encode(preferences); - localDataFile.writeAsStringSync(stringMap); - } catch (e) { - debugPrint('Error saving preferences to disk: $e'); - return false; - } - return true; + _cachedPreferences ??= await _reload( + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); + return _cachedPreferences!; } @override @@ -111,11 +74,17 @@ class SharedPreferencesLinux extends SharedPreferencesStorePlatform { @override Future clearWithParameters(ClearParameters parameters) async { final PreferencesFilter filter = parameters.filter; + final Map preferences = await _readPreferences(); preferences.removeWhere((String key, _) => key.startsWith(filter.prefix) && (filter.allowList == null || filter.allowList!.contains(key))); - return _writePreferences(preferences); + return _writePreferences( + preferences, + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); } @override @@ -148,13 +117,296 @@ class SharedPreferencesLinux extends SharedPreferencesStorePlatform { Future remove(String key) async { final Map preferences = await _readPreferences(); preferences.remove(key); - return _writePreferences(preferences); + return _writePreferences( + preferences, + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); } @override Future setValue(String valueType, String key, Object value) async { final Map preferences = await _readPreferences(); preferences[key] = value; - return _writePreferences(preferences); + return _writePreferences( + preferences, + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); + } +} + +/// The Linux implementation of [SharedPreferencesAsyncPlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for Linux. +base class SharedPreferencesAsyncLinux extends SharedPreferencesAsyncPlatform { + /// Registers the Linux implementation. + static void registerWith() { + SharedPreferencesAsyncPlatform.instance = SharedPreferencesAsyncLinux(); + } + + /// Local copy of preferences + Map? _cachedPreferences; + + /// File system used to store to disk. Exposed for testing only. + @visibleForTesting + FileSystem fs = const LocalFileSystem(); + + /// The path_provider_linux instance used to find the support directory. + @visibleForTesting + PathProviderLinux pathProvider = PathProviderLinux(); + + @override + Future> getKeys( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + return (await getPreferences(parameters, options)).keys.toSet(); + } + + @override + Future setString( + String key, + String value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setBool( + String key, + bool value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setDouble( + String key, + double value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setInt( + String key, + int value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setStringList( + String key, + List value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future getString( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as String?; + } + + @override + Future getBool( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as bool?; + } + + @override + Future getDouble( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as double?; + } + + @override + Future getInt( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as int?; + } + + @override + Future?> getStringList( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return (data[key] as List?)?.toList(); + } + + @override + Future clear(ClearPreferencesParameters parameters, + SharedPreferencesOptions options) async { + final SharedPreferencesLinuxOptions linuxOptions = + SharedPreferencesLinuxOptions.fromSharedPreferencesOptions(options); + final PreferencesFilters filter = parameters.filter; + final Map preferences = + await _readPreferences(linuxOptions.fileName); + preferences.removeWhere((String key, _) => + filter.allowList == null || filter.allowList!.contains(key)); + await _writePreferences( + preferences, + linuxOptions.fileName, + fs: fs, + pathProvider: pathProvider, + ); + } + + @override + Future> getPreferences( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + return _readAll(parameters.filter.allowList, options); + } + + Future> _readAll( + Set? allowList, + SharedPreferencesOptions options, + ) async { + final SharedPreferencesLinuxOptions linuxOptions = + SharedPreferencesLinuxOptions.fromSharedPreferencesOptions(options); + final Map prefs = + Map.from(await _readPreferences(linuxOptions.fileName)); + prefs.removeWhere((String key, _) => !(allowList?.contains(key) ?? true)); + return prefs; + } + + Future _setValue( + String key, Object value, SharedPreferencesOptions options) async { + final SharedPreferencesLinuxOptions linuxOptions = + SharedPreferencesLinuxOptions.fromSharedPreferencesOptions(options); + final Map preferences = + await _readPreferences(linuxOptions.fileName); + preferences[key] = value; + await _writePreferences( + preferences, + linuxOptions.fileName, + fs: fs, + pathProvider: pathProvider, + ); + } + + /// Checks for cached preferences and returns them or loads preferences from + /// file and returns and caches them. + Future> _readPreferences(String fileName) async { + _cachedPreferences ??= await _reload( + fileName, + fs: fs, + pathProvider: pathProvider, + ); + return _cachedPreferences!; + } +} + +/// Gets the file where the preferences are stored. +Future _getLocalDataFile( + String fileName, { + FileSystem fs = const LocalFileSystem(), + PathProviderLinux? pathProvider, +}) async { + pathProvider = pathProvider ?? PathProviderLinux(); + final String? directory = await pathProvider.getApplicationSupportPath(); + if (directory == null) { + return null; + } + final String fileLocation = path.join(directory, '$fileName.json'); + return fs.file(fileLocation); +} + +/// Gets the preferences from the stored file and saves them in cache. +Future> _reload( + String fileName, { + FileSystem fs = const LocalFileSystem(), + PathProviderLinux? pathProvider, +}) async { + Map preferences = {}; + final File? localDataFile = await _getLocalDataFile( + fileName, + fs: fs, + pathProvider: pathProvider, + ); + if (localDataFile != null && localDataFile.existsSync()) { + final String stringMap = localDataFile.readAsStringSync(); + if (stringMap.isNotEmpty) { + final Object? data = json.decode(stringMap); + if (data is Map) { + preferences = data.cast(); + } + } + } + return preferences; +} + +/// Writes the cached preferences to disk. Returns [true] if the operation +/// succeeded. +Future _writePreferences( + Map preferences, + String fileName, { + FileSystem fs = const LocalFileSystem(), + PathProviderLinux? pathProvider, +}) async { + try { + final File? localDataFile = await _getLocalDataFile( + fileName, + fs: fs, + pathProvider: pathProvider, + ); + if (localDataFile == null) { + debugPrint('Unable to determine where to write preferences.'); + return false; + } + if (!localDataFile.existsSync()) { + localDataFile.createSync(recursive: true); + } + final String stringMap = json.encode(preferences); + localDataFile.writeAsStringSync(stringMap); + } catch (e) { + debugPrint('Error saving preferences to disk: $e'); + return false; + } + return true; +} + +/// Linux specific SharedPreferences Options. +class SharedPreferencesLinuxOptions extends SharedPreferencesOptions { + /// Constructor for SharedPreferencesLinuxOptions. + const SharedPreferencesLinuxOptions({ + this.fileName = 'shared_preferences', + }); + + /// The name of the file to store preferences in. + final String fileName; + + /// Returns a new instance of [SharedPreferencesLinuxOptions] from an existing + /// [SharedPreferencesOptions]. + static SharedPreferencesLinuxOptions fromSharedPreferencesOptions( + SharedPreferencesOptions options) { + if (options is SharedPreferencesLinuxOptions) { + return options; + } + return const SharedPreferencesLinuxOptions(); } } diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index fab1ffeafd5..91a49c59840 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.3.2 +version: 2.4.0 environment: sdk: ^3.2.0 @@ -22,7 +22,7 @@ dependencies: path: ^1.8.0 path_provider_linux: ^2.0.0 path_provider_platform_interface: ^2.0.0 - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_linux/test/fake_path_provider_linux.dart b/packages/shared_preferences/shared_preferences_linux/test/fake_path_provider_linux.dart new file mode 100644 index 00000000000..f726abe3856 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/test/fake_path_provider_linux.dart @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_linux/path_provider_linux.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; + +/// Fake implementation of PathProviderLinux that returns hard-coded paths, +/// allowing tests to run on any platform. +/// +/// Note that this should only be used with an in-memory filesystem, as the +/// path it returns is a root path that does not actually exist on Linux. +class FakePathProviderLinux extends PathProviderPlatform + implements PathProviderLinux { + @override + Future getApplicationSupportPath() async => r'/appsupport'; + + @override + Future getTemporaryPath() async => null; + + @override + Future getLibraryPath() async => null; + + @override + Future getApplicationDocumentsPath() async => null; + + @override + Future getDownloadsPath() async => null; +} diff --git a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart b/packages/shared_preferences/shared_preferences_linux/test/legacy_shared_preferences_linux_test.dart similarity index 90% rename from packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart rename to packages/shared_preferences/shared_preferences_linux/test/legacy_shared_preferences_linux_test.dart index 185feb58c41..06faaa5c1e8 100644 --- a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/test/legacy_shared_preferences_linux_test.dart @@ -7,11 +7,12 @@ import 'package:file/memory.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as path; import 'package:path_provider_linux/path_provider_linux.dart'; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; +import 'fake_path_provider_linux.dart'; + void main() { late MemoryFileSystem fs; late PathProviderLinux pathProvider; @@ -75,7 +76,7 @@ void main() { return prefs; } - test('registered instance', () { + test('registered instance', () async { SharedPreferencesLinux.registerWith(); expect( SharedPreferencesStorePlatform.instance, isA()); @@ -254,26 +255,3 @@ void main() { expect(noValues, hasLength(0)); }); } - -/// Fake implementation of PathProviderLinux that returns hard-coded paths, -/// allowing tests to run on any platform. -/// -/// Note that this should only be used with an in-memory filesystem, as the -/// path it returns is a root path that does not actually exist on Linux. -class FakePathProviderLinux extends PathProviderPlatform - implements PathProviderLinux { - @override - Future getApplicationSupportPath() async => r'/appsupport'; - - @override - Future getTemporaryPath() async => null; - - @override - Future getLibraryPath() async => null; - - @override - Future getApplicationDocumentsPath() async => null; - - @override - Future getDownloadsPath() async => null; -} diff --git a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_async_test.dart b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_async_test.dart new file mode 100755 index 00000000000..d49bdc48eea --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_async_test.dart @@ -0,0 +1,203 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/memory.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_linux/path_provider_linux.dart'; +import 'package:shared_preferences_linux/shared_preferences_linux.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; + +import 'fake_path_provider_linux.dart'; + +void main() { + late MemoryFileSystem fs; + late PathProviderLinux pathProvider; + + SharedPreferencesAsyncLinux.registerWith(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + const SharedPreferencesLinuxOptions emptyOptions = + SharedPreferencesLinuxOptions(); + + setUp(() { + fs = MemoryFileSystem.test(); + pathProvider = FakePathProviderLinux(); + }); + + SharedPreferencesAsyncLinux getPreferences() { + final SharedPreferencesAsyncLinux prefs = SharedPreferencesAsyncLinux(); + prefs.fs = fs; + prefs.pathProvider = pathProvider; + return prefs; + } + + test('set and get String', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); + }); + + test('set and get bool', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); + }); + + test('set and get int', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + }); + + test('set and get double', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + test('set and get StringList', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + test('getPreferences', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); + }); + + test('getPreferences with filter', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: + PreferencesFilters(allowList: {stringKey, boolKey})), + emptyOptions); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + test('getKeys', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + test('getKeys with filter', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + }); + + test('clear', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + test('clear with filter', () async { + final SharedPreferencesAsyncLinux preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); +} diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index 5cb8dc3b612..dd8bf77ad13 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.0 + +* Adds `SharedPreferencesAsyncWeb` API. + ## 2.3.0 * Updates web code to package `web: ^0.5.0`. diff --git a/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart b/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart index 0238ba578a4..aeb1a6c2a65 100644 --- a/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart +++ b/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_platform_interface/method_channel_shared_preferences.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; import 'package:shared_preferences_web/shared_preferences_web.dart'; @@ -17,357 +18,564 @@ void main() { setUp(() { html.window.localStorage.clear(); }); + group('shared_preferences_web', () { + testWidgets('registers itself', (WidgetTester tester) async { + SharedPreferencesStorePlatform.instance = + MethodChannelSharedPreferencesStore(); + expect(SharedPreferencesStorePlatform.instance, + isNot(isA())); + SharedPreferencesPlugin.registerWith(null); + expect(SharedPreferencesStorePlatform.instance, + isA()); + }); - testWidgets('registers itself', (WidgetTester tester) async { - SharedPreferencesStorePlatform.instance = - MethodChannelSharedPreferencesStore(); - expect(SharedPreferencesStorePlatform.instance, - isNot(isA())); - SharedPreferencesPlugin.registerWith(null); - expect(SharedPreferencesStorePlatform.instance, - isA()); - }); - - const Map flutterTestValues = { - 'flutter.String': 'hello world', - 'flutter.Bool': true, - 'flutter.Int': 42, - 'flutter.Double': 3.14159, - 'flutter.StringList': ['foo', 'bar'], - }; - - const Map prefixTestValues = { - 'prefix.String': 'hello world', - 'prefix.Bool': true, - 'prefix.Int': 42, - 'prefix.Double': 3.14159, - 'prefix.StringList': ['foo', 'bar'], - }; - - const Map nonPrefixTestValues = { - 'String': 'hello world', - 'Bool': true, - 'Int': 42, - 'Double': 3.14159, - 'StringList': ['foo', 'bar'], - }; - - final Map allTestValues = {}; - - allTestValues.addAll(flutterTestValues); - allTestValues.addAll(prefixTestValues); - allTestValues.addAll(nonPrefixTestValues); - - late SharedPreferencesStorePlatform preferences; - - setUp(() async { - preferences = SharedPreferencesStorePlatform.instance; - }); - - tearDown(() async { - await preferences.clearWithParameters( - ClearParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - }); - - testWidgets('reading', (WidgetTester _) async { - final Map values = await preferences.getAll(); - expect(values.length, 0); - }); - - Future addData() async { - await preferences.setValue('String', 'String', allTestValues['String']!); - await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!); - await preferences.setValue('Int', 'Int', allTestValues['Int']!); - await preferences.setValue('Double', 'Double', allTestValues['Double']!); - await preferences.setValue( - 'StringList', 'StringList', allTestValues['StringList']!); - await preferences.setValue( - 'String', 'prefix.String', allTestValues['prefix.String']!); - await preferences.setValue( - 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!); - await preferences.setValue( - 'Int', 'prefix.Int', allTestValues['prefix.Int']!); - await preferences.setValue( - 'Double', 'prefix.Double', allTestValues['prefix.Double']!); - await preferences.setValue( - 'StringList', 'prefix.StringList', allTestValues['prefix.StringList']!); - await preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!); - await preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); - await preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!); - await preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!); - await preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!); - } - - testWidgets('keys', (WidgetTester _) async { - await addData(); - final Iterable keys = html.window.localStorage.keys; - final Iterable expectedKeys = allTestValues.keys; - - expect(keys, hasLength(expectedKeys.length)); - expect(keys, containsAll(expectedKeys)); - }); + const Map flutterTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + + const Map prefixTestValues = { + 'prefix.String': 'hello world', + 'prefix.Bool': true, + 'prefix.Int': 42, + 'prefix.Double': 3.14159, + 'prefix.StringList': ['foo', 'bar'], + }; + + const Map nonPrefixTestValues = { + 'String': 'hello world', + 'Bool': true, + 'Int': 42, + 'Double': 3.14159, + 'StringList': ['foo', 'bar'], + }; + + final Map allTestValues = {}; + + allTestValues.addAll(flutterTestValues); + allTestValues.addAll(prefixTestValues); + allTestValues.addAll(nonPrefixTestValues); + + late SharedPreferencesStorePlatform preferences; - testWidgets('clear', (WidgetTester _) async { - await addData(); - await preferences.clear(); - final Map values = await preferences.getAll(); - expect(values['flutter.String'], null); - expect(values['flutter.Bool'], null); - expect(values['flutter.Int'], null); - expect(values['flutter.Double'], null); - expect(values['flutter.StringList'], null); - }); - - group('withPrefix', () { setUp(() async { - await addData(); + preferences = SharedPreferencesStorePlatform.instance; }); - testWidgets('remove', (WidgetTester _) async { - const String key = 'flutter.String'; - await preferences.remove(key); - final Map values = - // ignore: deprecated_member_use - await preferences.getAllWithPrefix(''); - expect(values[key], isNull); + tearDown(() async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); }); - testWidgets('get all with prefix', (WidgetTester _) async { - final Map values = - // ignore: deprecated_member_use - await preferences.getAllWithPrefix('prefix.'); - expect(values['prefix.String'], allTestValues['prefix.String']); - expect(values['prefix.Bool'], allTestValues['prefix.Bool']); - expect(values['prefix.Int'], allTestValues['prefix.Int']); - expect(values['prefix.Double'], allTestValues['prefix.Double']); - expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + testWidgets('reading', (WidgetTester _) async { + final Map values = await preferences.getAll(); + expect(values.length, 0); }); - testWidgets('getAllWithNoPrefix', (WidgetTester _) async { - final Map values = - // ignore: deprecated_member_use - await preferences.getAllWithPrefix(''); - expect(values['String'], allTestValues['String']); - expect(values['Bool'], allTestValues['Bool']); - expect(values['Int'], allTestValues['Int']); - expect(values['Double'], allTestValues['Double']); - expect(values['StringList'], allTestValues['StringList']); - expect(values['flutter.String'], allTestValues['flutter.String']); - expect(values['flutter.Bool'], allTestValues['flutter.Bool']); - expect(values['flutter.Int'], allTestValues['flutter.Int']); - expect(values['flutter.Double'], allTestValues['flutter.Double']); - expect(values['flutter.StringList'], allTestValues['flutter.StringList']); - }); + Future addData() async { + await preferences.setValue('String', 'String', allTestValues['String']!); + await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!); + await preferences.setValue('Int', 'Int', allTestValues['Int']!); + await preferences.setValue('Double', 'Double', allTestValues['Double']!); + await preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!); + await preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!); + await preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!); + await preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!); + await preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!); + await preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); + } - testWidgets('clearWithPrefix', (WidgetTester _) async { - // ignore: deprecated_member_use - await preferences.clearWithPrefix('prefix.'); - Map values = - // ignore: deprecated_member_use - await preferences.getAllWithPrefix('prefix.'); - expect(values['prefix.String'], null); - expect(values['prefix.Bool'], null); - expect(values['prefix.Int'], null); - expect(values['prefix.Double'], null); - expect(values['prefix.StringList'], null); - // ignore: deprecated_member_use - values = await preferences.getAllWithPrefix('flutter.'); - expect(values['flutter.String'], allTestValues['flutter.String']); - expect(values['flutter.Bool'], allTestValues['flutter.Bool']); - expect(values['flutter.Int'], allTestValues['flutter.Int']); - expect(values['flutter.Double'], allTestValues['flutter.Double']); - expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + testWidgets('keys', (WidgetTester _) async { + await addData(); + final Iterable keys = html.window.localStorage.keys; + final Iterable expectedKeys = allTestValues.keys; + + expect(keys, hasLength(expectedKeys.length)); + expect(keys, containsAll(expectedKeys)); }); - testWidgets('clearWithNoPrefix', (WidgetTester _) async { - // ignore: deprecated_member_use - await preferences.clearWithPrefix(''); - final Map values = - // ignore: deprecated_member_use - await preferences.getAllWithPrefix(''); - expect(values['String'], null); - expect(values['Bool'], null); - expect(values['Int'], null); - expect(values['Double'], null); - expect(values['StringList'], null); + testWidgets('clear', (WidgetTester _) async { + await addData(); + await preferences.clear(); + final Map values = await preferences.getAll(); expect(values['flutter.String'], null); expect(values['flutter.Bool'], null); expect(values['flutter.Int'], null); expect(values['flutter.Double'], null); expect(values['flutter.StringList'], null); }); - }); - group('withParameters', () { - setUp(() async { - await addData(); + group('withPrefix', () { + setUp(() async { + await addData(); + }); + + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values[key], isNull); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithPrefix', (WidgetTester _) async { + // ignore: deprecated_member_use + await preferences.clearWithPrefix('prefix.'); + Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + // ignore: deprecated_member_use + values = await preferences.getAllWithPrefix('flutter.'); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + // ignore: deprecated_member_use + await preferences.clearWithPrefix(''); + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + }); + + group('withParameters', () { + setUp(() async { + await addData(); + }); + + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values[key], isNull); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('get all with allow list', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters with allow list', + (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); }); - testWidgets('remove', (WidgetTester _) async { - const String key = 'flutter.String'; - await preferences.remove(key); + testWidgets('simultaneous writes', (WidgetTester _) async { + final List> writes = >[]; + const int writeCount = 100; + for (int i = 1; i <= writeCount; i++) { + writes.add(preferences.setValue('Int', 'Int', i)); + } + final List result = await Future.wait(writes, eagerError: true); + // All writes should succeed. + expect(result.where((bool element) => !element), isEmpty); + // The last write should win. final Map values = await preferences.getAllWithParameters( GetAllParameters( filter: PreferencesFilter(prefix: ''), ), ); - expect(values[key], isNull); + expect(values['Int'], writeCount); }); + }); - testWidgets('get all with prefix', (WidgetTester _) async { - final Map values = await preferences.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(values['prefix.String'], allTestValues['prefix.String']); - expect(values['prefix.Bool'], allTestValues['prefix.Bool']); - expect(values['prefix.Int'], allTestValues['prefix.Int']); - expect(values['prefix.Double'], allTestValues['prefix.Double']); - expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + group('shared_preferences_async', () { + const SharedPreferencesWebOptions emptyOptions = + SharedPreferencesWebOptions(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + Future getPreferences() async { + final SharedPreferencesAsyncPlatform preferences = + SharedPreferencesAsyncPlatform.instance!; + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + return preferences; + } + + testWidgets('set and get String', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); }); - testWidgets('get all with allow list', (WidgetTester _) async { - final Map values = await preferences.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter( - prefix: 'prefix.', - allowList: {'prefix.String'}, - ), - ), - ); - expect(values['prefix.String'], allTestValues['prefix.String']); - expect(values['prefix.Bool'], null); - expect(values['prefix.Int'], null); - expect(values['prefix.Double'], null); - expect(values['prefix.StringList'], null); + testWidgets('set and get bool', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); }); - testWidgets('getAllWithNoPrefix', (WidgetTester _) async { - final Map values = await preferences.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - expect(values['String'], allTestValues['String']); - expect(values['Bool'], allTestValues['Bool']); - expect(values['Int'], allTestValues['Int']); - expect(values['Double'], allTestValues['Double']); - expect(values['StringList'], allTestValues['StringList']); - expect(values['flutter.String'], allTestValues['flutter.String']); - expect(values['flutter.Bool'], allTestValues['flutter.Bool']); - expect(values['flutter.Int'], allTestValues['flutter.Int']); - expect(values['flutter.Double'], allTestValues['flutter.Double']); - expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + testWidgets('set and get int', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); }); - testWidgets('clearWithParameters', (WidgetTester _) async { - await preferences.clearWithParameters( - ClearParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - Map values = await preferences.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(values['prefix.String'], null); - expect(values['prefix.Bool'], null); - expect(values['prefix.Int'], null); - expect(values['prefix.Double'], null); - expect(values['prefix.StringList'], null); - values = await preferences.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'flutter.'), - ), + testWidgets('set and get double', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + testWidgets('set and get StringList', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + testWidgets('getPreferences', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, ); - expect(values['flutter.String'], allTestValues['flutter.String']); - expect(values['flutter.Bool'], allTestValues['flutter.Bool']); - expect(values['flutter.Int'], allTestValues['flutter.Int']); - expect(values['flutter.Double'], allTestValues['flutter.Double']); - expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); }); - testWidgets('clearWithParameters with allow list', (WidgetTester _) async { - await addData(); - await preferences.clearWithParameters( - ClearParameters( - filter: PreferencesFilter( - prefix: 'prefix.', - allowList: {'prefix.StringList'}, - ), + testWidgets('getPreferences with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), ), + emptyOptions, ); - Map values = await preferences.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + testWidgets('getKeys', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, ); - expect(values['prefix.String'], allTestValues['prefix.String']); - expect(values['prefix.Bool'], allTestValues['prefix.Bool']); - expect(values['prefix.Int'], allTestValues['prefix.Int']); - expect(values['prefix.Double'], allTestValues['prefix.Double']); - expect(values['prefix.StringList'], null); - values = await preferences.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'flutter.'), + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + testWidgets('getKeys with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), ), + emptyOptions, ); - expect(values['flutter.String'], allTestValues['flutter.String']); - expect(values['flutter.Bool'], allTestValues['flutter.Bool']); - expect(values['flutter.Int'], allTestValues['flutter.Int']); - expect(values['flutter.Double'], allTestValues['flutter.Double']); - expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); }); - testWidgets('clearWithNoPrefix', (WidgetTester _) async { - await preferences.clearWithParameters( - ClearParameters( - filter: PreferencesFilter(prefix: ''), - ), + testWidgets('clear', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, ); - final Map values = await preferences.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: ''), + + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + testWidgets('clear with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), ), + emptyOptions, ); - expect(values['String'], null); - expect(values['Bool'], null); - expect(values['Int'], null); - expect(values['Double'], null); - expect(values['StringList'], null); - expect(values['flutter.String'], null); - expect(values['flutter.Bool'], null); - expect(values['flutter.Int'], null); - expect(values['flutter.Double'], null); - expect(values['flutter.StringList'], null); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); }); }); - - testWidgets('simultaneous writes', (WidgetTester _) async { - final List> writes = >[]; - const int writeCount = 100; - for (int i = 1; i <= writeCount; i++) { - writes.add(preferences.setValue('Int', 'Int', i)); - } - final List result = await Future.wait(writes, eagerError: true); - // All writes should succeed. - expect(result.where((bool element) => !element), isEmpty); - // The last write should win. - final Map values = await preferences.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - expect(values['Int'], writeCount); - }); } diff --git a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml index 44672c4e642..634e76da602 100644 --- a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 shared_preferences_web: path: ../ web: ^0.5.0 diff --git a/packages/shared_preferences/shared_preferences_web/lib/shared_preferences_web.dart b/packages/shared_preferences/shared_preferences_web/lib/shared_preferences_web.dart index b83c1a5fe37..be75e1786bd 100644 --- a/packages/shared_preferences/shared_preferences_web/lib/shared_preferences_web.dart +++ b/packages/shared_preferences/shared_preferences_web/lib/shared_preferences_web.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:convert' show json; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; import 'package:web/web.dart' as html; @@ -19,6 +20,7 @@ class SharedPreferencesPlugin extends SharedPreferencesStorePlatform { /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. static void registerWith(Registrar? registrar) { SharedPreferencesStorePlatform.instance = SharedPreferencesPlugin(); + SharedPreferencesAsyncWeb.registerWith(registrar); } static const String _defaultPrefix = 'flutter.'; @@ -44,7 +46,7 @@ class SharedPreferencesPlugin extends SharedPreferencesStorePlatform { // IMPORTANT: Do not use html.window.localStorage.clear() as that will // remove _all_ local data, not just the keys prefixed with // _prefix - _getFilteredKeys(filter.prefix, allowList: filter.allowList) + _getPrefixedKeys(filter.prefix, allowList: filter.allowList) .forEach(remove); return true; } @@ -70,7 +72,7 @@ class SharedPreferencesPlugin extends SharedPreferencesStorePlatform { final PreferencesFilter filter = parameters.filter; final Map allData = {}; for (final String key - in _getFilteredKeys(filter.prefix, allowList: filter.allowList)) { + in _getPrefixedKeys(filter.prefix, allowList: filter.allowList)) { allData[key] = _decodeValue(html.window.localStorage.getItem(key)!); } return allData; @@ -88,28 +90,189 @@ class SharedPreferencesPlugin extends SharedPreferencesStorePlatform { return true; } - Iterable _getFilteredKeys( + Iterable _getPrefixedKeys( String prefix, { Set? allowList, }) { - return html.window.localStorage.keys.where((String key) => - key.startsWith(prefix) && (allowList?.contains(key) ?? true)); + return _getAllowedKeys(allowList: allowList) + .where((String key) => key.startsWith(prefix)); } +} - String _encodeValue(Object? value) { - return json.encode(value); +/// The web implementation of [SharedPreferencesAsyncPlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for the web. +base class SharedPreferencesAsyncWeb extends SharedPreferencesAsyncPlatform { + /// Registers this class as the default instance of [SharedPreferencesAsyncPlatform]. + static void registerWith(Registrar? registrar) { + SharedPreferencesAsyncPlatform.instance = SharedPreferencesAsyncWeb(); } - Object _decodeValue(String encodedValue) { - final Object? decodedValue = json.decode(encodedValue); + @override + Future clear( + ClearPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + _getAllowedKeys(allowList: filter.allowList) + .forEach((String key) => html.window.localStorage.removeItem(key)); + } - if (decodedValue is List) { - // JSON does not preserve generics. The encode/decode roundtrip is - // `List` => JSON => `List`. We have to explicitly - // restore the RTTI. - return decodedValue.cast(); + @override + Future> getPreferences( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + return _readAllFromLocalStorage(parameters.filter.allowList, options); + } + + Future> _readAllFromLocalStorage( + Set? allowList, + SharedPreferencesOptions options, + ) async { + final Map allData = {}; + for (final String key in _getAllowedKeys(allowList: allowList)) { + allData[key] = _decodeValue(html.window.localStorage.getItem(key)!); } + return allData; + } + + @override + Future> getKeys(GetPreferencesParameters parameters, + SharedPreferencesOptions options) async { + return (await getPreferences(parameters, options)).keys.toSet(); + } + + @override + Future setString( + String key, + String value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setBool( + String key, + bool value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } - return decodedValue!; + @override + Future setDouble( + String key, + double value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setInt( + String key, + int value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setStringList( + String key, + List value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); } + + Future _setValue( + String key, + Object? value, + SharedPreferencesOptions options, + ) async { + html.window.localStorage.setItem(key, _encodeValue(value)); + } + + @override + Future getString( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = + await _readAllFromLocalStorage({key}, options); + return data[key] as String?; + } + + @override + Future getBool( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = + await _readAllFromLocalStorage({key}, options); + return data[key] as bool?; + } + + @override + Future getDouble( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = + await _readAllFromLocalStorage({key}, options); + return data[key] as double?; + } + + @override + Future getInt( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = + await _readAllFromLocalStorage({key}, options); + return data[key] as int?; + } + + @override + Future?> getStringList( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = + await _readAllFromLocalStorage({key}, options); + return data[key] as List?; + } +} + +Iterable _getAllowedKeys({ + Set? allowList, +}) { + return html.window.localStorage.keys + .where((String key) => allowList?.contains(key) ?? true); +} + +String _encodeValue(Object? value) { + return json.encode(value); +} + +Object _decodeValue(String encodedValue) { + final Object? decodedValue = json.decode(encodedValue); + + if (decodedValue is List) { + // JSON does not preserve generics. The encode/decode roundtrip is + // `List` => JSON => `List`. We have to explicitly + // restore the RTTI. + return decodedValue.cast(); + } + + return decodedValue!; +} + +/// Web specific SharedPreferences Options. +class SharedPreferencesWebOptions extends SharedPreferencesOptions { + /// Constructor for SharedPreferencesWebOptions. + const SharedPreferencesWebOptions(); } diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index 1278a54a3e5..375751af47b 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.3.0 +version: 2.4.0 environment: sdk: ^3.3.0 @@ -21,7 +21,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 web: ^0.5.0 dev_dependencies: diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index 8137dbb765d..c5a859d69f7 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.4.0 +* Adds `SharedPreferencesAsyncWindows` API. * Updates minimum supported SDK version to Flutter 3.16/Dart 3.2. ## 2.3.2 diff --git a/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart index bd30ddeab4a..5549b9b5deb 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart @@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; @@ -336,4 +337,187 @@ void main() { }); }); }); + + group('shared_preferences_async', () { + const SharedPreferencesWindowsOptions emptyOptions = + SharedPreferencesWindowsOptions(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + Future getPreferences() async { + final SharedPreferencesAsyncPlatform preferences = + SharedPreferencesAsyncPlatform.instance!; + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + return preferences; + } + + testWidgets('set and get String', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); + }); + + testWidgets('set and get bool', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); + }); + + testWidgets('set and get int', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + }); + + testWidgets('set and get double', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + testWidgets('set and get StringList', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + testWidgets('getPreferences', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); + }); + + testWidgets('getPreferences with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + testWidgets('getKeys', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + testWidgets('getKeys with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + }); + + testWidgets('clear', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + testWidgets('clear with filter', (WidgetTester _) async { + final SharedPreferencesAsyncPlatform preferences = await getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + }); } diff --git a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart index 3c5312aab60..fc07e68eb86 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/lib/main.dart @@ -4,9 +4,8 @@ // ignore_for_file: public_member_api_docs -import 'dart:async'; - import 'package:flutter/material.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; void main() { @@ -33,26 +32,36 @@ class SharedPreferencesDemo extends StatefulWidget { } class SharedPreferencesDemoState extends State { - final SharedPreferencesWindows prefs = SharedPreferencesWindows(); + final SharedPreferencesAsyncPlatform? _prefs = + SharedPreferencesAsyncPlatform.instance; + final SharedPreferencesWindowsOptions options = + const SharedPreferencesWindowsOptions(); + static const String _counterKey = 'counter'; late Future _counter; Future _incrementCounter() async { - final Map values = await prefs.getAll(); - final int counter = (values['counter'] as int? ?? 0) + 1; + final int? value = await _prefs!.getInt(_counterKey, options); + final int counter = (value ?? 0) + 1; setState(() { - _counter = prefs.setValue('Int', 'counter', counter).then((bool success) { + _counter = _prefs.setInt(_counterKey, counter, options).then((_) { return counter; }); }); } + Future _getAndSetCounter() async { + setState(() { + _counter = _prefs!.getInt(_counterKey, options).then((int? counter) { + return counter ?? 0; + }); + }); + } + @override void initState() { super.initState(); - _counter = prefs.getAll().then((Map values) { - return values['counter'] as int? ?? 0; - }); + _getAndSetCounter(); } @override diff --git a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml index 8331a914fbf..302b5e414fc 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: flutter: sdk: flutter - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 shared_preferences_windows: # When depending on this package from a real application you should use: # shared_preferences_windows: ^x.y.z diff --git a/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart b/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart index 097e0a6c63c..0a2ec7688e7 100644 --- a/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart +++ b/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart @@ -10,9 +10,14 @@ import 'package:file/local.dart'; import 'package:flutter/foundation.dart' show debugPrint, visibleForTesting; import 'package:path/path.dart' as path; import 'package:path_provider_windows/path_provider_windows.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; +const String _defaultFileName = 'shared_preferences'; + +const String _defaultPrefix = 'flutter.'; + /// The Windows implementation of [SharedPreferencesStorePlatform]. /// /// This class implements the `package:shared_preferences` functionality for Windows. @@ -25,9 +30,12 @@ class SharedPreferencesWindows extends SharedPreferencesStorePlatform { /// Registers the Windows implementation. static void registerWith() { SharedPreferencesStorePlatform.instance = SharedPreferencesWindows(); + // A temporary work-around for having two plugins contained in a single package. + SharedPreferencesAsyncWindows.registerWith(); } - static const String _defaultPrefix = 'flutter.'; + /// Local copy of preferences + Map? _cachedPreferences; /// File system used to store to disk. Exposed for testing only. @visibleForTesting @@ -37,66 +45,15 @@ class SharedPreferencesWindows extends SharedPreferencesStorePlatform { @visibleForTesting PathProviderWindows pathProvider = PathProviderWindows(); - /// Local copy of preferences - Map? _cachedPreferences; - - /// Cached file for storing preferences. - File? _localDataFilePath; - - /// Gets the file where the preferences are stored. - Future _getLocalDataFile() async { - if (_localDataFilePath != null) { - return _localDataFilePath!; - } - final String? directory = await pathProvider.getApplicationSupportPath(); - if (directory == null) { - return null; - } - return _localDataFilePath = - fs.file(path.join(directory, 'shared_preferences.json')); - } - - /// Gets the preferences from the stored file. Once read, the preferences are - /// maintained in memory. - Future> _reload() async { - Map preferences = {}; - final File? localDataFile = await _getLocalDataFile(); - if (localDataFile != null && localDataFile.existsSync()) { - final String stringMap = localDataFile.readAsStringSync(); - if (stringMap.isNotEmpty) { - final Object? data = json.decode(stringMap); - if (data is Map) { - preferences = data.cast(); - } - } - } - _cachedPreferences = preferences; - return preferences; - } - + /// Checks for cached preferences and returns them or loads preferences from + /// file and returns and caches them. Future> _readPreferences() async { - return _cachedPreferences ?? await _reload(); - } - - /// Writes the cached preferences to disk. Returns [true] if the operation - /// succeeded. - Future _writePreferences(Map preferences) async { - try { - final File? localDataFile = await _getLocalDataFile(); - if (localDataFile == null) { - debugPrint('Unable to determine where to write preferences.'); - return false; - } - if (!localDataFile.existsSync()) { - localDataFile.createSync(recursive: true); - } - final String stringMap = json.encode(preferences); - localDataFile.writeAsStringSync(stringMap); - } catch (e) { - debugPrint('Error saving preferences to disk: $e'); - return false; - } - return true; + _cachedPreferences ??= await _readFromFile( + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); + return _cachedPreferences!; } @override @@ -117,11 +74,17 @@ class SharedPreferencesWindows extends SharedPreferencesStorePlatform { @override Future clearWithParameters(ClearParameters parameters) async { final PreferencesFilter filter = parameters.filter; + final Map preferences = await _readPreferences(); preferences.removeWhere((String key, _) => key.startsWith(filter.prefix) && (filter.allowList == null || filter.allowList!.contains(key))); - return _writePreferences(preferences); + return _writePreferences( + preferences, + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); } @override @@ -154,13 +117,297 @@ class SharedPreferencesWindows extends SharedPreferencesStorePlatform { Future remove(String key) async { final Map preferences = await _readPreferences(); preferences.remove(key); - return _writePreferences(preferences); + return _writePreferences( + preferences, + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); } @override Future setValue(String valueType, String key, Object value) async { final Map preferences = await _readPreferences(); preferences[key] = value; - return _writePreferences(preferences); + return _writePreferences( + preferences, + _defaultFileName, + fs: fs, + pathProvider: pathProvider, + ); + } +} + +/// The Windows implementation of [SharedPreferencesAsyncPlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for Windows. +base class SharedPreferencesAsyncWindows + extends SharedPreferencesAsyncPlatform { + /// Registers the Windows implementation. + static void registerWith() { + SharedPreferencesAsyncPlatform.instance = SharedPreferencesAsyncWindows(); + } + + /// Local copy of preferences + Map? _cachedPreferences; + + /// File system used to store to disk. Exposed for testing only. + @visibleForTesting + FileSystem fs = const LocalFileSystem(); + + /// The path_provider_windows instance used to find the support directory. + @visibleForTesting + PathProviderWindows pathProvider = PathProviderWindows(); + + @override + Future> getKeys( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + return (await getPreferences(parameters, options)).keys.toSet(); + } + + @override + Future setString( + String key, + String value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setBool( + String key, + bool value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setDouble( + String key, + double value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setInt( + String key, + int value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future setStringList( + String key, + List value, + SharedPreferencesOptions options, + ) { + return _setValue(key, value, options); + } + + @override + Future getString( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as String?; + } + + @override + Future getBool( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as bool?; + } + + @override + Future getDouble( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as double?; + } + + @override + Future getInt( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return data[key] as int?; + } + + @override + Future?> getStringList( + String key, + SharedPreferencesOptions options, + ) async { + final Map data = await _readAll({key}, options); + return (data[key] as List?)?.toList(); + } + + @override + Future clear(ClearPreferencesParameters parameters, + SharedPreferencesOptions options) async { + final SharedPreferencesWindowsOptions windowsOptions = + SharedPreferencesWindowsOptions.fromSharedPreferencesOptions(options); + final PreferencesFilters filter = parameters.filter; + final Map preferences = + await _readPreferences(windowsOptions.fileName); + preferences.removeWhere((String key, _) => + filter.allowList == null || filter.allowList!.contains(key)); + await _writePreferences( + preferences, + windowsOptions.fileName, + fs: fs, + pathProvider: pathProvider, + ); + } + + @override + Future> getPreferences( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + return _readAll(parameters.filter.allowList, options); + } + + Future> _readAll( + Set? allowList, + SharedPreferencesOptions options, + ) async { + final SharedPreferencesWindowsOptions windowsOptions = + SharedPreferencesWindowsOptions.fromSharedPreferencesOptions(options); + final Map prefs = Map.from( + await _readPreferences(windowsOptions.fileName)); + prefs.removeWhere((String key, _) => !(allowList?.contains(key) ?? true)); + return prefs; + } + + Future _setValue( + String key, Object value, SharedPreferencesOptions options) async { + final SharedPreferencesWindowsOptions windowsOptions = + SharedPreferencesWindowsOptions.fromSharedPreferencesOptions(options); + final Map preferences = + await _readPreferences(windowsOptions.fileName); + preferences[key] = value; + await _writePreferences( + preferences, + windowsOptions.fileName, + fs: fs, + pathProvider: pathProvider, + ); + } + + /// Checks for cached preferences and returns them or loads preferences from + /// file and returns and caches them. + Future> _readPreferences(String fileName) async { + _cachedPreferences ??= await _readFromFile( + fileName, + fs: fs, + pathProvider: pathProvider, + ); + return _cachedPreferences!; + } +} + +/// Gets the file where the preferences are stored. +Future _getLocalDataFile( + String fileName, { + FileSystem fs = const LocalFileSystem(), + PathProviderWindows? pathProvider, +}) async { + pathProvider = pathProvider ?? PathProviderWindows(); + final String? directory = await pathProvider.getApplicationSupportPath(); + if (directory == null) { + return null; + } + final String fileLocation = path.join(directory, '$fileName.json'); + return fs.file(fileLocation); +} + +/// Gets the preferences from the stored file. +Future> _readFromFile( + String fileName, { + FileSystem fs = const LocalFileSystem(), + PathProviderWindows? pathProvider, +}) async { + Map preferences = {}; + final File? localDataFile = await _getLocalDataFile( + fileName, + fs: fs, + pathProvider: pathProvider, + ); + if (localDataFile != null && localDataFile.existsSync()) { + final String stringMap = localDataFile.readAsStringSync(); + if (stringMap.isNotEmpty) { + final Object? data = json.decode(stringMap); + if (data is Map) { + preferences = data.cast(); + } + } + } + return preferences; +} + +/// Writes the cached preferences to disk. Returns [true] if the operation +/// succeeded. +Future _writePreferences( + Map preferences, + String fileName, { + FileSystem fs = const LocalFileSystem(), + PathProviderWindows? pathProvider, +}) async { + try { + final File? localDataFile = await _getLocalDataFile( + fileName, + fs: fs, + pathProvider: pathProvider, + ); + if (localDataFile == null) { + debugPrint('Unable to determine where to write preferences.'); + return false; + } + if (!localDataFile.existsSync()) { + localDataFile.createSync(recursive: true); + } + final String stringMap = json.encode(preferences); + localDataFile.writeAsStringSync(stringMap); + } catch (e) { + debugPrint('Error saving preferences to disk: $e'); + return false; + } + return true; +} + +/// Windows specific SharedPreferences Options. +class SharedPreferencesWindowsOptions extends SharedPreferencesOptions { + /// Constructor for SharedPreferencesWindowsOptions. + const SharedPreferencesWindowsOptions({ + this.fileName = 'shared_preferences', // Same as current defaults. + }); + + /// The name of the file to store preferences in. + final String fileName; + + /// Returns a new instance of [SharedPreferencesWindowsOptions] from an existing + /// [SharedPreferencesOptions]. + static SharedPreferencesWindowsOptions fromSharedPreferencesOptions( + SharedPreferencesOptions options) { + if (options is SharedPreferencesWindowsOptions) { + return options; + } + return const SharedPreferencesWindowsOptions(); } } diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index b8481f59830..653fb51341a 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.3.2 +version: 2.4.0 environment: sdk: ^3.2.0 @@ -22,7 +22,7 @@ dependencies: path: ^1.8.0 path_provider_platform_interface: ^2.0.0 path_provider_windows: ^2.0.0 - shared_preferences_platform_interface: ^2.3.0 + shared_preferences_platform_interface: ^2.4.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_windows/test/fake_path_provider_windows.dart b/packages/shared_preferences/shared_preferences_windows/test/fake_path_provider_windows.dart new file mode 100644 index 00000000000..8b7b374118e --- /dev/null +++ b/packages/shared_preferences/shared_preferences_windows/test/fake_path_provider_windows.dart @@ -0,0 +1,36 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; +import 'package:path_provider_windows/path_provider_windows.dart'; + +/// Fake implementation of PathProviderWindows that returns hard-coded paths, +/// allowing tests to run on any platform. +/// +/// Note that this should only be used with an in-memory filesystem, as the +/// path it returns is a root path that does not actually exist on Windows. +class FakePathProviderWindows extends PathProviderPlatform + implements PathProviderWindows { + @override + late VersionInfoQuerier versionInfoQuerier; + + @override + Future getApplicationSupportPath() async => r'C:\appsupport'; + + @override + Future getTemporaryPath() async => null; + + @override + Future getLibraryPath() async => null; + + @override + Future getApplicationDocumentsPath() async => null; + + @override + Future getDownloadsPath() async => null; + + @override + Future getPath(String folderID) async => ''; +} diff --git a/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart b/packages/shared_preferences/shared_preferences_windows/test/legacy_shared_preferences_windows_test.dart similarity index 89% rename from packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart rename to packages/shared_preferences/shared_preferences_windows/test/legacy_shared_preferences_windows_test.dart index 499a74eeca3..6a8de0aaf4e 100644 --- a/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart +++ b/packages/shared_preferences/shared_preferences_windows/test/legacy_shared_preferences_windows_test.dart @@ -7,12 +7,13 @@ import 'dart:convert'; import 'package:file/memory.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as path; -import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:path_provider_windows/path_provider_windows.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; +import 'fake_path_provider_windows.dart'; + void main() { late MemoryFileSystem fs; late PathProviderWindows pathProvider; @@ -76,7 +77,7 @@ void main() { return prefs; } - test('registered instance', () { + test('registered instance', () async { SharedPreferencesWindows.registerWith(); expect(SharedPreferencesStorePlatform.instance, isA()); @@ -255,32 +256,3 @@ void main() { expect(noValues, hasLength(0)); }); } - -/// Fake implementation of PathProviderWindows that returns hard-coded paths, -/// allowing tests to run on any platform. -/// -/// Note that this should only be used with an in-memory filesystem, as the -/// path it returns is a root path that does not actually exist on Windows. -class FakePathProviderWindows extends PathProviderPlatform - implements PathProviderWindows { - @override - late VersionInfoQuerier versionInfoQuerier; - - @override - Future getApplicationSupportPath() async => r'C:\appsupport'; - - @override - Future getTemporaryPath() async => null; - - @override - Future getLibraryPath() async => null; - - @override - Future getApplicationDocumentsPath() async => null; - - @override - Future getDownloadsPath() async => null; - - @override - Future getPath(String folderID) async => ''; -} diff --git a/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_async_test.dart b/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_async_test.dart new file mode 100755 index 00000000000..6846f1fa995 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_async_test.dart @@ -0,0 +1,197 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:file/memory.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path_provider_windows/path_provider_windows.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; +import 'package:shared_preferences_windows/shared_preferences_windows.dart'; + +import 'fake_path_provider_windows.dart'; + +void main() { + late MemoryFileSystem fs; + late PathProviderWindows pathProvider; + + SharedPreferencesAsyncWindows.registerWith(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + const SharedPreferencesWindowsOptions emptyOptions = + SharedPreferencesWindowsOptions(); + + setUp(() { + fs = MemoryFileSystem.test(); + pathProvider = FakePathProviderWindows(); + }); + + SharedPreferencesAsyncWindows getPreferences() { + final SharedPreferencesAsyncWindows prefs = SharedPreferencesAsyncWindows(); + prefs.fs = fs; + prefs.pathProvider = pathProvider; + return prefs; + } + + test('set and get String', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); + }); + + test('set and get bool', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); + }); + + test('set and get int', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + }); + + test('set and get double', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + test('set and get StringList', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + test('getPreferences', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); + }); + + test('getPreferences with filter', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: + PreferencesFilters(allowList: {stringKey, boolKey})), + emptyOptions); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + test('getKeys', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + test('getKeys with filter', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + }); + + test('clear', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + test('clear with filter', () async { + final SharedPreferencesAsyncWindows preferences = getPreferences(); + await preferences.setString(stringKey, testString, emptyOptions); + await preferences.setBool(boolKey, testBool, emptyOptions); + await preferences.setInt(intKey, testInt, emptyOptions); + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + await preferences.setStringList(listKey, testList, emptyOptions); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); +} From b8b46439b0d2bcc7a3bf86645214dfecdfac2a09 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 18 Jul 2024 14:23:03 -0700 Subject: [PATCH 02/22] analyze --- .../shared_preferences_android/CHANGELOG.md | 2 +- .../test/shared_preferences_foundation_test.dart | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 6448b606605..39c0e47d7e0 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,4 +1,4 @@ -## 3.0.0 +## 2.3.0 * Renames `SharedPreferencesAndroid` to `LegacySharedPreferencesAndroid`. * Creates new `SharedPreferencesAndroid` that extends `SharedPreferencesAsyncPlatform`. diff --git a/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart index a82a3ad27fc..c2a1807ca05 100644 --- a/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart @@ -233,18 +233,6 @@ class _FakeSharedPreferencesApi implements UserDefaultsApi { return filteredItems; } - @override - Future getString( - String key, SharedPreferencesPigeonOptions options) async { - return items[key] as String?; - } - - @override - Future?> getStringList( - String key, SharedPreferencesPigeonOptions options) async { - return items[key] as List?; - } - @override Future set( String key, Object value, SharedPreferencesPigeonOptions options) async { From 3216359935fe57c4364efce053d9b2dccb88b023 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 18 Jul 2024 15:33:49 -0700 Subject: [PATCH 03/22] newer version --- .../shared_preferences_android/android/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 4985674f2b4..f5f0bafd6fb 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -65,9 +65,9 @@ android { testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core-ktx:1.5.0' testImplementation 'androidx.test.ext:junit-ktx:1.1.5' - testImplementation 'org.robolectric:robolectric:4.12.2' + testImplementation 'org.robolectric:robolectric:4.13' testImplementation 'org.mockito:mockito-inline:5.2.0' - testImplementation 'io.mockk:mockk:1.13.9' + testImplementation 'io.mockk:mockk:1.13.12' } From 351e1896d04b9eb8f500f39a8196d5b3bba5e8be Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 18 Jul 2024 16:12:52 -0700 Subject: [PATCH 04/22] revert gradle changes --- .../example/android/app/build.gradle | 2 -- .../shared_preferences_android/example/android/build.gradle | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle b/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle index 9e46ede2d7c..5e851db693e 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/example/android/app/build.gradle @@ -59,5 +59,3 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "androidx.datastore:datastore-preferences:1.0.0" } - -//implementation 'androidx.datastore:datastore:1.0.0' \ No newline at end of file diff --git a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle index f57c64e8ad3..4dec761be7b 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.9.0' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.2.2' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } From 167a191cbd88c0787ee51c5811d4cebed9659c5c Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 18 Jul 2024 17:32:56 -0700 Subject: [PATCH 05/22] revert gradle wrapper --- .../example/android/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_android/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/shared_preferences/shared_preferences_android/example/android/gradle/wrapper/gradle-wrapper.properties index 9087d16a204..c48e7025d19 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/shared_preferences/shared_preferences_android/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed Jan 17 19:21:34 PST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 751b7abaddb014f749761205165e2e81455ebf96 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 19 Jul 2024 12:41:43 -0700 Subject: [PATCH 06/22] gradle --- .../shared_preferences_android/android/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index f5f0bafd6fb..29564149ad8 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -59,13 +59,13 @@ android { lintOptions { checkAllWarnings true warningsAsErrors true - disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency' + disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency', 'NewerVersionAvailable' } dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core-ktx:1.5.0' testImplementation 'androidx.test.ext:junit-ktx:1.1.5' - testImplementation 'org.robolectric:robolectric:4.13' + testImplementation 'org.robolectric:robolectric:4.12.1' testImplementation 'org.mockito:mockito-inline:5.2.0' testImplementation 'io.mockk:mockk:1.13.12' } From 966cc82f27b7f92151ff160bfa1a3015aaa5f72e Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 19 Jul 2024 13:15:17 -0700 Subject: [PATCH 07/22] bom --- .../shared_preferences_android/android/build.gradle | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 29564149ad8..2ad4601e87a 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -61,7 +61,7 @@ android { warningsAsErrors true disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency', 'NewerVersionAvailable' } - dependencies { + dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core-ktx:1.5.0' testImplementation 'androidx.test.ext:junit-ktx:1.1.5' @@ -85,6 +85,9 @@ android { } dependencies { + // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. + // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 + implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10")) implementation 'androidx.datastore:datastore:1.0.0' implementation 'androidx.datastore:datastore-preferences:1.0.0' } \ No newline at end of file From f5a7965e030e499c2cece101ab52af6e1ac2a76b Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 19 Jul 2024 13:40:49 -0700 Subject: [PATCH 08/22] move bom --- .../android/build.gradle | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 2ad4601e87a..c8839d4bc88 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -61,7 +61,12 @@ android { warningsAsErrors true disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency', 'NewerVersionAvailable' } - dependencies { + dependencies { + // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. + // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 + implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10")) + implementation 'androidx.datastore:datastore:1.0.0' + implementation 'androidx.datastore:datastore-preferences:1.0.0' testImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:core-ktx:1.5.0' testImplementation 'androidx.test.ext:junit-ktx:1.1.5' @@ -83,11 +88,3 @@ android { } } } - -dependencies { - // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. - // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 - implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10")) - implementation 'androidx.datastore:datastore:1.0.0' - implementation 'androidx.datastore:datastore-preferences:1.0.0' -} \ No newline at end of file From 6d2127b062351c9260ee4e365c33a4fdaf2da989 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 19 Jul 2024 20:00:00 -0700 Subject: [PATCH 09/22] new kotlin version --- .../shared_preferences_android/android/build.gradle | 7 ++----- .../example/android/build.gradle | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index c8839d4bc88..8d3a053c357 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -2,7 +2,7 @@ group 'io.flutter.plugins.sharedpreferences' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.8.10' repositories { google() mavenCentral() @@ -61,10 +61,7 @@ android { warningsAsErrors true disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency', 'NewerVersionAvailable' } - dependencies { - // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. - // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 - implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10")) + dependencies { implementation 'androidx.datastore:datastore:1.0.0' implementation 'androidx.datastore:datastore-preferences:1.0.0' testImplementation 'junit:junit:4.13.2' diff --git a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle index 4dec761be7b..af575c8b5cd 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.8.10' repositories { google() mavenCentral() From 5e80acbec7018564df08e1b38498b08584b974d2 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 22 Jul 2024 14:41:18 -0700 Subject: [PATCH 10/22] rename classes and files --- .../SharedPreferencesTest.kt | 8 +- .../shared_preferences_test.dart | 4 +- .../example/lib/main.dart | 4 +- .../lib/shared_preferences_android.dart | 195 +------ .../shared_preferences_android.dart} | 8 +- .../src/shared_preferences_async_android.dart | 198 ++++++++ ...t => shared_preferences_android_test.dart} | 10 +- ...art => shared_preferences_async_test.dart} | 32 +- .../messages.g.swift | 40 +- .../shared_preferences_test.dart | 6 +- .../example/lib/main.dart | 4 +- .../lib/shared_preferences_foundation.dart | 235 +-------- .../lib/{ => src}/messages.g.dart | 24 +- .../shared_preferences_async_foundation.dart | 241 +++++++++ .../shared_preferences_foundation.dart} | 9 +- .../pigeons/messages.dart | 4 +- ...cy_shared_preferences_foundation_test.dart | 347 ------------- ...red_preferences_async_foundation_test.dart | 247 +++++++++ .../shared_preferences_foundation_test.dart | 475 +++++++++++------- .../test/test_api.g.dart | 44 +- 20 files changed, 1068 insertions(+), 1067 deletions(-) rename packages/shared_preferences/shared_preferences_android/lib/{legacy_shared_preferences_android.dart => src/shared_preferences_android.dart} (93%) create mode 100644 packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_async_android.dart rename packages/shared_preferences/shared_preferences_android/test/{legacy_shared_preferences_android_test.dart => shared_preferences_android_test.dart} (96%) rename packages/shared_preferences/shared_preferences_android/test/{shared_preferences_test.dart => shared_preferences_async_test.dart} (89%) rename packages/shared_preferences/shared_preferences_foundation/lib/{ => src}/messages.g.dart (94%) create mode 100644 packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_async_foundation.dart rename packages/shared_preferences/shared_preferences_foundation/lib/{legacy_shared_preferences_foundation.dart => src/shared_preferences_foundation.dart} (91%) delete mode 100644 packages/shared_preferences/shared_preferences_foundation/test/legacy_shared_preferences_foundation_test.dart create mode 100644 packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_async_foundation_test.dart diff --git a/packages/shared_preferences/shared_preferences_android/android/src/test/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.kt b/packages/shared_preferences/shared_preferences_android/android/src/test/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.kt index 64c08a1c4df..30dc92970dd 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/test/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.kt +++ b/packages/shared_preferences/shared_preferences_android/android/src/test/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.kt @@ -148,10 +148,10 @@ internal class SharedPreferencesTest { plugin.clear(listOf(boolKey, stringKey), emptyOptions) Assert.assertNull(plugin.getBool(boolKey, emptyOptions)) - Assert.assertNull(plugin.getBool(stringKey, emptyOptions)) - Assert.assertNotNull(plugin.getBool(intKey, emptyOptions)) - Assert.assertNotNull(plugin.getBool(doubleKey, emptyOptions)) - Assert.assertNotNull(plugin.getBool(listKey, emptyOptions)) + Assert.assertNull(plugin.getString(stringKey, emptyOptions)) + Assert.assertNotNull(plugin.getInt(intKey, emptyOptions)) + Assert.assertNotNull(plugin.getDouble(doubleKey, emptyOptions)) + Assert.assertNotNull(plugin.getStringList(listKey, emptyOptions)) } @Test diff --git a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart index d98d6419475..0c4527f4670 100644 --- a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart @@ -496,8 +496,8 @@ void main() { }); group('shared_preferences_async', () { - const SharedPreferencesAndroidOptions emptyOptions = - SharedPreferencesAndroidOptions(); + const SharedPreferencesAsyncAndroidOptions emptyOptions = + SharedPreferencesAsyncAndroidOptions(); const String stringKey = 'testString'; const String boolKey = 'testBool'; diff --git a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart index 7f2313f7e23..5bf8d060920 100644 --- a/packages/shared_preferences/shared_preferences_android/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_android/example/lib/main.dart @@ -34,8 +34,8 @@ class SharedPreferencesDemo extends StatefulWidget { class SharedPreferencesDemoState extends State { final SharedPreferencesAsyncPlatform _prefs = SharedPreferencesAsyncPlatform.instance!; - final SharedPreferencesAndroidOptions options = - const SharedPreferencesAndroidOptions(); + final SharedPreferencesAsyncAndroidOptions options = + const SharedPreferencesAsyncAndroidOptions(); static const String _counterKey = 'counter'; late Future _counter; diff --git a/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart index 5a17433ef8a..3a614b8f377 100644 --- a/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart +++ b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart @@ -2,196 +2,5 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; -import 'package:shared_preferences_platform_interface/types.dart'; - -import 'legacy_shared_preferences_android.dart'; -import 'src/messages_async.g.dart'; - -const String _listPrefix = 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu'; - -/// The Android implementation of [SharedPreferencesAsyncPlatform]. -/// -/// This class implements the `package:shared_preferences` functionality for Android. -base class SharedPreferencesAndroid extends SharedPreferencesAsyncPlatform { - /// Creates a new plugin implementation instance. - SharedPreferencesAndroid({ - @visibleForTesting SharedPreferencesAsyncApi? api, - }) : _api = api ?? SharedPreferencesAsyncApi(); - - final SharedPreferencesAsyncApi _api; - - /// Registers this class as the default instance of [SharedPreferencesAsyncPlatform]. - static void registerWith() { - SharedPreferencesAsyncPlatform.instance = SharedPreferencesAndroid(); - // A temporary work-around for having two plugins contained in a single package. - LegacySharedPreferencesAndroid.registerWith(); - } - - /// Returns a SharedPreferencesPigeonOptions for sending to platform. - SharedPreferencesPigeonOptions _convertOptionsToPigeonOptions( - SharedPreferencesOptions options) { - return SharedPreferencesPigeonOptions(); - } - - @override - Future> getKeys( - GetPreferencesParameters parameters, - SharedPreferencesOptions options, - ) async { - final PreferencesFilters filter = parameters.filter; - // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 - // is fixed. In practice, the values will never be null, and the native implementation assumes that. - return (await _api.getKeys( - filter.allowList?.toList(), - _convertOptionsToPigeonOptions(options), - )) - .cast() - .toSet(); - } - - @override - Future setString( - String key, - String value, - SharedPreferencesOptions options, - ) async { - if (value.startsWith(_listPrefix)) { - throw ArgumentError( - 'StorageError: This string cannot be stored as it clashes with special identifier prefixes'); - } - - return _api.setString(key, value, _convertOptionsToPigeonOptions(options)); - } - - @override - Future setInt( - String key, - int value, - SharedPreferencesOptions options, - ) async { - return _api.setInt(key, value, _convertOptionsToPigeonOptions(options)); - } - - @override - Future setDouble( - String key, - double value, - SharedPreferencesOptions options, - ) async { - return _api.setDouble(key, value, _convertOptionsToPigeonOptions(options)); - } - - @override - Future setBool( - String key, - bool value, - SharedPreferencesOptions options, - ) async { - return _api.setBool(key, value, _convertOptionsToPigeonOptions(options)); - } - - @override - Future setStringList( - String key, - List value, - SharedPreferencesOptions options, - ) async { - return _api.setStringList( - key, value, _convertOptionsToPigeonOptions(options)); - } - - @override - Future getString( - String key, - SharedPreferencesOptions options, - ) async { - return _convertKnownExceptions(() async => - _api.getString(key, _convertOptionsToPigeonOptions(options))); - } - - @override - Future getBool( - String key, - SharedPreferencesOptions options, - ) async { - return _convertKnownExceptions( - () async => _api.getBool(key, _convertOptionsToPigeonOptions(options))); - } - - @override - Future getDouble( - String key, - SharedPreferencesOptions options, - ) async { - return _convertKnownExceptions(() async => - _api.getDouble(key, _convertOptionsToPigeonOptions(options))); - } - - @override - Future getInt( - String key, - SharedPreferencesOptions options, - ) async { - return _convertKnownExceptions( - () async => _api.getInt(key, _convertOptionsToPigeonOptions(options))); - } - - @override - Future?> getStringList( - String key, - SharedPreferencesOptions options, - ) async { - // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 - // is fixed. In practice, the values will never be null, and the native implementation assumes that. - return _convertKnownExceptions>(() async => - (await _api.getStringList(key, _convertOptionsToPigeonOptions(options))) - ?.cast()); - } - - Future _convertKnownExceptions(Future Function() method) async { - try { - final T? value = await method(); - return value; - } on PlatformException catch (e) { - if (e.code == 'ClassCastException') { - throw TypeError(); - } else { - rethrow; - } - } - } - - @override - Future clear( - ClearPreferencesParameters parameters, - SharedPreferencesOptions options, - ) async { - final PreferencesFilters filter = parameters.filter; - return _api.clear( - filter.allowList?.toList(), - _convertOptionsToPigeonOptions(options), - ); - } - - @override - Future> getPreferences( - GetPreferencesParameters parameters, - SharedPreferencesOptions options, - ) async { - final PreferencesFilters filter = parameters.filter; - final Map data = await _api.getAll( - filter.allowList?.toList(), - _convertOptionsToPigeonOptions(options), - ); - return data.cast(); - } -} - -/// Options for the Android specific SharedPreferences plugin. -class SharedPreferencesAndroidOptions extends SharedPreferencesOptions { - /// Constructor for SharedPreferencesAndroidOptions. - const SharedPreferencesAndroidOptions(); -} +export 'src/shared_preferences_android.dart'; +export 'src/shared_preferences_async_android.dart'; diff --git a/packages/shared_preferences/shared_preferences_android/lib/legacy_shared_preferences_android.dart b/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_android.dart similarity index 93% rename from packages/shared_preferences/shared_preferences_android/lib/legacy_shared_preferences_android.dart rename to packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_android.dart index 942f81223a0..1f1d29b576e 100644 --- a/packages/shared_preferences/shared_preferences_android/lib/legacy_shared_preferences_android.dart +++ b/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_android.dart @@ -7,14 +7,14 @@ import 'package:flutter/services.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; -import 'src/messages.g.dart'; +import 'messages.g.dart'; /// The Android implementation of [SharedPreferencesStorePlatform]. /// /// This class implements the `package:shared_preferences` functionality for Android. -class LegacySharedPreferencesAndroid extends SharedPreferencesStorePlatform { +class SharedPreferencesAndroid extends SharedPreferencesStorePlatform { /// Creates a new plugin implementation instance. - LegacySharedPreferencesAndroid({ + SharedPreferencesAndroid({ @visibleForTesting SharedPreferencesApi? api, }) : _api = api ?? SharedPreferencesApi(); @@ -22,7 +22,7 @@ class LegacySharedPreferencesAndroid extends SharedPreferencesStorePlatform { /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. static void registerWith() { - SharedPreferencesStorePlatform.instance = LegacySharedPreferencesAndroid(); + SharedPreferencesStorePlatform.instance = SharedPreferencesAndroid(); } static const String _defaultPrefix = 'flutter.'; diff --git a/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_async_android.dart b/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_async_android.dart new file mode 100644 index 00000000000..e04bc799298 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_async_android.dart @@ -0,0 +1,198 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; + +import 'messages_async.g.dart'; +import 'shared_preferences_android.dart'; + +const String _listPrefix = 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu'; + +/// The Android implementation of [SharedPreferencesAsyncPlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for Android. +base class SharedPreferencesAsyncAndroid + extends SharedPreferencesAsyncPlatform { + /// Creates a new plugin implementation instance. + SharedPreferencesAsyncAndroid({ + @visibleForTesting SharedPreferencesAsyncApi? api, + }) : _api = api ?? SharedPreferencesAsyncApi(); + + final SharedPreferencesAsyncApi _api; + + /// Registers this class as the default instance of [SharedPreferencesAsyncPlatform]. + static void registerWith() { + SharedPreferencesAsyncPlatform.instance = SharedPreferencesAsyncAndroid(); + // A temporary work-around for having two plugins contained in a single package. + SharedPreferencesAndroid.registerWith(); + } + + /// Returns a SharedPreferencesPigeonOptions for sending to platform. + SharedPreferencesPigeonOptions _convertOptionsToPigeonOptions( + SharedPreferencesOptions options) { + return SharedPreferencesPigeonOptions(); + } + + @override + Future> getKeys( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 + // is fixed. In practice, the values will never be null, and the native implementation assumes that. + return (await _api.getKeys( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + )) + .cast() + .toSet(); + } + + @override + Future setString( + String key, + String value, + SharedPreferencesOptions options, + ) async { + if (value.startsWith(_listPrefix)) { + throw ArgumentError( + 'StorageError: This string cannot be stored as it clashes with special identifier prefixes'); + } + + return _api.setString(key, value, _convertOptionsToPigeonOptions(options)); + } + + @override + Future setInt( + String key, + int value, + SharedPreferencesOptions options, + ) async { + return _api.setInt(key, value, _convertOptionsToPigeonOptions(options)); + } + + @override + Future setDouble( + String key, + double value, + SharedPreferencesOptions options, + ) async { + return _api.setDouble(key, value, _convertOptionsToPigeonOptions(options)); + } + + @override + Future setBool( + String key, + bool value, + SharedPreferencesOptions options, + ) async { + return _api.setBool(key, value, _convertOptionsToPigeonOptions(options)); + } + + @override + Future setStringList( + String key, + List value, + SharedPreferencesOptions options, + ) async { + return _api.setStringList( + key, value, _convertOptionsToPigeonOptions(options)); + } + + @override + Future getString( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => + _api.getString(key, _convertOptionsToPigeonOptions(options))); + } + + @override + Future getBool( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions( + () async => _api.getBool(key, _convertOptionsToPigeonOptions(options))); + } + + @override + Future getDouble( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => + _api.getDouble(key, _convertOptionsToPigeonOptions(options))); + } + + @override + Future getInt( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions( + () async => _api.getInt(key, _convertOptionsToPigeonOptions(options))); + } + + @override + Future?> getStringList( + String key, + SharedPreferencesOptions options, + ) async { + // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 + // is fixed. In practice, the values will never be null, and the native implementation assumes that. + return _convertKnownExceptions>(() async => + (await _api.getStringList(key, _convertOptionsToPigeonOptions(options))) + ?.cast()); + } + + Future _convertKnownExceptions(Future Function() method) async { + try { + final T? value = await method(); + return value; + } on PlatformException catch (e) { + if (e.code == 'ClassCastException') { + throw TypeError(); + } else { + rethrow; + } + } + } + + @override + Future clear( + ClearPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + return _api.clear( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + ); + } + + @override + Future> getPreferences( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + final Map data = await _api.getAll( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + ); + return data.cast(); + } +} + +/// Options for the Android specific SharedPreferences plugin. +class SharedPreferencesAsyncAndroidOptions extends SharedPreferencesOptions { + /// Constructor for SharedPreferencesAsyncAndroidOptions. + const SharedPreferencesAsyncAndroidOptions(); +} diff --git a/packages/shared_preferences/shared_preferences_android/test/legacy_shared_preferences_android_test.dart b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart similarity index 96% rename from packages/shared_preferences/shared_preferences_android/test/legacy_shared_preferences_android_test.dart rename to packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart index 241f11cc099..c30fa08d18d 100644 --- a/packages/shared_preferences/shared_preferences_android/test/legacy_shared_preferences_android_test.dart +++ b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart @@ -4,7 +4,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:shared_preferences_android/legacy_shared_preferences_android.dart'; +import 'package:shared_preferences_android/shared_preferences_android.dart'; import 'package:shared_preferences_android/src/messages.g.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; @@ -12,7 +12,7 @@ import 'package:shared_preferences_platform_interface/types.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); late _FakeSharedPreferencesApi api; - late LegacySharedPreferencesAndroid plugin; + late SharedPreferencesAndroid plugin; const Map flutterTestValues = { 'flutter.String': 'hello world', @@ -46,13 +46,13 @@ void main() { setUp(() { api = _FakeSharedPreferencesApi(); - plugin = LegacySharedPreferencesAndroid(api: api); + plugin = SharedPreferencesAndroid(api: api); }); test('registerWith', () async { - LegacySharedPreferencesAndroid.registerWith(); + SharedPreferencesAndroid.registerWith(); expect(SharedPreferencesStorePlatform.instance, - isA()); + isA()); }); test('remove', () async { diff --git a/packages/shared_preferences/shared_preferences_android/test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_async_test.dart similarity index 89% rename from packages/shared_preferences/shared_preferences_android/test/shared_preferences_test.dart rename to packages/shared_preferences/shared_preferences_android/test/shared_preferences_async_test.dart index 6951e8d9dd4..45ee340a7cf 100755 --- a/packages/shared_preferences/shared_preferences_android/test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_async_test.dart @@ -22,54 +22,54 @@ void main() { const double testDouble = 3.14159; const List testList = ['foo', 'bar']; - const SharedPreferencesAndroidOptions emptyOptions = - SharedPreferencesAndroidOptions(); + const SharedPreferencesAsyncAndroidOptions emptyOptions = + SharedPreferencesAsyncAndroidOptions(); - SharedPreferencesAndroid getPreferences() { + SharedPreferencesAsyncAndroid getPreferences() { final _FakeSharedPreferencesApi api = _FakeSharedPreferencesApi(); - final SharedPreferencesAndroid preferences = - SharedPreferencesAndroid(api: api); + final SharedPreferencesAsyncAndroid preferences = + SharedPreferencesAsyncAndroid(api: api); return preferences; } test('set and get String', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await preferences.setString(stringKey, testString, emptyOptions); expect(await preferences.getString(stringKey, emptyOptions), testString); }); test('set and get bool', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await preferences.setBool(boolKey, testBool, emptyOptions); expect(await preferences.getBool(boolKey, emptyOptions), testBool); }); test('set and get int', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await preferences.setInt(intKey, testInt, emptyOptions); expect(await preferences.getInt(intKey, emptyOptions), testInt); }); test('set and get double', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await preferences.setDouble(doubleKey, testDouble, emptyOptions); expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); }); test('set and get StringList', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await preferences.setStringList(listKey, testList, emptyOptions); expect(await preferences.getStringList(listKey, emptyOptions), testList); }); test('getPreferences', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await Future.wait(>[ preferences.setString(stringKey, testString, emptyOptions), preferences.setBool(boolKey, testBool, emptyOptions), @@ -91,7 +91,7 @@ void main() { }); test('getPreferences with filter', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await Future.wait(>[ preferences.setString(stringKey, testString, emptyOptions), preferences.setBool(boolKey, testBool, emptyOptions), @@ -112,7 +112,7 @@ void main() { }); test('getKeys', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await Future.wait(>[ preferences.setString(stringKey, testString, emptyOptions), preferences.setBool(boolKey, testBool, emptyOptions), @@ -135,7 +135,7 @@ void main() { }); test('getKeys with filter', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await Future.wait(>[ preferences.setString(stringKey, testString, emptyOptions), preferences.setBool(boolKey, testBool, emptyOptions), @@ -157,7 +157,7 @@ void main() { }); test('clear', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await Future.wait(>[ preferences.setString(stringKey, testString, emptyOptions), preferences.setBool(boolKey, testBool, emptyOptions), @@ -176,7 +176,7 @@ void main() { }); test('clear with filter', () async { - final SharedPreferencesAndroid preferences = getPreferences(); + final SharedPreferencesAsyncAndroid preferences = getPreferences(); await Future.wait(>[ preferences.setString(stringKey, testString, emptyOptions), preferences.setBool(boolKey, testBool, emptyOptions), diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/messages.g.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/messages.g.swift index 65327a40513..1c843a6522c 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/messages.g.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/messages.g.swift @@ -59,7 +59,7 @@ struct SharedPreferencesPigeonOptions { ] } } -private class DeprecatedUserDefaultsApiCodecReader: FlutterStandardReader { +private class LegacyUserDefaultsApiCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { case 128: @@ -70,7 +70,7 @@ private class DeprecatedUserDefaultsApiCodecReader: FlutterStandardReader { } } -private class DeprecatedUserDefaultsApiCodecWriter: FlutterStandardWriter { +private class LegacyUserDefaultsApiCodecWriter: FlutterStandardWriter { override func writeValue(_ value: Any) { if let value = value as? SharedPreferencesPigeonOptions { super.writeByte(128) @@ -81,23 +81,23 @@ private class DeprecatedUserDefaultsApiCodecWriter: FlutterStandardWriter { } } -private class DeprecatedUserDefaultsApiCodecReaderWriter: FlutterStandardReaderWriter { +private class LegacyUserDefaultsApiCodecReaderWriter: FlutterStandardReaderWriter { override func reader(with data: Data) -> FlutterStandardReader { - return DeprecatedUserDefaultsApiCodecReader(data: data) + return LegacyUserDefaultsApiCodecReader(data: data) } override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return DeprecatedUserDefaultsApiCodecWriter(data: data) + return LegacyUserDefaultsApiCodecWriter(data: data) } } -class DeprecatedUserDefaultsApiCodec: FlutterStandardMessageCodec { - static let shared = DeprecatedUserDefaultsApiCodec( - readerWriter: DeprecatedUserDefaultsApiCodecReaderWriter()) +class LegacyUserDefaultsApiCodec: FlutterStandardMessageCodec { + static let shared = LegacyUserDefaultsApiCodec( + readerWriter: LegacyUserDefaultsApiCodecReaderWriter()) } /// Generated protocol from Pigeon that represents a handler of messages from Flutter. -protocol DeprecatedUserDefaultsApi { +protocol LegacyUserDefaultsApi { func remove(key: String) throws func setBool(key: String, value: Bool) throws func setDouble(key: String, value: Double) throws @@ -107,13 +107,13 @@ protocol DeprecatedUserDefaultsApi { } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. -class DeprecatedUserDefaultsApiSetup { - /// The codec used by DeprecatedUserDefaultsApi. - static var codec: FlutterStandardMessageCodec { DeprecatedUserDefaultsApiCodec.shared } - /// Sets up an instance of `DeprecatedUserDefaultsApi` to handle messages through the `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: DeprecatedUserDefaultsApi?) { +class LegacyUserDefaultsApiSetup { + /// The codec used by LegacyUserDefaultsApi. + static var codec: FlutterStandardMessageCodec { LegacyUserDefaultsApiCodec.shared } + /// Sets up an instance of `LegacyUserDefaultsApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: LegacyUserDefaultsApi?) { let removeChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove", + name: "dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.remove", binaryMessenger: binaryMessenger, codec: codec) if let api = api { removeChannel.setMessageHandler { message, reply in @@ -130,7 +130,7 @@ class DeprecatedUserDefaultsApiSetup { removeChannel.setMessageHandler(nil) } let setBoolChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool", + name: "dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setBool", binaryMessenger: binaryMessenger, codec: codec) if let api = api { setBoolChannel.setMessageHandler { message, reply in @@ -148,7 +148,7 @@ class DeprecatedUserDefaultsApiSetup { setBoolChannel.setMessageHandler(nil) } let setDoubleChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble", + name: "dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setDouble", binaryMessenger: binaryMessenger, codec: codec) if let api = api { setDoubleChannel.setMessageHandler { message, reply in @@ -166,7 +166,7 @@ class DeprecatedUserDefaultsApiSetup { setDoubleChannel.setMessageHandler(nil) } let setValueChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue", + name: "dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setValue", binaryMessenger: binaryMessenger, codec: codec) if let api = api { setValueChannel.setMessageHandler { message, reply in @@ -184,7 +184,7 @@ class DeprecatedUserDefaultsApiSetup { setValueChannel.setMessageHandler(nil) } let getAllChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll", + name: "dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.getAll", binaryMessenger: binaryMessenger, codec: codec) if let api = api { getAllChannel.setMessageHandler { message, reply in @@ -202,7 +202,7 @@ class DeprecatedUserDefaultsApiSetup { getAllChannel.setMessageHandler(nil) } let clearChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear", + name: "dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.clear", binaryMessenger: binaryMessenger, codec: codec) if let api = api { clearChannel.setMessageHandler { message, reply in diff --git a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart index 3bc0f156607..67777288d98 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart @@ -12,7 +12,7 @@ import 'package:shared_preferences_platform_interface/types.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - group('SharedPreferencesFoundation', () { + group('SharedPreferencesAsyncFoundation', () { const Map flutterTestValues = { 'flutter.String': 'hello world', 'flutter.Bool': true, @@ -485,8 +485,8 @@ void main() { }); group('shared_preferences_async', () { - final SharedPreferencesFoundationOptions emptyOptions = - SharedPreferencesFoundationOptions(); + final SharedPreferencesAsyncFoundationOptions emptyOptions = + SharedPreferencesAsyncFoundationOptions(); const String stringKey = 'testString'; const String boolKey = 'testBool'; diff --git a/packages/shared_preferences/shared_preferences_foundation/example/lib/main.dart b/packages/shared_preferences/shared_preferences_foundation/example/lib/main.dart index c58b785d551..6623eb653f1 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/lib/main.dart +++ b/packages/shared_preferences/shared_preferences_foundation/example/lib/main.dart @@ -34,8 +34,8 @@ class SharedPreferencesDemo extends StatefulWidget { class SharedPreferencesDemoState extends State { final SharedPreferencesAsyncPlatform? _prefs = SharedPreferencesAsyncPlatform.instance; - SharedPreferencesFoundationOptions options = - SharedPreferencesFoundationOptions(); + SharedPreferencesAsyncFoundationOptions options = + SharedPreferencesAsyncFoundationOptions(); static const String _counterKey = 'counter'; late Future _counter; diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart b/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart index a3149582712..43234666b9c 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart @@ -2,236 +2,5 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; -import 'package:shared_preferences_platform_interface/types.dart'; - -import './messages.g.dart'; -import 'legacy_shared_preferences_foundation.dart'; - -const String _argumentErrorCode = 'Argument Error'; - -/// iOS and macOS implementation of shared_preferences. -base class SharedPreferencesFoundation extends SharedPreferencesAsyncPlatform { - /// Creates a new plugin implementation instance. - SharedPreferencesFoundation({ - @visibleForTesting UserDefaultsApi? api, - }) : _api = api ?? UserDefaultsApi(); - - final UserDefaultsApi _api; - - /// Registers this class as the default instance of [SharedPreferencesAsyncPlatform]. - static void registerWith() { - SharedPreferencesAsyncPlatform.instance = SharedPreferencesFoundation(); - // A temporary work-around for having two plugins contained in a single package. - LegacySharedPreferencesFoundation.registerWith(); - } - - /// Returns a SharedPreferencesPigeonOptions for sending to platform. - SharedPreferencesPigeonOptions _convertOptionsToPigeonOptions( - SharedPreferencesOptions options) { - if (options is SharedPreferencesFoundationOptions) { - final String? suiteName = options.suiteName; - return SharedPreferencesPigeonOptions( - suiteName: suiteName, - ); - } - return SharedPreferencesPigeonOptions(); - } - - @override - Future> getKeys( - GetPreferencesParameters parameters, - SharedPreferencesOptions options, - ) async { - final PreferencesFilters filter = parameters.filter; - // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 - // is fixed. In practice, the values will never be null, and the native implementation assumes that. - return (await _convertKnownExceptions>( - () async => (await _api.getKeys( - filter.allowList?.toList(), - _convertOptionsToPigeonOptions(options), - )) - .cast()))! - .toSet(); - } - - Future _setValue( - String key, - Object value, - SharedPreferencesOptions options, - ) async { - return _convertKnownExceptions(() async => - _api.set(key, value, _convertOptionsToPigeonOptions(options))); - } - - @override - Future setString( - String key, - String value, - SharedPreferencesOptions options, - ) async { - await _setValue(key, value, options); - } - - @override - Future setInt( - String key, - int value, - SharedPreferencesOptions options, - ) async { - await _setValue(key, value, options); - } - - @override - Future setStringList( - String key, - List value, - SharedPreferencesOptions options, - ) async { - await _setValue(key, value, options); - } - - @override - Future setBool( - String key, - bool value, - SharedPreferencesOptions options, - ) async { - await _api.set(key, value, _convertOptionsToPigeonOptions(options)); - } - - @override - Future setDouble( - String key, - double value, - SharedPreferencesOptions options, - ) async { - await _api.set(key, value, _convertOptionsToPigeonOptions(options)); - } - - @override - Future getString( - String key, - SharedPreferencesOptions options, - ) async { - return _convertKnownExceptions(() async => (await _api.getValue( - key, _convertOptionsToPigeonOptions(options))) as String?); - } - - @override - Future getBool( - String key, - SharedPreferencesOptions options, - ) async { - return _convertKnownExceptions(() async => await _api.getValue( - key, _convertOptionsToPigeonOptions(options)) as bool?); - } - - @override - Future getDouble( - String key, - SharedPreferencesOptions options, - ) async { - return _convertKnownExceptions(() async => await _api.getValue( - key, _convertOptionsToPigeonOptions(options)) as double?); - } - - @override - Future getInt( - String key, - SharedPreferencesOptions options, - ) async { - return _convertKnownExceptions(() async => await _api.getValue( - key, _convertOptionsToPigeonOptions(options)) as int?); - } - - @override - Future?> getStringList( - String key, - SharedPreferencesOptions options, - ) async { - // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 - // is fixed. In practice, the values will never be null, and the native implementation assumes that. - return _convertKnownExceptions>(() async => - ((await _api.getValue(key, _convertOptionsToPigeonOptions(options))) - as List?) - ?.cast()); - } - - @override - Future clear( - ClearPreferencesParameters parameters, - SharedPreferencesOptions options, - ) async { - final PreferencesFilters filter = parameters.filter; - return _convertKnownExceptions(() async => _api.clear( - filter.allowList?.toList(), - _convertOptionsToPigeonOptions(options), - )); - } - - @override - Future> getPreferences( - GetPreferencesParameters parameters, - SharedPreferencesOptions options, - ) async { - final PreferencesFilters filter = parameters.filter; - final Map? data = - await _convertKnownExceptions>( - () async => _api.getAll( - filter.allowList?.toList(), - _convertOptionsToPigeonOptions(options), - )); - - return data!.cast(); - } - - Future _convertKnownExceptions(Future Function() method) async { - try { - final T? value = await method(); - return value; - } on PlatformException catch (e) { - if (e.code == _argumentErrorCode) { - throw ArgumentError( - 'shared_preferences_foundation argument error ${e.message ?? ''}'); - } else { - rethrow; - } - } - } -} - -/// Options for the Foundation specific SharedPreferences plugin. -@immutable -class SharedPreferencesFoundationOptions extends SharedPreferencesOptions { - /// Creates a new instance with the given options. - SharedPreferencesFoundationOptions({ - this.suiteName, - }) { - if (Platform.isIOS && !(suiteName?.startsWith('group.') ?? true)) { - throw ArgumentError('iOS suite name must begin with "group."'); - } - } - - /// Name of Foundation suite to get/set to. - /// - /// On iOS this represents a container ID which must begin with `group.` - /// followed by a custom string in reverse DNS notation. - /// - /// If this option is not set, the default NSUserDefaults will be used. - final String? suiteName; - - /// Returns a new instance of [SharedPreferencesFoundationOptions] from an existing - /// [SharedPreferencesOptions]. - static SharedPreferencesFoundationOptions fromSharedPreferencesOptions( - SharedPreferencesOptions options) { - if (options is SharedPreferencesFoundationOptions) { - return options; - } - return SharedPreferencesFoundationOptions(); - } -} +export 'src/shared_preferences_async_foundation.dart'; +export 'src/shared_preferences_foundation.dart'; diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart b/packages/shared_preferences/shared_preferences_foundation/lib/src/messages.g.dart similarity index 94% rename from packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart rename to packages/shared_preferences/shared_preferences_foundation/lib/src/messages.g.dart index d0f34b2a503..6594deec3bb 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/src/messages.g.dart @@ -50,8 +50,8 @@ class SharedPreferencesPigeonOptions { } } -class _DeprecatedUserDefaultsApiCodec extends StandardMessageCodec { - const _DeprecatedUserDefaultsApiCodec(); +class _LegacyUserDefaultsApiCodec extends StandardMessageCodec { + const _LegacyUserDefaultsApiCodec(); @override void writeValue(WriteBuffer buffer, Object? value) { if (value is SharedPreferencesPigeonOptions) { @@ -73,20 +73,20 @@ class _DeprecatedUserDefaultsApiCodec extends StandardMessageCodec { } } -class DeprecatedUserDefaultsApi { - /// Constructor for [DeprecatedUserDefaultsApi]. The [binaryMessenger] named argument is +class LegacyUserDefaultsApi { + /// Constructor for [LegacyUserDefaultsApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - DeprecatedUserDefaultsApi({BinaryMessenger? binaryMessenger}) + LegacyUserDefaultsApi({BinaryMessenger? binaryMessenger}) : __pigeon_binaryMessenger = binaryMessenger; final BinaryMessenger? __pigeon_binaryMessenger; static const MessageCodec pigeonChannelCodec = - _DeprecatedUserDefaultsApiCodec(); + _LegacyUserDefaultsApiCodec(); Future remove(String key) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove'; + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.remove'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -110,7 +110,7 @@ class DeprecatedUserDefaultsApi { Future setBool(String key, bool value) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool'; + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setBool'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -134,7 +134,7 @@ class DeprecatedUserDefaultsApi { Future setDouble(String key, double value) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble'; + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setDouble'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -158,7 +158,7 @@ class DeprecatedUserDefaultsApi { Future setValue(String key, Object value) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue'; + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setValue'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -183,7 +183,7 @@ class DeprecatedUserDefaultsApi { Future> getAll( String prefix, List? allowList) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll'; + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.getAll'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, @@ -213,7 +213,7 @@ class DeprecatedUserDefaultsApi { Future clear(String prefix, List? allowList) async { const String __pigeon_channelName = - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear'; + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.clear'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( __pigeon_channelName, diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_async_foundation.dart b/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_async_foundation.dart new file mode 100644 index 00000000000..dbe90c53dbe --- /dev/null +++ b/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_async_foundation.dart @@ -0,0 +1,241 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; + +import './messages.g.dart'; +import './shared_preferences_foundation.dart'; + +const String _argumentErrorCode = 'Argument Error'; + +/// iOS and macOS implementation of shared_preferences. +base class SharedPreferencesAsyncFoundation + extends SharedPreferencesAsyncPlatform { + /// Creates a new plugin implementation instance. + SharedPreferencesAsyncFoundation({ + @visibleForTesting UserDefaultsApi? api, + }) : _api = api ?? UserDefaultsApi(); + + final UserDefaultsApi _api; + + /// Registers this class as the default instance of [SharedPreferencesAsyncPlatform]. + static void registerWith() { + SharedPreferencesAsyncPlatform.instance = + SharedPreferencesAsyncFoundation(); + // A temporary work-around for having two plugins contained in a single package. + SharedPreferencesFoundation.registerWith(); + } + + /// Returns a SharedPreferencesPigeonOptions for sending to platform. + SharedPreferencesPigeonOptions _convertOptionsToPigeonOptions( + SharedPreferencesOptions options) { + if (options is SharedPreferencesAsyncFoundationOptions) { + final String? suiteName = options.suiteName; + return SharedPreferencesPigeonOptions( + suiteName: suiteName, + ); + } + return SharedPreferencesPigeonOptions(); + } + + @override + Future> getKeys( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 + // is fixed. In practice, the values will never be null, and the native implementation assumes that. + return (await _convertKnownExceptions>( + () async => (await _api.getKeys( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + )) + .cast()))! + .toSet(); + } + + Future _setValue( + String key, + Object value, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => + _api.set(key, value, _convertOptionsToPigeonOptions(options))); + } + + @override + Future setString( + String key, + String value, + SharedPreferencesOptions options, + ) async { + await _setValue(key, value, options); + } + + @override + Future setInt( + String key, + int value, + SharedPreferencesOptions options, + ) async { + await _setValue(key, value, options); + } + + @override + Future setStringList( + String key, + List value, + SharedPreferencesOptions options, + ) async { + await _setValue(key, value, options); + } + + @override + Future setBool( + String key, + bool value, + SharedPreferencesOptions options, + ) async { + await _api.set(key, value, _convertOptionsToPigeonOptions(options)); + } + + @override + Future setDouble( + String key, + double value, + SharedPreferencesOptions options, + ) async { + await _api.set(key, value, _convertOptionsToPigeonOptions(options)); + } + + @override + Future getString( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => (await _api.getValue( + key, _convertOptionsToPigeonOptions(options))) as String?); + } + + @override + Future getBool( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => await _api.getValue( + key, _convertOptionsToPigeonOptions(options)) as bool?); + } + + @override + Future getDouble( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => await _api.getValue( + key, _convertOptionsToPigeonOptions(options)) as double?); + } + + @override + Future getInt( + String key, + SharedPreferencesOptions options, + ) async { + return _convertKnownExceptions(() async => await _api.getValue( + key, _convertOptionsToPigeonOptions(options)) as int?); + } + + @override + Future?> getStringList( + String key, + SharedPreferencesOptions options, + ) async { + // TODO(tarrinneal): Remove cast once https://github.com/flutter/flutter/issues/97848 + // is fixed. In practice, the values will never be null, and the native implementation assumes that. + return _convertKnownExceptions>(() async => + ((await _api.getValue(key, _convertOptionsToPigeonOptions(options))) + as List?) + ?.cast()); + } + + @override + Future clear( + ClearPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + return _convertKnownExceptions(() async => _api.clear( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + )); + } + + @override + Future> getPreferences( + GetPreferencesParameters parameters, + SharedPreferencesOptions options, + ) async { + final PreferencesFilters filter = parameters.filter; + final Map? data = + await _convertKnownExceptions>( + () async => _api.getAll( + filter.allowList?.toList(), + _convertOptionsToPigeonOptions(options), + )); + + return data!.cast(); + } + + Future _convertKnownExceptions(Future Function() method) async { + try { + final T? value = await method(); + return value; + } on PlatformException catch (e) { + if (e.code == _argumentErrorCode) { + throw ArgumentError( + 'shared_preferences_foundation argument error ${e.message ?? ''}'); + } else { + rethrow; + } + } + } +} + +/// Options for the Foundation specific SharedPreferences plugin. +@immutable +class SharedPreferencesAsyncFoundationOptions extends SharedPreferencesOptions { + /// Creates a new instance with the given options. + SharedPreferencesAsyncFoundationOptions({ + this.suiteName, + }) { + // Ensure that use of suite is compliant with required reason API category 1C8F.1; see + // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api + if (Platform.isIOS && !(suiteName?.startsWith('group.') ?? true)) { + throw ArgumentError('iOS suite name must begin with "group."'); + } + } + + /// Name of Foundation suite to get/set to. + /// + /// On iOS this represents a container ID which must begin with `group.` + /// followed by a custom string in reverse DNS notation. + /// + /// If this option is not set, the default NSUserDefaults will be used. + final String? suiteName; + + /// Returns a new instance of [SharedPreferencesAsyncFoundationOptions] from an existing + /// [SharedPreferencesOptions]. + static SharedPreferencesAsyncFoundationOptions fromSharedPreferencesOptions( + SharedPreferencesOptions options) { + if (options is SharedPreferencesAsyncFoundationOptions) { + return options; + } + return SharedPreferencesAsyncFoundationOptions(); + } +} diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/legacy_shared_preferences_foundation.dart b/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_foundation.dart similarity index 91% rename from packages/shared_preferences/shared_preferences_foundation/lib/legacy_shared_preferences_foundation.dart rename to packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_foundation.dart index d8e0cb277bc..0632e8bdeb9 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/legacy_shared_preferences_foundation.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_foundation.dart @@ -5,13 +5,13 @@ import 'package:flutter/services.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; -import 'messages.g.dart'; +import './messages.g.dart'; typedef _Setter = Future Function(String key, Object value); /// iOS and macOS implementation of shared_preferences. -class LegacySharedPreferencesFoundation extends SharedPreferencesStorePlatform { - final DeprecatedUserDefaultsApi _api = DeprecatedUserDefaultsApi(); +class SharedPreferencesFoundation extends SharedPreferencesStorePlatform { + final LegacyUserDefaultsApi _api = LegacyUserDefaultsApi(); static const String _defaultPrefix = 'flutter.'; @@ -36,8 +36,7 @@ class LegacySharedPreferencesFoundation extends SharedPreferencesStorePlatform { /// Registers this class as the default instance of /// [SharedPreferencesStorePlatform]. static void registerWith() { - SharedPreferencesStorePlatform.instance = - LegacySharedPreferencesFoundation(); + SharedPreferencesStorePlatform.instance = SharedPreferencesFoundation(); } @override diff --git a/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart b/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart index 464e6a4599d..96430e55662 100644 --- a/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart +++ b/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart @@ -5,14 +5,14 @@ import 'package:pigeon/pigeon.dart'; @ConfigurePigeon(PigeonOptions( - dartOut: 'lib/messages.g.dart', + dartOut: 'lib/src/messages.g.dart', dartTestOut: 'test/test_api.g.dart', swiftOut: 'darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/messages.g.swift', copyrightHeader: 'pigeons/copyright_header.txt', )) @HostApi(dartHostTestHandler: 'TestUserDefaultsApi') -abstract class DeprecatedUserDefaultsApi { +abstract class LegacyUserDefaultsApi { void remove(String key); void setBool(String key, bool value); void setDouble(String key, double value); diff --git a/packages/shared_preferences/shared_preferences_foundation/test/legacy_shared_preferences_foundation_test.dart b/packages/shared_preferences/shared_preferences_foundation/test/legacy_shared_preferences_foundation_test.dart deleted file mode 100644 index 6c0a4ef2782..00000000000 --- a/packages/shared_preferences/shared_preferences_foundation/test/legacy_shared_preferences_foundation_test.dart +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:shared_preferences_foundation/legacy_shared_preferences_foundation.dart'; -import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; -import 'package:shared_preferences_platform_interface/types.dart'; - -import 'test_api.g.dart'; - -class _MockSharedPreferencesApi implements TestUserDefaultsApi { - final Map items = {}; - - @override - Map getAll( - String prefix, - List? allowList, - ) { - Set? allowSet; - if (allowList != null) { - allowSet = Set.from(allowList); - } - return { - for (final String key in items.keys) - if (key.startsWith(prefix) && - (allowSet == null || allowSet.contains(key))) - key: items[key] - }; - } - - @override - void remove(String key) { - items.remove(key); - } - - @override - void setBool(String key, bool value) { - items[key] = value; - } - - @override - void setDouble(String key, double value) { - items[key] = value; - } - - @override - void setValue(String key, Object value) { - items[key] = value; - } - - @override - bool clear(String prefix, List? allowList) { - items.keys.toList().forEach((String key) { - if (key.startsWith(prefix) && - (allowList == null || allowList.contains(key))) { - items.remove(key); - } - }); - return true; - } -} - -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - late _MockSharedPreferencesApi api; - - const Map flutterTestValues = { - 'flutter.String': 'hello world', - 'flutter.Bool': true, - 'flutter.Int': 42, - 'flutter.Double': 3.14159, - 'flutter.StringList': ['foo', 'bar'], - }; - - const Map prefixTestValues = { - 'prefix.String': 'hello world', - 'prefix.Bool': true, - 'prefix.Int': 42, - 'prefix.Double': 3.14159, - 'prefix.StringList': ['foo', 'bar'], - }; - - const Map nonPrefixTestValues = { - 'String': 'hello world', - 'Bool': true, - 'Int': 42, - 'Double': 3.14159, - 'StringList': ['foo', 'bar'], - }; - - final Map allTestValues = {}; - - allTestValues.addAll(flutterTestValues); - allTestValues.addAll(prefixTestValues); - allTestValues.addAll(nonPrefixTestValues); - - setUp(() { - api = _MockSharedPreferencesApi(); - TestUserDefaultsApi.setup(api); - }); - - test('registerWith', () async { - LegacySharedPreferencesFoundation.registerWith(); - expect(SharedPreferencesStorePlatform.instance, - isA()); - }); - - test('remove', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - api.items['flutter.hi'] = 'world'; - expect(await plugin.remove('flutter.hi'), isTrue); - expect(api.items.containsKey('flutter.hi'), isFalse); - }); - - test('clear', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - api.items['flutter.hi'] = 'world'; - expect(await plugin.clear(), isTrue); - expect(api.items.containsKey('flutter.hi'), isFalse); - }); - - test('clearWithPrefix', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - - Map all = await plugin.getAllWithPrefix('prefix.'); - expect(all.length, 5); - await plugin.clearWithPrefix('prefix.'); - all = await plugin.getAll(); - expect(all.length, 5); - all = await plugin.getAllWithPrefix('prefix.'); - expect(all.length, 0); - }); - - test('clearWithParameters', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - - Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(all.length, 5); - await plugin.clearWithParameters( - ClearParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - all = await plugin.getAll(); - expect(all.length, 5); - all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(all.length, 0); - }); - - test('clearWithParameters with allow list', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - - Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(all.length, 5); - await plugin.clearWithParameters( - ClearParameters( - filter: PreferencesFilter( - prefix: 'prefix.', - allowList: {'prefix.String'}, - ), - ), - ); - all = await plugin.getAll(); - expect(all.length, 5); - all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(all.length, 4); - }); - - test('getAll', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in flutterTestValues.keys) { - api.items[key] = flutterTestValues[key]!; - } - final Map all = await plugin.getAll(); - expect(all.length, 5); - expect(all, flutterTestValues); - }); - - test('getAllWithPrefix', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - final Map all = await plugin.getAllWithPrefix('prefix.'); - expect(all.length, 5); - expect(all, prefixTestValues); - }); - - test('getAllWithParameters', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - final Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: 'prefix.'), - ), - ); - expect(all.length, 5); - expect(all, prefixTestValues); - }); - - test('getAllWithParameters with allow list', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - final Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter( - prefix: 'prefix.', - allowList: {'prefix.Bool'}, - ), - ), - ); - expect(all.length, 1); - expect(all['prefix.Bool'], prefixTestValues['prefix.Bool']); - }); - - test('setValue', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - expect(await plugin.setValue('Bool', 'flutter.Bool', true), isTrue); - expect(api.items['flutter.Bool'], true); - expect(await plugin.setValue('Double', 'flutter.Double', 1.5), isTrue); - expect(api.items['flutter.Double'], 1.5); - expect(await plugin.setValue('Int', 'flutter.Int', 12), isTrue); - expect(api.items['flutter.Int'], 12); - expect(await plugin.setValue('String', 'flutter.String', 'hi'), isTrue); - expect(api.items['flutter.String'], 'hi'); - expect( - await plugin - .setValue('StringList', 'flutter.StringList', ['hi']), - isTrue); - expect(api.items['flutter.StringList'], ['hi']); - }); - - test('setValue with unsupported type', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - expect(() async { - await plugin.setValue('Map', 'flutter.key', {}); - }, throwsA(isA())); - }); - - test('getAllWithNoPrefix', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - final Map all = await plugin.getAllWithPrefix(''); - expect(all.length, 15); - expect(all, allTestValues); - }); - - test('clearWithNoPrefix', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - - Map all = await plugin.getAllWithPrefix(''); - expect(all.length, 15); - await plugin.clearWithPrefix(''); - all = await plugin.getAllWithPrefix(''); - expect(all.length, 0); - }); - - test('getAllWithNoPrefix with param', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - final Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - expect(all.length, 15); - expect(all, allTestValues); - }); - - test('clearWithNoPrefix with param', () async { - final LegacySharedPreferencesFoundation plugin = - LegacySharedPreferencesFoundation(); - for (final String key in allTestValues.keys) { - api.items[key] = allTestValues[key]!; - } - - Map all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - expect(all.length, 15); - await plugin.clearWithParameters( - ClearParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - all = await plugin.getAllWithParameters( - GetAllParameters( - filter: PreferencesFilter(prefix: ''), - ), - ); - expect(all.length, 0); - }); -} diff --git a/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_async_foundation_test.dart b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_async_foundation_test.dart new file mode 100644 index 00000000000..74278d6b344 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_async_foundation_test.dart @@ -0,0 +1,247 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences_foundation/shared_preferences_foundation.dart'; +import 'package:shared_preferences_foundation/src/messages.g.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + const String stringKey = 'testString'; + const String boolKey = 'testBool'; + const String intKey = 'testInt'; + const String doubleKey = 'testDouble'; + const String listKey = 'testList'; + + const String testString = 'hello world'; + const bool testBool = true; + const int testInt = 42; + const double testDouble = 3.14159; + const List testList = ['foo', 'bar']; + + final SharedPreferencesAsyncFoundationOptions emptyOptions = + SharedPreferencesAsyncFoundationOptions(); + + SharedPreferencesAsyncFoundation getPreferences() { + final _FakeSharedPreferencesApi api = _FakeSharedPreferencesApi(); + final SharedPreferencesAsyncFoundation preferences = + SharedPreferencesAsyncFoundation(api: api); + + return preferences; + } + + test('set and get String', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + + await preferences.setString(stringKey, testString, emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), testString); + }); + + test('set and get bool', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + + await preferences.setBool(boolKey, testBool, emptyOptions); + expect(await preferences.getBool(boolKey, emptyOptions), testBool); + }); + + test('set and get int', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + + await preferences.setInt(intKey, testInt, emptyOptions); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + }); + + test('set and get double', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + + await preferences.setDouble(doubleKey, testDouble, emptyOptions); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + }); + + test('set and get StringList', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + + await preferences.setStringList(listKey, testList, emptyOptions); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); + + test('getPreferences', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + + expect(gotAll.length, 5); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + expect(gotAll[intKey], testInt); + expect(gotAll[doubleKey], testDouble); + expect(gotAll[listKey], testList); + }); + + test('getPreferences with filter', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Map gotAll = await preferences.getPreferences( + const GetPreferencesParameters( + filter: + PreferencesFilters(allowList: {stringKey, boolKey})), + emptyOptions); + + expect(gotAll.length, 2); + expect(gotAll[stringKey], testString); + expect(gotAll[boolKey], testBool); + }); + + test('getKeys', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters(filter: PreferencesFilters()), + emptyOptions, + ); + + expect(keys.length, 5); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + expect(keys, contains(intKey)); + expect(keys, contains(doubleKey)); + expect(keys, contains(listKey)); + }); + + test('getKeys with filter', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + + final Set keys = await preferences.getKeys( + const GetPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + + expect(keys.length, 2); + expect(keys, contains(stringKey)); + expect(keys, contains(boolKey)); + }); + + test('clear', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters(filter: PreferencesFilters()), + emptyOptions); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), null); + expect(await preferences.getDouble(doubleKey, emptyOptions), null); + expect(await preferences.getStringList(listKey, emptyOptions), null); + }); + + test('clear with filter', () async { + final SharedPreferencesAsyncFoundation preferences = getPreferences(); + await Future.wait(>[ + preferences.setString(stringKey, testString, emptyOptions), + preferences.setBool(boolKey, testBool, emptyOptions), + preferences.setInt(intKey, testInt, emptyOptions), + preferences.setDouble(doubleKey, testDouble, emptyOptions), + preferences.setStringList(listKey, testList, emptyOptions) + ]); + await preferences.clear( + const ClearPreferencesParameters( + filter: PreferencesFilters(allowList: {stringKey, boolKey}), + ), + emptyOptions, + ); + expect(await preferences.getString(stringKey, emptyOptions), null); + expect(await preferences.getBool(boolKey, emptyOptions), null); + expect(await preferences.getInt(intKey, emptyOptions), testInt); + expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + expect(await preferences.getStringList(listKey, emptyOptions), testList); + }); +} + +class _FakeSharedPreferencesApi implements UserDefaultsApi { + final Map items = {}; + + @override + Future clear( + List? allowList, SharedPreferencesPigeonOptions options) async { + if (allowList != null) { + items.removeWhere((String key, _) => allowList.contains(key)); + } else { + items.clear(); + } + + return true; + } + + @override + Future> getAll( + List? allowList, SharedPreferencesPigeonOptions options) async { + final Map filteredItems = {...items}; + if (allowList != null) { + filteredItems.removeWhere((String key, _) => !allowList.contains(key)); + } + return filteredItems; + } + + @override + Future> getKeys( + List? allowList, SharedPreferencesPigeonOptions options) async { + final List filteredItems = items.keys.toList(); + if (allowList != null) { + filteredItems.removeWhere((String key) => !allowList.contains(key)); + } + return filteredItems; + } + + @override + Future set( + String key, Object value, SharedPreferencesPigeonOptions options) async { + items[key] = value; + } + + @override + Future getValue( + String key, SharedPreferencesPigeonOptions options) async { + return items[key]; + } +} diff --git a/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart index c2a1807ca05..f733122e7ad 100644 --- a/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart @@ -2,246 +2,331 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:shared_preferences_foundation/messages.g.dart'; -import 'package:shared_preferences_foundation/shared_preferences_foundation.dart'; +import 'package:shared_preferences_foundation/src/shared_preferences_foundation.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; -void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - const String stringKey = 'testString'; - const String boolKey = 'testBool'; - const String intKey = 'testInt'; - const String doubleKey = 'testDouble'; - const String listKey = 'testList'; +import 'test_api.g.dart'; - const String testString = 'hello world'; - const bool testBool = true; - const int testInt = 42; - const double testDouble = 3.14159; - const List testList = ['foo', 'bar']; +class _MockSharedPreferencesApi implements TestUserDefaultsApi { + final Map items = {}; - final SharedPreferencesFoundationOptions emptyOptions = - SharedPreferencesFoundationOptions(); + @override + Map getAll( + String prefix, + List? allowList, + ) { + Set? allowSet; + if (allowList != null) { + allowSet = Set.from(allowList); + } + return { + for (final String key in items.keys) + if (key.startsWith(prefix) && + (allowSet == null || allowSet.contains(key))) + key: items[key] + }; + } - SharedPreferencesFoundation getPreferences() { - final _FakeSharedPreferencesApi api = _FakeSharedPreferencesApi(); - final SharedPreferencesFoundation preferences = - SharedPreferencesFoundation(api: api); + @override + void remove(String key) { + items.remove(key); + } - return preferences; + @override + void setBool(String key, bool value) { + items[key] = value; } - test('set and get String', () async { - final SharedPreferencesFoundation preferences = getPreferences(); + @override + void setDouble(String key, double value) { + items[key] = value; + } - await preferences.setString(stringKey, testString, emptyOptions); - expect(await preferences.getString(stringKey, emptyOptions), testString); - }); + @override + void setValue(String key, Object value) { + items[key] = value; + } - test('set and get bool', () async { - final SharedPreferencesFoundation preferences = getPreferences(); + @override + bool clear(String prefix, List? allowList) { + items.keys.toList().forEach((String key) { + if (key.startsWith(prefix) && + (allowList == null || allowList.contains(key))) { + items.remove(key); + } + }); + return true; + } +} - await preferences.setBool(boolKey, testBool, emptyOptions); - expect(await preferences.getBool(boolKey, emptyOptions), testBool); +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + late _MockSharedPreferencesApi api; + + const Map flutterTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + + const Map prefixTestValues = { + 'prefix.String': 'hello world', + 'prefix.Bool': true, + 'prefix.Int': 42, + 'prefix.Double': 3.14159, + 'prefix.StringList': ['foo', 'bar'], + }; + + const Map nonPrefixTestValues = { + 'String': 'hello world', + 'Bool': true, + 'Int': 42, + 'Double': 3.14159, + 'StringList': ['foo', 'bar'], + }; + + final Map allTestValues = {}; + + allTestValues.addAll(flutterTestValues); + allTestValues.addAll(prefixTestValues); + allTestValues.addAll(nonPrefixTestValues); + + setUp(() { + api = _MockSharedPreferencesApi(); + TestUserDefaultsApi.setup(api); }); - test('set and get int', () async { - final SharedPreferencesFoundation preferences = getPreferences(); - - await preferences.setInt(intKey, testInt, emptyOptions); - expect(await preferences.getInt(intKey, emptyOptions), testInt); + test('registerWith', () async { + SharedPreferencesFoundation.registerWith(); + expect(SharedPreferencesStorePlatform.instance, + isA()); }); - test('set and get double', () async { - final SharedPreferencesFoundation preferences = getPreferences(); + test('remove', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + api.items['flutter.hi'] = 'world'; + expect(await plugin.remove('flutter.hi'), isTrue); + expect(api.items.containsKey('flutter.hi'), isFalse); + }); - await preferences.setDouble(doubleKey, testDouble, emptyOptions); - expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); + test('clear', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + api.items['flutter.hi'] = 'world'; + expect(await plugin.clear(), isTrue); + expect(api.items.containsKey('flutter.hi'), isFalse); }); - test('set and get StringList', () async { - final SharedPreferencesFoundation preferences = getPreferences(); + test('clearWithPrefix', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } - await preferences.setStringList(listKey, testList, emptyOptions); - expect(await preferences.getStringList(listKey, emptyOptions), testList); + Map all = await plugin.getAllWithPrefix('prefix.'); + expect(all.length, 5); + await plugin.clearWithPrefix('prefix.'); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithPrefix('prefix.'); + expect(all.length, 0); }); - test('getPreferences', () async { - final SharedPreferencesFoundation preferences = getPreferences(); - await Future.wait(>[ - preferences.setString(stringKey, testString, emptyOptions), - preferences.setBool(boolKey, testBool, emptyOptions), - preferences.setInt(intKey, testInt, emptyOptions), - preferences.setDouble(doubleKey, testDouble, emptyOptions), - preferences.setStringList(listKey, testList, emptyOptions) - ]); - - final Map gotAll = await preferences.getPreferences( - const GetPreferencesParameters(filter: PreferencesFilters()), - emptyOptions); + test('clearWithParameters', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } - expect(gotAll.length, 5); - expect(gotAll[stringKey], testString); - expect(gotAll[boolKey], testBool); - expect(gotAll[intKey], testInt); - expect(gotAll[doubleKey], testDouble); - expect(gotAll[listKey], testList); + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 0); }); - test('getPreferences with filter', () async { - final SharedPreferencesFoundation preferences = getPreferences(); - await Future.wait(>[ - preferences.setString(stringKey, testString, emptyOptions), - preferences.setBool(boolKey, testBool, emptyOptions), - preferences.setInt(intKey, testInt, emptyOptions), - preferences.setDouble(doubleKey, testDouble, emptyOptions), - preferences.setStringList(listKey, testList, emptyOptions) - ]); + test('clearWithParameters with allow list', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } - final Map gotAll = await preferences.getPreferences( - const GetPreferencesParameters( - filter: - PreferencesFilters(allowList: {stringKey, boolKey})), - emptyOptions); + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 4); + }); - expect(gotAll.length, 2); - expect(gotAll[stringKey], testString); - expect(gotAll[boolKey], testBool); + test('getAll', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in flutterTestValues.keys) { + api.items[key] = flutterTestValues[key]!; + } + final Map all = await plugin.getAll(); + expect(all.length, 5); + expect(all, flutterTestValues); }); - test('getKeys', () async { - final SharedPreferencesFoundation preferences = getPreferences(); - await Future.wait(>[ - preferences.setString(stringKey, testString, emptyOptions), - preferences.setBool(boolKey, testBool, emptyOptions), - preferences.setInt(intKey, testInt, emptyOptions), - preferences.setDouble(doubleKey, testDouble, emptyOptions), - preferences.setStringList(listKey, testList, emptyOptions) - ]); + test('getAllWithPrefix', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithPrefix('prefix.'); + expect(all.length, 5); + expect(all, prefixTestValues); + }); - final Set keys = await preferences.getKeys( - const GetPreferencesParameters(filter: PreferencesFilters()), - emptyOptions, + test('getAllWithParameters', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), ); + expect(all.length, 5); + expect(all, prefixTestValues); + }); - expect(keys.length, 5); - expect(keys, contains(stringKey)); - expect(keys, contains(boolKey)); - expect(keys, contains(intKey)); - expect(keys, contains(doubleKey)); - expect(keys, contains(listKey)); - }); - - test('getKeys with filter', () async { - final SharedPreferencesFoundation preferences = getPreferences(); - await Future.wait(>[ - preferences.setString(stringKey, testString, emptyOptions), - preferences.setBool(boolKey, testBool, emptyOptions), - preferences.setInt(intKey, testInt, emptyOptions), - preferences.setDouble(doubleKey, testDouble, emptyOptions), - preferences.setStringList(listKey, testList, emptyOptions) - ]); - - final Set keys = await preferences.getKeys( - const GetPreferencesParameters( - filter: PreferencesFilters(allowList: {stringKey, boolKey}), + test('getAllWithParameters with allow list', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.Bool'}, + ), ), - emptyOptions, ); + expect(all.length, 1); + expect(all['prefix.Bool'], prefixTestValues['prefix.Bool']); + }); - expect(keys.length, 2); - expect(keys, contains(stringKey)); - expect(keys, contains(boolKey)); + test('setValue', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + expect(await plugin.setValue('Bool', 'flutter.Bool', true), isTrue); + expect(api.items['flutter.Bool'], true); + expect(await plugin.setValue('Double', 'flutter.Double', 1.5), isTrue); + expect(api.items['flutter.Double'], 1.5); + expect(await plugin.setValue('Int', 'flutter.Int', 12), isTrue); + expect(api.items['flutter.Int'], 12); + expect(await plugin.setValue('String', 'flutter.String', 'hi'), isTrue); + expect(api.items['flutter.String'], 'hi'); + expect( + await plugin + .setValue('StringList', 'flutter.StringList', ['hi']), + isTrue); + expect(api.items['flutter.StringList'], ['hi']); }); - test('clear', () async { - final SharedPreferencesFoundation preferences = getPreferences(); - await Future.wait(>[ - preferences.setString(stringKey, testString, emptyOptions), - preferences.setBool(boolKey, testBool, emptyOptions), - preferences.setInt(intKey, testInt, emptyOptions), - preferences.setDouble(doubleKey, testDouble, emptyOptions), - preferences.setStringList(listKey, testList, emptyOptions) - ]); - await preferences.clear( - const ClearPreferencesParameters(filter: PreferencesFilters()), - emptyOptions); - expect(await preferences.getString(stringKey, emptyOptions), null); - expect(await preferences.getBool(boolKey, emptyOptions), null); - expect(await preferences.getInt(intKey, emptyOptions), null); - expect(await preferences.getDouble(doubleKey, emptyOptions), null); - expect(await preferences.getStringList(listKey, emptyOptions), null); - }); - - test('clear with filter', () async { - final SharedPreferencesFoundation preferences = getPreferences(); - await Future.wait(>[ - preferences.setString(stringKey, testString, emptyOptions), - preferences.setBool(boolKey, testBool, emptyOptions), - preferences.setInt(intKey, testInt, emptyOptions), - preferences.setDouble(doubleKey, testDouble, emptyOptions), - preferences.setStringList(listKey, testList, emptyOptions) - ]); - await preferences.clear( - const ClearPreferencesParameters( - filter: PreferencesFilters(allowList: {stringKey, boolKey}), - ), - emptyOptions, - ); - expect(await preferences.getString(stringKey, emptyOptions), null); - expect(await preferences.getBool(boolKey, emptyOptions), null); - expect(await preferences.getInt(intKey, emptyOptions), testInt); - expect(await preferences.getDouble(doubleKey, emptyOptions), testDouble); - expect(await preferences.getStringList(listKey, emptyOptions), testList); + test('setValue with unsupported type', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + expect(() async { + await plugin.setValue('Map', 'flutter.key', {}); + }, throwsA(isA())); }); -} -class _FakeSharedPreferencesApi implements UserDefaultsApi { - final Map items = {}; + test('getAllWithNoPrefix', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithPrefix(''); + expect(all.length, 15); + expect(all, allTestValues); + }); - @override - Future clear( - List? allowList, SharedPreferencesPigeonOptions options) async { - if (allowList != null) { - items.removeWhere((String key, _) => allowList.contains(key)); - } else { - items.clear(); + test('clearWithNoPrefix', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; } - return true; - } + Map all = await plugin.getAllWithPrefix(''); + expect(all.length, 15); + await plugin.clearWithPrefix(''); + all = await plugin.getAllWithPrefix(''); + expect(all.length, 0); + }); - @override - Future> getAll( - List? allowList, SharedPreferencesPigeonOptions options) async { - final Map filteredItems = {...items}; - if (allowList != null) { - filteredItems.removeWhere((String key, _) => !allowList.contains(key)); + test('getAllWithNoPrefix with param', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; } - return filteredItems; - } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(all.length, 15); + expect(all, allTestValues); + }); - @override - Future> getKeys( - List? allowList, SharedPreferencesPigeonOptions options) async { - final List filteredItems = items.keys.toList(); - if (allowList != null) { - filteredItems.removeWhere((String key) => !allowList.contains(key)); + test('clearWithNoPrefix with param', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; } - return filteredItems; - } - @override - Future set( - String key, Object value, SharedPreferencesPigeonOptions options) async { - items[key] = value; - } - - @override - Future getValue( - String key, SharedPreferencesPigeonOptions options) async { - return items[key]; - } + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(all.length, 15); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(all.length, 0); + }); } diff --git a/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart b/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart index 5d995154f5b..25617165035 100644 --- a/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart +++ b/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart @@ -11,7 +11,7 @@ import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:shared_preferences_foundation/messages.g.dart'; +import 'package:shared_preferences_foundation/src/messages.g.dart'; class _TestUserDefaultsApiCodec extends StandardMessageCodec { const _TestUserDefaultsApiCodec(); @@ -59,7 +59,7 @@ abstract class TestUserDefaultsApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove', + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.remove', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -70,11 +70,11 @@ abstract class TestUserDefaultsApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.remove was null.'); final List args = (message as List?)!; final String? arg_key = (args[0] as String?); assert(arg_key != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.remove was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.remove was null, expected non-null String.'); try { api.remove(arg_key!); return wrapResponse(empty: true); @@ -90,7 +90,7 @@ abstract class TestUserDefaultsApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool', + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setBool', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -101,14 +101,14 @@ abstract class TestUserDefaultsApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setBool was null.'); final List args = (message as List?)!; final String? arg_key = (args[0] as String?); assert(arg_key != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setBool was null, expected non-null String.'); final bool? arg_value = (args[1] as bool?); assert(arg_value != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setBool was null, expected non-null bool.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setBool was null, expected non-null bool.'); try { api.setBool(arg_key!, arg_value!); return wrapResponse(empty: true); @@ -124,7 +124,7 @@ abstract class TestUserDefaultsApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble', + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setDouble', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -135,14 +135,14 @@ abstract class TestUserDefaultsApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setDouble was null.'); final List args = (message as List?)!; final String? arg_key = (args[0] as String?); assert(arg_key != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setDouble was null, expected non-null String.'); final double? arg_value = (args[1] as double?); assert(arg_value != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setDouble was null, expected non-null double.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setDouble was null, expected non-null double.'); try { api.setDouble(arg_key!, arg_value!); return wrapResponse(empty: true); @@ -158,7 +158,7 @@ abstract class TestUserDefaultsApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue', + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setValue', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -169,14 +169,14 @@ abstract class TestUserDefaultsApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setValue was null.'); final List args = (message as List?)!; final String? arg_key = (args[0] as String?); assert(arg_key != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setValue was null, expected non-null String.'); final Object? arg_value = (args[1] as Object?); assert(arg_value != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.setValue was null, expected non-null Object.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.setValue was null, expected non-null Object.'); try { api.setValue(arg_key!, arg_value!); return wrapResponse(empty: true); @@ -192,7 +192,7 @@ abstract class TestUserDefaultsApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll', + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.getAll', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -203,11 +203,11 @@ abstract class TestUserDefaultsApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.getAll was null.'); final List args = (message as List?)!; final String? arg_prefix = (args[0] as String?); assert(arg_prefix != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.getAll was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.getAll was null, expected non-null String.'); final List? arg_allowList = (args[1] as List?)?.cast(); try { @@ -226,7 +226,7 @@ abstract class TestUserDefaultsApi { { final BasicMessageChannel __pigeon_channel = BasicMessageChannel< Object?>( - 'dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear', + 'dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.clear', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -237,11 +237,11 @@ abstract class TestUserDefaultsApi { .setMockDecodedMessageHandler(__pigeon_channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear was null.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.clear was null.'); final List args = (message as List?)!; final String? arg_prefix = (args[0] as String?); assert(arg_prefix != null, - 'Argument for dev.flutter.pigeon.shared_preferences_foundation.DeprecatedUserDefaultsApi.clear was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.shared_preferences_foundation.LegacyUserDefaultsApi.clear was null, expected non-null String.'); final List? arg_allowList = (args[1] as List?)?.cast(); try { From 6a148c4ab602854e594a98662b7ccac72f66d536 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 22 Jul 2024 16:51:39 -0700 Subject: [PATCH 11/22] legacy --- .../darwin/Tests/RunnerTests.swift | 12 ++++++------ .../SharedPreferencesPlugin.swift | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift index 2021dca9d41..88d38022d61 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift @@ -17,13 +17,13 @@ class RunnerTests: XCTestCase { let testKeyTwo = "baz" let testValue = "bar" - // Deprecated system tests. + // Legacy system tests. let prefixes: [String] = ["aPrefix", ""] func testSetAndGet() throws { for aPrefix in prefixes { - let plugin = DeprecatedSharedPreferencesPlugin() + let plugin = LegacySharedPreferencesPlugin() plugin.setBool(key: "\(aPrefix)aBool", value: true) plugin.setDouble(key: "\(aPrefix)aDouble", value: 3.14) @@ -42,7 +42,7 @@ class RunnerTests: XCTestCase { func testGetWithAllowList() throws { for aPrefix in prefixes { - let plugin = DeprecatedSharedPreferencesPlugin() + let plugin = LegacySharedPreferencesPlugin() plugin.setBool(key: "\(aPrefix)aBool", value: true) plugin.setDouble(key: "\(aPrefix)aDouble", value: 3.14) @@ -61,7 +61,7 @@ class RunnerTests: XCTestCase { func testRemove() throws { for aPrefix in prefixes { - let plugin = DeprecatedSharedPreferencesPlugin() + let plugin = LegacySharedPreferencesPlugin() let testKey = "\(aPrefix)\(testKey)" plugin.setValue(key: testKey, value: 42) @@ -79,7 +79,7 @@ class RunnerTests: XCTestCase { func testClearWithNoAllowlist() throws { for aPrefix in prefixes { - let plugin = DeprecatedSharedPreferencesPlugin() + let plugin = LegacySharedPreferencesPlugin() let testKey = "\(aPrefix)\(testKey)" plugin.setValue(key: testKey, value: 42) @@ -97,7 +97,7 @@ class RunnerTests: XCTestCase { func testClearWithAllowlist() throws { for aPrefix in prefixes { - let plugin = DeprecatedSharedPreferencesPlugin() + let plugin = LegacySharedPreferencesPlugin() let testKey = "\(aPrefix)\(testKey)" plugin.setValue(key: testKey, value: 42) diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift index 52b3caa8e8e..32709a44826 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift @@ -12,17 +12,17 @@ import Foundation let argumentError: String = "Argument Error" -public class DeprecatedSharedPreferencesPlugin: NSObject, FlutterPlugin, DeprecatedUserDefaultsApi { +public class LegacySharedPreferencesPlugin: NSObject, FlutterPlugin, LegacyUserDefaultsApi { public static func register(with registrar: FlutterPluginRegistrar) { - let instance = DeprecatedSharedPreferencesPlugin() + let instance = LegacySharedPreferencesPlugin() // Workaround for https://github.com/flutter/flutter/issues/118103. #if os(iOS) let messenger = registrar.messenger() #else let messenger = registrar.messenger #endif - DeprecatedUserDefaultsApiSetup.setUp(binaryMessenger: messenger, api: instance) + LegacyUserDefaultsApiSetup.setUp(binaryMessenger: messenger, api: instance) } func getAll(prefix: String, allowList: [String]?) -> [String?: Any?] { @@ -81,7 +81,7 @@ public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { let messenger = registrar.messenger #endif UserDefaultsApiSetup.setUp(binaryMessenger: messenger, api: instance) - DeprecatedSharedPreferencesPlugin.register(with: registrar) + LegacySharedPreferencesPlugin.register(with: registrar) } static private func getUserDefaults(options: SharedPreferencesPigeonOptions) throws From f3002f14e6e7ee308acaa9b5360c6d094dde16c9 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 22 Jul 2024 16:56:26 -0700 Subject: [PATCH 12/22] more legacy --- ...ncesPlugin.java => LegacySharedPreferencesPlugin.java} | 8 ++++---- .../DeprecatedSharedPreferencesTest.java | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) rename packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/{DeprecatedSharedPreferencesPlugin.java => LegacySharedPreferencesPlugin.java} (96%) diff --git a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesPlugin.java b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/LegacySharedPreferencesPlugin.java similarity index 96% rename from packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesPlugin.java rename to packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/LegacySharedPreferencesPlugin.java index 1c972dbdc15..7d4dfd5d7e4 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesPlugin.java +++ b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/LegacySharedPreferencesPlugin.java @@ -27,8 +27,8 @@ import java.util.Map; import java.util.Set; -/** DeprecatedSharedPreferencesPlugin */ -public class DeprecatedSharedPreferencesPlugin implements FlutterPlugin, SharedPreferencesApi { +/** LegacySharedPreferencesPlugin */ +public class LegacySharedPreferencesPlugin implements FlutterPlugin, SharedPreferencesApi { private static final String TAG = "SharedPreferencesPlugin"; private static final String SHARED_PREFERENCES_NAME = "FlutterSharedPreferences"; private static final String LIST_IDENTIFIER = "VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu"; @@ -38,12 +38,12 @@ public class DeprecatedSharedPreferencesPlugin implements FlutterPlugin, SharedP private SharedPreferences preferences; private SharedPreferencesListEncoder listEncoder; - public DeprecatedSharedPreferencesPlugin() { + public LegacySharedPreferencesPlugin() { this(new ListEncoder()); } @VisibleForTesting - DeprecatedSharedPreferencesPlugin(@NonNull SharedPreferencesListEncoder listEncoder) { + LegacySharedPreferencesPlugin(@NonNull SharedPreferencesListEncoder listEncoder) { this.listEncoder = listEncoder; } diff --git a/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesTest.java b/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesTest.java index 26dc6fee0f2..08bbf2fe219 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesTest.java +++ b/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesTest.java @@ -26,9 +26,9 @@ import org.mockito.Mock; import org.mockito.Mockito; -public class DeprecatedSharedPreferencesTest { +public class LegacySharedPreferencesTest { - DeprecatedSharedPreferencesPlugin plugin; + LegacySharedPreferencesPlugin plugin; @Mock BinaryMessenger mockMessenger; @Mock FlutterPlugin.FlutterPluginBinding flutterPluginBinding; @@ -44,7 +44,7 @@ public void before() { Mockito.when(flutterPluginBinding.getApplicationContext()).thenReturn(context); Mockito.when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs); - plugin = new DeprecatedSharedPreferencesPlugin(new ListEncoder()); + plugin = new LegacySharedPreferencesPlugin(new ListEncoder()); plugin.onAttachedToEngine(flutterPluginBinding); } From 48a294fbe7b32e796d19ea980171fe788c33c485 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 22 Jul 2024 17:08:21 -0700 Subject: [PATCH 13/22] one more legacy --- .../plugins/sharedpreferences/SharedPreferencesPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.kt b/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.kt index f524b222129..cb42dec87d9 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.kt +++ b/packages/shared_preferences/shared_preferences_android/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.kt @@ -56,7 +56,7 @@ class SharedPreferencesPlugin() : FlutterPlugin, SharedPreferencesAsyncApi { override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { setUp(binding.binaryMessenger, binding.applicationContext) - DeprecatedSharedPreferencesPlugin().onAttachedToEngine(binding) + LegacySharedPreferencesPlugin().onAttachedToEngine(binding) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { From 9d4ce2832d31a1fc6447b3d6a647233915e81235 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 22 Jul 2024 18:04:50 -0700 Subject: [PATCH 14/22] anotha one --- ...haredPreferencesTest.java => LegacySharedPreferencesTest.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/{DeprecatedSharedPreferencesTest.java => LegacySharedPreferencesTest.java} (100%) diff --git a/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesTest.java b/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/LegacySharedPreferencesTest.java similarity index 100% rename from packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/DeprecatedSharedPreferencesTest.java rename to packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/LegacySharedPreferencesTest.java From 2b7b3a668a870b639dc5b117ff8487086589abca Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 22 Jul 2024 18:42:58 -0700 Subject: [PATCH 15/22] fix tests --- .../example/integration_test/shared_preferences_test.dart | 1 + .../example/integration_test/shared_preferences_test.dart | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart index 0c4527f4670..1d80c19c048 100644 --- a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart @@ -512,6 +512,7 @@ void main() { const List testList = ['foo', 'bar']; Future getPreferences() async { + SharedPreferencesAsyncPlatform.instance = SharedPreferencesAsyncAndroid(); final SharedPreferencesAsyncPlatform preferences = SharedPreferencesAsyncPlatform.instance!; await preferences.clear( diff --git a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart index 67777288d98..598d81c4c1a 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart @@ -501,6 +501,8 @@ void main() { const List testList = ['foo', 'bar']; Future getPreferences() async { + SharedPreferencesAsyncPlatform.instance = + SharedPreferencesAsyncFoundation(); final SharedPreferencesAsyncPlatform preferences = SharedPreferencesAsyncPlatform.instance!; await preferences.clear( From fa7389d405ce870d71970e3cc75fb8194a084537 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 22 Jul 2024 19:37:25 -0700 Subject: [PATCH 16/22] revert test fix, fix real issue --- .../example/integration_test/shared_preferences_test.dart | 1 - .../lib/src/shared_preferences_android.dart | 3 +++ .../lib/src/shared_preferences_async_android.dart | 3 --- .../example/integration_test/shared_preferences_test.dart | 2 -- .../lib/src/shared_preferences_async_foundation.dart | 3 --- .../lib/src/shared_preferences_foundation.dart | 3 +++ 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart index 1d80c19c048..0c4527f4670 100644 --- a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart @@ -512,7 +512,6 @@ void main() { const List testList = ['foo', 'bar']; Future getPreferences() async { - SharedPreferencesAsyncPlatform.instance = SharedPreferencesAsyncAndroid(); final SharedPreferencesAsyncPlatform preferences = SharedPreferencesAsyncPlatform.instance!; await preferences.clear( diff --git a/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_android.dart b/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_android.dart index 1f1d29b576e..67cc0835635 100644 --- a/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_android.dart +++ b/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_android.dart @@ -8,6 +8,7 @@ import 'package:shared_preferences_platform_interface/shared_preferences_platfor import 'package:shared_preferences_platform_interface/types.dart'; import 'messages.g.dart'; +import 'shared_preferences_async_android.dart'; /// The Android implementation of [SharedPreferencesStorePlatform]. /// @@ -23,6 +24,8 @@ class SharedPreferencesAndroid extends SharedPreferencesStorePlatform { /// Registers this class as the default instance of [SharedPreferencesStorePlatform]. static void registerWith() { SharedPreferencesStorePlatform.instance = SharedPreferencesAndroid(); + // A temporary work-around for having two plugins contained in a single package. + SharedPreferencesAsyncAndroid.registerWith(); } static const String _defaultPrefix = 'flutter.'; diff --git a/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_async_android.dart b/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_async_android.dart index e04bc799298..7b9b8fa6ade 100644 --- a/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_async_android.dart +++ b/packages/shared_preferences/shared_preferences_android/lib/src/shared_preferences_async_android.dart @@ -8,7 +8,6 @@ import 'package:shared_preferences_platform_interface/shared_preferences_async_p import 'package:shared_preferences_platform_interface/types.dart'; import 'messages_async.g.dart'; -import 'shared_preferences_android.dart'; const String _listPrefix = 'VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu'; @@ -27,8 +26,6 @@ base class SharedPreferencesAsyncAndroid /// Registers this class as the default instance of [SharedPreferencesAsyncPlatform]. static void registerWith() { SharedPreferencesAsyncPlatform.instance = SharedPreferencesAsyncAndroid(); - // A temporary work-around for having two plugins contained in a single package. - SharedPreferencesAndroid.registerWith(); } /// Returns a SharedPreferencesPigeonOptions for sending to platform. diff --git a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart index 598d81c4c1a..67777288d98 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart @@ -501,8 +501,6 @@ void main() { const List testList = ['foo', 'bar']; Future getPreferences() async { - SharedPreferencesAsyncPlatform.instance = - SharedPreferencesAsyncFoundation(); final SharedPreferencesAsyncPlatform preferences = SharedPreferencesAsyncPlatform.instance!; await preferences.clear( diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_async_foundation.dart b/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_async_foundation.dart index dbe90c53dbe..3bf3bc24149 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_async_foundation.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_async_foundation.dart @@ -10,7 +10,6 @@ import 'package:shared_preferences_platform_interface/shared_preferences_async_p import 'package:shared_preferences_platform_interface/types.dart'; import './messages.g.dart'; -import './shared_preferences_foundation.dart'; const String _argumentErrorCode = 'Argument Error'; @@ -28,8 +27,6 @@ base class SharedPreferencesAsyncFoundation static void registerWith() { SharedPreferencesAsyncPlatform.instance = SharedPreferencesAsyncFoundation(); - // A temporary work-around for having two plugins contained in a single package. - SharedPreferencesFoundation.registerWith(); } /// Returns a SharedPreferencesPigeonOptions for sending to platform. diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_foundation.dart b/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_foundation.dart index 0632e8bdeb9..b68b6308c74 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_foundation.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/src/shared_preferences_foundation.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; import 'package:shared_preferences_platform_interface/types.dart'; import './messages.g.dart'; +import 'shared_preferences_async_foundation.dart'; typedef _Setter = Future Function(String key, Object value); @@ -37,6 +38,8 @@ class SharedPreferencesFoundation extends SharedPreferencesStorePlatform { /// [SharedPreferencesStorePlatform]. static void registerWith() { SharedPreferencesStorePlatform.instance = SharedPreferencesFoundation(); + // A temporary work-around for having two plugins contained in a single package. + SharedPreferencesAsyncFoundation.registerWith(); } @override From bc85c3051aa07f135db66a6b1e5c442a3ed5ef52 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Wed, 24 Jul 2024 14:59:59 -0700 Subject: [PATCH 17/22] fix explode --- .../SharedPreferencesPlugin.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift index 32709a44826..a91ec4aa411 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift @@ -121,7 +121,8 @@ public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { } func getValue(key: String, options: SharedPreferencesPigeonOptions) throws -> Any? { - return try SharedPreferencesPlugin.getUserDefaults(options: options).object(forKey: key) + let preference = try SharedPreferencesPlugin.getUserDefaults(options: options).object(forKey: key) + return SharedPreferencesPlugin.isTypeCompatible(value:preference as Any) ? preference : nil } func remove(key: String, options: SharedPreferencesPigeonOptions) throws { From 9780d68f0d57c7dc830dbd53c49e92c7b88159be Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Wed, 24 Jul 2024 15:55:44 -0700 Subject: [PATCH 18/22] format --- .../SharedPreferencesPlugin.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift index a91ec4aa411..ae86be7bab2 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/shared_preferences_foundation/Sources/shared_preferences_foundation/SharedPreferencesPlugin.swift @@ -121,8 +121,9 @@ public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { } func getValue(key: String, options: SharedPreferencesPigeonOptions) throws -> Any? { - let preference = try SharedPreferencesPlugin.getUserDefaults(options: options).object(forKey: key) - return SharedPreferencesPlugin.isTypeCompatible(value:preference as Any) ? preference : nil + let preference = try SharedPreferencesPlugin.getUserDefaults(options: options).object( + forKey: key) + return SharedPreferencesPlugin.isTypeCompatible(value: preference as Any) ? preference : nil } func remove(key: String, options: SharedPreferencesPigeonOptions) throws { From 92496485d09757f553f2ad01548f38c0ca1ca171 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 25 Jul 2024 13:48:27 -0700 Subject: [PATCH 19/22] revert kotlin version, update changelog --- .../shared_preferences/shared_preferences_android/CHANGELOG.md | 3 +-- .../shared_preferences_android/android/build.gradle | 2 +- .../shared_preferences_android/example/android/build.gradle | 2 +- .../shared_preferences_foundation/CHANGELOG.md | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index 39c0e47d7e0..b5ac952fdd5 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,7 +1,6 @@ ## 2.3.0 -* Renames `SharedPreferencesAndroid` to `LegacySharedPreferencesAndroid`. -* Creates new `SharedPreferencesAndroid` that extends `SharedPreferencesAsyncPlatform`. +* Adds new `SharedPreferencesAsyncAndroid` API. ## 2.2.3 diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 8d3a053c357..11095a1352f 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -2,7 +2,7 @@ group 'io.flutter.plugins.sharedpreferences' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.8.10' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle index af575c8b5cd..4dec761be7b 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.8.10' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() diff --git a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md index 38723c28c6c..a3d1b1531e2 100644 --- a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md @@ -1,7 +1,6 @@ ## 2.5.0 -* Renames `SharedPreferencesFoundation` to `LegacySharedPreferencesFoundation`. -* Creates new `SharedPreferencesFoundation` that extends `SharedPreferencesAsyncPlatform`. +* Adds new `SharedPreferencesAsyncFoundation` API. ## 2.4.0 From 475237f42a2dbbe71f2287085aea695bd76e51dc Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 26 Jul 2024 14:48:25 -0700 Subject: [PATCH 20/22] bom --- .../shared_preferences_android/android/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 11095a1352f..9a111276c9f 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -62,6 +62,7 @@ android { disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency', 'NewerVersionAvailable' } dependencies { + implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22")) implementation 'androidx.datastore:datastore:1.0.0' implementation 'androidx.datastore:datastore-preferences:1.0.0' testImplementation 'junit:junit:4.13.2' From 78ace3ac8be3f927c822db91829f9963d625e722 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 26 Jul 2024 16:21:30 -0700 Subject: [PATCH 21/22] move bom --- .../shared_preferences_android/android/build.gradle | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index 9a111276c9f..caae6ca1114 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -62,7 +62,6 @@ android { disable 'AndroidGradlePluginVersion', 'InvalidPackage', 'GradleDependency', 'NewerVersionAvailable' } dependencies { - implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.22")) implementation 'androidx.datastore:datastore:1.0.0' implementation 'androidx.datastore:datastore-preferences:1.0.0' testImplementation 'junit:junit:4.13.2' @@ -85,4 +84,9 @@ android { } } } + dependencies { + // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. + // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 + implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10")) + } } From 89ed80a4d4cd469e14bfdd9df62cb35fa7fb3da7 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 26 Jul 2024 19:28:11 -0700 Subject: [PATCH 22/22] kotlin version, how do they work? --- .../shared_preferences_android/android/build.gradle | 5 ----- .../shared_preferences_android/example/android/build.gradle | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/android/build.gradle b/packages/shared_preferences/shared_preferences_android/android/build.gradle index caae6ca1114..11095a1352f 100644 --- a/packages/shared_preferences/shared_preferences_android/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/android/build.gradle @@ -84,9 +84,4 @@ android { } } } - dependencies { - // org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions. - // See: https://youtrack.jetbrains.com/issue/KT-55297/kotlin-stdlib-should-declare-constraints-on-kotlin-stdlib-jdk8-and-kotlin-stdlib-jdk7 - implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.10")) - } } diff --git a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle index 4dec761be7b..af575c8b5cd 100644 --- a/packages/shared_preferences/shared_preferences_android/example/android/build.gradle +++ b/packages/shared_preferences/shared_preferences_android/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.8.10' repositories { google() mavenCentral()