diff --git a/.flutter b/.flutter index 2f708eb83..9e1c85788 160000 --- a/.flutter +++ b/.flutter @@ -1 +1 @@ -Subproject commit 2f708eb8396e362e280fac22cf171c2cb467343c +Subproject commit 9e1c857886f07d342cf106f2cd588bcd5e031bb2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a9edb946..9b6c75139 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [v1.10.0] - 2023-12-02 + +### Added + +- Viewer / Slideshow: cast images via DLNA/UPnP +- Icelandic translation (thanks Sveinn í Felli) + +### Changed + +- long press actions trigger haptic feedback according to OS settings +- target Android 14 (API 34) +- upgraded Flutter to stable v3.16.2 + +### Fixed + +- temporary files remaining in the cache directory forever +- detecting motion photos with more items in the XMP Container directory +- parsing EXIF date written as epoch time + ## [v1.9.7] - 2023-10-17 ### Added @@ -15,12 +34,12 @@ All notable changes to this project will be documented in this file. - mosaic layout: clamp ratio to 32/9 - Video: disable subtitles by default -- Map: Stamen Watercolor layer (no longer served for free by Stamen) now served by Smithsonian Institution +- Map: Stamen Watercolor layer (no longer hosted for free by Stamen) now hosted by Smithsonian Institution - upgraded Flutter to stable v3.13.7 ### Removed -- Map: Stamen Toner layer (no longer served for free by Stamen) +- Map: Stamen Toner layer (no longer hosted for free by Stamen) ### Fixed diff --git a/android/app/build.gradle b/android/app/build.gradle index d05b5cb13..797d29e5a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -76,14 +76,14 @@ android { defaultConfig { applicationId packageName - // minSdkVersion constraints: + // minSdk constraints: // - Flutter & other plugins: 16 // - google_maps_flutter v2.1.1: 20 // - to build XML documents from XMP data, `metadata-extractor` and `PixyMeta` rely on `DocumentBuilder`, // which implementation `DocumentBuilderImpl` is provided by the OS and is not customizable on Android, // but the implementation on API <19 is not robust enough and fails to build XMP documents - minSdkVersion 19 - targetSdkVersion 33 + minSdk 19 + targetSdk 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName manifestPlaceholders = [googleApiKey: keystoreProperties["googleApiKey"] ?: "", @@ -216,14 +216,14 @@ dependencies { implementation 'androidx.core:core-ktx:1.12.0' implementation 'androidx.exifinterface:exifinterface:1.3.6' implementation 'androidx.lifecycle:lifecycle-process:2.6.2' - implementation 'androidx.media:media:1.6.0' + implementation 'androidx.media:media:1.7.0' implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.security:security-crypto:1.1.0-alpha06' - implementation 'androidx.work:work-runtime-ktx:2.8.1' + implementation 'androidx.work:work-runtime-ktx:2.9.0' implementation 'com.caverock:androidsvg-aar:1.4' implementation 'com.commonsware.cwac:document:0.5.0' - implementation 'com.drewnoakes:metadata-extractor:2.18.0' + implementation 'com.drewnoakes:metadata-extractor:2.19.0' implementation "com.github.bumptech.glide:glide:$glide_version" // SLF4J implementation for `mp4parser` implementation 'org.slf4j:slf4j-simple:2.0.9' @@ -240,7 +240,7 @@ dependencies { // huawei flavor only huaweiImplementation "com.huawei.agconnect:agconnect-core:$huawei_agconnect_version" - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.1' kapt 'androidx.annotation:annotation:1.7.0' ksp "com.github.bumptech.glide:ksp:$glide_version" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c906a02e3..140a47077 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -33,10 +33,9 @@ - - - - + @@ -69,7 +68,7 @@ --> + tools:targetApi="tiramisu"> + + + - + diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/AnalysisWorker.kt b/android/app/src/main/kotlin/deckers/thibault/aves/AnalysisWorker.kt index 9c0e7cbfb..7de3a0c49 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/AnalysisWorker.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/AnalysisWorker.kt @@ -159,17 +159,15 @@ class AnalysisWorker(context: Context, parameters: WorkerParameters) : Coroutine .setContentIntent(openAppIntent) .addAction(stopAction) .build() - // TODO TLAD revisit with Android 14 >beta5 - return ForegroundInfo(NOTIFICATION_ID, notification); -// return if (Build.VERSION.SDK_INT >= 34) { -// // as of Android 14 beta 5, foreground service type is mandatory -// // despite the sample code omitting it at: -// // https://developer.android.com/guide/background/persistent/how-to/long-running -// val type = ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC -// ForegroundInfo(NOTIFICATION_ID, notification, type) -// } else { -// ForegroundInfo(NOTIFICATION_ID, notification) -// } + return if (Build.VERSION.SDK_INT >= 34) { + // from Android 14 (API 34), foreground service type is mandatory + // despite the sample code omitting it at: + // https://developer.android.com/guide/background/persistent/how-to/long-running + val type = ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + ForegroundInfo(NOTIFICATION_ID, notification, type) + } else { + ForegroundInfo(NOTIFICATION_ID, notification) + } } companion object { diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt index 028d4fec3..e956a25aa 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt @@ -33,7 +33,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.launch -import java.io.File import java.io.InputStream class EmbeddedDataHandler(private val context: Context) : MethodCallHandler { @@ -279,8 +278,7 @@ class EmbeddedDataHandler(private val context: Context) : MethodCallHandler { embeddedByteLength: Long, ) { val extension = extensionFor(mimeType) - val targetFile = File.createTempFile("aves", extension, context.cacheDir).apply { - deleteOnExit() + val targetFile = StorageUtils.createTempFile(context, extension).apply { transferFrom(embeddedByteStream, embeddedByteLength) } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt index ed5d4a2ad..22f54d6d9 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt @@ -36,6 +36,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler { "getRestrictedDirectories" -> ioScope.launch { safe(call, result, ::getRestrictedDirectories) } "revokeDirectoryAccess" -> safe(call, result, ::revokeDirectoryAccess) "deleteEmptyDirectories" -> ioScope.launch { safe(call, result, ::deleteEmptyDirectories) } + "deleteTempDirectory" -> ioScope.launch { safe(call, result, ::deleteTempDirectory) } "canRequestMediaFileBulkAccess" -> safe(call, result, ::canRequestMediaFileBulkAccess) "canInsertMedia" -> safe(call, result, ::canInsertMedia) else -> result.notImplemented() @@ -200,6 +201,10 @@ class StorageHandler(private val context: Context) : MethodCallHandler { result.success(deleted) } + private fun deleteTempDirectory(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { + result.success(StorageUtils.deleteTempDirectory(context)) + } + private fun canRequestMediaFileBulkAccess(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt index 60276c136..65a3af050 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt @@ -16,7 +16,6 @@ import deckers.thibault.aves.utils.BitmapUtils.getBytes import deckers.thibault.aves.utils.MimeTypes import deckers.thibault.aves.utils.StorageUtils import io.flutter.plugin.common.MethodChannel -import java.io.File import kotlin.math.roundToInt class RegionFetcher internal constructor( @@ -113,8 +112,7 @@ class RegionFetcher internal constructor( .submit() try { val bitmap = target.get() - val tempFile = File.createTempFile("aves", null, context.cacheDir).apply { - deleteOnExit() + val tempFile = StorageUtils.createTempFile(context).apply { outputStream().use { output -> bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output) } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt index a9ae20d6d..ce361ea32 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt @@ -388,7 +388,7 @@ enum class DirType { override fun createDirectory() = ExifIFD0Directory() }, EXIF_THUMBNAIL { - override fun createDirectory() = ExifThumbnailDirectory() + override fun createDirectory() = ExifThumbnailDirectory(0) }, GPS { override fun createDirectory() = GpsDirectory() diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifTags.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifTags.kt index de7c9c375..723bac061 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifTags.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifTags.kt @@ -18,7 +18,6 @@ object ExifTags { private const val SAMPLE_FORMAT = 0x0153 private const val SMIN_SAMPLE_VALUE = 0x0154 private const val SMAX_SAMPLE_VALUE = 0x0155 - private const val RATING_PERCENT = 0x4749 private const val SONY_RAW_FILE_TYPE = 0x7000 private const val SONY_TONE_CURVE = 0x7010 private const val MATTEING = 0x80e3 @@ -40,7 +39,6 @@ object ExifTags { SAMPLE_FORMAT to "Sample Format", SMIN_SAMPLE_VALUE to "S Min Sample Value", SMAX_SAMPLE_VALUE to "S Max Sample Value", - RATING_PERCENT to "Rating Percent", SONY_RAW_FILE_TYPE to "Sony Raw File Type", SONY_TONE_CURVE to "Sony Tone Curve", MATTEING to "Matteing", diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt index c8522a6cf..b82f1cf12 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt @@ -160,8 +160,7 @@ object Metadata { } fun createPreviewFile(context: Context, uri: Uri): File { - return File.createTempFile("aves", null, context.cacheDir).apply { - deleteOnExit() + return StorageUtils.createTempFile(context).apply { transferFrom(StorageUtils.openInputStream(context, uri), PREVIEW_SIZE) } } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt index 25f1bab85..53c5b472d 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt @@ -174,9 +174,7 @@ object MultiPage { } else if (xmpMeta.doesPropExist(XMP.GCONTAINER_DIRECTORY_PROP_NAME)) { // `Container` motion photo val count = xmpMeta.countPropArrayItems(XMP.GCONTAINER_DIRECTORY_PROP_NAME) - if (count == 2) { - // expect the video to be the second item - val i = 2 + for (i in 1 until count + 1) { val mime = xmpMeta.getSafeStructField(listOf(XMP.GCONTAINER_DIRECTORY_PROP_NAME, i, XMP.GCONTAINER_ITEM_PROP_NAME, XMP.GCONTAINER_ITEM_MIME_PROP_NAME))?.value val length = xmpMeta.getSafeStructField(listOf(XMP.GCONTAINER_DIRECTORY_PROP_NAME, i, XMP.GCONTAINER_ITEM_PROP_NAME, XMP.GCONTAINER_ITEM_LENGTH_PROP_NAME))?.value if (MimeTypes.isVideo(mime) && length != null) { diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt index 47d2b75f2..e593dd16c 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt @@ -188,17 +188,15 @@ object XMP { // Container motion photo if (doesPropExist(GCONTAINER_DIRECTORY_PROP_NAME)) { val count = countPropArrayItems(GCONTAINER_DIRECTORY_PROP_NAME) - if (count == 2) { - var hasImage = false - var hasVideo = false - for (i in 1 until count + 1) { - val mime = getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, GCONTAINER_ITEM_PROP_NAME, GCONTAINER_ITEM_MIME_PROP_NAME))?.value - val length = getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, GCONTAINER_ITEM_PROP_NAME, GCONTAINER_ITEM_LENGTH_PROP_NAME))?.value - hasImage = hasImage || MimeTypes.isImage(mime) && length != null - hasVideo = hasVideo || MimeTypes.isVideo(mime) && length != null - } - if (hasImage && hasVideo) return true + var hasImage = false + var hasVideo = false + for (i in 1 until count + 1) { + val mime = getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, GCONTAINER_ITEM_PROP_NAME, GCONTAINER_ITEM_MIME_PROP_NAME))?.value + val length = getSafeStructField(listOf(GCONTAINER_DIRECTORY_PROP_NAME, i, GCONTAINER_ITEM_PROP_NAME, GCONTAINER_ITEM_LENGTH_PROP_NAME))?.value + hasImage = hasImage || MimeTypes.isImage(mime) && length != null + hasVideo = hasVideo || MimeTypes.isVideo(mime) && length != null } + if (hasImage && hasVideo) return true } return false diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt index 2c71d16ed..6bbd418ff 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt @@ -31,8 +31,14 @@ import deckers.thibault.aves.utils.LogUtils import java.io.BufferedInputStream import java.io.IOException import java.io.InputStream +import java.text.ParseException import java.text.SimpleDateFormat -import java.util.* +import java.util.Calendar +import java.util.Date +import java.util.GregorianCalendar +import java.util.Locale +import java.util.TimeZone +import java.util.regex.Pattern object Helper { private val LOG_TAG = LogUtils.createTag() @@ -110,7 +116,7 @@ object Helper { fun safeReadTiff(input: InputStream): com.drew.metadata.Metadata { val reader = RandomAccessStreamReader(input, RandomAccessStreamReader.DEFAULT_CHUNK_LENGTH, safeReadStreamLength) val metadata = com.drew.metadata.Metadata() - val handler = SafeExifTiffHandler(metadata, null) + val handler = SafeExifTiffHandler(metadata, null, 0) TiffReader().processTiff(reader, handler, 0) return metadata } @@ -150,12 +156,105 @@ object Helper { fun Directory.getSafeDateMillis(tag: Int, subSecond: String?): Long? { if (this.containsTag(tag)) { - val date = this.getDate(tag, subSecond, TimeZone.getDefault()) + val date = this.getDatePlus(tag, subSecond, TimeZone.getDefault()) if (date != null) return date.time } return null } + // This seems to cover all known Exif and Xmp date strings + // Note that " : : : : " is a valid date string according to the Exif spec (which means 'unknown date'): http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/datetimeoriginal.html + private val datePatterns = arrayOf( + "yyyy:MM:dd HH:mm:ss", + "yyyy:MM:dd HH:mm", + "yyyy-MM-dd HH:mm:ss", + "yyyy-MM-dd HH:mm", + "yyyy.MM.dd HH:mm:ss", + "yyyy.MM.dd HH:mm", + "yyyy-MM-dd'T'HH:mm:ss", + "yyyy-MM-dd'T'HH:mm", + "yyyy-MM-dd", + "yyyy-MM", + "yyyyMMdd", // as used in IPTC data + "yyyy" + ) + private val subsecondPattern = Pattern.compile("(\\d\\d:\\d\\d:\\d\\d)(\\.\\d+)") + private val timeZonePattern = Pattern.compile("(Z|[+-]\\d\\d:\\d\\d|[+-]\\d\\d\\d\\d)$") + private val calendar: Calendar = GregorianCalendar() + private const val PARSED_DATE_YEAR_MAX = 10000 + + // adapted from `metadata-extractor` v2.18.0 `Directory.getDate()` + // to also parse dates written as timestamps + private fun Directory.getDatePlus(tagType: Int, subSecond: String?, timeZone: TimeZone?): Date? { + var effectiveSubSecond = subSecond + var effectiveTimeZone = timeZone + val o = this.getObject(tagType) + if (o is Date) return o + + var date: Date? = null + if (o is String || o is StringValue) { + var dateString = o.toString() + + // if the date string has subsecond information, it supersedes the subsecond parameter + val subsecondMatcher = subsecondPattern.matcher(dateString) + if (subsecondMatcher.find()) { + effectiveSubSecond = subsecondMatcher.group(2)?.substring(1) + dateString = subsecondMatcher.replaceAll("$1") + } + + // if the date string has time zone information, it supersedes the timeZone parameter + val timeZoneMatcher = timeZonePattern.matcher(dateString) + if (timeZoneMatcher.find()) { + effectiveTimeZone = TimeZone.getTimeZone("GMT" + timeZoneMatcher.group().replace("Z".toRegex(), "")) + dateString = timeZoneMatcher.replaceAll("") + } + for (datePattern in datePatterns) { + try { + val parsed = SimpleDateFormat(datePattern, Locale.ROOT).apply { + this.timeZone = effectiveTimeZone ?: TimeZone.getTimeZone("GMT") // don't interpret zone time + }.parse(dateString) + if (parsed != null) { + calendar.time = parsed + if (calendar.get(Calendar.YEAR) < PARSED_DATE_YEAR_MAX) { + date = parsed + break + } + } + } catch (ex: ParseException) { + // simply try the next pattern + } + } + if (date == null) { + val dateLong = dateString.toLongOrNull() + if (dateLong != null) { + val epochTimeMillis = when (dateLong) { + in 0..99999999999 -> dateLong * 1000 // seconds + in 100000000000..99999999999999 -> dateLong // millis + in 100000000000000..9999999999999999 -> dateLong / 1000 // micros + else -> dateLong / 1000000 // nanos + } + date = Date(epochTimeMillis) + } + } + } + if (date == null) return null + + if (effectiveSubSecond != null) { + try { + val millisecond = (".$effectiveSubSecond".toDouble() * 1000).toInt() + if (millisecond in 0..999) { + val calendar = Calendar.getInstance() + calendar.time = date + calendar[Calendar.MILLISECOND] = millisecond + return calendar.time + } + } catch (e: NumberFormatException) { + // ignore + } + } + return date + } + // time tag and sub-second tag are *not* in the same directory fun ExifSubIFDDirectory.getDateModifiedMillis(save: (value: Long) -> Unit) { val parent = parent diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeExifTiffHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeExifTiffHandler.kt index afad1de4c..39a4c4918 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeExifTiffHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeExifTiffHandler.kt @@ -8,7 +8,7 @@ import com.drew.metadata.exif.ExifSubIFDDirectory import com.drew.metadata.exif.ExifTiffHandler import java.io.IOException -class SafeExifTiffHandler(metadata: Metadata, parentDirectory: Directory?) : ExifTiffHandler(metadata, parentDirectory) { +class SafeExifTiffHandler(metadata: Metadata, parentDirectory: Directory?, exifStartOffset: Int) : ExifTiffHandler(metadata, parentDirectory, exifStartOffset) { @Throws(IOException::class) override fun customProcessTag( tagOffset: Int, diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt index c438068b5..05822f03a 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt @@ -45,6 +45,7 @@ object SafePngMetadataReader { private const val chunkSizeDangerThreshold = SafeXmpReader.SEGMENT_TYPE_SIZE_DANGER_THRESHOLD private val latin1Encoding = Charsets.ISO_8859_1 + private val utf8Encoding = Charsets.UTF_8 private val desiredChunkTypes: Set = hashSetOf( PngChunkType.IHDR, PngChunkType.PLTE, @@ -234,7 +235,7 @@ object SafePngMetadataReader { val reader: SequentialReader = SequentialByteArrayReader(bytes) // Keyword is 1-79 bytes, followed by the 1 byte null character - val keywordsv = reader.getNullTerminatedStringValue(79 + 1, latin1Encoding) + val keywordsv = reader.getNullTerminatedStringValue(79 + 1, utf8Encoding) val keyword = keywordsv.toString() val compressionFlag = reader.int8 val compressionMethod = reader.int8 @@ -273,7 +274,7 @@ object SafePngMetadataReader { XmpReader().extract(textBytes, metadata) } else { val textPairs: MutableList = ArrayList() - textPairs.add(KeyValuePair(keyword, StringValue(textBytes, latin1Encoding))) + textPairs.add(KeyValuePair(keyword, StringValue(textBytes, utf8Encoding))) val directory = PngDirectory(PngChunkType.iTXt) directory.setObject(PngDirectory.TAG_TEXTUAL_DATA, textPairs) metadata.addDirectory(directory) @@ -316,7 +317,7 @@ object SafePngMetadataReader { metadata.addDirectory(directory) } else if (chunkType == PngChunkType.eXIf) { try { - val handler = ExifTiffHandler(metadata, null) + val handler = ExifTiffHandler(metadata, null, 0) TiffReader().processTiff(ByteArrayReader(bytes), handler, 0) } catch (ex: TiffProcessingException) { val directory = PngDirectory(PngChunkType.eXIf) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt index 20fa622af..8abdc5648 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt @@ -381,8 +381,7 @@ abstract class ImageProvider { targetUri: Uri, targetPath: String, ) { - val editableFile = File.createTempFile("aves", null).apply { - deleteOnExit() + val editableFile = StorageUtils.createTempFile(context).apply { // copy original file to a temporary file for editing val inputStream = StorageUtils.openInputStream(context, targetUri) transferFrom(inputStream, File(targetPath).length()) @@ -514,8 +513,7 @@ abstract class ImageProvider { output.write(bytes) } } else { - val editableFile = withContext(Dispatchers.IO) { File.createTempFile("aves", null) }.apply { - deleteOnExit() + val editableFile = withContext(Dispatchers.IO) { StorageUtils.createTempFile(contextWrapper) }.apply { transferFrom(ByteArrayInputStream(bytes), bytes.size.toLong()) } @@ -649,8 +647,7 @@ abstract class ImageProvider { val originalFileSize = File(path).length() val videoSize = MultiPage.getMotionPhotoOffset(context, uri, mimeType, originalFileSize)?.let { it.toInt() + trailerDiff } var videoBytes: ByteArray? = null - val editableFile = File.createTempFile("aves", null).apply { - deleteOnExit() + val editableFile = StorageUtils.createTempFile(context).apply { try { if (videoSize != null) { // handle motion photo and embedded video separately @@ -733,8 +730,7 @@ abstract class ImageProvider { val originalFileSize = File(path).length() val videoSize = MultiPage.getMotionPhotoOffset(context, uri, mimeType, originalFileSize)?.let { it.toInt() + trailerDiff } var videoBytes: ByteArray? = null - val editableFile = File.createTempFile("aves", null).apply { - deleteOnExit() + val editableFile = StorageUtils.createTempFile(context).apply { try { if (videoSize != null) { // handle motion photo and embedded video separately @@ -898,8 +894,7 @@ abstract class ImageProvider { val originalFileSize = File(path).length() val videoSize = MultiPage.getMotionPhotoOffset(context, uri, mimeType, originalFileSize)?.let { it.toInt() + trailerDiff } - val editableFile = File.createTempFile("aves", null).apply { - deleteOnExit() + val editableFile = StorageUtils.createTempFile(context).apply { try { editXmpWithPixy( context = context, @@ -1275,8 +1270,7 @@ abstract class ImageProvider { return } - val editableFile = File.createTempFile("aves", null).apply { - deleteOnExit() + val editableFile = StorageUtils.createTempFile(context).apply { try { val inputStream = StorageUtils.openInputStream(context, uri) // partial copy @@ -1316,8 +1310,7 @@ abstract class ImageProvider { val originalFileSize = File(path).length() val videoSize = MultiPage.getMotionPhotoOffset(context, uri, mimeType, originalFileSize)?.toInt() - val editableFile = File.createTempFile("aves", null).apply { - deleteOnExit() + val editableFile = StorageUtils.createTempFile(context).apply { try { outputStream().use { output -> // reopen input to read from start diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt index 1dd700ebf..d921615bd 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt @@ -725,12 +725,19 @@ class MediaStoreImageProvider : ImageProvider() { val df = StorageUtils.getDocumentFile(activity, oldPath, oldMediaUri) df ?: throw Exception("failed to get document at path=$oldPath") + val requestedName = newFile.name val renamed = df.renameTo(newFile.name) if (!renamed) { throw Exception("failed to rename document at path=$oldPath") } + val effectiveName = df.name + if (requestedName != effectiveName) { + Log.w(LOG_TAG, "requested renaming document at uri=$oldMediaUri path=$oldPath with name=${requestedName} but got name=$effectiveName") + } + val newPath = File(newFile.parentFile, df.name).path + scanObsoletePath(activity, oldMediaUri, oldPath, mimeType) - return scanNewPathByMediaStore(activity, newFile.path, mimeType) + return scanNewPathByMediaStore(activity, newPath, mimeType) } private suspend fun renameSingleByFile( diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt index 306796ab3..a24cceda8 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt @@ -26,6 +26,7 @@ import deckers.thibault.aves.utils.PermissionManager.getGrantedDirForPath import deckers.thibault.aves.utils.UriUtils.tryParseId import java.io.File import java.io.FileInputStream +import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.util.* @@ -593,8 +594,7 @@ object StorageUtils { uriPath?.contains("/file/") == true -> { // e.g. `content://media/external/file/...` // create an ad-hoc temporary file for decoding only - File.createTempFile("aves", null).apply { - deleteOnExit() + createTempFile(context).apply { try { transferFrom(openInputStream(context, uri), sizeBytes) return Uri.fromFile(this) @@ -714,6 +714,25 @@ object StorageUtils { } } + private fun getTempDirectory(context: Context): File = File(context.cacheDir, "temp") + + fun createTempFile(context: Context, extension: String? = null): File { + val directory = getTempDirectory(context) + if (!directory.exists() && !directory.mkdirs()) { + throw IOException("failed to create directories at path=$directory") + } + val tempFile = File.createTempFile("aves", extension, directory) + // `deleteOnExit` is unreliable, but it does not hurt + tempFile.deleteOnExit() + return tempFile + } + + fun deleteTempDirectory(context: Context): Boolean { + val directory = getTempDirectory(context) + if (!directory.exists()) return false + return directory.deleteRecursively() + } + // convenience methods fun getFolderSize(f: File): Long { diff --git a/android/app/src/main/res/values-is/strings.xml b/android/app/src/main/res/values-is/strings.xml new file mode 100644 index 000000000..5087337fe --- /dev/null +++ b/android/app/src/main/res/values-is/strings.xml @@ -0,0 +1,12 @@ + + + Stöðva + Myndarammi + Skanna myndefni + Myndskeið + Öruggur hamur + Bakgrunnur + Aves + Skönnun myndefnis + Leita + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 10ae183fd..14ae3f66e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,8 +1,8 @@ buildscript { ext { - kotlin_version = '1.8.21' - ksp_version = "$kotlin_version-1.0.11" - agp_version = '7.4.2' + kotlin_version = '1.9.21' + ksp_version = "$kotlin_version-1.0.15" + agp_version = '8.1.4' glide_version = '4.16.0' // AppGallery Connect plugin versions: https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-sdk-changenotes-0000001058732550 huawei_agconnect_version = '1.9.1.300' @@ -27,8 +27,8 @@ buildscript { if (useCrashlytics) { // GMS & Firebase Crashlytics (used by some flavors only) - classpath 'com.google.gms:google-services:4.3.15' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.8' + classpath 'com.google.gms:google-services:4.4.0' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9' } if (useHms) { diff --git a/android/gradle.properties b/android/gradle.properties index 390313428..318036f25 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -11,8 +11,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official android.defaults.buildfeatures.buildconfig=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 8bc9958ab..7666e22b5 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip diff --git a/fastlane/metadata/android/cs/images/phoneScreenshots/1.png b/fastlane/metadata/android/cs/images/phoneScreenshots/1.png index ea8be8f13..472bcd0b3 100644 Binary files a/fastlane/metadata/android/cs/images/phoneScreenshots/1.png and b/fastlane/metadata/android/cs/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/cs/images/phoneScreenshots/2.png b/fastlane/metadata/android/cs/images/phoneScreenshots/2.png index f684259cf..b1e1fbb7e 100644 Binary files a/fastlane/metadata/android/cs/images/phoneScreenshots/2.png and b/fastlane/metadata/android/cs/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/cs/images/phoneScreenshots/3.png b/fastlane/metadata/android/cs/images/phoneScreenshots/3.png index 7940af98c..51d5c294b 100644 Binary files a/fastlane/metadata/android/cs/images/phoneScreenshots/3.png and b/fastlane/metadata/android/cs/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/cs/images/phoneScreenshots/4.png b/fastlane/metadata/android/cs/images/phoneScreenshots/4.png index 3ea278277..a7ceeb655 100644 Binary files a/fastlane/metadata/android/cs/images/phoneScreenshots/4.png and b/fastlane/metadata/android/cs/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/cs/images/phoneScreenshots/5.png b/fastlane/metadata/android/cs/images/phoneScreenshots/5.png index 6fbd3a037..f65ab3a1a 100644 Binary files a/fastlane/metadata/android/cs/images/phoneScreenshots/5.png and b/fastlane/metadata/android/cs/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/cs/images/phoneScreenshots/6.png b/fastlane/metadata/android/cs/images/phoneScreenshots/6.png index e63823d65..92f708e30 100644 Binary files a/fastlane/metadata/android/cs/images/phoneScreenshots/6.png and b/fastlane/metadata/android/cs/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/cs/images/phoneScreenshots/7.png b/fastlane/metadata/android/cs/images/phoneScreenshots/7.png index 0c8d9c0e7..9844a00a5 100644 Binary files a/fastlane/metadata/android/cs/images/phoneScreenshots/7.png and b/fastlane/metadata/android/cs/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/1.png b/fastlane/metadata/android/de/images/phoneScreenshots/1.png index ff49b0a6e..dee328b8b 100644 Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/1.png and b/fastlane/metadata/android/de/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/2.png b/fastlane/metadata/android/de/images/phoneScreenshots/2.png index f43179c6e..f40409037 100644 Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/2.png and b/fastlane/metadata/android/de/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/3.png b/fastlane/metadata/android/de/images/phoneScreenshots/3.png index c190bb5bd..0ea3e4fd4 100644 Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/3.png and b/fastlane/metadata/android/de/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/4.png b/fastlane/metadata/android/de/images/phoneScreenshots/4.png index 8c1cb648c..d0351b9fe 100644 Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/4.png and b/fastlane/metadata/android/de/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/5.png b/fastlane/metadata/android/de/images/phoneScreenshots/5.png index 64e691838..06b031df0 100644 Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/5.png and b/fastlane/metadata/android/de/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/6.png b/fastlane/metadata/android/de/images/phoneScreenshots/6.png index 9de79e28b..ca7c88fde 100644 Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/6.png and b/fastlane/metadata/android/de/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/7.png b/fastlane/metadata/android/de/images/phoneScreenshots/7.png index 6d0ff3f96..ff3a19e7a 100644 Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/7.png and b/fastlane/metadata/android/de/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/el/images/phoneScreenshots/1.png b/fastlane/metadata/android/el/images/phoneScreenshots/1.png index eefd8f1bb..f0722cbab 100644 Binary files a/fastlane/metadata/android/el/images/phoneScreenshots/1.png and b/fastlane/metadata/android/el/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/el/images/phoneScreenshots/2.png b/fastlane/metadata/android/el/images/phoneScreenshots/2.png index 96dae9f00..215036deb 100644 Binary files a/fastlane/metadata/android/el/images/phoneScreenshots/2.png and b/fastlane/metadata/android/el/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/el/images/phoneScreenshots/3.png b/fastlane/metadata/android/el/images/phoneScreenshots/3.png index bc74450d4..74d97a7d9 100644 Binary files a/fastlane/metadata/android/el/images/phoneScreenshots/3.png and b/fastlane/metadata/android/el/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/el/images/phoneScreenshots/4.png b/fastlane/metadata/android/el/images/phoneScreenshots/4.png index 77e81d3f9..21c1e62d2 100644 Binary files a/fastlane/metadata/android/el/images/phoneScreenshots/4.png and b/fastlane/metadata/android/el/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/el/images/phoneScreenshots/5.png b/fastlane/metadata/android/el/images/phoneScreenshots/5.png index 24d25bb3f..f767b3be2 100644 Binary files a/fastlane/metadata/android/el/images/phoneScreenshots/5.png and b/fastlane/metadata/android/el/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/el/images/phoneScreenshots/6.png b/fastlane/metadata/android/el/images/phoneScreenshots/6.png index b8873682f..9bcee393e 100644 Binary files a/fastlane/metadata/android/el/images/phoneScreenshots/6.png and b/fastlane/metadata/android/el/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/el/images/phoneScreenshots/7.png b/fastlane/metadata/android/el/images/phoneScreenshots/7.png index be70a0288..931e6370d 100644 Binary files a/fastlane/metadata/android/el/images/phoneScreenshots/7.png and b/fastlane/metadata/android/el/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/en-US/changelogs/100.txt b/fastlane/metadata/android/en-US/changelogs/100.txt deleted file mode 100644 index e126e5ad6..000000000 --- a/fastlane/metadata/android/en-US/changelogs/100.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.8.9: -- play your animated PNGs -- set your home to the Tags page -- enjoy the app in Norwegian (Nynorsk) -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/10001.txt b/fastlane/metadata/android/en-US/changelogs/10001.txt deleted file mode 100644 index e126e5ad6..000000000 --- a/fastlane/metadata/android/en-US/changelogs/10001.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.8.9: -- play your animated PNGs -- set your home to the Tags page -- enjoy the app in Norwegian (Nynorsk) -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/101.txt b/fastlane/metadata/android/en-US/changelogs/101.txt deleted file mode 100644 index ac542c6e0..000000000 --- a/fastlane/metadata/android/en-US/changelogs/101.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.9.0: -- play your animated AVIF, AV1, and HDR videos -- filter by rating ranges -- judge tonal distributions with the viewer histogram -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/10101.txt b/fastlane/metadata/android/en-US/changelogs/10101.txt deleted file mode 100644 index ac542c6e0..000000000 --- a/fastlane/metadata/android/en-US/changelogs/10101.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.9.0: -- play your animated AVIF, AV1, and HDR videos -- filter by rating ranges -- judge tonal distributions with the viewer histogram -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/102.txt b/fastlane/metadata/android/en-US/changelogs/102.txt deleted file mode 100644 index c5ba8521a..000000000 --- a/fastlane/metadata/android/en-US/changelogs/102.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.9.1: -- play your animated AVIF, AV1, and HDR videos -- filter by rating ranges -- judge tonal distributions with the viewer histogram -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/10201.txt b/fastlane/metadata/android/en-US/changelogs/10201.txt deleted file mode 100644 index c5ba8521a..000000000 --- a/fastlane/metadata/android/en-US/changelogs/10201.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.9.1: -- play your animated AVIF, AV1, and HDR videos -- filter by rating ranges -- judge tonal distributions with the viewer histogram -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/103.txt b/fastlane/metadata/android/en-US/changelogs/103.txt deleted file mode 100644 index f2c89cff5..000000000 --- a/fastlane/metadata/android/en-US/changelogs/103.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.9.2: -- play your animated AVIF, AV1, and HDR videos -- filter by rating ranges -- judge tonal distributions with the viewer histogram -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/10301.txt b/fastlane/metadata/android/en-US/changelogs/10301.txt deleted file mode 100644 index f2c89cff5..000000000 --- a/fastlane/metadata/android/en-US/changelogs/10301.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.9.2: -- play your animated AVIF, AV1, and HDR videos -- filter by rating ranges -- judge tonal distributions with the viewer histogram -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/104.txt b/fastlane/metadata/android/en-US/changelogs/104.txt deleted file mode 100644 index 5c5ca1669..000000000 --- a/fastlane/metadata/android/en-US/changelogs/104.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.9.3: -- play your animated AVIF, AV1, and HDR videos -- filter by rating ranges -- judge tonal distributions with the viewer histogram -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/10401.txt b/fastlane/metadata/android/en-US/changelogs/10401.txt deleted file mode 100644 index 5c5ca1669..000000000 --- a/fastlane/metadata/android/en-US/changelogs/10401.txt +++ /dev/null @@ -1,5 +0,0 @@ -In v1.9.3: -- play your animated AVIF, AV1, and HDR videos -- filter by rating ranges -- judge tonal distributions with the viewer histogram -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/109.txt b/fastlane/metadata/android/en-US/changelogs/109.txt new file mode 100644 index 000000000..3c18fca39 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/109.txt @@ -0,0 +1,4 @@ +In v1.10.0: +- cast images via DLNA/UPnP +- enjoy the app in Icelandic +Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/10901.txt b/fastlane/metadata/android/en-US/changelogs/10901.txt new file mode 100644 index 000000000..3c18fca39 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/10901.txt @@ -0,0 +1,4 @@ +In v1.10.0: +- cast images via DLNA/UPnP +- enjoy the app in Icelandic +Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png index da2db556d..ca7edfc76 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png index 3fdfd7a50..37f468cc8 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png index 5c427c13c..f867bbba5 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png index 2074b5ce4..094a087d7 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png index 82206f5be..5709828e3 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png index 1ab68a950..0e2e20374 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png index fd97f6418..0eb452dc8 100644 Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png index 7c0e52710..72009ab1b 100644 Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/2.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/2.png index e46458e7f..61afd897b 100644 Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/2.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/3.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/3.png index 7235df192..69ff37cda 100644 Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/3.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/4.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/4.png index 7576c4812..6491428e5 100644 Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/4.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/5.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/5.png index 0014da392..3d00d4077 100644 Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/5.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/6.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/6.png index b50b970eb..f45ac6185 100644 Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/6.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/7.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/7.png index 59dff9ed5..e8a280a16 100644 Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/7.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/eu/images/phoneScreenshots/1.png b/fastlane/metadata/android/eu/images/phoneScreenshots/1.png index 7f2dca48e..c9b016361 100644 Binary files a/fastlane/metadata/android/eu/images/phoneScreenshots/1.png and b/fastlane/metadata/android/eu/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/eu/images/phoneScreenshots/2.png b/fastlane/metadata/android/eu/images/phoneScreenshots/2.png index be2d319a7..4c701a4c6 100644 Binary files a/fastlane/metadata/android/eu/images/phoneScreenshots/2.png and b/fastlane/metadata/android/eu/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/eu/images/phoneScreenshots/3.png b/fastlane/metadata/android/eu/images/phoneScreenshots/3.png index 98fa51b08..c7cb9c666 100644 Binary files a/fastlane/metadata/android/eu/images/phoneScreenshots/3.png and b/fastlane/metadata/android/eu/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/eu/images/phoneScreenshots/4.png b/fastlane/metadata/android/eu/images/phoneScreenshots/4.png index f2a53a498..847627c87 100644 Binary files a/fastlane/metadata/android/eu/images/phoneScreenshots/4.png and b/fastlane/metadata/android/eu/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/eu/images/phoneScreenshots/5.png b/fastlane/metadata/android/eu/images/phoneScreenshots/5.png index 244028de4..2b8914ed8 100644 Binary files a/fastlane/metadata/android/eu/images/phoneScreenshots/5.png and b/fastlane/metadata/android/eu/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/eu/images/phoneScreenshots/6.png b/fastlane/metadata/android/eu/images/phoneScreenshots/6.png index 41a3c9e5b..1a9ff9000 100644 Binary files a/fastlane/metadata/android/eu/images/phoneScreenshots/6.png and b/fastlane/metadata/android/eu/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/eu/images/phoneScreenshots/7.png b/fastlane/metadata/android/eu/images/phoneScreenshots/7.png index 3d99af3ed..b9b6686cc 100644 Binary files a/fastlane/metadata/android/eu/images/phoneScreenshots/7.png and b/fastlane/metadata/android/eu/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/1.png b/fastlane/metadata/android/fr/images/phoneScreenshots/1.png index 40ad40622..1a10f9221 100644 Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/1.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/2.png b/fastlane/metadata/android/fr/images/phoneScreenshots/2.png index 111066ff1..21b65b162 100644 Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/2.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/3.png b/fastlane/metadata/android/fr/images/phoneScreenshots/3.png index 2a3e59ee1..cc4f7a584 100644 Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/3.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/4.png b/fastlane/metadata/android/fr/images/phoneScreenshots/4.png index e4433269e..0bb6490e1 100644 Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/4.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/5.png b/fastlane/metadata/android/fr/images/phoneScreenshots/5.png index 3378ac3e7..1388a5333 100644 Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/5.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/6.png b/fastlane/metadata/android/fr/images/phoneScreenshots/6.png index 7c792b21b..e3ba684a5 100644 Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/6.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/7.png b/fastlane/metadata/android/fr/images/phoneScreenshots/7.png index 5c1e6cd36..2699c480b 100644 Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/7.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/hu/images/phoneScreenshots/1.png b/fastlane/metadata/android/hu/images/phoneScreenshots/1.png index 7e43417d9..f50f91a94 100644 Binary files a/fastlane/metadata/android/hu/images/phoneScreenshots/1.png and b/fastlane/metadata/android/hu/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/hu/images/phoneScreenshots/2.png b/fastlane/metadata/android/hu/images/phoneScreenshots/2.png index f26b68fcc..96a5ccdc8 100644 Binary files a/fastlane/metadata/android/hu/images/phoneScreenshots/2.png and b/fastlane/metadata/android/hu/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/hu/images/phoneScreenshots/3.png b/fastlane/metadata/android/hu/images/phoneScreenshots/3.png index 506cafe4c..18a1922bc 100644 Binary files a/fastlane/metadata/android/hu/images/phoneScreenshots/3.png and b/fastlane/metadata/android/hu/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/hu/images/phoneScreenshots/4.png b/fastlane/metadata/android/hu/images/phoneScreenshots/4.png index 8e5ebb434..8fa0e498c 100644 Binary files a/fastlane/metadata/android/hu/images/phoneScreenshots/4.png and b/fastlane/metadata/android/hu/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/hu/images/phoneScreenshots/5.png b/fastlane/metadata/android/hu/images/phoneScreenshots/5.png index 2a11a7b48..8d8522b2f 100644 Binary files a/fastlane/metadata/android/hu/images/phoneScreenshots/5.png and b/fastlane/metadata/android/hu/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/hu/images/phoneScreenshots/6.png b/fastlane/metadata/android/hu/images/phoneScreenshots/6.png index 379e13ee5..54e31b8c7 100644 Binary files a/fastlane/metadata/android/hu/images/phoneScreenshots/6.png and b/fastlane/metadata/android/hu/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/hu/images/phoneScreenshots/7.png b/fastlane/metadata/android/hu/images/phoneScreenshots/7.png index fe1a650f4..ad7ee0ff0 100644 Binary files a/fastlane/metadata/android/hu/images/phoneScreenshots/7.png and b/fastlane/metadata/android/hu/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/1.png b/fastlane/metadata/android/id/images/phoneScreenshots/1.png index fc94cf155..6eaf2273b 100644 Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/1.png and b/fastlane/metadata/android/id/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/2.png b/fastlane/metadata/android/id/images/phoneScreenshots/2.png index 276edee53..dfdd8b26d 100644 Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/2.png and b/fastlane/metadata/android/id/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/3.png b/fastlane/metadata/android/id/images/phoneScreenshots/3.png index 0e6eb3db7..fd1bb92dd 100644 Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/3.png and b/fastlane/metadata/android/id/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/4.png b/fastlane/metadata/android/id/images/phoneScreenshots/4.png index dbedad8eb..5e575b60d 100644 Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/4.png and b/fastlane/metadata/android/id/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/5.png b/fastlane/metadata/android/id/images/phoneScreenshots/5.png index a0efbf155..e8b17d935 100644 Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/5.png and b/fastlane/metadata/android/id/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/6.png b/fastlane/metadata/android/id/images/phoneScreenshots/6.png index f1f00ca5e..763d3f053 100644 Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/6.png and b/fastlane/metadata/android/id/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/7.png b/fastlane/metadata/android/id/images/phoneScreenshots/7.png index e3af538e6..e64da42a5 100644 Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/7.png and b/fastlane/metadata/android/id/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/is/full_description.txt b/fastlane/metadata/android/is/full_description.txt new file mode 100644 index 000000000..6c92748f8 --- /dev/null +++ b/fastlane/metadata/android/is/full_description.txt @@ -0,0 +1,5 @@ +Aves can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like multi-page TIFFs, SVGs, old AVIs and more! It scans your media collection to identify motion photos, panoramas (aka photo spheres), 360° videos, as well as GeoTIFF files. + +Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. + +Aves integrates with Android (from KitKat to Android 13, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. \ No newline at end of file diff --git a/fastlane/metadata/android/is/images/featureGraphic.png b/fastlane/metadata/android/is/images/featureGraphic.png new file mode 100644 index 000000000..b15f02fb5 Binary files /dev/null and b/fastlane/metadata/android/is/images/featureGraphic.png differ diff --git a/fastlane/metadata/android/is/images/phoneScreenshots/1.png b/fastlane/metadata/android/is/images/phoneScreenshots/1.png new file mode 100644 index 000000000..37d8598a2 Binary files /dev/null and b/fastlane/metadata/android/is/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/is/images/phoneScreenshots/2.png b/fastlane/metadata/android/is/images/phoneScreenshots/2.png new file mode 100644 index 000000000..77cbf0153 Binary files /dev/null and b/fastlane/metadata/android/is/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/is/images/phoneScreenshots/3.png b/fastlane/metadata/android/is/images/phoneScreenshots/3.png new file mode 100644 index 000000000..2330679f7 Binary files /dev/null and b/fastlane/metadata/android/is/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/is/images/phoneScreenshots/4.png b/fastlane/metadata/android/is/images/phoneScreenshots/4.png new file mode 100644 index 000000000..80df4a727 Binary files /dev/null and b/fastlane/metadata/android/is/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/is/images/phoneScreenshots/5.png b/fastlane/metadata/android/is/images/phoneScreenshots/5.png new file mode 100644 index 000000000..691840c7d Binary files /dev/null and b/fastlane/metadata/android/is/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/is/images/phoneScreenshots/6.png b/fastlane/metadata/android/is/images/phoneScreenshots/6.png new file mode 100644 index 000000000..56f4213c5 Binary files /dev/null and b/fastlane/metadata/android/is/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/is/images/phoneScreenshots/7.png b/fastlane/metadata/android/is/images/phoneScreenshots/7.png new file mode 100644 index 000000000..a18ca83b0 Binary files /dev/null and b/fastlane/metadata/android/is/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/is/short_description.txt b/fastlane/metadata/android/is/short_description.txt new file mode 100644 index 000000000..8c9445bd5 --- /dev/null +++ b/fastlane/metadata/android/is/short_description.txt @@ -0,0 +1 @@ +Gallery and metadata explorer \ No newline at end of file diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/1.png b/fastlane/metadata/android/it/images/phoneScreenshots/1.png index d6b796478..2639a317b 100644 Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/1.png and b/fastlane/metadata/android/it/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/2.png b/fastlane/metadata/android/it/images/phoneScreenshots/2.png index 6ba6e70dc..ce01662ee 100644 Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/2.png and b/fastlane/metadata/android/it/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/3.png b/fastlane/metadata/android/it/images/phoneScreenshots/3.png index 5ce25cd4d..e83f88660 100644 Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/3.png and b/fastlane/metadata/android/it/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/4.png b/fastlane/metadata/android/it/images/phoneScreenshots/4.png index 5da901ece..dd89e1ef6 100644 Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/4.png and b/fastlane/metadata/android/it/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/5.png b/fastlane/metadata/android/it/images/phoneScreenshots/5.png index 7fc3c9d00..ee7841838 100644 Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/5.png and b/fastlane/metadata/android/it/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/6.png b/fastlane/metadata/android/it/images/phoneScreenshots/6.png index 854b14068..4f405d609 100644 Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/6.png and b/fastlane/metadata/android/it/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/7.png b/fastlane/metadata/android/it/images/phoneScreenshots/7.png index fe36b22d3..e5ac97ce6 100644 Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/7.png and b/fastlane/metadata/android/it/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/1.png b/fastlane/metadata/android/ja/images/phoneScreenshots/1.png index 197d4d7c9..f0295da57 100644 Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/2.png b/fastlane/metadata/android/ja/images/phoneScreenshots/2.png index 80352a8e4..1a09722c2 100644 Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/2.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/3.png b/fastlane/metadata/android/ja/images/phoneScreenshots/3.png index 1220cee5a..be1c197fa 100644 Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/3.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/4.png b/fastlane/metadata/android/ja/images/phoneScreenshots/4.png index f8c167d72..3c794709f 100644 Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/4.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/5.png b/fastlane/metadata/android/ja/images/phoneScreenshots/5.png index 6750f2a99..85da14dba 100644 Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/5.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/6.png b/fastlane/metadata/android/ja/images/phoneScreenshots/6.png index 94a630301..1fa22534d 100644 Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/6.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/7.png b/fastlane/metadata/android/ja/images/phoneScreenshots/7.png index 565e6ebef..859589ec8 100644 Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/7.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/1.png b/fastlane/metadata/android/ko/images/phoneScreenshots/1.png index dfafa5766..7082f118a 100644 Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/2.png b/fastlane/metadata/android/ko/images/phoneScreenshots/2.png index a90b98b25..656e9d065 100644 Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/2.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/3.png b/fastlane/metadata/android/ko/images/phoneScreenshots/3.png index 1f32a2593..653aafa90 100644 Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/3.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/4.png b/fastlane/metadata/android/ko/images/phoneScreenshots/4.png index 8530da0b9..49b756274 100644 Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/4.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/5.png b/fastlane/metadata/android/ko/images/phoneScreenshots/5.png index 09ff885d5..9deb97f29 100644 Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/5.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/6.png b/fastlane/metadata/android/ko/images/phoneScreenshots/6.png index 206370d9b..d95c27b54 100644 Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/6.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/7.png b/fastlane/metadata/android/ko/images/phoneScreenshots/7.png index 514fa72c2..dd7d136e4 100644 Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/7.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/lt/images/phoneScreenshots/1.png b/fastlane/metadata/android/lt/images/phoneScreenshots/1.png index 28589979e..9833ac27c 100644 Binary files a/fastlane/metadata/android/lt/images/phoneScreenshots/1.png and b/fastlane/metadata/android/lt/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/lt/images/phoneScreenshots/2.png b/fastlane/metadata/android/lt/images/phoneScreenshots/2.png index 88a3c59ae..3cc6610f8 100644 Binary files a/fastlane/metadata/android/lt/images/phoneScreenshots/2.png and b/fastlane/metadata/android/lt/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/lt/images/phoneScreenshots/3.png b/fastlane/metadata/android/lt/images/phoneScreenshots/3.png index 345fb90e6..cc118f076 100644 Binary files a/fastlane/metadata/android/lt/images/phoneScreenshots/3.png and b/fastlane/metadata/android/lt/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/lt/images/phoneScreenshots/4.png b/fastlane/metadata/android/lt/images/phoneScreenshots/4.png index b7a0bb546..66aa2f726 100644 Binary files a/fastlane/metadata/android/lt/images/phoneScreenshots/4.png and b/fastlane/metadata/android/lt/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/lt/images/phoneScreenshots/5.png b/fastlane/metadata/android/lt/images/phoneScreenshots/5.png index 0408fd3c5..3561b8d1a 100644 Binary files a/fastlane/metadata/android/lt/images/phoneScreenshots/5.png and b/fastlane/metadata/android/lt/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/lt/images/phoneScreenshots/6.png b/fastlane/metadata/android/lt/images/phoneScreenshots/6.png index 233411d03..e2495abf0 100644 Binary files a/fastlane/metadata/android/lt/images/phoneScreenshots/6.png and b/fastlane/metadata/android/lt/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/lt/images/phoneScreenshots/7.png b/fastlane/metadata/android/lt/images/phoneScreenshots/7.png index ba5df9955..849f2e560 100644 Binary files a/fastlane/metadata/android/lt/images/phoneScreenshots/7.png and b/fastlane/metadata/android/lt/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/1.png b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/1.png index a0301b619..887823e42 100644 Binary files a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/1.png and b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/2.png b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/2.png index c71169bb1..296bd4895 100644 Binary files a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/2.png and b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/3.png b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/3.png index b793e51ab..31347e3b2 100644 Binary files a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/3.png and b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/4.png b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/4.png index 517d27107..3e9afad93 100644 Binary files a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/4.png and b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/5.png b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/5.png index 35c2d588d..f900a93fa 100644 Binary files a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/5.png and b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/6.png b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/6.png index fff3807fd..05412e6d6 100644 Binary files a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/6.png and b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/7.png b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/7.png index 9e548bde8..07d6de655 100644 Binary files a/fastlane/metadata/android/nb-NO/images/phoneScreenshots/7.png and b/fastlane/metadata/android/nb-NO/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/nl/images/phoneScreenshots/1.png b/fastlane/metadata/android/nl/images/phoneScreenshots/1.png index 5a9beeef1..5605a8319 100644 Binary files a/fastlane/metadata/android/nl/images/phoneScreenshots/1.png and b/fastlane/metadata/android/nl/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/nl/images/phoneScreenshots/2.png b/fastlane/metadata/android/nl/images/phoneScreenshots/2.png index 0d12b0043..d503bd675 100644 Binary files a/fastlane/metadata/android/nl/images/phoneScreenshots/2.png and b/fastlane/metadata/android/nl/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/nl/images/phoneScreenshots/3.png b/fastlane/metadata/android/nl/images/phoneScreenshots/3.png index 71352d755..0a43eef47 100644 Binary files a/fastlane/metadata/android/nl/images/phoneScreenshots/3.png and b/fastlane/metadata/android/nl/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/nl/images/phoneScreenshots/4.png b/fastlane/metadata/android/nl/images/phoneScreenshots/4.png index 1022676f1..dead88501 100644 Binary files a/fastlane/metadata/android/nl/images/phoneScreenshots/4.png and b/fastlane/metadata/android/nl/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/nl/images/phoneScreenshots/5.png b/fastlane/metadata/android/nl/images/phoneScreenshots/5.png index d37238c12..857d999a8 100644 Binary files a/fastlane/metadata/android/nl/images/phoneScreenshots/5.png and b/fastlane/metadata/android/nl/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/nl/images/phoneScreenshots/6.png b/fastlane/metadata/android/nl/images/phoneScreenshots/6.png index 2bc15db64..833b7e922 100644 Binary files a/fastlane/metadata/android/nl/images/phoneScreenshots/6.png and b/fastlane/metadata/android/nl/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/nl/images/phoneScreenshots/7.png b/fastlane/metadata/android/nl/images/phoneScreenshots/7.png index f9c3c461a..b837ed5b3 100644 Binary files a/fastlane/metadata/android/nl/images/phoneScreenshots/7.png and b/fastlane/metadata/android/nl/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/nn/images/phoneScreenshots/1.png b/fastlane/metadata/android/nn/images/phoneScreenshots/1.png index 8d90d9b37..093c6c39a 100644 Binary files a/fastlane/metadata/android/nn/images/phoneScreenshots/1.png and b/fastlane/metadata/android/nn/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/nn/images/phoneScreenshots/2.png b/fastlane/metadata/android/nn/images/phoneScreenshots/2.png index 9fd360a64..2ed257108 100644 Binary files a/fastlane/metadata/android/nn/images/phoneScreenshots/2.png and b/fastlane/metadata/android/nn/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/nn/images/phoneScreenshots/3.png b/fastlane/metadata/android/nn/images/phoneScreenshots/3.png index 927ddecd0..57c909e7d 100644 Binary files a/fastlane/metadata/android/nn/images/phoneScreenshots/3.png and b/fastlane/metadata/android/nn/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/nn/images/phoneScreenshots/4.png b/fastlane/metadata/android/nn/images/phoneScreenshots/4.png index 825374766..b16da8406 100644 Binary files a/fastlane/metadata/android/nn/images/phoneScreenshots/4.png and b/fastlane/metadata/android/nn/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/nn/images/phoneScreenshots/5.png b/fastlane/metadata/android/nn/images/phoneScreenshots/5.png index c9ba7c3e0..77dd8e4f5 100644 Binary files a/fastlane/metadata/android/nn/images/phoneScreenshots/5.png and b/fastlane/metadata/android/nn/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/nn/images/phoneScreenshots/6.png b/fastlane/metadata/android/nn/images/phoneScreenshots/6.png index be4769665..486a92c63 100644 Binary files a/fastlane/metadata/android/nn/images/phoneScreenshots/6.png and b/fastlane/metadata/android/nn/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/nn/images/phoneScreenshots/7.png b/fastlane/metadata/android/nn/images/phoneScreenshots/7.png index 633599ce2..a424a0bcb 100644 Binary files a/fastlane/metadata/android/nn/images/phoneScreenshots/7.png and b/fastlane/metadata/android/nn/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/pl/images/phoneScreenshots/1.png b/fastlane/metadata/android/pl/images/phoneScreenshots/1.png index 01e38dc7f..8bb698c81 100644 Binary files a/fastlane/metadata/android/pl/images/phoneScreenshots/1.png and b/fastlane/metadata/android/pl/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/pl/images/phoneScreenshots/2.png b/fastlane/metadata/android/pl/images/phoneScreenshots/2.png index a1665b69e..e5104fdf0 100644 Binary files a/fastlane/metadata/android/pl/images/phoneScreenshots/2.png and b/fastlane/metadata/android/pl/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/pl/images/phoneScreenshots/3.png b/fastlane/metadata/android/pl/images/phoneScreenshots/3.png index b3e2d334c..79e1c8cdc 100644 Binary files a/fastlane/metadata/android/pl/images/phoneScreenshots/3.png and b/fastlane/metadata/android/pl/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/pl/images/phoneScreenshots/4.png b/fastlane/metadata/android/pl/images/phoneScreenshots/4.png index d66d85bbf..816a2d78e 100644 Binary files a/fastlane/metadata/android/pl/images/phoneScreenshots/4.png and b/fastlane/metadata/android/pl/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/pl/images/phoneScreenshots/5.png b/fastlane/metadata/android/pl/images/phoneScreenshots/5.png index d3f82ec46..754858c02 100644 Binary files a/fastlane/metadata/android/pl/images/phoneScreenshots/5.png and b/fastlane/metadata/android/pl/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/pl/images/phoneScreenshots/6.png b/fastlane/metadata/android/pl/images/phoneScreenshots/6.png index cf3ff0987..d8338d257 100644 Binary files a/fastlane/metadata/android/pl/images/phoneScreenshots/6.png and b/fastlane/metadata/android/pl/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/pl/images/phoneScreenshots/7.png b/fastlane/metadata/android/pl/images/phoneScreenshots/7.png index ed1aa3e5c..9cf6f6cae 100644 Binary files a/fastlane/metadata/android/pl/images/phoneScreenshots/7.png and b/fastlane/metadata/android/pl/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png index e09364a89..b0c3240de 100644 Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/2.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/2.png index 9ca056b3b..fcd83f21b 100644 Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/2.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/3.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/3.png index e2d92740f..72e7a5bd4 100644 Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/3.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/4.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/4.png index 7b5f3b674..9e0f5aa79 100644 Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/4.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/5.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/5.png index 8fdd9acfa..db56dd16f 100644 Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/5.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/6.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/6.png index 90ba8aaf1..3dae76240 100644 Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/6.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/7.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/7.png index ef878766c..9d7a1bb57 100644 Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/7.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/ro/images/phoneScreenshots/1.png b/fastlane/metadata/android/ro/images/phoneScreenshots/1.png index 1bed2e639..b3bb669f5 100644 Binary files a/fastlane/metadata/android/ro/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ro/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/ro/images/phoneScreenshots/2.png b/fastlane/metadata/android/ro/images/phoneScreenshots/2.png index c1b1f968c..71931d19b 100644 Binary files a/fastlane/metadata/android/ro/images/phoneScreenshots/2.png and b/fastlane/metadata/android/ro/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/ro/images/phoneScreenshots/3.png b/fastlane/metadata/android/ro/images/phoneScreenshots/3.png index bcfa87993..79db3e221 100644 Binary files a/fastlane/metadata/android/ro/images/phoneScreenshots/3.png and b/fastlane/metadata/android/ro/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/ro/images/phoneScreenshots/4.png b/fastlane/metadata/android/ro/images/phoneScreenshots/4.png index 7e56c6ef5..a9384fbe6 100644 Binary files a/fastlane/metadata/android/ro/images/phoneScreenshots/4.png and b/fastlane/metadata/android/ro/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/ro/images/phoneScreenshots/5.png b/fastlane/metadata/android/ro/images/phoneScreenshots/5.png index fd45fc167..04927bb34 100644 Binary files a/fastlane/metadata/android/ro/images/phoneScreenshots/5.png and b/fastlane/metadata/android/ro/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/ro/images/phoneScreenshots/6.png b/fastlane/metadata/android/ro/images/phoneScreenshots/6.png index d4f310193..444c8d903 100644 Binary files a/fastlane/metadata/android/ro/images/phoneScreenshots/6.png and b/fastlane/metadata/android/ro/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/ro/images/phoneScreenshots/7.png b/fastlane/metadata/android/ro/images/phoneScreenshots/7.png index 2f30e2968..ba3d16585 100644 Binary files a/fastlane/metadata/android/ro/images/phoneScreenshots/7.png and b/fastlane/metadata/android/ro/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/1.png b/fastlane/metadata/android/ru/images/phoneScreenshots/1.png index 59abfcced..f8c5321ef 100644 Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/2.png b/fastlane/metadata/android/ru/images/phoneScreenshots/2.png index 9f0d34127..6bf85ed4f 100644 Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/2.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/3.png b/fastlane/metadata/android/ru/images/phoneScreenshots/3.png index 962bb91c8..de4aa071c 100644 Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/3.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/4.png b/fastlane/metadata/android/ru/images/phoneScreenshots/4.png index 3add72aab..e5750ff6d 100644 Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/4.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/5.png b/fastlane/metadata/android/ru/images/phoneScreenshots/5.png index 4eb0476ff..d257fb384 100644 Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/5.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/6.png b/fastlane/metadata/android/ru/images/phoneScreenshots/6.png index 9e44c218f..f698f89ce 100644 Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/6.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/7.png b/fastlane/metadata/android/ru/images/phoneScreenshots/7.png index 4951e7996..fda462a26 100644 Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/7.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/sk/images/phoneScreenshots/1.png b/fastlane/metadata/android/sk/images/phoneScreenshots/1.png index ef90afd5a..97eb00138 100644 Binary files a/fastlane/metadata/android/sk/images/phoneScreenshots/1.png and b/fastlane/metadata/android/sk/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/sk/images/phoneScreenshots/2.png b/fastlane/metadata/android/sk/images/phoneScreenshots/2.png index 6ea4b89b9..3c4414785 100644 Binary files a/fastlane/metadata/android/sk/images/phoneScreenshots/2.png and b/fastlane/metadata/android/sk/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/sk/images/phoneScreenshots/3.png b/fastlane/metadata/android/sk/images/phoneScreenshots/3.png index c4f5aa63d..f5b6511b1 100644 Binary files a/fastlane/metadata/android/sk/images/phoneScreenshots/3.png and b/fastlane/metadata/android/sk/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/sk/images/phoneScreenshots/4.png b/fastlane/metadata/android/sk/images/phoneScreenshots/4.png index 6cc964f12..92e5a9df1 100644 Binary files a/fastlane/metadata/android/sk/images/phoneScreenshots/4.png and b/fastlane/metadata/android/sk/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/sk/images/phoneScreenshots/5.png b/fastlane/metadata/android/sk/images/phoneScreenshots/5.png index 43a137851..b99ed6e33 100644 Binary files a/fastlane/metadata/android/sk/images/phoneScreenshots/5.png and b/fastlane/metadata/android/sk/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/sk/images/phoneScreenshots/6.png b/fastlane/metadata/android/sk/images/phoneScreenshots/6.png index 615985626..ccf701513 100644 Binary files a/fastlane/metadata/android/sk/images/phoneScreenshots/6.png and b/fastlane/metadata/android/sk/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/sk/images/phoneScreenshots/7.png b/fastlane/metadata/android/sk/images/phoneScreenshots/7.png index 7855e4838..5534724ad 100644 Binary files a/fastlane/metadata/android/sk/images/phoneScreenshots/7.png and b/fastlane/metadata/android/sk/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/tr/images/phoneScreenshots/1.png b/fastlane/metadata/android/tr/images/phoneScreenshots/1.png index dd9987e27..a8d768ef9 100644 Binary files a/fastlane/metadata/android/tr/images/phoneScreenshots/1.png and b/fastlane/metadata/android/tr/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/tr/images/phoneScreenshots/2.png b/fastlane/metadata/android/tr/images/phoneScreenshots/2.png index 9f7ff6e8e..8ec0ecbb3 100644 Binary files a/fastlane/metadata/android/tr/images/phoneScreenshots/2.png and b/fastlane/metadata/android/tr/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/tr/images/phoneScreenshots/3.png b/fastlane/metadata/android/tr/images/phoneScreenshots/3.png index 20de8e056..a667c121c 100644 Binary files a/fastlane/metadata/android/tr/images/phoneScreenshots/3.png and b/fastlane/metadata/android/tr/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/tr/images/phoneScreenshots/4.png b/fastlane/metadata/android/tr/images/phoneScreenshots/4.png index da8f76c79..699f1e29f 100644 Binary files a/fastlane/metadata/android/tr/images/phoneScreenshots/4.png and b/fastlane/metadata/android/tr/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/tr/images/phoneScreenshots/5.png b/fastlane/metadata/android/tr/images/phoneScreenshots/5.png index 6601ee21a..78acdd853 100644 Binary files a/fastlane/metadata/android/tr/images/phoneScreenshots/5.png and b/fastlane/metadata/android/tr/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/tr/images/phoneScreenshots/6.png b/fastlane/metadata/android/tr/images/phoneScreenshots/6.png index 541c304a2..427eebf23 100644 Binary files a/fastlane/metadata/android/tr/images/phoneScreenshots/6.png and b/fastlane/metadata/android/tr/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/tr/images/phoneScreenshots/7.png b/fastlane/metadata/android/tr/images/phoneScreenshots/7.png index 001da561b..9f297340e 100644 Binary files a/fastlane/metadata/android/tr/images/phoneScreenshots/7.png and b/fastlane/metadata/android/tr/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/uk/images/phoneScreenshots/1.png b/fastlane/metadata/android/uk/images/phoneScreenshots/1.png index 6bd33fe07..3f73a6652 100644 Binary files a/fastlane/metadata/android/uk/images/phoneScreenshots/1.png and b/fastlane/metadata/android/uk/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/uk/images/phoneScreenshots/2.png b/fastlane/metadata/android/uk/images/phoneScreenshots/2.png index 87936023f..dae6783b0 100644 Binary files a/fastlane/metadata/android/uk/images/phoneScreenshots/2.png and b/fastlane/metadata/android/uk/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/uk/images/phoneScreenshots/3.png b/fastlane/metadata/android/uk/images/phoneScreenshots/3.png index 4290e81a9..aa8945fa3 100644 Binary files a/fastlane/metadata/android/uk/images/phoneScreenshots/3.png and b/fastlane/metadata/android/uk/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/uk/images/phoneScreenshots/4.png b/fastlane/metadata/android/uk/images/phoneScreenshots/4.png index 4a4cb3257..e28441b86 100644 Binary files a/fastlane/metadata/android/uk/images/phoneScreenshots/4.png and b/fastlane/metadata/android/uk/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/uk/images/phoneScreenshots/5.png b/fastlane/metadata/android/uk/images/phoneScreenshots/5.png index 7568e56aa..4418e6501 100644 Binary files a/fastlane/metadata/android/uk/images/phoneScreenshots/5.png and b/fastlane/metadata/android/uk/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/uk/images/phoneScreenshots/6.png b/fastlane/metadata/android/uk/images/phoneScreenshots/6.png index 66cad9b68..28cf95b24 100644 Binary files a/fastlane/metadata/android/uk/images/phoneScreenshots/6.png and b/fastlane/metadata/android/uk/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/uk/images/phoneScreenshots/7.png b/fastlane/metadata/android/uk/images/phoneScreenshots/7.png index 5e5198ff0..7d03e2d4d 100644 Binary files a/fastlane/metadata/android/uk/images/phoneScreenshots/7.png and b/fastlane/metadata/android/uk/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/vi/images/phoneScreenshots/1.png b/fastlane/metadata/android/vi/images/phoneScreenshots/1.png index f6deca26a..b263d13c1 100644 Binary files a/fastlane/metadata/android/vi/images/phoneScreenshots/1.png and b/fastlane/metadata/android/vi/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/vi/images/phoneScreenshots/2.png b/fastlane/metadata/android/vi/images/phoneScreenshots/2.png index 12d35f664..078f09ca0 100644 Binary files a/fastlane/metadata/android/vi/images/phoneScreenshots/2.png and b/fastlane/metadata/android/vi/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/vi/images/phoneScreenshots/3.png b/fastlane/metadata/android/vi/images/phoneScreenshots/3.png index 4b3faf29e..39fb4faae 100644 Binary files a/fastlane/metadata/android/vi/images/phoneScreenshots/3.png and b/fastlane/metadata/android/vi/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/vi/images/phoneScreenshots/4.png b/fastlane/metadata/android/vi/images/phoneScreenshots/4.png index 2f4b7efc4..a905ec4ba 100644 Binary files a/fastlane/metadata/android/vi/images/phoneScreenshots/4.png and b/fastlane/metadata/android/vi/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/vi/images/phoneScreenshots/5.png b/fastlane/metadata/android/vi/images/phoneScreenshots/5.png index 41571d022..5fc9215ec 100644 Binary files a/fastlane/metadata/android/vi/images/phoneScreenshots/5.png and b/fastlane/metadata/android/vi/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/vi/images/phoneScreenshots/6.png b/fastlane/metadata/android/vi/images/phoneScreenshots/6.png index 06ad94ba0..940c8696b 100644 Binary files a/fastlane/metadata/android/vi/images/phoneScreenshots/6.png and b/fastlane/metadata/android/vi/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/vi/images/phoneScreenshots/7.png b/fastlane/metadata/android/vi/images/phoneScreenshots/7.png index 7970586a0..8b377e490 100644 Binary files a/fastlane/metadata/android/vi/images/phoneScreenshots/7.png and b/fastlane/metadata/android/vi/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png index d9e7b1573..2903b3606 100644 Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png index d8b0242d3..25a6fb2a1 100644 Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png index 3f0627ce5..c703cf985 100644 Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/4.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/4.png index 124e5943c..a56e416f3 100644 Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/4.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/5.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/5.png index ef45bfcfb..3525b251d 100644 Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/5.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/6.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/6.png index 462d85e04..c755cf723 100644 Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/6.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/7.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/7.png index 44b97b4c7..1f7d88739 100644 Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/7.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/1.png b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/1.png index 633ed0bed..ccc1c93de 100644 Binary files a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/1.png and b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/2.png b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/2.png index 90e4c3b5f..0cc2fad55 100644 Binary files a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/2.png and b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/3.png b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/3.png index 3550a8cfd..67eb4b503 100644 Binary files a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/3.png and b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/4.png b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/4.png index 312db7cda..5d48b69c5 100644 Binary files a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/4.png and b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/5.png b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/5.png index 58d37c151..14916233f 100644 Binary files a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/5.png and b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/6.png b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/6.png index ca7ab4559..80467b75e 100644 Binary files a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/6.png and b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/7.png b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/7.png index 47a958104..6911abd49 100644 Binary files a/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/7.png and b/fastlane/metadata/android/zh-Hant/images/phoneScreenshots/7.png differ diff --git a/lib/geo/topojson.dart b/lib/geo/topojson.dart index de9961cd4..c03f7d553 100644 --- a/lib/geo/topojson.dart +++ b/lib/geo/topojson.dart @@ -55,7 +55,7 @@ class Topology extends TopologyJsonObject { final List>> arcs; final Transform? transform; - Topology.parse(Map data) + Topology.parse(super.data) : objects = Map.fromEntries((data['objects'] as Map).cast().entries.map((kv) { final name = kv.key; final geometry = Geometry.build(kv.value); @@ -63,7 +63,7 @@ class Topology extends TopologyJsonObject { }).whereNotNull()), arcs = (data['arcs'] as List).cast().map((arc) => arc.cast().map((position) => position.cast()).toList()).toList(), transform = data.containsKey('transform') ? Transform.parse((data['transform'] as Map).cast()) : null, - super.parse(data); + super.parse(); List> _arcAt(int index) { var arc = arcs[index < 0 ? ~index : index]; @@ -131,10 +131,10 @@ abstract class Geometry extends TopologyJsonObject { final dynamic id; final Map? properties; - Geometry.parse(Map data) + Geometry.parse(super.data) : id = data.containsKey('id') ? data['id'] : null, properties = data.containsKey('properties') ? data['properties'] as Map? : null, - super.parse(data); + super.parse(); static Geometry? build(Map data) { final type = _parseTopoJsonObjectType(data['type'] as String?); @@ -165,41 +165,41 @@ abstract class Geometry extends TopologyJsonObject { class Point extends Geometry { final List coordinates; - Point.parse(Map data) + Point.parse(super.data) : coordinates = (data['coordinates'] as List).cast(), - super.parse(data); + super.parse(); } class MultiPoint extends Geometry { final List> coordinates; - MultiPoint.parse(Map data) + MultiPoint.parse(super.data) : coordinates = (data['coordinates'] as List).cast().map((position) => position.cast()).toList(), - super.parse(data); + super.parse(); } class LineString extends Geometry { final List arcs; - LineString.parse(Map data) + LineString.parse(super.data) : arcs = (data['arcs'] as List).cast(), - super.parse(data); + super.parse(); } class MultiLineString extends Geometry { final List> arcs; - MultiLineString.parse(Map data) + MultiLineString.parse(super.data) : arcs = (data['arcs'] as List).cast().map((arc) => arc.cast()).toList(), - super.parse(data); + super.parse(); } class Polygon extends Geometry { final List> arcs; - Polygon.parse(Map data) + Polygon.parse(super.data) : arcs = (data['arcs'] as List).cast().map((arc) => arc.cast()).toList(), - super.parse(data); + super.parse(); List>>? _rings; @@ -217,9 +217,9 @@ class Polygon extends Geometry { class MultiPolygon extends Geometry { final List>> arcs; - MultiPolygon.parse(Map data) + MultiPolygon.parse(super.data) : arcs = (data['arcs'] as List).cast().map((polygon) => polygon.cast().map((arc) => arc.cast()).toList()).toList(), - super.parse(data); + super.parse(); List>>>? _polygons; @@ -237,9 +237,9 @@ class MultiPolygon extends Geometry { class GeometryCollection extends Geometry { final List geometries; - GeometryCollection.parse(Map data) + GeometryCollection.parse(super.data) : geometries = (data['geometries'] as List).cast>().map(Geometry.build).whereNotNull().toList(), - super.parse(data); + super.parse(); @override bool containsPoint(Topology topology, List point) { diff --git a/lib/l10n/app_be.arb b/lib/l10n/app_be.arb index f5d2e9766..04857893c 100644 --- a/lib/l10n/app_be.arb +++ b/lib/l10n/app_be.arb @@ -690,5 +690,145 @@ "viewerTransitionFade": "Згасанне", "@viewerTransitionFade": {}, "widgetDisplayedItemRandom": "Выпадковы", - "@widgetDisplayedItemRandom": {} + "@widgetDisplayedItemRandom": {}, + "viewerOpenPanoramaButtonLabel": "АДКРЫЦЬ ПАНАРАМУ", + "@viewerOpenPanoramaButtonLabel": {}, + "aboutDataUsageCache": "Кэш", + "@aboutDataUsageCache": {}, + "statsTopStatesSectionTitle": "Топ краін", + "@statsTopStatesSectionTitle": {}, + "viewerInfoLabelUri": "URI", + "@viewerInfoLabelUri": {}, + "aboutDataUsageClearCache": "Ачысціць кэш", + "@aboutDataUsageClearCache": {}, + "viewerInfoSearchSuggestionRights": "Правы", + "@viewerInfoSearchSuggestionRights": {}, + "setCoverDialogCustom": "Свая", + "@setCoverDialogCustom": {}, + "aboutBugCopyInfoInstruction": "Скапіяваць сістэмную інфармацыю", + "@aboutBugCopyInfoInstruction": {}, + "vaultBinUsageDialogMessage": "Некаторыя сховішчы выкарыстоўваюць кошык.", + "@vaultBinUsageDialogMessage": {}, + "aboutBugSaveLogInstruction": "Захаваць журналы праграмы ў файл", + "@aboutBugSaveLogInstruction": {}, + "aboutBugReportInstruction": "Адправіць справаздачу аб памылцы на GitHub разам з журналамі і сістэмнай інфармацыяй", + "@aboutBugReportInstruction": {}, + "entryActionCast": "Трансляцыя", + "@entryActionCast": {}, + "hideFilterConfirmationDialogMessage": "Адпаведныя фота і відэа будуць схаваны з вашай калекцыі. Вы можаце убачыць іх зноў у наладах «Прыватнасць».\n\nВы ўпэўнены, што хочаце іх схаваць?", + "@hideFilterConfirmationDialogMessage": {}, + "renameEntrySetPagePatternFieldLabel": "Шаблон наймення", + "@renameEntrySetPagePatternFieldLabel": {}, + "renameAlbumDialogLabel": "Новая назва", + "@renameAlbumDialogLabel": {}, + "renameAlbumDialogLabelAlreadyExistsHelper": "Каталог ужо ёсць", + "@renameAlbumDialogLabelAlreadyExistsHelper": {}, + "aboutBugReportButton": "Адправіць справаздачу", + "@aboutBugReportButton": {}, + "renameEntrySetPageTitle": "Перайменаваць", + "@renameEntrySetPageTitle": {}, + "aboutBugSectionTitle": "Паведаміць пра памылку", + "@aboutBugSectionTitle": {}, + "aboutBugCopyInfoButton": "Скапіяваць", + "@aboutBugCopyInfoButton": {}, + "binEntriesConfirmationDialogMessage": "{count, plural, =1{Перамясціць гэты элемент у кошык?} few{Перамясціць гэтыя {count} элемента у кошык?} other{Перамясціць гэтыя {count} элементаў у кошык?}}", + "@binEntriesConfirmationDialogMessage": { + "placeholders": { + "count": {} + } + }, + "settingsLanguageTile": "Мова", + "@settingsLanguageTile": {}, + "settingsRemoveAnimationsDialogTitle": "Выдаліць анімацыі", + "@settingsRemoveAnimationsDialogTitle": {}, + "settingsDisplayUseTvInterface": "Інтэрфейс Android TV", + "@settingsDisplayUseTvInterface": {}, + "settingsTimeToTakeActionTile": "Час выканання", + "@settingsTimeToTakeActionTile": {}, + "exportEntryDialogFormat": "Фармат:", + "@exportEntryDialogFormat": {}, + "exportEntryDialogQuality": "Якасць", + "@exportEntryDialogQuality": {}, + "deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Выдаліць гэты элемент?} few{Выдаліць гэтыя {count} элемента?} other{Выдаліць гэтыя {count} элементаў?}}", + "@deleteEntriesConfirmationDialogMessage": { + "placeholders": { + "count": {} + } + }, + "editEntryDateDialogTitle": "Дата і час", + "@editEntryDateDialogTitle": {}, + "statsTopCountriesSectionTitle": "Топ Краін", + "@statsTopCountriesSectionTitle": {}, + "settingsWidgetDisplayedItem": "Адлюстраваны элемент", + "@settingsWidgetDisplayedItem": {}, + "settingsLanguagePageTitle": "Мова", + "@settingsLanguagePageTitle": {}, + "settingsWidgetOpenPage": "Пры націску на віджэт", + "@settingsWidgetOpenPage": {}, + "renameProcessorName": "Імя", + "@renameProcessorName": {}, + "exportEntryDialogHeight": "Вышыня", + "@exportEntryDialogHeight": {}, + "exportEntryDialogWidth": "Шырыня", + "@exportEntryDialogWidth": {}, + "settingsWidgetShowOutline": "Вылучэнне", + "@settingsWidgetShowOutline": {}, + "settingsScreenSaverPageTitle": "Застаўка на экран", + "@settingsScreenSaverPageTitle": {}, + "settingsDisplayRefreshRateModeTile": "Частата абнаўлення экрана", + "@settingsDisplayRefreshRateModeTile": {}, + "settingsCoordinateFormatDialogTitle": "Фармат каардынат", + "@settingsCoordinateFormatDialogTitle": {}, + "settingsUnitSystemTile": "Адзінкі вымярэння", + "@settingsUnitSystemTile": {}, + "settingsCoordinateFormatTile": "Фармат каардынат", + "@settingsCoordinateFormatTile": {}, + "newVaultWarningDialogMessage": "Элементы ў сховішчах даступныя толькі гэтай праграме і нікому больш.\n\nКалі вы выдаліце гэту праграму або ачысціце даныя праграмы, вы страціце ўсе гэтыя элементы.", + "@newVaultWarningDialogMessage": {}, + "settingsDisplayRefreshRateModeDialogTitle": "Частата абнаўлення", + "@settingsDisplayRefreshRateModeDialogTitle": {}, + "settingsAccessibilityShowPinchGestureAlternatives": "Паказваць альтэрнатывы мультисенсорным жэстам", + "@settingsAccessibilityShowPinchGestureAlternatives": {}, + "settingsLanguageSectionTitle": "Мова і фарматы", + "@settingsLanguageSectionTitle": {}, + "renameEntrySetPagePreviewSectionTitle": "Папярэдні прагляд", + "@renameEntrySetPagePreviewSectionTitle": {}, + "settingsDisplaySectionTitle": "Адлюстраванне", + "@settingsDisplaySectionTitle": {}, + "settingsWidgetPageTitle": "Фотарамка", + "@settingsWidgetPageTitle": {}, + "editEntryDialogTargetFieldsHeader": "Поля для рэдагавання", + "@editEntryDialogTargetFieldsHeader": {}, + "renameProcessorCounter": "Лічыльнік", + "@renameProcessorCounter": {}, + "statsPageTitle": "Статыстыка", + "@statsPageTitle": {}, + "settingsUnitSystemDialogTitle": "Адзінкі вымярэння", + "@settingsUnitSystemDialogTitle": {}, + "editEntryDialogCopyFromItem": "Скапіюваць з іншага элемента", + "@editEntryDialogCopyFromItem": {}, + "settingsThemeEnableDynamicColor": "Дынамічны колер", + "@settingsThemeEnableDynamicColor": {}, + "renameEntryDialogLabel": "Новая назва", + "@renameEntryDialogLabel": {}, + "renameEntrySetPageInsertTooltip": "Поле для ўстаўкі", + "@renameEntrySetPageInsertTooltip": {}, + "settingsThemeBrightnessTile": "Тэма", + "@settingsThemeBrightnessTile": {}, + "settingsSystemDefault": "Як у сістэме", + "@settingsSystemDefault": {}, + "settingsCollectionTile": "Калекцыя", + "@settingsCollectionTile": {}, + "settingsThemeBrightnessDialogTitle": "Тэма", + "@settingsThemeBrightnessDialogTitle": {}, + "deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Выдаліць гэты альбом і элемент у ім?} few{Выдаліць гэты альбом і {count} элементы у ім?} other{Выдаліць гэты альбом і {count} элементаў у ім?}}", + "@deleteSingleAlbumConfirmationDialogMessage": { + "placeholders": { + "count": {} + } + }, + "settingsThemeColorHighlights": "Каляровыя акцэнты", + "@settingsThemeColorHighlights": {}, + "exportEntryDialogWriteMetadata": "Запісаць метададзеныя", + "@exportEntryDialogWriteMetadata": {} } diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb index a8e75435d..cbe9d7fc7 100644 --- a/lib/l10n/app_cs.arb +++ b/lib/l10n/app_cs.arb @@ -1307,7 +1307,7 @@ "@settingsSlideshowShuffle": {}, "settingsSlideshowAnimatedZoomEffect": "Animovaný efekt přiblížení", "@settingsSlideshowAnimatedZoomEffect": {}, - "settingsSlideshowIntervalTile": "Interval", + "settingsSlideshowIntervalTile": "Prodleva", "@settingsSlideshowIntervalTile": {}, "settingsSubtitleThemeSample": "Toto je příklad.", "@settingsSubtitleThemeSample": {}, @@ -1510,5 +1510,13 @@ "overlayHistogramRGB": "RGB", "@overlayHistogramRGB": {}, "settingsViewerShowHistogram": "Zobrazit histogram", - "@settingsViewerShowHistogram": {} + "@settingsViewerShowHistogram": {}, + "overlayHistogramLuminance": "Světelnost", + "@overlayHistogramLuminance": {}, + "aboutDataUsageClearCache": "Vymazat mezipaměť", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "Promítat", + "@entryActionCast": {}, + "castDialogTitle": "Zařízení pro promítání", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index afcdf2276..b8b4be190 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -1346,5 +1346,9 @@ "aboutDataUsageExternal": "Extern", "@aboutDataUsageExternal": {}, "aboutDataUsageDatabase": "Datenbank", - "@aboutDataUsageDatabase": {} + "@aboutDataUsageDatabase": {}, + "overlayHistogramLuminance": "Leuchtdichte", + "@overlayHistogramLuminance": {}, + "overlayHistogramRGB": "RGB", + "@overlayHistogramRGB": {} } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 65d5ab093..2b32b01fb 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -112,6 +112,7 @@ "entryActionEdit": "Edit", "entryActionOpen": "Open with", "entryActionSetAs": "Set as", + "entryActionCast": "Cast", "entryActionOpenMap": "Show in map app", "entryActionRotateScreen": "Rotate screen", "entryActionAddFavourite": "Add to favorites", @@ -518,6 +519,8 @@ "tileLayoutGrid": "Grid", "tileLayoutList": "List", + "castDialogTitle": "Cast Devices", + "coverDialogTabCover": "Cover", "coverDialogTabApp": "App", "coverDialogTabColor": "Color", @@ -543,6 +546,7 @@ "aboutDataUsageMisc": "Misc", "aboutDataUsageInternal": "Internal", "aboutDataUsageExternal": "External", + "aboutDataUsageClearCache": "Clear Cache", "aboutCreditsSectionTitle": "Credits", "aboutCreditsWorldAtlas1": "This app uses a TopoJSON file from", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 40983be6a..c90567521 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -1354,5 +1354,11 @@ "overlayHistogramRGB": "RGB", "@overlayHistogramRGB": {}, "settingsViewerShowHistogram": "Mostrar el histograma", - "@settingsViewerShowHistogram": {} + "@settingsViewerShowHistogram": {}, + "aboutDataUsageClearCache": "Borrar la cache", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "Echar", + "@entryActionCast": {}, + "castDialogTitle": "Dispositivos Cast", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_eu.arb b/lib/l10n/app_eu.arb index 4324e60c7..2f13c23b4 100644 --- a/lib/l10n/app_eu.arb +++ b/lib/l10n/app_eu.arb @@ -691,7 +691,7 @@ "@collectionActionHideTitleSearch": {}, "collectionActionAddShortcut": "Lasterbidea gehitu", "@collectionActionAddShortcut": {}, - "collectionActionCopy": "Albumera kopiatu", + "collectionActionCopy": "Kopiatu albumera", "@collectionActionCopy": {}, "aboutLicensesSectionTitle": "Kode-irekiko lizentziak", "@aboutLicensesSectionTitle": {}, @@ -867,7 +867,7 @@ "@albumGroupType": {}, "albumGroupNone": "Ez taldekatu", "@albumGroupNone": {}, - "albumPickPageTitleCopy": "Albumera kopiatu", + "albumPickPageTitleCopy": "Kopiatu albumera", "@albumPickPageTitleCopy": {}, "albumPickPageTitleExport": "Albumera esportatu", "@albumPickPageTitleExport": {}, @@ -933,7 +933,7 @@ "@settingsHomeTile": {}, "settingsShowBottomNavigationBar": "Azpiko nabigazio-barra erakutsi", "@settingsShowBottomNavigationBar": {}, - "settingsKeepScreenOnDialogTitle": "Pantaila piztuta mantendu", + "settingsKeepScreenOnDialogTitle": "Mantendu pantaila piztuta", "@settingsKeepScreenOnDialogTitle": {}, "settingsDoubleBackExit": "Sakatu «atzera» bi aldiz irteteko", "@settingsDoubleBackExit": {}, @@ -1003,7 +1003,7 @@ "@settingsDefault": {}, "settingsActionExportDialogTitle": "Esportazio", "@settingsActionExportDialogTitle": {}, - "settingsKeepScreenOnTile": "Pantaila piztuta mantendu", + "settingsKeepScreenOnTile": "Mantendu pantaila piztuta", "@settingsKeepScreenOnTile": {}, "settingsActionImportDialogTitle": "Inportazio", "@settingsActionImportDialogTitle": {}, @@ -1512,5 +1512,11 @@ "overlayHistogramRGB": "RGB", "@overlayHistogramRGB": {}, "settingsViewerShowHistogram": "Erakutsi histograma", - "@settingsViewerShowHistogram": {} + "@settingsViewerShowHistogram": {}, + "aboutDataUsageClearCache": "Garbitu cachea", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "Igorri", + "@entryActionCast": {}, + "castDialogTitle": "Igortzeko gailuak", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_fa.arb b/lib/l10n/app_fa.arb index f0e0a19ae..5c0d262aa 100644 --- a/lib/l10n/app_fa.arb +++ b/lib/l10n/app_fa.arb @@ -31,7 +31,7 @@ "@pickTooltip": {}, "actionRemove": "حذف", "@actionRemove": {}, - "chipActionGoToTagPage": "نمایش در برچسب ها", + "chipActionGoToTagPage": "نمایش در برچسب‌ها", "@chipActionGoToTagPage": {}, "applyButtonLabel": "اعمال کردن", "@applyButtonLabel": {}, @@ -81,7 +81,7 @@ "count": {} } }, - "chipActionGoToAlbumPage": "نمایش در البوم ها", + "chipActionGoToAlbumPage": "نمایش در البوم‌ها", "@chipActionGoToAlbumPage": {}, "filterNoTagLabel": "بدون برچسب", "@filterNoTagLabel": {}, @@ -109,7 +109,7 @@ "@entryActionInfo": {}, "entryActionRotateCCW": "چرخش پادساعت‌گرد", "@entryActionRotateCCW": {}, - "chipActionGoToCountryPage": "نمایش در کشور ها", + "chipActionGoToCountryPage": "نمایش در کشورها", "@chipActionGoToCountryPage": {}, "entryActionConvertMotionPhotoToStillImage": "تبدیل به عکس ثابت", "@entryActionConvertMotionPhotoToStillImage": {}, @@ -363,7 +363,7 @@ "@nameConflictStrategyReplace": {}, "displayRefreshRatePreferHighest": "بیشترین مقدار", "@displayRefreshRatePreferHighest": {}, - "storageAccessDialogMessage": "لطفا فولدر {directory} در {volume} را در صفحه بعد انتخاب کنید و اجازه را به برنامه بدهید.", + "storageAccessDialogMessage": "لطفا شاخهٔ {directory} در {volume} را در صفحه بعد انتخاب کنید و اجازه را به برنامه بدهید.", "@storageAccessDialogMessage": { "placeholders": { "directory": { @@ -376,5 +376,173 @@ "description": "the name of a storage volume" } } - } + }, + "widgetOpenPageViewer": "باز کردن مشاهده‌گر", + "@widgetOpenPageViewer": {}, + "saveCopyButtonLabel": "ذخیرهٔ رونوشت", + "@saveCopyButtonLabel": {}, + "editorActionTransform": "تبدیل", + "@editorActionTransform": {}, + "viewerActionLock": "قفل پخش‌کننده", + "@viewerActionLock": {}, + "viewerTransitionSlide": "اسلاید", + "@viewerTransitionSlide": {}, + "editorTransformRotate": "چرخش", + "@editorTransformRotate": {}, + "albumTierNew": "جدید", + "@albumTierNew": {}, + "videoResumptionModeNever": "هیچ‌وقت", + "@videoResumptionModeNever": {}, + "vaultLockTypePassword": "رمز عبور", + "@vaultLockTypePassword": {}, + "widgetOpenPageHome": "باز کردن خانه", + "@widgetOpenPageHome": {}, + "keepScreenOnAlways": "همیشه", + "@keepScreenOnAlways": {}, + "albumTierRegular": "سایر", + "@albumTierRegular": {}, + "accessibilityAnimationsKeep": "نمایش از جلوه های نمایشگر", + "@accessibilityAnimationsKeep": {}, + "widgetDisplayedItemMostRecent": "جدید‌ترین", + "@widgetDisplayedItemMostRecent": {}, + "videoResumptionModeAlways": "همیشه", + "@videoResumptionModeAlways": {}, + "wallpaperTargetLock": "صفحهٔ قفل", + "@wallpaperTargetLock": {}, + "accessibilityAnimationsRemove": "جلوگیری از جلوه‌های نمایشگر", + "@accessibilityAnimationsRemove": {}, + "vaultLockTypePattern": "الگو", + "@vaultLockTypePattern": {}, + "albumTierVaults": "گنجینه‌ها", + "@albumTierVaults": {}, + "filterAspectRatioLandscapeLabel": "افقی", + "@filterAspectRatioLandscapeLabel": {}, + "filterAspectRatioPortraitLabel": "عمودی", + "@filterAspectRatioPortraitLabel": {}, + "filterTypeGeotiffLabel": "GeoTIFF", + "@filterTypeGeotiffLabel": {}, + "videoControlsNone": "هیچ‌کدام", + "@videoControlsNone": {}, + "otherDirectoryDescription": "شاخهٔ \"{name}\"", + "@otherDirectoryDescription": { + "placeholders": { + "name": { + "type": "String", + "example": "Pictures", + "description": "the name of a specific directory" + } + } + }, + "viewerTransitionZoomIn": "زوم‌پیش", + "@viewerTransitionZoomIn": {}, + "rootDirectoryDescription": "شاخهٔ ریشه", + "@rootDirectoryDescription": {}, + "maxBrightnessAlways": "همیشه", + "@maxBrightnessAlways": {}, + "entryActionCast": "بازتاب", + "@entryActionCast": {}, + "viewerActionUnlock": "باز کردن قفل پخش‌کننده", + "@viewerActionUnlock": {}, + "videoActionPause": "مکث", + "@videoActionPause": {}, + "widgetOpenPageCollection": "باز کردن مجموعه", + "@widgetOpenPageCollection": {}, + "clearTooltip": "پاک‌کردن", + "@clearTooltip": {}, + "nameConflictStrategySkip": "رد شدن", + "@nameConflictStrategySkip": {}, + "filterTypeRawLabel": "خام", + "@filterTypeRawLabel": {}, + "keepScreenOnVideoPlayback": "هنگام پخش ویدیو", + "@keepScreenOnVideoPlayback": {}, + "vaultLockTypePin": "PIN", + "@vaultLockTypePin": {}, + "editorTransformCrop": "برش", + "@editorTransformCrop": {}, + "restrictedAccessDialogMessage": "این برنامه اجازهٔ ویرایش پرونده‌ها ی شاخهٔ {directory} در \"{volume}\" را ندارد.\n\nلطفا از یک برنامهٔ گالری یا مدیر پروندهٔ از پیش نصب شده استفاده کنید.", + "@restrictedAccessDialogMessage": { + "placeholders": { + "directory": { + "type": "String", + "description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "albumTierPinned": "سنجاق شده", + "@albumTierPinned": {}, + "chipActionCreateVault": "ایجاد گنجینه", + "@chipActionCreateVault": {}, + "chipActionGoToPlacePage": "نمایش در مکان‌ها", + "@chipActionGoToPlacePage": {}, + "viewerTransitionNone": "هیچ‌کدام", + "@viewerTransitionNone": {}, + "lengthUnitPercent": "٪", + "@lengthUnitPercent": {}, + "maxBrightnessNever": "هیچ‌وقت", + "@maxBrightnessNever": {}, + "widgetTapUpdateWidget": "به‌روز رسانی ابزارک", + "@widgetTapUpdateWidget": {}, + "viewerTransitionFade": "محو شدن", + "@viewerTransitionFade": {}, + "chipActionConfigureVault": "پیکربندی گنجینه", + "@chipActionConfigureVault": {}, + "cropAspectRatioFree": "آزاد", + "@cropAspectRatioFree": {}, + "chipActionLock": "قفل", + "@chipActionLock": {}, + "notEnoughSpaceDialogMessage": "این کار برای کامل شدن به {neededSize} فضای خالی در \"{volume}\" نیاز دارد اما فقط {freeSize} وجود دارد.", + "@notEnoughSpaceDialogMessage": { + "placeholders": { + "neededSize": { + "type": "String", + "example": "314 MB" + }, + "freeSize": { + "type": "String", + "example": "123 MB" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "videoActionPlay": "پخش", + "@videoActionPlay": {}, + "applyTooltip": "اعمال", + "@applyTooltip": {}, + "keepScreenOnViewerOnly": "فقط در صفحهٔ مشاهده‌ٔ تصویر", + "@keepScreenOnViewerOnly": {}, + "wallpaperTargetHome": "صفحهٔ خانه", + "@wallpaperTargetHome": {}, + "videoActionSelectStreams": "انتخاب قطعه‌ٔ صوتی", + "@videoActionSelectStreams": {}, + "widgetDisplayedItemRandom": "تصادفی", + "@widgetDisplayedItemRandom": {}, + "lengthUnitPixel": "پیکسل", + "@lengthUnitPixel": {}, + "slideshowActionResume": "ادامه", + "@slideshowActionResume": {}, + "cropAspectRatioSquare": "مربع", + "@cropAspectRatioSquare": {}, + "chipActionShowCountryStates": "نمایش شهر‌ها", + "@chipActionShowCountryStates": {}, + "cropAspectRatioOriginal": "اصلی", + "@cropAspectRatioOriginal": {}, + "keepScreenOnNever": "هیچ‌وقت", + "@keepScreenOnNever": {}, + "filterTypeAnimatedLabel": "متحرک", + "@filterTypeAnimatedLabel": {}, + "settingsVideoEnablePip": "تصویر در تصویر", + "@settingsVideoEnablePip": {}, + "wallpaperTargetHomeLock": "صفحهٔ خانه و صفحهٔ قفل", + "@wallpaperTargetHomeLock": {}, + "viewDialogSortSectionTitle": "ترتیب بندی", + "@viewDialogSortSectionTitle": {} } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index f23b7ebe8..5136e5c9d 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -1354,5 +1354,11 @@ "settingsViewerShowHistogram": "Afficher l'histogramme", "@settingsViewerShowHistogram": {}, "overlayHistogramNone": "Aucun", - "@overlayHistogramNone": {} + "@overlayHistogramNone": {}, + "aboutDataUsageClearCache": "Vider le cache", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "Caster", + "@entryActionCast": {}, + "castDialogTitle": "Appareils", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_hu.arb b/lib/l10n/app_hu.arb index 29a266694..ff32de97a 100644 --- a/lib/l10n/app_hu.arb +++ b/lib/l10n/app_hu.arb @@ -1512,5 +1512,11 @@ "overlayHistogramLuminance": "Fényerő", "@overlayHistogramLuminance": {}, "settingsViewerShowHistogram": "Hisztogram megjelenítése", - "@settingsViewerShowHistogram": {} + "@settingsViewerShowHistogram": {}, + "aboutDataUsageClearCache": "Gyorsítótár törlése", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "Kivetítés", + "@entryActionCast": {}, + "castDialogTitle": "Kivetítő eszközök", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index acbbcaf26..3a02840fa 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -1354,5 +1354,7 @@ "overlayHistogramLuminance": "Kecerahan", "@overlayHistogramLuminance": {}, "settingsViewerShowHistogram": "Tampilkan histogram", - "@settingsViewerShowHistogram": {} + "@settingsViewerShowHistogram": {}, + "aboutDataUsageClearCache": "Bersihkan cache", + "@aboutDataUsageClearCache": {} } diff --git a/lib/l10n/app_is.arb b/lib/l10n/app_is.arb new file mode 100644 index 000000000..ef10be1b8 --- /dev/null +++ b/lib/l10n/app_is.arb @@ -0,0 +1,1522 @@ +{ + "aboutCreditsWorldAtlas2": "með ISC-notkunarleyfi.", + "@aboutCreditsWorldAtlas2": {}, + "binEntriesConfirmationDialogMessage": "{count, plural, =1{Færa þetta atriði í ruslmöppuna?} other{Færa þessi {count} atriði í ruslmöppuna?}}", + "@binEntriesConfirmationDialogMessage": { + "placeholders": { + "count": {} + } + }, + "settingsNavigationDrawerAddAlbum": "Bæta við albúmi", + "@settingsNavigationDrawerAddAlbum": {}, + "settingsPrivacySectionTitle": "Persónuvernd", + "@settingsPrivacySectionTitle": {}, + "filePickerOpenFrom": "Opið frá", + "@filePickerOpenFrom": {}, + "settingsEnableBinSubtitle": "Halda eyddum atriðum í 30 daga", + "@settingsEnableBinSubtitle": {}, + "viewerInfoPageTitle": "Upplýsingar", + "@viewerInfoPageTitle": {}, + "mapAttributionStamen": "Kortagögn frá © [OpenStreetMap](https://www.openstreetmap.org/copyright) þátttakendum • Kortatíglar frá [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)", + "@mapAttributionStamen": {}, + "widgetOpenPageViewer": "Opna skoðara", + "@widgetOpenPageViewer": {}, + "saveCopyButtonLabel": "VISTA AFRIT", + "@saveCopyButtonLabel": {}, + "setCoverDialogLatest": "Síðasta atriðið", + "@setCoverDialogLatest": {}, + "settingsThumbnailShowRating": "Birta einkunn", + "@settingsThumbnailShowRating": {}, + "albumScreenRecordings": "Skjáupptökur", + "@albumScreenRecordings": {}, + "editEntryRatingDialogTitle": "Einkunn", + "@editEntryRatingDialogTitle": {}, + "entryInfoActionExportMetadata": "Flytja út lýsigögn", + "@entryInfoActionExportMetadata": {}, + "aboutDataUsageMisc": "Ýmislegt", + "@aboutDataUsageMisc": {}, + "editorActionTransform": "Ummyndun", + "@editorActionTransform": {}, + "collectionExportFailureFeedback": "{count, plural, =1{Mistókst að flytja út 1 atriði} other{Mistókst að flytja út {count} atriðum}}", + "@collectionExportFailureFeedback": { + "placeholders": { + "count": {} + } + }, + "vaultDialogLockModeWhenScreenOff": "Læsa þegar skjárinn slekkur á sér", + "@vaultDialogLockModeWhenScreenOff": {}, + "videoResumeDialogMessage": "Viltu halda afspilun áfram frá {time}?", + "@videoResumeDialogMessage": { + "placeholders": { + "time": { + "type": "String", + "example": "13:37" + } + } + }, + "albumVideoCaptures": "Upptökur myndskeiða", + "@albumVideoCaptures": {}, + "wallpaperUseScrollEffect": "Nota skrunbrellur á upphafsskjá", + "@wallpaperUseScrollEffect": {}, + "coordinateDms": "{coordinate} {direction}", + "@coordinateDms": { + "placeholders": { + "coordinate": { + "type": "String", + "example": "38° 41′ 47.72″" + }, + "direction": { + "type": "String", + "example": "S" + } + } + }, + "editEntryDateDialogSetCustom": "Stilla sérsniðna dagsetningu", + "@editEntryDateDialogSetCustom": {}, + "mapStyleGoogleTerrain": "Google Maps (yfirborð)", + "@mapStyleGoogleTerrain": {}, + "statsTopTagsSectionTitle": "Algengustu merki", + "@statsTopTagsSectionTitle": {}, + "settingsSubtitleThemeTextAlignmentDialogTitle": "Jöfnun texta", + "@settingsSubtitleThemeTextAlignmentDialogTitle": {}, + "settingsSearchEmpty": "Engin samsvarandi stilling", + "@settingsSearchEmpty": {}, + "removeEntryMetadataDialogMore": "Meira", + "@removeEntryMetadataDialogMore": {}, + "viewerActionLock": "Læsa skoðara", + "@viewerActionLock": {}, + "pinDialogConfirm": "Staðfestu PIN-númer", + "@pinDialogConfirm": {}, + "settingsViewerSlideshowPageTitle": "Skyggnusýning", + "@settingsViewerSlideshowPageTitle": {}, + "tooManyItemsErrorDialogMessage": "Reyndu aftur með færri atriðum.", + "@tooManyItemsErrorDialogMessage": {}, + "collectionActionEdit": "Breyta", + "@collectionActionEdit": {}, + "viewerInfoSearchSuggestionResolution": "Upplausn", + "@viewerInfoSearchSuggestionResolution": {}, + "viewerTransitionSlide": "Renna", + "@viewerTransitionSlide": {}, + "sortOrderLargestFirst": "Stærst fyrst", + "@sortOrderLargestFirst": {}, + "viewerTransitionParallax": "Hliðrun", + "@viewerTransitionParallax": {}, + "settingsViewerSectionTitle": "Skoðari", + "@settingsViewerSectionTitle": {}, + "mapStyleStamenWatercolor": "Stamen-vatnslitir", + "@mapStyleStamenWatercolor": {}, + "tagPlaceholderCountry": "Land", + "@tagPlaceholderCountry": {}, + "editEntryDateDialogSourceFileModifiedDate": "Breytingardagsetning skráar", + "@editEntryDateDialogSourceFileModifiedDate": {}, + "editorTransformRotate": "Snúa", + "@editorTransformRotate": {}, + "collectionEmptyFavourites": "Engin eftirlæti", + "@collectionEmptyFavourites": {}, + "albumTierNew": "Nýtt", + "@albumTierNew": {}, + "removeEntryMetadataDialogTitle": "Fjarlæging lýsigagna", + "@removeEntryMetadataDialogTitle": {}, + "settingsLanguageTile": "Tungumál", + "@settingsLanguageTile": {}, + "drawerCollectionVideos": "Myndskeið", + "@drawerCollectionVideos": {}, + "videoResumptionModeNever": "Aldrei", + "@videoResumptionModeNever": {}, + "countryEmpty": "Engin lönd", + "@countryEmpty": {}, + "collectionSelectSectionTooltip": "Velja hluta", + "@collectionSelectSectionTooltip": {}, + "settingsAllowInstalledAppAccessSubtitle": "Notað til að bæta birtingu albúma", + "@settingsAllowInstalledAppAccessSubtitle": {}, + "aboutLicensesBanner": "Þetta forrit notar eftirfarandi pakka og aðgerðasöfn með opnum grunnkóða.", + "@aboutLicensesBanner": {}, + "moveUndatedConfirmationDialogSetDate": "Vista dagsetningar", + "@moveUndatedConfirmationDialogSetDate": {}, + "coordinateDmsNorth": "N", + "@coordinateDmsNorth": {}, + "dateYesterday": "Í gær", + "@dateYesterday": {}, + "viewerInfoLabelDate": "Dagsetning", + "@viewerInfoLabelDate": {}, + "nameConflictStrategyReplace": "Skipta út", + "@nameConflictStrategyReplace": {}, + "chipActionHide": "Fela", + "@chipActionHide": {}, + "aboutDataUsageDatabase": "Gagnagrunnur", + "@aboutDataUsageDatabase": {}, + "collectionMoveFailureFeedback": "{count, plural, =1{Mistókst að flytja 1 atriði} other{Mistókst að flytja {count} atriðum}}", + "@collectionMoveFailureFeedback": { + "placeholders": { + "count": {} + } + }, + "videoLoopModeAlways": "Alltaf", + "@videoLoopModeAlways": {}, + "settingsRemoveAnimationsDialogTitle": "Fjarlægja hreyfimyndir", + "@settingsRemoveAnimationsDialogTitle": {}, + "tileLayoutMosaic": "Mósaík", + "@tileLayoutMosaic": {}, + "settingsSubtitleThemeTextColor": "Litur texta", + "@settingsSubtitleThemeTextColor": {}, + "collectionDeselectSectionTooltip": "Afvelja hluta", + "@collectionDeselectSectionTooltip": {}, + "settingsKeepScreenOnTile": "Halda skjá í gangi", + "@settingsKeepScreenOnTile": {}, + "tileLayoutGrid": "Reitir", + "@tileLayoutGrid": {}, + "settingsDisplayUseTvInterface": "Android TV viðmót", + "@settingsDisplayUseTvInterface": {}, + "settingsTimeToTakeActionTile": "Tími til að grípa til aðgerða", + "@settingsTimeToTakeActionTile": {}, + "aboutCreditsWorldAtlas1": "Þetta forrit notar TopoJSON-skrá frá", + "@aboutCreditsWorldAtlas1": {}, + "tagEditorSectionRecent": "Nýlegt", + "@tagEditorSectionRecent": {}, + "nameConflictDialogMultipleSourceMessage": "Einhverjar skrár eru með sama heiti.", + "@nameConflictDialogMultipleSourceMessage": {}, + "settingsVideoSectionTitle": "Myndskeið", + "@settingsVideoSectionTitle": {}, + "appExportCovers": "Umslög", + "@appExportCovers": {}, + "viewerOpenPanoramaButtonLabel": "OPNA VÍÐMYND", + "@viewerOpenPanoramaButtonLabel": {}, + "createAlbumButtonLabel": "BÚA TIL", + "@createAlbumButtonLabel": {}, + "welcomeTermsToggle": "Ég samþykki skilmálana og kvaðir", + "@welcomeTermsToggle": {}, + "newAlbumDialogStorageLabel": "Geymslurými:", + "@newAlbumDialogStorageLabel": {}, + "doubleBackExitMessage": "Ýttu aftur á „Til baka“ til að hætta.", + "@doubleBackExitMessage": {}, + "settingsSubtitleThemeBackgroundColor": "Litur bakgrunns", + "@settingsSubtitleThemeBackgroundColor": {}, + "menuActionSlideshow": "Skyggnusýning", + "@menuActionSlideshow": {}, + "vaultLockTypePassword": "Lykilorð", + "@vaultLockTypePassword": {}, + "settingsThumbnailOverlayTile": "Yfirlag", + "@settingsThumbnailOverlayTile": {}, + "showTooltip": "Birta", + "@showTooltip": {}, + "settingsNavigationDrawerTabPages": "Síður", + "@settingsNavigationDrawerTabPages": {}, + "settingsConfirmationDialogTitle": "Staðfestingargluggar", + "@settingsConfirmationDialogTitle": {}, + "videoStreamSelectionDialogText": "Skjátextar", + "@videoStreamSelectionDialogText": {}, + "settingsVideoAutoPlay": "Sjálfvirk spilun", + "@settingsVideoAutoPlay": {}, + "settingsVideoEnableHardwareAcceleration": "Vélbúnaðarhröðun", + "@settingsVideoEnableHardwareAcceleration": {}, + "editEntryDateDialogShift": "Hliðra", + "@editEntryDateDialogShift": {}, + "viewerInfoLabelCoordinates": "Hnit", + "@viewerInfoLabelCoordinates": {}, + "nameConflictDialogSingleSourceMessage": "Sumar skrár í móttökumöppunni eru með sama heiti.", + "@nameConflictDialogSingleSourceMessage": {}, + "exportEntryDialogFormat": "Snið:", + "@exportEntryDialogFormat": {}, + "widgetOpenPageHome": "Opna upphafsskjá", + "@widgetOpenPageHome": {}, + "mapStyleHuaweiNormal": "Petal Maps", + "@mapStyleHuaweiNormal": {}, + "keepScreenOnAlways": "Alltaf", + "@keepScreenOnAlways": {}, + "appExportSettings": "Stillingar", + "@appExportSettings": {}, + "exportEntryDialogQuality": "Gæði", + "@exportEntryDialogQuality": {}, + "searchPlacesSectionTitle": "Staðir", + "@searchPlacesSectionTitle": {}, + "settingsCollectionSelectionQuickActionEditorBanner": "Ýttu og haltu niðri til að færa hnappa og velja hvaða aðgerðir séu birtar þegar verið er að velja atriði.", + "@settingsCollectionSelectionQuickActionEditorBanner": {}, + "settingsSlideshowShuffle": "Stokka", + "@settingsSlideshowShuffle": {}, + "settingsThumbnailSectionTitle": "Smámyndir", + "@settingsThumbnailSectionTitle": {}, + "videoStreamSelectionDialogAudio": "Hljóð", + "@videoStreamSelectionDialogAudio": {}, + "videoSpeedDialogLabel": "Afspilunarhraði", + "@videoSpeedDialogLabel": {}, + "openMapPageTooltip": "Skoða á kortasíðu", + "@openMapPageTooltip": {}, + "videoActionSkip10": "Fara áfram um 10 sekúndur", + "@videoActionSkip10": {}, + "editEntryLocationDialogSetCustom": "Stilla sérsniðna staðsetningu", + "@editEntryLocationDialogSetCustom": {}, + "tagPlaceholderPlace": "Staður", + "@tagPlaceholderPlace": {}, + "viewerInfoUnknown": "óþekkt", + "@viewerInfoUnknown": {}, + "panoramaDisableSensorControl": "Gera skynjarastýringu óvirka", + "@panoramaDisableSensorControl": {}, + "displayRefreshRatePreferHighest": "Hæsti hraði", + "@displayRefreshRatePreferHighest": {}, + "settingsVideoShowVideos": "Sýna myndskeið", + "@settingsVideoShowVideos": {}, + "deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Eyða þessu atriði?} other{Eyða þessum {count} atriðum?}}", + "@deleteEntriesConfirmationDialogMessage": { + "placeholders": { + "count": {} + } + }, + "settingsViewerShowInformation": "Birta upplýsingar", + "@settingsViewerShowInformation": {}, + "placeEmpty": "Engir staðir", + "@placeEmpty": {}, + "settingsCollectionQuickActionsTile": "Flýtiaðgerðir", + "@settingsCollectionQuickActionsTile": {}, + "editEntryDateDialogExtractFromTitle": "Veiða úr titli", + "@editEntryDateDialogExtractFromTitle": {}, + "chipActionDelete": "Eyða", + "@chipActionDelete": {}, + "settingsViewerOverlayTile": "Yfirlag", + "@settingsViewerOverlayTile": {}, + "albumTierRegular": "Annað", + "@albumTierRegular": {}, + "settingsThumbnailShowFavouriteIcon": "Birta tákn fyrir eftirlæti", + "@settingsThumbnailShowFavouriteIcon": {}, + "aboutLinkLicense": "Notkunarleyfi", + "@aboutLinkLicense": {}, + "albumScreenshots": "Skjámyndir", + "@albumScreenshots": {}, + "editEntryDateDialogTitle": "Dagsetning og tími", + "@editEntryDateDialogTitle": {}, + "settingsSubtitleThemeTextPositionDialogTitle": "Textastaða", + "@settingsSubtitleThemeTextPositionDialogTitle": {}, + "viewerInfoLabelOwner": "Eigandi", + "@viewerInfoLabelOwner": {}, + "changeTooltip": "Breyta", + "@changeTooltip": {}, + "editEntryLocationDialogLongitude": "Lengdargráða", + "@editEntryLocationDialogLongitude": {}, + "setCoverDialogCustom": "Sérsniðið", + "@setCoverDialogCustom": {}, + "searchCountriesSectionTitle": "Lönd", + "@searchCountriesSectionTitle": {}, + "settingsSlideshowIntervalTile": "Millibil", + "@settingsSlideshowIntervalTile": {}, + "settingsAskEverytime": "Spyrja í hvert skipti", + "@settingsAskEverytime": {}, + "editEntryDateDialogCopyField": "Afrita úr annarri dagsetningu", + "@editEntryDateDialogCopyField": {}, + "searchTagsSectionTitle": "Merki", + "@searchTagsSectionTitle": {}, + "displayRefreshRatePreferLowest": "Lægsti hraði", + "@displayRefreshRatePreferLowest": {}, + "tagEditorPageAddTagTooltip": "Bæta við merki", + "@tagEditorPageAddTagTooltip": {}, + "filterNoAddressLabel": "Ekkert heimilisfang", + "@filterNoAddressLabel": {}, + "settingsModificationWarningDialogMessage": "Aðrar stillingar munu breytast.", + "@settingsModificationWarningDialogMessage": {}, + "videoStartOverButtonLabel": "BYRJA AFTUR", + "@videoStartOverButtonLabel": {}, + "accessibilityAnimationsKeep": "Halda skjábrellum", + "@accessibilityAnimationsKeep": {}, + "settingsCollectionQuickActionEditorPageTitle": "Flýtiaðgerðir", + "@settingsCollectionQuickActionEditorPageTitle": {}, + "policyPageTitle": "Meðferð persónuupplýsinga", + "@policyPageTitle": {}, + "tileLayoutList": "Listi", + "@tileLayoutList": {}, + "settingsStorageAccessRevokeTooltip": "Afturkalla", + "@settingsStorageAccessRevokeTooltip": {}, + "panoramaEnableSensorControl": "Virkja skynjarastýringu", + "@panoramaEnableSensorControl": {}, + "settingsSubtitleThemeTile": "Skjátextar", + "@settingsSubtitleThemeTile": {}, + "widgetDisplayedItemMostRecent": "Nýlegast", + "@widgetDisplayedItemMostRecent": {}, + "videoActionUnmute": "Hljóð á", + "@videoActionUnmute": {}, + "appPickDialogNone": "Ekkert", + "@appPickDialogNone": {}, + "filterMimeImageLabel": "Mynd", + "@filterMimeImageLabel": {}, + "settingsVideoGestureVerticalDragBrightnessVolume": "Strjúktu upp/niður til að breyta birtustigi/hljóðstyrk", + "@settingsVideoGestureVerticalDragBrightnessVolume": {}, + "settingsAccessibilitySectionTitle": "Auðveldað aðgengi", + "@settingsAccessibilitySectionTitle": {}, + "settingsActionExport": "Flytja út", + "@settingsActionExport": {}, + "aboutBugCopyInfoInstruction": "Afrita kerfisupplýsingar", + "@aboutBugCopyInfoInstruction": {}, + "filterNoTagLabel": "Ómerkt", + "@filterNoTagLabel": {}, + "viewerInfoViewXmlLinkText": "Skoða XML", + "@viewerInfoViewXmlLinkText": {}, + "appName": "Aves", + "@appName": {}, + "sortOrderOldestFirst": "Elsta fyrst", + "@sortOrderOldestFirst": {}, + "collectionRenameFailureFeedback": "{count, plural, =1{Mistókst að endurnefna 1 atriði} other{Mistókst að endurnefna {count} atriðum}}", + "@collectionRenameFailureFeedback": { + "placeholders": { + "count": {} + } + }, + "videoResumptionModeAlways": "Alltaf", + "@videoResumptionModeAlways": {}, + "tagEditorDiscardDialogMessage": "Viltu henda breytingum?", + "@tagEditorDiscardDialogMessage": {}, + "collectionEditSuccessFeedback": "{count, plural, =1{Breytti 1 atriði} other{Breytti {count} atriðum}}", + "@collectionEditSuccessFeedback": { + "placeholders": { + "count": {} + } + }, + "sourceStateLoading": "Innhleðsla", + "@sourceStateLoading": {}, + "sourceStateLocatingCountries": "Staðset lönd", + "@sourceStateLocatingCountries": {}, + "wallpaperTargetLock": "Læsa skjá", + "@wallpaperTargetLock": {}, + "coverDialogTabApp": "Forrit", + "@coverDialogTabApp": {}, + "settingsThumbnailShowMotionPhotoIcon": "Birta tákn fyrir hreyfiljósmyndir", + "@settingsThumbnailShowMotionPhotoIcon": {}, + "accessibilityAnimationsRemove": "Koma í veg fyrir skjábrellur", + "@accessibilityAnimationsRemove": {}, + "removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "XMP er nauðsynlegt til að geta spilað myndskeið inni í hreyfiljósmynd.\n\nErtu viss um að þú viljir fjarlægja það?", + "@removeEntryMetadataMotionPhotoXmpWarningDialogMessage": {}, + "nameConflictStrategyRename": "Endurnefna", + "@nameConflictStrategyRename": {}, + "settingsThumbnailShowRawIcon": "Birta tákn fyrir RAW", + "@settingsThumbnailShowRawIcon": {}, + "vaultLockTypePattern": "Mynstur", + "@vaultLockTypePattern": {}, + "tagPageTitle": "Merki", + "@tagPageTitle": {}, + "collectionEmptyVideos": "Engin myndskeið", + "@collectionEmptyVideos": {}, + "applyButtonLabel": "VIRKJA", + "@applyButtonLabel": {}, + "entryActionRemoveFavourite": "Fjarlægja úr eftirlætum", + "@entryActionRemoveFavourite": {}, + "aboutCreditsSectionTitle": "Framlög", + "@aboutCreditsSectionTitle": {}, + "drawerAlbumPage": "Albúm", + "@drawerAlbumPage": {}, + "statsTopCountriesSectionTitle": "Algengustu lönd", + "@statsTopCountriesSectionTitle": {}, + "settingsActionImport": "Flytja inn", + "@settingsActionImport": {}, + "viewerInfoLabelSize": "Stærð", + "@viewerInfoLabelSize": {}, + "locationPickerUseThisLocationButton": "Nota þessa staðsetningu", + "@locationPickerUseThisLocationButton": {}, + "settingsSlideshowFillScreen": "Fylla skjá", + "@settingsSlideshowFillScreen": {}, + "settingsHiddenFiltersBanner": "Myndir og myndskeið sem samsvara felusíum, munu ekki birtast í safninu þínu.", + "@settingsHiddenFiltersBanner": {}, + "viewerSetWallpaperButtonLabel": "SETJA BAKGRUNN", + "@viewerSetWallpaperButtonLabel": {}, + "settingsVideoResumptionModeTile": "Halda afspilun áfram", + "@settingsVideoResumptionModeTile": {}, + "collectionGroupNone": "Ekki hópa", + "@collectionGroupNone": {}, + "searchRatingSectionTitle": "Einkunnir", + "@searchRatingSectionTitle": {}, + "collectionCopyFailureFeedback": "{count, plural, =1{Mistókst að afrita 1 atriði} other{Mistókst að afrita {count} atriðum}}", + "@collectionCopyFailureFeedback": { + "placeholders": { + "count": {} + } + }, + "settingsDisabled": "Óvirkt", + "@settingsDisabled": {}, + "settingsVideoGestureDoubleTapTogglePlay": "Tvíbankaðu til að spila/setja í bið", + "@settingsVideoGestureDoubleTapTogglePlay": {}, + "coordinateFormatDecimal": "Gráður í tugakerfi", + "@coordinateFormatDecimal": {}, + "overlayHistogramLuminance": "Ljómi", + "@overlayHistogramLuminance": {}, + "coordinateDmsEast": "A", + "@coordinateDmsEast": {}, + "filterAspectRatioLandscapeLabel": "Lárétt", + "@filterAspectRatioLandscapeLabel": {}, + "albumTierApps": "Forrit", + "@albumTierApps": {}, + "filterAspectRatioPortraitLabel": "Lóðrétt", + "@filterAspectRatioPortraitLabel": {}, + "settingsWidgetDisplayedItem": "Birt atriði", + "@settingsWidgetDisplayedItem": {}, + "aboutBugSaveLogInstruction": "Vista atvikaskráningu forrits í skrá", + "@aboutBugSaveLogInstruction": {}, + "settingsActionImportDialogTitle": "Flytja inn", + "@settingsActionImportDialogTitle": {}, + "filterTypeGeotiffLabel": "GeoTIFF", + "@filterTypeGeotiffLabel": {}, + "entryActionRotateScreen": "Snúa skjánum", + "@entryActionRotateScreen": {}, + "albumGroupTier": "Eftir flokki", + "@albumGroupTier": {}, + "aboutBugReportInstruction": "Tilkynna á GitHub með atvikaskrám og kerfisupplýsingum", + "@aboutBugReportInstruction": {}, + "addPathTooltip": "Bæta við slóð", + "@addPathTooltip": {}, + "filePickerUseThisFolder": "Nota þessa möppu", + "@filePickerUseThisFolder": {}, + "videoActionCaptureFrame": "Taka ramma", + "@videoActionCaptureFrame": {}, + "patternDialogEnter": "Settu inn mynstur", + "@patternDialogEnter": {}, + "settingsEnableBin": "Nota ruslmöppu", + "@settingsEnableBin": {}, + "entryActionViewMotionPhotoVideo": "Opna myndskeið", + "@entryActionViewMotionPhotoVideo": {}, + "videoControlsNone": "Ekkert", + "@videoControlsNone": {}, + "otherDirectoryDescription": "„{name}“ möppu", + "@otherDirectoryDescription": { + "placeholders": { + "name": { + "type": "String", + "example": "Pictures", + "description": "the name of a specific directory" + } + } + }, + "viewerTransitionZoomIn": "Renna að", + "@viewerTransitionZoomIn": {}, + "drawerCollectionAll": "Allt safnið", + "@drawerCollectionAll": {}, + "collectionMoveSuccessFeedback": "{count, plural, =1{Færði 1 atriði} other{Færði {count} atriðum}}", + "@collectionMoveSuccessFeedback": { + "placeholders": { + "count": {} + } + }, + "settingsSubtitleThemeTextAlignmentLeft": "Vinstri", + "@settingsSubtitleThemeTextAlignmentLeft": {}, + "settingsVideoGestureSideDoubleTapSeek": "Tvíbankaðu á skjájaðra leita afturábak/áfram", + "@settingsVideoGestureSideDoubleTapSeek": {}, + "mapStyleHuaweiTerrain": "Petal Maps (yfirborð)", + "@mapStyleHuaweiTerrain": {}, + "sortByItemCount": "Eftir fjölda atriða", + "@sortByItemCount": {}, + "sectionUnknown": "Óþekkt", + "@sectionUnknown": {}, + "dateToday": "Í dag", + "@dateToday": {}, + "filterNoTitleLabel": "Án titils", + "@filterNoTitleLabel": {}, + "videoStreamSelectionDialogNoSelection": "Það eru engir aðrir ferlar.", + "@videoStreamSelectionDialogNoSelection": {}, + "searchRecentSectionTitle": "Nýlegt", + "@searchRecentSectionTitle": {}, + "albumPickPageTitlePick": "Veldu albúm", + "@albumPickPageTitlePick": {}, + "videoPlaybackMuted": "Afspilun án hljóðs", + "@videoPlaybackMuted": {}, + "settingsSubtitleThemeTextAlignmentRight": "Hægri", + "@settingsSubtitleThemeTextAlignmentRight": {}, + "menuActionMap": "Kort", + "@menuActionMap": {}, + "entryInfoActionRemoveMetadata": "Fjarlægja lýsigögn", + "@entryInfoActionRemoveMetadata": {}, + "collectionActionMove": "Færa í albúm", + "@collectionActionMove": {}, + "searchAlbumsSectionTitle": "Albúm", + "@searchAlbumsSectionTitle": {}, + "settingsLanguagePageTitle": "Tungumál", + "@settingsLanguagePageTitle": {}, + "rootDirectoryDescription": "rótarmöppu", + "@rootDirectoryDescription": {}, + "viewDialogGroupSectionTitle": "Hópur", + "@viewDialogGroupSectionTitle": {}, + "maxBrightnessAlways": "Alltaf", + "@maxBrightnessAlways": {}, + "settingsAllowMediaManagement": "Leyfa stýringu gagnamiðla", + "@settingsAllowMediaManagement": {}, + "aboutDataUsageSectionTitle": "Gagnanotkun", + "@aboutDataUsageSectionTitle": {}, + "durationDialogSeconds": "Sekúndur", + "@durationDialogSeconds": {}, + "themeBrightnessLight": "Ljóst", + "@themeBrightnessLight": {}, + "unitSystemImperial": "Breskt Imperial kerfi", + "@unitSystemImperial": {}, + "filterMimeVideoLabel": "Myndskeið", + "@filterMimeVideoLabel": {}, + "chipActionPin": "Festa efst", + "@chipActionPin": {}, + "albumEmpty": "Engin albúm", + "@albumEmpty": {}, + "editEntryLocationDialogChooseOnMap": "Velja á korti", + "@editEntryLocationDialogChooseOnMap": {}, + "settingsCollectionBrowsingQuickActionEditorBanner": "Ýttu og haltu niðri til að færa hnappa og velja hvaða aðgerðir séu birtar þegar verið er að fletta í gegnum atriði.", + "@settingsCollectionBrowsingQuickActionEditorBanner": {}, + "settingsConfirmationBeforeDeleteItems": "Spyrja áður en atriðum er endanlega eytt", + "@settingsConfirmationBeforeDeleteItems": {}, + "sortByName": "Eftir nafni", + "@sortByName": {}, + "settingsHomeDialogTitle": "Heim", + "@settingsHomeDialogTitle": {}, + "searchDateSectionTitle": "Dagsetning", + "@searchDateSectionTitle": {}, + "viewerErrorUnknown": "Úbbs!", + "@viewerErrorUnknown": {}, + "themeBrightnessDark": "Dökkt", + "@themeBrightnessDark": {}, + "doNotAskAgain": "Ekki spyrja aftur", + "@doNotAskAgain": {}, + "entryActionConvertMotionPhotoToStillImage": "Umbreyta í kyrrmynd", + "@entryActionConvertMotionPhotoToStillImage": {}, + "entryActionCast": "Leikarar", + "@entryActionCast": {}, + "entryInfoActionEditTitleDescription": "Breyta titli og lýsingu", + "@entryInfoActionEditTitleDescription": {}, + "nextTooltip": "Næsta", + "@nextTooltip": {}, + "collectionSearchTitlesHintText": "Leita í lagatitlum", + "@collectionSearchTitlesHintText": {}, + "entryInfoActionEditLocation": "Breyta staðsetningu", + "@entryInfoActionEditLocation": {}, + "viewerInfoSearchSuggestionDate": "Dagsetning og tími", + "@viewerInfoSearchSuggestionDate": {}, + "videoPlaybackWithSound": "Afspilun með hljóði", + "@videoPlaybackWithSound": {}, + "viewerInfoLabelTitle": "Titill", + "@viewerInfoLabelTitle": {}, + "searchCollectionFieldHint": "Leita í safni", + "@searchCollectionFieldHint": {}, + "viewerActionUnlock": "Aflæsa skoðara", + "@viewerActionUnlock": {}, + "settingsDoubleBackExit": "Ýttu tvisvar á „Til baka“ til að hætta", + "@settingsDoubleBackExit": {}, + "entryActionShare": "Deila", + "@entryActionShare": {}, + "drawerCollectionSphericalVideos": "360° myndskeið", + "@drawerCollectionSphericalVideos": {}, + "moveUndatedConfirmationDialogMessage": "Vista dagsetningar atriðis áður en haldið er áfram?", + "@moveUndatedConfirmationDialogMessage": {}, + "collectionEditFailureFeedback": "{count, plural, =1{Mistókst að breyta 1 atriði} other{Mistókst að breyta {count} atriðum}}", + "@collectionEditFailureFeedback": { + "placeholders": { + "count": {} + } + }, + "videoActionPause": "Í bið", + "@videoActionPause": {}, + "noMatchingAppDialogMessage": "Ekkert forrit fannst sem getur meðhöndlað þessa aðgerð.", + "@noMatchingAppDialogMessage": {}, + "settingsThumbnailOverlayPageTitle": "Yfirlag", + "@settingsThumbnailOverlayPageTitle": {}, + "settingsViewerShowInformationSubtitle": "Birta titil, dagsetningu, staðsetningu, o.s.frv.", + "@settingsViewerShowInformationSubtitle": {}, + "saveTooltip": "Vista", + "@saveTooltip": {}, + "searchMetadataSectionTitle": "Lýsigögn", + "@searchMetadataSectionTitle": {}, + "widgetOpenPageCollection": "Opna myndasafn", + "@widgetOpenPageCollection": {}, + "clearTooltip": "Hreinsa", + "@clearTooltip": {}, + "settingsWidgetOpenPage": "Þegar ýtt er á viðmótshlutann", + "@settingsWidgetOpenPage": {}, + "settingsShowBottomNavigationBar": "Birta flakkstiku neðst", + "@settingsShowBottomNavigationBar": {}, + "renameProcessorName": "Heiti", + "@renameProcessorName": {}, + "viewerInfoSearchFieldLabel": "Leita í lýsigögnum", + "@viewerInfoSearchFieldLabel": {}, + "viewerInfoLabelDescription": "Lýsing", + "@viewerInfoLabelDescription": {}, + "nameConflictStrategySkip": "Sleppa", + "@nameConflictStrategySkip": {}, + "addShortcutButtonLabel": "BÆTA VIÐ", + "@addShortcutButtonLabel": {}, + "exportEntryDialogHeight": "Hæð", + "@exportEntryDialogHeight": {}, + "collectionActionEmptyBin": "Tæma ruslið", + "@collectionActionEmptyBin": {}, + "settingsSlideshowRepeat": "Endurtaka", + "@settingsSlideshowRepeat": {}, + "settingsViewerQuickActionEditorPageTitle": "Flýtiaðgerðir", + "@settingsViewerQuickActionEditorPageTitle": {}, + "sortOrderAtoZ": "A til Ö", + "@sortOrderAtoZ": {}, + "settingsVideoPageTitle": "Myndstillingar", + "@settingsVideoPageTitle": {}, + "setCoverDialogAuto": "Sjálfvirkt", + "@setCoverDialogAuto": {}, + "tagEditorPageNewTagFieldLabel": "Nýtt merki", + "@tagEditorPageNewTagFieldLabel": {}, + "exportEntryDialogWidth": "Breidd", + "@exportEntryDialogWidth": {}, + "newAlbumDialogTitle": "Nýtt albúm", + "@newAlbumDialogTitle": {}, + "collectionRenameSuccessFeedback": "{count, plural, =1{Endurnefndi 1 atriði} other{Endurnefndi {count} atriðum}}", + "@collectionRenameSuccessFeedback": { + "placeholders": { + "count": {} + } + }, + "videoStreamSelectionDialogTrack": "Ferill", + "@videoStreamSelectionDialogTrack": {}, + "settingsViewerUseCutout": "Nota útklippt svæði", + "@settingsViewerUseCutout": {}, + "tagEditorPageTitle": "Breyta merkjum", + "@tagEditorPageTitle": {}, + "settingsWidgetShowOutline": "Útlínur", + "@settingsWidgetShowOutline": {}, + "settingsHomeTile": "Heim", + "@settingsHomeTile": {}, + "entryActionOpenMap": "Birta í landakortaforriti", + "@entryActionOpenMap": {}, + "filePickerShowHiddenFiles": "Birta faldar skrár", + "@filePickerShowHiddenFiles": {}, + "collectionGroupDay": "Eftir dögum", + "@collectionGroupDay": {}, + "collectionGroupAlbum": "Eftir albúmum", + "@collectionGroupAlbum": {}, + "newFilterBanner": "nýtt", + "@newFilterBanner": {}, + "drawerCollectionFavourites": "Eftirlæti", + "@drawerCollectionFavourites": {}, + "filterTypeRawLabel": "RAW", + "@filterTypeRawLabel": {}, + "videoControlsPlaySeek": "Spila og leita afturábak/áfram", + "@videoControlsPlaySeek": {}, + "settingsSubtitleThemeTextAlignmentCenter": "Miðjað", + "@settingsSubtitleThemeTextAlignmentCenter": {}, + "keepScreenOnVideoPlayback": "Á meðan myndskeið er í spilun", + "@keepScreenOnVideoPlayback": {}, + "vaultLockTypePin": "PIN-númer", + "@vaultLockTypePin": {}, + "settingsSubtitleThemeTextAlignmentTile": "Jöfnun texta", + "@settingsSubtitleThemeTextAlignmentTile": {}, + "editorTransformCrop": "Utansníða", + "@editorTransformCrop": {}, + "aboutLicensesFlutterPackagesSectionTitle": "Flutter-pakkar", + "@aboutLicensesFlutterPackagesSectionTitle": {}, + "settingsCollectionQuickActionTabSelecting": "Val", + "@settingsCollectionQuickActionTabSelecting": {}, + "focalLength": "{length} mm", + "@focalLength": { + "placeholders": { + "length": { + "type": "String", + "example": "5.4" + } + } + }, + "chipActionRename": "Endurnefna", + "@chipActionRename": {}, + "filterTaggedLabel": "Merkt", + "@filterTaggedLabel": {}, + "statePageTitle": "Héruð", + "@statePageTitle": {}, + "drawerTagPage": "Merki", + "@drawerTagPage": {}, + "newAlbumDialogNameLabelAlreadyExistsHelper": "Mappa er þegar til staðar", + "@newAlbumDialogNameLabelAlreadyExistsHelper": {}, + "patternDialogConfirm": "Staðfestu mynstrið", + "@patternDialogConfirm": {}, + "durationDialogMinutes": "Mínútur", + "@durationDialogMinutes": {}, + "settingsAllowErrorReporting": "Leyfa nafnlausar villutilkynningar", + "@settingsAllowErrorReporting": {}, + "settingsVideoLoopModeTile": "Endurtekningarhamur", + "@settingsVideoLoopModeTile": {}, + "hideTooltip": "Fela", + "@hideTooltip": {}, + "settingsScreenSaverPageTitle": "Skjáhvíla", + "@settingsScreenSaverPageTitle": {}, + "settingsNavigationDrawerEditorPageTitle": "Flakkvalmynd", + "@settingsNavigationDrawerEditorPageTitle": {}, + "settingsConfirmationBeforeMoveUndatedItems": "Spyrja áður en ódagsett atriði eru færð", + "@settingsConfirmationBeforeMoveUndatedItems": {}, + "albumPageTitle": "Albúm", + "@albumPageTitle": {}, + "editEntryLocationDialogTitle": "Staðsetning", + "@editEntryLocationDialogTitle": {}, + "statsWithGps": "{count, plural, =1{1 atriði með staðsetningu} other{{count} atriði með staðsetningum}}", + "@statsWithGps": { + "placeholders": { + "count": {} + } + }, + "settingsVideoResumptionModeDialogTitle": "Halda afspilun áfram", + "@settingsVideoResumptionModeDialogTitle": {}, + "albumTierPinned": "Fest", + "@albumTierPinned": {}, + "mapStyleDialogTitle": "Stíll korts", + "@mapStyleDialogTitle": {}, + "entryActionRotateCCW": "Snúa rangsælis", + "@entryActionRotateCCW": {}, + "settingsVideoBackgroundMode": "Bakgrunnshamur", + "@settingsVideoBackgroundMode": {}, + "chipActionGoToPlacePage": "Birta í Staðir", + "@chipActionGoToPlacePage": {}, + "albumPickPageTitleCopy": "Afrita í albúm", + "@albumPickPageTitleCopy": {}, + "collectionActionCopy": "Afrita í albúm", + "@collectionActionCopy": {}, + "entryActionPrint": "Prenta", + "@entryActionPrint": {}, + "settingsAllowInstalledAppAccess": "Leyfa aðgang að forritaskrá", + "@settingsAllowInstalledAppAccess": {}, + "entryActionShowGeoTiffOnMap": "Birta sem yfirlag á korti", + "@entryActionShowGeoTiffOnMap": {}, + "viewDialogReverseSortOrder": "Öfug röð", + "@viewDialogReverseSortOrder": {}, + "menuActionConfigureView": "Skoða", + "@menuActionConfigureView": {}, + "aboutLicensesDartPackagesSectionTitle": "Dart-pakkar", + "@aboutLicensesDartPackagesSectionTitle": {}, + "drawerCollectionMotionPhotos": "Hreyfiljósmyndir", + "@drawerCollectionMotionPhotos": {}, + "settingsDefault": "Sjálfgefið", + "@settingsDefault": {}, + "settingsHiddenItemsTabFilters": "Felusíur", + "@settingsHiddenItemsTabFilters": {}, + "settingsSlideshowAnimatedZoomEffect": "Aðdráttur með hreyfingu", + "@settingsSlideshowAnimatedZoomEffect": {}, + "viewerActionSettings": "Stillingar", + "@viewerActionSettings": {}, + "placePageTitle": "Staðir", + "@placePageTitle": {}, + "filterOnThisDayLabel": "Á þessum degi", + "@filterOnThisDayLabel": {}, + "columnCount": "{count, plural, =1{1 dálkur} other{{count} dálkar}}", + "@columnCount": { + "placeholders": { + "count": {} + } + }, + "viewerTransitionNone": "Ekkert", + "@viewerTransitionNone": {}, + "chipActionGoToAlbumPage": "Birta í Albúm", + "@chipActionGoToAlbumPage": {}, + "settingsDisplayRefreshRateModeTile": "Birta uppfærslutíðni", + "@settingsDisplayRefreshRateModeTile": {}, + "addShortcutDialogLabel": "Skýring flýtilykils", + "@addShortcutDialogLabel": {}, + "settingsViewerOverlayPageTitle": "Yfirlag", + "@settingsViewerOverlayPageTitle": {}, + "tagEmpty": "Engin merki", + "@tagEmpty": {}, + "pinDialogEnter": "Settu inn PIN-númer", + "@pinDialogEnter": {}, + "videoControlsPlayOutside": "Opna með öðrum spilara", + "@videoControlsPlayOutside": {}, + "filePickerDoNotShowHiddenFiles": "Ekki birta faldar skrár", + "@filePickerDoNotShowHiddenFiles": {}, + "filePickerNoItems": "Engir hlutir", + "@filePickerNoItems": {}, + "settingsCoordinateFormatDialogTitle": "Snið hnita", + "@settingsCoordinateFormatDialogTitle": {}, + "collectionActionShowTitleSearch": "Birta titilsíu", + "@collectionActionShowTitleSearch": {}, + "lengthUnitPercent": "%", + "@lengthUnitPercent": {}, + "settingsNavigationDrawerTabTypes": "Tegundir", + "@settingsNavigationDrawerTabTypes": {}, + "menuActionSelectAll": "Velja allt", + "@menuActionSelectAll": {}, + "settingsHiddenItemsTile": "Falin atriði", + "@settingsHiddenItemsTile": {}, + "maxBrightnessNever": "Aldrei", + "@maxBrightnessNever": {}, + "aboutDataUsageCache": "Skyndiminni", + "@aboutDataUsageCache": {}, + "settingsConfirmationTile": "Staðfestingargluggar", + "@settingsConfirmationTile": {}, + "coordinateDmsSouth": "S", + "@coordinateDmsSouth": {}, + "settingsThumbnailShowVideoDuration": "Birta tímalengd myndskeiða", + "@settingsThumbnailShowVideoDuration": {}, + "passwordDialogConfirm": "Staðfestu lykilorð", + "@passwordDialogConfirm": {}, + "videoActionReplay10": "Fara afturábak um 10 sekúndur", + "@videoActionReplay10": {}, + "settingsCollectionBurstPatternsNone": "Ekkert", + "@settingsCollectionBurstPatternsNone": {}, + "albumMimeTypeMixed": "Blandað", + "@albumMimeTypeMixed": {}, + "settingsViewerQuickActionEditorAvailableButtonsSectionTitle": "Tiltækir hnappar", + "@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {}, + "itemCount": "{count, plural, =1{1 atriði} other{{count} atriðum}}", + "@itemCount": { + "placeholders": { + "count": {} + } + }, + "widgetTapUpdateWidget": "Uppfæra viðmótshluta", + "@widgetTapUpdateWidget": {}, + "settingsViewerShowDescription": "Birta lýsingu", + "@settingsViewerShowDescription": {}, + "viewerTransitionFade": "Deyfing", + "@viewerTransitionFade": {}, + "actionRemove": "Fjarlægja", + "@actionRemove": {}, + "cropAspectRatioFree": "Frjálst", + "@cropAspectRatioFree": {}, + "aboutLinkPolicy": "Meðferð persónuupplýsinga", + "@aboutLinkPolicy": {}, + "filterTypeSphericalVideoLabel": "360° myndskeið", + "@filterTypeSphericalVideoLabel": {}, + "slideshowActionShowInCollection": "Sýna í safni", + "@slideshowActionShowInCollection": {}, + "chipActionLock": "Læsa", + "@chipActionLock": {}, + "filterNoRatingLabel": "Án einkunnar", + "@filterNoRatingLabel": {}, + "settingsUnitSystemTile": "Einingar", + "@settingsUnitSystemTile": {}, + "entryActionOpen": "Opna með", + "@entryActionOpen": {}, + "pickTooltip": "Veldu", + "@pickTooltip": {}, + "settingsCoordinateFormatTile": "Snið hnita", + "@settingsCoordinateFormatTile": {}, + "settingsViewerQuickActionEditorBanner": "Ýttu og haltu niðri til að færa hnappa og velja hvaða aðgerðir séu birtar í skoðaranum.", + "@settingsViewerQuickActionEditorBanner": {}, + "themeBrightnessBlack": "Svart", + "@themeBrightnessBlack": {}, + "filterNoLocationLabel": "Óstaðsett", + "@filterNoLocationLabel": {}, + "mapZoomInTooltip": "Renna að", + "@mapZoomInTooltip": {}, + "viewerInfoOpenLinkText": "Opna", + "@viewerInfoOpenLinkText": {}, + "storageVolumeDescriptionFallbackNonPrimary": "SD-minniskorti", + "@storageVolumeDescriptionFallbackNonPrimary": {}, + "renameEntrySetPagePatternFieldLabel": "Nefningarmynstur", + "@renameEntrySetPagePatternFieldLabel": {}, + "renameAlbumDialogLabel": "Nýtt heiti", + "@renameAlbumDialogLabel": {}, + "videoActionPlay": "Afspilun", + "@videoActionPlay": {}, + "chipActionGoToCountryPage": "Birta í Lönd", + "@chipActionGoToCountryPage": {}, + "deleteButtonLabel": "EYÐA", + "@deleteButtonLabel": {}, + "settingsDisablingBinWarningDialogMessage": "Atriðum í ruslinu verður endanlega eytt.", + "@settingsDisablingBinWarningDialogMessage": {}, + "entryActionSetAs": "Setja sem", + "@entryActionSetAs": {}, + "sortOrderLowestFirst": "Lægsta fyrst", + "@sortOrderLowestFirst": {}, + "albumGroupNone": "Ekki hópa", + "@albumGroupNone": {}, + "statsTopStatesSectionTitle": "Algengustu héruð", + "@statsTopStatesSectionTitle": {}, + "settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": "Birtir hnappar", + "@settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": {}, + "settingsVideoPlaybackPageTitle": "Afspilun", + "@settingsVideoPlaybackPageTitle": {}, + "filterLocatedLabel": "Staðsett", + "@filterLocatedLabel": {}, + "videoPlaybackSkip": "Sleppa", + "@videoPlaybackSkip": {}, + "mapStyleTooltip": "Veldu stíl korts", + "@mapStyleTooltip": {}, + "countryPageTitle": "Lönd", + "@countryPageTitle": {}, + "albumGroupType": "Eftir gerð", + "@albumGroupType": {}, + "entryActionInfo": "Upplýsingar", + "@entryActionInfo": {}, + "viewerErrorDoesNotExist": "Skráin er ekki lengur til.", + "@viewerErrorDoesNotExist": {}, + "albumCamera": "Myndavél", + "@albumCamera": {}, + "videoControlsPlay": "Afspilun", + "@videoControlsPlay": {}, + "settingsNavigationSectionTitle": "Flakk", + "@settingsNavigationSectionTitle": {}, + "settingsDisplayRefreshRateModeDialogTitle": "Uppfærslutíðni", + "@settingsDisplayRefreshRateModeDialogTitle": {}, + "viewerInfoLabelUri": "Vefslóð", + "@viewerInfoLabelUri": {}, + "entryActionViewSource": "Skoða uppruna", + "@entryActionViewSource": {}, + "entryActionShareVideoOnly": "Deila einungis myndskeiði", + "@entryActionShareVideoOnly": {}, + "filterTypePanoramaLabel": "Víðmynd", + "@filterTypePanoramaLabel": {}, + "coordinateFormatDms": "DMS", + "@coordinateFormatDms": {}, + "passwordDialogEnter": "Settu inn lykilorð", + "@passwordDialogEnter": {}, + "applyTooltip": "Virkja", + "@applyTooltip": {}, + "mapZoomOutTooltip": "Renna frá", + "@mapZoomOutTooltip": {}, + "entryInfoActionEditTags": "Breyta merkjum", + "@entryInfoActionEditTags": {}, + "collectionActionHideTitleSearch": "Fela titilsíu", + "@collectionActionHideTitleSearch": {}, + "mapAttributionOsmHot": "Kortagögn frá © [OpenStreetMap](https://www.openstreetmap.org/copyright) þátttakendum • Kortatíglar frá [HOT](https://www.hotosm.org/) • Hýst af [OSM France](https://openstreetmap.fr/)", + "@mapAttributionOsmHot": {}, + "settingsNavigationDrawerTile": "Leiðsagnarval", + "@settingsNavigationDrawerTile": {}, + "drawerCollectionAnimated": "Með hreyfingu", + "@drawerCollectionAnimated": {}, + "renameAlbumDialogLabelAlreadyExistsHelper": "Mappa er þegar til staðar", + "@renameAlbumDialogLabelAlreadyExistsHelper": {}, + "settingsHiddenItemsPageTitle": "Falin atriði", + "@settingsHiddenItemsPageTitle": {}, + "durationDialogHours": "Klukkustundir", + "@durationDialogHours": {}, + "settingsHiddenPathsBanner": "Myndir og myndskeið í þessum möppum, eða undirmöppum þeirra, munu ekki birtast í safninu þínu.", + "@settingsHiddenPathsBanner": {}, + "settingsKeepScreenOnDialogTitle": "Halda skjá í gangi", + "@settingsKeepScreenOnDialogTitle": {}, + "wallpaperTargetHome": "Upphafsskjár", + "@wallpaperTargetHome": {}, + "settingsViewerShowOverlayOnOpening": "Birta við opnun", + "@settingsViewerShowOverlayOnOpening": {}, + "nextButtonLabel": "NÆSTA", + "@nextButtonLabel": {}, + "settingsAccessibilityShowPinchGestureAlternatives": "Sýna aðra valkosti með mörgum bendingum", + "@settingsAccessibilityShowPinchGestureAlternatives": {}, + "videoActionSelectStreams": "Veldu ferla", + "@videoActionSelectStreams": {}, + "settingsViewerShowShootingDetails": "Sýna ítarlegri upplýsingar um myndatöku", + "@settingsViewerShowShootingDetails": {}, + "settingsConfirmationAfterMoveToBinItems": "Sýna skilaboð eftir að atriði eru færð í ruslmöppuna", + "@settingsConfirmationAfterMoveToBinItems": {}, + "widgetDisplayedItemRandom": "Handahóf", + "@widgetDisplayedItemRandom": {}, + "settingsLanguageSectionTitle": "Tungumál og snið", + "@settingsLanguageSectionTitle": {}, + "drawerPlacePage": "Staðir", + "@drawerPlacePage": {}, + "menuActionSelect": "Velja", + "@menuActionSelect": {}, + "drawerCollectionPanoramas": "Víðmyndir", + "@drawerCollectionPanoramas": {}, + "renameEntrySetPagePreviewSectionTitle": "Forskoðun", + "@renameEntrySetPagePreviewSectionTitle": {}, + "filterBinLabel": "Ruslmappa", + "@filterBinLabel": {}, + "settingsDisplaySectionTitle": "Birting", + "@settingsDisplaySectionTitle": {}, + "settingsSlideshowTransitionTile": "Millifærsla", + "@settingsSlideshowTransitionTile": {}, + "settingsPageTitle": "Stillingar", + "@settingsPageTitle": {}, + "welcomeMessage": "Velkomin í Aves", + "@welcomeMessage": {}, + "subtitlePositionTop": "Efst", + "@subtitlePositionTop": {}, + "menuActionSelectNone": "Velja ekkert", + "@menuActionSelectNone": {}, + "showButtonLabel": "BIRTA", + "@showButtonLabel": {}, + "entryActionConvert": "Umbreyta", + "@entryActionConvert": {}, + "sourceViewerPageTitle": "Uppruni", + "@sourceViewerPageTitle": {}, + "aboutDataUsageClearCache": "Hreinsa skyndiminni", + "@aboutDataUsageClearCache": {}, + "settingsSearchFieldLabel": "Leita í stillingum", + "@settingsSearchFieldLabel": {}, + "appExportFavourites": "Eftirlæti", + "@appExportFavourites": {}, + "collectionEmptyImages": "Engar myndir", + "@collectionEmptyImages": {}, + "settingsViewerMaximumBrightness": "Hámarksbirtustig", + "@settingsViewerMaximumBrightness": {}, + "albumPickPageTitleExport": "Flytja út í albúm", + "@albumPickPageTitleExport": {}, + "settingsThumbnailShowTagIcon": "Birta tákn fyrir merki", + "@settingsThumbnailShowTagIcon": {}, + "settingsStorageAccessEmpty": "Engiar aðgangsheimildir", + "@settingsStorageAccessEmpty": {}, + "settingsRemoveAnimationsTile": "Fjarlægja hreyfimyndir", + "@settingsRemoveAnimationsTile": {}, + "settingsActionExportDialogTitle": "Flytja út", + "@settingsActionExportDialogTitle": {}, + "genericDangerWarningDialogMessage": "Ertu viss?", + "@genericDangerWarningDialogMessage": {}, + "lengthUnitPixel": "px", + "@lengthUnitPixel": {}, + "vaultDialogLockTypeLabel": "Tegund læsingar", + "@vaultDialogLockTypeLabel": {}, + "aboutDataUsageExternal": "Utanaðkomandi", + "@aboutDataUsageExternal": {}, + "collectionSelectPageTitle": "Veldu atriði", + "@collectionSelectPageTitle": {}, + "entryInfoActionEditDate": "Breyta dagsetningu og tíma", + "@entryInfoActionEditDate": {}, + "hideButtonLabel": "FELA", + "@hideButtonLabel": {}, + "editEntryLocationDialogLatitude": "Breiddargráða", + "@editEntryLocationDialogLatitude": {}, + "slideshowActionResume": "Halda áfram", + "@slideshowActionResume": {}, + "cropAspectRatioSquare": "Ferningur", + "@cropAspectRatioSquare": {}, + "collectionActionRescan": "Endurskanna", + "@collectionActionRescan": {}, + "collectionCopySuccessFeedback": "{count, plural, =1{Afritaði 1 atriði} other{Afritaði {count} atriðum}}", + "@collectionCopySuccessFeedback": { + "placeholders": { + "count": {} + } + }, + "settingsNavigationDrawerBanner": "Ýttu og haltu niðri til að endurraða valmyndaratriðum.", + "@settingsNavigationDrawerBanner": {}, + "settingsWidgetPageTitle": "Myndarammi", + "@settingsWidgetPageTitle": {}, + "viewDialogSortSectionTitle": "Raða", + "@viewDialogSortSectionTitle": {}, + "continueButtonLabel": "HALDA ÁFRAM", + "@continueButtonLabel": {}, + "collectionPageTitle": "Safn", + "@collectionPageTitle": {}, + "unitSystemMetric": "Metrakerfi", + "@unitSystemMetric": {}, + "overlayHistogramNone": "Ekkert", + "@overlayHistogramNone": {}, + "filterRecentlyAddedLabel": "Nýlega bætt við", + "@filterRecentlyAddedLabel": {}, + "editEntryDialogTargetFieldsHeader": "Gagnasvið sem á að breyta", + "@editEntryDialogTargetFieldsHeader": {}, + "storageVolumeDescriptionFallbackPrimary": "innbyggðri geymslu", + "@storageVolumeDescriptionFallbackPrimary": {}, + "collectionActionAddShortcut": "Bæta við flýtileið", + "@collectionActionAddShortcut": {}, + "settingsViewerShowMinimap": "Birta smákort", + "@settingsViewerShowMinimap": {}, + "previousTooltip": "Fyrra", + "@previousTooltip": {}, + "viewerInfoLabelPath": "Slóð", + "@viewerInfoLabelPath": {}, + "albumGroupVolume": "Eftir gagnageymslu", + "@albumGroupVolume": {}, + "collectionGroupMonth": "Eftir mánuðum", + "@collectionGroupMonth": {}, + "viewerInfoLabelResolution": "Upplausn", + "@viewerInfoLabelResolution": {}, + "renameProcessorCounter": "Teljari", + "@renameProcessorCounter": {}, + "settingsImageBackground": "Bakgrunnur mynda", + "@settingsImageBackground": {}, + "newAlbumDialogNameLabel": "Heiti albúms", + "@newAlbumDialogNameLabel": {}, + "cropAspectRatioOriginal": "Upprunaleg", + "@cropAspectRatioOriginal": {}, + "sortByDate": "Eftir dagsetningu", + "@sortByDate": {}, + "statsTopAlbumsSectionTitle": "Vinsælustu albúm", + "@statsTopAlbumsSectionTitle": {}, + "entryActionRename": "Endurnefna", + "@entryActionRename": {}, + "viewerInfoLabelDuration": "Tímalengd", + "@viewerInfoLabelDuration": {}, + "sortOrderZtoA": "Ö til A", + "@sortOrderZtoA": {}, + "keepScreenOnNever": "Aldrei", + "@keepScreenOnNever": {}, + "statsPageTitle": "Tölfræði", + "@statsPageTitle": {}, + "viewerInfoSearchSuggestionDimensions": "Stærðir", + "@viewerInfoSearchSuggestionDimensions": {}, + "mapStyleOsmHot": "OSM fyrir hjálparstarf", + "@mapStyleOsmHot": {}, + "drawerAboutButton": "Um hugbúnaðinn", + "@drawerAboutButton": {}, + "aboutTranslatorsSectionTitle": "Þýðendur", + "@aboutTranslatorsSectionTitle": {}, + "statsTopPlacesSectionTitle": "Algengustu staðir", + "@statsTopPlacesSectionTitle": {}, + "videoStreamSelectionDialogOff": "Slökkt", + "@videoStreamSelectionDialogOff": {}, + "settingsVideoLoopModeDialogTitle": "Endurtekningarhamur", + "@settingsVideoLoopModeDialogTitle": {}, + "drawerCollectionImages": "Myndir", + "@drawerCollectionImages": {}, + "sortOrderSmallestFirst": "Minnsta fyrst", + "@sortOrderSmallestFirst": {}, + "timeSeconds": "{seconds, plural, =1{1 sekúnda} other{{seconds} sekúndur}}", + "@timeSeconds": { + "placeholders": { + "seconds": {} + } + }, + "sortBySize": "Eftir stærð", + "@sortBySize": {}, + "viewerInfoSearchSuggestionDescription": "Lýsing", + "@viewerInfoSearchSuggestionDescription": {}, + "settingsViewerShowOverlayThumbnails": "Birta smámyndir", + "@settingsViewerShowOverlayThumbnails": {}, + "collectionPickPageTitle": "Veldu", + "@collectionPickPageTitle": {}, + "settingsVideoControlsPageTitle": "Stýringar", + "@settingsVideoControlsPageTitle": {}, + "welcomeOptional": "Valfrjálst", + "@welcomeOptional": {}, + "aboutBugReportButton": "Tilkynna", + "@aboutBugReportButton": {}, + "collectionEmptyGrantAccessButtonLabel": "Veita aðgang", + "@collectionEmptyGrantAccessButtonLabel": {}, + "filterTypeMotionPhotoLabel": "Hreyfiljósmynd", + "@filterTypeMotionPhotoLabel": {}, + "resetTooltip": "Endurstilla", + "@resetTooltip": {}, + "mapStyleGoogleNormal": "Google Maps", + "@mapStyleGoogleNormal": {}, + "videoStreamSelectionDialogVideo": "Myndskeið", + "@videoStreamSelectionDialogVideo": {}, + "viewDialogLayoutSectionTitle": "Framsetning", + "@viewDialogLayoutSectionTitle": {}, + "chipActionUnpin": "Losa af efri hluta", + "@chipActionUnpin": {}, + "searchStatesSectionTitle": "Héruð", + "@searchStatesSectionTitle": {}, + "chipActionGoToTagPage": "Birta í Merki", + "@chipActionGoToTagPage": {}, + "filterTypeAnimatedLabel": "Með hreyfingu", + "@filterTypeAnimatedLabel": {}, + "videoLoopModeNever": "Aldrei", + "@videoLoopModeNever": {}, + "settingsUnitSystemDialogTitle": "Einingar", + "@settingsUnitSystemDialogTitle": {}, + "videoActionSetSpeed": "Afspilunarhraði", + "@videoActionSetSpeed": {}, + "dateThisMonth": "Í þessum mánuði", + "@dateThisMonth": {}, + "renameEntrySetPageTitle": "Endurnefna", + "@renameEntrySetPageTitle": {}, + "settingsConfirmationBeforeMoveToBinItems": "Spyrja áður en atriði eru færð í ruslmöppuna", + "@settingsConfirmationBeforeMoveToBinItems": {}, + "settingsSlideshowVideoPlaybackTile": "Afspilun myndskeiða", + "@settingsSlideshowVideoPlaybackTile": {}, + "settingsHiddenFiltersEmpty": "Engar felusíur", + "@settingsHiddenFiltersEmpty": {}, + "aboutPageTitle": "Um hugbúnaðinn", + "@aboutPageTitle": {}, + "sortByRating": "Eftir einkunn", + "@sortByRating": {}, + "stateEmpty": "Engin héruð", + "@stateEmpty": {}, + "viewerInfoSearchEmpty": "Engir samsvarandi lyklar", + "@viewerInfoSearchEmpty": {}, + "filterFavouriteLabel": "Eftirlæti", + "@filterFavouriteLabel": {}, + "drawerCountryPage": "Lönd", + "@drawerCountryPage": {}, + "aboutBugSectionTitle": "Villuskýrsla", + "@aboutBugSectionTitle": {}, + "filterRatingRejectedLabel": "Hafnað", + "@filterRatingRejectedLabel": {}, + "sourceStateLocatingPlaces": "Staðset staði", + "@sourceStateLocatingPlaces": {}, + "settingsThumbnailShowLocationIcon": "Birta tákn fyrir staðsetningu", + "@settingsThumbnailShowLocationIcon": {}, + "editEntryDialogCopyFromItem": "Afrita úr öðru atriði", + "@editEntryDialogCopyFromItem": {}, + "settingsStorageAccessPageTitle": "Aðgengi geymslu", + "@settingsStorageAccessPageTitle": {}, + "genericFailureFeedback": "Mistókst", + "@genericFailureFeedback": {}, + "aboutDataUsageData": "Gögn", + "@aboutDataUsageData": {}, + "settingsThemeEnableDynamicColor": "Breytilegur litur", + "@settingsThemeEnableDynamicColor": {}, + "viewerInfoOpenEmbeddedFailureFeedback": "Mistókst að ná í ívafin gögn", + "@viewerInfoOpenEmbeddedFailureFeedback": {}, + "aboutDataUsageInternal": "Innri", + "@aboutDataUsageInternal": {}, + "albumDownload": "Sækja", + "@albumDownload": {}, + "coverDialogTabColor": "Litur", + "@coverDialogTabColor": {}, + "coordinateDmsWest": "V", + "@coordinateDmsWest": {}, + "genericSuccessFeedback": "Lokið!", + "@genericSuccessFeedback": {}, + "videoActionMute": "Hljóð af", + "@videoActionMute": {}, + "renameEntryDialogLabel": "Nýtt heiti", + "@renameEntryDialogLabel": {}, + "renameEntrySetPageInsertTooltip": "Setja inn gagnasvið", + "@renameEntrySetPageInsertTooltip": {}, + "aboutLicensesShowAllButtonLabel": "Sýna öll notkunarleyfi", + "@aboutLicensesShowAllButtonLabel": {}, + "settingsSubtitleThemeBackgroundOpacity": "Ógegnsæi bakgrunns", + "@settingsSubtitleThemeBackgroundOpacity": {}, + "settingsStorageAccessTile": "Aðgengi geymslu", + "@settingsStorageAccessTile": {}, + "entryActionDelete": "Eyða", + "@entryActionDelete": {}, + "settingsThemeBrightnessTile": "Þema", + "@settingsThemeBrightnessTile": {}, + "settingsViewerQuickActionEmpty": "Engir hnappar", + "@settingsViewerQuickActionEmpty": {}, + "viewerInfoBackToViewerTooltip": "Til baka í skoðara", + "@viewerInfoBackToViewerTooltip": {}, + "settingsSystemDefault": "Sjálfgefið í kerfinu", + "@settingsSystemDefault": {}, + "settingsViewerSlideshowTile": "Skyggnusýning", + "@settingsViewerSlideshowTile": {}, + "settingsCollectionTile": "Safn", + "@settingsCollectionTile": {}, + "entryActionRotateCW": "Snúa réttsælis", + "@entryActionRotateCW": {}, + "settingsSubtitleThemeTextOpacity": "Ógegnsæi texta", + "@settingsSubtitleThemeTextOpacity": {}, + "sortOrderNewestFirst": "Nýjast fyrst", + "@sortOrderNewestFirst": {}, + "settingsViewerShowRatingTags": "Birta einkunn og merki", + "@settingsViewerShowRatingTags": {}, + "drawerSettingsButton": "Stillingar", + "@drawerSettingsButton": {}, + "entryActionFlip": "Fletta lárétt", + "@entryActionFlip": {}, + "drawerCollectionRaws": "RAW-myndir", + "@drawerCollectionRaws": {}, + "settingsViewerEnableOverlayBlurEffect": "Móðunarbrella", + "@settingsViewerEnableOverlayBlurEffect": {}, + "settingsMotionPhotoAutoPlay": "Spila hreyfiljósmyndir sjálfkrafa", + "@settingsMotionPhotoAutoPlay": {}, + "overlayHistogramRGB": "RGB", + "@overlayHistogramRGB": {}, + "settingsVideoControlsTile": "Stýringar", + "@settingsVideoControlsTile": {}, + "settingsSlideshowVideoPlaybackDialogTitle": "Afspilun myndskeiða", + "@settingsSlideshowVideoPlaybackDialogTitle": {}, + "settingsSubtitleThemeShowOutline": "Sýna útlínur og skugga", + "@settingsSubtitleThemeShowOutline": {}, + "settingsNavigationDrawerTabAlbums": "Albúm", + "@settingsNavigationDrawerTabAlbums": {}, + "subtitlePositionBottom": "Neðst", + "@subtitlePositionBottom": {}, + "castDialogTitle": "Útvörpunartæki", + "@castDialogTitle": {}, + "timeDays": "{days, plural, =1{1 dagur} other{{days} dagar}}", + "@timeDays": { + "placeholders": { + "days": {} + } + }, + "videoResumeButtonLabel": "HALDA ÁFRAM", + "@videoResumeButtonLabel": {}, + "entryActionExport": "Flytja út", + "@entryActionExport": {}, + "mapEmptyRegion": "Engar myndir á þessu svæði", + "@mapEmptyRegion": {}, + "settingsVideoEnablePip": "Mynd-í-mynd", + "@settingsVideoEnablePip": {}, + "settingsThemeBrightnessDialogTitle": "Þema", + "@settingsThemeBrightnessDialogTitle": {}, + "aboutLicensesFlutterPluginsSectionTitle": "Flutter-viðbætur", + "@aboutLicensesFlutterPluginsSectionTitle": {}, + "sortOrderHighestFirst": "Hæsta fyrst", + "@sortOrderHighestFirst": {}, + "aboutLicensesAndroidLibrariesSectionTitle": "Android-aðgerðasöfn", + "@aboutLicensesAndroidLibrariesSectionTitle": {}, + "settingsViewerQuickActionsTile": "Flýtiaðgerðir", + "@settingsViewerQuickActionsTile": {}, + "entryActionAddFavourite": "Bæta í eftirlæti", + "@entryActionAddFavourite": {}, + "entryActionEdit": "Breyta", + "@entryActionEdit": {}, + "entryInfoActionEditRating": "Breyta einkunn", + "@entryInfoActionEditRating": {}, + "timeMinutes": "{minutes, plural, =1{1 mínúta} other{{minutes} mínútur}}", + "@timeMinutes": { + "placeholders": { + "minutes": {} + } + }, + "albumTierSpecial": "Algengt", + "@albumTierSpecial": {}, + "settingsSubtitleThemePageTitle": "Skjátextar", + "@settingsSubtitleThemePageTitle": {}, + "settingsSubtitleThemeTextPositionTile": "Textastaðsetning", + "@settingsSubtitleThemeTextPositionTile": {}, + "entryActionRestore": "Endurheimta", + "@entryActionRestore": {}, + "entryActionCopyToClipboard": "Afrita á klippispjald", + "@entryActionCopyToClipboard": {}, + "tagEditorSectionPlaceholders": "Ígildisbreytur", + "@tagEditorSectionPlaceholders": {}, + "viewerInfoLabelAddress": "Heimilisfang", + "@viewerInfoLabelAddress": {}, + "settingsVideoBackgroundModeDialogTitle": "Bakgrunnshamur", + "@settingsVideoBackgroundModeDialogTitle": {}, + "binPageTitle": "Ruslmappa", + "@binPageTitle": {}, + "tagPlaceholderState": "Hérað", + "@tagPlaceholderState": {}, + "sortByAlbumFileName": "Eftir heiti albúma og skráa", + "@sortByAlbumFileName": {}, + "deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Eyða þessum albúmum og atriðinu í þeim?} other{Eyða þessum albúmum og {count} atriðum í þeim??}}", + "@deleteMultiAlbumConfirmationDialogMessage": { + "placeholders": { + "count": {} + } + }, + "settingsSubtitleThemeTextSize": "Stærð texta", + "@settingsSubtitleThemeTextSize": {}, + "aboutLicensesSectionTitle": "Notkunarleyfi opins hugbúnaðar", + "@aboutLicensesSectionTitle": {}, + "menuActionStats": "Tölfræði", + "@menuActionStats": {}, + "settingsCollectionQuickActionTabBrowsing": "Vafur", + "@settingsCollectionQuickActionTabBrowsing": {}, + "chipActionCreateAlbum": "Búa til albúm", + "@chipActionCreateAlbum": {}, + "videoLoopModeShortOnly": "Einungis stutt myndskeið", + "@videoLoopModeShortOnly": {}, + "deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Eyða þessu albúmi og atriðinu í því?} other{Eyða þessu albúmi og {count} atriðum í því??}}", + "@deleteSingleAlbumConfirmationDialogMessage": { + "placeholders": { + "count": {} + } + }, + "settingsThemeColorHighlights": "Hátónalitun", + "@settingsThemeColorHighlights": {}, + "appPickDialogTitle": "Veldu forrit", + "@appPickDialogTitle": {}, + "cancelTooltip": "Hætta við", + "@cancelTooltip": {}, + "settingsHiddenItemsTabPaths": "Faldar slóðir", + "@settingsHiddenItemsTabPaths": {}, + "viewerInfoSearchSuggestionRights": "Réttindi", + "@viewerInfoSearchSuggestionRights": {}, + "mapPointNorthUpTooltip": "Norður beint upp", + "@mapPointNorthUpTooltip": {}, + "entryInfoActionRemoveLocation": "Fjarlægja staðsetningu", + "@entryInfoActionRemoveLocation": {}, + "collectionDeleteFailureFeedback": "{count, plural, =1{Mistókst að eyða 1 atriði} other{Mistókst að eyða {count} atriðum}}", + "@collectionDeleteFailureFeedback": { + "placeholders": { + "count": {} + } + }, + "entryActionShareImageOnly": "Deila einungis mynd", + "@entryActionShareImageOnly": {}, + "settingsVideoButtonsTile": "Hnappar", + "@settingsVideoButtonsTile": {}, + "settingsSubtitleThemeSample": "Þetta er dæmi.", + "@settingsSubtitleThemeSample": {}, + "albumPickPageTitleMove": "Færa í albúm", + "@albumPickPageTitleMove": {}, + "settingsSaveSearchHistory": "Vista leitarferil", + "@settingsSaveSearchHistory": {}, + "settingsViewerShowHistogram": "Birta litatíðnirit", + "@settingsViewerShowHistogram": {}, + "aboutBugCopyInfoButton": "Afrita", + "@aboutBugCopyInfoButton": {}, + "mapStyleGoogleHybrid": "Google Maps (blandað)", + "@mapStyleGoogleHybrid": {}, + "wallpaperTargetHomeLock": "Upphafs- og lásskjáir", + "@wallpaperTargetHomeLock": {}, + "coverDialogTabCover": "Umslag", + "@coverDialogTabCover": {}, + "settingsVideoPlaybackTile": "Afspilun", + "@settingsVideoPlaybackTile": {}, + "filterNoDateLabel": "Ódagsett", + "@filterNoDateLabel": {}, + "exportEntryDialogWriteMetadata": "Skrifa lýsigögn", + "@exportEntryDialogWriteMetadata": {}, + "chipActionSetCover": "Setja umslag", + "@chipActionSetCover": {}, + "chipActionFilterOut": "Sía út", + "@chipActionFilterOut": {}, + "sourceStateCataloguing": "Gerð efnisyfirlits", + "@sourceStateCataloguing": {}, + "chipActionFilterIn": "Sía inn", + "@chipActionFilterIn": {}, + "albumTierVaults": "Öryggisgeymslur", + "@albumTierVaults": {}, + "vaultBinUsageDialogMessage": "Sumar öryggisgeymslur nota ruslmöppuna.", + "@vaultBinUsageDialogMessage": {}, + "chipActionCreateVault": "Búa til öryggisgeymslu", + "@chipActionCreateVault": {}, + "authenticateToUnlockVault": "Auðkenndu til að aflæsa öryggisgeymslu", + "@authenticateToUnlockVault": {}, + "chipActionConfigureVault": "Stilla öryggisgeymslu", + "@chipActionConfigureVault": {}, + "newVaultWarningDialogMessage": "Atriði í öryggisgeymslum eru einungis aðgengileg í þessu forriti og eingum öðrum.\n\nEf þú fjarlægir þetta forrit, eða hreinsar gögn forritsins, muntu tapa öllum þessum atriðum.", + "@newVaultWarningDialogMessage": {}, + "keepScreenOnViewerOnly": "Aðeins síða skoðara", + "@keepScreenOnViewerOnly": {}, + "settingsConfirmationVaultDataLoss": "Birta aðvörun um gagnatap öryggisgeymslu", + "@settingsConfirmationVaultDataLoss": {}, + "authenticateToConfigureVault": "Auðkenndu til að stilla öryggisgeymslu", + "@authenticateToConfigureVault": {}, + "chipActionShowCountryStates": "Birta héruð", + "@chipActionShowCountryStates": {}, + "configureVaultDialogTitle": "Stilla öryggisgeymslu", + "@configureVaultDialogTitle": {}, + "newVaultDialogTitle": "Ný öryggisgeymsla", + "@newVaultDialogTitle": {}, + "missingSystemFilePickerDialogMessage": "Skráaveljari kerfisins er óvirkur eða hann vantar. Virkjaðu hann og reydu síðan aftur.", + "@missingSystemFilePickerDialogMessage": {}, + "storageAccessDialogMessage": "Veldu {directory} á „{volume}“ í næsta skjá til að gefa forritinu aðgang að henni.", + "@storageAccessDialogMessage": { + "placeholders": { + "directory": { + "type": "String", + "description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "hideFilterConfirmationDialogMessage": "Samsvarandi ljósmyndir og myndskeið verða falin frá safninu þínu. Þú getur látið þetta birtast aftur með því að fara í „Persónuvernd“-stillingarnar.\n\nErtu viss um að þú viljir fela þetta?", + "@hideFilterConfirmationDialogMessage": {}, + "settingsViewerGestureSideTapNext": "Ýttu á skjájaðra til að birta fyrra/næsta atriði", + "@settingsViewerGestureSideTapNext": {}, + "restrictedAccessDialogMessage": "Þessu forriti er ekki heimilt að breyta skrám í {directory} á „{volume}“.\n\nNotaðu for-uppsettan skráastjóra eða myndasafnsforrit til að færa atriðin í aðra möppu.", + "@restrictedAccessDialogMessage": { + "placeholders": { + "directory": { + "type": "String", + "description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "unsupportedTypeDialogMessage": "{count, plural, =1{Þessi aðgerð er ekki studd fyrir atriði af tegundinni: {types}.} other{Þessi aðgerð er ekki studd fyrir atriði af eftirfarandi tegundum: {types}.}}", + "@unsupportedTypeDialogMessage": { + "placeholders": { + "count": {}, + "types": { + "type": "String", + "example": "GIF, TIFF, MP4", + "description": "a list of unsupported types" + } + } + }, + "notEnoughSpaceDialogMessage": "Þessi aðgerð þarfnast {neededSize} af lausu plássi á „{volume}“ til að geta klárað, en einungis {freeSize} eru eftir.", + "@notEnoughSpaceDialogMessage": { + "placeholders": { + "neededSize": { + "type": "String", + "example": "314 MB" + }, + "freeSize": { + "type": "String", + "example": "123 MB" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "settingsStorageAccessBanner": "Sumar möppur krefjast þess að gefin sé sérstök heimild til að breyta skrám í þeim. Þú getur yfirfarið hér þær möppur sem þú hefur gefið aðgangaheimildir fyrir.", + "@settingsStorageAccessBanner": {}, + "settingsCollectionBurstPatternsTile": "Mynstur runu", + "@settingsCollectionBurstPatternsTile": {} +} diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index aa20c5cd6..b2a80a83a 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -1354,5 +1354,11 @@ "overlayHistogramLuminance": "휘도", "@overlayHistogramLuminance": {}, "settingsViewerShowHistogram": "히스토그램 표시", - "@settingsViewerShowHistogram": {} + "@settingsViewerShowHistogram": {}, + "aboutDataUsageClearCache": "캐시 삭제", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "전송", + "@entryActionCast": {}, + "castDialogTitle": "전송 대상", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_or.arb b/lib/l10n/app_or.arb index 8de8aec37..03443ab5f 100644 --- a/lib/l10n/app_or.arb +++ b/lib/l10n/app_or.arb @@ -1,11 +1,11 @@ { - "aboutPageTitle": "ଵିଷୟରେ", + "aboutPageTitle": "ସମ୍ବନ୍ଧରେ", "@aboutPageTitle": {}, "sortBySize": "ଆକାର ଅନୁଯାୟୀ", "@sortBySize": {}, "albumCamera": "କ୍ୟାମେରା", "@albumCamera": {}, - "drawerAboutButton": "ଵିଷୟରେ", + "drawerAboutButton": "ସମ୍ବନ୍ଧରେ", "@drawerAboutButton": {}, "drawerSettingsButton": "ସେଟିଂ", "@drawerSettingsButton": {}, @@ -138,5 +138,11 @@ "setCoverDialogAuto": "ସ୍ୱତଃ", "@setCoverDialogAuto": {}, "welcomeOptional": "ଵୈକଳ୍ପିକ", - "@welcomeOptional": {} + "@welcomeOptional": {}, + "viewDialogGroupSectionTitle": "ଗୋଷ୍ଠୀ", + "@viewDialogGroupSectionTitle": {}, + "viewDialogSortSectionTitle": "ସଜାନ୍ତୁ", + "@viewDialogSortSectionTitle": {}, + "menuActionStats": "ପରିସଂଖ୍ୟାନ", + "@menuActionStats": {} } diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index f770b66fa..f8ff7e9e8 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1512,5 +1512,11 @@ "overlayHistogramLuminance": "Jasność", "@overlayHistogramLuminance": {}, "settingsViewerShowHistogram": "Pokaż histogram", - "@settingsViewerShowHistogram": {} + "@settingsViewerShowHistogram": {}, + "aboutDataUsageClearCache": "Wyczyść pamięć podręczną", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "Cast", + "@entryActionCast": {}, + "castDialogTitle": "Urządzenia Cast", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index f03aef8a7..0bc76c5b8 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -501,7 +501,7 @@ "@aboutLinkLicense": {}, "aboutLinkPolicy": "Политика конфиденциальности", "@aboutLinkPolicy": {}, - "aboutBugSectionTitle": "Отчет об ошибке", + "aboutBugSectionTitle": "Сообщить об ошибке", "@aboutBugSectionTitle": {}, "aboutBugSaveLogInstruction": "Сохраните логи приложения в файл", "@aboutBugSaveLogInstruction": {}, @@ -731,7 +731,7 @@ "@searchMetadataSectionTitle": {}, "settingsPageTitle": "Настройки", "@settingsPageTitle": {}, - "settingsSystemDefault": "Система", + "settingsSystemDefault": "Как в системе", "@settingsSystemDefault": {}, "settingsDefault": "По умолчанию", "@settingsDefault": {}, @@ -1354,5 +1354,11 @@ "settingsViewerShowHistogram": "Показать гистограмму", "@settingsViewerShowHistogram": {}, "overlayHistogramLuminance": "Яркость", - "@overlayHistogramLuminance": {} + "@overlayHistogramLuminance": {}, + "aboutDataUsageClearCache": "Очистить кэш", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "Трансляция", + "@entryActionCast": {}, + "castDialogTitle": "Устройства трансляции", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index 181bc9c66..a6491c7f3 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -1512,5 +1512,11 @@ "settingsViewerShowHistogram": "Zobraziť histogram", "@settingsViewerShowHistogram": {}, "settingsVideoPlaybackTile": "Prehrávanie", - "@settingsVideoPlaybackTile": {} + "@settingsVideoPlaybackTile": {}, + "entryActionCast": "Krúžky", + "@entryActionCast": {}, + "aboutDataUsageClearCache": "Vymazať cache", + "@aboutDataUsageClearCache": {}, + "castDialogTitle": "Cast zariadenia", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb index 00879c955..9f73f7463 100644 --- a/lib/l10n/app_uk.arb +++ b/lib/l10n/app_uk.arb @@ -1231,7 +1231,7 @@ "@tagPageTitle": {}, "searchAlbumsSectionTitle": "Альбоми", "@searchAlbumsSectionTitle": {}, - "settingsSystemDefault": "Система за замовчуванням", + "settingsSystemDefault": "Як у системі", "@settingsSystemDefault": {}, "searchRatingSectionTitle": "Рейтинги", "@searchRatingSectionTitle": {}, @@ -1512,5 +1512,11 @@ "overlayHistogramLuminance": "Яскравість", "@overlayHistogramLuminance": {}, "settingsViewerShowHistogram": "Показати гістограму", - "@settingsViewerShowHistogram": {} + "@settingsViewerShowHistogram": {}, + "aboutDataUsageClearCache": "Очистити кеш", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "Трансляція", + "@entryActionCast": {}, + "castDialogTitle": "Пристрої трансляції", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_vi.arb b/lib/l10n/app_vi.arb index fd2e869b1..7d628e698 100644 --- a/lib/l10n/app_vi.arb +++ b/lib/l10n/app_vi.arb @@ -1512,5 +1512,11 @@ "settingsVideoPlaybackTile": "Phát lại", "@settingsVideoPlaybackTile": {}, "exportEntryDialogWriteMetadata": "Viết metadata", - "@exportEntryDialogWriteMetadata": {} + "@exportEntryDialogWriteMetadata": {}, + "aboutDataUsageClearCache": "Xóa bộ nhớ đệm", + "@aboutDataUsageClearCache": {}, + "entryActionCast": "Truyền", + "@entryActionCast": {}, + "castDialogTitle": "Thiết bị truyền", + "@castDialogTitle": {} } diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index b9d502ed7..141cf4d61 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -1226,5 +1226,7 @@ "vaultLockTypePassword": "密码", "@vaultLockTypePassword": {}, "overlayHistogramRGB": "RGB", - "@overlayHistogramRGB": {} + "@overlayHistogramRGB": {}, + "saveCopyButtonLabel": "保存副本", + "@saveCopyButtonLabel": {} } diff --git a/lib/main_common.dart b/lib/main_common.dart index f525bab0d..91736a6e7 100644 --- a/lib/main_common.dart +++ b/lib/main_common.dart @@ -3,7 +3,9 @@ import 'dart:isolate'; import 'package:aves/app_flavor.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/aves_app.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:leak_tracker/leak_tracker.dart'; void mainCommon(AppFlavor flavor, {Map? debugIntentData}) { // HttpClient.enableTimelineLogging = true; // enable network traffic logging @@ -35,5 +37,9 @@ void mainCommon(AppFlavor flavor, {Map? debugIntentData}) { // ErrorWidget.builder = (details) => ErrorWidget(details.exception); // cf https://docs.flutter.dev/testing/errors + LeakTracking.start(); + MemoryAllocations.instance.addListener( + (event) => LeakTracking.dispatchObjectEvent(event.toMap()), + ); runApp(AvesApp(flavor: flavor, debugIntentData: debugIntentData)); } diff --git a/lib/model/app/contributors.dart b/lib/model/app/contributors.dart index 8bb1807fb..031b6c9a3 100644 --- a/lib/model/app/contributors.dart +++ b/lib/model/app/contributors.dart @@ -62,6 +62,10 @@ class Contributors { Contributor('Le Nhut Binh', 'bebinh2202@gmail.com'), Contributor('Martin Frandel', 'martinko.fr@gmail.com'), Contributor('Milan Šalka', 'salka.milan@googlemail.com'), + Contributor('Diviega Ayulsa', 'ayulsadiviega@gmail.com'), + Contributor('Fqwe1', 'Fqwe1@users.noreply.hosted.weblate.org'), + Contributor('Reza Almanda', 'rezaalmanda27@gmail.com'), + Contributor('Sveinn í Felli', 'sv1@fellsnet.is'), // Contributor('SAMIRAH AIL', 'samiratalzahrani@gmail.com'), // Arabic // Contributor('Salih Ail', 'rrrfff444@gmail.com'), // Arabic // Contributor('nasreddineloukriz', 'nasreddineloukriz@gmail.com'), // Arabic @@ -79,6 +83,7 @@ class Contributors { // Contributor('Subham Jena', 'subhamjena8465@gmail.com'), // Odia // Contributor('امیر جهانگرد', 'ijahangard.a@gmail.com'), // Persian // Contributor('slasb37', 'p84haghi@gmail.com'), // Persian + // Contributor('mimvahedi', 'vahedi0vahedi@gmail.com'), // Persian // Contributor('mytja', 'mamnju21@gmail.com'), // Slovenian // Contributor('Nattapong K', 'mixer5056@gmail.com'), // Thai }; diff --git a/lib/model/app/dependencies.dart b/lib/model/app/dependencies.dart index 90ec993aa..62836c974 100644 --- a/lib/model/app/dependencies.dart +++ b/lib/model/app/dependencies.dart @@ -73,6 +73,11 @@ class Dependencies { licenseUrl: 'https://github.com/material-foundation/flutter-packages/blob/main/packages/dynamic_color/LICENSE', sourceUrl: 'https://github.com/material-foundation/flutter-packages/tree/main/packages/dynamic_color', ), + Dependency( + name: 'FFmpegKit', + license: lgpl3, + sourceUrl: 'https://github.com/arthenica/ffmpeg-kit', + ), Dependency( name: 'Floating', license: mit, @@ -89,16 +94,17 @@ class Dependencies { licenseUrl: 'https://github.com/flutter/packages/blob/main/packages/local_auth/local_auth/LICENSE', sourceUrl: 'https://github.com/flutter/packages/tree/main/packages/local_auth/local_auth', ), - Dependency( - name: 'FFmpegKit', - license: lgpl3, - sourceUrl: 'https://github.com/arthenica/ffmpeg-kit', - ), Dependency( name: 'Media Kit', license: mit, sourceUrl: 'https://github.com/media-kit/media-kit', ), + Dependency( + name: 'Network Info Plus', + license: bsd3, + licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/network_info_plus/network_info_plus/LICENSE', + sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/network_info_plus', + ), Dependency( name: 'Package Info Plus', license: bsd3, @@ -305,6 +311,11 @@ class Dependencies { license: mit, sourceUrl: 'https://github.com/denixport/dart.country', ), + Dependency( + name: 'DLNA Dart', + license: bsd3, + sourceUrl: 'https://github.com/suconghou/dlna-dart', + ), Dependency( name: 'Equatable', license: mit, @@ -347,6 +358,11 @@ class Dependencies { licenseUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart/LICENSE', sourceUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart', ), + Dependency( + name: 'Memory Leak Tracker', + license: bsd3, + sourceUrl: 'https://github.com/dart-lang/leak_tracker', + ), Dependency( name: 'Path', license: bsd3, @@ -362,6 +378,11 @@ class Dependencies { license: mit, sourceUrl: 'https://github.com/maRci002/proj4dart', ), + Dependency( + name: 'Shelf', + license: bsd3, + sourceUrl: 'https://github.com/dart-lang/shelf', + ), Dependency( name: 'Stack Trace', license: bsd3, diff --git a/lib/model/device.dart b/lib/model/device.dart index c70c1d8ed..e2c687e21 100644 --- a/lib/model/device.dart +++ b/lib/model/device.dart @@ -8,11 +8,15 @@ import 'package:package_info_plus/package_info_plus.dart'; final Device device = Device._private(); class Device { - late final String _userAgent; + late final String _packageName, _packageVersion, _userAgent; late final bool _canAuthenticateUser, _canGrantDirectoryAccess, _canPinShortcut; late final bool _canRenderFlagEmojis, _canRenderSubdivisionFlagEmojis, _canRequestManageMedia, _canSetLockScreenWallpaper, _canUseCrypto; late final bool _hasGeocoder, _isDynamicColorAvailable, _isTelevision, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode, _supportPictureInPicture; + String get packageName => _packageName; + + String get packageVersion => _packageVersion; + String get userAgent => _userAgent; bool get canAuthenticateUser => _canAuthenticateUser; @@ -49,7 +53,9 @@ class Device { Future init() async { final packageInfo = await PackageInfo.fromPlatform(); - _userAgent = '${packageInfo.packageName}/${packageInfo.version}'; + _packageName = packageInfo.packageName; + _packageVersion = packageInfo.version; + _userAgent = '$_packageName/$_packageVersion'; final androidInfo = await DeviceInfoPlugin().androidInfo; _isTelevision = androidInfo.systemFeatures.contains('android.software.leanback'); diff --git a/lib/model/entry/entry.dart b/lib/model/entry/entry.dart index a58d369b2..5b8f7c12d 100644 --- a/lib/model/entry/entry.dart +++ b/lib/model/entry/entry.dart @@ -49,8 +49,7 @@ class AvesEntry with AvesEntryBase { @override final AChangeNotifier visualChangeNotifier = AChangeNotifier(); - final AChangeNotifier metadataChangeNotifier = AChangeNotifier(); - final AChangeNotifier addressChangeNotifier = AChangeNotifier(); + final AChangeNotifier metadataChangeNotifier = AChangeNotifier(), addressChangeNotifier = AChangeNotifier(); AvesEntry({ required int? id, @@ -72,6 +71,13 @@ class AvesEntry with AvesEntryBase { required this.origin, this.burstEntries, }) : id = id ?? 0 { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$AvesEntry', + object: this, + ); + } this.path = path; this.sourceTitle = sourceTitle; this.dateModifiedSecs = dateModifiedSecs; @@ -181,6 +187,9 @@ class AvesEntry with AvesEntryBase { } void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } visualChangeNotifier.dispose(); metadataChangeNotifier.dispose(); addressChangeNotifier.dispose(); diff --git a/lib/model/entry/extensions/metadata_edition.dart b/lib/model/entry/extensions/metadata_edition.dart index e0ddc829f..cf92324c9 100644 --- a/lib/model/entry/extensions/metadata_edition.dart +++ b/lib/model/entry/extensions/metadata_edition.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:aves/convert/convert.dart'; +import 'package:aves/model/device.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/catalog.dart'; import 'package:aves/model/entry/extensions/props.dart'; @@ -18,7 +19,6 @@ import 'package:aves_model/aves_model.dart'; import 'package:flutter/foundation.dart'; import 'package:intl/intl.dart'; import 'package:latlong2/latlong.dart'; -import 'package:package_info_plus/package_info_plus.dart'; import 'package:xml/xml.dart'; extension ExtraAvesEntryMetadataEdition on AvesEntry { @@ -569,7 +569,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry { final editedXmpString = await XMP.edit( xmpString, - () => PackageInfo.fromPlatform().then((v) => 'Aves v${v.version}'), + 'Aves v${device.packageVersion}', apply, ); diff --git a/lib/model/events.dart b/lib/model/events.dart index e6fea841f..8a82b1e23 100644 --- a/lib/model/events.dart +++ b/lib/model/events.dart @@ -13,10 +13,10 @@ class ActionEvent extends Equatable { @immutable class ActionStartedEvent extends ActionEvent { - const ActionStartedEvent(T action) : super(action); + const ActionStartedEvent(super.action); } @immutable class ActionEndedEvent extends ActionEvent { - const ActionEndedEvent(T action) : super(action); + const ActionEndedEvent(super.action); } diff --git a/lib/model/filters/location.dart b/lib/model/filters/location.dart index 3a22391b5..f66bf26ea 100644 --- a/lib/model/filters/location.dart +++ b/lib/model/filters/location.dart @@ -96,7 +96,7 @@ class LocationFilter extends CoveredCollectionFilter { return Text( flag, style: TextStyle(fontSize: size), - textScaleFactor: 1.0, + textScaler: TextScaler.noScaling, ); } } @@ -108,7 +108,7 @@ class LocationFilter extends CoveredCollectionFilter { return Text( flag, style: TextStyle(fontSize: size), - textScaleFactor: 1.0, + textScaler: TextScaler.noScaling, ); } } diff --git a/lib/model/query.dart b/lib/model/query.dart index 29d950240..518d6941f 100644 --- a/lib/model/query.dart +++ b/lib/model/query.dart @@ -16,6 +16,13 @@ class Query extends ChangeNotifier { } } + @override + void dispose() { + _focusRequestNotifier.dispose(); + _queryNotifier.dispose(); + super.dispose(); + } + bool _enabled = false; bool get enabled => _enabled; diff --git a/lib/model/source/analysis_controller.dart b/lib/model/source/analysis_controller.dart index f6996674c..b36c2306e 100644 --- a/lib/model/source/analysis_controller.dart +++ b/lib/model/source/analysis_controller.dart @@ -4,7 +4,8 @@ class AnalysisController { final bool canStartService, force; final int progressTotal, progressOffset; final List? entryIds; - final ValueNotifier stopSignal; + + final ValueNotifier _stopSignal = ValueNotifier(false); AnalysisController({ this.canStartService = true, @@ -12,8 +13,24 @@ class AnalysisController { this.force = false, this.progressTotal = 0, this.progressOffset = 0, - ValueNotifier? stopSignal, - }) : stopSignal = stopSignal ?? ValueNotifier(false); + }) { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$AnalysisController', + object: this, + ); + } + } + + void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } + _stopSignal.dispose(); + } + + bool get isStopping => _stopSignal.value; - bool get isStopping => stopSignal.value; + void enableStopSignal() => _stopSignal.value = true; } diff --git a/lib/model/source/collection_lens.dart b/lib/model/source/collection_lens.dart index 23af9b736..747e3bd1e 100644 --- a/lib/model/source/collection_lens.dart +++ b/lib/model/source/collection_lens.dart @@ -37,6 +37,7 @@ class CollectionLens with ChangeNotifier { bool listenToSource, groupBursts, fixedSort; List? fixedSelection; + final Set _syntheticEntries = {}; List _filteredSortedEntries = []; Map> sections = Map.unmodifiable({}); @@ -99,6 +100,9 @@ class CollectionLens with ChangeNotifier { ..forEach((sub) => sub.cancel()) ..clear(); favourites.removeListener(_onFavouritesChanged); + filterChangeNotifier.dispose(); + sortSectionChangeNotifier.dispose(); + _disposeSyntheticEntries(); super.dispose(); } @@ -116,6 +120,11 @@ class CollectionLens with ChangeNotifier { fixedSelection: fixedSelection ?? this.fixedSelection, ); + void _disposeSyntheticEntries() { + _syntheticEntries.forEach((v) => v.dispose()); + _syntheticEntries.clear(); + } + bool get isEmpty => _filteredSortedEntries.isEmpty; int get entryCount => _filteredSortedEntries.length; @@ -180,6 +189,7 @@ class CollectionLens with ChangeNotifier { void _applyFilters() { final entries = fixedSelection ?? (filters.contains(TrashFilter.instance) ? source.trashedEntries : source.visibleEntries); + _disposeSyntheticEntries(); _filteredSortedEntries = List.of(filters.isEmpty ? entries : entries.where((entry) => filters.every((filter) => filter.test(entry)))); if (groupBursts) { @@ -194,6 +204,7 @@ class CollectionLens with ChangeNotifier { entries.sort(AvesEntrySort.compareByName); final mainEntry = entries.first; final burstEntry = mainEntry.copyWith(burstEntries: entries); + _syntheticEntries.add(burstEntry); entries.skip(1).toList().forEach((subEntry) { _filteredSortedEntries.remove(subEntry); diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart index 385131a9f..75c4f1091 100644 --- a/lib/model/source/collection_source.dart +++ b/lib/model/source/collection_source.dart @@ -61,6 +61,13 @@ mixin SourceBase { abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, PlaceMixin, StateMixin, LocationMixin, TagMixin, TrashMixin { CollectionSource() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$CollectionSource', + object: this, + ); + } settings.updateStream.where((event) => event.key == SettingKeys.localeKey).listen((_) => invalidateAlbumDisplayNames()); settings.updateStream.where((event) => event.key == SettingKeys.hiddenFiltersKey).listen((event) { final oldValue = event.oldValue; @@ -76,6 +83,14 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place }); } + @mustCallSuper + void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } + _rawEntries.forEach((v) => v.dispose()); + } + final EventBus _eventBus = EventBus(); @override @@ -447,7 +462,8 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place Future analyze(AnalysisController? analysisController, {Set? entries}) async { final todoEntries = entries ?? visibleEntries; - final _analysisController = analysisController ?? AnalysisController(); + final defaultAnalysisController = AnalysisController(); + final _analysisController = analysisController ?? defaultAnalysisController; final force = _analysisController.force; if (!_analysisController.isStopping) { var startAnalysisService = false; @@ -481,6 +497,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place updateDerivedFilters(todoEntries); } } + defaultAnalysisController.dispose(); state = SourceState.ready; } diff --git a/lib/ref/metadata/xmp.dart b/lib/ref/metadata/xmp.dart index 7548cdc13..fa2dbdb13 100644 --- a/lib/ref/metadata/xmp.dart +++ b/lib/ref/metadata/xmp.dart @@ -37,6 +37,7 @@ class XmpNamespaces { static const gettyImagesGift = 'http://xmp.gettyimages.com/gift/1.0/'; static const gimp210 = 'http://www.gimp.org/ns/2.10/'; static const gimpXmp = 'http://www.gimp.org/xmp/'; + static const hdrgm = 'http://ns.adobe.com/hdr-gain-map/1.0/'; static const illustrator = 'http://ns.adobe.com/illustrator/1.0/'; static const iptc4xmpCore = 'http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/'; static const iptc4xmpExt = 'http://iptc.org/std/Iptc4xmpExt/2008-02-29/'; diff --git a/lib/ref/upnp.dart b/lib/ref/upnp.dart new file mode 100644 index 000000000..c6ee0337f --- /dev/null +++ b/lib/ref/upnp.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +class Upnp { + static const String upnpDeviceTypeMediaRenderer = 'urn:schemas-upnp-org:device:MediaRenderer:1'; + static const String upnpServiceTypeConnectionManager = 'urn:schemas-upnp-org:service:ConnectionManager:1'; + + static String getProtocolInfoActionXml() { + return ''' + + + + 0 + + +'''; + } +} + +class UpnpProtocolInfo { + late final Set entries; + + UpnpProtocolInfo(String text) { + entries = text.split(',').where((v) => v.isNotEmpty).map(UpnpProtocolInfoEntry.new).toSet(); + } +} + +@immutable +class UpnpProtocolInfoEntry { + late final String protocol, network, contentFormat, additionalInfo; + + UpnpProtocolInfoEntry(String text) { + final parts = text.split(':'); + protocol = parts[0]; + network = parts[1]; + contentFormat = parts[2]; + additionalInfo = parts[3]; + } +} diff --git a/lib/services/analysis_service.dart b/lib/services/analysis_service.dart index eeb91fb8b..d0f1db0cb 100644 --- a/lib/services/analysis_service.dart +++ b/lib/services/analysis_service.dart @@ -10,6 +10,7 @@ import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/view/view.dart'; import 'package:aves_model/aves_model.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; @@ -92,15 +93,27 @@ class Analyzer { Analyzer() { debugPrint('$runtimeType create'); + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$Analyzer', + object: this, + ); + } _serviceStateNotifier.addListener(_onServiceStateChanged); _source.stateNotifier.addListener(_onSourceStateChanged); } void dispose() { debugPrint('$runtimeType dispose'); + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } + _stopUpdateTimer(); + _controller?.dispose(); _serviceStateNotifier.removeListener(_onServiceStateChanged); _source.stateNotifier.removeListener(_onSourceStateChanged); - _stopUpdateTimer(); + _source.dispose(); } Future start(dynamic args) async { @@ -114,13 +127,13 @@ class Analyzer { progressOffset = args['progressOffset']; } debugPrint('$runtimeType start for ${entryIds?.length ?? 'all'} entries, at $progressOffset/$progressTotal'); + _controller?.dispose(); _controller = AnalysisController( canStartService: false, entryIds: entryIds, force: force, progressTotal: progressTotal, progressOffset: progressOffset, - stopSignal: ValueNotifier(false), ); settings.systemLocalesFallback = await deviceService.getLocales(); @@ -149,7 +162,7 @@ class Analyzer { await _stopPlatformService(); _serviceStateNotifier.value = AnalyzerState.stopped; case AnalyzerState.stopped: - _controller?.stopSignal.value = true; + _controller?.enableStopSignal(); _stopUpdateTimer(); } } diff --git a/lib/services/storage_service.dart b/lib/services/storage_service.dart index 268076c27..835e0027a 100644 --- a/lib/services/storage_service.dart +++ b/lib/services/storage_service.dart @@ -27,6 +27,8 @@ abstract class StorageService { // returns number of deleted directories Future deleteEmptyRegularDirectories(Set dirPaths); + Future deleteTempDirectory(); + // returns whether user granted access to a directory of his choosing Future requestDirectoryAccess(String path); @@ -158,6 +160,17 @@ class PlatformStorageService implements StorageService { return 0; } + @override + Future deleteTempDirectory() async { + try { + final result = await _platform.invokeMethod('deleteTempDirectory'); + if (result != null) return result as bool; + } on PlatformException catch (e, stack) { + await reportService.recordError(e, stack); + } + return false; + } + @override Future canRequestMediaFileBulkAccess() async { try { diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index c57ed4b1b..d3ab7017d 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -10,6 +10,7 @@ import 'package:provider/provider.dart'; class AColors { static const starEnabled = Colors.amber; static const starDisabled = Colors.grey; + static const warning = Colors.amber; static const boraBoraGradient = [ Color(0xff2bc0e4), diff --git a/lib/theme/icons.dart b/lib/theme/icons.dart index 876e6420d..ca9191812 100644 --- a/lib/theme/icons.dart +++ b/lib/theme/icons.dart @@ -76,6 +76,7 @@ class AIcons { static const addShortcut = Icons.add_to_home_screen_outlined; static const cancel = Icons.cancel_outlined; static const captureFrame = Icons.screenshot_outlined; + static const cast = Icons.cast_outlined; static const clear = Icons.clear_outlined; static const clipboard = Icons.content_copy_outlined; static const convert = Icons.transform_outlined; diff --git a/lib/theme/themes.dart b/lib/theme/themes.dart index 30579055c..c4b860602 100644 --- a/lib/theme/themes.dart +++ b/lib/theme/themes.dart @@ -1,180 +1,263 @@ import 'dart:ui'; -import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/aves_app.dart'; -import 'package:aves_model/aves_model.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class Themes { - static const _tooltipTheme = TooltipThemeData( - verticalOffset: 32, - ); - - static const _appBarTitleTextStyle = TextStyle( + static const _titleTextStyle = TextStyle( fontSize: 20, fontWeight: FontWeight.normal, fontFeatures: [FontFeature.enable('smcp')], ); - static SnackBarThemeData _snackBarTheme(Color accentColor) => SnackBarThemeData( - actionTextColor: accentColor, + // adapted from M3 defaults + static MaterialStateProperty _popupMenuTextStyle(ColorScheme colors, TextTheme textTheme) { + return MaterialStateProperty.resolveWith((states) { + final TextStyle style = textTheme.labelLarge!; + if (states.contains(MaterialState.disabled)) { + return style.apply(color: colors.onSurface.withOpacity(0.38)); + } + return style.apply(color: colors.onSurface); + }); + } + + static TextStyle searchFieldStyle(BuildContext context) => Theme.of(context).textTheme.bodyLarge!; + + static Color overlayBackgroundColor({ + required Brightness brightness, + required bool blurred, + }) { + switch (brightness) { + case Brightness.dark: + return blurred ? Colors.black26 : Colors.black45; + case Brightness.light: + return blurred ? Colors.white54 : const Color(0xCCFFFFFF); + } + } + + static Color _schemeCardLayer(ColorScheme colors) => ElevationOverlay.applySurfaceTint(colors.surface, colors.surfaceTint, 1); + + static Color secondLayerColor(BuildContext context) => _schemeSecondLayer(Theme.of(context).colorScheme); + + // `DialogTheme` M3 defaults use `6.0` elevation + static Color _schemeSecondLayer(ColorScheme colors) => ElevationOverlay.applySurfaceTint(colors.surface, colors.surfaceTint, 6); + + static Color thirdLayerColor(BuildContext context) => _schemeThirdLayer(Theme.of(context).colorScheme); + + static Color _schemeThirdLayer(ColorScheme colors) => ElevationOverlay.applySurfaceTint(colors.surface, colors.surfaceTint, 12); + + static Color _unselectedWidgetColor(ColorScheme colors) => colors.onSurface.withOpacity(0.6); + + static Color backgroundTextColor(BuildContext context) { + final colors = Theme.of(context).colorScheme; + return Color.alphaBlend(colors.surfaceTint, colors.onSurface).withOpacity(.5); + } + + static final _typography = Typography.material2021(platform: TargetPlatform.android); + + static ThemeData _baseTheme(ColorScheme colors, bool deviceInitialized) { + return ThemeData( + // COLOR + brightness: colors.brightness, + canvasColor: _schemeSecondLayer(colors), + cardColor: _schemeCardLayer(colors), + colorScheme: colors, + dividerColor: colors.outlineVariant, + indicatorColor: colors.primary, + scaffoldBackgroundColor: colors.background, + // TYPOGRAPHY & ICONOGRAPHY + typography: _typography, + // COMPONENT THEMES + checkboxTheme: _checkboxTheme(colors), + radioTheme: _radioTheme(colors), + sliderTheme: _sliderTheme(colors), + tooltipTheme: _tooltipTheme, + ); + } + + static CheckboxThemeData _checkboxTheme(ColorScheme colors) => CheckboxThemeData( + side: BorderSide(width: 2.0, color: _unselectedWidgetColor(colors)), + ); + + static const _listTileTheme = ListTileThemeData( + contentPadding: EdgeInsets.symmetric(horizontal: 16), + ); + + // adapted from M3 defaults + static RadioThemeData _radioTheme(ColorScheme colors) => RadioThemeData( + fillColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.selected)) { + if (states.contains(MaterialState.disabled)) { + return colors.onSurface.withOpacity(0.38); + } + return colors.primary; + } + if (states.contains(MaterialState.disabled)) { + return colors.onSurface.withOpacity(0.38); + } + if (states.contains(MaterialState.pressed)) { + return colors.onSurface; + } + if (states.contains(MaterialState.hovered)) { + return colors.onSurface; + } + if (states.contains(MaterialState.focused)) { + return colors.onSurface; + } + return _unselectedWidgetColor(colors); + }), + ); + + static SliderThemeData _sliderTheme(ColorScheme colors) => SliderThemeData( + inactiveTrackColor: colors.primary.withOpacity(0.24), + ); + + static SnackBarThemeData _snackBarTheme(ColorScheme colors) => SnackBarThemeData( + actionTextColor: colors.primary, behavior: SnackBarBehavior.floating, ); - static final _typography = Typography.material2018(platform: TargetPlatform.android); + static const _tooltipTheme = TooltipThemeData( + verticalOffset: 32, + ); + + // light static final _lightThemeTypo = _typography.black; static final _lightTitleColor = _lightThemeTypo.titleMedium!.color!; static final _lightBodyColor = _lightThemeTypo.bodyMedium!.color!; static final _lightLabelColor = _lightThemeTypo.labelMedium!.color!; static const _lightActionIconColor = Color(0xAA000000); - static const _lightFirstLayer = Color(0xFFFAFAFA); // aka `Colors.grey[50]` - static const _lightSecondLayer = Color(0xFFF5F5F5); // aka `Colors.grey[100]` - static const _lightThirdLayer = Color(0xFFEEEEEE); // aka `Colors.grey[200]` - - static ThemeData lightTheme(Color accentColor, bool deviceInitialized) => ThemeData( - colorScheme: ColorScheme.light( - primary: accentColor, - secondary: accentColor, - onPrimary: _lightBodyColor, - onSecondary: _lightBodyColor, - ), - brightness: Brightness.light, - // `canvasColor` is used by `Drawer`, `DropdownButton` and `ExpansionTileCard` - canvasColor: _lightSecondLayer, - scaffoldBackgroundColor: _lightFirstLayer, - // `cardColor` is used by `ExpansionPanel` - cardColor: _lightSecondLayer, - dialogBackgroundColor: _lightSecondLayer, - indicatorColor: accentColor, - typography: _typography, - appBarTheme: AppBarTheme( - backgroundColor: _lightFirstLayer, - // `foregroundColor` is used by icons - foregroundColor: _lightActionIconColor, - // `titleTextStyle.color` is used by text - titleTextStyle: _appBarTitleTextStyle.copyWith(color: _lightTitleColor), - systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(Brightness.light, _lightFirstLayer) : null, - ), - listTileTheme: const ListTileThemeData( - iconColor: _lightActionIconColor, - ), - popupMenuTheme: const PopupMenuThemeData( - color: _lightSecondLayer, - ), - snackBarTheme: _snackBarTheme(accentColor), - tabBarTheme: TabBarTheme( - labelColor: _lightTitleColor, - unselectedLabelColor: Colors.black54, - ), - textButtonTheme: TextButtonThemeData( - style: TextButton.styleFrom( - foregroundColor: _lightLabelColor, - ), + static const _lightOnSurface = Colors.black; + + static ThemeData lightTheme(Color accentColor, bool deviceInitialized) { + final colors = ColorScheme.fromSeed( + seedColor: accentColor, + brightness: Brightness.light, + primary: accentColor, + onPrimary: _lightBodyColor, + secondary: accentColor, + onSecondary: _lightBodyColor, + onSurface: _lightOnSurface, + ); + final textTheme = _lightThemeTypo; + return _baseTheme(colors, deviceInitialized).copyWith( + // TYPOGRAPHY & ICONOGRAPHY + textTheme: textTheme, + // COMPONENT THEMES + appBarTheme: AppBarTheme( + // `foregroundColor` is used by icons + foregroundColor: _lightActionIconColor, + // `titleTextStyle.color` is used by text + titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor), + systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, colors.background) : null, + ), + dialogTheme: DialogTheme( + titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor), + ), + listTileTheme: _listTileTheme.copyWith( + iconColor: _lightActionIconColor, + ), + popupMenuTheme: PopupMenuThemeData( + labelTextStyle: _popupMenuTextStyle(colors, textTheme), + ), + snackBarTheme: _snackBarTheme(colors), + switchTheme: SwitchThemeData( + thumbColor: MaterialStateProperty.resolveWith((states) { + final active = states.contains(MaterialState.selected); + return active ? Colors.white : Colors.grey.shade600; + }), + trackColor: MaterialStateProperty.resolveWith((states) { + final active = states.contains(MaterialState.selected); + return colors.primary.withOpacity(active ? 1 : .1); + }), + trackOutlineColor: MaterialStateProperty.resolveWith((states) { + final active = states.contains(MaterialState.selected); + return active ? colors.primary : colors.onPrimary.withOpacity(.5); + }), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: _lightLabelColor, ), - tooltipTheme: _tooltipTheme, - ); + ), + ); + } + + // dark static final _darkThemeTypo = _typography.white; static final _darkTitleColor = _darkThemeTypo.titleMedium!.color!; static final _darkBodyColor = _darkThemeTypo.bodyMedium!.color!; static final _darkLabelColor = _darkThemeTypo.labelMedium!.color!; - static const _darkFirstLayer = Color(0xFF212121); // aka `Colors.grey[900]` - static const _darkSecondLayer = Color(0xFF363636); - static const _darkThirdLayer = Color(0xFF424242); // aka `Colors.grey[800]` - - static ThemeData darkTheme(Color accentColor, bool deviceInitialized) => ThemeData( - colorScheme: ColorScheme.dark( - primary: accentColor, - secondary: accentColor, - // surface color is used by the date/time pickers - surface: Colors.grey.shade800, - onPrimary: _darkBodyColor, - onSecondary: _darkBodyColor, - ), - brightness: Brightness.dark, - // `canvasColor` is used by `Drawer`, `DropdownButton` and `ExpansionTileCard` - canvasColor: _darkSecondLayer, - scaffoldBackgroundColor: _darkFirstLayer, - // `cardColor` is used by `ExpansionPanel` - cardColor: _darkSecondLayer, - dialogBackgroundColor: _darkSecondLayer, - indicatorColor: accentColor, - typography: _typography, - appBarTheme: AppBarTheme( - backgroundColor: _darkFirstLayer, - // `foregroundColor` is used by icons - foregroundColor: _darkTitleColor, - // `titleTextStyle.color` is used by text - titleTextStyle: _appBarTitleTextStyle.copyWith(color: _darkTitleColor), - systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(Brightness.dark, _darkFirstLayer) : null, - ), - popupMenuTheme: const PopupMenuThemeData( - color: _darkSecondLayer, - ), - snackBarTheme: _snackBarTheme(accentColor).copyWith( - backgroundColor: Colors.grey.shade800, - contentTextStyle: TextStyle( - color: _darkBodyColor, - ), - ), - tabBarTheme: TabBarTheme( - labelColor: _darkTitleColor, + static const _darkOnSurface = Colors.white; + + static ThemeData _baseDarkTheme(ColorScheme colors, bool deviceInitialized) { + final textTheme = _darkThemeTypo; + return _baseTheme(colors, deviceInitialized).copyWith( + // TYPOGRAPHY & ICONOGRAPHY + textTheme: textTheme, + // COMPONENT THEMES + appBarTheme: AppBarTheme( + // `foregroundColor` is used by icons + foregroundColor: _darkTitleColor, + // `titleTextStyle.color` is used by text + titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor), + systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, colors.background) : null, + ), + dialogTheme: DialogTheme( + titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor), + ), + listTileTheme: _listTileTheme, + popupMenuTheme: PopupMenuThemeData( + labelTextStyle: _popupMenuTextStyle(colors, textTheme), + ), + snackBarTheme: _snackBarTheme(colors).copyWith( + backgroundColor: _schemeSecondLayer(colors), + contentTextStyle: TextStyle( + color: _darkBodyColor, ), - textButtonTheme: TextButtonThemeData( - style: TextButton.styleFrom( - foregroundColor: _darkLabelColor, - ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + foregroundColor: _darkLabelColor, ), - tooltipTheme: _tooltipTheme, - ); + ), + ); + } + + static ThemeData darkTheme(Color accentColor, bool deviceInitialized) { + final colors = ColorScheme.fromSeed( + seedColor: accentColor, + brightness: Brightness.dark, + primary: accentColor, + onPrimary: _darkBodyColor, + secondary: accentColor, + onSecondary: _darkBodyColor, + onSurface: _darkOnSurface, + ); + return _baseDarkTheme(colors, deviceInitialized); + } - static const _blackFirstLayer = Colors.black; - static const _blackSecondLayer = Color(0xFF212121); // aka `Colors.grey[900]` - static const _blackThirdLayer = Color(0xFF303030); // aka `Colors.grey[850]` + // black static ThemeData blackTheme(Color accentColor, bool deviceInitialized) { - final baseTheme = darkTheme(accentColor, deviceInitialized); + final colors = ColorScheme.fromSeed( + seedColor: accentColor, + brightness: Brightness.dark, + primary: accentColor, + onPrimary: _darkBodyColor, + secondary: accentColor, + onSecondary: _darkBodyColor, + onSurface: _darkOnSurface, + ).copyWith( + background: Colors.black, + ); + final baseTheme = _baseDarkTheme(colors, deviceInitialized); return baseTheme.copyWith( - // `canvasColor` is used by `Drawer`, `DropdownButton` and `ExpansionTileCard` - canvasColor: _blackSecondLayer, - scaffoldBackgroundColor: _blackFirstLayer, - // `cardColor` is used by `ExpansionPanel` - cardColor: _blackSecondLayer, - dialogBackgroundColor: _blackSecondLayer, appBarTheme: baseTheme.appBarTheme.copyWith( - backgroundColor: _blackFirstLayer, - ), - popupMenuTheme: baseTheme.popupMenuTheme.copyWith( - color: _blackSecondLayer, + backgroundColor: colors.background, ), ); } - - static Color overlayBackgroundColor({ - required Brightness brightness, - required bool blurred, - }) { - switch (brightness) { - case Brightness.dark: - return blurred ? Colors.black26 : Colors.black45; - case Brightness.light: - return blurred ? Colors.white54 : const Color(0xCCFFFFFF); - } - } - - static Color thirdLayerColor(BuildContext context) { - final isBlack = context.select((v) => v.themeBrightness == AvesThemeBrightness.black); - if (isBlack) { - return _blackThirdLayer; - } else { - switch (Theme.of(context).brightness) { - case Brightness.dark: - return _darkThirdLayer; - case Brightness.light: - return _lightThirdLayer; - } - } - } } diff --git a/lib/utils/xmp_utils.dart b/lib/utils/xmp_utils.dart index 0953bac31..72b120b32 100644 --- a/lib/utils/xmp_utils.dart +++ b/lib/utils/xmp_utils.dart @@ -188,7 +188,7 @@ class XMP { static Future edit( String? xmpString, - Future Function() toolkit, + String toolkit, bool Function(List descriptions) apply, { DateTime? modifyDate, }) async { @@ -202,7 +202,7 @@ class XMP { builder.element(XmpElements.xXmpmeta, namespace: nsX, namespaces: { nsX: prefixOf(nsX), }, attributes: { - '${prefixOf(nsX)}$propNamespaceSeparator${XmpAttributes.xXmptk}': await toolkit(), + '${prefixOf(nsX)}$propNamespaceSeparator${XmpAttributes.xXmptk}': toolkit, }); xmpDoc = builder.buildDocument(); } diff --git a/lib/view/src/actions/entry.dart b/lib/view/src/actions/entry.dart index 356ce01cf..301489473 100644 --- a/lib/view/src/actions/entry.dart +++ b/lib/view/src/actions/entry.dart @@ -47,6 +47,7 @@ extension ExtraEntryActionView on EntryAction { EntryAction.open || EntryAction.openVideo => l10n.entryActionOpen, EntryAction.openMap => l10n.entryActionOpenMap, EntryAction.setAs => l10n.entryActionSetAs, + EntryAction.cast => l10n.entryActionCast, // platform EntryAction.rotateScreen => l10n.entryActionRotateScreen, // metadata @@ -120,6 +121,7 @@ extension ExtraEntryActionView on EntryAction { EntryAction.open || EntryAction.openVideo => AIcons.openOutside, EntryAction.openMap => AIcons.map, EntryAction.setAs => AIcons.setAs, + EntryAction.cast => AIcons.cast, // platform EntryAction.rotateScreen => AIcons.rotateScreen, // metadata diff --git a/lib/view/src/actions/slideshow.dart b/lib/view/src/actions/slideshow.dart index 69635df33..a047322cc 100644 --- a/lib/view/src/actions/slideshow.dart +++ b/lib/view/src/actions/slideshow.dart @@ -9,6 +9,7 @@ extension ExtraSlideshowActionView on SlideshowAction { return switch (this) { SlideshowAction.resume => l10n.slideshowActionResume, SlideshowAction.showInCollection => l10n.slideshowActionShowInCollection, + SlideshowAction.cast => l10n.entryActionCast, SlideshowAction.settings => l10n.viewerActionSettings, }; } @@ -19,6 +20,7 @@ extension ExtraSlideshowActionView on SlideshowAction { return switch (this) { SlideshowAction.resume => AIcons.play, SlideshowAction.showInCollection => AIcons.allCollection, + SlideshowAction.cast => AIcons.cast, SlideshowAction.settings => AIcons.settings, }; } diff --git a/lib/view/src/xmp.dart b/lib/view/src/xmp.dart index e2f185726..438a5d4fe 100644 --- a/lib/view/src/xmp.dart +++ b/lib/view/src/xmp.dart @@ -34,6 +34,7 @@ class XmpNamespaceView { XmpNamespaces.gettyImagesGift: 'Getty Images', XmpNamespaces.gimp210: 'GIMP 2.10', XmpNamespaces.gimpXmp: 'GIMP', + XmpNamespaces.hdrgm: 'HDR Gain Map', XmpNamespaces.illustrator: 'Illustrator', XmpNamespaces.iptc4xmpCore: 'IPTC Core', XmpNamespaces.iptc4xmpExt: 'IPTC Extension', diff --git a/lib/widget_common.dart b/lib/widget_common.dart index 4197f0311..caca41659 100644 --- a/lib/widget_common.dart +++ b/lib/widget_common.dart @@ -90,5 +90,6 @@ Future _getWidgetEntry(int widgetId, bool reuseEntry) async { if (entry != null) { settings.setWidgetUri(widgetId, entry.uri); } + source.dispose(); return entry; } diff --git a/lib/widgets/about/about_tv_page.dart b/lib/widgets/about/about_tv_page.dart index 9f52238ac..04ebd24c8 100644 --- a/lib/widgets/about/about_tv_page.dart +++ b/lib/widgets/about/about_tv_page.dart @@ -1,3 +1,4 @@ +import 'package:aves/model/device.dart'; import 'package:aves/widgets/about/app_ref.dart'; import 'package:aves/widgets/about/credits.dart'; import 'package:aves/widgets/about/translators.dart'; @@ -10,7 +11,6 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart'; import 'package:aves/widgets/navigation/tv_rail.dart'; import 'package:flutter/material.dart'; -import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; class AboutTvPage extends StatelessWidget { @@ -61,7 +61,7 @@ class AboutTvPage extends StatelessWidget { } class _Content extends StatefulWidget { - const _Content({Key? key}) : super(key: key); + const _Content(); @override State<_Content> createState() => _ContentState(); @@ -72,16 +72,9 @@ enum _Section { links, credits, translators, licenses } class _ContentState extends State<_Content> { final FocusNode _railFocusNode = FocusNode(); final ValueNotifier _railIndexNotifier = ValueNotifier(0); - late Future _packageInfoLoader; static const double railWidth = 256; - @override - void initState() { - super.initState(); - _packageInfoLoader = PackageInfo.fromPlatform(); - } - @override void dispose() { _railIndexNotifier.dispose(); @@ -119,7 +112,6 @@ class _ContentState extends State<_Content> { shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(123)), ), - // tileColor: theme.scaffoldBackgroundColor, ); }, itemCount: _Section.values.length, @@ -149,12 +141,7 @@ class _ContentState extends State<_Content> { Widget _getTitle(_Section key) { switch (key) { case _Section.links: - return FutureBuilder( - future: _packageInfoLoader, - builder: (context, snapshot) { - return Text('${context.l10n.appName} ${snapshot.data?.version}'); - }, - ); + return Text('${context.l10n.appName} ${device.packageVersion}'); case _Section.credits: return Text(context.l10n.aboutCreditsSectionTitle); case _Section.translators: @@ -205,7 +192,7 @@ class _ContentState extends State<_Content> { return Theme( data: theme.copyWith( listTileTheme: listTileTheme.copyWith( - tileColor: theme.scaffoldBackgroundColor, + tileColor: theme.colorScheme.background, ), ), child: const TvLicensePage(), diff --git a/lib/widgets/about/app_ref.dart b/lib/widgets/about/app_ref.dart index 99d814f00..b5db10133 100644 --- a/lib/widgets/about/app_ref.dart +++ b/lib/widgets/about/app_ref.dart @@ -1,20 +1,59 @@ import 'dart:ui'; +import 'package:aves/model/device.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/about/policy_page.dart'; import 'package:aves/widgets/common/basic/link_chip.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_logo.dart'; import 'package:flutter/material.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -class AppReference extends StatefulWidget { +class AppReference extends StatelessWidget { static const avesGithub = 'https://github.com/deckerst/aves'; + static const _appTitleStyle = TextStyle( + fontSize: 20, + fontWeight: FontWeight.normal, + letterSpacing: 1.0, + fontFeatures: [FontFeature.enable('smcp')], + ); + const AppReference({super.key}); @override - State createState() => _AppReferenceState(); + Widget build(BuildContext context) { + return Center( + child: Column( + children: [ + _buildAvesLine(context), + const SizedBox(height: 16), + Wrap( + alignment: WrapAlignment.center, + spacing: 16, + crossAxisAlignment: WrapCrossAlignment.center, + children: AppReference.buildLinks(context), + ), + ], + ), + ); + } + + Widget _buildAvesLine(BuildContext context) { + final textScaler = MediaQuery.textScalerOf(context); + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + AvesLogo( + size: textScaler.scale(_appTitleStyle.fontSize!) * 1.3, + ), + const SizedBox(width: 8), + Text( + '${context.l10n.appName} ${device.packageVersion}', + style: _appTitleStyle, + ), + ], + ); + } static List buildLinks(BuildContext context) { final l10n = context.l10n; @@ -55,60 +94,3 @@ class AppReference extends StatefulWidget { ); } } - -class _AppReferenceState extends State { - late Future _packageInfoLoader; - - static const _appTitleStyle = TextStyle( - fontSize: 20, - fontWeight: FontWeight.normal, - letterSpacing: 1.0, - fontFeatures: [FontFeature.enable('smcp')], - ); - - @override - void initState() { - super.initState(); - _packageInfoLoader = PackageInfo.fromPlatform(); - } - - @override - Widget build(BuildContext context) { - return Center( - child: Column( - children: [ - _buildAvesLine(), - const SizedBox(height: 16), - Wrap( - alignment: WrapAlignment.center, - spacing: 16, - crossAxisAlignment: WrapCrossAlignment.center, - children: AppReference.buildLinks(context), - ), - ], - ), - ); - } - - Widget _buildAvesLine() { - return FutureBuilder( - future: _packageInfoLoader, - builder: (context, snapshot) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - AvesLogo( - size: _appTitleStyle.fontSize! * textScaleFactor * 1.3, - ), - const SizedBox(width: 8), - Text( - '${context.l10n.appName} ${snapshot.data?.version}', - style: _appTitleStyle, - ), - ], - ); - }, - ); - } -} diff --git a/lib/widgets/about/bug_report.dart b/lib/widgets/about/bug_report.dart index 6c5ad4a43..3c8276b15 100644 --- a/lib/widgets/about/bug_report.dart +++ b/lib/widgets/about/bug_report.dart @@ -10,10 +10,12 @@ import 'package:aves/services/common/services.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/styles.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/about/app_ref.dart'; import 'package:aves/widgets/aves_app.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/common/fx/borders.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart'; import 'package:aves_model/aves_model.dart'; @@ -77,24 +79,27 @@ class _BugReportState extends State with FeedbackMixin { final info = snapshot.data; if (info == null) return const SizedBox(); - final theme = Theme.of(context); return Container( decoration: BoxDecoration( - color: theme.cardColor, + color: Themes.secondLayerColor(context), border: Border.all( - color: theme.colorScheme.onPrimary, + color: Theme.of(context).dividerColor, + width: AvesBorder.curvedBorderWidth(context), ), - borderRadius: const BorderRadius.all(Radius.circular(8)), + borderRadius: const BorderRadius.all(Radius.circular(16)), ), constraints: const BoxConstraints(maxHeight: 100), margin: const EdgeInsets.symmetric(vertical: 8), clipBehavior: Clip.antiAlias, child: SingleChildScrollView( - padding: const EdgeInsetsDirectional.only(start: 8, top: 4, end: 16, bottom: 4), + padding: const EdgeInsets.all(8), // to show a scroll bar, we would need to provide a scroll controller // to both the `Scrollable` and the `Scrollbar`, but // as of Flutter v3.0.0, `SelectableText` does not allow passing the `scrollController` - child: SelectableText(info), + child: SelectableText( + info, + style: Theme.of(context).textTheme.bodySmall, + ), ), ); }, @@ -122,7 +127,7 @@ class _BugReportState extends State with FeedbackMixin { padding: const EdgeInsets.all(12), decoration: BoxDecoration( border: Border.fromBorderSide(BorderSide( - color: isMonochrome ? context.select((v) => v.neutral) : Theme.of(context).colorScheme.secondary, + color: isMonochrome ? context.select((v) => v.neutral) : Theme.of(context).colorScheme.primary, width: AvesFilterChip.outlineWidth, )), shape: BoxShape.circle, @@ -148,9 +153,9 @@ class _BugReportState extends State with FeedbackMixin { final storageVolumes = await storageService.getStorageVolumes(); final storageGrants = await storageService.getGrantedDirectories(); return [ - 'Package: ${packageInfo.packageName}', + 'Package: ${device.packageName}', 'Installer: ${packageInfo.installerStore}', - 'Aves version: ${packageInfo.version}-$flavor, build ${packageInfo.buildNumber}', + 'Aves version: ${device.packageVersion}-$flavor, build ${packageInfo.buildNumber}', 'Flutter: ${version['channel']} ${version['frameworkVersion']}', 'Android version: ${androidInfo.version.release}, API ${androidInfo.version.sdkInt}', 'Android build: ${androidInfo.display}', diff --git a/lib/widgets/about/data_usage.dart b/lib/widgets/about/data_usage.dart index a522355cc..b811a28f5 100644 --- a/lib/widgets/about/data_usage.dart +++ b/lib/widgets/about/data_usage.dart @@ -7,6 +7,7 @@ import 'package:aves/utils/file_utils.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_donut.dart'; +import 'package:aves/widgets/common/identity/buttons/outlined_button.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -24,7 +25,7 @@ class _AboutDataUsageState extends State with FeedbackMixin { @override void initState() { super.initState(); - _loader = storageService.getDataUsage(); + _reload(); } @override @@ -81,6 +82,18 @@ class _AboutDataUsageState extends State with FeedbackMixin { byTypes: cacheMap, animationDuration: animationDuration, ), + Center( + child: AvesOutlinedButton( + label: context.l10n.aboutDataUsageClearCache, + onPressed: () async { + await storageService.deleteTempDirectory(); + await mediaFetchService.clearSizedThumbnailDiskCache(); + imageCache.clear(); + _reload(); + setState(() {}); + }, + ), + ), ], ); }, @@ -92,6 +105,10 @@ class _AboutDataUsageState extends State with FeedbackMixin { ], ); } + + void _reload() { + _loader = storageService.getDataUsage(); + } } class DataUsageDonut extends StatelessWidget { diff --git a/lib/widgets/about/licenses.dart b/lib/widgets/about/licenses.dart index 83982ae7c..f5a603673 100644 --- a/lib/widgets/about/licenses.dart +++ b/lib/widgets/about/licenses.dart @@ -32,6 +32,12 @@ class _LicensesState extends State { _sortPackages(); } + @override + void dispose() { + _expandedNotifier.dispose(); + super.dispose(); + } + void _sortPackages() { int compare(Dependency a, Dependency b) => compareAsciiUpperCase(a.name, b.name); _platform.sort(compare); @@ -82,7 +88,7 @@ class _LicensesState extends State { builder: (context) => Theme( data: Theme.of(context).copyWith( // as of Flutter v3.7.8, `cardColor` is used as a background color by `LicensePage` - cardColor: Theme.of(context).scaffoldBackgroundColor, + cardColor: Theme.of(context).colorScheme.background, ), child: const LicensePage(), ), @@ -121,29 +127,12 @@ class _LicenseRow extends StatelessWidget { @override Widget build(BuildContext context) { - final textTheme = Theme.of(context).textTheme; - final bodyTextStyle = textTheme.bodyMedium!; - final subColor = bodyTextStyle.color!.withOpacity(.6); - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LinkChip( - text: package.name, - urlString: package.sourceUrl, - textStyle: const TextStyle(fontWeight: FontWeight.bold), - ), - Padding( - padding: const EdgeInsetsDirectional.only(start: 16), - child: LinkChip( - text: package.license, - urlString: package.licenseUrl, - color: subColor, - ), - ), - ], + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), + child: LinkChip( + text: package.name, + urlString: package.sourceUrl, + textStyle: const TextStyle(fontWeight: FontWeight.bold), ), ); } diff --git a/lib/widgets/about/tv_license_page.dart b/lib/widgets/about/tv_license_page.dart index ef229b3e9..76eb7a847 100644 --- a/lib/widgets/about/tv_license_page.dart +++ b/lib/widgets/about/tv_license_page.dart @@ -77,7 +77,7 @@ class _TvLicensePageState extends State { final isSelected = index == selectedIndex; final theme = Theme.of(context); return Ink( - color: isSelected ? theme.highlightColor : theme.cardColor, + color: isSelected ? theme.highlightColor : theme.colorScheme.background, child: ListTile( title: Text(packageName), subtitle: Text(MaterialLocalizations.of(context).licensesPackageDetailText(bindings.length)), @@ -302,7 +302,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { ), body: Center( child: Material( - color: theme.cardColor, + color: theme.colorScheme.background, elevation: 4.0, child: Container( constraints: BoxConstraints.loose(const Size.fromWidth(600.0)), @@ -332,7 +332,7 @@ class _PackageLicensePageState extends State<_PackageLicensePage> { SliverAppBar( automaticallyImplyLeading: false, pinned: true, - backgroundColor: theme.cardColor, + backgroundColor: theme.colorScheme.background, title: _PackageLicensePageTitle( title: title, subtitle: subtitle, diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index 526d9a017..90b3e8876 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -96,18 +96,18 @@ class AvesApp extends StatefulWidget { State createState() => _AvesAppState(); static void setSystemUIStyle(ThemeData theme) { - final style = systemUIStyleForBrightness(theme.brightness, theme.scaffoldBackgroundColor); + final style = systemUIStyleForBrightness(theme.brightness, theme.colorScheme.background); SystemChrome.setSystemUIOverlayStyle(style); } - static SystemUiOverlayStyle systemUIStyleForBrightness(Brightness themeBrightness, Color scaffoldBackgroundColor) { + static SystemUiOverlayStyle systemUIStyleForBrightness(Brightness themeBrightness, Color backgroundColor) { final barBrightness = themeBrightness == Brightness.light ? Brightness.dark : Brightness.light; const statusBarColor = Colors.transparent; // as of Flutter v3.3.0-0.2.pre, setting `SystemUiOverlayStyle` (whether manually or automatically because of `AppBar`) // prevents the canvas from drawing behind the nav bar on Android <10 (API <29), // so the nav bar is opaque, even when requesting `SystemUiMode.edgeToEdge` from Flutter // or setting `android:windowTranslucentNavigation` in Android themes. - final navBarColor = device.supportEdgeToEdgeUIMode ? Colors.transparent : scaffoldBackgroundColor; + final navBarColor = device.supportEdgeToEdgeUIMode ? Colors.transparent : backgroundColor; return SystemUiOverlayStyle( systemNavigationBarColor: navBarColor, systemNavigationBarDividerColor: navBarColor, @@ -196,13 +196,14 @@ class _AvesAppState extends State with WidgetsBindingObserver { @override void dispose() { - _pageTransitionsBuilderNotifier.dispose(); - _tvMediaQueryModifierNotifier.dispose(); - _appModeNotifier.dispose(); _subscriptions ..forEach((sub) => sub.cancel()) ..clear(); WidgetsBinding.instance.removeObserver(this); + _pageTransitionsBuilderNotifier.dispose(); + _tvMediaQueryModifierNotifier.dispose(); + _appModeNotifier.dispose(); + _mediaStoreSource.dispose(); super.dispose(); } @@ -457,6 +458,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { _monitorSettings(); videoControllerFactory.init(); + unawaited(storageService.deleteTempDirectory()); unawaited(_setupErrorReporting()); debugPrint('App setup in ${stopwatch.elapsed.inMilliseconds}ms'); @@ -491,7 +493,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { ); return mq.copyWith( - textScaleFactor: 1.1, + textScaler: const TextScaler.linear(1.1), padding: newPadding, viewPadding: newViewPadding, navigationMode: NavigationMode.directional, diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index 542cdede8..ede4129d6 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -12,6 +12,7 @@ import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/view/view.dart'; import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/collection/entry_set_action_delegate.dart'; @@ -123,7 +124,6 @@ class _CollectionAppBarState extends State with SingleTickerPr _unregisterWidget(widget); _queryBarFocusNode.dispose(); _queryFocusRequestNotifier.removeListener(_onQueryFocusRequest); - _isSelectingNotifier.removeListener(_onActivityChanged); _isSelectingNotifier.dispose(); _browseToSelectAnimation.dispose(); _subscriptions @@ -223,8 +223,8 @@ class _CollectionAppBarState extends State with SingleTickerPr } double get appBarContentHeight { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - double height = kToolbarHeight * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + double height = textScaler.scale(kToolbarHeight); if (settings.useTvLayout) { height += CaptionedButton.getTelevisionButtonHeight(context); } @@ -232,7 +232,7 @@ class _CollectionAppBarState extends State with SingleTickerPr height += FilterBar.preferredHeight; } if (context.read().enabled) { - height += EntryQueryBar.getPreferredHeight(textScaleFactor); + height += EntryQueryBar.getPreferredHeight(textScaler); } return height; } @@ -540,6 +540,7 @@ class _CollectionAppBarState extends State with SingleTickerPr Widget buildItem(EntrySetAction action) => Expanded( child: Material( + color: Colors.transparent, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), ), @@ -683,6 +684,7 @@ class _CollectionAppBarState extends State with SingleTickerPr SearchPageRoute( delegate: CollectionSearchDelegate( searchFieldLabel: context.l10n.searchCollectionFieldHint, + searchFieldStyle: Themes.searchFieldStyle(context), source: collection.source, parentCollection: collection, ), diff --git a/lib/widgets/collection/collection_grid.dart b/lib/widgets/collection/collection_grid.dart index 974eba8bc..057052675 100644 --- a/lib/widgets/collection/collection_grid.dart +++ b/lib/widgets/collection/collection_grid.dart @@ -302,7 +302,7 @@ class _CollectionSectionedContentState extends State<_CollectionSectionedContent @override void dispose() { - _appBarHeightNotifier.removeListener(_onAppBarHeightChanged); + _appBarHeightNotifier.dispose(); super.dispose(); } @@ -367,7 +367,7 @@ class _CollectionScaler extends StatelessWidget { Widget build(BuildContext context) { final (tileSpacing, horizontalPadding) = context.select((v) => (v.spacing, v.horizontalPadding)); final brightness = Theme.of(context).brightness; - final borderColor = DecoratedThumbnail.borderColor; + final borderColor = DecoratedThumbnail.borderColor(context); final borderWidth = DecoratedThumbnail.borderWidth(context); return GridScaleGestureDetector( scrollableKey: scrollableKey, diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index 564724969..6a0b9c49a 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -21,6 +21,7 @@ import 'package:aves/services/app_service.dart'; import 'package:aves/services/common/image_op_events.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/theme/durations.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/utils/collection_utils.dart'; import 'package:aves/utils/mime_utils.dart'; import 'package:aves/widgets/common/action_mixins/entry_editor.dart'; @@ -257,7 +258,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware final controller = AnalysisController(canStartService: true, force: true); final collection = context.read(); - collection.source.analyze(controller, entries: entries); + collection.source.analyze(controller, entries: entries).then((_) => controller.dispose()); _browse(context); } @@ -644,7 +645,6 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware builder: (context) => MapPage(collection: mapCollection), ), ); - mapCollection.dispose(); } void _goToSlideshow(BuildContext context) { @@ -690,6 +690,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware SearchPageRoute( delegate: CollectionSearchDelegate( searchFieldLabel: context.l10n.searchCollectionFieldHint, + searchFieldStyle: Themes.searchFieldStyle(context), source: collection.source, parentCollection: collection, ), diff --git a/lib/widgets/collection/grid/headers/any.dart b/lib/widgets/collection/grid/headers/any.dart index 556380fcc..f2f423829 100644 --- a/lib/widgets/collection/grid/headers/any.dart +++ b/lib/widgets/collection/grid/headers/any.dart @@ -89,8 +89,8 @@ class CollectionSectionHeader extends StatelessWidget { headerExtent = AlbumSectionHeader.getPreferredHeight(context, maxWidth, source, sectionKey); } - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - headerExtent = max(headerExtent, SectionHeader.leadingSize.height * textScaleFactor) + SectionHeader.padding.vertical + SectionHeader.margin.vertical; + final textScaler = MediaQuery.textScalerOf(context); + headerExtent = max(headerExtent, textScaler.scale(SectionHeader.leadingSize.height)) + SectionHeader.padding.vertical + SectionHeader.margin.vertical; return headerExtent; } } diff --git a/lib/widgets/collection/grid/list_details_theme.dart b/lib/widgets/collection/grid/list_details_theme.dart index e9f666570..38fe2b901 100644 --- a/lib/widgets/collection/grid/list_details_theme.dart +++ b/lib/widgets/collection/grid/list_details_theme.dart @@ -26,26 +26,27 @@ class EntryListDetailsTheme extends StatelessWidget { final locale = context.l10n.localeName; final use24hour = mq.alwaysUse24HourFormat; - final textScaleFactor = mq.textScaleFactor; + final textScaler = mq.textScaler; final textTheme = Theme.of(context).textTheme; final titleStyle = textTheme.bodyMedium!; final captionStyle = textTheme.bodySmall!; - final titleLineHeight = (RenderParagraph( + final titleLineHeightParagraph = RenderParagraph( TextSpan(text: 'Fake Title', style: titleStyle), textDirection: TextDirection.ltr, - textScaleFactor: textScaleFactor, - )..layout(const BoxConstraints(), parentUsesSize: true)) - .getMaxIntrinsicHeight(double.infinity); + textScaler: textScaler, + )..layout(const BoxConstraints(), parentUsesSize: true); + final titleLineHeight = titleLineHeightParagraph.getMaxIntrinsicHeight(double.infinity); + titleLineHeightParagraph.dispose(); - final captionLineHeight = (RenderParagraph( + final captionLineHeightParagraph = RenderParagraph( TextSpan(text: formatDateTime(DateTime.now(), locale, use24hour), style: captionStyle), textDirection: TextDirection.ltr, - textScaleFactor: textScaleFactor, + textScaler: textScaler, strutStyle: AStyles.overflowStrut, - )..layout(const BoxConstraints(), parentUsesSize: true)) - .getMaxIntrinsicHeight(double.infinity); + )..layout(const BoxConstraints(), parentUsesSize: true); + final captionLineHeight = captionLineHeightParagraph.getMaxIntrinsicHeight(double.infinity); var titleMaxLines = 1; var showDate = false; @@ -71,7 +72,7 @@ class EntryListDetailsTheme extends StatelessWidget { captionStyle: captionStyle, iconTheme: IconThemeData( color: captionStyle.color, - size: captionStyle.fontSize! * textScaleFactor, + size: textScaler.scale(captionStyle.fontSize!), ), ); }, diff --git a/lib/widgets/collection/query_bar.dart b/lib/widgets/collection/query_bar.dart index 70a7a43e1..cc508042b 100644 --- a/lib/widgets/collection/query_bar.dart +++ b/lib/widgets/collection/query_bar.dart @@ -19,7 +19,7 @@ class EntryQueryBar extends StatefulWidget { @override State createState() => _EntryQueryBarState(); - static double getPreferredHeight(double textScaleFactor) => QueryBar.getPreferredHeight(textScaleFactor); + static double getPreferredHeight(TextScaler textScaler) => QueryBar.getPreferredHeight(textScaler); } class _EntryQueryBarState extends State { @@ -52,9 +52,9 @@ class _EntryQueryBarState extends State { @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); return Container( - height: EntryQueryBar.getPreferredHeight(textScaleFactor), + height: EntryQueryBar.getPreferredHeight(textScaler), alignment: Alignment.topCenter, child: Selector, bool>( selector: (context, selection) => !selection.isSelecting, diff --git a/lib/widgets/common/action_controls/quick_choosers/common/button.dart b/lib/widgets/common/action_controls/quick_choosers/common/button.dart index eddc578a2..b4d409d1f 100644 --- a/lib/widgets/common/action_controls/quick_choosers/common/button.dart +++ b/lib/widgets/common/action_controls/quick_choosers/common/button.dart @@ -49,6 +49,7 @@ abstract class ChooserQuickButtonState, U> exten void dispose() { _animationController?.dispose(); _clearChooserOverlayEntry(); + _chooserValueNotifier.dispose(); super.dispose(); } @@ -69,28 +70,39 @@ abstract class ChooserQuickButtonState, U> exten ); } - return GestureDetector( - behavior: HitTestBehavior.opaque, - onLongPressStart: _hasChooser ? _showChooser : null, - onLongPressMoveUpdate: _hasChooser ? _moveUpdateStreamController.add : null, - onLongPressEnd: _hasChooser - ? (details) { - _clearChooserOverlayEntry(); - final selectedValue = _chooserValueNotifier.value; - if (selectedValue != null) { - widget.onChooserValue?.call(selectedValue); + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + return Theme( + data: theme.copyWith( + colorScheme: colorScheme.copyWith( + onSurfaceVariant: colorScheme.onSurface, + ), + ), + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onLongPressStart: _hasChooser ? _showChooser : null, + onLongPressMoveUpdate: _hasChooser ? _moveUpdateStreamController.add : null, + onLongPressEnd: _hasChooser + ? (details) { + _clearChooserOverlayEntry(); + final selectedValue = _chooserValueNotifier.value; + if (selectedValue != null) { + widget.onChooserValue?.call(selectedValue); + } } - } - : null, - onLongPressCancel: _clearChooserOverlayEntry, - child: child, + : null, + onLongPressCancel: _clearChooserOverlayEntry, + child: child, + ), ); } void _clearChooserOverlayEntry() { - if (_chooserOverlayEntry != null) { - _chooserOverlayEntry!.remove(); - _chooserOverlayEntry = null; + final overlayEntry = _chooserOverlayEntry; + _chooserOverlayEntry = null; + if (overlayEntry != null) { + overlayEntry.remove(); + overlayEntry.dispose(); } } diff --git a/lib/widgets/common/action_controls/togglers/favourite.dart b/lib/widgets/common/action_controls/togglers/favourite.dart index 4589b528e..31b0bbd79 100644 --- a/lib/widgets/common/action_controls/togglers/favourite.dart +++ b/lib/widgets/common/action_controls/togglers/favourite.dart @@ -29,7 +29,7 @@ class FavouriteToggler extends StatefulWidget { } class _FavouriteTogglerState extends State { - final ValueNotifier isFavouriteNotifier = ValueNotifier(false); + final ValueNotifier _isFavouriteNotifier = ValueNotifier(false); Set get entries => widget.entries; @@ -53,14 +53,14 @@ class _FavouriteTogglerState extends State { @override void dispose() { favourites.removeListener(_onChanged); - isFavouriteNotifier.dispose(); + _isFavouriteNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ValueListenableBuilder( - valueListenable: isFavouriteNotifier, + valueListenable: _isFavouriteNotifier, builder: (context, isFavourite, child) { if (widget.isMenuItem) { return isFavourite @@ -88,7 +88,7 @@ class _FavouriteTogglerState extends State { favouriteSweeperIcon, color: context.select((v) => v.favourite), ), - toggledNotifier: isFavouriteNotifier, + toggledNotifier: _isFavouriteNotifier, ), ], ); @@ -97,7 +97,7 @@ class _FavouriteTogglerState extends State { } void _onChanged() { - isFavouriteNotifier.value = entries.isNotEmpty && entries.every((entry) => entry.isFavourite); + _isFavouriteNotifier.value = entries.isNotEmpty && entries.every((entry) => entry.isFavourite); } } @@ -116,7 +116,7 @@ class FavouriteTogglerCaption extends StatefulWidget { } class _FavouriteTogglerCaptionState extends State { - final ValueNotifier isFavouriteNotifier = ValueNotifier(false); + final ValueNotifier _isFavouriteNotifier = ValueNotifier(false); Set get entries => widget.entries; @@ -136,14 +136,14 @@ class _FavouriteTogglerCaptionState extends State { @override void dispose() { favourites.removeListener(_onChanged); - isFavouriteNotifier.dispose(); + _isFavouriteNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ValueListenableBuilder( - valueListenable: isFavouriteNotifier, + valueListenable: _isFavouriteNotifier, builder: (context, isFavourite, child) { return CaptionedButtonText( text: isFavourite ? context.l10n.entryActionRemoveFavourite : context.l10n.entryActionAddFavourite, @@ -154,6 +154,6 @@ class _FavouriteTogglerCaptionState extends State { } void _onChanged() { - isFavouriteNotifier.value = entries.isNotEmpty && entries.every((entry) => entry.isFavourite); + _isFavouriteNotifier.value = entries.isNotEmpty && entries.every((entry) => entry.isFavourite); } } diff --git a/lib/widgets/common/action_controls/togglers/mute.dart b/lib/widgets/common/action_controls/togglers/mute.dart index fa9a2a693..caff7729e 100644 --- a/lib/widgets/common/action_controls/togglers/mute.dart +++ b/lib/widgets/common/action_controls/togglers/mute.dart @@ -4,6 +4,7 @@ import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/buttons/captioned_button.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:aves_video/aves_video.dart'; import 'package:flutter/material.dart'; @@ -25,9 +26,10 @@ class MuteToggler extends StatelessWidget { @override Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: controller?.canMuteNotifier ?? ValueNotifier(false), - builder: (context, canDo, child) { + return NullableValueListenableBuilder( + valueListenable: controller?.canMuteNotifier, + builder: (context, value, child) { + final canDo = value ?? false; return StreamBuilder( stream: controller?.volumeStream ?? Stream.value(1.0), builder: (context, snapshot) { @@ -66,9 +68,10 @@ class MuteTogglerCaption extends StatelessWidget { @override Widget build(BuildContext context) { - return ValueListenableBuilder( - valueListenable: controller?.canMuteNotifier ?? ValueNotifier(false), - builder: (context, canDo, child) { + return NullableValueListenableBuilder( + valueListenable: controller?.canMuteNotifier, + builder: (context, value, child) { + final canDo = value ?? false; return StreamBuilder( stream: controller?.volumeStream ?? Stream.value(1.0), builder: (context, snapshot) { diff --git a/lib/widgets/common/action_mixins/entry_storage.dart b/lib/widgets/common/action_mixins/entry_storage.dart index 3cb84cb70..be9f0e111 100644 --- a/lib/widgets/common/action_mixins/entry_storage.dart +++ b/lib/widgets/common/action_mixins/entry_storage.dart @@ -391,12 +391,15 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { return dateMillis == null || dateMillis == 0; }).toSet(); if (undatedItems.isNotEmpty) { - if (!await showSkippableConfirmationDialog( + final confirmationDialogDelegate = MoveUndatedConfirmationDialogDelegate(); + final confirmed = await showSkippableConfirmationDialog( context: context, type: ConfirmationDialog.moveUndatedItems, - delegate: MoveUndatedConfirmationDialogDelegate(), + delegate: confirmationDialogDelegate, confirmationButtonLabel: context.l10n.continueButtonLabel, - )) return false; + ); + confirmationDialogDelegate.dispose(); + if (!confirmed) return false; if (settings.setMetadataDateBeforeFileOp) { final entriesToDate = undatedItems.where((entry) => entry.canEditDate).toSet(); @@ -461,6 +464,10 @@ class MoveUndatedConfirmationDialogDelegate extends ConfirmationDialogDelegate { _setMetadataDate.value = settings.setMetadataDateBeforeFileOp; } + void dispose() { + _setMetadataDate.dispose(); + } + @override List build(BuildContext context) => [ Padding( diff --git a/lib/widgets/common/action_mixins/feedback.dart b/lib/widgets/common/action_mixins/feedback.dart index 30fd47900..871cca7e6 100644 --- a/lib/widgets/common/action_mixins/feedback.dart +++ b/lib/widgets/common/action_mixins/feedback.dart @@ -4,6 +4,7 @@ import 'dart:math'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/enums/accessibility_timeout.dart'; import 'package:aves/model/settings/settings.dart'; +import 'package:aves/theme/colors.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/action_mixins/overlay_snack_bar.dart'; @@ -11,7 +12,6 @@ import 'package:aves/widgets/common/basic/circle.dart'; import 'package:aves/widgets/common/basic/text/change_highlight.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/viewer/entry_viewer_page.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:overlay_support/overlay_support.dart'; @@ -47,7 +47,7 @@ mixin FeedbackMixin { final snackBarContent = _FeedbackMessage( type: type, message: message, - progressColor: theme.colorScheme.secondary, + progressColor: theme.colorScheme.primary, start: start, stop: action != null ? start.add(duration) : null, ); @@ -80,6 +80,7 @@ mixin FeedbackMixin { child: Text(action.label), ) : null, + animation: kAlwaysCompleteAnimation, dismissDirection: DismissDirection.horizontal, onDismiss: () => notificationOverlayEntry?.dismiss(), ), @@ -201,10 +202,11 @@ class _ReportOverlayState extends State> with SingleTickerPr @override Widget build(BuildContext context) { - final progressColor = Theme.of(context).colorScheme.secondary; + final colorScheme = Theme.of(context).colorScheme; + final progressColor = colorScheme.primary; final animate = context.select((v) => v.accessibilityAnimations.animate); - return WillPopScope( - onWillPop: () => SynchronousFuture(false), + return PopScope( + canPop: false, child: StreamBuilder( stream: opStream, builder: (context, snapshot) { @@ -220,7 +222,7 @@ class _ReportOverlayState extends State> with SingleTickerPr width: diameter + 2, height: diameter + 2, decoration: BoxDecoration( - color: Theme.of(context).brightness == Brightness.dark ? const Color(0xBB000000) : const Color(0xEEFFFFFF), + color: colorScheme.brightness == Brightness.dark ? const Color(0xBB000000) : const Color(0xEEFFFFFF), shape: BoxShape.circle, ), ), @@ -238,7 +240,7 @@ class _ReportOverlayState extends State> with SingleTickerPr percent: percent, lineWidth: strokeWidth, radius: diameter / 2, - backgroundColor: Theme.of(context).colorScheme.onSurface.withOpacity(.2), + backgroundColor: colorScheme.onSurface.withOpacity(.2), progressColor: progressColor, animation: animate, center: total != null @@ -331,18 +333,21 @@ class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerPro @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); final theme = Theme.of(context); - final contentTextStyle = theme.snackBarTheme.contentTextStyle ?? ThemeData(brightness: theme.brightness).textTheme.titleMedium!; - final fontSize = theme.snackBarTheme.contentTextStyle?.fontSize ?? theme.textTheme.bodyMedium!.fontSize!; - final timerChangeShadowColor = theme.colorScheme.primary; + final colorScheme = theme.colorScheme; + final contentTextStyle = theme.snackBarTheme.contentTextStyle ?? + theme.textTheme.bodyMedium!.copyWith( + color: colorScheme.onInverseSurface, + ); + final timerChangeShadowColor = colorScheme.primary; return Row( children: [ if (widget.type == FeedbackType.warn) ...[ CustomPaint( - painter: _WarnIndicator(), - size: Size(4, fontSize * textScaleFactor), + painter: const _WarnIndicator(AColors.warning), + size: Size(4, textScaler.scale(contentTextStyle.fontSize!)), ), const SizedBox(width: 8), ], @@ -391,13 +396,17 @@ class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerPro } class _WarnIndicator extends CustomPainter { + final Color color; + + const _WarnIndicator(this.color); + @override void paint(Canvas canvas, Size size) { canvas.drawRRect( RRect.fromLTRBR(0, 0, size.width, size.height, Radius.circular(size.shortestSide / 2)), Paint() ..style = PaintingStyle.fill - ..color = Colors.amber, + ..color = color, ); } diff --git a/lib/widgets/common/action_mixins/overlay_snack_bar.dart b/lib/widgets/common/action_mixins/overlay_snack_bar.dart index 1c212f712..515c4a7cf 100644 --- a/lib/widgets/common/action_mixins/overlay_snack_bar.dart +++ b/lib/widgets/common/action_mixins/overlay_snack_bar.dart @@ -1,43 +1,123 @@ -import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:flutter/material.dart'; // adapted from Flutter `SnackBar` in `/material/snack_bar.dart` // As of Flutter v3.0.1, `SnackBar` is not customizable enough to add margin // and ignore pointers in that area, so we use an overlay entry instead. -// This overlay entry is not below a `Scaffold` (which is expected by `SnackBar` +// This overlay entry is not under a `Scaffold` (which is expected by `SnackBar` // and `SnackBarAction`), and is not dismissed the same way. -// This adaptation assumes the `SnackBarBehavior.floating` behavior and no animation. + +const double _singleLineVerticalPadding = 14.0; +const Duration _snackBarDisplayDuration = Duration(milliseconds: 4000); +const Curve _snackBarHeightCurve = Curves.fastOutSlowIn; +const Curve _snackBarM3HeightCurve = Curves.easeInOutQuart; + +const Curve _snackBarFadeInCurve = Interval(0.4, 1.0); +const Curve _snackBarM3FadeInCurve = Interval(0.4, 0.6, curve: Curves.easeInCirc); +const Curve _snackBarFadeOutCurve = Interval(0.72, 1.0, curve: Curves.fastOutSlowIn); + class OverlaySnackBar extends StatefulWidget { final Widget content; + final Color? backgroundColor; + final double? elevation; + final EdgeInsetsGeometry? margin; + final EdgeInsetsGeometry? padding; + final double? width; + final ShapeBorder? shape; + final HitTestBehavior? hitTestBehavior; + final SnackBarBehavior? behavior; final Widget? action; + final double? actionOverflowThreshold; + final bool? showCloseIcon; + final Color? closeIconColor; + final Duration duration; + final Animation? animation; + final VoidCallback? onVisible; final DismissDirection dismissDirection; - final VoidCallback onDismiss; final Clip clipBehavior; + final VoidCallback onDismiss; const OverlaySnackBar({ super.key, required this.content, + this.backgroundColor, + this.elevation, + this.margin, + this.padding, + this.width, + this.shape, + this.hitTestBehavior, + this.behavior, this.action, + this.actionOverflowThreshold, + this.showCloseIcon, + this.closeIconColor, + this.duration = _snackBarDisplayDuration, + this.animation, + this.onVisible, this.dismissDirection = DismissDirection.down, this.clipBehavior = Clip.hardEdge, required this.onDismiss, - }); + }) : assert(elevation == null || elevation >= 0.0), + assert( + width == null || margin == null, + 'Width and margin can not be used together', + ), + assert(actionOverflowThreshold == null || (actionOverflowThreshold >= 0 && actionOverflowThreshold <= 1), 'Action overflow threshold must be between 0 and 1 inclusive'); @override State createState() => _OverlaySnackBarState(); } class _OverlaySnackBarState extends State { + bool _wasVisible = false; + + @override + void initState() { + super.initState(); + widget.animation!.addStatusListener(_onAnimationStatusChanged); + } + + @override + void didUpdateWidget(OverlaySnackBar oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.animation != oldWidget.animation) { + oldWidget.animation!.removeStatusListener(_onAnimationStatusChanged); + widget.animation!.addStatusListener(_onAnimationStatusChanged); + } + } + + @override + void dispose() { + widget.animation!.removeStatusListener(_onAnimationStatusChanged); + super.dispose(); + } + + void _onAnimationStatusChanged(AnimationStatus animationStatus) { + switch (animationStatus) { + case AnimationStatus.dismissed: + case AnimationStatus.forward: + case AnimationStatus.reverse: + break; + case AnimationStatus.completed: + if (widget.onVisible != null && !_wasVisible) { + widget.onVisible!(); + } + _wasVisible = true; + } + } + @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); + final bool accessibleNavigation = MediaQuery.accessibleNavigationOf(context); + assert(widget.animation != null); final ThemeData theme = Theme.of(context); final ColorScheme colorScheme = theme.colorScheme; final SnackBarThemeData snackBarTheme = theme.snackBarTheme; final bool isThemeDark = theme.brightness == Brightness.dark; final Color buttonColor = isThemeDark ? colorScheme.primary : colorScheme.secondary; - final SnackBarThemeData defaults = theme.useMaterial3 ? _SnackbarDefaultsM3(context) : _SnackbarDefaultsM2(context); + final SnackBarThemeData defaults = _SnackbarDefaultsM3(context); // SnackBar uses a theme that is the opposite brightness from // the surrounding theme. @@ -63,12 +143,63 @@ class _OverlaySnackBarState extends State { ); final TextStyle? contentTextStyle = snackBarTheme.contentTextStyle ?? defaults.contentTextStyle; + final SnackBarBehavior snackBarBehavior = widget.behavior ?? snackBarTheme.behavior ?? defaults.behavior!; + final double? width = widget.width ?? snackBarTheme.width; + assert(() { + // Whether the behavior is set through the constructor or the theme, + // assert that our other properties are configured properly. + if (snackBarBehavior != SnackBarBehavior.floating) { + String message(String parameter) { + final String prefix = '$parameter can only be used with floating behavior.'; + if (widget.behavior != null) { + return '$prefix SnackBarBehavior.fixed was set in the SnackBar constructor.'; + } else if (snackBarTheme.behavior != null) { + return '$prefix SnackBarBehavior.fixed was set by the inherited SnackBarThemeData.'; + } else { + return '$prefix SnackBarBehavior.fixed was set by default.'; + } + } + + assert(widget.margin == null, message('Margin')); + assert(width == null, message('Width')); + } + return true; + }()); + + final bool showCloseIcon = widget.showCloseIcon ?? snackBarTheme.showCloseIcon ?? defaults.showCloseIcon!; + + final bool isFloatingSnackBar = snackBarBehavior == SnackBarBehavior.floating; + final double horizontalPadding = isFloatingSnackBar ? 16.0 : 24.0; + final EdgeInsetsGeometry padding = widget.padding ?? EdgeInsetsDirectional.only(start: horizontalPadding, end: widget.action != null || showCloseIcon ? 0 : horizontalPadding); + + final double iconHorizontalMargin = (widget.padding?.resolve(TextDirection.ltr).right ?? horizontalPadding) / 12.0; - final horizontalPadding = FeedbackMixin.snackBarHorizontalPadding(snackBarTheme); - final padding = EdgeInsetsDirectional.only(start: horizontalPadding, end: widget.action != null ? 0 : horizontalPadding); - const singleLineVerticalPadding = 14.0; + final CurvedAnimation heightAnimation = CurvedAnimation(parent: widget.animation!, curve: _snackBarHeightCurve); + final CurvedAnimation fadeInAnimation = CurvedAnimation(parent: widget.animation!, curve: _snackBarFadeInCurve); + final CurvedAnimation fadeInM3Animation = CurvedAnimation(parent: widget.animation!, curve: _snackBarM3FadeInCurve); - final EdgeInsets margin = snackBarTheme.insetPadding ?? defaults.insetPadding!; + final CurvedAnimation fadeOutAnimation = CurvedAnimation( + parent: widget.animation!, + curve: _snackBarFadeOutCurve, + reverseCurve: const Threshold(0.0), + ); + // Material 3 Animation has a height animation on entry, but a direct fade out on exit. + final CurvedAnimation heightM3Animation = CurvedAnimation( + parent: widget.animation!, + curve: _snackBarM3HeightCurve, + reverseCurve: const Threshold(0.0), + ); + + final IconButton? iconButton = showCloseIcon + ? IconButton( + icon: const Icon(Icons.close), + iconSize: 24.0, + color: widget.closeIconColor ?? snackBarTheme.closeIconColor ?? defaults.closeIconColor, + onPressed: () => ScaffoldMessenger.of(context).hideCurrentSnackBar(reason: SnackBarClosedReason.dismiss), + ) + : null; + + final EdgeInsets margin = widget.margin?.resolve(TextDirection.ltr) ?? snackBarTheme.insetPadding ?? defaults.insetPadding!; Widget snackBar = Padding( padding: padding, @@ -76,7 +207,7 @@ class _OverlaySnackBarState extends State { children: [ Expanded( child: Container( - padding: widget.action != null ? null : const EdgeInsets.symmetric(vertical: singleLineVerticalPadding), + padding: widget.action != null ? null : const EdgeInsets.symmetric(vertical: _singleLineVerticalPadding), child: DefaultTextStyle( style: contentTextStyle!, child: widget.content, @@ -93,34 +224,62 @@ class _OverlaySnackBarState extends State { ), child: widget.action!, ), + if (showCloseIcon) + Padding( + padding: EdgeInsets.symmetric(horizontal: iconHorizontalMargin), + child: iconButton, + ), ], ), ); - final double elevation = snackBarTheme.elevation ?? defaults.elevation!; - final Color backgroundColor = snackBarTheme.backgroundColor ?? defaults.backgroundColor!; - final ShapeBorder? shape = snackBarTheme.shape ?? defaults.shape; + if (!isFloatingSnackBar) { + snackBar = SafeArea( + top: false, + child: snackBar, + ); + } + + final double elevation = widget.elevation ?? snackBarTheme.elevation ?? defaults.elevation!; + final Color backgroundColor = widget.backgroundColor ?? snackBarTheme.backgroundColor ?? defaults.backgroundColor!; + final ShapeBorder? shape = widget.shape ?? snackBarTheme.shape ?? (isFloatingSnackBar ? defaults.shape : null); snackBar = Material( shape: shape, elevation: elevation, color: backgroundColor, + clipBehavior: widget.clipBehavior, child: Theme( data: effectiveTheme, - child: snackBar, + child: accessibleNavigation || theme.useMaterial3 + ? snackBar + : FadeTransition( + opacity: fadeOutAnimation, + child: snackBar, + ), ), ); - snackBar = Padding( - padding: margin, - child: snackBar, - ); - - snackBar = SafeArea( - top: false, - bottom: false, - child: snackBar, - ); + if (isFloatingSnackBar) { + // If width is provided, do not include horizontal margins. + if (width != null) { + snackBar = Container( + margin: EdgeInsets.only(top: margin.top, bottom: margin.bottom), + width: width, + child: snackBar, + ); + } else { + snackBar = Padding( + padding: margin, + child: snackBar, + ); + } + snackBar = SafeArea( + top: false, + bottom: false, + child: snackBar, + ); + } snackBar = Semantics( container: true, @@ -130,12 +289,49 @@ class _OverlaySnackBarState extends State { key: const Key('dismissible'), direction: widget.dismissDirection, resizeDuration: null, + behavior: widget.hitTestBehavior ?? (widget.margin != null ? HitTestBehavior.deferToChild : HitTestBehavior.opaque), onDismissed: (direction) => widget.onDismiss(), child: snackBar, ), ); - final Widget snackBarTransition = snackBar; + final Widget snackBarTransition; + if (accessibleNavigation) { + snackBarTransition = snackBar; + } else if (isFloatingSnackBar && !theme.useMaterial3) { + snackBarTransition = FadeTransition( + opacity: fadeInAnimation, + child: snackBar, + ); + // Is Material 3 Floating Snack Bar. + } else if (isFloatingSnackBar && theme.useMaterial3) { + snackBarTransition = FadeTransition( + opacity: fadeInM3Animation, + child: AnimatedBuilder( + animation: heightM3Animation, + builder: (context, child) { + return Align( + alignment: AlignmentDirectional.bottomStart, + heightFactor: heightM3Animation.value, + child: child, + ); + }, + child: snackBar, + ), + ); + } else { + snackBarTransition = AnimatedBuilder( + animation: heightAnimation, + builder: (context, child) { + return Align( + alignment: AlignmentDirectional.topStart, + heightFactor: heightAnimation.value, + child: child, + ); + }, + child: snackBar, + ); + } return Hero( tag: '', @@ -148,51 +344,6 @@ class _OverlaySnackBarState extends State { } } -// Hand coded defaults based on Material Design 2. -class _SnackbarDefaultsM2 extends SnackBarThemeData { - _SnackbarDefaultsM2(BuildContext context) - : _theme = Theme.of(context), - _colors = Theme.of(context).colorScheme, - super(elevation: 6.0); - - late final ThemeData _theme; - late final ColorScheme _colors; - - @override - Color get backgroundColor => _theme.brightness == Brightness.light ? Color.alphaBlend(_colors.onSurface.withOpacity(0.80), _colors.surface) : _colors.onSurface; - - @override - TextStyle? get contentTextStyle => ThemeData(brightness: _theme.brightness == Brightness.light ? Brightness.dark : Brightness.light).textTheme.titleMedium; - - @override - SnackBarBehavior get behavior => SnackBarBehavior.fixed; - - @override - Color get actionTextColor => _colors.secondary; - - @override - Color get disabledActionTextColor => _colors.onSurface.withOpacity(_theme.brightness == Brightness.light ? 0.38 : 0.3); - - @override - ShapeBorder get shape => const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(4.0), - ), - ); - - @override - EdgeInsets get insetPadding => const EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 10.0); - - @override - bool get showCloseIcon => false; - - @override - Color get closeIconColor => _colors.onSurface; - - @override - double get actionOverflowThreshold => 0.25; -} - // BEGIN GENERATED TOKEN PROPERTIES - Snackbar // Do not edit by hand. The code between the "BEGIN GENERATED" and @@ -200,8 +351,6 @@ class _SnackbarDefaultsM2 extends SnackBarThemeData { // Design token database by the script: // dev/tools/gen_defaults/bin/gen_defaults.dart. -// Token database version: v0_162 - class _SnackbarDefaultsM3 extends SnackBarThemeData { _SnackbarDefaultsM3(this.context); diff --git a/lib/widgets/common/app_bar/app_bar_title.dart b/lib/widgets/common/app_bar/app_bar_title.dart index a0274db45..cd9f0f97b 100644 --- a/lib/widgets/common/app_bar/app_bar_title.dart +++ b/lib/widgets/common/app_bar/app_bar_title.dart @@ -12,7 +12,7 @@ class InteractiveAppBarTitle extends StatelessWidget { @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); return GestureDetector( onTap: onTap, // use a `Container` with a dummy color to make it expand @@ -20,7 +20,7 @@ class InteractiveAppBarTitle extends StatelessWidget { child: Container( alignment: AlignmentDirectional.centerStart, color: Colors.transparent, - height: kToolbarHeight * textScaleFactor, + height: textScaler.scale(kToolbarHeight), child: child, ), ); diff --git a/lib/widgets/common/basic/font_size_icon_theme.dart b/lib/widgets/common/basic/font_size_icon_theme.dart index 7b143fcf8..0ef46d6b7 100644 --- a/lib/widgets/common/basic/font_size_icon_theme.dart +++ b/lib/widgets/common/basic/font_size_icon_theme.dart @@ -11,11 +11,11 @@ class FontSizeIconTheme extends StatelessWidget { @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); final iconTheme = IconTheme.of(context); return IconTheme( data: iconTheme.copyWith( - size: iconTheme.size! * textScaleFactor, + size: textScaler.scale(iconTheme.size!), ), child: child, ); diff --git a/lib/widgets/common/basic/insets.dart b/lib/widgets/common/basic/insets.dart index 106756878..1e083a3ce 100644 --- a/lib/widgets/common/basic/insets.dart +++ b/lib/widgets/common/basic/insets.dart @@ -6,6 +6,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/media_query.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/tile_extent_controller.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -122,7 +123,7 @@ class BottomPaddingSliver extends StatelessWidget { } class TvTileGridBottomPaddingSliver extends StatelessWidget { - const TvTileGridBottomPaddingSliver({Key? key}) : super(key: key); + const TvTileGridBottomPaddingSliver({super.key}); @override Widget build(BuildContext context) { @@ -151,9 +152,10 @@ class SafeCutoutArea extends StatelessWidget { return ValueListenableBuilder( valueListenable: AvesApp.cutoutInsetsNotifier, builder: (context, cutoutInsets, child) { - return ValueListenableBuilder( - valueListenable: animation ?? ValueNotifier(1), - builder: (context, factor, child) { + return NullableValueListenableBuilder( + valueListenable: animation, + builder: (context, value, child) { + final double factor = value ?? 1.0; final effectiveInsets = cutoutInsets * factor; return Padding( padding: effectiveInsets, diff --git a/lib/widgets/common/basic/labeled_checkbox.dart b/lib/widgets/common/basic/labeled_checkbox.dart index 286b89247..c106e0749 100644 --- a/lib/widgets/common/basic/labeled_checkbox.dart +++ b/lib/widgets/common/basic/labeled_checkbox.dart @@ -7,11 +7,11 @@ class LabeledCheckbox extends StatefulWidget { final String text; const LabeledCheckbox({ - Key? key, + super.key, required this.value, required this.onChanged, required this.text, - }) : super(key: key); + }); @override State createState() => _LabeledCheckboxState(); diff --git a/lib/widgets/common/basic/list_tiles/reselectable_radio.dart b/lib/widgets/common/basic/list_tiles/reselectable_radio.dart index f6a33aacf..1f937dab7 100644 --- a/lib/widgets/common/basic/list_tiles/reselectable_radio.dart +++ b/lib/widgets/common/basic/list_tiles/reselectable_radio.dart @@ -60,7 +60,7 @@ class ReselectableRadioListTile extends StatelessWidget { } return MergeSemantics( child: ListTileTheme.merge( - selectedColor: activeColor ?? Theme.of(context).colorScheme.secondary, + selectedColor: activeColor ?? Theme.of(context).colorScheme.primary, child: ListTile( leading: leading, title: title, diff --git a/lib/widgets/common/basic/list_tiles/slider.dart b/lib/widgets/common/basic/list_tiles/slider.dart index 63ea68d52..1a95a2ea6 100644 --- a/lib/widgets/common/basic/list_tiles/slider.dart +++ b/lib/widgets/common/basic/list_tiles/slider.dart @@ -24,44 +24,46 @@ class SliderListTile extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); + final listTileTitleTextStyle = ListTileTheme.of(context).titleTextStyle ?? theme.textTheme.bodyLarge!.copyWith(color: theme.colorScheme.onSurface); return SliderTheme( - data: const SliderThemeData( - overlayShape: RoundSliderOverlayShape( + data: SliderTheme.of(context).copyWith( + overlayShape: const RoundSliderOverlayShape( // align `Slider`s on `Switch`es by matching their overlay/reaction radius // `kRadialReactionRadius` is used when `SwitchThemeData.splashRadius` is undefined overlayRadius: kRadialReactionRadius, ), ), - child: DefaultTextStyle( - style: Theme.of(context).textTheme.titleMedium!, - child: Padding( - padding: const EdgeInsets.only(top: 16, bottom: 8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: titlePadding, - child: Row( - children: [ - Text(title), - const Spacer(), - if (titleTrailing != null) titleTrailing!(context, value), - ], - ), + child: Padding( + padding: const EdgeInsets.only(top: 16, bottom: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: titlePadding, + child: Row( + children: [ + Text( + title, + style: listTileTitleTextStyle, + ), + const Spacer(), + if (titleTrailing != null) titleTrailing!(context, value), + ], ), - Padding( - // match `SwitchListTile.contentPadding` - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Slider( - value: value, - onChanged: onChanged, - min: min, - max: max, - divisions: divisions, - ), + ), + Padding( + // match `SwitchListTile.contentPadding` + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Slider( + value: value, + onChanged: onChanged, + min: min, + max: max, + divisions: divisions, ), - ], - ), + ), + ], ), ), ); diff --git a/lib/widgets/common/basic/markdown_container.dart b/lib/widgets/common/basic/markdown_container.dart index 62c054210..613680250 100644 --- a/lib/widgets/common/basic/markdown_container.dart +++ b/lib/widgets/common/basic/markdown_container.dart @@ -1,4 +1,5 @@ import 'package:aves/model/settings/settings.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/aves_app.dart'; import 'package:aves/widgets/common/fx/borders.dart'; import 'package:flutter/material.dart'; @@ -53,8 +54,11 @@ class MarkdownContainer extends StatelessWidget { return Container( margin: const EdgeInsets.symmetric(horizontal: 8), decoration: BoxDecoration( - color: Theme.of(context).canvasColor, - border: Border.all(color: Theme.of(context).dividerColor, width: AvesBorder.curvedBorderWidth(context)), + color: Themes.secondLayerColor(context), + border: Border.all( + color: Theme.of(context).dividerColor, + width: AvesBorder.curvedBorderWidth(context), + ), borderRadius: const BorderRadius.all(Radius.circular(16)), ), constraints: BoxConstraints(maxWidth: useTvLayout ? double.infinity : mobileMaxWidth), diff --git a/lib/widgets/common/basic/popup/expansion_panel.dart b/lib/widgets/common/basic/popup/expansion_panel.dart index 10308dfd9..3d4501def 100644 --- a/lib/widgets/common/basic/popup/expansion_panel.dart +++ b/lib/widgets/common/basic/popup/expansion_panel.dart @@ -6,21 +6,21 @@ import 'package:provider/provider.dart'; class PopupMenuExpansionPanel extends PopupMenuEntry { final bool enabled; final String value; - final ValueNotifier expandedNotifier; + final ValueNotifier? expandedNotifier; final IconData icon; final String title; final List> items; - PopupMenuExpansionPanel({ + const PopupMenuExpansionPanel({ super.key, this.enabled = true, this.height = kMinInteractiveDimension, required this.value, - ValueNotifier? expandedNotifier, + this.expandedNotifier, required this.icon, required this.title, required this.items, - }) : expandedNotifier = expandedNotifier ?? ValueNotifier(null); + }); @override final double height; @@ -33,24 +33,31 @@ class PopupMenuExpansionPanel extends PopupMenuEntry { } class _PopupMenuExpansionPanelState extends State> { - // ref `_kMenuHorizontalPadding` used in `PopupMenuItem` - static const double _horizontalPadding = 16; + final ValueNotifier _internalExpandedNotifier = ValueNotifier(null); + + ValueNotifier get expandedNotifier => widget.expandedNotifier ?? _internalExpandedNotifier; + + @override + void dispose() { + _internalExpandedNotifier.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { - final theme = Theme.of(context); - var style = PopupMenuTheme.of(context).textStyle ?? theme.textTheme.titleMedium!; - if (!widget.enabled) { - style = style.copyWith(color: theme.disabledColor); - } + final style = PopupMenuTheme.of(context).labelTextStyle!.resolve( + { + if (!widget.enabled) MaterialState.disabled, + }, + )!; final animationDuration = context.select((v) => v.expansionTileAnimation); Widget child = ValueListenableBuilder( - valueListenable: widget.expandedNotifier, + valueListenable: expandedNotifier, builder: (context, expandedValue, child) { return ExpansionPanelList( expansionCallback: (index, isExpanded) { - widget.expandedNotifier.value = isExpanded ? widget.value : null; + expandedNotifier.value = isExpanded ? widget.value : null; }, animationDuration: animationDuration, expandedHeaderPadding: EdgeInsets.zero, @@ -65,7 +72,7 @@ class _PopupMenuExpansionPanelState extends State> color: widget.enabled ? null : Theme.of(context).disabledColor, ), child: Padding( - padding: const EdgeInsets.symmetric(horizontal: _horizontalPadding), + padding: const EdgeInsets.symmetric(horizontal: 12), child: MenuRow( text: widget.title, icon: Icon(widget.icon), diff --git a/lib/widgets/common/basic/query_bar.dart b/lib/widgets/common/basic/query_bar.dart index 24dd2637b..e2ddb31bc 100644 --- a/lib/widgets/common/basic/query_bar.dart +++ b/lib/widgets/common/basic/query_bar.dart @@ -26,7 +26,7 @@ class QueryBar extends StatefulWidget { @override State createState() => _QueryBarState(); - static double getPreferredHeight(double textScaleFactor) => kToolbarHeight * textScaleFactor; + static double getPreferredHeight(TextScaler textScaler) => textScaler.scale(kToolbarHeight); } class _QueryBarState extends State { @@ -41,6 +41,12 @@ class _QueryBarState extends State { _controller = TextEditingController(text: queryNotifier.value); } + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final clearButton = IconButton( diff --git a/lib/widgets/common/basic/text/animated_diff.dart b/lib/widgets/common/basic/text/animated_diff.dart index 091804f12..213a2a58e 100644 --- a/lib/widgets/common/basic/text/animated_diff.dart +++ b/lib/widgets/common/basic/text/animated_diff.dart @@ -114,14 +114,15 @@ class _AnimatedDiffTextState extends State with SingleTickerPr } Size textSize(String text) { - final para = RenderParagraph( + final paragraph = RenderParagraph( TextSpan(text: text, style: widget.textStyle), textDirection: Directionality.of(context), - textScaleFactor: MediaQuery.textScaleFactorOf(context), + textScaler: MediaQuery.textScalerOf(context), strutStyle: widget.strutStyle, )..layout(const BoxConstraints(), parentUsesSize: true); - final width = para.getMaxIntrinsicWidth(double.infinity); - final height = para.getMaxIntrinsicHeight(double.infinity); + final width = paragraph.getMaxIntrinsicWidth(double.infinity); + final height = paragraph.getMaxIntrinsicHeight(double.infinity); + paragraph.dispose(); return Size(width, height); } diff --git a/lib/widgets/common/basic/text/background_painter.dart b/lib/widgets/common/basic/text/background_painter.dart index e738614fc..d6a4254e1 100644 --- a/lib/widgets/common/basic/text/background_painter.dart +++ b/lib/widgets/common/basic/text/background_painter.dart @@ -36,7 +36,7 @@ class TextBackgroundPainter extends StatelessWidget { ), textAlign: textAlign, textDirection: Directionality.of(context), - textScaleFactor: MediaQuery.textScaleFactorOf(context), + textScaler: MediaQuery.textScalerOf(context), )..layout(constraints, parentUsesSize: true); final textLength = spans.map((v) => v.text?.length ?? 0).sum; @@ -44,6 +44,7 @@ class TextBackgroundPainter extends StatelessWidget { TextSelection(baseOffset: 0, extentOffset: textLength), boxHeightStyle: ui.BoxHeightStyle.max, ); + paragraph.dispose(); // merge boxes to avoid artifacts at box edges, from anti-aliasing and rounding hacks final lineRects = groupBy(allBoxes, (v) => v.top).entries.map((kv) { diff --git a/lib/widgets/common/basic/text_dropdown_button.dart b/lib/widgets/common/basic/text_dropdown_button.dart index 8efc50028..e282446c2 100644 --- a/lib/widgets/common/basic/text_dropdown_button.dart +++ b/lib/widgets/common/basic/text_dropdown_button.dart @@ -1,48 +1,58 @@ import 'package:flutter/material.dart'; -class TextDropdownButton extends DropdownButton { - TextDropdownButton({ +class TextDropdownButton extends StatefulWidget { + final List values; + final String Function(T value) valueText; + final IconData Function(T value)? valueIcon; + final T? value; + final Widget? underline; + final bool isExpanded; + final double? itemHeight; + final Color? dropdownColor; + final ValueChanged? onChanged; + + const TextDropdownButton({ super.key, - required List values, - required String Function(T value) valueText, - IconData Function(T value)? valueIcon, - super.value, - super.hint, - super.disabledHint, - required super.onChanged, - super.onTap, - super.elevation, - super.style, - super.underline, - super.icon, - super.iconDisabledColor, - super.iconEnabledColor, - super.iconSize, - super.isDense, - super.isExpanded, - super.itemHeight, - super.focusColor, - super.focusNode, - super.autofocus, - super.dropdownColor, - super.menuMaxHeight, - super.enableFeedback, - super.alignment, - super.borderRadius, - }) : super( - items: values - .map((v) => DropdownMenuItem( - value: v, - child: _buildItem(valueText(v), valueIcon?.call(v), selected: false), - )) - .toList(), - selectedItemBuilder: (context) => values - .map((v) => DropdownMenuItem( - value: v, - child: _buildItem(valueText(v), valueIcon?.call(v), selected: true), - )) - .toList(), - ); + required this.values, + required this.valueText, + this.valueIcon, + this.value, + this.underline, + this.isExpanded = false, + this.itemHeight = kMinInteractiveDimension, + this.dropdownColor, + required this.onChanged, + }); + + @override + State> createState() => _TextDropdownButtonState(); +} + +class _TextDropdownButtonState extends State> { + @override + Widget build(BuildContext context) { + return DropdownButton( + items: widget.values + .map((v) => DropdownMenuItem( + value: v, + child: _buildItem(widget.valueText(v), widget.valueIcon?.call(v), selected: false), + )) + .toList(), + selectedItemBuilder: (context) => widget.values + .map((v) => DropdownMenuItem( + value: v, + child: _buildItem(widget.valueText(v), widget.valueIcon?.call(v), selected: true), + )) + .toList(), + value: widget.value, + style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.normal), + underline: widget.underline, + isExpanded: widget.isExpanded, + itemHeight: widget.itemHeight, + dropdownColor: widget.dropdownColor, + onChanged: widget.onChanged, + ); + } static Widget _buildItem(String text, IconData? icon, {required bool selected}) { final softWrap = selected ? false : null; diff --git a/lib/widgets/common/basic/wheel.dart b/lib/widgets/common/basic/wheel.dart index 1682eb5cb..6007e9dfd 100644 --- a/lib/widgets/common/basic/wheel.dart +++ b/lib/widgets/common/basic/wheel.dart @@ -46,11 +46,11 @@ class _WheelSelectorState extends State> { @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); const background = Colors.transparent; final foreground = DefaultTextStyle.of(context).style.color!; final transitionDuration = context.select((v) => v.formTransition); - final itemSize = Size.square(40 * textScaleFactor); + final itemSize = Size.square(textScaler.scale(40)); return FocusableActionDetector( shortcuts: const { diff --git a/lib/widgets/common/behaviour/eager_scale_gesture_recognizer.dart b/lib/widgets/common/behaviour/eager_scale_gesture_recognizer.dart index ce2b4c682..aeba82b62 100644 --- a/lib/widgets/common/behaviour/eager_scale_gesture_recognizer.dart +++ b/lib/widgets/common/behaviour/eager_scale_gesture_recognizer.dart @@ -181,6 +181,14 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { /// {@endtemplate} Offset trackpadScrollToScaleFactor; + /// The number of pointers being tracked by the gesture recognizer. + /// + /// Typically this is the number of fingers being used to pan the widget using the gesture + /// recognizer. + int get pointerCount { + return _pointerPanZooms.length + _pointerQueue.length; + } + late Offset _initialFocalPoint; Offset? _currentFocalPoint; late double _initialSpan; @@ -231,10 +239,6 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { return scale; } - int get _pointerCount { - return _pointerPanZooms.length + _pointerQueue.length; - } - double _computeRotationFactor() { double factor = 0.0; if (_initialLine != null && _currentLine != null) { @@ -354,7 +358,7 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { for (final _PointerPanZoomData p in _pointerPanZooms.values) { focalPoint += p.focalPoint; } - _currentFocalPoint = _pointerCount > 0 ? focalPoint / _pointerCount.toDouble() : Offset.zero; + _currentFocalPoint = pointerCount > 0 ? focalPoint / pointerCount.toDouble() : Offset.zero; if (previousFocalPoint == null) { _localFocalPoint = PointerEvent.transformPosition( @@ -449,9 +453,9 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { if (pixelsPerSecond.distanceSquared > kMaxFlingVelocity * kMaxFlingVelocity) { velocity = Velocity(pixelsPerSecond: (pixelsPerSecond / pixelsPerSecond.distance) * kMaxFlingVelocity); } - invokeCallback('onEnd', () => onEnd!(ScaleEndDetails(velocity: velocity, scaleVelocity: _scaleVelocityTracker?.getVelocity().pixelsPerSecond.dx ?? -1, pointerCount: _pointerCount))); + invokeCallback('onEnd', () => onEnd!(ScaleEndDetails(velocity: velocity, scaleVelocity: _scaleVelocityTracker?.getVelocity().pixelsPerSecond.dx ?? -1, pointerCount: pointerCount))); } else { - invokeCallback('onEnd', () => onEnd!(ScaleEndDetails(scaleVelocity: _scaleVelocityTracker?.getVelocity().pixelsPerSecond.dx ?? -1, pointerCount: _pointerCount))); + invokeCallback('onEnd', () => onEnd!(ScaleEndDetails(scaleVelocity: _scaleVelocityTracker?.getVelocity().pixelsPerSecond.dx ?? -1, pointerCount: pointerCount))); } } _state = _ScaleState.accepted; @@ -499,7 +503,7 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { focalPoint: _currentFocalPoint!, localFocalPoint: _localFocalPoint, rotation: _computeRotationFactor(), - pointerCount: _pointerCount, + pointerCount: pointerCount, focalPointDelta: _delta, )); }); @@ -514,7 +518,7 @@ class EagerScaleGestureRecognizer extends OneSequenceGestureRecognizer { onStart!(ScaleStartDetails( focalPoint: _currentFocalPoint!, localFocalPoint: _localFocalPoint, - pointerCount: _pointerCount, + pointerCount: pointerCount, )); }); } diff --git a/lib/widgets/common/behaviour/known_extent_scroll_physics.dart b/lib/widgets/common/behaviour/known_extent_scroll_physics.dart index 57bb1b809..018a049ab 100644 --- a/lib/widgets/common/behaviour/known_extent_scroll_physics.dart +++ b/lib/widgets/common/behaviour/known_extent_scroll_physics.dart @@ -9,8 +9,8 @@ class KnownExtentScrollPhysics extends ScrollPhysics { const KnownExtentScrollPhysics({ required this.indexToScrollOffset, required this.scrollOffsetToIndex, - ScrollPhysics? parent, - }) : super(parent: parent); + super.parent, + }); @override KnownExtentScrollPhysics applyTo(ScrollPhysics? ancestor) { diff --git a/lib/widgets/common/behaviour/pop/double_back.dart b/lib/widgets/common/behaviour/pop/double_back.dart index e2dd820e9..948946c8a 100644 --- a/lib/widgets/common/behaviour/pop/double_back.dart +++ b/lib/widgets/common/behaviour/pop/double_back.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:overlay_support/overlay_support.dart'; @@ -10,7 +11,20 @@ class DoubleBackPopHandler { bool _backOnce = false; Timer? _backTimer; + DoubleBackPopHandler() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$DoubleBackPopHandler', + object: this, + ); + } + } + void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _stopBackTimer(); } diff --git a/lib/widgets/common/behaviour/pop/scope.dart b/lib/widgets/common/behaviour/pop/scope.dart index c099d79c6..317e59504 100644 --- a/lib/widgets/common/behaviour/pop/scope.dart +++ b/lib/widgets/common/behaviour/pop/scope.dart @@ -1,4 +1,4 @@ -import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; // as of Flutter v3.3.10, the resolution order of multiple `WillPopScope` is random @@ -15,8 +15,21 @@ class AvesPopScope extends StatelessWidget { @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () => SynchronousFuture(handlers.fold(true, (prev, v) => prev ? v(context) : false)), + return PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (didPop) return; + + final shouldPop = handlers.fold(true, (prev, v) => prev ? v(context) : false); + if (shouldPop) { + if (Navigator.canPop(context)) { + Navigator.maybeOf(context)?.pop(); + } else { + // exit + SystemNavigator.pop(); + } + } + }, child: child, ); } diff --git a/lib/widgets/common/behaviour/routes.dart b/lib/widgets/common/behaviour/routes.dart index a9f5760f9..e9a42d8a0 100644 --- a/lib/widgets/common/behaviour/routes.dart +++ b/lib/widgets/common/behaviour/routes.dart @@ -16,10 +16,9 @@ class DirectPageTransitionsTheme extends PageTransitionsTheme { class DirectMaterialPageRoute extends PageRouteBuilder { DirectMaterialPageRoute({ - RouteSettings? settings, + super.settings, required WidgetBuilder builder, }) : super( - settings: settings, transitionDuration: Duration.zero, pageBuilder: (context, a, sa) => builder(context), ); @@ -32,9 +31,9 @@ class DirectMaterialPageRoute extends PageRouteBuilder { class TransparentMaterialPageRoute extends PageRouteBuilder { TransparentMaterialPageRoute({ - RouteSettings? settings, - required RoutePageBuilder pageBuilder, - }) : super(settings: settings, pageBuilder: pageBuilder); + super.settings, + required super.pageBuilder, + }); @override bool get opaque => false; diff --git a/lib/widgets/common/behaviour/sloppy_scroll_physics.dart b/lib/widgets/common/behaviour/sloppy_scroll_physics.dart index 67b321183..f850795ad 100644 --- a/lib/widgets/common/behaviour/sloppy_scroll_physics.dart +++ b/lib/widgets/common/behaviour/sloppy_scroll_physics.dart @@ -13,8 +13,8 @@ class SloppyScrollPhysics extends ScrollPhysics { const SloppyScrollPhysics({ required this.gestureSettings, this.touchSlopFactor = 1, - ScrollPhysics? parent, - }) : super(parent: parent); + super.parent, + }); @override SloppyScrollPhysics applyTo(ScrollPhysics? ancestor) { diff --git a/lib/widgets/common/fx/highlight_decoration.dart b/lib/widgets/common/fx/highlight_decoration.dart index b888fa5ff..f3a323ad7 100644 --- a/lib/widgets/common/fx/highlight_decoration.dart +++ b/lib/widgets/common/fx/highlight_decoration.dart @@ -22,8 +22,8 @@ class _HighlightDecorationPainter extends BoxPainter { if (size == null) return; final confHeight = size.height; - final paintHeight = confHeight * .4; - final rect = Rect.fromLTWH(offset.dx, offset.dy + confHeight - paintHeight, size.width, paintHeight); + final paintHeight = confHeight * .32; + final rect = Rect.fromLTWH(offset.dx, offset.dy + size.height * .58, size.width, paintHeight); final rrect = RRect.fromRectAndRadius(rect, Radius.circular(paintHeight)); final paint = Paint()..color = decoration.color; canvas.drawRRect(rrect, paint); diff --git a/lib/widgets/common/fx/sweeper.dart b/lib/widgets/common/fx/sweeper.dart index bb01c693b..fc218fa3b 100644 --- a/lib/widgets/common/fx/sweeper.dart +++ b/lib/widgets/common/fx/sweeper.dart @@ -114,14 +114,12 @@ class _SweeperState extends State with SingleTickerProviderStateMixin { setState(() {}); await Future.delayed(ADurations.sweeperOpacityAnimation * timeDilation); _isAppearing = false; - if (mounted) { - _angleAnimationController.reset(); - unawaited(_angleAnimationController.forward()); - } - } - if (mounted) { - setState(() {}); + if (!mounted) return; + _angleAnimationController.reset(); + unawaited(_angleAnimationController.forward()); } + if (!mounted) return; + setState(() {}); } } diff --git a/lib/widgets/common/grid/header.dart b/lib/widgets/common/grid/header.dart index ce4213cc2..18ba54ec6 100644 --- a/lib/widgets/common/grid/header.dart +++ b/lib/widgets/common/grid/header.dart @@ -40,7 +40,7 @@ class SectionHeader extends StatelessWidget { child: GestureDetector( onTap: onTap, onLongPress: selectable - ? () { + ? Feedback.wrapForLongPress(() { final selection = context.read>(); if (selection.isSelecting) { _toggleSectionSelection(context); @@ -48,7 +48,7 @@ class SectionHeader extends StatelessWidget { selection.select(); selection.addToSelection(_getSectionEntries(context)); } - } + }, context) : null, child: Text.rich( TextSpan( @@ -125,9 +125,11 @@ class SectionHeader extends StatelessWidget { bool hasLeading = false, bool hasTrailing = false, }) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); + final leadingFontSize = leadingSize.height; + final textScaleFactor = textScaler.scale(leadingFontSize) / leadingFontSize; final maxContentWidth = maxWidth - (SectionHeader.padding.horizontal + SectionHeader.margin.horizontal); - final para = RenderParagraph( + final paragraph = RenderParagraph( TextSpan( children: [ // as of Flutter v3.7.7, `RenderParagraph` fails to lay out `WidgetSpan` offscreen @@ -146,9 +148,11 @@ class SectionHeader extends StatelessWidget { ], ), textDirection: TextDirection.ltr, - textScaleFactor: textScaleFactor, + textScaler: textScaler, )..layout(BoxConstraints(maxWidth: maxContentWidth), parentUsesSize: true); - return para.getMaxIntrinsicHeight(maxContentWidth); + final height = paragraph.getMaxIntrinsicHeight(maxContentWidth); + paragraph.dispose(); + return height; } } @@ -237,16 +241,25 @@ class _SectionSelectingLeading extends StatelessWidget { data: TooltipTheme.of(context).copyWith( preferBelow: false, ), - child: IconButton( - iconSize: 26, - padding: const EdgeInsetsDirectional.only(end: 6, bottom: 4), - onPressed: onPressed, - tooltip: isSelected ? context.l10n.collectionDeselectSectionTooltip : context.l10n.collectionSelectSectionTooltip, - constraints: BoxConstraints( - minHeight: SectionHeader.leadingSize.height, - minWidth: SectionHeader.leadingSize.width, + child: Padding( + padding: const EdgeInsetsDirectional.only(end: 8, bottom: 6), + child: Theme( + data: Theme.of(context).copyWith( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: IconButton( + iconSize: 26, + visualDensity: const VisualDensity(horizontal: VisualDensity.minimumDensity, vertical: VisualDensity.minimumDensity), + padding: const EdgeInsets.symmetric(horizontal: 7), + onPressed: onPressed, + tooltip: isSelected ? context.l10n.collectionDeselectSectionTooltip : context.l10n.collectionSelectSectionTooltip, + constraints: BoxConstraints( + minHeight: SectionHeader.leadingSize.height, + minWidth: SectionHeader.leadingSize.width, + ), + icon: Icon(isSelected ? AIcons.selected : AIcons.unselected), + ), ), - icon: Icon(isSelected ? AIcons.selected : AIcons.unselected), ), ), ); diff --git a/lib/widgets/common/grid/item_tracker.dart b/lib/widgets/common/grid/item_tracker.dart index 8ef7203fa..52048a563 100644 --- a/lib/widgets/common/grid/item_tracker.dart +++ b/lib/widgets/common/grid/item_tracker.dart @@ -135,11 +135,9 @@ class _GridItemTrackerState extends State> with WidgetsBin // so that we can handle window orientation change with the previous metrics, // regardless of the `View`/`WidgetsBindingObserver` order uncertainty await Future.delayed(const Duration(milliseconds: 500)); - - if (mounted) { - _lastSectionedListLayout = context.read>(); - _lastScrollableSize = scrollableSize; - } + if (!mounted) return; + _lastSectionedListLayout = context.read>(); + _lastScrollableSize = scrollableSize; } void _onLayoutChanged() { diff --git a/lib/widgets/common/grid/overlay.dart b/lib/widgets/common/grid/overlay.dart index fe7515494..f50b1a11c 100644 --- a/lib/widgets/common/grid/overlay.dart +++ b/lib/widgets/common/grid/overlay.dart @@ -32,7 +32,7 @@ class GridItemSelectionOverlay extends StatelessWidget { alignment: AlignmentDirectional.topEnd, padding: padding, decoration: BoxDecoration( - color: isSelected ? Theme.of(context).colorScheme.secondary.withOpacity(.6) : Colors.transparent, + color: isSelected ? Theme.of(context).colorScheme.primary.withOpacity(.6) : Colors.transparent, borderRadius: borderRadius, ), duration: duration, diff --git a/lib/widgets/common/grid/scaling.dart b/lib/widgets/common/grid/scaling.dart index 9f4493a61..c05b3f63e 100644 --- a/lib/widgets/common/grid/scaling.dart +++ b/lib/widgets/common/grid/scaling.dart @@ -55,6 +55,12 @@ class _GridScaleGestureDetectorState extends State widget.tileLayout; + @override + void dispose() { + _scaledSizeNotifier?.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final gestureSettings = MediaQuery.gestureSettingsOf(context); @@ -166,6 +172,7 @@ class _GridScaleGestureDetectorState extends State extends State extends State children, - }) : super(children: children); + required super.children, + }); @override RenderObject createRenderObject(BuildContext context) { diff --git a/lib/widgets/common/grid/sections/mosaic/row.dart b/lib/widgets/common/grid/sections/mosaic/row.dart index 8d0aae525..2cf0aa829 100644 --- a/lib/widgets/common/grid/sections/mosaic/row.dart +++ b/lib/widgets/common/grid/sections/mosaic/row.dart @@ -13,8 +13,8 @@ class MosaicGridRow extends MultiChildRenderObjectWidget { required this.rowLayout, required this.spacing, required this.textDirection, - required List children, - }) : super(children: children); + required super.children, + }); @override RenderObject createRenderObject(BuildContext context) { diff --git a/lib/widgets/common/grid/sections/mosaic/scale_overlay.dart b/lib/widgets/common/grid/sections/mosaic/scale_overlay.dart index c5df7da96..050995676 100644 --- a/lib/widgets/common/grid/sections/mosaic/scale_overlay.dart +++ b/lib/widgets/common/grid/sections/mosaic/scale_overlay.dart @@ -51,7 +51,7 @@ class MosaicScaleOverlay extends StatelessWidget { alignment: Alignment.center, children: [ _buildBar(extentMax, colorScheme.onPrimary.withOpacity(.1)), - _buildBar(scaledSize.width, colorScheme.secondary), + _buildBar(scaledSize.width, colorScheme.primary), ], ), ), diff --git a/lib/widgets/common/grid/selector.dart b/lib/widgets/common/grid/selector.dart index 675be6834..ba1eeb6cf 100644 --- a/lib/widgets/common/grid/selector.dart +++ b/lib/widgets/common/grid/selector.dart @@ -98,6 +98,7 @@ class _GridSelectionGestureDetectorState extends State>(); selection.toggleSelection(fromItem); _selecting = selection.isSelected([fromItem]); diff --git a/lib/widgets/common/grid/sliver.dart b/lib/widgets/common/grid/sliver.dart index 5be6b8415..299e70134 100644 --- a/lib/widgets/common/grid/sliver.dart +++ b/lib/widgets/common/grid/sliver.dart @@ -41,9 +41,9 @@ class _SliverKnownExtentList extends SliverMultiBoxAdaptorWidget { final List sectionLayouts; const _SliverKnownExtentList({ - required SliverChildDelegate delegate, + required super.delegate, required this.sectionLayouts, - }) : super(delegate: delegate); + }); @override _RenderSliverKnownExtentBoxAdaptor createRenderObject(BuildContext context) { @@ -69,10 +69,9 @@ class _RenderSliverKnownExtentBoxAdaptor extends RenderSliverMultiBoxAdaptor { } _RenderSliverKnownExtentBoxAdaptor({ - required RenderSliverBoxChildManager childManager, + required super.childManager, required List sectionLayouts, - }) : _sectionLayouts = sectionLayouts, - super(childManager: childManager); + }) : _sectionLayouts = sectionLayouts; SectionLayout? sectionAtIndex(int index) => sectionLayouts.firstWhereOrNull((section) => section.hasChild(index)); diff --git a/lib/widgets/common/grid/theme.dart b/lib/widgets/common/grid/theme.dart index 53487f19a..134308a0b 100644 --- a/lib/widgets/common/grid/theme.dart +++ b/lib/widgets/common/grid/theme.dart @@ -31,7 +31,7 @@ class GridTheme extends StatelessWidget { final margin = OverlayIcon.defaultMargin.vertical; var iconSize = min(24.0, ((extent - margin) / 5).floorToDouble() - margin); final fontSize = (iconSize * .7).floorToDouble(); - iconSize *= mq.textScaleFactor; + iconSize = mq.textScaler.scale(iconSize); final highlightBorderWidth = extent * .1; final interactiveDimension = min(iconSize * 2, kMinInteractiveDimension); return GridThemeData( diff --git a/lib/widgets/common/identity/aves_app_bar.dart b/lib/widgets/common/identity/aves_app_bar.dart index 590be02f1..01664c414 100644 --- a/lib/widgets/common/identity/aves_app_bar.dart +++ b/lib/widgets/common/identity/aves_app_bar.dart @@ -32,7 +32,9 @@ class AvesAppBar extends StatelessWidget { @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; + final textScaler = MediaQuery.textScalerOf(context); final useTvLayout = settings.useTvLayout; return SliverPersistentHeader( floating: !useTvLayout, @@ -45,54 +47,66 @@ class AvesAppBar extends StatelessWidget { child: AvesFloatingBar( builder: (context, backgroundColor, child) => Material( color: backgroundColor, - child: child, + child: InkWell( + // absorb taps while providing visual feedback + onTap: () {}, + onLongPress: () {}, + child: child, + ), ), - child: Column( - children: [ - SizedBox( - height: kToolbarHeight * textScaleFactor, - child: Row( - children: [ - leading != null - ? Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Hero( - tag: leadingHeroTag, - flightShuttleBuilder: _flightShuttleBuilder, - transitionOnUserGestures: true, - child: FontSizeIconTheme( - child: leading!, + child: Theme( + data: theme.copyWith( + colorScheme: colorScheme.copyWith( + onSurfaceVariant: colorScheme.onSurface, + ), + ), + child: Column( + children: [ + SizedBox( + height: textScaler.scale(kToolbarHeight), + child: Row( + children: [ + leading != null + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Hero( + tag: leadingHeroTag, + flightShuttleBuilder: _flightShuttleBuilder, + transitionOnUserGestures: true, + child: FontSizeIconTheme( + child: leading!, + ), ), - ), - ) - : const SizedBox(width: 16), - Expanded( - child: DefaultTextStyle( - style: Theme.of(context).appBarTheme.titleTextStyle!, - child: Hero( - tag: titleHeroTag, - flightShuttleBuilder: _flightShuttleBuilder, - transitionOnUserGestures: true, - child: AnimatedSwitcher( - duration: context.read().iconAnimation, - child: FontSizeIconTheme( - child: Row( - key: ValueKey(transitionKey), - children: [ - Expanded(child: title), - ...actions, - ], + ) + : const SizedBox(width: 16), + Expanded( + child: DefaultTextStyle( + style: theme.appBarTheme.titleTextStyle!, + child: Hero( + tag: titleHeroTag, + flightShuttleBuilder: _flightShuttleBuilder, + transitionOnUserGestures: true, + child: AnimatedSwitcher( + duration: context.read().iconAnimation, + child: FontSizeIconTheme( + child: Row( + key: ValueKey(transitionKey), + children: [ + Expanded(child: title), + ...actions, + ], + ), ), ), ), ), ), - ), - ], + ], + ), ), - ), - if (bottom != null) bottom!, - ], + if (bottom != null) bottom!, + ], + ), ), ), ), @@ -190,6 +204,7 @@ class _AvesFloatingBarState extends State with RouteAware { @override void dispose() { AvesApp.pageRouteObserver.unsubscribe(this); + _isBlurAllowedNotifier.dispose(); super.dispose(); } @@ -212,7 +227,7 @@ class _AvesFloatingBarState extends State with RouteAware { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final backgroundColor = theme.appBarTheme.backgroundColor!; + final backgroundColor = theme.appBarTheme.backgroundColor ?? theme.colorScheme.surface; return ValueListenableBuilder( valueListenable: _isBlurAllowedNotifier, builder: (context, isBlurAllowed, child) { diff --git a/lib/widgets/common/identity/aves_donut.dart b/lib/widgets/common/identity/aves_donut.dart index ab587a730..50c967356 100644 --- a/lib/widgets/common/identity/aves_donut.dart +++ b/lib/widgets/common/identity/aves_donut.dart @@ -4,6 +4,7 @@ import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/theme/themes.dart'; import 'package:charts_flutter/flutter.dart' as charts; import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; @@ -85,8 +86,8 @@ class _AvesDonutState extends State with AutomaticKeepAliveClientMixi ), ]; - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final minWidth = avesDonutMinWidth * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + final minWidth = textScaler.scale(avesDonutMinWidth); final availableWidth = constraints.maxWidth; final dim = max(minWidth, availableWidth / (availableWidth > 4 * minWidth ? 4 : (availableWidth > 2 * minWidth ? 2 : 1))); @@ -94,7 +95,16 @@ class _AvesDonutState extends State with AutomaticKeepAliveClientMixi width: dim, height: dim, child: Stack( + alignment: Alignment.center, children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(dim)), + child: Container( + width: dim * .7, + height: dim * .7, + color: Themes.secondLayerColor(context), + ), + ), charts.PieChart( series, animate: context.select((v) => v.accessibilityAnimations.animate), @@ -145,7 +155,7 @@ class _AvesDonutState extends State with AutomaticKeepAliveClientMixi Text( formatValue(d.value), style: TextStyle( - color: Theme.of(context).textTheme.bodySmall!.color, + color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), const SizedBox(width: 4), diff --git a/lib/widgets/common/identity/aves_expansion_tile.dart b/lib/widgets/common/identity/aves_expansion_tile.dart index 994d89ac3..b8ef518fb 100644 --- a/lib/widgets/common/identity/aves_expansion_tile.dart +++ b/lib/widgets/common/identity/aves_expansion_tile.dart @@ -46,6 +46,7 @@ class AvesExpansionTile extends StatelessWidget { final animationDuration = context.select((v) => v.expansionTileAnimation); final theme = Theme.of(context); + final colorScheme = theme.colorScheme; return ExpansionTileCard( // key is expected by test driver key: Key('tilecard-$value'), @@ -55,9 +56,8 @@ class AvesExpansionTile extends StatelessWidget { expandable: enabled, initiallyExpanded: initiallyExpanded, finalPadding: const EdgeInsets.symmetric(vertical: 6.0), - baseColor: theme.scaffoldBackgroundColor, - expandedColor: theme.canvasColor, - expandedTextColor: theme.colorScheme.onBackground, + baseColor: theme.colorScheme.background, + expandedTextColor: colorScheme.onBackground, duration: animationDuration, shadowColor: theme.shadowColor, child: Column( diff --git a/lib/widgets/common/identity/aves_filter_chip.dart b/lib/widgets/common/identity/aves_filter_chip.dart index 4022b6553..59ddfb083 100644 --- a/lib/widgets/common/identity/aves_filter_chip.dart +++ b/lib/widgets/common/identity/aves_filter_chip.dart @@ -232,7 +232,7 @@ class _AvesFilterChipState extends State { @override Widget build(BuildContext context) { final decoration = widget.decoration; - final chipBackground = Theme.of(context).scaffoldBackgroundColor; + final chipBackground = Theme.of(context).colorScheme.background; final onTap = widget.onTap != null ? () { @@ -247,25 +247,30 @@ class _AvesFilterChipState extends State { } : null; final onLongPress = widget.onLongPress != null - ? () { + ? Feedback.wrapForLongPress(() { if (_tapPosition != null) { widget.onLongPress?.call(context, filter, _tapPosition!); } - } + }, context) : null; Widget? content; if (widget.showText) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final iconSize = AvesFilterChip.iconSize * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + final iconSize = textScaler.scale(AvesFilterChip.iconSize); final leading = widget.leadingOverride ?? filter.iconBuilder(context, iconSize, showGenericIcon: widget.showGenericIcon); final trailing = onRemove != null - ? IconButton( - icon: Icon(AIcons.clear, size: iconSize), - padding: EdgeInsets.zero, - splashRadius: IconTheme.of(context).size, - constraints: const BoxConstraints(), - onPressed: onRemove, + ? Theme( + data: Theme.of(context).copyWith( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: IconButton( + icon: Icon(AIcons.clear, size: iconSize), + padding: EdgeInsets.zero, + splashRadius: IconTheme.of(context).size, + constraints: const BoxConstraints(), + onPressed: onRemove, + ), ) : null; @@ -397,7 +402,7 @@ class _AvesFilterChipState extends State { child: Banner( message: banner.toUpperCase(), location: BannerLocation.topStart, - color: Theme.of(context).colorScheme.secondary, + color: Theme.of(context).colorScheme.primary, child: const SizedBox(), ), ), diff --git a/lib/widgets/common/identity/aves_logo.dart b/lib/widgets/common/identity/aves_logo.dart index cd25fe4d5..2273ef3de 100644 --- a/lib/widgets/common/identity/aves_logo.dart +++ b/lib/widgets/common/identity/aves_logo.dart @@ -22,7 +22,7 @@ class AvesLogo extends StatelessWidget { painter: AvesLogoPainter(), ); if (context.select((v) => v.themeColorMode == AvesThemeColorMode.monochrome)) { - final tint = Color.lerp(theme.colorScheme.secondary, Colors.white, .5)!; + final tint = Color.lerp(theme.colorScheme.primary, Colors.white, .5)!; child = ColorFiltered( colorFilter: ColorFilter.mode(tint, BlendMode.modulate), child: ColorFiltered( diff --git a/lib/widgets/common/identity/buttons/captioned_button.dart b/lib/widgets/common/identity/buttons/captioned_button.dart index 10cc09602..7417cd866 100644 --- a/lib/widgets/common/identity/buttons/captioned_button.dart +++ b/lib/widgets/common/identity/buttons/captioned_button.dart @@ -38,13 +38,14 @@ class CaptionedButton extends StatefulWidget { final width = getWidth(context); var height = width; if (showCaption) { - final para = RenderParagraph( + final paragraph = RenderParagraph( TextSpan(text: text, style: CaptionedButtonText.textStyle(context)), textDirection: TextDirection.ltr, - textScaleFactor: MediaQuery.textScaleFactorOf(context), + textScaler: MediaQuery.textScalerOf(context), maxLines: CaptionedButtonText.maxLines, )..layout(const BoxConstraints(), parentUsesSize: true); - height += para.getMaxIntrinsicHeight(width) + padding.vertical; + height += paragraph.getMaxIntrinsicHeight(width) + padding.vertical; + paragraph.dispose(); } return Size(width, height); } diff --git a/lib/widgets/common/identity/buttons/outlined_button.dart b/lib/widgets/common/identity/buttons/outlined_button.dart index 9b3ecc2f8..97b715280 100644 --- a/lib/widgets/common/identity/buttons/outlined_button.dart +++ b/lib/widgets/common/identity/buttons/outlined_button.dart @@ -18,11 +18,11 @@ class AvesOutlinedButton extends StatelessWidget { final style = ButtonStyle( side: MaterialStateProperty.resolveWith((states) { return BorderSide( - color: states.contains(MaterialState.disabled) ? theme.disabledColor : theme.colorScheme.secondary, + color: states.contains(MaterialState.disabled) ? theme.disabledColor : theme.colorScheme.primary, ); }), foregroundColor: MaterialStateProperty.resolveWith((states) { - return states.contains(MaterialState.disabled) ? theme.disabledColor : theme.colorScheme.onSecondary; + return states.contains(MaterialState.disabled) ? theme.disabledColor : theme.colorScheme.onPrimary; }), ); return icon != null diff --git a/lib/widgets/common/identity/buttons/overlay_button.dart b/lib/widgets/common/identity/buttons/overlay_button.dart index ce1aeeffd..0485cce4c 100644 --- a/lib/widgets/common/identity/buttons/overlay_button.dart +++ b/lib/widgets/common/identity/buttons/overlay_button.dart @@ -58,57 +58,66 @@ class _OverlayButtonState extends State { @override Widget build(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; final borderRadius = widget.borderRadius; final blurred = settings.enableBlurEffect; final overlayBackground = Themes.overlayBackgroundColor( - brightness: Theme.of(context).brightness, + brightness: colorScheme.brightness, blurred: blurred, ); - return ScaleTransition( - scale: widget.scale, - child: ValueListenableBuilder( - valueListenable: _focusedNotifier, - builder: (context, focused, child) { - final border = AvesBorder.border( - context, - width: AvesBorder.curvedBorderWidth(context) * (focused ? 3 : 1), - ); - return borderRadius != null - ? BlurredRRect( - enabled: blurred, - borderRadius: borderRadius, - child: Material( - type: MaterialType.button, + return Theme( + data: theme.copyWith( + colorScheme: colorScheme.copyWith( + onSurfaceVariant: colorScheme.onSurface, + ), + ), + child: ScaleTransition( + scale: widget.scale, + child: ValueListenableBuilder( + valueListenable: _focusedNotifier, + builder: (context, focused, child) { + final border = AvesBorder.border( + context, + width: AvesBorder.curvedBorderWidth(context) * (focused ? 3 : 1), + ); + return borderRadius != null + ? BlurredRRect( + enabled: blurred, borderRadius: borderRadius, - color: overlayBackground, - child: AnimatedContainer( - foregroundDecoration: BoxDecoration( - border: border, - borderRadius: borderRadius, + child: Material( + type: MaterialType.button, + borderRadius: borderRadius, + color: overlayBackground, + child: AnimatedContainer( + foregroundDecoration: BoxDecoration( + border: border, + borderRadius: borderRadius, + ), + duration: const Duration(milliseconds: 200), + child: widget.child, ), - duration: const Duration(milliseconds: 200), - child: widget.child, ), - ), - ) - : BlurredOval( - enabled: blurred, - child: Material( - type: MaterialType.circle, - color: overlayBackground, - child: AnimatedContainer( - foregroundDecoration: BoxDecoration( - border: border, - shape: BoxShape.circle, + ) + : BlurredOval( + enabled: blurred, + child: Material( + type: MaterialType.circle, + color: overlayBackground, + child: AnimatedContainer( + foregroundDecoration: BoxDecoration( + border: border, + shape: BoxShape.circle, + ), + duration: const Duration(milliseconds: 200), + child: widget.child, ), - duration: const Duration(milliseconds: 200), - child: widget.child, ), - ), - ); - }, + ); + }, + ), ), ); } diff --git a/lib/widgets/common/identity/empty.dart b/lib/widgets/common/identity/empty.dart index 0d617e9ea..dc3a074b3 100644 --- a/lib/widgets/common/identity/empty.dart +++ b/lib/widgets/common/identity/empty.dart @@ -22,7 +22,7 @@ class EmptyContent extends StatelessWidget { @override Widget build(BuildContext context) { - final color = Theme.of(context).colorScheme.secondary.withOpacity(.5); + final color = Theme.of(context).colorScheme.primary.withOpacity(.5); return Padding( padding: safeBottom ? EdgeInsets.only( diff --git a/lib/widgets/common/identity/highlight_title.dart b/lib/widgets/common/identity/highlight_title.dart index 44984ffa9..69b6bba65 100644 --- a/lib/widgets/common/identity/highlight_title.dart +++ b/lib/widgets/common/identity/highlight_title.dart @@ -61,7 +61,7 @@ class HighlightTitle extends StatelessWidget { style: style, ), ], - outlineColor: Theme.of(context).scaffoldBackgroundColor, + outlineColor: Theme.of(context).colorScheme.background, softWrap: false, overflow: TextOverflow.fade, maxLines: 1, diff --git a/lib/widgets/common/map/attribution.dart b/lib/widgets/common/map/attribution.dart index e025d64ea..e050d8a99 100644 --- a/lib/widgets/common/map/attribution.dart +++ b/lib/widgets/common/map/attribution.dart @@ -33,7 +33,7 @@ class Attribution extends StatelessWidget { data: data, selectable: true, styleSheet: MarkdownStyleSheet( - a: TextStyle(color: theme.colorScheme.secondary), + a: TextStyle(color: theme.colorScheme.primary), p: theme.textTheme.bodySmall!.merge(const TextStyle(fontSize: InfoRowGroup.fontSize)), ), onTapLink: (text, href, title) => AvesApp.launchUrl(href), diff --git a/lib/widgets/common/map/buttons/button.dart b/lib/widgets/common/map/buttons/button.dart index 67416d296..f7f383522 100644 --- a/lib/widgets/common/map/buttons/button.dart +++ b/lib/widgets/common/map/buttons/button.dart @@ -22,32 +22,42 @@ class MapOverlayButton extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); + final colorScheme = theme.colorScheme; final blurred = settings.enableBlurEffect; - return Selector>( - selector: (context, v) => v.scale, - builder: (context, scale, child) => ScaleTransition( - scale: scale, - child: child, + + return Theme( + data: theme.copyWith( + colorScheme: colorScheme.copyWith( + onSurfaceVariant: colorScheme.onSurface, + ), ), - child: BlurredOval( - enabled: blurred, - child: Material( - type: MaterialType.circle, - color: Themes.overlayBackgroundColor(brightness: Theme.of(context).brightness, blurred: blurred), - child: Ink( - decoration: BoxDecoration( - border: AvesBorder.border(context), - shape: BoxShape.circle, - ), - child: Selector( - selector: (context, v) => v.visualDensity, - builder: (context, visualDensity, child) => IconButton( - key: buttonKey, - iconSize: 20, - visualDensity: visualDensity, - icon: icon, - onPressed: onPressed, - tooltip: tooltip, + child: Selector>( + selector: (context, v) => v.scale, + builder: (context, scale, child) => ScaleTransition( + scale: scale, + child: child, + ), + child: BlurredOval( + enabled: blurred, + child: Material( + type: MaterialType.circle, + color: Themes.overlayBackgroundColor(brightness: Theme.of(context).brightness, blurred: blurred), + child: Ink( + decoration: BoxDecoration( + border: AvesBorder.border(context), + shape: BoxShape.circle, + ), + child: Selector( + selector: (context, v) => v.visualDensity, + builder: (context, visualDensity, child) => IconButton( + key: buttonKey, + iconSize: 20, + visualDensity: visualDensity, + icon: icon, + onPressed: onPressed, + tooltip: tooltip, + ), ), ), ), diff --git a/lib/widgets/common/map/buttons/coordinate_filter.dart b/lib/widgets/common/map/buttons/coordinate_filter.dart index 87c22e446..938abb715 100644 --- a/lib/widgets/common/map/buttons/coordinate_filter.dart +++ b/lib/widgets/common/map/buttons/coordinate_filter.dart @@ -44,6 +44,7 @@ class _OverlayCoordinateFilterChipState extends State { @override void dispose() { + _clusterChangeNotifier.dispose(); _unregisterWidget(widget); super.dispose(); } diff --git a/lib/widgets/common/map/leaflet/latlng_tween.dart b/lib/widgets/common/map/leaflet/latlng_tween.dart index 2b3d6960c..1593c9c04 100644 --- a/lib/widgets/common/map/leaflet/latlng_tween.dart +++ b/lib/widgets/common/map/leaflet/latlng_tween.dart @@ -4,12 +4,9 @@ import 'package:latlong2/latlong.dart'; class LatLngTween extends Tween { LatLngTween({ - required LatLng? begin, - required LatLng? end, - }) : super( - begin: begin, - end: end, - ); + required super.begin, + required super.end, + }); @override LatLng? lerp(double t) => LatLngUtils.lerp(begin, end, t); diff --git a/lib/widgets/common/map/leaflet/map.dart b/lib/widgets/common/map/leaflet/map.dart index 7deeb2781..901ffe79e 100644 --- a/lib/widgets/common/map/leaflet/map.dart +++ b/lib/widgets/common/map/leaflet/map.dart @@ -3,10 +3,11 @@ import 'dart:async'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/utils/debouncer.dart'; -import 'package:aves/widgets/common/map/leaflet/latlng_tween.dart'; +import 'package:aves/widgets/common/map/leaflet/latlng_tween.dart' as llt; import 'package:aves/widgets/common/map/leaflet/scale_layer.dart'; import 'package:aves/widgets/common/map/leaflet/tile_layers.dart'; import 'package:aves_map/aves_map.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; @@ -137,7 +138,7 @@ class _EntryLeafletMapState extends State> with TickerProv // marker tap handling prevents the default handling of focal zoom on double tap, // so we reimplement the double tap gesture here onDoubleTap: interactive ? () => _zoomBy(1, focalPoint: latLng) : null, - onLongPress: () => widget.onMarkerLongPress?.call(geoEntry, LatLng(geoEntry.latitude!, geoEntry.longitude!)), + onLongPress: Feedback.wrapForLongPress(() => widget.onMarkerLongPress?.call(geoEntry, LatLng(geoEntry.latitude!, geoEntry.longitude!)), context), child: widget.markerWidgetBuilder(markerKey), ), width: markerSize.width, @@ -172,8 +173,8 @@ class _EntryLeafletMapState extends State> with TickerProv rotate: true, alignment: Alignment.bottomCenter, ), - ValueListenableBuilder( - valueListenable: widget.dotLocationNotifier ?? ValueNotifier(null), + NullableValueListenableBuilder( + valueListenable: widget.dotLocationNotifier, builder: (context, dotLocation, child) => MarkerLayer( markers: [ if (dotLocation != null) @@ -214,9 +215,10 @@ class _EntryLeafletMapState extends State> with TickerProv final corner2 = overlayEntry.bottomRight; if (corner1 == null || corner2 == null) return const SizedBox(); - return ValueListenableBuilder( - valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1), - builder: (context, overlayOpacity, child) { + return NullableValueListenableBuilder( + valueListenable: widget.overlayOpacityNotifier, + builder: (context, value, child) { + final double overlayOpacity = value ?? 1.0; return OverlayImageLayer( overlayImages: [ OverlayImage( @@ -267,7 +269,7 @@ class _EntryLeafletMapState extends State> with TickerProv widget.onUserZoomChange?.call(endZoom); final center = camera.center; - final centerTween = LatLngTween(begin: center, end: focalPoint ?? center); + final centerTween = llt.LatLngTween(begin: center, end: focalPoint ?? center); final zoomTween = Tween(begin: camera.zoom, end: endZoom); await _animateCamera((animation) => _leafletMapController.move(centerTween.evaluate(animation)!, zoomTween.evaluate(animation))); @@ -275,7 +277,7 @@ class _EntryLeafletMapState extends State> with TickerProv Future _moveTo(LatLng point) async { final camera = _leafletMapController.camera; - final centerTween = LatLngTween(begin: camera.center, end: point); + final centerTween = llt.LatLngTween(begin: camera.center, end: point); await _animateCamera((animation) => _leafletMapController.move(centerTween.evaluate(animation)!, camera.zoom)); } diff --git a/lib/widgets/common/search/delegate.dart b/lib/widgets/common/search/delegate.dart index 9d89bac86..aa8947882 100644 --- a/lib/widgets/common/search/delegate.dart +++ b/lib/widgets/common/search/delegate.dart @@ -2,25 +2,33 @@ import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/search/route.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; abstract class AvesSearchDelegate extends SearchDelegate { final String routeName; final bool canPop; + final TextEditingController queryTextController = TextEditingController(); + final ValueNotifier currentBodyNotifier = ValueNotifier(null); AvesSearchDelegate({ required this.routeName, this.canPop = true, String? initialQuery, required super.searchFieldLabel, + required super.searchFieldStyle, }) { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$AvesSearchDelegate', + object: this, + ); + } query = initialQuery ?? ''; } - @mustCallSuper - void dispose() {} - @override Widget? buildLeading(BuildContext context) { if (settings.useTvLayout) { @@ -101,8 +109,6 @@ abstract class AvesSearchDelegate extends SearchDelegate { ScrollController? get suggestionsScrollController => null; - final TextEditingController queryTextController = TextEditingController(); - final ProxyAnimation proxyAnimation = ProxyAnimation(kAlwaysDismissedAnimation); @override @@ -116,8 +122,6 @@ abstract class AvesSearchDelegate extends SearchDelegate { } } - final ValueNotifier currentBodyNotifier = ValueNotifier(null); - SearchBody? get currentBody => currentBodyNotifier.value; set currentBody(SearchBody? value) { @@ -125,4 +129,17 @@ abstract class AvesSearchDelegate extends SearchDelegate { } SearchPageRoute? route; + + /// Releases the resources. + @override + @mustCallSuper + void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } + currentBodyNotifier.dispose(); + queryTextController.dispose(); + proxyAnimation.parent = null; + super.dispose(); + } } diff --git a/lib/widgets/common/search/page.dart b/lib/widgets/common/search/page.dart index fe1f32f61..3ae881aef 100644 --- a/lib/widgets/common/search/page.dart +++ b/lib/widgets/common/search/page.dart @@ -1,6 +1,7 @@ import 'dart:ui'; import 'package:aves/theme/durations.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/utils/debouncer.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/behaviour/pop/double_back.dart'; @@ -91,10 +92,9 @@ class _SearchPageState extends State { void _onQueryChanged() { _debouncer(() { - if (mounted) { - // rebuild ourselves because query changed. - setState(() {}); - } + if (!mounted) return; + // rebuild ourselves because query changed. + setState(() {}); }); } @@ -143,7 +143,7 @@ class _SearchPageState extends State { hintStyle: theme.inputDecorationTheme.hintStyle, ), textInputAction: TextInputAction.search, - style: theme.textTheme.titleLarge, + style: Themes.searchFieldStyle(context), onSubmitted: (_) => widget.delegate.showResults(context), ), ), diff --git a/lib/widgets/common/search/route.dart b/lib/widgets/common/search/route.dart index 93bf133f0..8b5d15452 100644 --- a/lib/widgets/common/search/route.dart +++ b/lib/widgets/common/search/route.dart @@ -50,7 +50,7 @@ class SearchPageRoute extends PageRoute { @override Animation createAnimation() { - final animation = super.createAnimation(); + final Animation animation = super.createAnimation(); delegate.proxyAnimation.parent = animation; return animation; } diff --git a/lib/widgets/common/thumbnail/decorated.dart b/lib/widgets/common/thumbnail/decorated.dart index 6a1616d25..34ddd3709 100644 --- a/lib/widgets/common/thumbnail/decorated.dart +++ b/lib/widgets/common/thumbnail/decorated.dart @@ -14,7 +14,7 @@ class DecoratedThumbnail extends StatelessWidget { final bool isMosaic, selectable, highlightable; final Object? Function()? heroTagger; - static final Color borderColor = Colors.grey.shade700; + static Color borderColor(BuildContext context) => Theme.of(context).dividerColor; static double borderWidth(BuildContext context) => AvesBorder.straightBorderWidth(context); @@ -75,7 +75,7 @@ class DecoratedThumbnail extends StatelessWidget { // so we use `foregroundDecoration` instead foregroundDecoration: BoxDecoration( border: Border.fromBorderSide(BorderSide( - color: borderColor, + color: borderColor(context), width: borderWidth(context), )), ), diff --git a/lib/widgets/common/thumbnail/error.dart b/lib/widgets/common/thumbnail/error.dart index 0ad1eb85f..01320bf54 100644 --- a/lib/widgets/common/thumbnail/error.dart +++ b/lib/widgets/common/thumbnail/error.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/utils/mime_utils.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -36,7 +37,7 @@ class _ErrorThumbnailState extends State { @override Widget build(BuildContext context) { - const color = Colors.blueGrey; + final color = Themes.backgroundTextColor(context); return FutureBuilder( future: _exists, builder: (context, snapshot) { @@ -65,7 +66,7 @@ class _ErrorThumbnailState extends State { } return Container( alignment: Alignment.center, - color: Colors.black, + color: Theme.of(context).colorScheme.background, width: extent, height: extent, child: child, diff --git a/lib/widgets/common/thumbnail/image.dart b/lib/widgets/common/thumbnail/image.dart index d2e06f994..4c19eabcd 100644 --- a/lib/widgets/common/thumbnail/image.dart +++ b/lib/widgets/common/thumbnail/image.dart @@ -88,6 +88,7 @@ class _ThumbnailImageState extends State { } void _registerWidget(ThumbnailImage widget) { + // TODO TLAD [leak] `widget.entry.visualChangeNotifier` widget.entry.visualChangeNotifier.addListener(_onVisualChanged); _initProvider(); } @@ -160,9 +161,8 @@ class _ThumbnailImageState extends State { } void _onError(Object exception, StackTrace? stackTrace) { - if (mounted) { - setState(() => _lastException = exception); - } + if (!mounted) return; + setState(() => _lastException = exception); } bool _needSizedProvider(ImageInfo? currentImageInfo) { diff --git a/lib/widgets/common/thumbnail/overlay.dart b/lib/widgets/common/thumbnail/overlay.dart index e1e2dd518..abbe77111 100644 --- a/lib/widgets/common/thumbnail/overlay.dart +++ b/lib/widgets/common/thumbnail/overlay.dart @@ -53,6 +53,12 @@ class _ThumbnailHighlightOverlayState extends State { static const startAngle = pi * -3 / 4; + @override + void dispose() { + _highlightedNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final highlightInfo = context.watch(); @@ -61,7 +67,7 @@ class _ThumbnailHighlightOverlayState extends State { builder: (context) => Container( decoration: BoxDecoration( border: Border.fromBorderSide(BorderSide( - color: Theme.of(context).colorScheme.secondary, + color: Theme.of(context).colorScheme.primary, width: context.select((t) => t.highlightBorderWidth), )), ), diff --git a/lib/widgets/common/thumbnail/scroller.dart b/lib/widgets/common/thumbnail/scroller.dart index 40dd73e6a..c0adf63d1 100644 --- a/lib/widgets/common/thumbnail/scroller.dart +++ b/lib/widgets/common/thumbnail/scroller.dart @@ -36,7 +36,7 @@ class ThumbnailScroller extends StatefulWidget { } class _ThumbnailScrollerState extends State { - final _cancellableNotifier = ValueNotifier(true); + final ValueNotifier _cancellableNotifier = ValueNotifier(true); late ScrollController _scrollController; bool _isAnimating = false, _isScrolling = false; @@ -71,6 +71,7 @@ class _ThumbnailScrollerState extends State { @override void dispose() { _unregisterWidget(widget); + _cancellableNotifier.dispose(); super.dispose(); } diff --git a/lib/widgets/common/tile_extent_controller.dart b/lib/widgets/common/tile_extent_controller.dart index baaaefc84..f7a953ee3 100644 --- a/lib/widgets/common/tile_extent_controller.dart +++ b/lib/widgets/common/tile_extent_controller.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:math'; import 'package:aves/model/settings/settings.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/widgets.dart'; @@ -26,6 +27,13 @@ class TileExtentController { required this.spacing, required this.horizontalPadding, }) { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$TileExtentController', + object: this, + ); + } // initialize extent to 0, so that it will be dynamically sized on first launch extentNotifier = ValueNotifier(0); userPreferredExtent = settings.getTileExtent(settingsRouteKey); @@ -33,6 +41,9 @@ class TileExtentController { } void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _subscriptions ..forEach((sub) => sub.cancel()) ..clear(); diff --git a/lib/widgets/debug/android_apps.dart b/lib/widgets/debug/android_apps.dart index b7f42041c..708566eda 100644 --- a/lib/widgets/debug/android_apps.dart +++ b/lib/widgets/debug/android_apps.dart @@ -26,6 +26,12 @@ class _DebugAndroidAppSectionState extends State with Au _loader = appService.getPackages(); } + @override + void dispose() { + _queryNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { super.build(context); diff --git a/lib/widgets/debug/android_codecs.dart b/lib/widgets/debug/android_codecs.dart index 054e4a7d3..dfdfe169a 100644 --- a/lib/widgets/debug/android_codecs.dart +++ b/lib/widgets/debug/android_codecs.dart @@ -23,6 +23,12 @@ class _DebugAndroidCodecSectionState extends State wit _loader = AndroidDebugService.getCodecs(); } + @override + void dispose() { + _queryNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { super.build(context); diff --git a/lib/widgets/debug/app_debug_page.dart b/lib/widgets/debug/app_debug_page.dart index ffc38373d..bd0ab2e00 100644 --- a/lib/widgets/debug/app_debug_page.dart +++ b/lib/widgets/debug/app_debug_page.dart @@ -6,43 +6,33 @@ import 'package:aves/model/filters/path.dart'; import 'package:aves/model/filters/tag.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; -import 'package:aves/services/analysis_service.dart'; import 'package:aves/theme/durations.dart'; -import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/widgets/common/basic/font_size_icon_theme.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/behaviour/pop/scope.dart'; import 'package:aves/widgets/common/behaviour/pop/tv_navigation.dart'; -import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/debug/android_apps.dart'; import 'package:aves/widgets/debug/android_codecs.dart'; import 'package:aves/widgets/debug/android_dirs.dart'; import 'package:aves/widgets/debug/app_debug_action.dart'; import 'package:aves/widgets/debug/cache.dart'; +import 'package:aves/widgets/debug/colors.dart'; import 'package:aves/widgets/debug/database.dart'; +import 'package:aves/widgets/debug/general.dart'; import 'package:aves/widgets/debug/media_store_scan_dialog.dart'; -import 'package:aves/widgets/debug/overlay.dart'; import 'package:aves/widgets/debug/report.dart'; import 'package:aves/widgets/debug/settings.dart'; import 'package:aves/widgets/debug/storage.dart'; -import 'package:aves/widgets/viewer/info/common.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:provider/provider.dart'; -class AppDebugPage extends StatefulWidget { +class AppDebugPage extends StatelessWidget { static const routeName = '/debug'; const AppDebugPage({super.key}); - @override - State createState() => _AppDebugPageState(); -} - -class _AppDebugPageState extends State { - static OverlayEntry? _taskQueueOverlayEntry; - @override Widget build(BuildContext context) { return Directionality( @@ -66,7 +56,7 @@ class _AppDebugPageState extends State { onSelected: (action) async { // wait for the popup menu to hide before proceeding with the action await Future.delayed(ADurations.popupMenuAnimation * timeDilation); - unawaited(_onActionSelected(action)); + unawaited(_onActionSelected(context, action)); }, ), ), @@ -77,16 +67,17 @@ class _AppDebugPageState extends State { child: SafeArea( child: ListView( padding: const EdgeInsets.all(8), - children: [ - _buildGeneralTabView(), - const DebugAndroidAppSection(), - const DebugAndroidCodecSection(), - const DebugAndroidDirSection(), - const DebugCacheSection(), - const DebugAppDatabaseSection(), - const DebugErrorReportingSection(), - const DebugSettingsSection(), - const DebugStorageSection(), + children: const [ + DebugGeneralSection(), + DebugAndroidAppSection(), + DebugAndroidCodecSection(), + DebugAndroidDirSection(), + DebugCacheSection(), + DebugColorSection(), + DebugAppDatabaseSection(), + DebugErrorReportingSection(), + DebugSettingsSection(), + DebugStorageSection(), ], ), ), @@ -95,83 +86,7 @@ class _AppDebugPageState extends State { ); } - Widget _buildGeneralTabView() { - final source = context.read(); - final visibleEntries = source.visibleEntries; - final catalogued = visibleEntries.where((entry) => entry.isCatalogued); - final withGps = catalogued.where((entry) => entry.hasGps); - final withAddress = withGps.where((entry) => entry.hasAddress); - final withFineAddress = withGps.where((entry) => entry.hasFineAddress); - return AvesExpansionTile( - title: 'General', - children: [ - const Padding( - padding: EdgeInsets.all(8), - child: Text('Time dilation'), - ), - Slider( - value: timeDilation, - onChanged: (v) => setState(() => timeDilation = v), - min: 1.0, - max: 10.0, - divisions: 9, - label: '$timeDilation', - ), - SwitchListTile( - value: _taskQueueOverlayEntry != null, - onChanged: (v) { - _taskQueueOverlayEntry?.remove(); - if (v) { - _taskQueueOverlayEntry = OverlayEntry( - builder: (context) => const DebugTaskQueueOverlay(), - ); - Overlay.of(context).insert(_taskQueueOverlayEntry!); - } else { - _taskQueueOverlayEntry = null; - } - setState(() {}); - }, - title: const Text('Show tasks overlay'), - ), - ElevatedButton( - onPressed: () => source.init(loadTopEntriesFirst: false), - child: const Text('Source refresh (top off)'), - ), - ElevatedButton( - onPressed: () => source.init(loadTopEntriesFirst: true), - child: const Text('Source refresh (top on)'), - ), - ElevatedButton( - onPressed: () => source.init(directory: '${androidFileUtils.dcimPath}/Camera'), - child: const Text('Source refresh (camera)'), - ), - ElevatedButton( - onPressed: () => source.init(directory: androidFileUtils.picturesPath), - child: const Text('Source refresh (pictures)'), - ), - ElevatedButton( - onPressed: () => AnalysisService.startService(force: false), - child: const Text('Start analysis service'), - ), - const Divider(), - Padding( - padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8), - child: InfoRowGroup( - info: { - 'All entries': '${source.allEntries.length}', - 'Visible entries': '${visibleEntries.length}', - 'Catalogued': '${catalogued.length}', - 'With GPS': '${withGps.length}', - 'With address': '${withAddress.length}', - 'With fine address': '${withFineAddress.length}', - }, - ), - ), - ], - ); - } - - Future _onActionSelected(AppDebugAction action) async { + Future _onActionSelected(BuildContext context, AppDebugAction action) async { switch (action) { case AppDebugAction.prepScreenshotThumbnails: // get source beforehand, as widget may be unmounted during action handling diff --git a/lib/widgets/debug/colors.dart b/lib/widgets/debug/colors.dart new file mode 100644 index 000000000..f0a6f3471 --- /dev/null +++ b/lib/widgets/debug/colors.dart @@ -0,0 +1,77 @@ +import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; +import 'package:flutter/material.dart'; + +class DebugColorSection extends StatefulWidget { + const DebugColorSection({super.key}); + + @override + State createState() => _DebugColorSectionState(); +} + +class _DebugColorSectionState extends State with AutomaticKeepAliveClientMixin { + @override + Widget build(BuildContext context) { + super.build(context); + + final scheme = Theme.of(context).colorScheme; + final List<(String, Color)> colors = [ + ('primary', scheme.primary), + ('onPrimary', scheme.onPrimary), + ('primaryContainer', scheme.primaryContainer), + ('onPrimaryContainer', scheme.onPrimaryContainer), + ('secondary', scheme.secondary), + ('onSecondary', scheme.onSecondary), + ('secondaryContainer', scheme.secondaryContainer), + ('onSecondaryContainer', scheme.onSecondaryContainer), + ('tertiary', scheme.tertiary), + ('onTertiary', scheme.onTertiary), + ('tertiaryContainer', scheme.tertiaryContainer), + ('onTertiaryContainer', scheme.onTertiaryContainer), + ('error', scheme.error), + ('onError', scheme.onError), + ('errorContainer', scheme.errorContainer), + ('onErrorContainer', scheme.onErrorContainer), + ('background', scheme.background), + ('onBackground', scheme.onBackground), + ('surface', scheme.surface), + ('onSurface', scheme.onSurface), + ('surfaceVariant', scheme.surfaceVariant), + ('onSurfaceVariant', scheme.onSurfaceVariant), + ('outline', scheme.outline), + ('outlineVariant', scheme.outlineVariant), + ('shadow', scheme.shadow), + ('scrim', scheme.scrim), + ('inverseSurface', scheme.inverseSurface), + ('onInverseSurface', scheme.onInverseSurface), + ('inversePrimary', scheme.inversePrimary), + ('surfaceTint', scheme.surfaceTint), + ]; + return AvesExpansionTile( + title: 'Colors', + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + children: colors.map((kv) { + final (text, color) = kv; + return Row( + children: [ + Text(text), + const Spacer(), + Container( + width: 20, + height: 20, + color: color, + ), + ], + ); + }).toList(), + ), + ), + ], + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/widgets/debug/database.dart b/lib/widgets/debug/database.dart index d5d2eb5cf..2e2eb39b8 100644 --- a/lib/widgets/debug/database.dart +++ b/lib/widgets/debug/database.dart @@ -38,6 +38,12 @@ class _DebugAppDatabaseSectionState extends State with _startDbReport(); } + @override + void dispose() { + _disposeLoadedContent(); + super.dispose(); + } + @override Widget build(BuildContext context) { super.build(context); @@ -63,7 +69,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => metadataDb.reset().then((_) => _startDbReport()), + onPressed: () => metadataDb.reset().then((_) => _reload()), child: const Text('Reset'), ), ], @@ -86,7 +92,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => metadataDb.clearEntries().then((_) => _startDbReport()), + onPressed: () => metadataDb.clearEntries().then((_) => _reload()), child: const Text('Clear'), ), ], @@ -107,7 +113,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => metadataDb.clearDates().then((_) => _startDbReport()), + onPressed: () => metadataDb.clearDates().then((_) => _reload()), child: const Text('Clear'), ), ], @@ -128,7 +134,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => metadataDb.clearCatalogMetadata().then((_) => _startDbReport()), + onPressed: () => metadataDb.clearCatalogMetadata().then((_) => _reload()), child: const Text('Clear'), ), ], @@ -149,7 +155,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => metadataDb.clearAddresses().then((_) => _startDbReport()), + onPressed: () => metadataDb.clearAddresses().then((_) => _reload()), child: const Text('Clear'), ), ], @@ -170,7 +176,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => metadataDb.clearTrashDetails().then((_) => _startDbReport()), + onPressed: () => metadataDb.clearTrashDetails().then((_) => _reload()), child: const Text('Clear'), ), ], @@ -191,7 +197,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => vaults.clear().then((_) => _startDbReport()), + onPressed: () => vaults.clear().then((_) => _reload()), child: const Text('Clear'), ), ], @@ -212,7 +218,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => favourites.clear().then((_) => _startDbReport()), + onPressed: () => favourites.clear().then((_) => _reload()), child: const Text('Clear'), ), ], @@ -233,7 +239,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => covers.clear().then((_) => _startDbReport()), + onPressed: () => covers.clear().then((_) => _reload()), child: const Text('Clear'), ), ], @@ -254,7 +260,7 @@ class _DebugAppDatabaseSectionState extends State with ), const SizedBox(width: 8), ElevatedButton( - onPressed: () => metadataDb.clearVideoPlayback().then((_) => _startDbReport()), + onPressed: () => metadataDb.clearVideoPlayback().then((_) => _reload()), child: const Text('Clear'), ), ], @@ -268,6 +274,11 @@ class _DebugAppDatabaseSectionState extends State with ); } + Future _reload() async { + await _disposeLoadedContent(); + _startDbReport(); + } + void _startDbReport() { _dbFileSizeLoader = metadataDb.dbFileSize(); _dbEntryLoader = metadataDb.loadEntries(); @@ -281,6 +292,10 @@ class _DebugAppDatabaseSectionState extends State with _dbVideoPlaybackLoader = metadataDb.loadAllVideoPlayback(); setState(() {}); } + + Future _disposeLoadedContent() async { + (await _dbEntryLoader).forEach((v) => v.dispose()); + } @override bool get wantKeepAlive => true; diff --git a/lib/widgets/debug/general.dart b/lib/widgets/debug/general.dart new file mode 100644 index 000000000..a85913b0b --- /dev/null +++ b/lib/widgets/debug/general.dart @@ -0,0 +1,113 @@ +import 'package:aves/model/source/collection_source.dart'; +import 'package:aves/services/analysis_service.dart'; +import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; +import 'package:aves/widgets/debug/overlay.dart'; +import 'package:aves/widgets/viewer/info/common.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:leak_tracker/leak_tracker.dart'; +import 'package:provider/provider.dart'; + +class DebugGeneralSection extends StatefulWidget { + const DebugGeneralSection({super.key}); + + @override + State createState() => _DebugGeneralSectionState(); +} + +class _DebugGeneralSectionState extends State with AutomaticKeepAliveClientMixin { + static OverlayEntry? _taskQueueOverlayEntry; + + @override + Widget build(BuildContext context) { + super.build(context); + + final source = context.read(); + final visibleEntries = source.visibleEntries; + final catalogued = visibleEntries.where((entry) => entry.isCatalogued); + final withGps = catalogued.where((entry) => entry.hasGps); + final withAddress = withGps.where((entry) => entry.hasAddress); + final withFineAddress = withGps.where((entry) => entry.hasFineAddress); + return AvesExpansionTile( + title: 'General', + children: [ + const Padding( + padding: EdgeInsets.all(8), + child: Text('Time dilation'), + ), + Slider( + value: timeDilation, + onChanged: (v) => setState(() => timeDilation = v), + min: 1.0, + max: 10.0, + divisions: 9, + label: '$timeDilation', + ), + SwitchListTile( + value: _taskQueueOverlayEntry != null, + onChanged: (v) { + final overlayEntry = _taskQueueOverlayEntry; + _taskQueueOverlayEntry = null; + if (overlayEntry != null) { + overlayEntry.remove(); + overlayEntry.dispose(); + } + if (v) { + _taskQueueOverlayEntry = OverlayEntry( + builder: (context) => const DebugTaskQueueOverlay(), + ); + Overlay.of(context).insert(_taskQueueOverlayEntry!); + } + setState(() {}); + }, + title: const Text('Show tasks overlay'), + ), + ElevatedButton( + onPressed: () => LeakTracking.collectLeaks().then((leaks) { + leaks.byType.forEach((type, reports) { + debugPrint('* leak type=$type'); + groupBy(reports, (report) => report.type).forEach((reportType, typedReports) { + debugPrint(' * report type=$reportType'); + groupBy(typedReports, (report) => report.trackedClass).forEach((trackedClass, classedReports) { + debugPrint(' trackedClass=$trackedClass reports=${classedReports.length}'); + classedReports.forEach((report) { + final phase = report.phase; + final retainingPath = report.retainingPath; + final detailedPath = report.detailedPath; + final context = report.context; + if (phase != null || retainingPath != null || detailedPath != null || context != null) { + debugPrint(' phase=$phase retainingPath=$retainingPath detailedPath=$detailedPath context=$context'); + } + }); + }); + }); + }); + }), + child: const Text('Collect leaks'), + ), + ElevatedButton( + onPressed: () => AnalysisService.startService(force: false), + child: const Text('Start analysis service'), + ), + const Divider(), + Padding( + padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8), + child: InfoRowGroup( + info: { + 'All entries': '${source.allEntries.length}', + 'Visible entries': '${visibleEntries.length}', + 'Catalogued': '${catalogued.length}', + 'With GPS': '${withGps.length}', + 'With address': '${withAddress.length}', + 'With fine address': '${withFineAddress.length}', + }, + ), + ), + ], + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/widgets/debug/report.dart b/lib/widgets/debug/report.dart index c59676c94..4a7e72d4b 100644 --- a/lib/widgets/debug/report.dart +++ b/lib/widgets/debug/report.dart @@ -4,11 +4,18 @@ import 'package:aves/widgets/common/identity/aves_expansion_tile.dart'; import 'package:aves/widgets/viewer/info/common.dart'; import 'package:flutter/material.dart'; -class DebugErrorReportingSection extends StatelessWidget { +class DebugErrorReportingSection extends StatefulWidget { const DebugErrorReportingSection({super.key}); + @override + State createState() => _DebugErrorReportingSectionState(); +} + +class _DebugErrorReportingSectionState extends State with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { + super.build(context); + return AvesExpansionTile( title: 'Reporting', children: [ @@ -56,4 +63,7 @@ class DebugErrorReportingSection extends StatelessWidget { ], ); } + + @override + bool get wantKeepAlive => true; } diff --git a/lib/widgets/debug/settings.dart b/lib/widgets/debug/settings.dart index cbc87137c..99dbf7913 100644 --- a/lib/widgets/debug/settings.dart +++ b/lib/widgets/debug/settings.dart @@ -9,11 +9,18 @@ import 'package:aves/widgets/viewer/info/common.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class DebugSettingsSection extends StatelessWidget { +class DebugSettingsSection extends StatefulWidget { const DebugSettingsSection({super.key}); + @override + State createState() => _DebugSettingsSectionState(); +} + +class _DebugSettingsSectionState extends State with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { + super.build(context); + return Consumer( builder: (context, settings, child) { String toMultiline(Iterable? l) => l != null && l.isNotEmpty ? '\n${l.join('\n')}' : '$l'; @@ -76,4 +83,7 @@ class DebugSettingsSection extends StatelessWidget { }, ); } + + @override + bool get wantKeepAlive => true; } diff --git a/lib/widgets/dialogs/add_shortcut_dialog.dart b/lib/widgets/dialogs/add_shortcut_dialog.dart index 308a2d1a1..88af929c3 100644 --- a/lib/widgets/dialogs/add_shortcut_dialog.dart +++ b/lib/widgets/dialogs/add_shortcut_dialog.dart @@ -50,6 +50,7 @@ class _AddShortcutDialogState extends State { @override void dispose() { _nameController.dispose(); + _isValidNotifier.dispose(); super.dispose(); } diff --git a/lib/widgets/dialogs/aves_confirmation_dialog.dart b/lib/widgets/dialogs/aves_confirmation_dialog.dart index dd9f4b312..6e1a61a13 100644 --- a/lib/widgets/dialogs/aves_confirmation_dialog.dart +++ b/lib/widgets/dialogs/aves_confirmation_dialog.dart @@ -119,7 +119,13 @@ class _SkippableConfirmationDialog extends StatefulWidget { } class _SkippableConfirmationDialogState extends State<_SkippableConfirmationDialog> { - final ValueNotifier _skip = ValueNotifier(false); + final ValueNotifier _skipNotifier = ValueNotifier(false); + + @override + void dispose() { + _skipNotifier.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { @@ -127,10 +133,10 @@ class _SkippableConfirmationDialogState extends State<_SkippableConfirmationDial scrollableContent: [ ...widget.delegate.build(context), ValueListenableBuilder( - valueListenable: _skip, + valueListenable: _skipNotifier, builder: (context, flag, child) => SwitchListTile( value: flag, - onChanged: (v) => _skip.value = v, + onChanged: (v) => _skipNotifier.value = v, title: Text(context.l10n.doNotAskAgain), ), ), @@ -139,7 +145,7 @@ class _SkippableConfirmationDialogState extends State<_SkippableConfirmationDial const CancelButton(), TextButton( onPressed: () { - if (_skip.value) { + if (_skipNotifier.value) { _skipConfirmation(widget.type); } Navigator.maybeOf(context)?.pop(true); diff --git a/lib/widgets/dialogs/aves_dialog.dart b/lib/widgets/dialogs/aves_dialog.dart index 8127a61d1..04d68cd9e 100644 --- a/lib/widgets/dialogs/aves_dialog.dart +++ b/lib/widgets/dialogs/aves_dialog.dart @@ -1,5 +1,3 @@ -import 'dart:ui'; - import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/material.dart'; @@ -144,10 +142,6 @@ class DialogTitle extends StatelessWidget { decoration: AvesDialog.contentDecoration(context), child: Text( title, - style: const TextStyle( - fontWeight: FontWeight.normal, - fontFeatures: [FontFeature.enable('smcp')], - ), textAlign: TextAlign.center, ), ); diff --git a/lib/widgets/dialogs/cast_dialog.dart b/lib/widgets/dialogs/cast_dialog.dart new file mode 100644 index 000000000..3b7ba6058 --- /dev/null +++ b/lib/widgets/dialogs/cast_dialog.dart @@ -0,0 +1,63 @@ +import 'package:aves/ref/upnp.dart'; +import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/dialogs/aves_dialog.dart'; +import 'package:dlna_dart/dlna.dart'; +import 'package:flutter/material.dart'; + +class CastDialog extends StatefulWidget { + static const routeName = '/dialog/cast'; + + const CastDialog({super.key}); + + @override + State createState() => _CastDialogState(); +} + +class _CastDialogState extends State { + final DLNAManager _dlnaManager = DLNAManager(); + final Map _seenRenderers = {}; + + @override + void initState() { + super.initState(); + + _dlnaManager.start().then((deviceManager) { + deviceManager.devices.stream.listen((devices) { + _seenRenderers.addAll(Map.fromEntries(devices.entries.where((kv) => kv.value.info.deviceType == Upnp.upnpDeviceTypeMediaRenderer).map((kv) { + final device = kv.value; + return MapEntry(device.info.friendlyName, device); + }))); + setState(() {}); + }); + }); + } + + @override + void dispose() { + _dlnaManager.stop(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AvesDialog( + title: context.l10n.castDialogTitle, + scrollableContent: [ + if (_seenRenderers.isEmpty) + const Padding( + padding: EdgeInsets.all(16), + child: Center( + child: CircularProgressIndicator(), + ), + ), + ..._seenRenderers.values.map((dev) => ListTile( + title: Text(dev.info.friendlyName), + onTap: () => Navigator.maybeOf(context)?.pop(dev), + )), + ], + actions: const [ + CancelButton(), + ], + ); + } +} diff --git a/lib/widgets/dialogs/convert_entry_dialog.dart b/lib/widgets/dialogs/convert_entry_dialog.dart index fe9bae4a0..10fd78821 100644 --- a/lib/widgets/dialogs/convert_entry_dialog.dart +++ b/lib/widgets/dialogs/convert_entry_dialog.dart @@ -88,6 +88,8 @@ class _ConvertEntryDialogState extends State { void dispose() { _widthController.dispose(); _heightController.dispose(); + _isValidNotifier.dispose(); + _mimeTypeNotifier.dispose(); super.dispose(); } @@ -95,14 +97,14 @@ class _ConvertEntryDialogState extends State { Widget build(BuildContext context) { final l10n = context.l10n; const contentHorizontalPadding = EdgeInsets.symmetric(horizontal: AvesDialog.defaultHorizontalContentPadding); - final theme = Theme.of(context); - final trailingStyle = TextStyle(color: theme.textTheme.bodySmall!.color); - final trailingChangeShadowColor = theme.colorScheme.onPrimary; + final colorScheme = Theme.of(context).colorScheme; + final trailingStyle = TextStyle(color: colorScheme.onSurfaceVariant); + final trailingChangeShadowColor = colorScheme.onPrimary; // used by the drop down to match input decoration final textFieldDecorationBorder = Border( bottom: BorderSide( - color: Theme.of(context).colorScheme.onSurface.withOpacity(0.38), //Color(0xFFBDBDBD), + color: colorScheme.onSurface.withOpacity(0.38), //Color(0xFFBDBDBD), width: 1.0, ), ); diff --git a/lib/widgets/dialogs/duration_dialog.dart b/lib/widgets/dialogs/duration_dialog.dart index b6a6b920b..22190f831 100644 --- a/lib/widgets/dialogs/duration_dialog.dart +++ b/lib/widgets/dialogs/duration_dialog.dart @@ -28,6 +28,13 @@ class _DurationDialogState extends State { _seconds = ValueNotifier(seconds % secondsInMinute); } + @override + void dispose() { + _minutes.dispose(); + _seconds.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return MediaQueryDataProvider( diff --git a/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart index bd40664d6..e2eb254f9 100644 --- a/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_date_dialog.dart @@ -60,6 +60,9 @@ class _EditEntryDateDialogState extends State { @override void dispose() { _isValidNotifier.dispose(); + _shiftHour.dispose(); + _shiftMinute.dispose(); + _shiftSign.dispose(); super.dispose(); } @@ -298,6 +301,7 @@ class _EditEntryDateDialogState extends State { initialDate: _customDateTime, firstDate: DateTime(0), lastDate: DateTime(2100), + cancelText: MaterialLocalizations.of(context).cancelButtonLabel.toUpperCase(), confirmText: context.l10n.nextButtonLabel, ); if (_date == null) return; @@ -305,6 +309,7 @@ class _EditEntryDateDialogState extends State { final _time = await showTimePicker( context: context, initialTime: TimeOfDay.fromDateTime(_customDateTime), + cancelText: MaterialLocalizations.of(context).cancelButtonLabel.toUpperCase(), ); if (_time == null) return; diff --git a/lib/widgets/dialogs/entry_editors/edit_description_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_description_dialog.dart index 239885316..60c77b9e2 100644 --- a/lib/widgets/dialogs/entry_editors/edit_description_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_description_dialog.dart @@ -34,6 +34,13 @@ class _EditEntryTitleDescriptionDialogState extends State { void dispose() { _latitudeController.dispose(); _longitudeController.dispose(); + _isValidNotifier.dispose(); super.dispose(); } @@ -280,7 +281,7 @@ class _EditEntryLocationDialogState extends State { return Text( l10n.viewerInfoUnknown, style: TextStyle( - color: Theme.of(context).textTheme.bodySmall!.color, + color: Theme.of(context).colorScheme.onSurfaceVariant, ), ); } diff --git a/lib/widgets/dialogs/entry_editors/remove_metadata_dialog.dart b/lib/widgets/dialogs/entry_editors/remove_metadata_dialog.dart index 18bff131c..272b9576b 100644 --- a/lib/widgets/dialogs/entry_editors/remove_metadata_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/remove_metadata_dialog.dart @@ -45,6 +45,12 @@ class _RemoveEntryMetadataDialogState extends State { _validate(); } + @override + void dispose() { + _isValidNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final l10n = context.l10n; @@ -107,7 +113,7 @@ class _RemoveEntryMetadataDialogState extends State { ), ), ], - outlineColor: Theme.of(context).scaffoldBackgroundColor, + outlineColor: Theme.of(context).colorScheme.background, ); if (context.select((v) => v.themeColorMode == AvesThemeColorMode.polychrome)) { final colors = context.watch(); diff --git a/lib/widgets/dialogs/entry_editors/rename_entry_dialog.dart b/lib/widgets/dialogs/entry_editors/rename_entry_dialog.dart index c524323bb..05d4f89f1 100644 --- a/lib/widgets/dialogs/entry_editors/rename_entry_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/rename_entry_dialog.dart @@ -37,6 +37,7 @@ class _RenameEntryDialogState extends State { @override void dispose() { _nameController.dispose(); + _isValidNotifier.dispose(); super.dispose(); } diff --git a/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart b/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart index 20be3112c..6e55b82c2 100644 --- a/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart +++ b/lib/widgets/dialogs/entry_editors/rename_entry_set_page.dart @@ -52,15 +52,16 @@ class _RenameEntrySetPageState extends State { @override void dispose() { - _patternTextController.removeListener(_onUserPatternChanged); + _patternTextController.dispose(); + _namingPatternNotifier.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final l10n = context.l10n; - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final effectiveThumbnailExtent = max(thumbnailExtent, thumbnailExtent * textScaleFactor); + final textScaler = MediaQuery.textScalerOf(context); + final effectiveThumbnailExtent = max(thumbnailExtent, textScaler.scale(thumbnailExtent)); return AvesScaffold( appBar: AppBar( title: Text(l10n.renameEntrySetPageTitle), @@ -145,7 +146,7 @@ class _RenameEntrySetPageState extends State { children: [ Text( sourceName, - style: TextStyle(color: Theme.of(context).textTheme.bodySmall!.color), + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), softWrap: false, overflow: TextOverflow.fade, maxLines: 1, diff --git a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart index 885bf0fba..4f3892aa5 100644 --- a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart +++ b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart @@ -6,6 +6,7 @@ import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/expandable_filter_row.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; @@ -36,7 +37,6 @@ class _TagEditorPageState extends State { late final List _topTags; final List _userAddedFilters = []; - static const Color untaggedColor = Colors.blueGrey; static final List _placeholders = [ PlaceholderFilter.country, PlaceholderFilter.state, @@ -54,6 +54,7 @@ class _TagEditorPageState extends State { @override void dispose() { + _newTagTextController.dispose(); _newTagTextFocusNode.dispose(); _expandedSectionNotifier.dispose(); super.dispose(); @@ -69,10 +70,14 @@ class _TagEditorPageState extends State { }); List> sortedTags = _sortCurrentTags(entryCountByTag); - return WillPopScope( - onWillPop: () async { - if (!_isModified) return true; + final untaggedColor = Themes.backgroundTextColor(context); + return PopScope( + canPop: !_isModified, + onPopInvoked: (didPop) async { + if (didPop) return; + + final NavigatorState navigator = Navigator.of(context); final confirmed = await showDialog( context: context, builder: (context) => AvesDialog( @@ -88,7 +93,9 @@ class _TagEditorPageState extends State { routeSettings: const RouteSettings(name: AvesDialog.warningRouteName), ) ?? false; - return confirmed; + if (confirmed) { + navigator.pop(); + } }, child: AvesScaffold( appBar: AppBar( @@ -104,6 +111,7 @@ class _TagEditorPageState extends State { onPressed: () => Navigator.maybeOf(context)?.pop(filtersByEntry), tooltip: l10n.saveTooltip, ), + const SizedBox(width: 16), ], ), body: SafeArea( @@ -175,7 +183,7 @@ class _TagEditorPageState extends State { const SizedBox(width: 8), Text( l10n.filterNoTagLabel, - style: const TextStyle(color: untaggedColor), + style: TextStyle(color: untaggedColor), ), ], ), diff --git a/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart b/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart index 57d093628..213c177f5 100644 --- a/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart @@ -57,8 +57,7 @@ class _CoverSelectionDialogState extends State { static const double appPickerExtent = 32; double tabBarHeight(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - return 64 * max(1, textScaleFactor); + return MediaQuery.textScalerOf(context).clamp(minScaleFactor: 1).scale(64); } static const double tabIndicatorWeight = 2; @@ -401,8 +400,8 @@ class _CoverSelectionDialogState extends State { }) { // cannot use `IconTheme` over `TabBar` to change size, // because `TabBar` does so internally - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final iconSize = IconTheme.of(context).size! * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + final iconSize = textScaler.scale(IconTheme.of(context).size!); return Tab( key: key, height: tabBarHeight(context), @@ -437,12 +436,13 @@ class _CoverSelectionDialogState extends State { l10n.setCoverDialogCustom, }.fold('', (previousValue, element) => '$previousValue\n$element'); - final para = RenderParagraph( + final paragraph = RenderParagraph( TextSpan(text: _optionLines, style: Theme.of(context).textTheme.titleMedium!), textDirection: TextDirection.ltr, - textScaleFactor: MediaQuery.textScaleFactorOf(context), + textScaler: MediaQuery.textScalerOf(context), )..layout(const BoxConstraints(), parentUsesSize: true); - final textWidth = para.getMaxIntrinsicWidth(double.infinity); + final textWidth = paragraph.getMaxIntrinsicWidth(double.infinity); + paragraph.dispose(); // from `RadioListTile` layout const contentPadding = 32; diff --git a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart b/lib/widgets/dialogs/filter_editors/create_album_dialog.dart index 20bb2a2ea..9943eab7c 100644 --- a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/create_album_dialog.dart @@ -39,6 +39,7 @@ class _CreateAlbumDialogState extends State { @override void dispose() { + _scrollController.dispose(); _nameController.dispose(); _nameFieldFocusNode.removeListener(_onFocus); _nameFieldFocusNode.dispose(); diff --git a/lib/widgets/dialogs/filter_editors/password_dialog.dart b/lib/widgets/dialogs/filter_editors/password_dialog.dart index 08f4864e8..72f633131 100644 --- a/lib/widgets/dialogs/filter_editors/password_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/password_dialog.dart @@ -28,6 +28,13 @@ class _PasswordDialogState extends State { _focusNode.requestFocus(); } + @override + void dispose() { + _controller.dispose(); + _focusNode.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return AvesDialog( diff --git a/lib/widgets/dialogs/filter_editors/pattern_dialog.dart b/lib/widgets/dialogs/filter_editors/pattern_dialog.dart index d027af570..4518c75db 100644 --- a/lib/widgets/dialogs/filter_editors/pattern_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/pattern_dialog.dart @@ -35,7 +35,7 @@ class _PatternDialogState extends State { dimension: MediaQuery.sizeOf(context).shortestSide / 2, child: PatternLock( relativePadding: .4, - selectedColor: colorScheme.secondary, + selectedColor: colorScheme.primary, notSelectedColor: colorScheme.onBackground, pointRadius: 8, fillPoints: true, diff --git a/lib/widgets/dialogs/filter_editors/pin_dialog.dart b/lib/widgets/dialogs/filter_editors/pin_dialog.dart index 52098c5b7..04b9d206b 100644 --- a/lib/widgets/dialogs/filter_editors/pin_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/pin_dialog.dart @@ -22,6 +22,12 @@ class _PinDialogState extends State { bool _confirming = false; String? _firstPin; + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; @@ -46,8 +52,8 @@ class _PinDialogState extends State { pinTheme: PinTheme( activeColor: colorScheme.onBackground, inactiveColor: colorScheme.onBackground, - selectedColor: colorScheme.secondary, - selectedFillColor: colorScheme.secondary, + selectedColor: colorScheme.primary, + selectedFillColor: colorScheme.primary, borderRadius: BorderRadius.circular(8), shape: PinCodeFieldShape.box, ), diff --git a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart index c2055cb6d..259c40606 100644 --- a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart @@ -89,6 +89,12 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { ChipSetAction.createVault, ]; + @override + void dispose() { + _appBarHeightNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return ListenableProvider>.value( diff --git a/lib/widgets/dialogs/pick_dialogs/app_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/app_pick_page.dart index 997388968..f38d386a9 100644 --- a/lib/widgets/dialogs/pick_dialogs/app_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/app_pick_page.dart @@ -37,6 +37,12 @@ class _AppPickPageState extends State { _loader = appService.getPackages(); } + @override + void dispose() { + _queryNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final useTvLayout = settings.useTvLayout; diff --git a/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart index 452f3e9d2..82f928955 100644 --- a/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/location_pick_page.dart @@ -96,8 +96,10 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin _subscriptions ..forEach((sub) => sub.cancel()) ..clear(); - _dotLocationNotifier.removeListener(_updateLocationInfo); _mapController.dispose(); + _isPageAnimatingNotifier.dispose(); + _dotLocationNotifier.dispose(); + _infoLocationNotifier.dispose(); super.dispose(); } @@ -218,8 +220,8 @@ class _LocationInfo extends StatelessWidget { } static double getIconSize(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - return 16 * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + return textScaler.scale(16); } } @@ -251,9 +253,15 @@ class _AddressRowState extends State<_AddressRow> { } } + @override + void dispose() { + _addressLineNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); return Row( mainAxisSize: MainAxisSize.min, children: [ @@ -266,7 +274,7 @@ class _AddressRowState extends State<_AddressRow> { // addresses can include non-latin scripts with inconsistent line height, // which is especially an issue for relayout/painting of heavy Google map, // so we give extra height to give breathing room to the text and stabilize layout - height: Theme.of(context).textTheme.bodyMedium!.fontSize! * textScaleFactor * 2, + height: textScaler.scale(Theme.of(context).textTheme.bodyMedium!.fontSize!) * 2, child: ValueListenableBuilder( valueListenable: _addressLineNotifier, builder: (context, addressLine, child) { @@ -288,7 +296,8 @@ class _AddressRowState extends State<_AddressRow> { Future _updateAddress() async { final location = widget.location; final addressLine = await _getAddressLine(location); - if (mounted && location == widget.location) { + if (!mounted) return; + if (location == widget.location) { _addressLineNotifier.value = addressLine; } } diff --git a/lib/widgets/dialogs/tile_view_dialog.dart b/lib/widgets/dialogs/tile_view_dialog.dart index 2c0af6253..b0111b67c 100644 --- a/lib/widgets/dialogs/tile_view_dialog.dart +++ b/lib/widgets/dialogs/tile_view_dialog.dart @@ -73,6 +73,12 @@ class _TileViewDialogState extends State> with _columnMax = columnRange.$2; } + @override + void dispose() { + _columnCountNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final l10n = context.l10n; @@ -194,8 +200,8 @@ class _TileViewDialogState extends State> with dropdownColor: Themes.thirdLayerColor(context), ); - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final iconSize = IconTheme.of(context).size! * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + final iconSize = textScaler.scale(IconTheme.of(context).size!); final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; final child = isPortrait ? Column( diff --git a/lib/widgets/editor/control_panel.dart b/lib/widgets/editor/control_panel.dart index 2a3dc2711..531fd2848 100644 --- a/lib/widgets/editor/control_panel.dart +++ b/lib/widgets/editor/control_panel.dart @@ -8,7 +8,6 @@ import 'package:aves/widgets/editor/transform/control_panel.dart'; import 'package:aves/widgets/editor/transform/controller.dart'; import 'package:aves/widgets/viewer/overlay/viewer_buttons.dart'; import 'package:aves_model/aves_model.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -29,13 +28,12 @@ class EditorControlPanel extends StatelessWidget { @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () { - if (actionNotifier.value != null) { - _cancelAction(context); - return SynchronousFuture(false); - } - return SynchronousFuture(true); + return PopScope( + canPop: actionNotifier.value == null, + onPopInvoked: (didPop) { + if (didPop) return; + + _cancelAction(context); }, child: Padding( padding: const EdgeInsets.all(padding), diff --git a/lib/widgets/editor/transform/controller.dart b/lib/widgets/editor/transform/controller.dart index 0c414a326..b13ae269d 100644 --- a/lib/widgets/editor/transform/controller.dart +++ b/lib/widgets/editor/transform/controller.dart @@ -33,11 +33,21 @@ class TransformController { final Size displaySize; TransformController(this.displaySize) { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$TransformController', + object: this, + ); + } reset(); aspectRatioNotifier.addListener(_onAspectRatioChanged); } void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } aspectRatioNotifier.dispose(); } diff --git a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart index daf10d73d..d51281ac8 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart @@ -11,6 +11,7 @@ import 'package:aves/model/source/collection_source.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/durations.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/view/view.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/action_mixins/permission_aware.dart'; @@ -255,7 +256,6 @@ abstract class ChipSetActionDelegate with FeedbackMi builder: (context) => MapPage(collection: mapCollection), ), ); - mapCollection.dispose(); } void _goToSlideshow(BuildContext context, Set filters) { @@ -293,6 +293,7 @@ abstract class ChipSetActionDelegate with FeedbackMi SearchPageRoute( delegate: CollectionSearchDelegate( searchFieldLabel: context.l10n.searchCollectionFieldHint, + searchFieldStyle: Themes.searchFieldStyle(context), source: context.read(), ), ), diff --git a/lib/widgets/filter_grids/common/app_bar.dart b/lib/widgets/filter_grids/common/app_bar.dart index d68cc0878..c0a610de0 100644 --- a/lib/widgets/filter_grids/common/app_bar.dart +++ b/lib/widgets/filter_grids/common/app_bar.dart @@ -7,6 +7,7 @@ import 'package:aves/model/selection.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/durations.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/view/view.dart'; import 'package:aves/widgets/common/action_controls/togglers/title_search.dart'; import 'package:aves/widgets/common/app_bar/app_bar_subtitle.dart'; @@ -111,7 +112,6 @@ class _FilterGridAppBarState().enabled) { - height += FilterQueryBar.getPreferredHeight(textScaleFactor); + height += FilterQueryBar.getPreferredHeight(textScaler); } return height; } @@ -447,6 +447,7 @@ class _FilterGridAppBarState extends StatelessWidget { this.heroType = HeroType.onTap, }) : thumbnailExtent = thumbnailExtent ?? extent; - static double tileHeight({required double extent, required double textScaleFactor, required bool showText}) { - return extent + infoHeight(extent: extent, textScaleFactor: textScaleFactor, showText: showText); + static double tileHeight({required double extent, required TextScaler textScaler, required bool showText}) { + return extent + infoHeight(extent: extent, textScaler: textScaler, showText: showText); } // info includes title and content details - static double infoHeight({required double extent, required double textScaleFactor, required bool showText}) { + static double infoHeight({required double extent, required TextScaler textScaler, required bool showText}) { if (!showText) return 0; // height can actually be a little larger or smaller, when info includes icons or non-latin scripts // but it's not worth measuring text metrics, as the widget is flexible enough to absorb the difference - return (AvesFilterChip.fontSize + detailFontSize(extent) + 4) * textScaleFactor + AvesFilterChip.decoratedContentVerticalPadding * 2; + return textScaler.scale(AvesFilterChip.fontSize + detailFontSize(extent) + 4) + AvesFilterChip.decoratedContentVerticalPadding * 2; } static Radius radius(double extent) => Radius.circular(min(AvesFilterChip.defaultRadius, extent / 4)); @@ -68,26 +68,26 @@ class CoveredFilterChip extends StatelessWidget { stream: covers.entryChangeStream.where((event) => event == null || event.contains(filter)), builder: (context, snapshot) => Consumer( builder: (context, source, child) { - switch (T) { - case AlbumFilter: + switch (filter) { + case AlbumFilter filter: { - final album = (filter as AlbumFilter).album; + final album = filter.album; return StreamBuilder( stream: source.eventBus.on().where((event) => event.directories == null || event.directories!.contains(album)), builder: (context, snapshot) => _buildChip(context, source), ); } - case LocationFilter: + case LocationFilter filter: { - final countryCode = (filter as LocationFilter).code; + final countryCode = filter.code; return StreamBuilder( stream: source.eventBus.on().where((event) => event.countryCodes == null || event.countryCodes!.contains(countryCode)), builder: (context, snapshot) => _buildChip(context, source), ); } - case TagFilter: + case TagFilter filter: { - final tag = (filter as TagFilter).tag; + final tag = filter.tag; return StreamBuilder( stream: source.eventBus.on().where((event) => event.tags == null || event.tags!.contains(tag)), builder: (context, snapshot) => _buildChip(context, source), @@ -111,7 +111,7 @@ class CoveredFilterChip extends StatelessWidget { // album filters themselves do not change, but decoration derived from it does chipKey = ValueKey(appInventory.areAppNamesReadyNotifier.value); } - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); return AvesFilterChip( key: chipKey, filter: _filter, @@ -123,7 +123,7 @@ class CoveredFilterChip extends StatelessWidget { padding: EdgeInsets.only( bottom: infoHeight( extent: extent, - textScaleFactor: textScaleFactor, + textScaler: textScaler, showText: showText, ), ), @@ -168,7 +168,7 @@ class CoveredFilterChip extends StatelessWidget { ); } - Color _detailColor(BuildContext context) => Theme.of(context).textTheme.bodySmall!.color!; + Color _detailColor(BuildContext context) => Theme.of(context).colorScheme.onSurfaceVariant; Widget _buildDetails(BuildContext context, CollectionSource source, T filter) { final locale = context.l10n.localeName; diff --git a/lib/widgets/filter_grids/common/filter_grid_page.dart b/lib/widgets/filter_grids/common/filter_grid_page.dart index abc85db03..c6ea9775e 100644 --- a/lib/widgets/filter_grids/common/filter_grid_page.dart +++ b/lib/widgets/filter_grids/common/filter_grid_page.dart @@ -324,10 +324,10 @@ class _FilterGridContentState extends State<_FilterG final target = context.read().staggeredAnimationPageTarget; final tileAnimationDelay = context.read().getTileAnimationDelay(target); - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); final tileHeight = CoveredFilterChip.tileHeight( extent: thumbnailExtent, - textScaleFactor: textScaleFactor, + textScaler: textScaler, showText: tileLayout != TileLayout.list, ); return GridTheme( @@ -566,13 +566,13 @@ class _FilterScaler extends StatelessWidget { @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); final (tileSpacing, horizontalPadding) = context.select((v) => (v.spacing, v.horizontalPadding)); final brightness = Theme.of(context).brightness; return GridScaleGestureDetector>( scrollableKey: scrollableKey, tileLayout: tileLayout, - heightForWidth: (width) => CoveredFilterChip.tileHeight(extent: width, textScaleFactor: textScaleFactor, showText: true), + heightForWidth: (width) => CoveredFilterChip.tileHeight(extent: width, textScaler: textScaler, showText: true), gridBuilder: (center, tileSize, child) => CustomPaint( painter: FixedExtentGridPainter( tileLayout: tileLayout, diff --git a/lib/widgets/filter_grids/common/filter_nav_page.dart b/lib/widgets/filter_grids/common/filter_nav_page.dart index 864bef00d..92463e2d2 100644 --- a/lib/widgets/filter_grids/common/filter_nav_page.dart +++ b/lib/widgets/filter_grids/common/filter_nav_page.dart @@ -99,6 +99,12 @@ class FilterNavigationPage> extends State> { final ValueNotifier _appBarHeightNotifier = ValueNotifier(0); + @override + void dispose() { + _appBarHeightNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return SelectionProvider>( diff --git a/lib/widgets/filter_grids/common/list_details.dart b/lib/widgets/filter_grids/common/list_details.dart index 872fef703..ea43c32ea 100644 --- a/lib/widgets/filter_grids/common/list_details.dart +++ b/lib/widgets/filter_grids/common/list_details.dart @@ -70,9 +70,9 @@ class FilterListDetails extends StatelessWidget { softWrap: false, overflow: detailsTheme.titleMaxLines == 1 ? TextOverflow.fade : TextOverflow.ellipsis, maxLines: detailsTheme.titleMaxLines, - // `textScaleFactor` is applied to font size and icon size at the theme level, + // `textScaler` is applied to font size and icon size at the theme level, // otherwise the leading icon will be low-res scaled up/down - textScaleFactor: 1, + textScaler: TextScaler.noScaling, ), if (!locked) ...[ const SizedBox(height: FilterListDetailsTheme.titleDetailPadding), diff --git a/lib/widgets/filter_grids/common/list_details_theme.dart b/lib/widgets/filter_grids/common/list_details_theme.dart index 4176ff615..049f91c73 100644 --- a/lib/widgets/filter_grids/common/list_details_theme.dart +++ b/lib/widgets/filter_grids/common/list_details_theme.dart @@ -30,28 +30,30 @@ class FilterListDetailsTheme extends StatelessWidget { final locale = context.l10n.localeName; final use24hour = mq.alwaysUse24HourFormat; - final textScaleFactor = mq.textScaleFactor; + final textScaler = mq.textScaler; final textTheme = Theme.of(context).textTheme; final titleStyleBase = textTheme.bodyMedium!; - final titleStyle = titleStyleBase.copyWith(fontSize: titleStyleBase.fontSize! * textScaleFactor); + final titleStyle = titleStyleBase.copyWith(fontSize: textScaler.scale(titleStyleBase.fontSize!)); final captionStyle = textTheme.bodySmall!; - final titleIconSize = AvesFilterChip.iconSize * textScaleFactor; - final titleLineHeight = (RenderParagraph( + final titleIconSize = textScaler.scale(AvesFilterChip.iconSize); + final titleLineHeightParagraph = RenderParagraph( TextSpan(text: 'Fake Title', style: titleStyle), textDirection: TextDirection.ltr, - textScaleFactor: textScaleFactor, - )..layout(const BoxConstraints(), parentUsesSize: true)) - .getMaxIntrinsicHeight(double.infinity); + textScaler: textScaler, + )..layout(const BoxConstraints(), parentUsesSize: true); + final titleLineHeight = titleLineHeightParagraph.getMaxIntrinsicHeight(double.infinity); + titleLineHeightParagraph.dispose(); - final captionLineHeight = (RenderParagraph( + final captionLineHeightParagraph = RenderParagraph( TextSpan(text: formatDateTime(DateTime.now(), locale, use24hour), style: captionStyle), textDirection: TextDirection.ltr, - textScaleFactor: textScaleFactor, + textScaler: textScaler, strutStyle: AStyles.overflowStrut, - )..layout(const BoxConstraints(), parentUsesSize: true)) - .getMaxIntrinsicHeight(double.infinity); + )..layout(const BoxConstraints(), parentUsesSize: true); + final captionLineHeight = captionLineHeightParagraph.getMaxIntrinsicHeight(double.infinity); + captionLineHeightParagraph.dispose(); var titleMaxLines = 1; var showCount = false; @@ -79,7 +81,7 @@ class FilterListDetailsTheme extends StatelessWidget { titleIconSize: titleIconSize, captionIconTheme: IconThemeData( color: captionStyle.color, - size: captionStyle.fontSize! * textScaleFactor, + size: textScaler.scale(captionStyle.fontSize!), ), ); }, diff --git a/lib/widgets/filter_grids/common/overlay.dart b/lib/widgets/filter_grids/common/overlay.dart index 1e479a9d8..c2fb1f0fb 100644 --- a/lib/widgets/filter_grids/common/overlay.dart +++ b/lib/widgets/filter_grids/common/overlay.dart @@ -27,6 +27,12 @@ class _ChipHighlightOverlayState extends State { CollectionFilter get filter => widget.filter; + @override + void dispose() { + _highlightedNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final highlightInfo = context.watch(); @@ -35,7 +41,7 @@ class _ChipHighlightOverlayState extends State { builder: (context) => Container( decoration: BoxDecoration( border: Border.fromBorderSide(BorderSide( - color: Theme.of(context).colorScheme.secondary, + color: Theme.of(context).colorScheme.primary, width: widget.extent * .1, )), borderRadius: widget.borderRadius, diff --git a/lib/widgets/filter_grids/common/query_bar.dart b/lib/widgets/filter_grids/common/query_bar.dart index a6a09228b..5e4452b61 100644 --- a/lib/widgets/filter_grids/common/query_bar.dart +++ b/lib/widgets/filter_grids/common/query_bar.dart @@ -17,9 +17,9 @@ class FilterQueryBar extends StatelessWidget { @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); return Container( - height: getPreferredHeight(textScaleFactor), + height: getPreferredHeight(textScaler), alignment: Alignment.topCenter, child: Selector>, bool>( selector: (context, selection) => !selection.isSelecting, @@ -33,5 +33,5 @@ class FilterQueryBar extends StatelessWidget { ); } - static double getPreferredHeight(double textScaleFactor) => QueryBar.getPreferredHeight(textScaleFactor); + static double getPreferredHeight(TextScaler textScaler) => QueryBar.getPreferredHeight(textScaler); } diff --git a/lib/widgets/filter_grids/common/section_header.dart b/lib/widgets/filter_grids/common/section_header.dart index b275f8549..597f0abf9 100644 --- a/lib/widgets/filter_grids/common/section_header.dart +++ b/lib/widgets/filter_grids/common/section_header.dart @@ -23,7 +23,7 @@ class FilterChipSectionHeader extends StatelessWidget { } static double getPreferredHeight(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - return SectionHeader.leadingSize.height * textScaleFactor + SectionHeader.padding.vertical + SectionHeader.margin.vertical; + final textScaler = MediaQuery.textScalerOf(context); + return textScaler.scale(SectionHeader.leadingSize.height) + SectionHeader.padding.vertical + SectionHeader.margin.vertical; } } diff --git a/lib/widgets/home_page.dart b/lib/widgets/home_page.dart index 5f55824e1..34bbc477c 100644 --- a/lib/widgets/home_page.dart +++ b/lib/widgets/home_page.dart @@ -16,6 +16,7 @@ import 'package:aves/services/common/services.dart'; import 'package:aves/services/global_search.dart'; import 'package:aves/services/intent_service.dart'; import 'package:aves/services/widget_service.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/common/basic/scaffold.dart'; @@ -360,6 +361,7 @@ class _HomePageState extends State { return SearchPageRoute( delegate: CollectionSearchDelegate( searchFieldLabel: context.l10n.searchCollectionFieldHint, + searchFieldStyle: Themes.searchFieldStyle(context), source: source, canPop: false, initialQuery: _initialSearchQuery, diff --git a/lib/widgets/home_widget.dart b/lib/widgets/home_widget.dart index f01bce2ce..62f106af3 100644 --- a/lib/widgets/home_widget.dart +++ b/lib/widgets/home_widget.dart @@ -33,7 +33,7 @@ class HomeWidgetPainter { ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba, }) async { final widgetSizePx = Size(widthPx.toDouble(), heightPx.toDouble()); - late final ui.Image? entryImage; + final ui.Image? entryImage; if (entry != null) { final extent = shape.extentPx(widgetSizePx, entry!) / devicePixelRatio; entryImage = await _getEntryImage(entry, extent); @@ -48,6 +48,7 @@ class HomeWidgetPainter { canvas.clipPath(path); if (entryImage != null) { canvas.drawImage(entryImage, Offset(widgetSizePx.width - entryImage.width, widgetSizePx.height - entryImage.height) / 2, Paint()); + entryImage.dispose(); } else { canvas.drawPaint(Paint()..shader = backgroundGradient.createShader(rect)); } diff --git a/lib/widgets/map/address_row.dart b/lib/widgets/map/address_row.dart index daeab20ed..983d7ca06 100644 --- a/lib/widgets/map/address_row.dart +++ b/lib/widgets/map/address_row.dart @@ -41,15 +41,21 @@ class _MapAddressRowState extends State { } } + @override + void dispose() { + _addressLineNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); return Container( alignment: AlignmentDirectional.centerStart, // addresses can include non-latin scripts with inconsistent line height, // which is especially an issue for relayout/painting of heavy Google map, // so we give extra height to give breathing room to the text and stabilize layout - height: Theme.of(context).textTheme.bodyMedium!.fontSize! * textScaleFactor * 2, + height: textScaler.scale(Theme.of(context).textTheme.bodyMedium!.fontSize!) * 2, child: ValueListenableBuilder( valueListenable: _addressLineNotifier, builder: (context, addressLine, child) { @@ -86,7 +92,8 @@ class _MapAddressRowState extends State { Future _updateAddress() async { final entry = widget.entry; final addressLine = await _getAddressLine(entry); - if (mounted && entry == widget.entry) { + if (!mounted) return; + if (entry == widget.entry) { _addressLineNotifier.value = addressLine; } } diff --git a/lib/widgets/map/info_row.dart b/lib/widgets/map/info_row.dart index 2bbb4eb25..2313c4741 100644 --- a/lib/widgets/map/info_row.dart +++ b/lib/widgets/map/info_row.dart @@ -58,7 +58,7 @@ class MapInfoRow extends StatelessWidget { } static double getIconSize(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - return 16 * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + return textScaler.scale(16); } } diff --git a/lib/widgets/map/map_page.dart b/lib/widgets/map/map_page.dart index 59bf3b2f2..372db19dd 100644 --- a/lib/widgets/map/map_page.dart +++ b/lib/widgets/map/map_page.dart @@ -159,12 +159,21 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin _subscriptions ..forEach((sub) => sub.cancel()) ..clear(); + _mapController.dispose(); + _isPageAnimatingNotifier.dispose(); + _selectedIndexNotifier.dispose(); + _regionCollectionNotifier.value?.dispose(); + _regionCollectionNotifier.dispose(); + _dotLocationNotifier.dispose(); _dotEntryNotifier.value?.metadataChangeNotifier.removeListener(_onMarkerEntryMetadataChanged); + _dotEntryNotifier.dispose(); + _overlayOpacityNotifier.dispose(); + _overlayVisible.dispose(); _overlayAnimationController.dispose(); - _overlayVisible.removeListener(_onOverlayVisibleChanged); - _mapController.dispose(); - _selectedIndexNotifier.removeListener(_onThumbnailIndexChanged); - regionCollection?.dispose(); + + // provided collection should be a new instance specifically created + // for the `MapPage` widget, so it can be safely disposed here + widget.collection.dispose(); super.dispose(); } @@ -394,10 +403,11 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin TransparentMaterialPageRoute( settings: const RouteSettings(name: EntryViewerPage.routeName), pageBuilder: (context, a, sa) { + final viewerCollection = regionCollection?.copyWith( + listenToSource: false, + ); return EntryViewerPage( - collection: regionCollection?.copyWith( - listenToSource: false, - ), + collection: viewerCollection, initialEntry: initialEntry, ); }, diff --git a/lib/widgets/navigation/drawer/app_drawer.dart b/lib/widgets/navigation/drawer/app_drawer.dart index b499a33fb..e3c94d672 100644 --- a/lib/widgets/navigation/drawer/app_drawer.dart +++ b/lib/widgets/navigation/drawer/app_drawer.dart @@ -82,11 +82,13 @@ class _AppDrawerState extends State { return Drawer( child: ListTileTheme.merge( - selectedColor: Theme.of(context).colorScheme.secondary, + selectedColor: Theme.of(context).colorScheme.primary, + horizontalTitleGap: 20, + visualDensity: VisualDensity.comfortable, child: Selector( selector: (context, mq) => mq.effectiveBottomPadding, builder: (context, mqPaddingBottom, child) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); final iconTheme = IconTheme.of(context); return SingleChildScrollView( controller: _scrollController, @@ -95,7 +97,7 @@ class _AppDrawerState extends State { padding: EdgeInsets.only(bottom: mqPaddingBottom), child: IconTheme( data: iconTheme.copyWith( - size: iconTheme.size! * textScaleFactor, + size: textScaler.scale(iconTheme.size!), ), child: Column( children: drawerItems, @@ -118,9 +120,12 @@ class _AppDrawerState extends State { )); } + final drawerButtonStyle = ButtonStyle( + padding: MaterialStateProperty.all(const EdgeInsetsDirectional.only(start: 12, end: 16)), + ); return Container( - padding: const EdgeInsets.only(left: 16, top: 16, right: 16, bottom: 8), - color: Theme.of(context).colorScheme.secondary, + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + color: Theme.of(context).colorScheme.primary, child: SafeArea( bottom: false, child: Column( @@ -132,12 +137,12 @@ class _AppDrawerState extends State { spacing: 16, crossAxisAlignment: WrapCrossAlignment.center, children: [ - const AvesLogo(size: 56), + const AvesLogo(size: 48), Text( context.l10n.appName, style: const TextStyle( color: Colors.white, - fontSize: 44, + fontSize: 38, fontWeight: FontWeight.w300, letterSpacing: 1.0, fontFeatures: [FontFeature.enable('smcp')], @@ -162,6 +167,7 @@ class _AppDrawerState extends State { // key is expected by test driver key: const Key('drawer-about-button'), onPressed: () => goTo(AboutPage.routeName, (_) => const AboutPage()), + style: drawerButtonStyle, icon: const Icon(AIcons.info), label: Text(context.l10n.drawerAboutButton), ), @@ -169,6 +175,7 @@ class _AppDrawerState extends State { // key is expected by test driver key: const Key('drawer-settings-button'), onPressed: () => goTo(SettingsPage.routeName, (_) => const SettingsPage()), + style: drawerButtonStyle, icon: const Icon(AIcons.settings), label: Text(context.l10n.drawerSettingsButton), ), diff --git a/lib/widgets/navigation/drawer/collection_nav_tile.dart b/lib/widgets/navigation/drawer/collection_nav_tile.dart index c3e3afdda..e0a5029f9 100644 --- a/lib/widgets/navigation/drawer/collection_nav_tile.dart +++ b/lib/widgets/navigation/drawer/collection_nav_tile.dart @@ -13,7 +13,6 @@ class CollectionNavTile extends StatelessWidget { final Widget? leading; final Widget title; final Widget? trailing; - final bool dense; final CollectionFilter? filter; final bool Function() isSelected; @@ -22,10 +21,9 @@ class CollectionNavTile extends StatelessWidget { required this.leading, required this.title, this.trailing, - bool? dense, required this.filter, required this.isSelected, - }) : dense = dense ?? false; + }); @override Widget build(BuildContext context) { @@ -49,7 +47,6 @@ class CollectionNavTile extends StatelessWidget { }, ) : null, - dense: dense, onTap: () => _goToCollection(context), selected: context.currentRouteName == CollectionPage.routeName && isSelected(), ), diff --git a/lib/widgets/navigation/drawer/page_nav_tile.dart b/lib/widgets/navigation/drawer/page_nav_tile.dart index 8315f82a8..8636828b8 100644 --- a/lib/widgets/navigation/drawer/page_nav_tile.dart +++ b/lib/widgets/navigation/drawer/page_nav_tile.dart @@ -1,5 +1,6 @@ import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_source.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/about/about_page.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/search/page.dart'; @@ -71,6 +72,7 @@ class PageNavTile extends StatelessWidget { return SearchPageRoute( delegate: CollectionSearchDelegate( searchFieldLabel: context.l10n.searchCollectionFieldHint, + searchFieldStyle: Themes.searchFieldStyle(context), source: context.read(), parentCollection: currentCollection?.copyWith(), ), diff --git a/lib/widgets/navigation/drawer/tile.dart b/lib/widgets/navigation/drawer/tile.dart index dee17e807..10b5a7e92 100644 --- a/lib/widgets/navigation/drawer/tile.dart +++ b/lib/widgets/navigation/drawer/tile.dart @@ -15,8 +15,8 @@ class DrawerFilterIcon extends StatelessWidget { @override Widget build(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final iconSize = 24 * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + final iconSize = textScaler.scale(24); final _filter = filter; if (_filter == null) return Icon(AIcons.allCollection, size: iconSize); diff --git a/lib/widgets/navigation/nav_bar/floating.dart b/lib/widgets/navigation/nav_bar/floating.dart index 85d729eb3..2c4f27ad7 100644 --- a/lib/widgets/navigation/nav_bar/floating.dart +++ b/lib/widgets/navigation/nav_bar/floating.dart @@ -44,9 +44,8 @@ class _FloatingNavBarState extends State with SingleTickerProvid curve: Curves.linear, )) ..addListener(() { - if (mounted) { - setState(() {}); - } + if (!mounted) return; + setState(() {}); }); _registerWidget(widget); } diff --git a/lib/widgets/navigation/nav_bar/nav_bar.dart b/lib/widgets/navigation/nav_bar/nav_bar.dart index bd772bd6e..e4c3a9f3f 100644 --- a/lib/widgets/navigation/nav_bar/nav_bar.dart +++ b/lib/widgets/navigation/nav_bar/nav_bar.dart @@ -81,6 +81,7 @@ class _AppBottomNavBarState extends State { .map((item) => BottomNavigationBarItem( icon: item.icon(context), label: item.label(context), + tooltip: item.label(context), )) .toList(), onTap: (index) => _goTo(context, items, index), diff --git a/lib/widgets/navigation/nav_bar/nav_item.dart b/lib/widgets/navigation/nav_bar/nav_item.dart index fbd916396..f617488ff 100644 --- a/lib/widgets/navigation/nav_bar/nav_item.dart +++ b/lib/widgets/navigation/nav_bar/nav_item.dart @@ -22,8 +22,8 @@ class AvesBottomNavItem extends Equatable { return DrawerFilterIcon(filter: filter); } - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final iconSize = 24 * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + final iconSize = textScaler.scale(24); return Icon(NavigationDisplay.getPageIcon(route), size: iconSize); } diff --git a/lib/widgets/navigation/tv_rail.dart b/lib/widgets/navigation/tv_rail.dart index e1ef726d5..a54799173 100644 --- a/lib/widgets/navigation/tv_rail.dart +++ b/lib/widgets/navigation/tv_rail.dart @@ -121,7 +121,7 @@ class _TvRailState extends State { focusNode: _focusNode, skipTraversal: true, child: NavigationRail( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, + backgroundColor: Theme.of(context).colorScheme.background, extended: extended, destinations: navEntries .map((v) => NavigationRailDestination( diff --git a/lib/widgets/search/search_delegate.dart b/lib/widgets/search/search_delegate.dart index 290514d4d..6e37f4eb9 100644 --- a/lib/widgets/search/search_delegate.dart +++ b/lib/widgets/search/search_delegate.dart @@ -66,6 +66,7 @@ class CollectionSearchDelegate extends AvesSearchDelegate with FeedbackMixin, Va CollectionSearchDelegate({ required super.searchFieldLabel, + required super.searchFieldStyle, required this.source, this.parentCollection, super.canPop, diff --git a/lib/widgets/settings/common/collection_tile.dart b/lib/widgets/settings/common/collection_tile.dart index abbe133c8..ff5bfefe4 100644 --- a/lib/widgets/settings/common/collection_tile.dart +++ b/lib/widgets/settings/common/collection_tile.dart @@ -44,7 +44,7 @@ class SettingsCollectionTile extends StatelessWidget { if (hasSubtitle) Text( l10n.drawerCollectionAll, - style: textTheme.bodyMedium!.copyWith(color: textTheme.bodySmall!.color), + style: textTheme.bodyMedium!.copyWith(color: theme.colorScheme.onSurfaceVariant), ), ], ), diff --git a/lib/widgets/settings/common/quick_actions/action_panel.dart b/lib/widgets/settings/common/quick_actions/action_panel.dart index 636b5923d..e83f4186a 100644 --- a/lib/widgets/settings/common/quick_actions/action_panel.dart +++ b/lib/widgets/settings/common/quick_actions/action_panel.dart @@ -1,4 +1,5 @@ import 'package:aves/theme/durations.dart'; +import 'package:aves/theme/themes.dart'; import 'package:flutter/material.dart'; class ActionPanel extends StatelessWidget { @@ -14,11 +15,7 @@ class ActionPanel extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final color = highlight - ? theme.colorScheme.secondary - : theme.brightness == Brightness.dark - ? Colors.blueGrey - : Colors.blueGrey.shade100; + final color = highlight ? theme.colorScheme.primary : Color.alphaBlend(theme.colorScheme.surfaceTint.withOpacity(.2), Themes.secondLayerColor(context)); return AnimatedContainer( foregroundDecoration: BoxDecoration( color: color.withOpacity(.2), diff --git a/lib/widgets/settings/common/quick_actions/editor_page.dart b/lib/widgets/settings/common/quick_actions/editor_page.dart index 331dcd49d..49f0c63af 100644 --- a/lib/widgets/settings/common/quick_actions/editor_page.dart +++ b/lib/widgets/settings/common/quick_actions/editor_page.dart @@ -14,7 +14,6 @@ import 'package:aves/widgets/settings/common/quick_actions/placeholder.dart'; import 'package:aves/widgets/settings/common/quick_actions/quick_actions.dart'; import 'package:aves_utils/aves_utils.dart'; import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; @@ -104,6 +103,13 @@ class _QuickActionEditorBodyState extends State extends State widget.save(_quickActions), child: ListView( children: [ Padding( @@ -272,7 +276,7 @@ class _QuickActionEditorBodyState extends State { _selectedValue = settings.locale ?? LocaleTile.systemLocaleOption; } + @override + void dispose() { + _queryNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final useTvLayout = settings.useTvLayout; diff --git a/lib/widgets/settings/language/locales.dart b/lib/widgets/settings/language/locales.dart index ffb3c9f41..fa32ecf73 100644 --- a/lib/widgets/settings/language/locales.dart +++ b/lib/widgets/settings/language/locales.dart @@ -12,6 +12,7 @@ class SupportedLocales { 'fr': 'Français', 'hu': 'Magyar', 'id': 'Bahasa Indonesia', + 'is': 'Íslenska', 'it': 'Italiano', 'ja': '日本語', 'ko': '한국어', diff --git a/lib/widgets/settings/navigation/drawer.dart b/lib/widgets/settings/navigation/drawer.dart index cd82b9a34..2b3c2c91c 100644 --- a/lib/widgets/settings/navigation/drawer.dart +++ b/lib/widgets/settings/navigation/drawer.dart @@ -13,7 +13,6 @@ import 'package:aves/widgets/navigation/drawer/tile.dart'; import 'package:aves/widgets/search/search_delegate.dart'; import 'package:aves/widgets/settings/navigation/drawer_tab_albums.dart'; import 'package:aves/widgets/settings/navigation/drawer_tab_fixed.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; class NavigationDrawerEditorPage extends StatefulWidget { @@ -101,12 +100,12 @@ class _NavigationDrawerEditorPageState extends State tabs: tabs.map((t) => t.$1).toList(), ), ), - body: WillPopScope( - onWillPop: () { + body: PopScope( + canPop: true, + onPopInvoked: (didPop) { settings.drawerTypeBookmarks = _typeItems.where(_visibleTypes.contains).toList(); settings.drawerAlbumBookmarks = _albumItems; settings.drawerPageBookmarks = _pageItems.where(_visiblePages.contains).toList(); - return SynchronousFuture(true); }, child: SafeArea( child: TabBarView( diff --git a/lib/widgets/settings/privacy/file_picker/crumb_line.dart b/lib/widgets/settings/privacy/file_picker/crumb_line.dart index 30cdfdfa6..7ad896d88 100644 --- a/lib/widgets/settings/privacy/file_picker/crumb_line.dart +++ b/lib/widgets/settings/privacy/file_picker/crumb_line.dart @@ -68,7 +68,7 @@ class _CrumbLineState extends State { return Center( child: DefaultTextStyle.merge( style: TextStyle( - color: Theme.of(context).colorScheme.secondary, + color: Theme.of(context).colorScheme.primary, ), child: _buildText(text), ), diff --git a/lib/widgets/settings/privacy/file_picker/file_picker_page.dart b/lib/widgets/settings/privacy/file_picker/file_picker_page.dart index 592411125..a458578b0 100644 --- a/lib/widgets/settings/privacy/file_picker/file_picker_page.dart +++ b/lib/widgets/settings/privacy/file_picker/file_picker_page.dart @@ -57,15 +57,14 @@ class _FilePickerPageState extends State { return !isHidden; } }).toList(); - return WillPopScope( - onWillPop: () { - if (_directory.relativeDir.isEmpty) { - return SynchronousFuture(true); - } + return PopScope( + canPop: _directory.relativeDir.isEmpty, + onPopInvoked: (didPop) { + if (didPop) return; + final parent = pContext.dirname(currentDirectoryPath); _goTo(parent); setState(() {}); - return SynchronousFuture(false); }, child: AvesScaffold( appBar: AppBar( diff --git a/lib/widgets/settings/settings_mobile_page.dart b/lib/widgets/settings/settings_mobile_page.dart index 6dc4cbd2d..27633b15b 100644 --- a/lib/widgets/settings/settings_mobile_page.dart +++ b/lib/widgets/settings/settings_mobile_page.dart @@ -6,6 +6,7 @@ import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; import 'package:aves/widgets/common/app_bar/app_bar_title.dart'; import 'package:aves/widgets/common/basic/font_size_icon_theme.dart'; @@ -179,6 +180,7 @@ class _SettingsMobilePageState extends State with FeedbackMi SearchPageRoute( delegate: SettingsSearchDelegate( searchFieldLabel: context.l10n.settingsSearchFieldLabel, + searchFieldStyle: Themes.searchFieldStyle(context), sections: SettingsPage.sections, ), ), diff --git a/lib/widgets/settings/settings_search.dart b/lib/widgets/settings/settings_search.dart index 7c92ea90a..ac547b44f 100644 --- a/lib/widgets/settings/settings_search.dart +++ b/lib/widgets/settings/settings_search.dart @@ -15,6 +15,7 @@ class SettingsSearchDelegate extends AvesSearchDelegate { SettingsSearchDelegate({ required super.searchFieldLabel, + required super.searchFieldStyle, required this.sections, }) : super( routeName: pageRouteName, diff --git a/lib/widgets/settings/settings_tv_page.dart b/lib/widgets/settings/settings_tv_page.dart index 1fc742bb8..66a612989 100644 --- a/lib/widgets/settings/settings_tv_page.dart +++ b/lib/widgets/settings/settings_tv_page.dart @@ -80,7 +80,7 @@ class _ContentState extends State<_Content> { valueListenable: _indexNotifier, builder: (context, selectedIndex, child) { final rail = NavigationRail( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, + backgroundColor: Theme.of(context).colorScheme.background, extended: true, destinations: sections .map((section) => NavigationRailDestination( diff --git a/lib/widgets/settings/thumbnails/overlay.dart b/lib/widgets/settings/thumbnails/overlay.dart index d136ddbe1..a4904a372 100644 --- a/lib/widgets/settings/thumbnails/overlay.dart +++ b/lib/widgets/settings/thumbnails/overlay.dart @@ -90,8 +90,8 @@ class ThumbnailOverlayPage extends StatelessWidget { } static double _getIconSize(BuildContext context) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - return IconTheme.of(context).size! * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + return textScaler.scale(IconTheme.of(context).size!); } static Color _getIconColor(BuildContext context) => context.select((v) => v.neutral); diff --git a/lib/widgets/settings/viewer/viewer_actions_editor.dart b/lib/widgets/settings/viewer/viewer_actions_editor.dart index 824d63fac..ccbd6fdd6 100644 --- a/lib/widgets/settings/viewer/viewer_actions_editor.dart +++ b/lib/widgets/settings/viewer/viewer_actions_editor.dart @@ -28,6 +28,8 @@ class ViewerActionEditorPage extends StatelessWidget { ], [ ...EntryActions.export, + ], + [ ...EntryActions.video.whereNot((v) => v == EntryAction.videoSettings), ], EntryActions.commonMetadataActions, diff --git a/lib/widgets/stats/date/histogram.dart b/lib/widgets/stats/date/histogram.dart index 69acdcc41..55cebcaa9 100644 --- a/lib/widgets/stats/date/histogram.dart +++ b/lib/widgets/stats/date/histogram.dart @@ -194,7 +194,7 @@ class _HistogramState extends State with AutomaticKeepAliveClientMixi final drawPoints = !isInterpolated; final colorScheme = Theme.of(context).colorScheme; - final accentColor = colorScheme.secondary; + final accentColor = colorScheme.primary; final axisColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onPrimary.withOpacity(.9) : Colors.transparent); final measureLineColor = charts.ColorUtil.fromDartColor(drawPoints ? colorScheme.onPrimary.withOpacity(.1) : Colors.transparent); final histogramLineColor = charts.ColorUtil.fromDartColor(drawLine ? accentColor : Colors.white); @@ -326,7 +326,7 @@ class _HistogramState extends State with AutomaticKeepAliveClientMixi Text( numberFormat.format(count), style: TextStyle( - color: Theme.of(context).textTheme.bodySmall!.color, + color: Theme.of(context).colorScheme.onSurfaceVariant, ), textAlign: TextAlign.end, ), diff --git a/lib/widgets/stats/filter_table.dart b/lib/widgets/stats/filter_table.dart index c6fe71c41..7e0673940 100644 --- a/lib/widgets/stats/filter_table.dart +++ b/lib/widgets/stats/filter_table.dart @@ -1,6 +1,7 @@ import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/settings.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/stats/percent_text.dart'; @@ -46,8 +47,8 @@ class FilterTable extends StatelessWidget { }); } - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final lineHeight = 16 * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + final lineHeight = textScaler.scale(16); final barRadius = Radius.circular(lineHeight / 2); final isRtl = context.isRtl; @@ -57,13 +58,13 @@ class FilterTable extends StatelessWidget { builder: (context, constraints) { final showPercentIndicator = constraints.maxWidth - (chipWidth + countWidth) > percentIndicatorMinWidth; final displayedEntries = maxRowCount != null ? sortedEntries.take(maxRowCount!) : sortedEntries; - final theme = Theme.of(context); final isMonochrome = settings.themeColorMode == AvesThemeColorMode.monochrome; return Table( children: displayedEntries.map((kv) { final filter = filterBuilder(kv.key); final count = kv.value; final percent = count / totalEntryCount; + final colorScheme = Theme.of(context).colorScheme; return TableRow( children: [ Container( @@ -82,23 +83,30 @@ class FilterTable extends StatelessWidget { future: filter.color(context), builder: (context, snapshot) { final color = snapshot.data; - return LinearPercentIndicator( - percent: percent, - lineHeight: lineHeight, - backgroundColor: theme.colorScheme.onPrimary.withOpacity(.1), - progressColor: isMonochrome ? theme.colorScheme.secondary : color, - animation: animate, - isRTL: isRtl, - barRadius: barRadius, - center: LinearPercentIndicatorText(percent: percent), - padding: EdgeInsets.symmetric(horizontal: lineHeight), + return Stack( + // use a stack instead of `center` field, so that the widgets + // are centered even when the center child has larger height + alignment: Alignment.center, + children: [ + LinearPercentIndicator( + percent: percent, + lineHeight: lineHeight, + backgroundColor: Themes.secondLayerColor(context), + progressColor: isMonochrome ? colorScheme.primary : color, + animation: animate, + isRTL: isRtl, + barRadius: barRadius, + padding: EdgeInsets.symmetric(horizontal: lineHeight), + ), + LinearPercentIndicatorText(percent: percent), + ], ); }, ), Text( numberFormat.format(count), style: TextStyle( - color: theme.textTheme.bodySmall!.color, + color: colorScheme.onSurfaceVariant, ), textAlign: TextAlign.end, ), diff --git a/lib/widgets/stats/percent_text.dart b/lib/widgets/stats/percent_text.dart index 1ead911d5..839b41ce0 100644 --- a/lib/widgets/stats/percent_text.dart +++ b/lib/widgets/stats/percent_text.dart @@ -24,7 +24,7 @@ class LinearPercentIndicatorText extends StatelessWidget { ), ) ], - outlineColor: theme.scaffoldBackgroundColor, + outlineColor: theme.colorScheme.background, ); } } diff --git a/lib/widgets/stats/stats_page.dart b/lib/widgets/stats/stats_page.dart index a0a146bf6..07d6596cd 100644 --- a/lib/widgets/stats/stats_page.dart +++ b/lib/widgets/stats/stats_page.dart @@ -13,6 +13,7 @@ import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/theme/styles.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/utils/file_utils.dart'; import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; @@ -108,6 +109,12 @@ class _StatsPageState extends State with FeedbackMixin, VaultAwareMix }); } + @override + void dispose() { + _isPageAnimatingNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final useTvLayout = settings.useTvLayout; @@ -410,8 +417,8 @@ class _LocationIndicator extends StatelessWidget { final withGps = catalogued.where((entry) => entry.hasGps); final withGpsCount = withGps.length; final withGpsPercent = withGpsCount / entries.length; - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final lineHeight = 16 * textScaleFactor; + final textScaler = MediaQuery.textScalerOf(context); + final lineHeight = textScaler.scale(16); final barRadius = Radius.circular(lineHeight / 2); return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), @@ -422,16 +429,23 @@ class _LocationIndicator extends StatelessWidget { children: [ const Icon(AIcons.location), Expanded( - child: LinearPercentIndicator( - percent: withGpsPercent, - lineHeight: lineHeight, - backgroundColor: theme.colorScheme.onPrimary.withOpacity(.1), - progressColor: theme.colorScheme.secondary, - animation: context.select((v) => v.accessibilityAnimations.animate), - isRTL: context.isRtl, - barRadius: barRadius, - center: LinearPercentIndicatorText(percent: withGpsPercent), - padding: EdgeInsets.symmetric(horizontal: lineHeight), + child: Stack( + // use a stack instead of `center` field, so that the widgets + // are centered even when the center child has larger height + alignment: Alignment.center, + children: [ + LinearPercentIndicator( + percent: withGpsPercent, + lineHeight: lineHeight, + backgroundColor: Themes.secondLayerColor(context), + progressColor: theme.colorScheme.primary, + animation: context.select((v) => v.accessibilityAnimations.animate), + isRTL: context.isRtl, + barRadius: barRadius, + padding: EdgeInsets.symmetric(horizontal: lineHeight), + ), + LinearPercentIndicatorText(percent: withGpsPercent), + ], ), ), // end padding to match leading, so that inside label is aligned with outside label below diff --git a/lib/widgets/viewer/action/entry_action_delegate.dart b/lib/widgets/viewer/action/entry_action_delegate.dart index 56685c672..9a86970a6 100644 --- a/lib/widgets/viewer/action/entry_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_action_delegate.dart @@ -108,6 +108,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix case EntryAction.copyToClipboard: case EntryAction.open: case EntryAction.setAs: + case EntryAction.cast: return !settings.useTvLayout; case EntryAction.info: case EntryAction.share: @@ -256,6 +257,8 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix appService.setAs(targetEntry.uri, targetEntry.mimeType).then((success) { if (!success) showNoMatchingAppDialog(context); }); + case EntryAction.cast: + const CastNotification(true).dispatch(context); // platform case EntryAction.rotateScreen: _rotateScreen(context); diff --git a/lib/widgets/viewer/action/entry_info_action_delegate.dart b/lib/widgets/viewer/action/entry_info_action_delegate.dart index bce630316..5517d70cf 100644 --- a/lib/widgets/viewer/action/entry_info_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_info_action_delegate.dart @@ -271,7 +271,6 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi ), ), ); - mapCollection.dispose(); } void _goToDebug(BuildContext context, AvesEntry targetEntry) { diff --git a/lib/widgets/viewer/action/video_action_delegate.dart b/lib/widgets/viewer/action/video_action_delegate.dart index 847fd0cf7..a99e62460 100644 --- a/lib/widgets/viewer/action/video_action_delegate.dart +++ b/lib/widgets/viewer/action/video_action_delegate.dart @@ -23,6 +23,7 @@ import 'package:aves/widgets/viewer/controls/notifications.dart'; import 'package:aves_model/aves_model.dart'; import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -32,9 +33,20 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix VideoActionDelegate({ required this.collection, - }); + }) { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$VideoActionDelegate', + object: this, + ); + } + } void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } stopOverlayHidingTimer(); } diff --git a/lib/widgets/viewer/controls/cast.dart b/lib/widgets/viewer/controls/cast.dart new file mode 100644 index 000000000..731712a59 --- /dev/null +++ b/lib/widgets/viewer/controls/cast.dart @@ -0,0 +1,174 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:aves/model/entry/entry.dart'; +import 'package:aves/model/entry/extensions/props.dart'; +import 'package:aves/ref/upnp.dart'; +import 'package:aves/services/common/services.dart'; +import 'package:aves/widgets/dialogs/cast_dialog.dart'; +import 'package:collection/collection.dart'; +import 'package:dlna_dart/dlna.dart'; +import 'package:dlna_dart/xmlParser.dart'; +import 'package:flutter/material.dart'; +import 'package:network_info_plus/network_info_plus.dart'; +import 'package:shelf/shelf.dart'; +import 'package:shelf/shelf_io.dart' as shelf_io; +import 'package:xml/xml.dart'; + +mixin CastMixin { + DLNADevice? _renderer; + HttpServer? _mediaServer; + + bool get isCasting => _renderer != null && _mediaServer != null; + + Future initCast(BuildContext context, List entries) async { + await stopCast(); + + final renderer = await _selectRenderer(context); + _renderer = renderer; + if (renderer == null) return; + debugPrint('cast: select renderer `${renderer.info.friendlyName}` at ${renderer.info.URLBase}'); + + final ip = await NetworkInfo().getWifiIP(); + if (ip == null) return; + + Set? supportedMimeTypes; + + final handler = const Pipeline().addHandler((request) async { + debugPrint('cast: received request for id=${request.url}'); + final id = int.tryParse(request.url.path); + if (id == null) { + return Response.notFound('invalid url=${request.url}'); + } + + final entry = entries.firstWhereOrNull((v) => v.id == id); + if (entry == null) { + return Response.notFound('no resource for url=${request.url}'); + } + + if (supportedMimeTypes == null) { + // do not call `GetProtocolInfo` before serving files, + // as it somehow makes `Play` time out (but not `SetAVTransportURI`) + supportedMimeTypes = await renderer.getSinkSupportedMimeTypes(); + debugPrint('cast: supported MIME types=$supportedMimeTypes'); + } + + // TODO TLAD [cast] transcode when MIME type is not supported by renderer + + return await _sendEntry(entry); + }); + _mediaServer = await shelf_io.serve(handler, ip, 8080); + debugPrint('cast: serving media on $_serverBaseUrl'); + } + + Future stopCast() async { + if (isCasting) { + debugPrint('cast: stop'); + } + + await _mediaServer?.close(); + _mediaServer = null; + + await _renderer?.stop(); + _renderer = null; + } + + Future _selectRenderer(BuildContext context) async { + return await showDialog( + context: context, + builder: (context) => const CastDialog(), + routeSettings: const RouteSettings(name: CastDialog.routeName), + ); + } + + Future castEntry(AvesEntry entry) async { + final server = _mediaServer; + final renderer = _renderer; + if (server == null || renderer == null) return; + + try { + debugPrint('cast: set entry=$entry'); + await renderer.setUrl( + '$_serverBaseUrl/${entry.id}', + title: entry.bestTitle ?? '', + type: entry.isVideo ? PlayType.Video : PlayType.Image, + ); + debugPrint('cast: play entry=$entry'); + unawaited(renderer.play()); + } catch (error, stack) { + await reportService.recordError(error, stack); + } + } + + String? get _serverBaseUrl { + final server = _mediaServer; + return server != null ? 'http://${server.address.host}:${server.port}' : null; + } + + Future _sendEntry(AvesEntry entry) async { + // TODO TLAD [cast] providing downscaled versions is suitable when properly serving with `MediaServer`, as the renderer can pick what is best + final bytes = await mediaFetchService.getImage( + entry.uri, + entry.mimeType, + rotationDegrees: entry.rotationDegrees, + isFlipped: entry.isFlipped, + pageId: entry.pageId, + sizeBytes: entry.sizeBytes, + ); + + debugPrint('cast: send ${bytes.length} bytes for entry=$entry'); + return Response.ok( + bytes, + headers: { + 'Content-Type': entry.mimeType, + }, + ); + } +} + +extension DLNADeviceExtra on DLNADevice { + Future requestCustom({ + required String serviceId, + required String serviceType, + required String action, + required String data, + }) async { + return DLNAHttp.post( + Uri.parse(controlURL(serviceId)), + Map.from({ + 'SOAPAction': '"$serviceType#$action"', + 'Content-Type': 'text/xml', + }), + const Utf8Encoder().convert(data), + ); + } + + Future> getProtocolInfo() async { + final result = await requestCustom( + serviceId: 'ConnectionManager', + serviceType: Upnp.upnpServiceTypeConnectionManager, + action: 'GetProtocolInfo', + data: Upnp.getProtocolInfoActionXml(), + ); + final doc = XmlDocument.parse(result); + final sink = UpnpProtocolInfo(doc.findAllElements('Sink').first.innerText); + final source = UpnpProtocolInfo(doc.findAllElements('Source').first.innerText); + return { + 'sink': sink, + 'source': source, + }; + } + + Future?> getSinkSupportedMimeTypes() async { + final sinkProtocolInfo = (await getProtocolInfo())['sink']; + if (sinkProtocolInfo != null) { + final byProtocol = groupBy(sinkProtocolInfo.entries, (v) => v.protocol); + final httpGet = byProtocol['http-get']; + if (httpGet != null) { + return httpGet.map((v) => v.contentFormat).toSet(); + } + } + return null; + } +} diff --git a/lib/widgets/viewer/controls/controller.dart b/lib/widgets/viewer/controls/controller.dart index 9e5a87f4b..084cd2ce9 100644 --- a/lib/widgets/viewer/controls/controller.dart +++ b/lib/widgets/viewer/controls/controller.dart @@ -3,12 +3,14 @@ import 'dart:math'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/theme/durations.dart'; +import 'package:aves/widgets/viewer/controls/cast.dart'; import 'package:aves/widgets/viewer/controls/events.dart'; import 'package:aves_magnifier/aves_magnifier.dart'; import 'package:aves_model/aves_model.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -class ViewerController { +class ViewerController with CastMixin { final ValueNotifier entryNotifier = ValueNotifier(null); final ViewerTransition transition; final Duration? autopilotInterval; @@ -47,6 +49,13 @@ class ViewerController { this.autopilotInterval, this.autopilotAnimatedZoom = false, }) { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$ViewerController', + object: this, + ); + } _initialScale = initialScale; _autopilotNotifier = ValueNotifier(autopilot); _autopilotNotifier.addListener(_onAutopilotChanged); @@ -54,7 +63,10 @@ class ViewerController { } void dispose() { - _autopilotNotifier.removeListener(_onAutopilotChanged); + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } + _autopilotNotifier.dispose(); _clearAutopilotAnimations(); _stopPlayTimer(); _streamController.close(); diff --git a/lib/widgets/viewer/controls/notifications.dart b/lib/widgets/viewer/controls/notifications.dart index a170bb91e..7d5d9006f 100644 --- a/lib/widgets/viewer/controls/notifications.dart +++ b/lib/widgets/viewer/controls/notifications.dart @@ -78,6 +78,16 @@ class VideoActionNotification extends Notification { }); } +@immutable +class CastNotification extends Notification with EquatableMixin { + final bool enabled; + + @override + List get props => [enabled]; + + const CastNotification(this.enabled); +} + @immutable class FilterSelectedNotification extends Notification with EquatableMixin { final CollectionFilter filter; diff --git a/lib/widgets/viewer/entry_vertical_pager.dart b/lib/widgets/viewer/entry_vertical_pager.dart index b81cb75fa..a3eaa15e1 100644 --- a/lib/widgets/viewer/entry_vertical_pager.dart +++ b/lib/widgets/viewer/entry_vertical_pager.dart @@ -358,19 +358,16 @@ class _ViewerVerticalPageViewState extends State { } else { Navigator.maybeOf(context)?.pop(); } - + if (!mounted) return; // needed to refresh when entry changes but the page does not (e.g. on page deletion) - if (mounted) { - setState(() {}); - } + setState(() {}); } // when the entry image itself changed (e.g. after rotation) void _onVisualChanged() async { - // rebuild to refresh the Image inside ImagePage - if (mounted) { - setState(() {}); - } + if (!mounted) return; + // rebuild to refresh the `Image` inside `ImagePage` + setState(() {}); } void _onPlayPauseIntent(PlayPauseIntent intent) { diff --git a/lib/widgets/viewer/entry_viewer_page.dart b/lib/widgets/viewer/entry_viewer_page.dart index 94ef5b5d7..766d0cb45 100644 --- a/lib/widgets/viewer/entry_viewer_page.dart +++ b/lib/widgets/viewer/entry_viewer_page.dart @@ -34,6 +34,9 @@ class _EntryViewerPageState extends State { @override void dispose() { _viewerController.dispose(); + // provided collection should be a new instance specifically created + // for the `EntryViewerPage` widget, so it can be safely disposed here + widget.collection?.dispose(); super.dispose(); } diff --git a/lib/widgets/viewer/entry_viewer_stack.dart b/lib/widgets/viewer/entry_viewer_stack.dart index 01399c26f..e34da51ee 100644 --- a/lib/widgets/viewer/entry_viewer_stack.dart +++ b/lib/widgets/viewer/entry_viewer_stack.dart @@ -192,7 +192,10 @@ class _EntryViewerStackState extends State with EntryViewContr _overlayVisible.dispose(); _viewLocked.dispose(); _overlayExpandedNotifier.dispose(); + _currentVerticalPage.dispose(); + _horizontalPager.dispose(); _verticalPager.dispose(); + _verticalScrollNotifier.dispose(); _heroInfoNotifier.dispose(); _stopOverlayHidingTimer(); AvesApp.lifecycleStateNotifier.removeListener(_onAppLifecycleStateChanged); @@ -211,10 +214,12 @@ class _EntryViewerStackState extends State with EntryViewContr @override Widget build(BuildContext context) { final viewStateConductor = context.read(); - return WillPopScope( - onWillPop: () { - _onWillPop(); - return SynchronousFuture(false); + return PopScope( + canPop: false, + onPopInvoked: (didPop) { + if (didPop) return; + + _onPopInvoked(); }, child: ValueListenableProvider.value( value: _heroInfoNotifier, @@ -513,6 +518,8 @@ class _EntryViewerStackState extends State with EntryViewContr bool _handleNotification(dynamic notification) { if (notification is FilterSelectedNotification) { _goToCollection(notification.filter); + } else if (notification is CastNotification) { + _cast(notification.enabled); } else if (notification is FullImageLoadedNotification) { final viewStateController = context.read().getOrCreateController(notification.entry); // microtask so that listeners do not trigger during build @@ -556,7 +563,7 @@ class _EntryViewerStackState extends State with EntryViewContr if (_overlayVisible.value) { _overlayVisible.value = false; } else { - _onWillPop(); + _onPopInvoked(); } } else if (notification is TvShowMoreInfoNotification) { if (!_overlayVisible.value) { @@ -578,6 +585,21 @@ class _EntryViewerStackState extends State with EntryViewContr return true; } + Future _cast(bool enabled) async { + if (enabled) { + final entries = collection?.sortedEntries; + if (entries != null) { + await viewerController.initCast(context, entries); + final entry = entryNotifier.value; + if (entry != null) { + await viewerController.castEntry(entry); + } + } + } else { + await viewerController.stopCast(); + } + } + Future _onVideoAction({ required BuildContext context, required AvesEntry entry, @@ -753,9 +775,16 @@ class _EntryViewerStackState extends State with EntryViewContr _isEntryTracked = false; await pauseVideoControllers(); await initEntryControllers(newEntry); + + if (viewerController.isCasting) { + final entry = entryNotifier.value; + if (entry != null) { + await viewerController.castEntry(entry); + } + } } - void _onWillPop() { + void _onPopInvoked() { if (_currentVerticalPage.value == infoPage) { // back from info to image _goToVerticalPage(imagePage); @@ -814,6 +843,8 @@ class _EntryViewerStackState extends State with EntryViewContr // to be unmounted after the other async steps final theme = Theme.of(context); + await viewerController.stopCast(); + switch (settings.maxBrightness) { case MaxBrightness.never: case MaxBrightness.viewerOnly: diff --git a/lib/widgets/viewer/info/common.dart b/lib/widgets/viewer/info/common.dart index 6d4981c57..cf10ebcf9 100644 --- a/lib/widgets/viewer/info/common.dart +++ b/lib/widgets/viewer/info/common.dart @@ -51,7 +51,10 @@ class InfoRowGroup extends StatefulWidget { static const valueStyle = TextStyle(fontSize: fontSize); static final _keyStyle = valueStyle.copyWith(height: 2.0); - static TextStyle keyStyle(BuildContext context) => Theme.of(context).textTheme.bodySmall!.merge(_keyStyle); + static TextStyle keyStyle(BuildContext context) { + final theme = Theme.of(context); + return theme.textTheme.bodySmall!.copyWith(color: theme.colorScheme.onSurfaceVariant).merge(_keyStyle); + } const InfoRowGroup({ super.key, @@ -97,8 +100,8 @@ class _InfoRowGroupState extends State { final _keyStyle = InfoRowGroup.keyStyle(context); // compute the size of keys and space in order to align values - final textScaleFactor = MediaQuery.textScaleFactorOf(context); - final keySizes = Map.fromEntries(keyValues.keys.map((key) => MapEntry(key, _getSpanWidth(TextSpan(text: _buildTextValue(key), style: _keyStyle), textScaleFactor)))); + final textScaler = MediaQuery.textScalerOf(context); + final keySizes = Map.fromEntries(keyValues.keys.map((key) => MapEntry(key, _getSpanWidth(TextSpan(text: _buildTextValue(key), style: _keyStyle), textScaler)))); final lastKey = keyValues.keys.last; return LayoutBuilder( @@ -115,6 +118,7 @@ class _InfoRowGroupState extends State { final value = kv.value; final spanBuilder = spanBuilders[key] ?? _buildTextValueSpans; final thisSpaceSize = max(0.0, (baseValueX - keySizes[key]!)) + InfoRowGroup.keyValuePadding; + final textScaleFactor = textScaler.scale(thisSpaceSize) / thisSpaceSize; return [ TextSpan(text: _buildTextValue(key), style: _keyStyle), @@ -138,13 +142,15 @@ class _InfoRowGroupState extends State { ); } - double _getSpanWidth(TextSpan span, double textScaleFactor) { - final para = RenderParagraph( + double _getSpanWidth(TextSpan span, TextScaler textScaler) { + final paragraph = RenderParagraph( span, textDirection: TextDirection.ltr, - textScaleFactor: textScaleFactor, + textScaler: textScaler, )..layout(const BoxConstraints(), parentUsesSize: true); - return para.getMaxIntrinsicWidth(double.infinity); + final width = paragraph.getMaxIntrinsicWidth(double.infinity); + paragraph.dispose(); + return width; } List _buildTextValueSpans(BuildContext context, String key, String value) { diff --git a/lib/widgets/viewer/info/info_app_bar.dart b/lib/widgets/viewer/info/info_app_bar.dart index 767a4b1d6..178cf8bc1 100644 --- a/lib/widgets/viewer/info/info_app_bar.dart +++ b/lib/widgets/viewer/info/info_app_bar.dart @@ -6,6 +6,7 @@ import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/theme/themes.dart'; import 'package:aves/view/view.dart'; import 'package:aves/widgets/common/app_bar/app_bar_title.dart'; import 'package:aves/widgets/common/app_bar/sliver_app_bar_title.dart'; @@ -113,6 +114,7 @@ class InfoAppBar extends StatelessWidget { context: context, delegate: InfoSearchDelegate( searchFieldLabel: context.l10n.viewerInfoSearchFieldLabel, + searchFieldStyle: Themes.searchFieldStyle(context), entry: entry, metadataNotifier: metadataNotifier, isSelecting: isSelecting, diff --git a/lib/widgets/viewer/info/info_search.dart b/lib/widgets/viewer/info/info_search.dart index 5f5d48307..081f3410b 100644 --- a/lib/widgets/viewer/info/info_search.dart +++ b/lib/widgets/viewer/info/info_search.dart @@ -18,11 +18,13 @@ class InfoSearchDelegate extends SearchDelegate { InfoSearchDelegate({ required String searchFieldLabel, + required TextStyle searchFieldStyle, required this.entry, required this.metadataNotifier, required this.isSelecting, }) : super( searchFieldLabel: searchFieldLabel, + searchFieldStyle: searchFieldStyle, ); @override diff --git a/lib/widgets/viewer/info/location_section.dart b/lib/widgets/viewer/info/location_section.dart index 1010fc999..df001809f 100644 --- a/lib/widgets/viewer/info/location_section.dart +++ b/lib/widgets/viewer/info/location_section.dart @@ -153,7 +153,6 @@ class _LocationSectionState extends State { ), ), ); - mapCollection.dispose(); } void _onMetadataChanged() { diff --git a/lib/widgets/viewer/info/metadata/metadata_section.dart b/lib/widgets/viewer/info/metadata/metadata_section.dart index c040b20b4..c72588edc 100644 --- a/lib/widgets/viewer/info/metadata/metadata_section.dart +++ b/lib/widgets/viewer/info/metadata/metadata_section.dart @@ -55,6 +55,7 @@ class _MetadataSectionSliverState extends State { @override void dispose() { _unregisterWidget(widget); + _expandedDirectoryNotifier.dispose(); super.dispose(); } @@ -143,7 +144,6 @@ class _MetadataSectionSliverState extends State { Future _getMetadata() async { if (!mounted) return; - final titledDirectories = await entry.getMetadataDirectories(context); metadataNotifier.value = Map.fromEntries(titledDirectories); _expandedDirectoryNotifier.value = null; diff --git a/lib/widgets/viewer/info/metadata/tv_page.dart b/lib/widgets/viewer/info/metadata/tv_page.dart index 46af8c63b..5ed23345f 100644 --- a/lib/widgets/viewer/info/metadata/tv_page.dart +++ b/lib/widgets/viewer/info/metadata/tv_page.dart @@ -64,7 +64,7 @@ class _TvMetadataPageState extends State { if (selectedDir == null) return const SizedBox(); final rail = NavigationRail( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, + backgroundColor: Theme.of(context).colorScheme.background, extended: true, destinations: titles.mapIndexed((i, title) { final dir = metadata[titles[i]]!; diff --git a/lib/widgets/viewer/info/metadata/xmp_card.dart b/lib/widgets/viewer/info/metadata/xmp_card.dart index ac359fa52..49ec41cf1 100644 --- a/lib/widgets/viewer/info/metadata/xmp_card.dart +++ b/lib/widgets/viewer/info/metadata/xmp_card.dart @@ -61,6 +61,12 @@ class _XmpCardState extends State { } } + @override + void dispose() { + _indexNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final _isIndexed = isIndexed; diff --git a/lib/widgets/viewer/multipage/conductor.dart b/lib/widgets/viewer/multipage/conductor.dart index bac45d9f2..fe63491d4 100644 --- a/lib/widgets/viewer/multipage/conductor.dart +++ b/lib/widgets/viewer/multipage/conductor.dart @@ -1,14 +1,30 @@ +import 'dart:async'; + import 'package:aves/model/entry/entry.dart'; import 'package:aves/widgets/viewer/multipage/controller.dart'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; class MultiPageConductor { final List _controllers = []; static const maxControllerCount = 3; + MultiPageConductor() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$MultiPageConductor', + object: this, + ); + } + } + Future dispose() async { - await Future.forEach(_controllers, (controller) => controller.dispose()); + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } + await _disposeAll(); _controllers.clear(); } @@ -29,4 +45,8 @@ class MultiPageConductor { MultiPageController? getController(AvesEntry entry) { return _controllers.firstWhereOrNull((c) => c.entry.uri == entry.uri && c.entry.pageId == entry.pageId); } + + Future _applyToAll(FutureOr Function(MultiPageController controller) action) => Future.forEach(_controllers, action); + + Future _disposeAll() => _applyToAll((controller) => controller.dispose()); } diff --git a/lib/widgets/viewer/multipage/controller.dart b/lib/widgets/viewer/multipage/controller.dart index a83a8b7c3..429fb582d 100644 --- a/lib/widgets/viewer/multipage/controller.dart +++ b/lib/widgets/viewer/multipage/controller.dart @@ -24,6 +24,13 @@ class MultiPageController { set page(int? page) => pageNotifier.value = page; MultiPageController(this.entry) { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$MultiPageController', + object: this, + ); + } reset(); } @@ -40,6 +47,9 @@ class MultiPageController { }); void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _disposed = true; pageNotifier.dispose(); } diff --git a/lib/widgets/viewer/overlay/details/rating_tags.dart b/lib/widgets/viewer/overlay/details/rating_tags.dart index f2a93d3f5..f2b92c0d0 100644 --- a/lib/widgets/viewer/overlay/details/rating_tags.dart +++ b/lib/widgets/viewer/overlay/details/rating_tags.dart @@ -29,10 +29,13 @@ class OverlayRatingTagsRow extends AnimatedWidget { ratingString = '${'★' * rating}${'☆' * (5 - rating)}'; } - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); final tags = entry.tags.toList()..sort(compareAsciiUpperCaseNatural); final hasTags = tags.isNotEmpty; + const iconSize = ViewerDetailOverlayContent.iconSize; + final textScaleFactor = textScaler.scale(iconSize) / iconSize; + return Text.rich( TextSpan( children: [ @@ -45,7 +48,7 @@ class OverlayRatingTagsRow extends AnimatedWidget { padding: const EdgeInsetsDirectional.only(end: ViewerDetailOverlayContent.iconPadding), child: DecoratedIcon( AIcons.tag, - size: ViewerDetailOverlayContent.iconSize / textScaleFactor, + size: iconSize / textScaleFactor, shadows: ViewerDetailOverlayContent.shadows(context), ), ), diff --git a/lib/widgets/viewer/overlay/histogram.dart b/lib/widgets/viewer/overlay/histogram.dart index 0508c1104..62a4e9ca8 100644 --- a/lib/widgets/viewer/overlay/histogram.dart +++ b/lib/widgets/viewer/overlay/histogram.dart @@ -80,9 +80,8 @@ class _ImageHistogramState extends State { final targetEntry = entry; final forceUpdate = targetEntry.isAnimated; final newLevels = await viewStateController.getHistogramLevels(info, forceUpdate); - if (mounted) { - setState(() => _levels = targetEntry == entry ? newLevels : {}); - } + if (!mounted) return; + setState(() => _levels = targetEntry == entry ? newLevels : {}); } } diff --git a/lib/widgets/viewer/overlay/slideshow_buttons.dart b/lib/widgets/viewer/overlay/slideshow_buttons.dart index bb42659a2..5b84cc031 100644 --- a/lib/widgets/viewer/overlay/slideshow_buttons.dart +++ b/lib/widgets/viewer/overlay/slideshow_buttons.dart @@ -30,6 +30,7 @@ class _SlideshowButtonsState extends State { static const List _actions = [ SlideshowAction.resume, SlideshowAction.showInCollection, + SlideshowAction.cast, SlideshowAction.settings, ]; static const double _padding = ViewerButtonRowContent.padding; @@ -116,7 +117,14 @@ class _SlideshowButtonsState extends State { ); } - void _onAction(BuildContext context, SlideshowAction action) => SlideshowActionNotification(action).dispatch(context); + void _onAction(BuildContext context, SlideshowAction action) { + switch (action) { + case SlideshowAction.cast: + const CastNotification(true).dispatch(context); + default: + SlideshowActionNotification(action).dispatch(context); + } + } void _requestFocus() => _buttonRowFocusScopeNode.children.firstOrNull?.requestFocus(); } diff --git a/lib/widgets/viewer/overlay/thumbnail_preview.dart b/lib/widgets/viewer/overlay/thumbnail_preview.dart index 01f9b57c0..5a64c3db3 100644 --- a/lib/widgets/viewer/overlay/thumbnail_preview.dart +++ b/lib/widgets/viewer/overlay/thumbnail_preview.dart @@ -24,7 +24,7 @@ class ViewerThumbnailPreview extends StatefulWidget { } class _ViewerThumbnailPreviewState extends State { - final ValueNotifier _entryIndexNotifier = ValueNotifier(0); + late final ValueNotifier _entryIndexNotifier; final Debouncer _debouncer = Debouncer(delay: ADurations.viewerThumbnailScrollDebounceDelay); List get entries => widget.entries; @@ -34,7 +34,7 @@ class _ViewerThumbnailPreviewState extends State { @override void initState() { super.initState(); - _entryIndexNotifier.value = widget.displayedIndex; + _entryIndexNotifier = ValueNotifier(widget.displayedIndex); _entryIndexNotifier.addListener(_onScrollerIndexChanged); } @@ -49,7 +49,7 @@ class _ViewerThumbnailPreviewState extends State { @override void dispose() { - _entryIndexNotifier.removeListener(_onScrollerIndexChanged); + _entryIndexNotifier.dispose(); super.dispose(); } @@ -65,8 +65,7 @@ class _ViewerThumbnailPreviewState extends State { } void _onScrollerIndexChanged() => _debouncer(() { - if (mounted) { - ShowEntryNotification(animate: false, index: _entryIndexNotifier.value).dispatch(context); - } + if (!mounted) return; + ShowEntryNotification(animate: false, index: _entryIndexNotifier.value).dispatch(context); }); } diff --git a/lib/widgets/viewer/overlay/video/progress_bar.dart b/lib/widgets/viewer/overlay/video/progress_bar.dart index cdf245690..2c547f16a 100644 --- a/lib/widgets/viewer/overlay/video/progress_bar.dart +++ b/lib/widgets/viewer/overlay/video/progress_bar.dart @@ -74,7 +74,7 @@ class _VideoProgressBarState extends State { ), child: MediaQuery( data: MediaQuery.of(context).copyWith( - textScaleFactor: 1, + textScaler: TextScaler.noScaling, ), child: Column( key: _progressBarKey, @@ -154,14 +154,14 @@ class _VideoProgressBarState extends State { Widget _buildMuteIndicator() => StreamBuilder( stream: controller?.volumeStream ?? Stream.value(1.0), builder: (context, snapshot) { - final textScaleFactor = MediaQuery.textScaleFactorOf(context); + final textScaler = MediaQuery.textScalerOf(context); final isMuted = controller?.isMuted ?? false; return isMuted ? Padding( padding: const EdgeInsetsDirectional.only(end: 8), child: Icon( AIcons.mute, - size: 16 * textScaleFactor, + size: textScaler.scale(16), ), ) : const SizedBox(); diff --git a/lib/widgets/viewer/overlay/viewer_buttons.dart b/lib/widgets/viewer/overlay/viewer_buttons.dart index bc80e1141..4c3069986 100644 --- a/lib/widgets/viewer/overlay/viewer_buttons.dart +++ b/lib/widgets/viewer/overlay/viewer_buttons.dart @@ -28,6 +28,7 @@ import 'package:aves/widgets/viewer/action/entry_action_delegate.dart'; import 'package:aves/widgets/viewer/controls/notifications.dart'; import 'package:aves/widgets/viewer/video/conductor.dart'; import 'package:aves_model/aves_model.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; @@ -143,7 +144,7 @@ class _TvButtonRowContent extends StatelessWidget { final enabled = actionDelegate.canApply(action); return CaptionedButton( scale: scale, - iconButtonBuilder: (context, focusNode) => ViewerButtonRowContent._buildButtonIcon( + iconButtonBuilder: (context, focusNode) => _ViewerButtonRowContentState._buildButtonIcon( context: context, action: action, mainEntry: mainEntry, @@ -202,18 +203,15 @@ class _TvButtonRowContent extends StatelessWidget { } } -class ViewerButtonRowContent extends StatelessWidget { +class ViewerButtonRowContent extends StatefulWidget { final EntryActionDelegate actionDelegate; final List quickActions, topLevelActions, exportActions, videoActions; final Animation scale; final AvesEntry mainEntry, pageEntry; - final ValueNotifier _popupExpandedNotifier = ValueNotifier(null); - - AvesEntry get favouriteTargetEntry => mainEntry.isBurst ? pageEntry : mainEntry; static const double padding = 8; - ViewerButtonRowContent({ + const ViewerButtonRowContent({ super.key, required this.actionDelegate, required this.quickActions, @@ -225,8 +223,32 @@ class ViewerButtonRowContent extends StatelessWidget { required this.pageEntry, }); + @override + State createState() => _ViewerButtonRowContentState(); +} + +class _ViewerButtonRowContentState extends State { + final ValueNotifier _popupExpandedNotifier = ValueNotifier(null); + + AvesEntry get mainEntry => widget.mainEntry; + + AvesEntry get pageEntry => widget.pageEntry; + + AvesEntry get favouriteTargetEntry => mainEntry.isBurst ? pageEntry : mainEntry; + + static const double padding = ViewerButtonRowContent.padding; + + @override + void dispose() { + _popupExpandedNotifier.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { + final topLevelActions = widget.topLevelActions; + final exportActions = widget.exportActions; + final videoActions = widget.videoActions; final hasOverflowMenu = pageEntry.canRotate || pageEntry.canFlip || topLevelActions.isNotEmpty || exportActions.isNotEmpty || videoActions.isNotEmpty; return Selector( selector: (context, vc) => vc.getController(pageEntry), @@ -236,12 +258,12 @@ class ViewerButtonRowContent extends StatelessWidget { child: Row( children: [ const Spacer(), - ...quickActions.map((action) => _buildOverlayButton(context, action, videoController)), + ...widget.quickActions.map((action) => _buildOverlayButton(context, action, videoController)), if (hasOverflowMenu) Padding( padding: const EdgeInsets.symmetric(horizontal: padding / 2), child: OverlayButton( - scale: scale, + scale: widget.scale, child: FontSizeIconTheme( child: AvesPopupMenuButton( key: const Key('entry-menu-button'), @@ -258,9 +280,9 @@ class ViewerButtonRowContent extends StatelessWidget { icon: AIcons.export, title: context.l10n.entryActionExport, items: [ - ...exportInternalActions.map((action) => _buildPopupMenuItem(context, action, videoController)).toList(), + ...exportInternalActions.map((action) => _buildPopupMenuItem(context, action, videoController)), if (exportInternalActions.isNotEmpty && exportExternalActions.isNotEmpty) const PopupMenuDivider(height: 0), - ...exportExternalActions.map((action) => _buildPopupMenuItem(context, action, videoController)).toList(), + ...exportExternalActions.map((action) => _buildPopupMenuItem(context, action, videoController)), ], ), if (videoActions.isNotEmpty) @@ -270,7 +292,7 @@ class ViewerButtonRowContent extends StatelessWidget { icon: AIcons.video, title: context.l10n.settingsVideoSectionTitle, items: [ - ...videoActions.map((action) => _buildPopupMenuItem(context, action, videoController)).toList(), + ...videoActions.map((action) => _buildPopupMenuItem(context, action, videoController)), ], ), if (!kReleaseMode) ...[ @@ -282,7 +304,7 @@ class ViewerButtonRowContent extends StatelessWidget { onSelected: (action) { _popupExpandedNotifier.value = null; // wait for the popup menu to hide before proceeding with the action - Future.delayed(ADurations.popupMenuAnimation * timeDilation, () => actionDelegate.onActionSelected(context, action)); + Future.delayed(ADurations.popupMenuAnimation * timeDilation, () => widget.actionDelegate.onActionSelected(context, action)); }, onCanceled: () { _popupExpandedNotifier.value = null; @@ -309,14 +331,14 @@ class ViewerButtonRowContent extends StatelessWidget { return Padding( padding: const EdgeInsets.symmetric(horizontal: padding / 2), child: OverlayButton( - scale: scale, + scale: widget.scale, child: _buildButtonIcon( context: context, action: action, mainEntry: mainEntry, pageEntry: pageEntry, videoController: videoController, - actionDelegate: actionDelegate, + actionDelegate: widget.actionDelegate, ), ), ); @@ -375,13 +397,14 @@ class ViewerButtonRowContent extends StatelessWidget { Widget buildItem(EntryAction action) => Expanded( child: Material( + color: Colors.transparent, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(8)), ), clipBehavior: Clip.antiAlias, child: PopupMenuItem( value: action, - enabled: actionDelegate.canApply(action), + enabled: widget.actionDelegate.canApply(action), child: Tooltip( message: action.getText(context), child: Center(child: action.getIcon()), @@ -417,15 +440,18 @@ class ViewerButtonRowContent extends StatelessWidget { Widget? child; void onPressed() => actionDelegate.onActionSelected(context, action); - ValueListenableBuilder _buildFromListenable(ValueListenable? enabledNotifier) { - return ValueListenableBuilder( - valueListenable: enabledNotifier ?? ValueNotifier(false), - builder: (context, canDo, child) => IconButton( - icon: child!, - onPressed: canDo ? onPressed : null, - focusNode: focusNode, - tooltip: action.getText(context), - ), + Widget _buildFromListenable(ValueListenable? enabledNotifier) { + return NullableValueListenableBuilder( + valueListenable: enabledNotifier, + builder: (context, value, child) { + final canDo = value ?? false; + return IconButton( + icon: child!, + onPressed: canDo ? onPressed : null, + focusNode: focusNode, + tooltip: action.getText(context), + ); + }, child: action.getIcon(), ); } diff --git a/lib/widgets/viewer/page_entry_builder.dart b/lib/widgets/viewer/page_entry_builder.dart index 97fcb5db0..b50c7fb3b 100644 --- a/lib/widgets/viewer/page_entry_builder.dart +++ b/lib/widgets/viewer/page_entry_builder.dart @@ -1,6 +1,7 @@ import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/multipage.dart'; import 'package:aves/widgets/viewer/multipage/controller.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:flutter/widgets.dart'; class PageEntryBuilder extends StatelessWidget { @@ -20,8 +21,8 @@ class PageEntryBuilder extends StatelessWidget { stream: controller != null ? controller.infoStream : Stream.value(null), builder: (context, snapshot) { final multiPageInfo = controller?.info; - return ValueListenableBuilder( - valueListenable: controller?.pageNotifier ?? ValueNotifier(null), + return NullableValueListenableBuilder( + valueListenable: controller?.pageNotifier, builder: (context, page, child) { final pageEntry = multiPageInfo?.getPageEntryByIndex(page); return builder(pageEntry); diff --git a/lib/widgets/viewer/panorama_page.dart b/lib/widgets/viewer/panorama_page.dart index 464dc5dc8..aa9c74691 100644 --- a/lib/widgets/viewer/panorama_page.dart +++ b/lib/widgets/viewer/panorama_page.dart @@ -11,7 +11,6 @@ import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/media_query.dart'; import 'package:aves/widgets/common/identity/buttons/overlay_button.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:panorama/panorama.dart'; @@ -50,17 +49,16 @@ class _PanoramaPageState extends State { @override void dispose() { - _overlayVisible.removeListener(_onOverlayVisibleChanged); + _overlayVisible.dispose(); + _sensorControl.dispose(); super.dispose(); } @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () { - _onLeave(); - return SynchronousFuture(true); - }, + return PopScope( + canPop: true, + onPopInvoked: (didPop) => _onLeave(), child: AvesScaffold( body: Stack( children: [ diff --git a/lib/widgets/viewer/slideshow_page.dart b/lib/widgets/viewer/slideshow_page.dart index 2d59ac63a..12331b58a 100644 --- a/lib/widgets/viewer/slideshow_page.dart +++ b/lib/widgets/viewer/slideshow_page.dart @@ -125,6 +125,9 @@ class _SlideshowPageState extends State { _viewerController.autopilot = true; case SlideshowAction.showInCollection: _showInCollection(); + case SlideshowAction.cast: + // ignore, as it should be handled at the viewer level + break; case SlideshowAction.settings: _showSettings(context); } diff --git a/lib/widgets/viewer/video/conductor.dart b/lib/widgets/viewer/video/conductor.dart index 8557b20c2..aa75ab20c 100644 --- a/lib/widgets/viewer/video/conductor.dart +++ b/lib/widgets/viewer/video/conductor.dart @@ -9,6 +9,7 @@ import 'package:aves/widgets/viewer/video/db_playback_state_handler.dart'; import 'package:aves_model/aves_model.dart'; import 'package:aves_video/aves_video.dart'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; class VideoConductor { final CollectionLens? _collection; @@ -18,13 +19,24 @@ class VideoConductor { static const _defaultMaxControllerCount = 3; - VideoConductor({CollectionLens? collection}) : _collection = collection; + VideoConductor({CollectionLens? collection}) : _collection = collection { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$VideoConductor', + object: this, + ); + } + } Future dispose() async { - await disposeAll(); + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _subscriptions ..forEach((sub) => sub.cancel()) ..clear(); + await _disposeAll(); _controllers.clear(); if (settings.keepScreenOn == KeepScreenOn.videoPlayback) { await windowService.keepScreenOn(false); @@ -81,7 +93,7 @@ class VideoConductor { Future _applyToAll(FutureOr Function(AvesVideoController controller) action) => Future.forEach(_controllers, action); - Future disposeAll() => _applyToAll((controller) => controller.dispose()); + Future _disposeAll() => _applyToAll((controller) => controller.dispose()); Future pauseAll() => _applyToAll((controller) => controller.pause()); diff --git a/lib/widgets/viewer/view/conductor.dart b/lib/widgets/viewer/view/conductor.dart index be2a6a751..9cfa8fdf7 100644 --- a/lib/widgets/viewer/view/conductor.dart +++ b/lib/widgets/viewer/view/conductor.dart @@ -12,7 +12,20 @@ class ViewStateConductor { static const maxControllerCount = 3; + ViewStateConductor() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$ViewStateConductor', + object: this, + ); + } + } + Future dispose() async { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _controllers.forEach((v) => v.dispose()); _controllers.clear(); } @@ -60,6 +73,10 @@ class ViewStateConductor { entry, ...?entry.burstEntries, }.map((v) => v.uri).toSet(); - _controllers.removeWhere((v) => uris.contains(v.entry.uri)); + final entryControllers = _controllers.where((v) => uris.contains(v.entry.uri)).toSet(); + entryControllers.forEach((controller) { + _controllers.remove(controller); + controller.dispose(); + }); } } diff --git a/lib/widgets/viewer/view/controller.dart b/lib/widgets/viewer/view/controller.dart index 481163a6c..5b7451ce0 100644 --- a/lib/widgets/viewer/view/controller.dart +++ b/lib/widgets/viewer/view/controller.dart @@ -1,6 +1,7 @@ import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/view_state.dart'; import 'package:aves/widgets/viewer/view/histogram.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; class ViewStateController with HistogramMixin { @@ -13,9 +14,21 @@ class ViewStateController with HistogramMixin { ViewStateController({ required this.entry, required this.viewStateNotifier, - }); + }) { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$ViewStateController', + object: this, + ); + } + } void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } viewStateNotifier.dispose(); + fullImageNotifier.dispose(); } } diff --git a/lib/widgets/viewer/visual/entry_page_view.dart b/lib/widgets/viewer/visual/entry_page_view.dart index 5ab65e474..3b598c336 100644 --- a/lib/widgets/viewer/visual/entry_page_view.dart +++ b/lib/widgets/viewer/visual/entry_page_view.dart @@ -84,6 +84,7 @@ class _EntryPageViewState extends State with SingleTickerProvider void dispose() { _unregisterWidget(widget); widget.onDisposed?.call(); + _actionFeedbackChildNotifier.dispose(); super.dispose(); } @@ -267,14 +268,14 @@ class _EntryPageViewState extends State with SingleTickerProvider var move = Offset.zero; var dropped = false; double? startValue; - final valueNotifier = ValueNotifier(null); + ValueNotifier? valueNotifier; onScaleStart = (details, doubleTap, boundaries) { dropped = details.pointerCount > 1 || doubleTap; if (dropped) return; startValue = null; - valueNotifier.value = null; + valueNotifier = ValueNotifier(null); final alignmentX = details.focalPoint.dx / boundaries.viewportSize.width; final action = alignmentX > .5 ? SwipeAction.volume : SwipeAction.brightness; action.get().then((v) => startValue = v); @@ -283,15 +284,17 @@ class _EntryPageViewState extends State with SingleTickerProvider _actionFeedbackOverlayEntry = OverlayEntry( builder: (context) => SwipeActionFeedback( action: action, - valueNotifier: valueNotifier, + valueNotifier: valueNotifier!, ), ); Overlay.of(context).insert(_actionFeedbackOverlayEntry!); }; onScaleUpdate = (details) { + if (valueNotifier == null) return false; + move += details.focalPointDelta; dropped |= details.pointerCount > 1; - if (valueNotifier.value == null) { + if (valueNotifier!.value == null) { dropped |= MagnifierGestureRecognizer.isXPan(move); } if (dropped) return false; @@ -299,15 +302,19 @@ class _EntryPageViewState extends State with SingleTickerProvider final _startValue = startValue; if (_startValue != null) { final double value = (_startValue - move.dy / SwipeActionFeedback.height).clamp(0, 1); - valueNotifier.value = value; + valueNotifier!.value = value; swipeAction?.set(value); } return true; }; onScaleEnd = (details) { - if (_actionFeedbackOverlayEntry != null) { - _actionFeedbackOverlayEntry!.remove(); - _actionFeedbackOverlayEntry = null; + valueNotifier?.dispose(); + + final overlayEntry = _actionFeedbackOverlayEntry; + _actionFeedbackOverlayEntry = null; + if (overlayEntry != null) { + overlayEntry.remove(); + overlayEntry.dispose(); } }; } @@ -479,6 +486,7 @@ class _EntryPageViewState extends State with SingleTickerProvider } double? _getSideRatio() { + if (!mounted) return null; final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; return isPortrait ? 1 / 5 : 1 / 8; } diff --git a/lib/widgets/viewer/visual/raster.dart b/lib/widgets/viewer/visual/raster.dart index c90f8744b..7e38eed88 100644 --- a/lib/widgets/viewer/visual/raster.dart +++ b/lib/widgets/viewer/visual/raster.dart @@ -88,6 +88,7 @@ class _RasterImageViewState extends State { @override void dispose() { + _fullImageLoaded.dispose(); _unregisterFullImage(); super.dispose(); } diff --git a/lib/widgets/viewer/visual/vector.dart b/lib/widgets/viewer/visual/vector.dart index fbc535646..eae983d37 100644 --- a/lib/widgets/viewer/visual/vector.dart +++ b/lib/widgets/viewer/visual/vector.dart @@ -78,6 +78,7 @@ class _VectorImageViewState extends State { @override void dispose() { + _fullImageLoaded.dispose(); _unregisterFullImage(); super.dispose(); } diff --git a/lib/widgets/viewer/visual/video/cover.dart b/lib/widgets/viewer/visual/video/cover.dart index 0d6cb9aca..3a09a897f 100644 --- a/lib/widgets/viewer/visual/video/cover.dart +++ b/lib/widgets/viewer/visual/video/cover.dart @@ -79,6 +79,7 @@ class _VideoCoverState extends State { @override void dispose() { _unregisterWidget(widget); + _videoCoverInfoNotifier.dispose(); super.dispose(); } diff --git a/lib/widgets/viewer/visual/video/subtitle/subtitle.dart b/lib/widgets/viewer/visual/video/subtitle/subtitle.dart index 517c99002..1855fe33c 100644 --- a/lib/widgets/viewer/visual/video/subtitle/subtitle.dart +++ b/lib/widgets/viewer/visual/video/subtitle/subtitle.dart @@ -153,13 +153,14 @@ class VideoSubtitles extends StatelessWidget { var transform = Matrix4.identity(); if (position != null) { - final para = RenderParagraph( + final paragraph = RenderParagraph( TextSpan(children: spans), textDirection: TextDirection.ltr, - textScaleFactor: MediaQuery.textScaleFactorOf(context), + textScaler: MediaQuery.textScalerOf(context), )..layout(const BoxConstraints()); - final textWidth = para.getMaxIntrinsicWidth(double.infinity); - final textHeight = para.getMaxIntrinsicHeight(double.infinity); + final textWidth = paragraph.getMaxIntrinsicWidth(double.infinity); + final textHeight = paragraph.getMaxIntrinsicHeight(double.infinity); + paragraph.dispose(); late double anchorOffsetX, anchorOffsetY; switch (textHAlign) { diff --git a/lib/widgets/wallpaper_page.dart b/lib/widgets/wallpaper_page.dart index 5d9311ac7..0d438810d 100644 --- a/lib/widgets/wallpaper_page.dart +++ b/lib/widgets/wallpaper_page.dart @@ -31,9 +31,9 @@ class WallpaperPage extends StatelessWidget { final AvesEntry? entry; const WallpaperPage({ - Key? key, + super.key, required this.entry, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -60,9 +60,9 @@ class EntryEditor extends StatefulWidget { final AvesEntry entry; const EntryEditor({ - Key? key, + super.key, required this.entry, - }) : super(key: key); + }); @override State createState() => _EntryEditorState(); @@ -119,10 +119,10 @@ class _EntryEditorState extends State with EntryViewControllerMixin @override void dispose() { cleanEntryControllers(entry); - _viewerController.dispose(); - _videoActionDelegate.dispose(); + _overlayVisible.dispose(); _overlayAnimationController.dispose(); - _overlayVisible.removeListener(_onOverlayVisibleChanged); + _videoActionDelegate.dispose(); + _viewerController.dispose(); super.dispose(); } diff --git a/plugins/aves_magnifier/lib/src/controller/controller.dart b/plugins/aves_magnifier/lib/src/controller/controller.dart index e57d35c10..24fb27ba3 100644 --- a/plugins/aves_magnifier/lib/src/controller/controller.dart +++ b/plugins/aves_magnifier/lib/src/controller/controller.dart @@ -4,6 +4,7 @@ import 'package:aves_magnifier/src/controller/state.dart'; import 'package:aves_magnifier/src/scale/scale_boundaries.dart'; import 'package:aves_magnifier/src/scale/scale_level.dart'; import 'package:aves_magnifier/src/scale/state.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; class AvesMagnifierController { @@ -19,6 +20,13 @@ class AvesMagnifierController { AvesMagnifierController({ MagnifierState? initialState, }) : super() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$AvesMagnifierController', + object: this, + ); + } const source = ChangeSource.internal; initial = initialState ?? const MagnifierState(position: Offset.zero, scale: null, source: source); previousState = initial; @@ -31,6 +39,16 @@ class AvesMagnifierController { _setScaleState(_initialScaleState); } + void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } + _disposed = true; + _stateStreamController.close(); + _scaleBoundariesStreamController.close(); + _scaleStateChangeStreamController.close(); + } + Stream get stateStream => _stateStreamController.stream; Stream get scaleBoundariesStream => _scaleBoundariesStreamController.stream; @@ -51,13 +69,6 @@ class AvesMagnifierController { bool get isZooming => scaleState.state == ScaleState.zoomedIn || scaleState.state == ScaleState.zoomedOut; - void dispose() { - _disposed = true; - _stateStreamController.close(); - _scaleBoundariesStreamController.close(); - _scaleStateChangeStreamController.close(); - } - void update({ Offset? position, double? scale, diff --git a/plugins/aves_magnifier/lib/src/controller/controller_delegate.dart b/plugins/aves_magnifier/lib/src/controller/controller_delegate.dart index 26955113c..8ab9907de 100644 --- a/plugins/aves_magnifier/lib/src/controller/controller_delegate.dart +++ b/plugins/aves_magnifier/lib/src/controller/controller_delegate.dart @@ -27,8 +27,8 @@ mixin AvesMagnifierControllerDelegate on State { final List _subscriptions = []; void registerDelegate(AvesMagnifier widget) { - _subscriptions.add(widget.controller.stateStream.listen(_onMagnifierStateChange)); - _subscriptions.add(widget.controller.scaleStateChangeStream.listen(_onScaleStateChange)); + _subscriptions.add(widget.controller.stateStream.listen(_onMagnifierStateChanged)); + _subscriptions.add(widget.controller.scaleStateChangeStream.listen(_onScaleStateChanged)); } void unregisterDelegate(AvesMagnifier oldWidget) { @@ -38,7 +38,7 @@ mixin AvesMagnifierControllerDelegate on State { ..clear(); } - void _onScaleStateChange(ScaleStateChange scaleStateChange) { + void _onScaleStateChanged(ScaleStateChange scaleStateChange) { if (scaleStateChange.source == ChangeSource.internal) return; if (!controller.hasScaleSateChanged) return; @@ -66,7 +66,7 @@ mixin AvesMagnifierControllerDelegate on State { _animateScale = animateScale; } - void _onMagnifierStateChange(MagnifierState state) { + void _onMagnifierStateChanged(MagnifierState state) { final boundaries = scaleBoundaries; if (boundaries == null) return; diff --git a/plugins/aves_magnifier/lib/src/core/gesture_detector.dart b/plugins/aves_magnifier/lib/src/core/gesture_detector.dart index eac8c8e18..c737108a5 100644 --- a/plugins/aves_magnifier/lib/src/core/gesture_detector.dart +++ b/plugins/aves_magnifier/lib/src/core/gesture_detector.dart @@ -37,6 +37,12 @@ class MagnifierGestureDetector extends StatefulWidget { class _MagnifierGestureDetectorState extends State { final ValueNotifier doubleTapDetails = ValueNotifier(null); + @override + void dispose() { + doubleTapDetails.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final gestureSettings = MediaQuery.gestureSettingsOf(context); diff --git a/plugins/aves_magnifier/lib/src/pan/gesture_detector_scope.dart b/plugins/aves_magnifier/lib/src/pan/gesture_detector_scope.dart index 0ac3689a6..d46412132 100644 --- a/plugins/aves_magnifier/lib/src/pan/gesture_detector_scope.dart +++ b/plugins/aves_magnifier/lib/src/pan/gesture_detector_scope.dart @@ -27,8 +27,8 @@ class MagnifierGestureDetectorScope extends InheritedWidget { this.touchSlopFactor = .8, this.escapeByFling = true, this.acceptPointerEvent, - required Widget child, - }) : super(child: child); + required super.child, + }); static MagnifierGestureDetectorScope? maybeOf(BuildContext context) { return context.dependOnInheritedWidgetOfExactType(); diff --git a/plugins/aves_magnifier/pubspec.lock b/plugins/aves_magnifier/pubspec.lock index 490b26e85..4a5a1557f 100644 --- a/plugins/aves_magnifier/pubspec.lock +++ b/plugins/aves_magnifier/pubspec.lock @@ -20,10 +20,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" equatable: dependency: "direct main" description: @@ -41,18 +41,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -65,10 +65,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" nested: dependency: transitive description: @@ -81,10 +81,10 @@ packages: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" sky_engine: dependency: transitive description: flutter @@ -102,10 +102,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=1.16.0" diff --git a/plugins/aves_magnifier/pubspec.yaml b/plugins/aves_magnifier/pubspec.yaml index d51f0aa56..66636960d 100644 --- a/plugins/aves_magnifier/pubspec.yaml +++ b/plugins/aves_magnifier/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_map/lib/src/controller.dart b/plugins/aves_map/lib/src/controller.dart index 7f853c2a8..a321057d3 100644 --- a/plugins/aves_map/lib/src/controller.dart +++ b/plugins/aves_map/lib/src/controller.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:aves_map/src/zoomed_bounds.dart'; +import 'package:flutter/foundation.dart'; import 'package:latlong2/latlong.dart'; class AvesMapController { @@ -16,7 +17,20 @@ class AvesMapController { Stream get markerLocationChanges => _events.where((event) => event is MapMarkerLocationChangeEvent).cast(); + AvesMapController() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$AvesMapController', + object: this, + ); + } + } + void dispose() { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _streamController.close(); } diff --git a/plugins/aves_map/lib/src/geo_entry.dart b/plugins/aves_map/lib/src/geo_entry.dart index 046ce4c1b..4699e38aa 100644 --- a/plugins/aves_map/lib/src/geo_entry.dart +++ b/plugins/aves_map/lib/src/geo_entry.dart @@ -6,22 +6,14 @@ class GeoEntry extends Clusterable { GeoEntry({ this.entry, - double? latitude, - double? longitude, - bool? isCluster = false, - int? clusterId, - int? pointsSize, - String? markerId, - String? childMarkerId, - }) : super( - latitude: latitude, - longitude: longitude, - isCluster: isCluster, - clusterId: clusterId, - pointsSize: pointsSize, - markerId: markerId, - childMarkerId: childMarkerId, - ); + super.latitude, + super.longitude, + super.isCluster = false, + super.clusterId, + super.pointsSize, + super.markerId, + super.childMarkerId, + }); factory GeoEntry.createCluster(BaseCluster cluster, double longitude, double latitude) { return GeoEntry( diff --git a/plugins/aves_map/lib/src/marker/image.dart b/plugins/aves_map/lib/src/marker/image.dart index 3a2c33f52..d8ade2496 100644 --- a/plugins/aves_map/lib/src/marker/image.dart +++ b/plugins/aves_map/lib/src/marker/image.dart @@ -79,7 +79,7 @@ class ImageMarker extends StatelessWidget { Container( padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 2), decoration: ShapeDecoration( - color: theme.colorScheme.secondary, + color: theme.colorScheme.primary, shape: Directionality.of(context) == TextDirection.rtl ? CustomRoundedRectangleBorder( leftSide: borderSide, diff --git a/plugins/aves_map/pubspec.lock b/plugins/aves_map/pubspec.lock index 3cb0de8f7..261a1a3d8 100644 --- a/plugins/aves_map/pubspec.lock +++ b/plugins/aves_map/pubspec.lock @@ -36,10 +36,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" custom_rounded_rectangle_border: dependency: "direct main" description: @@ -73,26 +73,26 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_map: dependency: "direct main" description: name: flutter_map - sha256: e625957146c7d2e847da2cdefd893d6f5315ced6ee5228d2c05fec760cab3ad7 + sha256: "2b925948b675ef74ca524179fb133dbe0a21741889ccf56ad08fc8dcc38ba94b" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.0.1" http: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_parser: dependency: transitive description: @@ -121,10 +121,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" lists: dependency: transitive description: @@ -153,10 +153,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -201,10 +201,10 @@ packages: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" sky_engine: dependency: transitive description: flutter @@ -262,10 +262,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" wkt_parser: dependency: transitive description: @@ -275,5 +275,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.10.0" diff --git a/plugins/aves_map/pubspec.yaml b/plugins/aves_map/pubspec.yaml index 9509d443f..7fc819894 100644 --- a/plugins/aves_map/pubspec.yaml +++ b/plugins/aves_map/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_model/lib/src/actions/entry.dart b/plugins/aves_model/lib/src/actions/entry.dart index e0a6b68c9..96369cfc1 100644 --- a/plugins/aves_model/lib/src/actions/entry.dart +++ b/plugins/aves_model/lib/src/actions/entry.dart @@ -33,6 +33,7 @@ enum EntryAction { openVideo, openMap, setAs, + cast, // platform rotateScreen, // metadata @@ -82,6 +83,7 @@ class EntryActions { EntryAction.open, EntryAction.openMap, EntryAction.setAs, + EntryAction.cast, ]; static const pageActions = { diff --git a/plugins/aves_model/lib/src/actions/slideshow.dart b/plugins/aves_model/lib/src/actions/slideshow.dart index bb0facbd6..a4f7825a4 100644 --- a/plugins/aves_model/lib/src/actions/slideshow.dart +++ b/plugins/aves_model/lib/src/actions/slideshow.dart @@ -1,5 +1,6 @@ enum SlideshowAction { resume, showInCollection, + cast, settings, } diff --git a/plugins/aves_model/lib/src/settings/keys.dart b/plugins/aves_model/lib/src/settings/keys.dart index 36f03e422..77dd5b85e 100644 --- a/plugins/aves_model/lib/src/settings/keys.dart +++ b/plugins/aves_model/lib/src/settings/keys.dart @@ -183,4 +183,4 @@ class SettingKeys { // cf Android `Settings.Global.TRANSITION_ANIMATION_SCALE` static const platformTransitionAnimationScaleKey = 'transition_animation_scale'; -} \ No newline at end of file +} diff --git a/plugins/aves_model/pubspec.lock b/plugins/aves_model/pubspec.lock index 9edc64bfa..a9c9e5861 100644 --- a/plugins/aves_model/pubspec.lock +++ b/plugins/aves_model/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" equatable: dependency: "direct main" description: @@ -34,18 +34,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -58,10 +58,10 @@ packages: dependency: "direct main" description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sky_engine: dependency: transitive description: flutter @@ -79,9 +79,9 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/plugins/aves_model/pubspec.yaml b/plugins/aves_model/pubspec.yaml index fe317bde2..72d9ab0ba 100644 --- a/plugins/aves_model/pubspec.yaml +++ b/plugins/aves_model/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_platform_meta/android/build.gradle b/plugins/aves_platform_meta/android/build.gradle index 757ddd31e..e42ad0305 100644 --- a/plugins/aves_platform_meta/android/build.gradle +++ b/plugins/aves_platform_meta/android/build.gradle @@ -4,7 +4,7 @@ version '1.0-SNAPSHOT' buildscript { ext { kotlin_version = '1.8.21' - agp_version = '8.0.1' + agp_version = '8.1.2' } repositories { @@ -30,7 +30,7 @@ apply plugin: 'kotlin-android' android { namespace 'deckers.thibault.aves.aves_platform_meta' - compileSdkVersion 33 + compileSdk 34 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -52,6 +52,6 @@ android { } defaultConfig { - minSdkVersion 19 + minSdk 19 } } diff --git a/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties b/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties index 34953c675..817e87b6b 100644 --- a/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties +++ b/plugins/aves_platform_meta/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip \ No newline at end of file +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip \ No newline at end of file diff --git a/plugins/aves_platform_meta/pubspec.lock b/plugins/aves_platform_meta/pubspec.lock index 684b0b7d0..290892640 100644 --- a/plugins/aves_platform_meta/pubspec.lock +++ b/plugins/aves_platform_meta/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" flutter: dependency: "direct main" description: flutter @@ -26,18 +26,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -50,18 +50,18 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" plugin_platform_interface: dependency: "direct main" description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" sky_engine: dependency: transitive description: flutter @@ -79,9 +79,9 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/plugins/aves_platform_meta/pubspec.yaml b/plugins/aves_platform_meta/pubspec.yaml index c87c30f04..5b8468351 100644 --- a/plugins/aves_platform_meta/pubspec.yaml +++ b/plugins/aves_platform_meta/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_report/pubspec.lock b/plugins/aves_report/pubspec.lock index fe97c83ca..0d7dc5310 100644 --- a/plugins/aves_report/pubspec.lock +++ b/plugins/aves_report/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" flutter: dependency: "direct main" description: flutter @@ -26,18 +26,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -50,10 +50,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -87,9 +87,9 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/plugins/aves_report/pubspec.yaml b/plugins/aves_report/pubspec.yaml index ea3090164..5d8ef6fd9 100644 --- a/plugins/aves_report/pubspec.yaml +++ b/plugins/aves_report/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_report_console/pubspec.lock b/plugins/aves_report_console/pubspec.lock index e7335379f..4c0f5e17b 100644 --- a/plugins/aves_report_console/pubspec.lock +++ b/plugins/aves_report_console/pubspec.lock @@ -20,10 +20,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" flutter: dependency: "direct main" description: flutter @@ -33,18 +33,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -57,10 +57,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -94,9 +94,9 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/plugins/aves_report_console/pubspec.yaml b/plugins/aves_report_console/pubspec.yaml index 369f1c8b5..cdc06eb1c 100644 --- a/plugins/aves_report_console/pubspec.yaml +++ b/plugins/aves_report_console/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_report_crashlytics/pubspec.lock b/plugins/aves_report_crashlytics/pubspec.lock index e2260da37..7054c1619 100644 --- a/plugins/aves_report_crashlytics/pubspec.lock +++ b/plugins/aves_report_crashlytics/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: d84d98f1992976775f83083523a34c5d22fea191eec3abb2bd09537fb623c2e0 + sha256: eb0ac20f704799b986049fbb3c1c16421eca319a1b872378d669513e12452ba5 url: "https://pub.dev" source: hosted - version: "1.3.7" + version: "1.3.14" async: dependency: transitive description: @@ -52,10 +52,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" fake_async: dependency: transitive description: @@ -68,42 +68,42 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "95580fa07c8ca3072a2bb1fecd792616a33f8683477d25b7d29d3a6a399e6ece" + sha256: d301561d614487688d797717bef013a264c517d1d09e4c5c1325c3a64c835efb url: "https://pub.dev" source: hosted - version: "2.17.0" + version: "2.24.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: b63e3be6c96ef5c33bdec1aab23c91eb00696f6452f0519401d640938c94cba2 + sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "5.0.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: e8c408923cd3a25bd342c576a114f2126769cd1a57106a4edeaa67ea4a84e962 + sha256: "10159d9ee42c79f4548971d92f3f0fcd5791f6738cda3583a4e3b2c8b244c018" url: "https://pub.dev" source: hosted - version: "2.8.0" + version: "2.9.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: "833cf891d10e5e819a2034048ff7e8882bcc0b51055c0e17f5fe3f3c3c177a9d" + sha256: "60ef0016c0c2a7d16bf02468e3b27cd0ad4606f6d35535998dde3150cc0bc771" url: "https://pub.dev" source: hosted - version: "3.3.7" + version: "3.4.6" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: dfdf1172f35fc0b0132bc5ec815aed52c07643ee56732e6807ca7dc12f7fce86 + sha256: d185100facc6f7c43c5718103111488d008c52df8c19cbc5e5f9d2115d734909 url: "https://pub.dev" source: hosted - version: "3.6.7" + version: "3.6.14" flutter: dependency: "direct main" description: flutter @@ -113,10 +113,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_test: dependency: transitive description: flutter @@ -139,10 +139,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" matcher: dependency: transitive description: @@ -163,10 +163,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -179,10 +179,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" sky_engine: dependency: transitive description: flutter @@ -200,18 +200,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -232,10 +232,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" vector_math: dependency: transitive description: @@ -248,10 +248,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.3.0" diff --git a/plugins/aves_report_crashlytics/pubspec.yaml b/plugins/aves_report_crashlytics/pubspec.yaml index 9186c9e74..1ce3651ec 100644 --- a/plugins/aves_report_crashlytics/pubspec.yaml +++ b/plugins/aves_report_crashlytics/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_screen_state/android/build.gradle b/plugins/aves_screen_state/android/build.gradle index f8ca69e20..763daf679 100644 --- a/plugins/aves_screen_state/android/build.gradle +++ b/plugins/aves_screen_state/android/build.gradle @@ -4,7 +4,7 @@ version '1.0-SNAPSHOT' buildscript { ext { kotlin_version = '1.8.21' - agp_version = '8.0.1' + agp_version = '8.1.2' } repositories { @@ -30,7 +30,7 @@ apply plugin: 'kotlin-android' android { namespace 'deckers.thibault.aves.aves_screen_state' - compileSdkVersion 33 + compileSdk 34 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -52,6 +52,6 @@ android { } defaultConfig { - minSdkVersion 19 + minSdk 19 } } diff --git a/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties b/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties index 41681a771..d59564b34 100644 --- a/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties +++ b/plugins/aves_screen_state/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip \ No newline at end of file +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip \ No newline at end of file diff --git a/plugins/aves_screen_state/pubspec.lock b/plugins/aves_screen_state/pubspec.lock index 684b0b7d0..290892640 100644 --- a/plugins/aves_screen_state/pubspec.lock +++ b/plugins/aves_screen_state/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" flutter: dependency: "direct main" description: flutter @@ -26,18 +26,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -50,18 +50,18 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" plugin_platform_interface: dependency: "direct main" description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" sky_engine: dependency: transitive description: flutter @@ -79,9 +79,9 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/plugins/aves_screen_state/pubspec.yaml b/plugins/aves_screen_state/pubspec.yaml index 9b6570b39..8efc400d2 100644 --- a/plugins/aves_screen_state/pubspec.yaml +++ b/plugins/aves_screen_state/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_services/pubspec.lock b/plugins/aves_services/pubspec.lock index 0a575fd78..2509df018 100644 --- a/plugins/aves_services/pubspec.lock +++ b/plugins/aves_services/pubspec.lock @@ -43,10 +43,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" custom_rounded_rectangle_border: dependency: transitive description: @@ -80,26 +80,26 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_map: dependency: transitive description: name: flutter_map - sha256: e625957146c7d2e847da2cdefd893d6f5315ced6ee5228d2c05fec760cab3ad7 + sha256: "2b925948b675ef74ca524179fb133dbe0a21741889ccf56ad08fc8dcc38ba94b" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.0.1" http: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_parser: dependency: transitive description: @@ -128,10 +128,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" lists: dependency: transitive description: @@ -160,10 +160,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -208,10 +208,10 @@ packages: dependency: transitive description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" sky_engine: dependency: transitive description: flutter @@ -269,10 +269,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" wkt_parser: dependency: transitive description: @@ -282,5 +282,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.10.0" diff --git a/plugins/aves_services/pubspec.yaml b/plugins/aves_services/pubspec.yaml index b19e726a8..33c13b011 100644 --- a/plugins/aves_services/pubspec.yaml +++ b/plugins/aves_services/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_services_google/lib/src/map.dart b/plugins/aves_services_google/lib/src/map.dart index d78377e4f..58d48a037 100644 --- a/plugins/aves_services_google/lib/src/map.dart +++ b/plugins/aves_services_google/lib/src/map.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:aves_map/aves_map.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:latlong2/latlong.dart' as ll; @@ -66,12 +67,13 @@ class _EntryGoogleMapState extends State> with WidgetsBindi ZoomedBounds get bounds => boundsNotifier.value; static const uninitializedLatLng = LatLng(0, 0); + static const boundInitDelay = Duration(milliseconds: 100); @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); - _sizeNotifier.addListener(_onSizeChange); + _sizeNotifier.addListener(_onSizeChanged); _registerWidget(widget); } @@ -87,7 +89,7 @@ class _EntryGoogleMapState extends State> with WidgetsBindi _unregisterWidget(widget); _serviceMapController?.dispose(); WidgetsBinding.instance.removeObserver(this); - _sizeNotifier.removeListener(_onSizeChange); + _sizeNotifier.dispose(); super.dispose(); } @@ -140,37 +142,49 @@ class _EntryGoogleMapState extends State> with WidgetsBindi } Widget _buildMap() { - final _onMarkerLongPress = widget.onMarkerLongPress; return StreamBuilder( stream: _markerBitmapReadyStreamController.stream, builder: (context, _) { - final markers = {}; + final mediaMarkers = {}; _geoEntryByMarkerKey.forEach((markerKey, geoEntry) { final bytes = _markerBitmaps[markerKey]; if (bytes != null) { final point = LatLng(geoEntry.latitude!, geoEntry.longitude!); - markers.add(Marker( + mediaMarkers.add(Marker( markerId: MarkerId(geoEntry.markerId!), consumeTapEvents: true, icon: BitmapDescriptor.fromBytes(bytes), position: point, onTap: () => widget.onMarkerTap?.call(geoEntry), + // TODO TLAD [map] GoogleMap.onLongPress is not appropriate for mediaMarkers, so the call should be here when this is fixed: https://github.com/flutter/flutter/issues/107148 + // onLongPress: widget.onMarkerLongPress != null + // ? (v) { + // final pressLocation = _fromServiceLatLng(v); + // final mediaMarkers = _geoEntryByMarkerKey.values.toSet(); + // final geoEntry = ImageMarker.markerMatch(pressLocation, bounds.zoom, mediaMarkers); + // if (geoEntry != null) { + // widget.onMarkerLongPress?.call(geoEntry, pressLocation); + // } + // } + // : null, )); } }); final interactive = context.select((v) => v.interactive); final overlayEntry = widget.overlayEntry; - return ValueListenableBuilder( - valueListenable: widget.dotLocationNotifier ?? ValueNotifier(null), + return NullableValueListenableBuilder( + valueListenable: widget.dotLocationNotifier, builder: (context, dotLocation, child) { - return ValueListenableBuilder( - valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1), - builder: (context, overlayOpacity, child) { + return NullableValueListenableBuilder( + valueListenable: widget.overlayOpacityNotifier, + builder: (context, value, child) { + final double overlayOpacity = value ?? 1.0; return LayoutBuilder( builder: (context, constraints) { _sizeNotifier.value = constraints.biggest; - return GoogleMap( + return _GoogleMap( + dotLocationNotifier: widget.dotLocationNotifier ?? ValueNotifier(null), initialCameraPosition: CameraPosition( bearing: -bounds.rotation, target: _toServiceLatLng(bounds.projectedCenter), @@ -179,30 +193,17 @@ class _EntryGoogleMapState extends State> with WidgetsBindi onMapCreated: (controller) async { _serviceMapController = controller; final zoom = await controller.getZoomLevel(); + // the visible region is sometimes incorrect when queried right after creation, + await Future.delayed(boundInitDelay); await _updateVisibleRegion(zoom: zoom, rotation: bounds.rotation); - if (mounted) { - setState(() {}); - } + // `onCameraIdle` is not always automatically triggered following map creation + _onIdle(); }, - // compass disabled to use provider agnostic controls - compassEnabled: false, - mapToolbarEnabled: false, mapType: _toMapType(widget.style), minMaxZoomPreference: MinMaxZoomPreference(widget.minZoom, widget.maxZoom), - rotateGesturesEnabled: true, - scrollGesturesEnabled: interactive, - // zoom controls disabled to use provider agnostic controls - zoomControlsEnabled: false, - zoomGesturesEnabled: interactive, - // lite mode disabled because it lacks camera animation - liteModeEnabled: false, - // tilt disabled to match leaflet - tiltGesturesEnabled: false, - myLocationEnabled: false, - myLocationButtonEnabled: false, + interactive: interactive, markers: { - // TODO TLAD workaround for dot location marker not showing the last value until this is fixed: https://github.com/flutter/flutter/issues/103686 - ...markers, + ...mediaMarkers, if (dotLocation != null && _dotMarkerBitmap != null) Marker( markerId: const MarkerId('dot'), @@ -225,16 +226,6 @@ class _EntryGoogleMapState extends State> with WidgetsBindi onCameraMove: (position) => _updateVisibleRegion(zoom: position.zoom, rotation: -position.bearing), onCameraIdle: _onIdle, onTap: (v) => widget.onMapTap?.call(_fromServiceLatLng(v)), - onLongPress: _onMarkerLongPress != null - ? (v) { - final pressLocation = _fromServiceLatLng(v); - final markers = _geoEntryByMarkerKey.values.toSet(); - final geoEntry = ImageMarker.markerMatch(pressLocation, bounds.zoom, markers); - if (geoEntry != null) { - _onMarkerLongPress(geoEntry, pressLocation); - } - } - : null, ); }, ); @@ -248,11 +239,9 @@ class _EntryGoogleMapState extends State> with WidgetsBindi // sometimes the map does not properly update after changing the widget size, // so we monitor the size and force refreshing after an arbitrary small delay - // TODO TLAD [map] this workaround no longer works with Flutter beta v3.3.0-0.0.pre - Future _onSizeChange() async { - await Future.delayed(const Duration(milliseconds: 100)); - debugPrint('refresh map for size=${_sizeNotifier.value}'); - await _serviceMapController?.setMapStyle(null); + Future _onSizeChanged() async { + await Future.delayed(boundInitDelay); + _onIdle(); } void _onIdle() { @@ -278,12 +267,6 @@ class _EntryGoogleMapState extends State> with WidgetsBindi zoom: zoom, rotation: rotation, ); - } else { - // the visible region is sometimes uninitialized when queried right after creation, - // so we query it again next frame - WidgetsBinding.instance.addPostFrameCallback((_) { - _updateVisibleRegion(zoom: zoom, rotation: rotation); - }); } } @@ -345,3 +328,102 @@ class GmsGeoTiffTileProvider extends TileProvider { return TileProvider.noTile; } } + +class _GoogleMap extends StatefulWidget { + final ValueNotifier? dotLocationNotifier; + final CameraPosition initialCameraPosition; + final MapCreatedCallback? onMapCreated; + final MapType mapType; + final MinMaxZoomPreference minMaxZoomPreference; + final bool interactive; + final Set markers; + final Set tileOverlays; + final CameraPositionCallback? onCameraMove; + final VoidCallback? onCameraIdle; + final ArgumentCallback? onTap; + + const _GoogleMap({ + required this.dotLocationNotifier, + required this.initialCameraPosition, + required this.onMapCreated, + required this.mapType, + required this.minMaxZoomPreference, + required this.interactive, + required this.markers, + required this.tileOverlays, + required this.onCameraMove, + required this.onCameraIdle, + required this.onTap, + }); + + @override + State<_GoogleMap> createState() => _GoogleMapState(); +} + +class _GoogleMapState extends State<_GoogleMap> { + @override + void initState() { + super.initState(); + _registerWidget(widget); + } + + @override + void didUpdateWidget(covariant _GoogleMap oldWidget) { + super.didUpdateWidget(oldWidget); + _unregisterWidget(oldWidget); + _registerWidget(widget); + } + + @override + void dispose() { + _unregisterWidget(widget); + super.dispose(); + } + + void _registerWidget(_GoogleMap widget) { + widget.dotLocationNotifier?.addListener(_onDotLocationChanged); + } + + void _unregisterWidget(_GoogleMap widget) { + widget.dotLocationNotifier?.removeListener(_onDotLocationChanged); + } + + // TODO TLAD [map] remove when this is fixed: https://github.com/flutter/flutter/issues/103686 + Future _onDotLocationChanged() async { + // workaround for dot location marker not always reflecting the current location, + // despite `ValueListenableBuilder` on `widget.dotLocationNotifier` + await Future.delayed(const Duration(milliseconds: 100)); + if (mounted) { + setState(() {}); + } + } + + @override + Widget build(BuildContext context) { + return GoogleMap( + initialCameraPosition: widget.initialCameraPosition, + onMapCreated: widget.onMapCreated, + // compass disabled to use provider agnostic controls + compassEnabled: false, + mapToolbarEnabled: false, + mapType: widget.mapType, + minMaxZoomPreference: widget.minMaxZoomPreference, + rotateGesturesEnabled: true, + scrollGesturesEnabled: widget.interactive, + // zoom controls disabled to use provider agnostic controls + zoomControlsEnabled: false, + zoomGesturesEnabled: widget.interactive, + // lite mode disabled because it lacks camera animation + liteModeEnabled: false, + // tilt disabled to match leaflet + tiltGesturesEnabled: false, + myLocationEnabled: false, + myLocationButtonEnabled: false, + markers: widget.markers, + tileOverlays: widget.tileOverlays, + onCameraMove: widget.onCameraMove, + onCameraIdle: widget.onCameraIdle, + onTap: widget.onTap, + ); + } +} diff --git a/plugins/aves_services_google/pubspec.lock b/plugins/aves_services_google/pubspec.lock index 0745daee6..ff01098ca 100644 --- a/plugins/aves_services_google/pubspec.lock +++ b/plugins/aves_services_google/pubspec.lock @@ -30,6 +30,13 @@ packages: relative: true source: path version: "0.0.1" + aves_utils: + dependency: "direct main" + description: + path: "../aves_utils" + relative: true + source: path + version: "0.0.1" characters: dependency: transitive description: @@ -50,10 +57,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" csslib: dependency: transitive description: @@ -74,10 +81,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419" + sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.1" device_info_plus_platform_interface: dependency: transitive description: @@ -127,26 +134,26 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_map: dependency: transitive description: name: flutter_map - sha256: e625957146c7d2e847da2cdefd893d6f5315ced6ee5228d2c05fec760cab3ad7 + sha256: "2b925948b675ef74ca524179fb133dbe0a21741889ccf56ad08fc8dcc38ba94b" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.0.1" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.17" flutter_web_plugins: dependency: transitive description: flutter @@ -196,10 +203,10 @@ packages: dependency: "direct main" description: name: google_maps_flutter_android - sha256: e6cb018169e49332f88d23b1d2119b09e8ab4e7d3a1b889a1b7b3fd113e034ba + sha256: "4279a338b79288fad5c8b03e5ae6ec30888bff210e0bab10b1f31f31e5a90558" url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "2.6.0" google_maps_flutter_ios: dependency: transitive description: @@ -236,10 +243,10 @@ packages: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_parser: dependency: transitive description: @@ -284,10 +291,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" lists: dependency: transitive description: @@ -316,10 +323,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -348,10 +355,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" polylabel: dependency: transitive description: @@ -372,10 +379,10 @@ packages: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" sanitize_html: dependency: transitive description: @@ -449,18 +456,18 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" win32: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.1.0" win32_registry: dependency: transitive description: @@ -478,5 +485,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=3.1.0 <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.13.0" diff --git a/plugins/aves_services_google/pubspec.yaml b/plugins/aves_services_google/pubspec.yaml index c072898a4..41e580892 100644 --- a/plugins/aves_services_google/pubspec.yaml +++ b/plugins/aves_services_google/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: @@ -12,6 +12,8 @@ dependencies: path: ../aves_map aves_services: path: ../aves_services + aves_utils: + path: ../aves_utils device_info_plus: google_api_availability: google_maps_flutter: diff --git a/plugins/aves_services_huawei/lib/src/map.dart b/plugins/aves_services_huawei/lib/src/map.dart index a51a0354f..b53555ac0 100644 --- a/plugins/aves_services_huawei/lib/src/map.dart +++ b/plugins/aves_services_huawei/lib/src/map.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:aves_map/aves_map.dart'; +import 'package:aves_utils/aves_utils.dart'; import 'package:flutter/material.dart'; import 'package:huawei_map/huawei_map.dart'; import 'package:latlong2/latlong.dart' as ll; @@ -146,12 +147,13 @@ class _EntryHmsMapState extends State> { final interactive = context.select((v) => v.interactive); // final overlayEntry = widget.overlayEntry; - return ValueListenableBuilder( - valueListenable: widget.dotLocationNotifier ?? ValueNotifier(null), + return NullableValueListenableBuilder( + valueListenable: widget.dotLocationNotifier, builder: (context, dotLocation, child) { - return ValueListenableBuilder( - valueListenable: widget.overlayOpacityNotifier ?? ValueNotifier(1), - builder: (context, overlayOpacity, child) { + return NullableValueListenableBuilder( + valueListenable: widget.overlayOpacityNotifier, + builder: (context, value, child) { + // final double overlayOpacity = value ?? 1.0; return HuaweiMap( initialCameraPosition: CameraPosition( bearing: bounds.rotation, diff --git a/plugins/aves_services_huawei/pubspec.lock b/plugins/aves_services_huawei/pubspec.lock index 7ce676c1c..c02c76e5e 100644 --- a/plugins/aves_services_huawei/pubspec.lock +++ b/plugins/aves_services_huawei/pubspec.lock @@ -37,6 +37,13 @@ packages: relative: true source: path version: "0.0.1" + aves_utils: + dependency: "direct main" + description: + path: "../aves_utils" + relative: true + source: path + version: "0.0.1" characters: dependency: transitive description: @@ -57,10 +64,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" custom_rounded_rectangle_border: dependency: transitive description: @@ -94,26 +101,26 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_map: dependency: transitive description: name: flutter_map - sha256: e625957146c7d2e847da2cdefd893d6f5315ced6ee5228d2c05fec760cab3ad7 + sha256: "2b925948b675ef74ca524179fb133dbe0a21741889ccf56ad08fc8dcc38ba94b" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.0.1" http: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_parser: dependency: transitive description: @@ -160,10 +167,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" lists: dependency: transitive description: @@ -192,10 +199,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -224,10 +231,10 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" polylabel: dependency: transitive description: @@ -248,10 +255,10 @@ packages: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" sky_engine: dependency: transitive description: flutter @@ -309,10 +316,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" wkt_parser: dependency: transitive description: @@ -322,5 +329,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.10.0" diff --git a/plugins/aves_services_huawei/pubspec.yaml b/plugins/aves_services_huawei/pubspec.yaml index 1016be8e0..ad685727b 100644 --- a/plugins/aves_services_huawei/pubspec.yaml +++ b/plugins/aves_services_huawei/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: @@ -14,6 +14,8 @@ dependencies: path: ../aves_platform_meta aves_services: path: ../aves_services + aves_utils: + path: ../aves_utils # cf https://github.com/HMS-Core/hms-flutter-plugin/pull/296 huawei_hmsavailability: git: diff --git a/plugins/aves_services_none/pubspec.lock b/plugins/aves_services_none/pubspec.lock index 03b7b3396..3b9e8af2e 100644 --- a/plugins/aves_services_none/pubspec.lock +++ b/plugins/aves_services_none/pubspec.lock @@ -50,10 +50,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" custom_rounded_rectangle_border: dependency: transitive description: @@ -87,26 +87,26 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_map: dependency: transitive description: name: flutter_map - sha256: e625957146c7d2e847da2cdefd893d6f5315ced6ee5228d2c05fec760cab3ad7 + sha256: "2b925948b675ef74ca524179fb133dbe0a21741889ccf56ad08fc8dcc38ba94b" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.0.1" http: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_parser: dependency: transitive description: @@ -135,10 +135,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" lists: dependency: transitive description: @@ -167,10 +167,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -215,10 +215,10 @@ packages: dependency: transitive description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" sky_engine: dependency: transitive description: flutter @@ -276,10 +276,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" wkt_parser: dependency: transitive description: @@ -289,5 +289,5 @@ packages: source: hosted version: "2.0.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.10.0" diff --git a/plugins/aves_services_none/pubspec.yaml b/plugins/aves_services_none/pubspec.yaml index c189e084e..6b647c69d 100644 --- a/plugins/aves_services_none/pubspec.yaml +++ b/plugins/aves_services_none/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_ui/lib/src/dot.dart b/plugins/aves_ui/lib/src/dot.dart index 9ca5e2726..5ae99afb5 100644 --- a/plugins/aves_ui/lib/src/dot.dart +++ b/plugins/aves_ui/lib/src/dot.dart @@ -49,7 +49,7 @@ class AvesDot extends StatelessWidget { child: Container( width: diameter, height: diameter, - color: theme.colorScheme.secondary, + color: theme.colorScheme.primary, ), ), ), diff --git a/plugins/aves_ui/pubspec.lock b/plugins/aves_ui/pubspec.lock index ce8fcf357..fdb60c503 100644 --- a/plugins/aves_ui/pubspec.lock +++ b/plugins/aves_ui/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" flutter: dependency: "direct main" description: flutter @@ -26,18 +26,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -50,10 +50,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sky_engine: dependency: transitive description: flutter @@ -71,9 +71,9 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/plugins/aves_ui/pubspec.yaml b/plugins/aves_ui/pubspec.yaml index bc1256456..9e86b2f71 100644 --- a/plugins/aves_ui/pubspec.yaml +++ b/plugins/aves_ui/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_utils/lib/aves_utils.dart b/plugins/aves_utils/lib/aves_utils.dart index c2f027c4e..153778df2 100644 --- a/plugins/aves_utils/lib/aves_utils.dart +++ b/plugins/aves_utils/lib/aves_utils.dart @@ -1,6 +1,6 @@ library aves_utils; -export 'src/change_notifier.dart'; export 'src/colors.dart'; +export 'src/listenable.dart'; export 'src/optional_event_channel.dart'; export 'src/vector_utils.dart'; diff --git a/plugins/aves_utils/lib/src/change_notifier.dart b/plugins/aves_utils/lib/src/change_notifier.dart deleted file mode 100644 index 1e0b0b9f5..000000000 --- a/plugins/aves_utils/lib/src/change_notifier.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:flutter/foundation.dart'; - -// `ChangeNotifier` wrapper to call `notify` without constraint -class AChangeNotifier extends ChangeNotifier { - void notify() { - // why is this protected? - super.notifyListeners(); - } -} diff --git a/plugins/aves_utils/lib/src/listenable.dart b/plugins/aves_utils/lib/src/listenable.dart new file mode 100644 index 000000000..75701cd6d --- /dev/null +++ b/plugins/aves_utils/lib/src/listenable.dart @@ -0,0 +1,55 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +// `ChangeNotifier` wrapper to call `notify` without constraint +class AChangeNotifier extends ChangeNotifier { + void notify() { + // why is this protected? + super.notifyListeners(); + } +} + +// contrary to standard `ValueListenableBuilder`, this widget allows providing a null listenable +class NullableValueListenableBuilder extends StatefulWidget { + final ValueListenable? valueListenable; + final ValueWidgetBuilder builder; + final Widget? child; + + const NullableValueListenableBuilder({ + super.key, + required this.valueListenable, + required this.builder, + this.child, + }); + + @override + State createState() => _NullableValueListenableBuilderState(); +} + +class _NullableValueListenableBuilderState extends State> { + ValueNotifier? _internalValueListenable; + + ValueListenable get _valueListenable { + var listenable = widget.valueListenable; + if (listenable == null) { + _internalValueListenable ??= ValueNotifier(null); + listenable = _internalValueListenable; + } + return listenable!; + } + + @override + void dispose() { + _internalValueListenable?.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: _valueListenable, + builder: widget.builder, + child: widget.child, + ); + } +} diff --git a/plugins/aves_utils/pubspec.lock b/plugins/aves_utils/pubspec.lock index f5a37c6a6..03319746f 100644 --- a/plugins/aves_utils/pubspec.lock +++ b/plugins/aves_utils/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" flutter: dependency: "direct main" description: flutter @@ -26,18 +26,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -50,10 +50,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sky_engine: dependency: transitive description: flutter @@ -71,9 +71,9 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/plugins/aves_utils/pubspec.yaml b/plugins/aves_utils/pubspec.yaml index 52f2d1f3d..3544183b4 100644 --- a/plugins/aves_utils/pubspec.yaml +++ b/plugins/aves_utils/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_video/lib/src/controller.dart b/plugins/aves_video/lib/src/controller.dart index 7fb227629..9894fed83 100644 --- a/plugins/aves_video/lib/src/controller.dart +++ b/plugins/aves_video/lib/src/controller.dart @@ -9,16 +9,17 @@ abstract class AvesVideoControllerFactory { void init(); AvesVideoController buildController( - AvesEntryBase entry, { - required PlaybackStateHandler playbackStateHandler, - required VideoSettings settings, - }); + AvesEntryBase entry, { + required PlaybackStateHandler playbackStateHandler, + required VideoSettings settings, + }); } abstract class AvesVideoController { final AvesEntryBase _entry; final PlaybackStateHandler playbackStateHandler; final VideoSettings settings; + bool _disposed = false; AvesEntryBase get entry => _entry; @@ -29,11 +30,23 @@ abstract class AvesVideoController { required this.playbackStateHandler, required this.settings, }) : _entry = entry { + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectCreated( + library: 'aves', + className: '$AvesVideoController', + object: this, + ); + } entry.visualChangeNotifier.addListener(onVisualChanged); } @mustCallSuper Future dispose() async { + assert(!_disposed); + _disposed = true; + if (kFlutterMemoryAllocationsEnabled) { + MemoryAllocations.instance.dispatchObjectDisposed(object: this); + } _entry.visualChangeNotifier.removeListener(onVisualChanged); await _savePlaybackState(); } diff --git a/plugins/aves_video/pubspec.lock b/plugins/aves_video/pubspec.lock index 003e4bb71..0da6fab12 100644 --- a/plugins/aves_video/pubspec.lock +++ b/plugins/aves_video/pubspec.lock @@ -27,10 +27,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" equatable: dependency: transitive description: @@ -48,18 +48,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -72,10 +72,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sky_engine: dependency: transitive description: flutter @@ -93,9 +93,9 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/plugins/aves_video/pubspec.yaml b/plugins/aves_video/pubspec.yaml index 258cd6ded..6a11dc7a5 100644 --- a/plugins/aves_video/pubspec.yaml +++ b/plugins/aves_video/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_video_ffmpeg/pubspec.lock b/plugins/aves_video_ffmpeg/pubspec.lock index d3c918a91..67e6be94a 100644 --- a/plugins/aves_video_ffmpeg/pubspec.lock +++ b/plugins/aves_video_ffmpeg/pubspec.lock @@ -34,10 +34,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" equatable: dependency: transitive description: @@ -72,18 +72,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -96,18 +96,18 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" sky_engine: dependency: transitive description: flutter @@ -125,10 +125,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=2.0.0" diff --git a/plugins/aves_video_ffmpeg/pubspec.yaml b/plugins/aves_video_ffmpeg/pubspec.yaml index 7a1469924..866cc9e35 100644 --- a/plugins/aves_video_ffmpeg/pubspec.yaml +++ b/plugins/aves_video_ffmpeg/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_video_ijk/lib/src/controller.dart b/plugins/aves_video_ijk/lib/src/controller.dart index 56915091c..0b594406d 100644 --- a/plugins/aves_video_ijk/lib/src/controller.dart +++ b/plugins/aves_video_ijk/lib/src/controller.dart @@ -80,11 +80,19 @@ class IjkVideoController extends AvesVideoController { @override Future dispose() async { await super.dispose(); + _initialPlayTimer?.cancel(); _stopListening(); await _valueStreamController.close(); await _timedTextStreamController.close(); await _instance.release(); + + _completedNotifier.dispose(); + canCaptureFrameNotifier.dispose(); + canMuteNotifier.dispose(); + canSetSpeedNotifier.dispose(); + canSelectStreamNotifier.dispose(); + sarNotifier.dispose(); } void _startListening() { diff --git a/plugins/aves_video_ijk/pubspec.lock b/plugins/aves_video_ijk/pubspec.lock index 0529b3bd0..e91054b9e 100644 --- a/plugins/aves_video_ijk/pubspec.lock +++ b/plugins/aves_video_ijk/pubspec.lock @@ -34,10 +34,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" equatable: dependency: transitive description: @@ -64,18 +64,18 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -88,10 +88,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sky_engine: dependency: transitive description: flutter @@ -109,9 +109,9 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" diff --git a/plugins/aves_video_ijk/pubspec.yaml b/plugins/aves_video_ijk/pubspec.yaml index e1cdc9f77..b32a0f928 100644 --- a/plugins/aves_video_ijk/pubspec.yaml +++ b/plugins/aves_video_ijk/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/plugins/aves_video_mpv/lib/src/controller.dart b/plugins/aves_video_mpv/lib/src/controller.dart index 457cda6cf..5b36db0ca 100644 --- a/plugins/aves_video_mpv/lib/src/controller.dart +++ b/plugins/aves_video_mpv/lib/src/controller.dart @@ -64,11 +64,20 @@ class MpvVideoController extends AvesVideoController { @override Future dispose() async { await super.dispose(); + _stopListening(); _stopStreamFetchTimer(); await _statusStreamController.close(); await _timedTextStreamController.close(); await _instance.dispose(); + _controllerNotifier.dispose(); + + _completedNotifier.dispose(); + canCaptureFrameNotifier.dispose(); + canMuteNotifier.dispose(); + canSetSpeedNotifier.dispose(); + canSelectStreamNotifier.dispose(); + sarNotifier.dispose(); } void _startListening() { diff --git a/plugins/aves_video_mpv/pubspec.lock b/plugins/aves_video_mpv/pubspec.lock index 9e1686292..7ae539f49 100644 --- a/plugins/aves_video_mpv/pubspec.lock +++ b/plugins/aves_video_mpv/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: archive - sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" + sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" url: "https://pub.dev" source: hosted - version: "3.4.6" + version: "3.4.9" args: dependency: transitive description: @@ -58,10 +58,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" convert: dependency: transitive description: @@ -82,10 +82,10 @@ packages: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" equatable: dependency: transitive description: @@ -111,10 +111,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_web_plugins: dependency: transitive description: flutter @@ -124,10 +124,10 @@ packages: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_parser: dependency: transitive description: @@ -156,10 +156,10 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" material_color_utilities: dependency: transitive description: @@ -172,10 +172,10 @@ packages: dependency: "direct main" description: name: media_kit - sha256: "3dffc6d0c19117d51fbc42a7f89612e0595665800a596289ab7a80bdd93e0ad1" + sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" url: "https://pub.dev" source: hosted - version: "1.1.9" + version: "1.1.10+1" media_kit_libs_android_video: dependency: "direct main" description: @@ -196,26 +196,26 @@ packages: dependency: "direct main" description: name: media_kit_video - sha256: b8df9cf97aba1861af83b00ac16f5cac536debe0781a934a554b77c157a8f7e8 + sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.4" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" package_info_plus: dependency: transitive description: name: package_info_plus - sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "5.0.1" package_info_plus_platform_interface: dependency: transitive description: @@ -236,18 +236,18 @@ packages: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" pointycastle: dependency: transitive description: @@ -385,10 +385,10 @@ packages: dependency: transitive description: name: uuid - sha256: b715b8d3858b6fa9f68f87d20d98830283628014750c2b09b6f516c1da4af2a7 + sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.1" vector_math: dependency: transitive description: @@ -409,10 +409,10 @@ packages: dependency: transitive description: name: wakelock_plus - sha256: f45a6c03aa3f8322e0a9d7f4a0482721c8789cb41d555407367650b8f9c26018 + sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.4" wakelock_plus_platform_interface: dependency: transitive description: @@ -425,26 +425,26 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" win32: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.1.0" xml: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.4.2" sdks: - dart: ">=3.1.0-185.0.dev <4.0.0" + dart: ">=3.2.0 <4.0.0" flutter: ">=3.7.0" diff --git a/plugins/aves_video_mpv/pubspec.yaml b/plugins/aves_video_mpv/pubspec.yaml index 4a15a0563..4f2fb26d0 100644 --- a/plugins/aves_video_mpv/pubspec.yaml +++ b/plugins/aves_video_mpv/pubspec.yaml @@ -3,7 +3,7 @@ version: 0.0.1 publish_to: none environment: - sdk: ">=3.0.0 <4.0.0" + sdk: '>=3.2.0 <4.0.0' dependencies: flutter: diff --git a/pubspec.lock b/pubspec.lock index d9858601f..a6a7b3561 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,34 +5,34 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "64.0.0" _flutterfire_internals: dependency: transitive description: name: _flutterfire_internals - sha256: d84d98f1992976775f83083523a34c5d22fea191eec3abb2bd09537fb623c2e0 + sha256: eb0ac20f704799b986049fbb3c1c16421eca319a1b872378d669513e12452ba5 url: "https://pub.dev" source: hosted - version: "1.3.7" + version: "1.3.14" analyzer: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.2.0" archive: dependency: transitive description: name: archive - sha256: "7e0d52067d05f2e0324268097ba723b71cb41ac8a6a2b24d1edf9c536b987b03" + sha256: "7b875fd4a20b165a3084bd2d210439b22ebc653f21cea4842729c0c30c82596b" url: "https://pub.dev" source: hosted - version: "3.4.6" + version: "3.4.9" args: dependency: transitive description: @@ -202,18 +202,18 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: name: connectivity_plus - sha256: b502a681ba415272ecc41400bd04fe543ed1a62632137dc84d25a91e7746f55f + sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.2" connectivity_plus_platform_interface: dependency: transitive description: @@ -242,10 +242,10 @@ packages: dependency: transitive description: name: coverage - sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" url: "https://pub.dev" source: hosted - version: "1.6.3" + version: "1.6.4" crypto: dependency: transitive description: @@ -274,10 +274,10 @@ packages: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" decorated_icon: dependency: "direct main" description: @@ -290,10 +290,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "7035152271ff67b072a211152846e9f1259cf1be41e34cd3e0b5463d2d6b8419" + sha256: "0042cb3b2a76413ea5f8a2b40cec2a33e01d0c937e91f0f7c211fde4f7739ba6" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "9.1.1" device_info_plus_platform_interface: dependency: transitive description: @@ -302,14 +302,22 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + dlna_dart: + dependency: "direct main" + description: + name: dlna_dart + sha256: ae07c1c53077bbf58756fa589f936968719b0085441981d33e74f82f89d1d281 + url: "https://pub.dev" + source: hosted + version: "0.0.8" dynamic_color: dependency: "direct main" description: name: dynamic_color - sha256: "96bff3df72e3d428bda2b874c7a521e8c86f592cae626ea594922fcc8d166e0c" + sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f" url: "https://pub.dev" source: hosted - version: "1.6.7" + version: "1.6.8" equatable: dependency: "direct main" description: @@ -331,7 +339,7 @@ packages: description: path: "." ref: HEAD - resolved-ref: cfcb070b627f110d01dff88809dcea20faa13444 + resolved-ref: "7c4032fa0fc17b5a5d3fec1ac854afd6cd55670e" url: "https://github.com/deckerst/expansion_tile_card.git" source: git version: "3.0.0" @@ -380,42 +388,42 @@ packages: dependency: transitive description: name: firebase_core - sha256: "95580fa07c8ca3072a2bb1fecd792616a33f8683477d25b7d29d3a6a399e6ece" + sha256: d301561d614487688d797717bef013a264c517d1d09e4c5c1325c3a64c835efb url: "https://pub.dev" source: hosted - version: "2.17.0" + version: "2.24.0" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: b63e3be6c96ef5c33bdec1aab23c91eb00696f6452f0519401d640938c94cba2 + sha256: c437ae5d17e6b5cc7981cf6fd458a5db4d12979905f9aafd1fea930428a9fe63 url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "5.0.0" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: e8c408923cd3a25bd342c576a114f2126769cd1a57106a4edeaa67ea4a84e962 + sha256: "10159d9ee42c79f4548971d92f3f0fcd5791f6738cda3583a4e3b2c8b244c018" url: "https://pub.dev" source: hosted - version: "2.8.0" + version: "2.9.0" firebase_crashlytics: dependency: transitive description: name: firebase_crashlytics - sha256: "833cf891d10e5e819a2034048ff7e8882bcc0b51055c0e17f5fe3f3c3c177a9d" + sha256: "60ef0016c0c2a7d16bf02468e3b27cd0ad4606f6d35535998dde3150cc0bc771" url: "https://pub.dev" source: hosted - version: "3.3.7" + version: "3.4.6" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: dfdf1172f35fc0b0132bc5ec815aed52c07643ee56732e6807ca7dc12f7fce86 + sha256: d185100facc6f7c43c5718103111488d008c52df8c19cbc5e5f9d2115d734909 url: "https://pub.dev" source: hosted - version: "3.6.7" + version: "3.6.14" flex_color_picker: dependency: "direct main" description: @@ -486,16 +494,16 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "3.0.1" flutter_localization_nn: dependency: "direct main" description: path: "." ref: HEAD - resolved-ref: da1bd8f16cbce3fa60bae8fff478718fe9d6835e + resolved-ref: "051ba00b4a3edfdbf8d053ebfe946e76419912b9" url: "https://github.com/deckerst/flutter_localization_nn.git" source: git version: "0.0.1" @@ -508,26 +516,26 @@ packages: dependency: "direct main" description: name: flutter_map - sha256: e625957146c7d2e847da2cdefd893d6f5315ced6ee5228d2c05fec760cab3ad7 + sha256: "2b925948b675ef74ca524179fb133dbe0a21741889ccf56ad08fc8dcc38ba94b" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.0.1" flutter_markdown: dependency: "direct main" description: name: flutter_markdown - sha256: "8afc9a6aa6d8e8063523192ba837149dbf3d377a37c0b0fc579149a1fbd4a619" + sha256: "35108526a233cc0755664d445f8a6b4b61e6f8fe993b3658b80b4a26827fc196" url: "https://pub.dev" source: hosted - version: "0.6.18" + version: "0.6.18+2" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f185ac890306b5779ecbd611f52502d8d4d63d27703ef73161ca0407e815f02c + sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.17" flutter_staggered_animations: dependency: "direct main" description: @@ -619,10 +627,10 @@ packages: dependency: transitive description: name: google_maps_flutter_android - sha256: e6cb018169e49332f88d23b1d2119b09e8ab4e7d3a1b889a1b7b3fd113e034ba + sha256: "4279a338b79288fad5c8b03e5ae6ec30888bff210e0bab10b1f31f31e5a90558" url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "2.6.0" google_maps_flutter_ios: dependency: transitive description: @@ -667,10 +675,10 @@ packages: dependency: transitive description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.2" http_multi_server: dependency: transitive description: @@ -735,14 +743,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.0" + leak_tracker: + dependency: "direct main" + description: + name: leak_tracker + sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739" + url: "https://pub.dev" + source: hosted + version: "9.0.16" lints: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "3.0.0" lists: dependency: transitive description: @@ -763,18 +779,18 @@ packages: dependency: transitive description: name: local_auth_android - sha256: "9ad0b1ffa6f04f4d91e38c2d4c5046583e23f4cae8345776a994e8670df57fb1" + sha256: df4ccb3193525b8a60c78a5ca7bf188a47705bcf77bcc837a6b2cf6da64ae0e2 url: "https://pub.dev" source: hosted - version: "1.0.34" + version: "1.0.35" local_auth_ios: dependency: transitive description: name: local_auth_ios - sha256: "26a8d1ad0b4ef6f861d29921be8383000fda952e323a5b6752cf82ca9cf9a7a9" + sha256: "8293faf72ef0ac4710f209edd03916c2d4c1eeab0483bdcf9b2e659c2f7d737b" url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.1.5" local_auth_platform_interface: dependency: transitive description: @@ -843,10 +859,10 @@ packages: dependency: transitive description: name: media_kit - sha256: "3dffc6d0c19117d51fbc42a7f89612e0595665800a596289ab7a80bdd93e0ad1" + sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" url: "https://pub.dev" source: hosted - version: "1.1.9" + version: "1.1.10+1" media_kit_libs_android_video: dependency: transitive description: @@ -867,18 +883,18 @@ packages: dependency: transitive description: name: media_kit_video - sha256: b8df9cf97aba1861af83b00ac16f5cac536debe0781a934a554b77c157a8f7e8 + sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.4" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -912,6 +928,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + network_info_plus: + dependency: "direct main" + description: + name: network_info_plus + sha256: "2d9e88b9a459e5d4e224f828d26cc38ea140511e89b943116939994324be5c96" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + network_info_plus_platform_interface: + dependency: transitive + description: + name: network_info_plus_platform_interface + sha256: "881f5029c5edaf19c616c201d3d8b366c5b1384afd5c1da5a49e4345de82fb8b" + url: "https://pub.dev" + source: hosted + version: "1.1.3" nm: dependency: transitive description: @@ -948,10 +980,10 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "5.0.1" package_info_plus_platform_interface: dependency: transitive description: @@ -1029,10 +1061,10 @@ packages: dependency: "direct main" description: name: pdf - sha256: "9f75fc7f5580ea5e635b5724de58fb27f684c9ad03ed46fdc1aac768e4557315" + sha256: aa8835fcb9cfaf57ab2f1970e8548ceed3d0cb53eda7da906648f8153eaf37c9 url: "https://pub.dev" source: hosted - version: "3.10.4" + version: "3.10.6" percent_indicator: dependency: "direct main" description: @@ -1045,50 +1077,58 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8" + sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78" url: "https://pub.dev" source: hosted - version: "11.0.1" + version: "11.1.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e + sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6" url: "https://pub.dev" source: hosted - version: "11.1.0" + version: "12.0.1" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5" + sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306" + url: "https://pub.dev" + source: hosted + version: "9.2.0" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: d96ff56a757b7f04fa825c469d296c5aebc55f743e87bd639fef91a466a24da8 url: "https://pub.dev" source: hosted - version: "9.1.4" + version: "0.1.0+1" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4" + sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1 url: "https://pub.dev" source: hosted - version: "3.12.0" + version: "4.0.2" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004" url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.2.0" petitparser: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" pin_code_fields: dependency: "direct main" description: @@ -1101,18 +1141,18 @@ packages: dependency: transitive description: name: platform - sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.2" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8 url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.7" pointycastle: dependency: transitive description: @@ -1141,10 +1181,10 @@ packages: dependency: "direct main" description: name: printing - sha256: e7c383dca95ee7b88c02dc1c66638628d3dcdc2fb2cc47e7a595facd47e46b56 + sha256: ad39a42a5f83125952457dfd94f395c8cf0eb1f7759583dadb769be5c7f99d24 url: "https://pub.dev" source: hosted - version: "5.11.0" + version: "5.11.1" process: dependency: transitive description: @@ -1165,10 +1205,10 @@ packages: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" pub_semver: dependency: transitive description: @@ -1293,10 +1333,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_windows: dependency: transitive description: @@ -1306,7 +1346,7 @@ packages: source: hosted version: "2.3.2" shelf: - dependency: transitive + dependency: "direct main" description: name: shelf sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 @@ -1394,26 +1434,26 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.5.0+2" stack_trace: dependency: "direct main" description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1467,26 +1507,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.3" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.5.9" transparent_image: dependency: "direct main" description: @@ -1531,74 +1571,74 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: b1c9e98774adf8820c96fbc7ae3601231d324a7d5ebd8babe27b6dfac91357ba url: "https://pub.dev" source: hosted - version: "6.1.14" + version: "6.2.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.2.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.2.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e + sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.0" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.0" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "2942294a500b4fa0b918685aff406773ba0a4cd34b7f42198742a94083020ce5" + sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7" url: "https://pub.dev" source: hosted - version: "2.0.20" + version: "2.2.1" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" + sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.1.0" uuid: dependency: transitive description: name: uuid - sha256: b715b8d3858b6fa9f68f87d20d98830283628014750c2b09b6f516c1da4af2a7 + sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921 url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.2.1" vector_math: dependency: "direct main" description: @@ -1611,10 +1651,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "11.7.1" + version: "11.10.0" volume_controller: dependency: "direct main" description: @@ -1627,10 +1667,10 @@ packages: dependency: transitive description: name: wakelock_plus - sha256: f45a6c03aa3f8322e0a9d7f4a0482721c8789cb41d555407367650b8f9c26018 + sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.4" wakelock_plus_platform_interface: dependency: transitive description: @@ -1651,10 +1691,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1683,10 +1723,10 @@ packages: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "7c99c0e1e2fa190b48d25c81ca5e42036d5cac81430ef249027d97b0935c553f" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.1.0" win32_registry: dependency: transitive description: @@ -1715,10 +1755,10 @@ packages: dependency: "direct main" description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: af5e77e9b83f2f4adc5d3f0a4ece1c7f45a2467b695c2540381bac793e34e556 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.4.2" yaml: dependency: transitive description: @@ -1728,5 +1768,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.0 <4.0.0" - flutter: ">=3.13.7" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.1" diff --git a/pubspec.yaml b/pubspec.yaml index 3d171ea08..b2303691b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,14 +7,14 @@ repository: https://github.com/deckerst/aves # - play changelog: /whatsnew/whatsnew-en-US # - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt # - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt -version: 1.9.7+108 +version: 1.10.0+109 publish_to: none environment: # this project bundles Flutter SDK via `flutter_wrapper` # cf https://github.com/passsy/flutter_wrapper - flutter: 3.13.7 - sdk: ">=3.0.0 <4.0.0" + flutter: 3.16.1 + sdk: '>=3.2.0 <4.0.0' # use `scripts/apply_flavor_{flavor}.sh` to set the right dependencies for the flavor dependencies: @@ -63,6 +63,7 @@ dependencies: country_code: decorated_icon: device_info_plus: + dlna_dart: dynamic_color: equatable: event_bus: @@ -83,9 +84,11 @@ dependencies: get_it: intl: latlong2: + leak_tracker: local_auth: material_color_utilities: material_design_icons_flutter: + network_info_plus: overlay_support: package_info_plus: palette_generator: @@ -104,6 +107,7 @@ dependencies: provider: screen_brightness: shared_preferences: + shelf: smooth_page_indicator: sqflite: stack_trace: @@ -164,20 +168,20 @@ flutter: # adapts from package `flutter_highlight` v0.7.0 # # `OutputBuffer` in `/services/common/output_buffer.dart` -# adapts from Flutter v3.10.0 `_OutputBuffer` in `/foundation/consolidate_response.dart` +# adapts from Flutter v3.16.0 `_OutputBuffer` in `/foundation/consolidate_response.dart` # # `TvLicensePage` in `/widgets/about/tv_license_page.dart` -# adapts from Flutter v3.10.0 `_LicenseData` in `/material/about.dart` +# adapts from Flutter v3.16.0 `_LicenseData` in `/material/about.dart` # and `_PackageLicensePage` in `/material/about.dart` # # `OverlaySnackBar` in `/widgets/common/action_mixins/overlay_snack_bar.dart` -# adapts from Flutter v3.10.0 `SnackBar` in `/material/snack_bar.dart` +# adapts from Flutter v3.16.0 `SnackBar` in `/material/snack_bar.dart` # # `EagerScaleGestureRecognizer` in `/widgets/common/behaviour/eager_scale_gesture_recognizer.dart` -# adapts from Flutter v3.10.0 `ScaleGestureRecognizer` in `/gestures/scale.dart` +# adapts from Flutter v3.16.0 `ScaleGestureRecognizer` in `/gestures/scale.dart` # # `KnownExtentScrollPhysics` in `/widgets/common/behaviour/known_extent_scroll_physics.dart` -# adapts from Flutter v3.10.0 `FixedExtentScrollPhysics` in `/widgets/list_wheel_scroll_view.dart` +# adapts from Flutter v3.16.0 `FixedExtentScrollPhysics` in `/widgets/list_wheel_scroll_view.dart` # # `TransitionImage` in `/widgets/common/fx/transition_image.dart` # adapts from Flutter v3.10.0 `_ImageState` in `/widgets/image.dart` @@ -186,5 +190,5 @@ flutter: # `_RenderSliverKnownExtentBoxAdaptor` in `/widgets/common/grid/sliver.dart` # adapts from Flutter v3.10.0 `RenderSliverFixedExtentBoxAdaptor` in `/rendering/sliver_fixed_extent_list.dart` # -# `CollectionSearchDelegate`, `SearchPageRoute` in `/widgets/search/search_delegate.dart` -# adapts from Flutter v3.10.0 `SearchDelegate`, `_SearchPageRoute` in `/material/search.dart` +# `AvesSearchDelegate`, `SearchPageRoute` in `/widgets/common/search/*.dart` +# adapts from Flutter v3.16.0 `SearchDelegate`, `_SearchPageRoute` in `/material/search.dart` diff --git a/scripts/screenshot_post_process.sh b/scripts/screenshot_post_process.sh index 901ecb3f1..861b7cb9e 100755 --- a/scripts/screenshot_post_process.sh +++ b/scripts/screenshot_post_process.sh @@ -45,9 +45,10 @@ for source in overlay/*/*; do done mv izzy/en izzy/en-US mv izzy/es izzy/es-MX +mv izzy/nb izzy/nb-NO mv izzy/pt izzy/pt-BR mv izzy/zh izzy/zh-CN -mv izzy/zh_Hant izzy/zh-TW +mv izzy/zh_Hant izzy/zh-Hant # play: add device frame for source in overlay/*/*; do diff --git a/shaders.sksl.json b/shaders.sksl.json index e374a489a..fbaf64825 100644 --- a/shaders.sksl.json +++ b/shaders.sksl.json @@ -1 +1 @@ -{"platform":"android","name":"SM G970N","engineRevision":"a794cf2681c6c9fe7b260e0e84de96298dc9c18b","data":{"HWQACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1ONAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDQgZ2VvbVN1YnNldDsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpmbGF0IG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAB5AgAAZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKZmxhdCBpbiBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWZsb2F0NCBnZW9TdWJzZXQ7CglnZW9TdWJzZXQgPSB2Z2VvbVN1YnNldF9TMDsKCWhhbGY0IGRpc3RzNCA9IGNsYW1wKGhhbGY0KDEsIDEsIC0xLCAtMSkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIGdlb1N1YnNldCksIDAsIDEpOwoJaGFsZjIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWhhbGYgc3Vic2V0Q292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJY292ZXJhZ2UgPSBtaW4oY292ZXJhZ2UsIHN1YnNldENvdmVyYWdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","AYTRVAADQAAAOAEARAFQJAABBADAAAILBYAACCYUQD777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1NyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7CmluIGhhbGYzIGluQ2xpcFBsYW5lOwppbiBoYWxmMyBpbklzZWN0UGxhbmU7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGYzIHZpbkNsaXBQbGFuZV9TMDsKb3V0IGhhbGYzIHZpbklzZWN0UGxhbmVfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5DbGlwUGxhbmVfUzAgPSBpbkNsaXBQbGFuZTsKCXZpbklzZWN0UGxhbmVfUzAgPSBpbklzZWN0UGxhbmU7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gdWxvY2FsTWF0cml4X1MwLnh6ICogaW5Qb3NpdGlvbiArIHVsb2NhbE1hdHJpeF9TMC55dzsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAADdAwAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGYzIHZpbkNsaXBQbGFuZV9TMDsKaW4gaGFsZjMgdmluSXNlY3RQbGFuZV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGYzIGNsaXBQbGFuZTsKCWNsaXBQbGFuZSA9IHZpbkNsaXBQbGFuZV9TMDsKCWhhbGYzIGlzZWN0UGxhbmU7Cglpc2VjdFBsYW5lID0gdmluSXNlY3RQbGFuZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZiBkaXN0YW5jZVRvSW5uZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoZCAtIGNpcmNsZUVkZ2UudykpOwoJaGFsZiBpbm5lckFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb0lubmVyRWRnZSk7CgllZGdlQWxwaGEgKj0gaW5uZXJBbHBoYTsKCWhhbGYgY2xpcCA9IGhhbGYoc2F0dXJhdGUoY2lyY2xlRWRnZS56ICogZG90KGNpcmNsZUVkZ2UueHksIGNsaXBQbGFuZS54eSkgKyBjbGlwUGxhbmUueikpOwoJY2xpcCAqPSBoYWxmKHNhdHVyYXRlKGNpcmNsZUVkZ2UueiAqIGRvdChjaXJjbGVFZGdlLnh5LCBpc2VjdFBsYW5lLnh5KSArIGlzZWN0UGxhbmUueikpOwoJZWRnZUFscGhhICo9IGNsaXA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAMAAAAaW5DaXJjbGVFZGdlCwAAAGluQ2xpcFBsYW5lAAwAAABpbklzZWN0UGxhbmUAAAAA","DBAAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAIAAAAAAAAAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1NVAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludDIgY29vcmRzID0gaW50MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJaW50IHRleElkeCA9IGNvb3Jkcy54ID4+IDEzOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGNvb3Jkcy54ICYgMHgxRkZGLCBjb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAAEICAAB1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzFfUzA7CmluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQml0bWFwVGV4dAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJaGFsZjQgdGV4Q29sb3I7CglpZiAodlRleEluZGV4X1MwID09IDApIAoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWVsc2UgCgl7CgkJdGV4Q29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzFfUzAsIHZUZXh0dXJlQ29vcmRzX1MwKS5ycnJyOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSB0ZXhDb2xvcjsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","DAQAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAEAAAAAAAAAAAAIAD2AAAAAGQMH4A6TYBAAADAAAAACAAAAAAQCGHIGH6YNJQAAAABQAAAAAAAAAAAAFAAMAAAABAAAAAAABBAMAAA":"DAAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAABAAAARQUAAGNvbnN0IGludCBrRmlsbEJXX1MxX2MwID0gMDsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEJXX1MxX2MwID0gMjsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEFBX1MxX2MwID0gMzsKdW5pZm9ybSBmbG9hdDQgdXJlY3RVbmlmb3JtX1MxX2MwOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpmbGF0IGluIGZsb2F0IHZUZXhJbmRleF9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IFJlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGNvdmVyYWdlOwoJaWYgKGludCgxKSA9PSBrRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKGFsbChncmVhdGVyVGhhbihmbG9hdDQoc2tfRnJhZ0Nvb3JkLnh5LCB1cmVjdFVuaWZvcm1fUzFfYzAuencpLCBmbG9hdDQodXJlY3RVbmlmb3JtX1MxX2MwLnh5LCBza19GcmFnQ29vcmQueHkpKSkpOwoJfQoJZWxzZSAKCXsKCQloYWxmNCBkaXN0czQgPSBzYXR1cmF0ZShoYWxmNCgxLjAsIDEuMCwgLTEuMCwgLTEuMCkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIHVyZWN0VW5pZm9ybV9TMV9jMCkpOwoJCWhhbGYyIGRpc3RzMiA9IChkaXN0czQueHkgKyBkaXN0czQuencpIC0gMS4wOwoJCWNvdmVyYWdlID0gZGlzdHMyLnggKiBkaXN0czIueTsKCX0KCWlmIChpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzFfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEFBX1MxX2MwKSAKCXsKCQljb3ZlcmFnZSA9IDEuMCAtIGNvdmVyYWdlOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGNvdmVyYWdlKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShSZWN0X1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQml0bWFwVGV4dAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJaGFsZjQgdGV4Q29sb3I7Cgl7CgkJdGV4Q29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHZUZXh0dXJlQ29vcmRzX1MwKS5ycnJyOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSB0ZXhDb2xvcjsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1PUAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoBAAAAoAIAAHVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQMAAAAAAABAEAAAABJYQAAAAAACAIAAAAAWCBAAAIBAAAAANAECAZAAAAQAAAAAAFAAMAAAABAAAAAAABBAM":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAC8GAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmMiB1SW5jcmVtZW50X1MxX2MwOwp1bmlmb3JtIGhhbGYyIHVPZmZzZXRzQW5kS2VybmVsX1MxX2MwWzEzXTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglmbG9hdDIgaW5Db29yZCA9IF9jb29yZHM7CglmbG9hdDIgc3Vic2V0Q29vcmQ7CglzdWJzZXRDb29yZC54ID0gaW5Db29yZC54OwoJc3Vic2V0Q29vcmQueSA9IGluQ29vcmQueTsKCWZsb2F0MiBjbGFtcGVkQ29vcmQ7CgljbGFtcGVkQ29vcmQueCA9IHN1YnNldENvb3JkLng7CgljbGFtcGVkQ29vcmQueSA9IGNsYW1wKHN1YnNldENvb3JkLnksIHVjbGFtcF9TMV9jMF9jMF9jMC55LCB1Y2xhbXBfUzFfYzBfYzBfYzAudyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBTbW9vdGhfUzFfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgY29vcmQsIGhhbGYyIG9mZnNldEFuZEtlcm5lbCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX2lucHV0LCAoY29vcmQgKyBvZmZzZXRBbmRLZXJuZWwueCAqIHVJbmNyZW1lbnRfUzFfYzApKSAqIG9mZnNldEFuZEtlcm5lbC55Owp9CmhhbGY0IEdhdXNzaWFuQ29udm9sdXRpb25fUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgY29sb3IgPSBoYWxmNCgwKTsKCWZsb2F0MiBjb29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZm9yIChpbnQgaT0wOyBpPDEzOyArK2kpIAoJewoJCWNvbG9yICs9IFNtb290aF9TMV9jMChfaW5wdXQsIGNvb3JkLCB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFtpXSk7Cgl9CglyZXR1cm4gY29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBHYXVzc2lhbkNvbnZvbHV0aW9uX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1PUAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoAAAAAEQEAAGZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","EADQAAAAAEAAAAAUAABQAAQPAAABCFYMAAKAUEAAAAAAAAABAAAAAAAAAAANAAIAAAABAAAAACAJAAIAAAAA":"DAAAAExTS1NyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7CmluIGZsb2F0MyBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgZmxvYXQyIHZJbnRUZXh0dXJlQ29vcmRzX1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIERpc3RhbmNlRmllbGRQYXRoCglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJdkludFRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkczsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MyBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwejsKfQoAAAAAAACfAgAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7CmluIGZsb2F0MiB2SW50VGV4dHVyZUNvb3Jkc19TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBEaXN0YW5jZUZpZWxkUGF0aAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQyIHV2ID0gdlRleHR1cmVDb29yZHNfUzA7CgloYWxmNCB0ZXhDb2xvcjsKCXsKCQl0ZXhDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdXYpLnJycnI7Cgl9CgloYWxmIGRpc3RhbmNlID0gNy45Njg3NSoodGV4Q29sb3IuciAtIDAuNTAxOTYwNzg0MzEpOwoJaGFsZiBhZndpZHRoOwoJYWZ3aWR0aCA9IGFicygwLjY1KmhhbGYoZEZkeCh2SW50VGV4dHVyZUNvb3Jkc19TMC54KSkpOwoJaGFsZiB2YWwgPSBzbW9vdGhzdGVwKC1hZndpZHRoLCBhZndpZHRoLCBkaXN0YW5jZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KHZhbCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","HUQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1PPAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAEAAACbAgAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGhhbGY0IHZjb2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQAAEAQAAAAGQCBAMQAAAIAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAHADAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgaW5Db29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBzdWJzZXRDb29yZC54OwoJY2xhbXBlZENvb3JkLnkgPSBjbGFtcChzdWJzZXRDb29yZC55LCB1Y2xhbXBfUzFfYzAueSwgdWNsYW1wX1MxX2MwLncpOwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBjbGFtcGVkQ29vcmQpOwoJcmV0dXJuIHRleHR1cmVDb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFIBVWKMG7QAAAAAAGVXOVKVEAAAAADAAAAAFQ454NUDACAAAIAQZUOAMQAAAAIAAAAAEARTKLVK5LSAAAAABQAAAAAYGP6G2BSBAAAAAIO2HAGIAAAAAAAEAAAAAZ3RZTY3IGAEAAAAAAAAAGARI4UKA4WAAAAAAAEAAAABSMQII2XAGAAAAAAAAAAACAA4AAAACAAAAAAACCAYAA":"DAAAAExTS1OAAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNV9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNV9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAIIGAAB1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMDsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNV9TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzAsIHVlbmRfUzFfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IExpbmVhckxheW91dF9TMV9jMF9jMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzJfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzNfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzVfUzA7CglyZXR1cm4gaGFsZjQoaGFsZjQoaGFsZihfdG1wXzNfY29vcmRzLngpICsgMWUtMDUsIDEuMCwgMC4wLCAwLjApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIExpbmVhckxheW91dF9TMV9jMF9jMV9jMChfaW5wdXQpOwp9CmhhbGY0IENsYW1wZWRHcmFkaWVudF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzRfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGY0IHQgPSBNYXRyaXhFZmZlY3RfUzFfYzBfYzEoX3RtcF80X2luQ29sb3IpOwoJaGFsZjQgb3V0Q29sb3I7CglpZiAoIWJvb2woaW50KDEpKSAmJiB0LnkgPCAwLjApIAoJewoJCW91dENvbG9yID0gaGFsZjQoMC4wKTsKCX0KCWVsc2UgaWYgKHQueCA8IDAuMCkgCgl7CgkJb3V0Q29sb3IgPSB1bGVmdEJvcmRlckNvbG9yX1MxX2MwOwoJfQoJZWxzZSBpZiAodC54ID4gMS4wKSAKCXsKCQlvdXRDb2xvciA9IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwOwoJfQoJZWxzZSAKCXsKCQlvdXRDb2xvciA9IFNpbmdsZUludGVydmFsQ29sb3JpemVyX1MxX2MwX2MwKF90bXBfNF9pbkNvbG9yLCBmbG9hdDIoaGFsZjIodC54LCAwLjApKSk7Cgl9CglyZXR1cm4gaGFsZjQob3V0Q29sb3IpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gQ2xhbXBlZEdyYWRpZW50X1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzVfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGACQAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAAAAACsAQAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACABYQA6AAAEAAAAAAAIADQAAAAIAAAAAAAIIDA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAB5AwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","DAQAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAEAAAAAAAAAAAAIAHSADQAAAQAAAAAAFAAMAAAABAAAAAAABBAM":"DAAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAABAAAAQAMAAHVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpmbGF0IGluIGZsb2F0IHZUZXhJbmRleF9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5UZXh0dXJlQ29vcmRzAAAAAAA=","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACABZQA6AAAEAAAAAAAIADQAAAAIAAAAAAAIIDA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAACPAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCWFscGhhID0gMS4wIC0gYWxwaGE7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGABZAA6IAAAAACAAAAAADUAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAA7AwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HVIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAFIBVWKMG7QAAAAAAGVXOVKVEAAAAADAAAAAAAAEAABMHHPDNAYAQAACAEGNDQDEAAAACAAAAABAEM2S5KXK4QAAAAAMAAAAAAAAQAABQM74NUDECAAAAAQ5UOAMQAAAAAAAIAACAAZBAAAAANQ4Z4NUDACAAAEAAAAACAI33BYT43IEAAAAAAAAAAAWARQ5UOAMQAAAAIAAAAAAABS4UIAYXAGAAAAAAAAAAA2AAQAAAAFAAAAAEASAAQAAAA":"DAAAAExTS1PlAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc182X1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KfQoAAAAAAAAAxwcAAHVuaWZvcm0gaGFsZjQgdXN0YXJ0X1MxX2MwX2MwX2MwOwp1bmlmb3JtIGhhbGY0IHVlbmRfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMTsKdW5pZm9ybSBoYWxmNCB1bGVmdEJvcmRlckNvbG9yX1MxX2MwOwp1bmlmb3JtIGhhbGY0IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzA7CmhhbGY0IFNpbmdsZUludGVydmFsQ29sb3JpemVyX1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gX2Nvb3JkczsKCXJldHVybiBoYWxmNChtaXgodXN0YXJ0X1MxX2MwX2MwX2MwLCB1ZW5kX1MxX2MwX2MwX2MwLCBoYWxmKF90bXBfMV9jb29yZHMueCkpKTsKfQpoYWxmNCBjb2xvcl94Zm9ybV9TMV9jMF9jMChmbG9hdDQgY29sb3IpIAp7Cgljb2xvci5yZ2IgKj0gY29sb3IuYTsKCXJldHVybiBoYWxmNChjb2xvcik7Cn0KaGFsZjQgQ29sb3JTcGFjZVhmb3JtX1MxX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gY29sb3JfeGZvcm1fUzFfYzBfYzAoU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzAoX2lucHV0LCBfY29vcmRzKSk7Cn0KaGFsZjQgTGluZWFyTGF5b3V0X1MxX2MwX2MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMl9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfM19jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKCXJldHVybiBoYWxmNChoYWxmNChoYWxmKF90bXBfM19jb29yZHMueCkgKyAxZS0wNSwgMS4wLCAwLjAsIDAuMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gTGluZWFyTGF5b3V0X1MxX2MwX2MxX2MwKF9pbnB1dCk7Cn0KaGFsZjQgQ2xhbXBlZEdyYWRpZW50X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfNF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjQgdCA9IE1hdHJpeEVmZmVjdF9TMV9jMF9jMShfdG1wXzRfaW5Db2xvcik7CgloYWxmNCBvdXRDb2xvcjsKCWlmICghYm9vbChpbnQoMSkpICYmIHQueSA8IDAuMCkgCgl7CgkJb3V0Q29sb3IgPSBoYWxmNCgwLjApOwoJfQoJZWxzZSBpZiAodC54IDwgMC4wKSAKCXsKCQlvdXRDb2xvciA9IHVsZWZ0Qm9yZGVyQ29sb3JfUzFfYzA7Cgl9CgllbHNlIGlmICh0LnggPiAxLjApIAoJewoJCW91dENvbG9yID0gdXJpZ2h0Qm9yZGVyQ29sb3JfUzFfYzA7Cgl9CgllbHNlIAoJewoJCW91dENvbG9yID0gQ29sb3JTcGFjZVhmb3JtX1MxX2MwX2MwKF90bXBfNF9pbkNvbG9yLCBmbG9hdDIoaGFsZjIodC54LCAwLjApKSk7Cgl9CglyZXR1cm4gaGFsZjQob3V0Q29sb3IpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gQ2xhbXBlZEdyYWRpZW50X1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzVfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gKGhhbGY0KDEuMCkgLSBvdXRwdXRfUzEpICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAAA5AAAAAAABAAAAACAZAAAAA":"DAAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgAAAABFAgAAZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2YXJjY29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBGaWxsUlJlY3RPcDo6UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgeF9wbHVzXzE9dmFyY2Nvb3JkX1MwLngsIHk9dmFyY2Nvb3JkX1MwLnk7CgloYWxmIGNvdmVyYWdlOwoJaWYgKDAgPT0geF9wbHVzXzEpIAoJewoJCWNvdmVyYWdlID0gaGFsZih5KTsKCX0KCWVsc2UgCgl7CgkJZmxvYXQgZm4gPSB4X3BsdXNfMSAqICh4X3BsdXNfMSAtIDIpOwoJCWZuID0gZm1hKHkseSwgZm4pOwoJCWZsb2F0IGZud2lkdGggPSBmd2lkdGgoZm4pOwoJCWNvdmVyYWdlID0gLjUgLSBoYWxmKGZuL2Zud2lkdGgpOwoJCWNvdmVyYWdlID0gY2xhbXAoY292ZXJhZ2UsIDAsIDEpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEADZABYAAAIAAAAAACQAGAAAAAQAAAAAAAQQG":"DAAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAADUAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdmFyY2Nvb3JkX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBGaWxsUlJlY3RPcDo6UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgeF9wbHVzXzE9dmFyY2Nvb3JkX1MwLngsIHk9dmFyY2Nvb3JkX1MwLnk7CgloYWxmIGNvdmVyYWdlOwoJaWYgKDAgPT0geF9wbHVzXzEpIAoJewoJCWNvdmVyYWdlID0gaGFsZih5KTsKCX0KCWVsc2UgCgl7CgkJZmxvYXQgZm4gPSB4X3BsdXNfMSAqICh4X3BsdXNfMSAtIDIpOwoJCWZuID0gZm1hKHkseSwgZm4pOwoJCWZsb2F0IGZud2lkdGggPSBmd2lkdGgoZm4pOwoJCWNvdmVyYWdlID0gLjUgLSBoYWxmKGZuL2Zud2lkdGgpOwoJCWNvdmVyYWdlID0gY2xhbXAoY292ZXJhZ2UsIDAsIDEpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","B2AAQAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1MOAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmIGluQ292ZXJhZ2U7Cm91dCBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cgl2aW5Db3ZlcmFnZV9TMCA9IGluQ292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAATQEAAHVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwppbiBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGVmYXVsdEdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdUNvbG9yX1MwOwoJaGFsZiBhbHBoYSA9IDEuMDsKCWFscGhhID0gdmluQ292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAAoAAABpbkNvdmVyYWdlAAAAAAAA","HVJAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAABAAAAAABBAMABAAOAAAABAAAAAAABBAMAAA":"DAAAAExTS1MjAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cgl2bG9jYWxDb29yZF9TMCA9IGxvY2FsQ29vcmQ7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAADdAQAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIG91dHB1dENvbG9yX1MwKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","HTQAAGAABBYAAAEIXBAAAGEAMAAAAAAAAAAAAAAAQAHAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1M/AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5RdWFkRWRnZTsKb3V0IGZsb2F0NCB2UXVhZEVkZ2VfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZEVkZ2UKCXZRdWFkRWRnZV9TMCA9IGluUXVhZEVkZ2U7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgABAAAABQMAAGluIGZsb2F0NCB2UXVhZEVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZEVkZ2UKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGYgZWRnZUFscGhhOwoJaGFsZjIgZHV2ZHggPSBoYWxmMihkRmR4KHZRdWFkRWRnZV9TMC54eSkpOwoJaGFsZjIgZHV2ZHkgPSBoYWxmMihkRmR5KHZRdWFkRWRnZV9TMC54eSkpOwoJaWYgKHZRdWFkRWRnZV9TMC56ID4gMC4wICYmIHZRdWFkRWRnZV9TMC53ID4gMC4wKSAKCXsKCQllZGdlQWxwaGEgPSBoYWxmKG1pbihtaW4odlF1YWRFZGdlX1MwLnosIHZRdWFkRWRnZV9TMC53KSArIDAuNSwgMS4wKSk7Cgl9CgllbHNlIAoJewoJCWhhbGYyIGdGID0gaGFsZjIoaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHgueCAtIGR1dmR4LnkpLCAgICAgICAgICAgICAgICAgaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHkueCAtIGR1dmR5LnkpKTsKCQllZGdlQWxwaGEgPSBoYWxmKHZRdWFkRWRnZV9TMC54KnZRdWFkRWRnZV9TMC54IC0gdlF1YWRFZGdlX1MwLnkpOwoJCWVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNSAtIGVkZ2VBbHBoYSAvIGxlbmd0aChnRikpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IACgAAAGluUXVhZEVkZ2UAAAAAAAA=","DASAAAAAQAAWAABAYAAQBYH7777Z6QQBAEAAAAAAEAAAAAAAEBSAAAB2AAAAAAACAAAAAEBSAAAAA":"DAAAAExTS1PVAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBCaXRtYXBUZXh0CglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc1NpemVJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAADgAQAAdW5pZm9ybSBoYWxmNCB1Q29sb3JfUzA7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdlRleHR1cmVDb29yZHNfUzA7CmZsYXQgaW4gZmxvYXQgdlRleEluZGV4X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQml0bWFwVGV4dAoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHVDb2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCk7Cgl9CglvdXRwdXRDb2xvcl9TMCA9IG91dHB1dENvbG9yX1MwICogdGV4Q29sb3I7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAKAAAAaW5Qb3NpdGlvbgAADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAGIBIAAABAAAAANAEAAAAAAAAAAAAAABAAOAAAABAAAAAAABBAMAAAAA":"DAAAAExTS1N0AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAHMCAAB1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwKS5ycnJyOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","AYQQ5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAB7AgAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGYgZGlzdGFuY2VUb0lubmVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKGQgLSBjaXJjbGVFZGdlLncpKTsKCWhhbGYgaW5uZXJBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9Jbm5lckVkZ2UpOwoJZWRnZUFscGhhICo9IGlubmVyQWxwaGE7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","DAQAAAAAAABGAABAYAAQAIHCAIAYAQUBAEAAAAAAEAAAAAAAAAAAAAB2AAAAAAACAAAAAEBSAAAAA":"DAAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAsQEAAHVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdlRleHR1cmVDb29yZHNfUzA7CmZsYXQgaW4gZmxvYXQgdlRleEluZGV4X1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAPAAAAaW5UZXh0dXJlQ29vcmRzAAAAAAA=","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAADqAQAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","CMRQCIAABBYAAAEIXBAAACDQMAABRAFAAAAAAAAAAAAAAAEABYAAAAEAAAAAAAEEBQAAAAA":"DAAAAExTS1MyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0MiBpbkVsbGlwc2VPZmZzZXQ7CmluIGZsb2F0NCBpbkVsbGlwc2VSYWRpaTsKb3V0IGZsb2F0MiB2RWxsaXBzZU9mZnNldHNfUzA7Cm91dCBmbG9hdDQgdkVsbGlwc2VSYWRpaV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCXZFbGxpcHNlT2Zmc2V0c19TMCA9IGluRWxsaXBzZU9mZnNldDsKCXZFbGxpcHNlUmFkaWlfUzAgPSBpbkVsbGlwc2VSYWRpaTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAHIDAABpbiBmbG9hdDIgdkVsbGlwc2VPZmZzZXRzX1MwOwppbiBmbG9hdDQgdkVsbGlwc2VSYWRpaV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0MiBvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHk7CglvZmZzZXQgKj0gdkVsbGlwc2VSYWRpaV9TMC54eTsKCWZsb2F0IHRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZmxvYXQyIGdyYWQgPSAyLjAqb2Zmc2V0KnZFbGxpcHNlUmFkaWlfUzAueHk7CglmbG9hdCBncmFkX2RvdCA9IGRvdChncmFkLCBncmFkKTsKCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjE3NTVlLTM4KTsKCWZsb2F0IGludmxlbiA9IGludmVyc2VzcXJ0KGdyYWRfZG90KTsKCWZsb2F0IGVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNS10ZXN0Kmludmxlbik7CglvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHkqdkVsbGlwc2VSYWRpaV9TMC56dzsKCXRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZ3JhZCA9IDIuMCpvZmZzZXQqdkVsbGlwc2VSYWRpaV9TMC56dzsKCWdyYWRfZG90ID0gZG90KGdyYWQsIGdyYWQpOwoJaW52bGVuID0gaW52ZXJzZXNxcnQoZ3JhZF9kb3QpOwoJZWRnZUFscGhhICo9IHNhdHVyYXRlKDAuNSt0ZXN0Kmludmxlbik7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGhhbGYoZWRnZUFscGhhKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpbkVsbGlwc2VPZmZzZXQADgAAAGluRWxsaXBzZVJhZGlpAAAAAAAA","HWQACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1ONAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDQgZ2VvbVN1YnNldDsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpmbGF0IG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAAIBAAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdCB2Y292ZXJhZ2VfUzA7CmZsYXQgaW4gZmxvYXQ0IHZnZW9tU3Vic2V0X1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWZsb2F0NCBnZW9TdWJzZXQ7CglnZW9TdWJzZXQgPSB2Z2VvbVN1YnNldF9TMDsKCWhhbGY0IGRpc3RzNCA9IGNsYW1wKGhhbGY0KDEsIDEsIC0xLCAtMSkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIGdlb1N1YnNldCksIDAsIDEpOwoJaGFsZjIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWhhbGYgc3Vic2V0Q292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJY292ZXJhZ2UgPSBtaW4oY292ZXJhZ2UsIHN1YnNldENvdmVyYWdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQBAAAQAAAAGQCBAMQACAAAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAHADAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgaW5Db29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBjbGFtcChzdWJzZXRDb29yZC54LCB1Y2xhbXBfUzFfYzAueCwgdWNsYW1wX1MxX2MwLnopOwoJY2xhbXBlZENvb3JkLnkgPSBzdWJzZXRDb29yZC55OwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBjbGFtcGVkQ29vcmQpOwoJcmV0dXJuIHRleHR1cmVDb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQBAEAQAAAAGQCBAMQACAIAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAE0DAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgaW5Db29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkID0gY2xhbXAoc3Vic2V0Q29vcmQsIHVjbGFtcF9TMV9jMC54eSwgdWNsYW1wX1MxX2MwLnp3KTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFQBVWKMG7QAAAAAAAAAACAAAAAVQEAAQAAAAAQCDAEQQGAAAAAAAAAAAA4IAPAAACAAAAAAAEABYAAAAEAAAAAAAEEBQA":"DAAAAExTS1N6AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAABAAAArAQAAHVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMjsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzI7CnVuaWZvcm0gc2FtcGxlckV4dGVybmFsT0VTIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKGhhbGY0IF9pbnB1dCkgCnsKCV9pbnB1dCA9IE1hdHJpeEVmZmVjdF9TMV9jMChfaW5wdXQpOwoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglyZXR1cm4gaGFsZjQoX2lucHV0KTsKfQpoYWxmNCBDaXJjdWxhclJSZWN0X1MyKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMi5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMi5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MyLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBDaXJjdWxhclJSZWN0X1MyKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRfUzI7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAIAAAAcG9zaXRpb24FAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","AYAA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1OCAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpvdXQgZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMl9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAADqAQAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQMAAAAAAIAAEAAAABJYQAAAAAQAAIAAAAAWCBACAABAAAAANAECAZAAEAAAAAAAAFAAMAAAABAAAAAAABBAM":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAC8GAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmMiB1SW5jcmVtZW50X1MxX2MwOwp1bmlmb3JtIGhhbGYyIHVPZmZzZXRzQW5kS2VybmVsX1MxX2MwWzEzXTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglmbG9hdDIgaW5Db29yZCA9IF9jb29yZHM7CglmbG9hdDIgc3Vic2V0Q29vcmQ7CglzdWJzZXRDb29yZC54ID0gaW5Db29yZC54OwoJc3Vic2V0Q29vcmQueSA9IGluQ29vcmQueTsKCWZsb2F0MiBjbGFtcGVkQ29vcmQ7CgljbGFtcGVkQ29vcmQueCA9IGNsYW1wKHN1YnNldENvb3JkLngsIHVjbGFtcF9TMV9jMF9jMF9jMC54LCB1Y2xhbXBfUzFfYzBfYzBfYzAueik7CgljbGFtcGVkQ29vcmQueSA9IHN1YnNldENvb3JkLnk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBTbW9vdGhfUzFfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgY29vcmQsIGhhbGYyIG9mZnNldEFuZEtlcm5lbCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX2lucHV0LCAoY29vcmQgKyBvZmZzZXRBbmRLZXJuZWwueCAqIHVJbmNyZW1lbnRfUzFfYzApKSAqIG9mZnNldEFuZEtlcm5lbC55Owp9CmhhbGY0IEdhdXNzaWFuQ29udm9sdXRpb25fUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgY29sb3IgPSBoYWxmNCgwKTsKCWZsb2F0MiBjb29yZCA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZm9yIChpbnQgaT0wOyBpPDEzOyArK2kpIAoJewoJCWNvbG9yICs9IFNtb290aF9TMV9jMChfaW5wdXQsIGNvb3JkLCB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFtpXSk7Cgl9CglyZXR1cm4gY29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBHYXVzc2lhbkNvbnZvbHV0aW9uX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQMAAAAAAAAAEAAAABJYQAAAAAAAAIAAAAAWCBAAAABAAAAANAECAZAAAAAAAAAAAFAAMAAAABAAAAAAABBAM":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAPAEAAB1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CnVuaWZvcm0gaGFsZjIgdUluY3JlbWVudF9TMV9jMDsKdW5pZm9ybSBoYWxmMiB1T2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxM107CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgX2Nvb3Jkcyk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IFNtb290aF9TMV9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBjb29yZCwgaGFsZjIgb2Zmc2V0QW5kS2VybmVsKSAKewoJcmV0dXJuIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfaW5wdXQsIChjb29yZCArIG9mZnNldEFuZEtlcm5lbC54ICogdUluY3JlbWVudF9TMV9jMCkpICogb2Zmc2V0QW5kS2VybmVsLnk7Cn0KaGFsZjQgR2F1c3NpYW5Db252b2x1dGlvbl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBjb2xvciA9IGhhbGY0KDApOwoJZmxvYXQyIGNvb3JkID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7Cglmb3IgKGludCBpPTA7IGk8MTM7ICsraSkgCgl7CgkJY29sb3IgKz0gU21vb3RoX1MxX2MwKF9pbnB1dCwgY29vcmQsIHVPZmZzZXRzQW5kS2VybmVsX1MxX2MwW2ldKTsKCX0KCXJldHVybiBjb2xvcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIEdhdXNzaWFuQ29udm9sdXRpb25fUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAEQQGABZAA6IAAAAACAAAAAADUAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAABEAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gc2FtcGxlckV4dGVybmFsT0VTIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","BYIBQAAABQAAIAABBYAAAEIXBAAP777777777777AAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1M+AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwpvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gaW5Db2xvcjsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzNfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAABgEAAGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIERlZmF1bHRHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAAAAAA=","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEAB5AAAAACQNNQSEIYAQAADQAAAABAAAAAAABAEMVC2TBEKRAAAAAHAAAAAAAAAAACQAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAABRBQAAY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MxX2MwOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZhcmNjb29yZF9TMDsKaGFsZjQgQ2lyY2xlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBkOwoJaWYgKGludCgzKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWQgPSBoYWxmKChsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSAtIDEuMCkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJZWxzZSAKCXsKCQlkID0gaGFsZigoMS4wIC0gbGVuZ3RoKCh1Y2lyY2xlX1MxX2MwLnh5IC0gc2tfRnJhZ0Nvb3JkLnh5KSAqIHVjaXJjbGVfUzFfYzAudykpICogdWNpcmNsZV9TMV9jMC56KTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChpbnQoMykgPT0ga0ZpbGxBQV9TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzAgPyBzYXR1cmF0ZShkKSA6IGhhbGYoZCA+IDAuNSA/IDEgOiAwKSkpOwp9CmhhbGY0IEJsZW5kX1MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoX3NyYywgQ2lyY2xlX1MxX2MwKF9zcmMpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IHhfcGx1c18xPXZhcmNjb29yZF9TMC54LCB5PXZhcmNjb29yZF9TMC55OwoJaGFsZiBjb3ZlcmFnZTsKCWlmICgwID09IHhfcGx1c18xKSAKCXsKCQljb3ZlcmFnZSA9IGhhbGYoeSk7Cgl9CgllbHNlIAoJewoJCWZsb2F0IGZuID0geF9wbHVzXzEgKiAoeF9wbHVzXzEgLSAyKTsKCQlmbiA9IGZtYSh5LHksIGZuKTsKCQlmbG9hdCBmbndpZHRoID0gZndpZHRoKGZuKTsKCQljb3ZlcmFnZSA9IC41IC0gaGFsZihmbi9mbndpZHRoKTsKCQljb3ZlcmFnZSA9IGNsYW1wKGNvdmVyYWdlLCAwLCAxKTsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoY292ZXJhZ2UpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAB3QA6AAAEAAAAAAAMAAPEAEAAABAAAAAAB2AAAAAAACAAAAAEBSAAAA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAAeBQAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzI7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MyOwppbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglhbHBoYSA9IDEuMCAtIGFscGhhOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzIoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MyLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzIueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCWhhbGY0IG91dHB1dF9TMjsKCW91dHB1dF9TMiA9IENpcmN1bGFyUlJlY3RfUzIob3V0cHV0X1MxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMjsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADEAANAAAAAZ3N3DJRAAAAAAAAABAAAAAGJZFMBV5RUAQAAAAAAQAAAAAMACQCAACAAAAA2AIBAEIAAAAAAAAAAAAIADQAAAAIAAAAAAAIIDAAAAAA":"DAAAAExTS1PUAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoBAAAAeAQAAHVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1Y2lyY2xlRGF0YV9TMV9jMDsKdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCBfY29vcmRzKS4wMDByOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBDaXJjbGVCbHVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjIgdmVjID0gaGFsZjIoKHNrX0ZyYWdDb29yZC54eSAtIGZsb2F0Mih1Y2lyY2xlRGF0YV9TMV9jMC54eSkpICogZmxvYXQodWNpcmNsZURhdGFfUzFfYzAudykpOwoJaGFsZiBkaXN0ID0gbGVuZ3RoKHZlYykgKyAoMC41IC0gdWNpcmNsZURhdGFfUzFfYzAueikgKiB1Y2lyY2xlRGF0YV9TMV9jMC53OwoJcmV0dXJuIGhhbGY0KE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgZmxvYXQyKGhhbGYyKGRpc3QsIDAuNSkpKS53d3d3KTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKENpcmNsZUJsdXJfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","B2ABSAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1N4AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZiBpbkNvdmVyYWdlOwpvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gdUNvbG9yX1MwOwoJY29sb3IgPSBjb2xvciAqIGluQ292ZXJhZ2U7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8zX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzFfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAGAQAAaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGVmYXVsdEdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAKAAAAaW5Qb3NpdGlvbgAACgAAAGluQ292ZXJhZ2UAAAAAAAA=","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEAKPABAAAAAAB2AAAAAAACAAAAAEBSAAAAAAA":"DAAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAACbBAAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBmbG9hdDIgdWludlJhZGlpWFlfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdmFyY2Nvb3JkX1MwOwpoYWxmNCBFbGxpcHRpY2FsUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CglmbG9hdDIgWiA9IGR4eSAqIHVpbnZSYWRpaVhZX1MxLnh5OwoJaGFsZiBpbXBsaWNpdCA9IGhhbGYoZG90KFosIGR4eSkgLSAxLjApOwoJaGFsZiBncmFkX2RvdCA9IGhhbGYoNC4wICogZG90KFosIFopKTsKCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjBlLTQpOwoJaGFsZiBhcHByb3hfZGlzdCA9IGltcGxpY2l0ICogaGFsZihpbnZlcnNlc3FydChncmFkX2RvdCkpOwoJaGFsZiBhbHBoYSA9IGNsYW1wKDAuNSArIGFwcHJveF9kaXN0LCAwLjAsIDEuMCk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEZpbGxSUmVjdE9wOjpQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCB4X3BsdXNfMT12YXJjY29vcmRfUzAueCwgeT12YXJjY29vcmRfUzAueTsKCWhhbGYgY292ZXJhZ2U7CglpZiAoMCA9PSB4X3BsdXNfMSkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKHkpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdCBmbiA9IHhfcGx1c18xICogKHhfcGx1c18xIC0gMik7CgkJZm4gPSBmbWEoeSx5LCBmbik7CgkJZmxvYXQgZm53aWR0aCA9IGZ3aWR0aChmbik7CgkJY292ZXJhZ2UgPSAuNSAtIGhhbGYoZm4vZm53aWR0aCk7CgkJY292ZXJhZ2UgPSBjbGFtcChjb3ZlcmFnZSwgMCwgMSk7Cgl9CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEVsbGlwdGljYWxSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAIAAAADgAAAHJhZGlpX3NlbGVjdG9yAAAZAAAAY29ybmVyX2FuZF9yYWRpdXNfb3V0c2V0cwAAABUAAABhYV9ibG9hdF9hbmRfY292ZXJhZ2UAAAAHAAAAcmFkaWlfeAAHAAAAcmFkaWlfeQAEAAAAc2tldxkAAAB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlAAAABQAAAGNvbG9yAAAAAAAAAA==","HUQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1PPAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAMAQAAaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAADIAAAAGULKMMQKAAAAAAMAAAAAIAAAAAAGIRBNAWEYZAUAABQAAAAAAAAAAAAADUAAAAAAAEAAAAAIDEAAA":"DAAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAC4BAAAY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MxX2MwOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBDaXJjbGVfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGQ7CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMV9jMCkgCgl7CgkJZCA9IGhhbGYoKGxlbmd0aCgodWNpcmNsZV9TMV9jMC54eSAtIHNrX0ZyYWdDb29yZC54eSkgKiB1Y2lyY2xlX1MxX2MwLncpIC0gMS4wKSAqIHVjaXJjbGVfUzFfYzAueik7Cgl9CgllbHNlIAoJewoJCWQgPSBoYWxmKCgxLjAgLSBsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGludCgxKSA9PSBrRmlsbEFBX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMV9jMCA/IHNhdHVyYXRlKGQpIDogaGFsZihkID4gMC41ID8gMSA6IDApKSk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9tb2R1bGF0ZShfc3JjLCBDaXJjbGVfUzFfYzAoX3NyYykpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFIBVWKMG7QAAAAAAGVXOVKVEAAAAADAAAAAAAAEAABMHHPDNAYAQAACAEGNDQDEAAAACAAAAABAEM2S5KXK4QAAAAAMAAAAAAAAQAABQM74NUDECAAAAAQ5UOAMQAAAAAAAIAACAAZBAAAAANQ4Z4NUDACAAAEAAAAACAI33BYT43IEAAAAAAAAAAAWARQ5UOAMQAAAAIAAAAAAABS4UIAYXAGAAAAAAAAAAA2AAQAAAACAAAAAEASAAQAAAA":"DAAAAExTS1OAAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAHoHAAB1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMDsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzBfYzAsIHVlbmRfUzFfYzBfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IGNvbG9yX3hmb3JtX1MxX2MwX2MwKGZsb2F0NCBjb2xvcikgCnsKCWNvbG9yLnJnYiAqPSBjb2xvci5hOwoJcmV0dXJuIGhhbGY0KGNvbG9yKTsKfQpoYWxmNCBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBjb2xvcl94Zm9ybV9TMV9jMF9jMChTaW5nbGVJbnRlcnZhbENvbG9yaXplcl9TMV9jMF9jMF9jMChfaW5wdXQsIF9jb29yZHMpKTsKfQpoYWxmNCBMaW5lYXJMYXlvdXRfUzFfYzBfYzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8yX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8zX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc182X1MwOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGhhbGYoX3RtcF8zX2Nvb3Jkcy54KSArIDFlLTA1LCAxLjAsIDAuMCwgMC4wKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBMaW5lYXJMYXlvdXRfUzFfYzBfYzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDbGFtcGVkR3JhZGllbnRfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF80X2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmNCB0ID0gTWF0cml4RWZmZWN0X1MxX2MwX2MxKF90bXBfNF9pbkNvbG9yKTsKCWhhbGY0IG91dENvbG9yOwoJaWYgKCFib29sKGludCgxKSkgJiYgdC55IDwgMC4wKSAKCXsKCQlvdXRDb2xvciA9IGhhbGY0KDAuMCk7Cgl9CgllbHNlIGlmICh0LnggPCAwLjApIAoJewoJCW91dENvbG9yID0gdWxlZnRCb3JkZXJDb2xvcl9TMV9jMDsKCX0KCWVsc2UgaWYgKHQueCA+IDEuMCkgCgl7CgkJb3V0Q29sb3IgPSB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMDsKCX0KCWVsc2UgCgl7CgkJb3V0Q29sb3IgPSBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzAoX3RtcF80X2luQ29sb3IsIGZsb2F0MihoYWxmMih0LngsIDAuMCkpKTsKCX0KCXJldHVybiBoYWxmNChvdXRDb2xvcik7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBDbGFtcGVkR3JhZGllbnRfUzFfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfNV9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACAAYQADAAAEBB7BH46AKAAAYAAAAAAAAIAAAABS2JQ7QD2PAEAAAMAAAAAAAAAAAAAIADQAAAAIAAAAAAAIIDAAAA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAB+BQAAY29uc3QgaW50IGtGaWxsQldfUzFfYzAgPSAwOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1cmVjdFVuaWZvcm1fUzFfYzA7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgUmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGYgY292ZXJhZ2U7CglpZiAoaW50KDEpID09IGtGaWxsQldfUzFfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MxX2MwKSAKCXsKCQljb3ZlcmFnZSA9IGhhbGYoYWxsKGdyZWF0ZXJUaGFuKGZsb2F0NChza19GcmFnQ29vcmQueHksIHVyZWN0VW5pZm9ybV9TMV9jMC56dyksIGZsb2F0NCh1cmVjdFVuaWZvcm1fUzFfYzAueHksIHNrX0ZyYWdDb29yZC54eSkpKSk7Cgl9CgllbHNlIAoJewoJCWhhbGY0IGRpc3RzNCA9IHNhdHVyYXRlKGhhbGY0KDEuMCwgMS4wLCAtMS4wLCAtMS4wKSAqIGhhbGY0KHNrX0ZyYWdDb29yZC54eXh5IC0gdXJlY3RVbmlmb3JtX1MxX2MwKSk7CgkJaGFsZjIgZGlzdHMyID0gKGRpc3RzNC54eSArIGRpc3RzNC56dykgLSAxLjA7CgkJY292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJfQoJaWYgKGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWNvdmVyYWdlID0gMS4wIC0gY292ZXJhZ2U7Cgl9CglyZXR1cm4gaGFsZjQoaGFsZjQoY292ZXJhZ2UpKTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKFJlY3RfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAADUAANAAAAAAAAAIAAAABLAIABAAAAABAEGABBAMAAAAAAAAAAAAB2AAAAAAACAAAAAEBSAAAAA":"DAAAAExTS1M8AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxX2MwKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAC2AgAAdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwOwp1bmlmb3JtIHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc18zX1MwKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKE1hdHJpeEVmZmVjdF9TMV9jMChfc3JjKSwgX3NyYyk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb2xvcl9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAADUAANAAAAAAIBAIAAAABLCIIBAAAAABAEGABBAMAACAIAAAAAAAB2AAAAAAACAAAAAEBSAAAAA":"DAAAAExTS1M8AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxX2MwKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAADJAwAAdW5pZm9ybSBmbG9hdDQgdWNsYW1wX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzA7CnVuaWZvcm0gc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZCA9IGNsYW1wKHN1YnNldENvb3JkLCB1Y2xhbXBfUzFfYzBfYzAueHksIHVjbGFtcF9TMV9jMF9jMC56dyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IEJsZW5kX1MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoTWF0cml4RWZmZWN0X1MxX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMCA9IGhhbGY0KDEpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvbG9yX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","GEMAAAYAAEHAAAARC4EAAAQWBQAAAAAAAAAQAAAAIBCAAAGQAEAAAAAQAAAABAEQAEAAAAA":"DAAAAExTS1NUAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBoYWxmMyBpblNoYWRvd1BhcmFtczsKb3V0IGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBSUmVjdFNoYWRvdwoJdmluU2hhZG93UGFyYW1zX1MwID0gaW5TaGFkb3dQYXJhbXM7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAALAgAAdW5pZm9ybSBzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBSUmVjdFNoYWRvdwoJaGFsZjMgc2hhZG93UGFyYW1zOwoJc2hhZG93UGFyYW1zID0gdmluU2hhZG93UGFyYW1zX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJaGFsZiBkID0gbGVuZ3RoKHNoYWRvd1BhcmFtcy54eSk7CglmbG9hdDIgdXYgPSBmbG9hdDIoc2hhZG93UGFyYW1zLnogKiAoMS4wIC0gZCksIDAuNSk7CgloYWxmIGZhY3RvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdXYpLjAwMHIuYTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZmFjdG9yKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAOAAAAaW5TaGFkb3dQYXJhbXMAAAAAAAA="}} \ No newline at end of file +{"platform":"android","name":"SM G970N","engineRevision":"cf7a9d0800f2a5da166dbe0eb9fb2476018269b1","data":{"HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGABZAA6IAAAAACAAAAAADUAAAAAAAEAAAAAIDEAAAAAAA":"DAAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAAzAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","HUQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1PPAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAMAQAAaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQBAAAQAAAAGQCBAMQACAAAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAGgDAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZC54ID0gY2xhbXAoc3Vic2V0Q29vcmQueCwgdWNsYW1wX1MxX2MwLngsIHVjbGFtcF9TMV9jMC56KTsKCWNsYW1wZWRDb29yZC55ID0gc3Vic2V0Q29vcmQueTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFIBVWKMG7QAAAAAAAEAQCAAAAAVREEAQAAAAAQCDAAQQGAABAEAAAAAAHIAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1N6AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAHwQAAHVuaWZvcm0gZmxvYXQ0IHVjbGFtcF9TMV9jMF9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZCA9IGNsYW1wKHN1YnNldENvb3JkLCB1Y2xhbXBfUzFfYzBfYzAueHksIHVjbGFtcF9TMV9jMF9jMC56dyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gTWF0cml4RWZmZWN0X1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","CMRQCIAABBYAAAEIXBAAACDQMAABRAFAAAAAAAAAAAAAAAEABYAAAAEAAAAAAAEEBQAAAAA":"DAAAAExTS1MyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0MiBpbkVsbGlwc2VPZmZzZXQ7CmluIGZsb2F0NCBpbkVsbGlwc2VSYWRpaTsKb3V0IGZsb2F0MiB2RWxsaXBzZU9mZnNldHNfUzA7Cm91dCBmbG9hdDQgdkVsbGlwc2VSYWRpaV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCXZFbGxpcHNlT2Zmc2V0c19TMCA9IGluRWxsaXBzZU9mZnNldDsKCXZFbGxpcHNlUmFkaWlfUzAgPSBpbkVsbGlwc2VSYWRpaTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAHIDAABpbiBmbG9hdDIgdkVsbGlwc2VPZmZzZXRzX1MwOwppbiBmbG9hdDQgdkVsbGlwc2VSYWRpaV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBFbGxpcHNlR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0MiBvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHk7CglvZmZzZXQgKj0gdkVsbGlwc2VSYWRpaV9TMC54eTsKCWZsb2F0IHRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZmxvYXQyIGdyYWQgPSAyLjAqb2Zmc2V0KnZFbGxpcHNlUmFkaWlfUzAueHk7CglmbG9hdCBncmFkX2RvdCA9IGRvdChncmFkLCBncmFkKTsKCWdyYWRfZG90ID0gbWF4KGdyYWRfZG90LCAxLjE3NTVlLTM4KTsKCWZsb2F0IGludmxlbiA9IGludmVyc2VzcXJ0KGdyYWRfZG90KTsKCWZsb2F0IGVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNS10ZXN0Kmludmxlbik7CglvZmZzZXQgPSB2RWxsaXBzZU9mZnNldHNfUzAueHkqdkVsbGlwc2VSYWRpaV9TMC56dzsKCXRlc3QgPSBkb3Qob2Zmc2V0LCBvZmZzZXQpIC0gMS4wOwoJZ3JhZCA9IDIuMCpvZmZzZXQqdkVsbGlwc2VSYWRpaV9TMC56dzsKCWdyYWRfZG90ID0gZG90KGdyYWQsIGdyYWQpOwoJaW52bGVuID0gaW52ZXJzZXNxcnQoZ3JhZF9kb3QpOwoJZWRnZUFscGhhICo9IHNhdHVyYXRlKDAuNSt0ZXN0Kmludmxlbik7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGhhbGYoZWRnZUFscGhhKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpbkVsbGlwc2VPZmZzZXQADgAAAGluRWxsaXBzZVJhZGlpAAAAAAAA","HVJAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAABAAAAAABBAMABAAOAAAABAAAAAAABBAMAAA":"DAAAAExTS1MjAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cgl2bG9jYWxDb29yZF9TMCA9IGxvY2FsQ29vcmQ7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAAAAADVAQAAc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZsb2NhbENvb3JkX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQyIHRleENvb3JkOwoJdGV4Q29vcmQgPSB2bG9jYWxDb29yZF9TMDsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpLCBvdXRwdXRDb2xvcl9TMCkpOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","AYQQ5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAB7AgAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGYgZGlzdGFuY2VUb0lubmVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKGQgLSBjaXJjbGVFZGdlLncpKTsKCWhhbGYgaW5uZXJBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9Jbm5lckVkZ2UpOwoJZWRnZUFscGhhICo9IGlubmVyQWxwaGE7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HVIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAHQBNGZODK5YIAAAAAAQAAAAAIADCNS4GV3QYBAAAAAAAAAAAIAALIAAAAB2BQ7SD2OAAAAAMAAAAAEAHQAACAAAAAAQCGHIGP7YJJAAAAABQAAAAAAAAAAA4JAPAAACAAAAAAAAAB2AAAAAAACAAAAAEBSAAA":"DAAAAExTS1PZAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KfQoAAAABAAAA2CAAAGZsb2F0NCBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMDsKZmxvYXQ0IGZyYWdDb2xvcl9TMV9jMDsKZmxvYXQgdV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9zcGFya2xlX2FscGhhX1MxX2MwOwpmbG9hdCB1X2JsdXJfUzFfYzA7CmZsb2F0IHVfcmFkaXVzX3NjYWxlX1MxX2MwOwpjb25zdCBpbnQga0ZpbGxCV19TMl9jMCA9IDA7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxCV19TMl9jMCA9IDI7CmNvbnN0IGludCBrSW52ZXJzZUZpbGxBQV9TMl9jMCA9IDM7CnVuaWZvcm0gZmxvYXQ0IHVfY29sb3JfUzFfYzA7CnVuaWZvcm0gZmxvYXQ0IHVfY29tcG9zaXRlXzFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2VudGVyX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbWF4X3JhZGl1c19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X25vaXNlX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbm9pc2VfcGhhc2VfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUyX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjNfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBmbG9hdDQgdXJlY3RVbmlmb3JtX1MyX2MwOwp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MyX2MxOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMl9jMTsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpOwp2b2lkIEZMVF9tYWluX1MxX2MwKCk7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCkgCnsKCXJldHVybiBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMC54eTsKfQpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKSAKewoJcmV0dXJuIGZsb2F0MngyKHJhZC54LCAtcmFkLnksIHJhZC55LCByYWQueCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cikgCnsKCWZsb2F0IGJsdXJfaGFsZiA9IGJsdXIgKiAwLjU7CglmbG9hdCBkID0gZGlzdGFuY2UodXYsIHh5KTsKCXJldHVybiAxLjAgLSBzbW9vdGhzdGVwKDEuMCAtIGJsdXJfaGFsZiwgMS4wICsgYmx1cl9oYWxmLCBkIC8gcmFkaXVzKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKSAKewoJZmxvYXQyIHBhcmFtID0gcm90YXRpb247CglwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAocGFyYW0pICogKHh5IC0gcCkgKyB4eTsKCXAgPSBtb2QocCwgZmxvYXQyKGNlbGxfZGlhbWV0ZXIpKSAvIHJlc29sdXRpb247CglmbG9hdCBjZWxsX3V2ID0gKGNlbGxfZGlhbWV0ZXIgLyByZXNvbHV0aW9uLnkpICogMC41OwoJZmxvYXQgciA9IDAuNjUgKiBjZWxsX3V2OwoJZmxvYXQyIHBhcmFtXzEgPSBwOwoJZmxvYXQyIHBhcmFtXzIgPSBmbG9hdDIoY2VsbF91dik7CglmbG9hdCBwYXJhbV8zID0gcjsKCWZsb2F0IHBhcmFtXzQgPSByICogNTAuMDsKCXJldHVybiBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dikgCnsKCWZsb2F0MiB1dl9zY2FsZSA9IHV2ICogZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW0gPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMiA9IHVfY2lyY2xlMV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8zID0gdV9yb3RhdGlvbjFfUzFfYzA7CglmbG9hdCBwYXJhbV80ID0gMC4xNzsKCWZsb2F0IF8zMTkgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7CglmbG9hdCBnMSA9IF8zMTk7CglmbG9hdDIgcGFyYW1fNSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzYgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV83ID0gdV9jaXJjbGUyX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzggPSB1X3JvdGF0aW9uMl9TMV9jMDsKCWZsb2F0IHBhcmFtXzkgPSAwLjI7CglmbG9hdCBfMzMxID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBnMiA9IF8zMzE7CglmbG9hdDIgcGFyYW1fMTAgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzEyID0gdV9jaXJjbGUzX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdV9yb3RhdGlvbjNfUzFfYzA7CglmbG9hdCBwYXJhbV8xNCA9IDAuMjc1OwoJZmxvYXQgXzM0NCA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIsIHBhcmFtXzEzLCBwYXJhbV8xNCk7CglmbG9hdCBnMyA9IF8zNDQ7CglmbG9hdCB2ID0gKChnMSAqIGcxICsgZzIpIC0gZzMpICogMC41OwoJcmV0dXJuIGNsYW1wKDAuNDUgKyAwLjggKiB2LCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cikgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQyIHBhcmFtXzEgPSB4eTsKCWZsb2F0IHBhcmFtXzIgPSByYWRpdXMgKyB0aGlja25lc3M7CglmbG9hdCBwYXJhbV8zID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9vdXRlciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0MiBwYXJhbV80ID0gdXY7CglmbG9hdDIgcGFyYW1fNSA9IHh5OwoJZmxvYXQgcGFyYW1fNiA9IG1heChyYWRpdXMgLSB0aGlja25lc3MsIDAuMCk7CglmbG9hdCBwYXJhbV83ID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9pbm5lciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcpOwoJcmV0dXJuIGNsYW1wKGNpcmNsZV9vdXRlciAtIGNpcmNsZV9pbm5lciwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKSAKewoJbiA9IGZyYWN0KG4gKiBmbG9hdDIoNS4zOTg3LCA1LjQ0MjEpKTsKCW4gKz0gZmxvYXQyKGRvdChuLnl4LCBuICsgZmxvYXQyKDIxLjUzNTEsIDE0LjMxMzcpKSk7CglmbG9hdCB4eSA9IG4ueCAqIG4ueTsKCXJldHVybiAoZnJhY3QoeHkgKiA5NS40MzA3KSArIGZyYWN0KHh5ICogNzUuMDQ5NjEpKSAtIDEuMDsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCkgCnsKCXJldHVybiBzdGVwKGwsIHYpICogKDEuMCAtIHN0ZXAoaCwgdikpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdCBfMjQyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAocGFyYW0pOwoJZmxvYXQgbiA9IF8yNDI7CglmbG9hdCBwYXJhbV8xID0gbjsKCWZsb2F0IHBhcmFtXzIgPSAwLjA7CglmbG9hdCBwYXJhbV8zID0gMC4wNTsKCWZsb2F0IHMgPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdCBwYXJhbV80ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjM1KSk7CglmbG9hdCBwYXJhbV81ID0gMC4xOwoJZmxvYXQgcGFyYW1fNiA9IDAuMTU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82KTsKCWZsb2F0IHBhcmFtXzcgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuNykpOwoJZmxvYXQgcGFyYW1fOCA9IDAuMjsKCWZsb2F0IHBhcmFtXzkgPSAwLjI1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBwYXJhbV8xMCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMS4wNSkpOwoJZmxvYXQgcGFyYW1fMTEgPSAwLjM7CglmbG9hdCBwYXJhbV8xMiA9IDAuMzU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyKTsKCXJldHVybiBjbGFtcChzLCAwLjAsIDEuMCkgKiAwLjU1Owp9CnZvaWQgRkxUX21haW5fUzFfYzAoKSAKewoJdV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueDsKCXVfc3BhcmtsZV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueTsKCXVfYmx1cl9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAuejsKCXVfcmFkaXVzX3NjYWxlX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC53OwoJZmxvYXQyIHAgPSBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CglmbG9hdDIgdXZfMSA9IHAgKiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CglmbG9hdDIgZGVuc2l0eV91diA9IHV2XzEgLSBtb2QocCwgdV9ub2lzZV9zY2FsZV9TMV9jMCk7CglmbG9hdCByYWRpdXMgPSB1X21heF9yYWRpdXNfUzFfYzAgKiB1X3JhZGl1c19zY2FsZV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHV2XzE7CglmbG9hdCB0dXJidWxlbmNlID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChwYXJhbV8xMyk7CglmbG9hdDIgcGFyYW1fMTQgPSBwOwoJZmxvYXQyIHBhcmFtXzE1ID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8xNiA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzE3ID0gMC4wNSAqIHVfbWF4X3JhZGl1c19TMV9jMDsKCWZsb2F0IHBhcmFtXzE4ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgcmluZyA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChwYXJhbV8xNCwgcGFyYW1fMTUsIHBhcmFtXzE2LCBwYXJhbV8xNywgcGFyYW1fMTgpOwoJZmxvYXQyIHBhcmFtXzE5ID0gZGVuc2l0eV91djsKCWZsb2F0IHBhcmFtXzIwID0gdV9ub2lzZV9waGFzZV9TMV9jMDsKCWZsb2F0IHNwYXJrbGUgPSAoKEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAocGFyYW1fMTksIHBhcmFtXzIwKSAqIHJpbmcpICogdHVyYnVsZW5jZSkgKiB1X3NwYXJrbGVfYWxwaGFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMjEgPSBwOwoJZmxvYXQyIHBhcmFtXzIyID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8yMyA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzI0ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgd2F2ZV9hbHBoYSA9IChGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8yMSwgcGFyYW1fMjIsIHBhcmFtXzIzLCBwYXJhbV8yNCkgKiB1X2FscGhhX1MxX2MwKSAqIHVfY29sb3JfUzFfYzAudzsKCWZsb2F0NCB3YXZlX2NvbG9yID0gZmxvYXQ0KHVfY29sb3JfUzFfYzAueHl6ICogd2F2ZV9hbHBoYSwgd2F2ZV9hbHBoYSk7CglmcmFnQ29sb3JfUzFfYzAgPSBtaXgod2F2ZV9jb2xvciwgZmxvYXQ0KDEuMCksIGZsb2F0NChzcGFya2xlKSk7Cn0KaGFsZjQgcnVudGltZV9zaGFkZXJfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAgPSBmbG9hdDQoX3RtcF8xX2Nvb3JkcywgMC4wLCAwLjApOwoJRkxUX21haW5fUzFfYzAoKTsKCXJldHVybiBoYWxmNChoYWxmNChmcmFnQ29sb3JfUzFfYzApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIHJ1bnRpbWVfc2hhZGVyX1MxX2MwKF9pbnB1dCk7Cn0KaGFsZjQgUmVjdF9TMl9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzJfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGYgY292ZXJhZ2U7CglpZiAoaW50KDEpID09IGtGaWxsQldfUzJfYzAgfHwgaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MwKSAKCXsKCQljb3ZlcmFnZSA9IGhhbGYoYWxsKGdyZWF0ZXJUaGFuKGZsb2F0NChza19GcmFnQ29vcmQueHksIHVyZWN0VW5pZm9ybV9TMl9jMC56dyksIGZsb2F0NCh1cmVjdFVuaWZvcm1fUzJfYzAueHksIHNrX0ZyYWdDb29yZC54eSkpKSk7Cgl9CgllbHNlIAoJewoJCWhhbGY0IGRpc3RzNCA9IHNhdHVyYXRlKGhhbGY0KDEuMCwgMS4wLCAtMS4wLCAtMS4wKSAqIGhhbGY0KHNrX0ZyYWdDb29yZC54eXh5IC0gdXJlY3RVbmlmb3JtX1MyX2MwKSk7CgkJaGFsZjIgZGlzdHMyID0gKGRpc3RzNC54eSArIGRpc3RzNC56dykgLSAxLjA7CgkJY292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJfQoJaWYgKGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMl9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzJfYzApIAoJewoJCWNvdmVyYWdlID0gMS4wIC0gY292ZXJhZ2U7Cgl9CglyZXR1cm4gaGFsZjQoaGFsZjQoY292ZXJhZ2UpKTsKfQpoYWxmNCBDaXJjdWxhclJSZWN0X1MyX2MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMl9jMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMl9jMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MyX2MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CmhhbGY0IEJsZW5kX1MyKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoUmVjdF9TMl9jMChfc3JjKSwgQ2lyY3VsYXJSUmVjdF9TMl9jMShfc3JjKSk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWZsb2F0IGNvdmVyYWdlID0gdmNvdmVyYWdlX1MwOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChoYWxmKGNvdmVyYWdlKSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJaGFsZjQgb3V0cHV0X1MyOwoJb3V0cHV0X1MyID0gQmxlbmRfUzIob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRfUzI7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAbG9jYWxDb29yZAAAAAAAAA==","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAEAB5AAAAACQNNQSEIYAQAADQAAAABAAAAAAABAEMVC2TBEKRAAAAAHAAAAAAAAAAACQAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgEAAABJBQAAY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MxX2MwOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZhcmNjb29yZF9TMDsKaGFsZjQgQ2lyY2xlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBkOwoJaWYgKGludCgzKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWQgPSBoYWxmKChsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSAtIDEuMCkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJZWxzZSAKCXsKCQlkID0gaGFsZigoMS4wIC0gbGVuZ3RoKCh1Y2lyY2xlX1MxX2MwLnh5IC0gc2tfRnJhZ0Nvb3JkLnh5KSAqIHVjaXJjbGVfUzFfYzAudykpICogdWNpcmNsZV9TMV9jMC56KTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChpbnQoMykgPT0ga0ZpbGxBQV9TMV9jMCB8fCBpbnQoMykgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzAgPyBzYXR1cmF0ZShkKSA6IGhhbGYoZCA+IDAuNSkpKTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKF9zcmMsIENpcmNsZV9TMV9jMChfc3JjKSk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEZpbGxSUmVjdE9wOjpQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCB4X3BsdXNfMT12YXJjY29vcmRfUzAueCwgeT12YXJjY29vcmRfUzAueTsKCWhhbGYgY292ZXJhZ2U7CglpZiAoMCA9PSB4X3BsdXNfMSkgCgl7CgkJY292ZXJhZ2UgPSBoYWxmKHkpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdCBmbiA9IHhfcGx1c18xICogKHhfcGx1c18xIC0gMik7CgkJZm4gPSBmbWEoeSx5LCBmbik7CgkJZmxvYXQgZm53aWR0aCA9IGZ3aWR0aChmbik7CgkJY292ZXJhZ2UgPSAuNSAtIGhhbGYoZm4vZm53aWR0aCk7CgkJY292ZXJhZ2UgPSBjbGFtcChjb3ZlcmFnZSwgMCwgMSk7Cgl9CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGNvdmVyYWdlKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IEJsZW5kX1MxKG91dHB1dENvdmVyYWdlX1MwLCBoYWxmNCgxKSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAOAAAAcmFkaWlfc2VsZWN0b3IAABkAAABjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzAAAAFQAAAGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZQAAAAcAAAByYWRpaV94AAcAAAByYWRpaV95AAQAAABza2V3GQAAAHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUAAAAFAAAAY29sb3IAAAAAAAAA","HVIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAHQBNGZODK5YIAAAAAAQAAAAAIADCNS4GV3QYBAAAAAAAAAAAIAA4IAEAAACAAAAAABUABAAAAAEAAAAAIBEABAA":"DAAAAExTS1PZAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KfQoAAAABAAAAth0AAGZsb2F0NCBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMDsKZmxvYXQ0IGZyYWdDb2xvcl9TMV9jMDsKZmxvYXQgdV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9zcGFya2xlX2FscGhhX1MxX2MwOwpmbG9hdCB1X2JsdXJfUzFfYzA7CmZsb2F0IHVfcmFkaXVzX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbG9yX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbXBvc2l0ZV8xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NlbnRlcl9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X21heF9yYWRpdXNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9ub2lzZV9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X25vaXNlX3BoYXNlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24zX1MxX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzI7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MyOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCk7CnZvaWQgRkxUX21haW5fUzFfYzAoKTsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKSAKewoJcmV0dXJuIGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwLnh5Owp9CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpIAp7CglyZXR1cm4gZmxvYXQyeDIocmFkLngsIC1yYWQueSwgcmFkLnksIHJhZC54KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQgYmx1cl9oYWxmID0gYmx1ciAqIDAuNTsKCWZsb2F0IGQgPSBkaXN0YW5jZSh1diwgeHkpOwoJcmV0dXJuIDEuMCAtIHNtb290aHN0ZXAoMS4wIC0gYmx1cl9oYWxmLCAxLjAgKyBibHVyX2hhbGYsIGQgLyByYWRpdXMpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpIAp7CglmbG9hdDIgcGFyYW0gPSByb3RhdGlvbjsKCXAgPSBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChwYXJhbSkgKiAoeHkgLSBwKSArIHh5OwoJcCA9IG1vZChwLCBmbG9hdDIoY2VsbF9kaWFtZXRlcikpIC8gcmVzb2x1dGlvbjsKCWZsb2F0IGNlbGxfdXYgPSAoY2VsbF9kaWFtZXRlciAvIHJlc29sdXRpb24ueSkgKiAwLjU7CglmbG9hdCByID0gMC42NSAqIGNlbGxfdXY7CglmbG9hdDIgcGFyYW1fMSA9IHA7CglmbG9hdDIgcGFyYW1fMiA9IGZsb2F0MihjZWxsX3V2KTsKCWZsb2F0IHBhcmFtXzMgPSByOwoJZmxvYXQgcGFyYW1fNCA9IHIgKiA1MC4wOwoJcmV0dXJuIEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KSAKewoJZmxvYXQyIHV2X3NjYWxlID0gdXYgKiBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8yID0gdV9jaXJjbGUxX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzMgPSB1X3JvdGF0aW9uMV9TMV9jMDsKCWZsb2F0IHBhcmFtXzQgPSAwLjE3OwoJZmxvYXQgXzMxOSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKCWZsb2F0IGcxID0gXzMxOTsKCWZsb2F0MiBwYXJhbV81ID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fNiA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzcgPSB1X2NpcmNsZTJfUzFfYzA7CglmbG9hdDIgcGFyYW1fOCA9IHVfcm90YXRpb24yX1MxX2MwOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjsKCWZsb2F0IF8zMzEgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IGcyID0gXzMzMTsKCWZsb2F0MiBwYXJhbV8xMCA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzExID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMTIgPSB1X2NpcmNsZTNfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1X3JvdGF0aW9uM19TMV9jMDsKCWZsb2F0IHBhcmFtXzE0ID0gMC4yNzU7CglmbG9hdCBfMzQ0ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMiwgcGFyYW1fMTMsIHBhcmFtXzE0KTsKCWZsb2F0IGczID0gXzM0NDsKCWZsb2F0IHYgPSAoKGcxICogZzEgKyBnMikgLSBnMykgKiAwLjU7CglyZXR1cm4gY2xhbXAoMC40NSArIDAuOCAqIHYsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdDIgcGFyYW1fMSA9IHh5OwoJZmxvYXQgcGFyYW1fMiA9IHJhZGl1cyArIHRoaWNrbmVzczsKCWZsb2F0IHBhcmFtXzMgPSBibHVyOwoJZmxvYXQgY2lyY2xlX291dGVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQyIHBhcmFtXzQgPSB1djsKCWZsb2F0MiBwYXJhbV81ID0geHk7CglmbG9hdCBwYXJhbV82ID0gbWF4KHJhZGl1cyAtIHRoaWNrbmVzcywgMC4wKTsKCWZsb2F0IHBhcmFtXzcgPSBibHVyOwoJZmxvYXQgY2lyY2xlX2lubmVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNyk7CglyZXR1cm4gY2xhbXAoY2lyY2xlX291dGVyIC0gY2lyY2xlX2lubmVyLCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pIAp7CgluID0gZnJhY3QobiAqIGZsb2F0Mig1LjM5ODcsIDUuNDQyMSkpOwoJbiArPSBmbG9hdDIoZG90KG4ueXgsIG4gKyBmbG9hdDIoMjEuNTM1MSwgMTQuMzEzNykpKTsKCWZsb2F0IHh5ID0gbi54ICogbi55OwoJcmV0dXJuIChmcmFjdCh4eSAqIDk1LjQzMDcpICsgZnJhY3QoeHkgKiA3NS4wNDk2MSkpIC0gMS4wOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKSAKewoJcmV0dXJuIHN0ZXAobCwgdikgKiAoMS4wIC0gc3RlcChoLCB2KSk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0IF8yNDIgPSBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChwYXJhbSk7CglmbG9hdCBuID0gXzI0MjsKCWZsb2F0IHBhcmFtXzEgPSBuOwoJZmxvYXQgcGFyYW1fMiA9IDAuMDsKCWZsb2F0IHBhcmFtXzMgPSAwLjA1OwoJZmxvYXQgcyA9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0IHBhcmFtXzQgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuMzUpKTsKCWZsb2F0IHBhcmFtXzUgPSAwLjE7CglmbG9hdCBwYXJhbV82ID0gMC4xNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYpOwoJZmxvYXQgcGFyYW1fNyA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC43KSk7CglmbG9hdCBwYXJhbV84ID0gMC4yOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IHBhcmFtXzEwID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAxLjA1KSk7CglmbG9hdCBwYXJhbV8xMSA9IDAuMzsKCWZsb2F0IHBhcmFtXzEyID0gMC4zNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIpOwoJcmV0dXJuIGNsYW1wKHMsIDAuMCwgMS4wKSAqIDAuNTU7Cn0Kdm9pZCBGTFRfbWFpbl9TMV9jMCgpIAp7Cgl1X2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC54OwoJdV9zcGFya2xlX2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC55OwoJdV9ibHVyX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC56OwoJdV9yYWRpdXNfc2NhbGVfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnc7CglmbG9hdDIgcCA9IEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKCWZsb2F0MiB1dl8xID0gcCAqIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKCWZsb2F0MiBkZW5zaXR5X3V2ID0gdXZfMSAtIG1vZChwLCB1X25vaXNlX3NjYWxlX1MxX2MwKTsKCWZsb2F0IHJhZGl1cyA9IHVfbWF4X3JhZGl1c19TMV9jMCAqIHVfcmFkaXVzX3NjYWxlX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdXZfMTsKCWZsb2F0IHR1cmJ1bGVuY2UgPSBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKHBhcmFtXzEzKTsKCWZsb2F0MiBwYXJhbV8xNCA9IHA7CglmbG9hdDIgcGFyYW1fMTUgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzE2ID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMTcgPSAwLjA1ICogdV9tYXhfcmFkaXVzX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTggPSB1X2JsdXJfUzFfYzA7CglmbG9hdCByaW5nID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKHBhcmFtXzE0LCBwYXJhbV8xNSwgcGFyYW1fMTYsIHBhcmFtXzE3LCBwYXJhbV8xOCk7CglmbG9hdDIgcGFyYW1fMTkgPSBkZW5zaXR5X3V2OwoJZmxvYXQgcGFyYW1fMjAgPSB1X25vaXNlX3BoYXNlX1MxX2MwOwoJZmxvYXQgc3BhcmtsZSA9ICgoRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChwYXJhbV8xOSwgcGFyYW1fMjApICogcmluZykgKiB0dXJidWxlbmNlKSAqIHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8yMSA9IHA7CglmbG9hdDIgcGFyYW1fMjIgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzIzID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMjQgPSB1X2JsdXJfUzFfYzA7CglmbG9hdCB3YXZlX2FscGhhID0gKEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzIxLCBwYXJhbV8yMiwgcGFyYW1fMjMsIHBhcmFtXzI0KSAqIHVfYWxwaGFfUzFfYzApICogdV9jb2xvcl9TMV9jMC53OwoJZmxvYXQ0IHdhdmVfY29sb3IgPSBmbG9hdDQodV9jb2xvcl9TMV9jMC54eXogKiB3YXZlX2FscGhhLCB3YXZlX2FscGhhKTsKCWZyYWdDb2xvcl9TMV9jMCA9IG1peCh3YXZlX2NvbG9yLCBmbG9hdDQoMS4wKSwgZmxvYXQ0KHNwYXJrbGUpKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CglmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMCA9IGZsb2F0NChfdG1wXzFfY29vcmRzLCAwLjAsIDAuMCk7CglGTFRfbWFpbl9TMV9jMCgpOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGZyYWdDb2xvcl9TMV9jMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDaXJjdWxhclJSZWN0X1MyKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0IGR5MCA9IHVpbm5lclJlY3RfUzIuVCAtIHNrX0ZyYWdDb29yZC55OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyLlJCOwoJZmxvYXQyIGR4eSA9IG1heChmbG9hdDIoZHh5MS54LCBtYXgoZHkwLCBkeHkxLnkpKSwgMC4wKTsKCWhhbGYgbGVmdEFscGhhID0gaGFsZihzYXR1cmF0ZShza19GcmFnQ29vcmQueCAtIHVpbm5lclJlY3RfUzIuTCkpOwoJaGFsZiBhbHBoYSA9IGxlZnRBbHBoYSAqIGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MyLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvbG9yX1MwKTsKCWhhbGY0IG91dHB1dF9TMjsKCW91dHB1dF9TMiA9IENpcmN1bGFyUlJlY3RfUzIob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","DASAAAAAAAAAAAEAAFQAAIGAAEAOB77776PUEAIBAAAAAABAAAAAAABAMQAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1PVAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBCaXRtYXBUZXh0CglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc1NpemVJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAAAADYAQAAdW5pZm9ybSBoYWxmNCB1Q29sb3JfUzA7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpmbGF0IGluIGZsb2F0IHZUZXhJbmRleF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB1Q29sb3JfUzA7CgloYWxmNCB0ZXhDb2xvcjsKCXsKCQl0ZXhDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdlRleHR1cmVDb29yZHNfUzApOwoJfQoJb3V0cHV0Q29sb3JfUzAgPSBvdXRwdXRDb2xvcl9TMCAqIHRleENvbG9yOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAA8AAABpblRleHR1cmVDb29yZHMAAAAAAA==","AYAA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1OCAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5DaXJjbGVFZGdlOwpvdXQgZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMl9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAADqAQAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HUQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1PPAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7Cm91dCBoYWxmNCB2Y29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cn0KAAEAAACbAgAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGhhbGY0IHZjb2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IENpcmN1bGFyUlJlY3RfUzEob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","DAQAAAAAAAAAAAAAAJQAAIGAAEACBYQCAGAEFAIBAAAAAABAAAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAAqQEAAHNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMDsKaW4gZmxvYXQyIHZUZXh0dXJlQ29vcmRzX1MwOwpmbGF0IGluIGZsb2F0IHZUZXhJbmRleF9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBCaXRtYXBUZXh0CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CgloYWxmNCB0ZXhDb2xvcjsKCXsKCQl0ZXhDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdlRleHR1cmVDb29yZHNfUzApLnJycnI7Cgl9CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IHRleENvbG9yOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","FAAQMYAAMAAAEADAAABAEYAAAICIAB5AABQAAAQAMAAAEATAAABAIIGAAEDCBYQCA4AAAAAAAA5AAAAAAABAAAAACAZAAAAA":"DAAAAExTS1PUCwAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0NCByYWRpaV9zZWxlY3RvcjsKaW4gZmxvYXQ0IGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHM7CmluIGZsb2F0NCBhYV9ibG9hdF9hbmRfY292ZXJhZ2U7CmluIGZsb2F0NCByYWRpaV94OwppbiBmbG9hdDQgcmFkaWlfeTsKaW4gZmxvYXQ0IHNrZXc7CmluIGZsb2F0MiB0cmFuc2xhdGVfYW5kX2xvY2Fscm90YXRlOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZhcmNjb29yZF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgRmlsbFJSZWN0T3A6OlByb2Nlc3NvcgoJdmNvbG9yX1MwID0gY29sb3I7CglmbG9hdCBhYV9ibG9hdF9tdWx0aXBsaWVyID0gMTsKCWZsb2F0MiBjb3JuZXIgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnh5OwoJZmxvYXQyIHJhZGl1c19vdXRzZXQgPSBjb3JuZXJfYW5kX3JhZGl1c19vdXRzZXRzLnp3OwoJZmxvYXQyIGFhX2Jsb2F0X2RpcmVjdGlvbiA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS54eTsKCWZsb2F0IGlzX2xpbmVhcl9jb3ZlcmFnZSA9IGFhX2Jsb2F0X2FuZF9jb3ZlcmFnZS53OwoJZmxvYXQyIHBpeGVsbGVuZ3RoID0gaW52ZXJzZXNxcnQoZmxvYXQyKGRvdChza2V3Lnh6LCBza2V3Lnh6KSwgZG90KHNrZXcueXcsIHNrZXcueXcpKSk7CglmbG9hdDQgbm9ybWFsaXplZF9heGlzX2RpcnMgPSBza2V3ICogcGl4ZWxsZW5ndGgueHl4eTsKCWZsb2F0MiBheGlzd2lkdGhzID0gKGFicyhub3JtYWxpemVkX2F4aXNfZGlycy54eSkgKyBhYnMobm9ybWFsaXplZF9heGlzX2RpcnMuencpKTsKCWZsb2F0MiBhYV9ibG9hdHJhZGl1cyA9IGF4aXN3aWR0aHMgKiBwaXhlbGxlbmd0aCAqIC41OwoJZmxvYXQ0IHJhZGlpX2FuZF9uZWlnaGJvcnMgPSByYWRpaV9zZWxlY3RvciogZmxvYXQ0eDQocmFkaWlfeCwgcmFkaWlfeSwgcmFkaWlfeC55eHd6LCByYWRpaV95Lnd6eXgpOwoJZmxvYXQyIHJhZGlpID0gcmFkaWlfYW5kX25laWdoYm9ycy54eTsKCWZsb2F0MiBuZWlnaGJvcl9yYWRpaSA9IHJhZGlpX2FuZF9uZWlnaGJvcnMuenc7CglmbG9hdCBjb3ZlcmFnZV9tdWx0aXBsaWVyID0gMTsKCWlmIChhbnkoZ3JlYXRlclRoYW4oYWFfYmxvYXRyYWRpdXMsIGZsb2F0MigxKSkpKSAKCXsKCQljb3JuZXIgPSBtYXgoYWJzKGNvcm5lciksIGFhX2Jsb2F0cmFkaXVzKSAqIHNpZ24oY29ybmVyKTsKCQljb3ZlcmFnZV9tdWx0aXBsaWVyID0gMSAvIChtYXgoYWFfYmxvYXRyYWRpdXMueCwgMSkgKiBtYXgoYWFfYmxvYXRyYWRpdXMueSwgMSkpOwoJCXJhZGlpID0gZmxvYXQyKDApOwoJfQoJZmxvYXQgY292ZXJhZ2UgPSBhYV9ibG9hdF9hbmRfY292ZXJhZ2UuejsKCWlmIChhbnkobGVzc1RoYW4ocmFkaWksIGFhX2Jsb2F0cmFkaXVzICogMS41KSkpIAoJewoJCXJhZGlpID0gZmxvYXQyKDApOwoJCWFhX2Jsb2F0X2RpcmVjdGlvbiA9IHNpZ24oY29ybmVyKTsKCQlpZiAoY292ZXJhZ2UgPiAuNSkgCgkJewoJCQlhYV9ibG9hdF9kaXJlY3Rpb24gPSAtYWFfYmxvYXRfZGlyZWN0aW9uOwoJCX0KCQlpc19saW5lYXJfY292ZXJhZ2UgPSAxOwoJfQoJZWxzZSAKCXsKCQlyYWRpaSA9IGNsYW1wKHJhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQluZWlnaGJvcl9yYWRpaSA9IGNsYW1wKG5laWdoYm9yX3JhZGlpLCBwaXhlbGxlbmd0aCAqIDEuNSwgMiAtIHBpeGVsbGVuZ3RoICogMS41KTsKCQlmbG9hdDIgc3BhY2luZyA9IDIgLSByYWRpaSAtIG5laWdoYm9yX3JhZGlpOwoJCWZsb2F0MiBleHRyYV9wYWQgPSBtYXgocGl4ZWxsZW5ndGggKiAuMDYyNSAtIHNwYWNpbmcsIGZsb2F0MigwKSk7CgkJcmFkaWkgLT0gZXh0cmFfcGFkICogLjU7Cgl9CglmbG9hdDIgYWFfb3V0c2V0ID0gYWFfYmxvYXRfZGlyZWN0aW9uICogYWFfYmxvYXRyYWRpdXMgKiBhYV9ibG9hdF9tdWx0aXBsaWVyOwoJZmxvYXQyIHZlcnRleHBvcyA9IGNvcm5lciArIHJhZGl1c19vdXRzZXQgKiByYWRpaSArIGFhX291dHNldDsKCWlmIChjb3ZlcmFnZSA+IC41KSAKCXsKCQlpZiAoYWFfYmxvYXRfZGlyZWN0aW9uLnggIT0gMCAmJiB2ZXJ0ZXhwb3MueCAqIGNvcm5lci54IDwgMCkgCgkJewoJCQlmbG9hdCBiYWNrc2V0ID0gYWJzKHZlcnRleHBvcy54KTsKCQkJdmVydGV4cG9zLnggPSAwOwoJCQl2ZXJ0ZXhwb3MueSArPSBiYWNrc2V0ICogc2lnbihjb3JuZXIueSkgKiBwaXhlbGxlbmd0aC55L3BpeGVsbGVuZ3RoLng7CgkJCWNvdmVyYWdlID0gKGNvdmVyYWdlIC0gLjUpICogYWJzKGNvcm5lci54KSAvIChhYnMoY29ybmVyLngpICsgYmFja3NldCkgKyAuNTsKCQl9CgkJaWYgKGFhX2Jsb2F0X2RpcmVjdGlvbi55ICE9IDAgJiYgdmVydGV4cG9zLnkgKiBjb3JuZXIueSA8IDApIAoJCXsKCQkJZmxvYXQgYmFja3NldCA9IGFicyh2ZXJ0ZXhwb3MueSk7CgkJCXZlcnRleHBvcy55ID0gMDsKCQkJdmVydGV4cG9zLnggKz0gYmFja3NldCAqIHNpZ24oY29ybmVyLngpICogcGl4ZWxsZW5ndGgueC9waXhlbGxlbmd0aC55OwoJCQljb3ZlcmFnZSA9IChjb3ZlcmFnZSAtIC41KSAqIGFicyhjb3JuZXIueSkgLyAoYWJzKGNvcm5lci55KSArIGJhY2tzZXQpICsgLjU7CgkJfQoJfQoJZmxvYXQyeDIgc2tld21hdHJpeCA9IGZsb2F0MngyKHNrZXcueHksIHNrZXcuencpOwoJZmxvYXQyIGRldmNvb3JkID0gdmVydGV4cG9zICogc2tld21hdHJpeCArIHRyYW5zbGF0ZV9hbmRfbG9jYWxyb3RhdGUueHk7CglpZiAoMCAhPSBpc19saW5lYXJfY292ZXJhZ2UpIAoJewoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MigwLCBjb3ZlcmFnZSAqIGNvdmVyYWdlX211bHRpcGxpZXIpOwoJfQoJZWxzZSAKCXsKCQlmbG9hdDIgYXJjY29vcmQgPSAxIC0gYWJzKHJhZGl1c19vdXRzZXQpICsgYWFfb3V0c2V0L3JhZGlpICogY29ybmVyOwoJCXZhcmNjb29yZF9TMC54eSA9IGZsb2F0MihhcmNjb29yZC54KzEsIGFyY2Nvb3JkLnkpOwoJfQoJc2tfUG9zaXRpb24gPSBkZXZjb29yZC54eTAxOwp9CgAAAABFAgAAZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2YXJjY29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBGaWxsUlJlY3RPcDo6UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJZmxvYXQgeF9wbHVzXzE9dmFyY2Nvb3JkX1MwLngsIHk9dmFyY2Nvb3JkX1MwLnk7CgloYWxmIGNvdmVyYWdlOwoJaWYgKDAgPT0geF9wbHVzXzEpIAoJewoJCWNvdmVyYWdlID0gaGFsZih5KTsKCX0KCWVsc2UgCgl7CgkJZmxvYXQgZm4gPSB4X3BsdXNfMSAqICh4X3BsdXNfMSAtIDIpOwoJCWZuID0gZm1hKHkseSwgZm4pOwoJCWZsb2F0IGZud2lkdGggPSBmd2lkdGgoZm4pOwoJCWNvdmVyYWdlID0gLjUgLSBoYWxmKGZuL2Zud2lkdGgpOwoJCWNvdmVyYWdlID0gY2xhbXAoY292ZXJhZ2UsIDAsIDEpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChjb3ZlcmFnZSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAACAAAAA4AAAByYWRpaV9zZWxlY3RvcgAAGQAAAGNvcm5lcl9hbmRfcmFkaXVzX291dHNldHMAAAAVAAAAYWFfYmxvYXRfYW5kX2NvdmVyYWdlAAAABwAAAHJhZGlpX3gABwAAAHJhZGlpX3kABAAAAHNrZXcZAAAAdHJhbnNsYXRlX2FuZF9sb2NhbHJvdGF0ZQAAAAUAAABjb2xvcgAAAAAAAAA=","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFIBVWKMG7QAAAAAAGVXOVKVEAAAAADAAAAAFQ454NUDACAAAIAQZUOAMQAAAAIAAAAAEARTKLVK5LSAAAAABQAAAAAYGP6G2BSBAAAAAIO2HAGIAAAAAAAEAAAAAZ3RZTY3IGAEAAAAAAAAAGARI4UKA4WAAAAAAAEAAAABSMQII2XAGAAAAAAAAAAACAA4AAAACAAAAAAACCAYAA":"DAAAAExTS1OAAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNV9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNV9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAIIGAAB1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMDsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNV9TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzAsIHVlbmRfUzFfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IExpbmVhckxheW91dF9TMV9jMF9jMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzJfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzNfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzVfUzA7CglyZXR1cm4gaGFsZjQoaGFsZjQoaGFsZihfdG1wXzNfY29vcmRzLngpICsgMWUtMDUsIDEuMCwgMC4wLCAwLjApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIExpbmVhckxheW91dF9TMV9jMF9jMV9jMChfaW5wdXQpOwp9CmhhbGY0IENsYW1wZWRHcmFkaWVudF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzRfaW5Db2xvciA9IF9pbnB1dDsKCWhhbGY0IHQgPSBNYXRyaXhFZmZlY3RfUzFfYzBfYzEoX3RtcF80X2luQ29sb3IpOwoJaGFsZjQgb3V0Q29sb3I7CglpZiAoIWJvb2woaW50KDEpKSAmJiB0LnkgPCAwLjApIAoJewoJCW91dENvbG9yID0gaGFsZjQoMC4wKTsKCX0KCWVsc2UgaWYgKHQueCA8IDAuMCkgCgl7CgkJb3V0Q29sb3IgPSB1bGVmdEJvcmRlckNvbG9yX1MxX2MwOwoJfQoJZWxzZSBpZiAodC54ID4gMS4wKSAKCXsKCQlvdXRDb2xvciA9IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwOwoJfQoJZWxzZSAKCXsKCQlvdXRDb2xvciA9IFNpbmdsZUludGVydmFsQ29sb3JpemVyX1MxX2MwX2MwKF90bXBfNF9pbkNvbG9yLCBmbG9hdDIoaGFsZjIodC54LCAwLjApKSk7Cgl9CglyZXR1cm4gaGFsZjQob3V0Q29sb3IpOwp9CmhhbGY0IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEoaGFsZjQgX2lucHV0KSAKewoJX2lucHV0ID0gQ2xhbXBlZEdyYWRpZW50X1MxX2MwKF9pbnB1dCk7CgloYWxmNCBfdG1wXzVfaW5Db2xvciA9IF9pbnB1dDsKCXJldHVybiBoYWxmNChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShvdXRwdXRDb2xvcl9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHIBNGZODK5YIAAAAAAQAAAAAIADCNS4GV3QYBAAAAAAAAAAAAAHIAAAAAAAIAAAAAQGIAAAAAA":"DAAAAExTS1N0AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAIwbAABmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb2xvcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb21wb3NpdGVfMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jZW50ZXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9tYXhfcmFkaXVzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfbm9pc2Vfc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9ub2lzZV9waGFzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUxX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlM19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24yX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uM19TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpOwp2b2lkIEZMVF9tYWluX1MxX2MwKCk7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCkgCnsKCXJldHVybiBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMC54eTsKfQpmbG9hdDJ4MiBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChmbG9hdDIgcmFkKSAKewoJcmV0dXJuIGZsb2F0MngyKHJhZC54LCAtcmFkLnksIHJhZC55LCByYWQueCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgYmx1cikgCnsKCWZsb2F0IGJsdXJfaGFsZiA9IGJsdXIgKiAwLjU7CglmbG9hdCBkID0gZGlzdGFuY2UodXYsIHh5KTsKCXJldHVybiAxLjAgLSBzbW9vdGhzdGVwKDEuMCAtIGJsdXJfaGFsZiwgMS4wICsgYmx1cl9oYWxmLCBkIC8gcmFkaXVzKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKSAKewoJZmxvYXQyIHBhcmFtID0gcm90YXRpb247CglwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAocGFyYW0pICogKHh5IC0gcCkgKyB4eTsKCXAgPSBtb2QocCwgZmxvYXQyKGNlbGxfZGlhbWV0ZXIpKSAvIHJlc29sdXRpb247CglmbG9hdCBjZWxsX3V2ID0gKGNlbGxfZGlhbWV0ZXIgLyByZXNvbHV0aW9uLnkpICogMC41OwoJZmxvYXQgciA9IDAuNjUgKiBjZWxsX3V2OwoJZmxvYXQyIHBhcmFtXzEgPSBwOwoJZmxvYXQyIHBhcmFtXzIgPSBmbG9hdDIoY2VsbF91dik7CglmbG9hdCBwYXJhbV8zID0gcjsKCWZsb2F0IHBhcmFtXzQgPSByICogNTAuMDsKCXJldHVybiBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKGZsb2F0MiB1dikgCnsKCWZsb2F0MiB1dl9zY2FsZSA9IHV2ICogZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW0gPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMiA9IHVfY2lyY2xlMV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8zID0gdV9yb3RhdGlvbjFfUzFfYzA7CglmbG9hdCBwYXJhbV80ID0gMC4xNzsKCWZsb2F0IF8zMTkgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7CglmbG9hdCBnMSA9IF8zMTk7CglmbG9hdDIgcGFyYW1fNSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzYgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV83ID0gdV9jaXJjbGUyX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzggPSB1X3JvdGF0aW9uMl9TMV9jMDsKCWZsb2F0IHBhcmFtXzkgPSAwLjI7CglmbG9hdCBfMzMxID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBnMiA9IF8zMzE7CglmbG9hdDIgcGFyYW1fMTAgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV8xMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzEyID0gdV9jaXJjbGUzX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdV9yb3RhdGlvbjNfUzFfYzA7CglmbG9hdCBwYXJhbV8xNCA9IDAuMjc1OwoJZmxvYXQgXzM0NCA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIsIHBhcmFtXzEzLCBwYXJhbV8xNCk7CglmbG9hdCBnMyA9IF8zNDQ7CglmbG9hdCB2ID0gKChnMSAqIGcxICsgZzIpIC0gZzMpICogMC41OwoJcmV0dXJuIGNsYW1wKDAuNDUgKyAwLjggKiB2LCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cikgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQyIHBhcmFtXzEgPSB4eTsKCWZsb2F0IHBhcmFtXzIgPSByYWRpdXMgKyB0aGlja25lc3M7CglmbG9hdCBwYXJhbV8zID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9vdXRlciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0MiBwYXJhbV80ID0gdXY7CglmbG9hdDIgcGFyYW1fNSA9IHh5OwoJZmxvYXQgcGFyYW1fNiA9IG1heChyYWRpdXMgLSB0aGlja25lc3MsIDAuMCk7CglmbG9hdCBwYXJhbV83ID0gYmx1cjsKCWZsb2F0IGNpcmNsZV9pbm5lciA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcpOwoJcmV0dXJuIGNsYW1wKGNpcmNsZV9vdXRlciAtIGNpcmNsZV9pbm5lciwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKSAKewoJbiA9IGZyYWN0KG4gKiBmbG9hdDIoNS4zOTg3LCA1LjQ0MjEpKTsKCW4gKz0gZmxvYXQyKGRvdChuLnl4LCBuICsgZmxvYXQyKDIxLjUzNTEsIDE0LjMxMzcpKSk7CglmbG9hdCB4eSA9IG4ueCAqIG4ueTsKCXJldHVybiAoZnJhY3QoeHkgKiA5NS40MzA3KSArIGZyYWN0KHh5ICogNzUuMDQ5NjEpKSAtIDEuMDsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCkgCnsKCXJldHVybiBzdGVwKGwsIHYpICogKDEuMCAtIHN0ZXAoaCwgdikpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdCBfMjQyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAocGFyYW0pOwoJZmxvYXQgbiA9IF8yNDI7CglmbG9hdCBwYXJhbV8xID0gbjsKCWZsb2F0IHBhcmFtXzIgPSAwLjA7CglmbG9hdCBwYXJhbV8zID0gMC4wNTsKCWZsb2F0IHMgPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdCBwYXJhbV80ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjM1KSk7CglmbG9hdCBwYXJhbV81ID0gMC4xOwoJZmxvYXQgcGFyYW1fNiA9IDAuMTU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82KTsKCWZsb2F0IHBhcmFtXzcgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuNykpOwoJZmxvYXQgcGFyYW1fOCA9IDAuMjsKCWZsb2F0IHBhcmFtXzkgPSAwLjI1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNywgcGFyYW1fOCwgcGFyYW1fOSk7CglmbG9hdCBwYXJhbV8xMCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMS4wNSkpOwoJZmxvYXQgcGFyYW1fMTEgPSAwLjM7CglmbG9hdCBwYXJhbV8xMiA9IDAuMzU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyKTsKCXJldHVybiBjbGFtcChzLCAwLjAsIDEuMCkgKiAwLjU1Owp9CnZvaWQgRkxUX21haW5fUzFfYzAoKSAKewoJdV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueDsKCXVfc3BhcmtsZV9hbHBoYV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAueTsKCXVfYmx1cl9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAuejsKCXVfcmFkaXVzX3NjYWxlX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC53OwoJZmxvYXQyIHAgPSBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CglmbG9hdDIgdXZfMSA9IHAgKiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CglmbG9hdDIgZGVuc2l0eV91diA9IHV2XzEgLSBtb2QocCwgdV9ub2lzZV9zY2FsZV9TMV9jMCk7CglmbG9hdCByYWRpdXMgPSB1X21heF9yYWRpdXNfUzFfYzAgKiB1X3JhZGl1c19zY2FsZV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHV2XzE7CglmbG9hdCB0dXJidWxlbmNlID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChwYXJhbV8xMyk7CglmbG9hdDIgcGFyYW1fMTQgPSBwOwoJZmxvYXQyIHBhcmFtXzE1ID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8xNiA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzE3ID0gMC4wNSAqIHVfbWF4X3JhZGl1c19TMV9jMDsKCWZsb2F0IHBhcmFtXzE4ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgcmluZyA9IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChwYXJhbV8xNCwgcGFyYW1fMTUsIHBhcmFtXzE2LCBwYXJhbV8xNywgcGFyYW1fMTgpOwoJZmxvYXQyIHBhcmFtXzE5ID0gZGVuc2l0eV91djsKCWZsb2F0IHBhcmFtXzIwID0gdV9ub2lzZV9waGFzZV9TMV9jMDsKCWZsb2F0IHNwYXJrbGUgPSAoKEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAocGFyYW1fMTksIHBhcmFtXzIwKSAqIHJpbmcpICogdHVyYnVsZW5jZSkgKiB1X3NwYXJrbGVfYWxwaGFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMjEgPSBwOwoJZmxvYXQyIHBhcmFtXzIyID0gdV9jZW50ZXJfUzFfYzA7CglmbG9hdCBwYXJhbV8yMyA9IHJhZGl1czsKCWZsb2F0IHBhcmFtXzI0ID0gdV9ibHVyX1MxX2MwOwoJZmxvYXQgd2F2ZV9hbHBoYSA9IChGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV8yMSwgcGFyYW1fMjIsIHBhcmFtXzIzLCBwYXJhbV8yNCkgKiB1X2FscGhhX1MxX2MwKSAqIHVfY29sb3JfUzFfYzAudzsKCWZsb2F0NCB3YXZlX2NvbG9yID0gZmxvYXQ0KHVfY29sb3JfUzFfYzAueHl6ICogd2F2ZV9hbHBoYSwgd2F2ZV9hbHBoYSk7CglmcmFnQ29sb3JfUzFfYzAgPSBtaXgod2F2ZV9jb2xvciwgZmxvYXQ0KDEuMCksIGZsb2F0NChzcGFya2xlKSk7Cn0KaGFsZjQgcnVudGltZV9zaGFkZXJfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8wX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8xX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwoJZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAgPSBmbG9hdDQoX3RtcF8xX2Nvb3JkcywgMC4wLCAwLjApOwoJRkxUX21haW5fUzFfYzAoKTsKCXJldHVybiBoYWxmNChoYWxmNChmcmFnQ29sb3JfUzFfYzApKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIHJ1bnRpbWVfc2hhZGVyX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","HWQACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1ONAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDQgZ2VvbVN1YnNldDsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpmbGF0IG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAB5AgAAZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKZmxhdCBpbiBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWZsb2F0NCBnZW9TdWJzZXQ7CglnZW9TdWJzZXQgPSB2Z2VvbVN1YnNldF9TMDsKCWhhbGY0IGRpc3RzNCA9IGNsYW1wKGhhbGY0KDEsIDEsIC0xLCAtMSkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIGdlb1N1YnNldCksIDAsIDEpOwoJaGFsZjIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWhhbGYgc3Vic2V0Q292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJY292ZXJhZ2UgPSBtaW4oY292ZXJhZ2UsIHN1YnNldENvdmVyYWdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFQBVWKMG7QAAAAAAAAAACAAAAAVQEAAQAAAAAQCDAEQQGAAAAAAAAAAAA4IAPAAACAAAAAAAEABYAAAAEAAAAAAAEEBQA":"DAAAAExTS1N6AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAABAAAApAQAAHVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMjsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzI7CnNhbXBsZXJFeHRlcm5hbE9FUyB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCB2VHJhbnNmb3JtZWRDb29yZHNfM19TMCk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBNYXRyaXhFZmZlY3RfUzFfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0KaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMihoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzIuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzIuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMi54IC0gbGVuZ3RoKGR4eSkpKTsKCXJldHVybiBfaW5wdXQgKiBhbHBoYTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IERpc2FibGVDb3ZlcmFnZUFzQWxwaGFfUzEob3V0cHV0Q29sb3JfUzApOwoJaGFsZjQgb3V0cHV0X1MyOwoJb3V0cHV0X1MyID0gQ2lyY3VsYXJSUmVjdF9TMihvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0X1MyOwoJfQp9CgEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","HUQACAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADEAAGAAAAAAAAAIAAAAAPAIABAAAAACYGEAAAAEAAAABUARCAIAAAAAAAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1NjAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNF9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogcG9zaXRpb24ueHkxOwoJfQp9CgAAAAAANAMAAHVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwppbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMCkuMDAwcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBEZXZpY2VTcGFjZV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gTWF0cml4RWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9kc3RfaW4oRGV2aWNlU3BhY2VfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","B2AAQAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1MOAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmIGluQ292ZXJhZ2U7Cm91dCBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cgl2aW5Db3ZlcmFnZV9TMCA9IGluQ292ZXJhZ2U7Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAATQEAAHVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwppbiBoYWxmIHZpbkNvdmVyYWdlX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGVmYXVsdEdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdUNvbG9yX1MwOwoJaGFsZiBhbHBoYSA9IDEuMDsKCWFscGhhID0gdmluQ292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAAoAAABpbkNvdmVyYWdlAAAAAAAA","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAADIAAAAGULKMMQKAAAAAAMAAAAAIAAAAAAGIRBNAWEYZAUAABQAAAAAAAAAAAAADUAAAAAAAEAAAAAIDEAAA":"DAAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAACoBAAAY29uc3QgaW50IGtGaWxsQUFfUzFfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzFfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzFfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1Y2lyY2xlX1MxX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKaGFsZjQgQ2lyY2xlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBkOwoJaWYgKGludCgxKSA9PSBrSW52ZXJzZUZpbGxCV19TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzApIAoJewoJCWQgPSBoYWxmKChsZW5ndGgoKHVjaXJjbGVfUzFfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMV9jMC53KSAtIDEuMCkgKiB1Y2lyY2xlX1MxX2MwLnopOwoJfQoJZWxzZSAKCXsKCQlkID0gaGFsZigoMS4wIC0gbGVuZ3RoKCh1Y2lyY2xlX1MxX2MwLnh5IC0gc2tfRnJhZ0Nvb3JkLnh5KSAqIHVjaXJjbGVfUzFfYzAudykpICogdWNpcmNsZV9TMV9jMC56KTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChpbnQoMSkgPT0ga0ZpbGxBQV9TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQUFfUzFfYzAgPyBzYXR1cmF0ZShkKSA6IGhhbGYoZCA+IDAuNSkpKTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX21vZHVsYXRlKF9zcmMsIENpcmNsZV9TMV9jMChfc3JjKSk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","HVIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAFIBUAFS7EOCAAAAAGZJY26AAQAAAA2S5KXK4QAAAAAMAAAAAAAAQAABQM74NUDECAAAAAQ5UOAMQAAAAAAAAAAEAAAAAZ3FHDLYADAAAABKDVK5LSCAIAABQAAAAAAACAAAGAT3RWSMYIAAAAADWRYBSQAAAAAAAQAAAAGJJOXLVOIIBAAAGAAAAAAAAIAAAIAPOH2NTBAAAAAAOKFAOLAAAAAAAEAAAAAMQQAIAAAYGP6G2BSBAAACAAAAAAAAM5S4Z4NUDACAAAAAAAAADAIUOKFAOLAAAAAAACAAAAAZGIEENLQDAAAAAAAAAAABQAKAIAAIAAAADIBCEAQAAAAAAAAAAAIADQAAAAUAAAAAAAIIDA":"DAAAAExTS1PrAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzdfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc183X1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMF9jMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KfQoAAQAAAG4KAAB1bmlmb3JtIGhhbGY0IHVzdGFydF9TMV9jMF9jMF9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1ZW5kX1MxX2MwX2MwX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzBfYzE7CnVuaWZvcm0gaGFsZjQgdWxlZnRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MxOwp1bmlmb3JtIGhhbGYgdXJhbmdlX1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdCB2Y292ZXJhZ2VfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfN19TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzBfYzBfYzAsIHVlbmRfUzFfYzBfYzBfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IGNvbG9yX3hmb3JtX1MxX2MwX2MwX2MwKGZsb2F0NCBjb2xvcikgCnsKCWNvbG9yLnJnYiAqPSBjb2xvci5hOwoJcmV0dXJuIGhhbGY0KGNvbG9yKTsKfQpoYWxmNCBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBjb2xvcl94Zm9ybV9TMV9jMF9jMF9jMChTaW5nbGVJbnRlcnZhbENvbG9yaXplcl9TMV9jMF9jMF9jMF9jMChfaW5wdXQsIF9jb29yZHMpKTsKfQpoYWxmNCBMaW5lYXJMYXlvdXRfUzFfYzBfYzBfYzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8yX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8zX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc183X1MwOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGhhbGYoX3RtcF8zX2Nvb3Jkcy54KSArIDFlLTA1LCAxLjAsIDAuMCwgMC4wKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwX2MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBMaW5lYXJMYXlvdXRfUzFfYzBfYzBfYzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDbGFtcGVkR3JhZGllbnRfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF80X2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmNCB0ID0gTWF0cml4RWZmZWN0X1MxX2MwX2MwX2MxKF90bXBfNF9pbkNvbG9yKTsKCWhhbGY0IG91dENvbG9yOwoJaWYgKCFib29sKGludCgxKSkgJiYgdC55IDwgMC4wKSAKCXsKCQlvdXRDb2xvciA9IGhhbGY0KDAuMCk7Cgl9CgllbHNlIGlmICh0LnggPCAwLjApIAoJewoJCW91dENvbG9yID0gdWxlZnRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKCX0KCWVsc2UgaWYgKHQueCA+IDEuMCkgCgl7CgkJb3V0Q29sb3IgPSB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKCX0KCWVsc2UgCgl7CgkJb3V0Q29sb3IgPSBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzBfYzAoX3RtcF80X2luQ29sb3IsIGZsb2F0MihoYWxmMih0LngsIDAuMCkpKTsKCX0KCXJldHVybiBoYWxmNChvdXRDb2xvcik7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBDbGFtcGVkR3JhZGllbnRfUzFfYzBfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfNV9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0KaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMV9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgX2Nvb3JkcykuMDAwcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzEoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MxX2MwKF9pbnB1dCwgZmxvYXQzeDIodW1hdHJpeF9TMV9jMSkgKiBfY29vcmRzLnh5MSk7Cn0KaGFsZjQgRGl0aGVyX1MxKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfNl9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjQgY29sb3IgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxX2MwKF90bXBfNl9pbkNvbG9yKTsKCWhhbGYgdmFsdWUgPSBNYXRyaXhFZmZlY3RfUzFfYzEoX3RtcF82X2luQ29sb3IsIHNrX0ZyYWdDb29yZC54eSkudyAtIDAuNTsKCXJldHVybiBoYWxmNChoYWxmNChjbGFtcChjb2xvci54eXogKyB2YWx1ZSAqIHVyYW5nZV9TMSwgMC4wLCBjb2xvci53KSwgY29sb3IudykpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGl0aGVyX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSAoaGFsZjQoMS4wKSAtIG91dHB1dF9TMSkgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAGIBIAAABAAAAANAEAAAAAAAAAAAAAABAAOAAAABAAAAAAABBAMAAAAA":"DAAAAExTS1N0AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAGsCAAB1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCkucnJycjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzAoX2lucHV0KTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUASJ3EZYN2AAAAAAAIAEAAAABSCQKL3IYIJ2AAAAAAAIAEAAAABLBCABAAAAABAEGABBAMAAACAAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAGgHAABjb25zdCBpbnQga01heExvb3BMaW1pdF9TMV9jMCA9IDg7CnVuaWZvcm0gaGFsZjQgdWJvcmRlcl9TMV9jMF9jMF9jMDsKdW5pZm9ybSBmbG9hdDQgdXN1YnNldF9TMV9jMF9jMF9jMDsKdW5pZm9ybSBmbG9hdDQgdWNsYW1wX1MxX2MwX2MwX2MwOwp1bmlmb3JtIGZsb2F0MiB1aWRpbXNfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxNF07CnVuaWZvcm0gaGFsZjIgdWRpcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJZmxvYXQyIGluQ29vcmQgPSBfY29vcmRzOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBzdWJzZXRDb29yZC54OwoJY2xhbXBlZENvb3JkLnkgPSBjbGFtcChzdWJzZXRDb29yZC55LCB1Y2xhbXBfUzFfYzBfYzBfYzAueSwgdWNsYW1wX1MxX2MwX2MwX2MwLncpOwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCAoY2xhbXBlZENvb3JkKSAqIHVpZGltc19TMV9jMF9jMF9jMCk7CgloYWxmIGVyclkgPSBoYWxmKHN1YnNldENvb3JkLnkgLSBjbGFtcGVkQ29vcmQueSk7Cgl0ZXh0dXJlQ29sb3IgPSBtaXgodGV4dHVyZUNvbG9yLCB1Ym9yZGVyX1MxX2MwX2MwX2MwLCBtaW4oYWJzKGVyclkpLCAxKSk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBHYXVzc2lhbkJsdXIxRF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CgloYWxmNCBzdW0gPSBoYWxmNCgwLjApOwoJZm9yIChpbnQgaSA9IDA7aSA8IGtNYXhMb29wTGltaXRfUzFfYzA7ICsraSkgCgl7CgkJaGFsZjQgcyA9IHVvZmZzZXRzQW5kS2VybmVsX1MxX2MwW2ldOwoJCXN1bSArPSBzLnkgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIocy54ICogdWRpcl9TMV9jMCkpOwoJCXN1bSArPSBzLncgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIocy56ICogdWRpcl9TMV9jMCkpOwoJfQoJcmV0dXJuIGhhbGY0KHN1bSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBHYXVzc2lhbkJsdXIxRF9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","DAQAAAAAAAAAAAAAAJQAAIGAAEACBYQCAGAEFAIBAAAAAABAAAAAAAAAAAACAB4QA4AAAEAAAAAABIADAAAAAIAAAAAAAIID":"DAAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAABAAAAOAMAAHVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzE7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBCaXRtYXBUZXh0CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CgloYWxmNCB0ZXhDb2xvcjsKCXsKCQl0ZXhDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdlRleHR1cmVDb29yZHNfUzApLnJycnI7Cgl9CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IHRleENvbG9yOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADwAAAGluVGV4dHVyZUNvb3JkcwAAAAAA","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGACQAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1PlAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7Cm91dCBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJdmxvY2FsQ29vcmRfUzAgPSBsb2NhbENvb3JkOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAAAAACkAQAAc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7CglmbG9hdDIgdGV4Q29vcmQ7Cgl0ZXhDb29yZCA9IHZsb2NhbENvb3JkX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSAoYmxlbmRfbW9kdWxhdGUoc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB0ZXhDb29yZCksIGhhbGY0KDEpKSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","DAQAAAAAAAAAAAAAAJQAAIGAAEACBYQCAGAEFAIBAAAAAABAAAAAAAAAAAACAA6QAAAABUDB7AHU6AIAAAYAAAAAQAAAAAEARR2BR7WDKMAAAAAMAAAAAAAAAAAABIADAAAAAIAAAAAAAIIDAAAA":"DAAAAExTS1MWAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc1NpemVJbnZfUzA7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIEJpdG1hcFRleHQKCWludCB0ZXhJZHggPSAwOwoJZmxvYXQyIHVub3JtVGV4Q29vcmRzID0gZmxvYXQyKGluVGV4dHVyZUNvb3Jkcy54LCBpblRleHR1cmVDb29yZHMueSk7Cgl2VGV4dHVyZUNvb3Jkc19TMCA9IHVub3JtVGV4Q29vcmRzICogdUF0bGFzU2l6ZUludl9TMDsKCXZUZXhJbmRleF9TMCA9IGZsb2F0KHRleElkeCk7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBpblBvc2l0aW9uLnh5MDE7Cn0KAAABAAAAPQUAAGNvbnN0IGludCBrRmlsbEJXX1MxX2MwID0gMDsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEJXX1MxX2MwID0gMjsKY29uc3QgaW50IGtJbnZlcnNlRmlsbEFBX1MxX2MwID0gMzsKdW5pZm9ybSBmbG9hdDQgdXJlY3RVbmlmb3JtX1MxX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzA7CmluIGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBpbiBmbG9hdCB2VGV4SW5kZXhfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwpoYWxmNCBSZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJaGFsZiBjb3ZlcmFnZTsKCWlmIChpbnQoMSkgPT0ga0ZpbGxCV19TMV9jMCB8fCBpbnQoMSkgPT0ga0ludmVyc2VGaWxsQldfUzFfYzApIAoJewoJCWNvdmVyYWdlID0gaGFsZihhbGwoZ3JlYXRlclRoYW4oZmxvYXQ0KHNrX0ZyYWdDb29yZC54eSwgdXJlY3RVbmlmb3JtX1MxX2MwLnp3KSwgZmxvYXQ0KHVyZWN0VW5pZm9ybV9TMV9jMC54eSwgc2tfRnJhZ0Nvb3JkLnh5KSkpKTsKCX0KCWVsc2UgCgl7CgkJaGFsZjQgZGlzdHM0ID0gc2F0dXJhdGUoaGFsZjQoMS4wLCAxLjAsIC0xLjAsIC0xLjApICogaGFsZjQoc2tfRnJhZ0Nvb3JkLnh5eHkgLSB1cmVjdFVuaWZvcm1fUzFfYzApKTsKCQloYWxmMiBkaXN0czIgPSAoZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3KSAtIDEuMDsKCQljb3ZlcmFnZSA9IGRpc3RzMi54ICogZGlzdHMyLnk7Cgl9CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MxX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMV9jMCkgCgl7CgkJY292ZXJhZ2UgPSAxLjAgLSBjb3ZlcmFnZTsKCX0KCXJldHVybiBoYWxmNChoYWxmNChjb3ZlcmFnZSkpOwp9CmhhbGY0IEJsZW5kX1MxKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoUmVjdF9TMV9jMChfc3JjKSwgX3NyYyk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIEJpdG1hcFRleHQKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGY0IHRleENvbG9yOwoJewoJCXRleENvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MwLCB2VGV4dHVyZUNvb3Jkc19TMCkucnJycjsKCX0KCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gdGV4Q29sb3I7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgAAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpblRleHR1cmVDb29yZHMAAAAAAA==","HVIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAHIBNGZODK5YIAAAAAAQAAAAAIADCNS4GV3QYBAAAAAAAAAAAAAHIAAAAAAAIAAAAAQGIAAAAAA":"DAAAAExTS1PZAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KfQoAAAAAAAAAyhsAAGZsb2F0NCBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMDsKZmxvYXQ0IGZyYWdDb2xvcl9TMV9jMDsKZmxvYXQgdV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9zcGFya2xlX2FscGhhX1MxX2MwOwpmbG9hdCB1X2JsdXJfUzFfYzA7CmZsb2F0IHVfcmFkaXVzX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbG9yX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbXBvc2l0ZV8xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NlbnRlcl9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X21heF9yYWRpdXNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9ub2lzZV9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X25vaXNlX3BoYXNlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24zX1MxX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdCB2Y292ZXJhZ2VfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KTsKdm9pZCBGTFRfbWFpbl9TMV9jMCgpOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpIAp7CglyZXR1cm4gZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAueHk7Cn0KZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCkgCnsKCXJldHVybiBmbG9hdDJ4MihyYWQueCwgLXJhZC55LCByYWQueSwgcmFkLngpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpIAp7CglmbG9hdCBibHVyX2hhbGYgPSBibHVyICogMC41OwoJZmxvYXQgZCA9IGRpc3RhbmNlKHV2LCB4eSk7CglyZXR1cm4gMS4wIC0gc21vb3Roc3RlcCgxLjAgLSBibHVyX2hhbGYsIDEuMCArIGJsdXJfaGFsZiwgZCAvIHJhZGl1cyk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcikgCnsKCWZsb2F0MiBwYXJhbSA9IHJvdGF0aW9uOwoJcCA9IEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKHBhcmFtKSAqICh4eSAtIHApICsgeHk7CglwID0gbW9kKHAsIGZsb2F0MihjZWxsX2RpYW1ldGVyKSkgLyByZXNvbHV0aW9uOwoJZmxvYXQgY2VsbF91diA9IChjZWxsX2RpYW1ldGVyIC8gcmVzb2x1dGlvbi55KSAqIDAuNTsKCWZsb2F0IHIgPSAwLjY1ICogY2VsbF91djsKCWZsb2F0MiBwYXJhbV8xID0gcDsKCWZsb2F0MiBwYXJhbV8yID0gZmxvYXQyKGNlbGxfdXYpOwoJZmxvYXQgcGFyYW1fMyA9IHI7CglmbG9hdCBwYXJhbV80ID0gciAqIDUwLjA7CglyZXR1cm4gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpIAp7CglmbG9hdDIgdXZfc2NhbGUgPSB1diAqIGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzIgPSB1X2NpcmNsZTFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMyA9IHVfcm90YXRpb24xX1MxX2MwOwoJZmxvYXQgcGFyYW1fNCA9IDAuMTc7CglmbG9hdCBfMzE5ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwoJZmxvYXQgZzEgPSBfMzE5OwoJZmxvYXQyIHBhcmFtXzUgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV82ID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fNyA9IHVfY2lyY2xlMl9TMV9jMDsKCWZsb2F0MiBwYXJhbV84ID0gdV9yb3RhdGlvbjJfUzFfYzA7CglmbG9hdCBwYXJhbV85ID0gMC4yOwoJZmxvYXQgXzMzMSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgZzIgPSBfMzMxOwoJZmxvYXQyIHBhcmFtXzEwID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMTEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8xMiA9IHVfY2lyY2xlM19TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHVfcm90YXRpb24zX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTQgPSAwLjI3NTsKCWZsb2F0IF8zNDQgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyLCBwYXJhbV8xMywgcGFyYW1fMTQpOwoJZmxvYXQgZzMgPSBfMzQ0OwoJZmxvYXQgdiA9ICgoZzEgKiBnMSArIGcyKSAtIGczKSAqIDAuNTsKCXJldHVybiBjbGFtcCgwLjQ1ICsgMC44ICogdiwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0MiBwYXJhbV8xID0geHk7CglmbG9hdCBwYXJhbV8yID0gcmFkaXVzICsgdGhpY2tuZXNzOwoJZmxvYXQgcGFyYW1fMyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfb3V0ZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdDIgcGFyYW1fNCA9IHV2OwoJZmxvYXQyIHBhcmFtXzUgPSB4eTsKCWZsb2F0IHBhcmFtXzYgPSBtYXgocmFkaXVzIC0gdGhpY2tuZXNzLCAwLjApOwoJZmxvYXQgcGFyYW1fNyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfaW5uZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83KTsKCXJldHVybiBjbGFtcChjaXJjbGVfb3V0ZXIgLSBjaXJjbGVfaW5uZXIsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbikgCnsKCW4gPSBmcmFjdChuICogZmxvYXQyKDUuMzk4NywgNS40NDIxKSk7CgluICs9IGZsb2F0Mihkb3Qobi55eCwgbiArIGZsb2F0MigyMS41MzUxLCAxNC4zMTM3KSkpOwoJZmxvYXQgeHkgPSBuLnggKiBuLnk7CglyZXR1cm4gKGZyYWN0KHh5ICogOTUuNDMwNykgKyBmcmFjdCh4eSAqIDc1LjA0OTYxKSkgLSAxLjA7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpIAp7CglyZXR1cm4gc3RlcChsLCB2KSAqICgxLjAgLSBzdGVwKGgsIHYpKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCkgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQgXzI0MiA9IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKHBhcmFtKTsKCWZsb2F0IG4gPSBfMjQyOwoJZmxvYXQgcGFyYW1fMSA9IG47CglmbG9hdCBwYXJhbV8yID0gMC4wOwoJZmxvYXQgcGFyYW1fMyA9IDAuMDU7CglmbG9hdCBzID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQgcGFyYW1fNCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC4zNSkpOwoJZmxvYXQgcGFyYW1fNSA9IDAuMTsKCWZsb2F0IHBhcmFtXzYgPSAwLjE1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNik7CglmbG9hdCBwYXJhbV83ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjcpKTsKCWZsb2F0IHBhcmFtXzggPSAwLjI7CglmbG9hdCBwYXJhbV85ID0gMC4yNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgcGFyYW1fMTAgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDEuMDUpKTsKCWZsb2F0IHBhcmFtXzExID0gMC4zOwoJZmxvYXQgcGFyYW1fMTIgPSAwLjM1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMik7CglyZXR1cm4gY2xhbXAocywgMC4wLCAxLjApICogMC41NTsKfQp2b2lkIEZMVF9tYWluX1MxX2MwKCkgCnsKCXVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLng7Cgl1X3NwYXJrbGVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnk7Cgl1X2JsdXJfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLno7Cgl1X3JhZGl1c19zY2FsZV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAudzsKCWZsb2F0MiBwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwoJZmxvYXQyIHV2XzEgPSBwICogdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwoJZmxvYXQyIGRlbnNpdHlfdXYgPSB1dl8xIC0gbW9kKHAsIHVfbm9pc2Vfc2NhbGVfUzFfYzApOwoJZmxvYXQgcmFkaXVzID0gdV9tYXhfcmFkaXVzX1MxX2MwICogdV9yYWRpdXNfc2NhbGVfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1dl8xOwoJZmxvYXQgdHVyYnVsZW5jZSA9IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAocGFyYW1fMTMpOwoJZmxvYXQyIHBhcmFtXzE0ID0gcDsKCWZsb2F0MiBwYXJhbV8xNSA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTYgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8xNyA9IDAuMDUgKiB1X21heF9yYWRpdXNfUzFfYzA7CglmbG9hdCBwYXJhbV8xOCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHJpbmcgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAocGFyYW1fMTQsIHBhcmFtXzE1LCBwYXJhbV8xNiwgcGFyYW1fMTcsIHBhcmFtXzE4KTsKCWZsb2F0MiBwYXJhbV8xOSA9IGRlbnNpdHlfdXY7CglmbG9hdCBwYXJhbV8yMCA9IHVfbm9pc2VfcGhhc2VfUzFfYzA7CglmbG9hdCBzcGFya2xlID0gKChGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKHBhcmFtXzE5LCBwYXJhbV8yMCkgKiByaW5nKSAqIHR1cmJ1bGVuY2UpICogdV9zcGFya2xlX2FscGhhX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzIxID0gcDsKCWZsb2F0MiBwYXJhbV8yMiA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMjMgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8yNCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHdhdmVfYWxwaGEgPSAoRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMjEsIHBhcmFtXzIyLCBwYXJhbV8yMywgcGFyYW1fMjQpICogdV9hbHBoYV9TMV9jMCkgKiB1X2NvbG9yX1MxX2MwLnc7CglmbG9hdDQgd2F2ZV9jb2xvciA9IGZsb2F0NCh1X2NvbG9yX1MxX2MwLnh5eiAqIHdhdmVfYWxwaGEsIHdhdmVfYWxwaGEpOwoJZnJhZ0NvbG9yX1MxX2MwID0gbWl4KHdhdmVfY29sb3IsIGZsb2F0NCgxLjApLCBmbG9hdDQoc3BhcmtsZSkpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwID0gZmxvYXQ0KF90bXBfMV9jb29yZHMsIDAuMCwgMC4wKTsKCUZMVF9tYWluX1MxX2MwKCk7CglyZXR1cm4gaGFsZjQoaGFsZjQoZnJhZ0NvbG9yX1MxX2MwKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","HWJQAAAAABEAADAAAIOAAAAADIIAB7X7777QGHAYAD7P7777A4QCQAAAAAAAAAQAAAAAAQQGACQAGAAAAAQAAAAAAAQQGAAA":"DAAAAExTS1OaAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGZsb2F0MiBsb2NhbENvb3JkOwppbiBmbG9hdDQgdGV4U3Vic2V0OwpvdXQgZmxvYXQyIHZsb2NhbENvb3JkX1MwOwpmbGF0IG91dCBmbG9hdDQgdnRleFN1YnNldF9TMDsKb3V0IGZsb2F0IHZjb3ZlcmFnZV9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDIgcG9zaXRpb24gPSBwb3NpdGlvbi54eTsKCXZsb2NhbENvb3JkX1MwID0gbG9jYWxDb29yZDsKCXZ0ZXhTdWJzZXRfUzAgPSB0ZXhTdWJzZXQ7Cgl2Y292ZXJhZ2VfUzAgPSBjb3ZlcmFnZTsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoAAAAAAABcAgAAc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdmxvY2FsQ29vcmRfUzA7CmZsYXQgaW4gZmxvYXQ0IHZ0ZXhTdWJzZXRfUzA7CmluIGZsb2F0IHZjb3ZlcmFnZV9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglmbG9hdDQgc3Vic2V0OwoJc3Vic2V0ID0gdnRleFN1YnNldF9TMDsKCXRleENvb3JkID0gY2xhbXAodGV4Q29vcmQsIHN1YnNldC5MVCwgc3Vic2V0LlJCKTsKCW91dHB1dENvbG9yX1MwID0gKGJsZW5kX21vZHVsYXRlKHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMCwgdGV4Q29vcmQpLCBoYWxmNCgxKSkpOwoJZmxvYXQgY292ZXJhZ2UgPSB2Y292ZXJhZ2VfUzA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGhhbGYoY292ZXJhZ2UpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAHBvc2l0aW9uCAAAAGNvdmVyYWdlCgAAAGxvY2FsQ29vcmQAAAkAAAB0ZXhTdWJzZXQAAAAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAACABZQA6AAAEAAAAAAAIADQAAAAIAAAAAAAIIDA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAACPAwAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmluIGZsb2F0NCB2aW5DaXJjbGVFZGdlX1MwOwppbiBoYWxmNCB2aW5Db2xvcl9TMDsKaGFsZjQgQ2lyY3VsYXJSUmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglmbG9hdDIgZHh5MCA9IHVpbm5lclJlY3RfUzEuTFQgLSBza19GcmFnQ29vcmQueHk7CglmbG9hdDIgZHh5MSA9IHNrX0ZyYWdDb29yZC54eSAtIHVpbm5lclJlY3RfUzEuUkI7CglmbG9hdDIgZHh5ID0gbWF4KG1heChkeHkwLCBkeHkxKSwgMC4wKTsKCWhhbGYgYWxwaGEgPSBoYWxmKHNhdHVyYXRlKHVyYWRpdXNQbHVzSGFsZl9TMS54IC0gbGVuZ3RoKGR4eSkpKTsKCWFscGhhID0gMS4wIC0gYWxwaGE7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","AYTRVAADQAAAOAEARAFQJAABBADAAAILBYAACCYUQD777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1NyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7CmluIGhhbGYzIGluQ2xpcFBsYW5lOwppbiBoYWxmMyBpbklzZWN0UGxhbmU7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGYzIHZpbkNsaXBQbGFuZV9TMDsKb3V0IGhhbGYzIHZpbklzZWN0UGxhbmVfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCXZpbkNpcmNsZUVkZ2VfUzAgPSBpbkNpcmNsZUVkZ2U7Cgl2aW5DbGlwUGxhbmVfUzAgPSBpbkNsaXBQbGFuZTsKCXZpbklzZWN0UGxhbmVfUzAgPSBpbklzZWN0UGxhbmU7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gdWxvY2FsTWF0cml4X1MwLnh6ICogaW5Qb3NpdGlvbiArIHVsb2NhbE1hdHJpeF9TMC55dzsKCXNrX1Bvc2l0aW9uID0gX3RtcF8wX2luUG9zaXRpb24ueHkwMTsKfQoAAAAAAADdAwAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGYzIHZpbkNsaXBQbGFuZV9TMDsKaW4gaGFsZjMgdmluSXNlY3RQbGFuZV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQ0IGNpcmNsZUVkZ2U7CgljaXJjbGVFZGdlID0gdmluQ2lyY2xlRWRnZV9TMDsKCWhhbGYzIGNsaXBQbGFuZTsKCWNsaXBQbGFuZSA9IHZpbkNsaXBQbGFuZV9TMDsKCWhhbGYzIGlzZWN0UGxhbmU7Cglpc2VjdFBsYW5lID0gdmluSXNlY3RQbGFuZV9TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0IGQgPSBsZW5ndGgoY2lyY2xlRWRnZS54eSk7CgloYWxmIGRpc3RhbmNlVG9PdXRlckVkZ2UgPSBoYWxmKGNpcmNsZUVkZ2UueiAqICgxLjAgLSBkKSk7CgloYWxmIGVkZ2VBbHBoYSA9IHNhdHVyYXRlKGRpc3RhbmNlVG9PdXRlckVkZ2UpOwoJaGFsZiBkaXN0YW5jZVRvSW5uZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoZCAtIGNpcmNsZUVkZ2UudykpOwoJaGFsZiBpbm5lckFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb0lubmVyRWRnZSk7CgllZGdlQWxwaGEgKj0gaW5uZXJBbHBoYTsKCWhhbGYgY2xpcCA9IGhhbGYoc2F0dXJhdGUoY2lyY2xlRWRnZS56ICogZG90KGNpcmNsZUVkZ2UueHksIGNsaXBQbGFuZS54eSkgKyBjbGlwUGxhbmUueikpOwoJY2xpcCAqPSBoYWxmKHNhdHVyYXRlKGNpcmNsZUVkZ2UueiAqIGRvdChjaXJjbGVFZGdlLnh5LCBpc2VjdFBsYW5lLnh5KSArIGlzZWN0UGxhbmUueikpOwoJZWRnZUFscGhhICo9IGNsaXA7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAoAAABpblBvc2l0aW9uAAAHAAAAaW5Db2xvcgAMAAAAaW5DaXJjbGVFZGdlCwAAAGluQ2xpcFBsYW5lAAwAAABpbklzZWN0UGxhbmUAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAADqAQAAaW4gZmxvYXQ0IHZpbkNpcmNsZUVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgQ2lyY2xlR2VvbWV0cnlQcm9jZXNzb3IKCWZsb2F0NCBjaXJjbGVFZGdlOwoJY2lyY2xlRWRnZSA9IHZpbkNpcmNsZUVkZ2VfUzA7CgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmluQ29sb3JfUzA7CglmbG9hdCBkID0gbGVuZ3RoKGNpcmNsZUVkZ2UueHkpOwoJaGFsZiBkaXN0YW5jZVRvT3V0ZXJFZGdlID0gaGFsZihjaXJjbGVFZGdlLnogKiAoMS4wIC0gZCkpOwoJaGFsZiBlZGdlQWxwaGEgPSBzYXR1cmF0ZShkaXN0YW5jZVRvT3V0ZXJFZGdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoZWRnZUFscGhhKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADAAAAGluQ2lyY2xlRWRnZQAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQBAEAQAAAAGQCBAMQACAIAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAEUDAAB1bmlmb3JtIGZsb2F0NCB1Y2xhbXBfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MxOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZCA9IGNsYW1wKHN1YnNldENvb3JkLCB1Y2xhbXBfUzFfYzAueHksIHVjbGFtcF9TMV9jMC56dyk7CgloYWxmNCB0ZXh0dXJlQ29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIGNsYW1wZWRDb29yZCk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHQBNGZODK5YIAAAAAAQAAAAAIADCNS4GV3QYBAAAAAAAAAAAIAA6IAMAAACAAAAAABUABAAAAAEAAAAAIBEABAA":"DAAAAExTS1N0AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAQAAABsdAABmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb2xvcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDQgdV9jb21wb3NpdGVfMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jZW50ZXJfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9tYXhfcmFkaXVzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3Jlc29sdXRpb25fc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfbm9pc2Vfc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQgdV9ub2lzZV9waGFzZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUxX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlM19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24yX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uM19TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwp1bmlmb3JtIGZsb2F0NCB1aW5uZXJSZWN0X1MyOwp1bmlmb3JtIGhhbGYyIHVyYWRpdXNQbHVzSGFsZl9TMjsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KTsKdm9pZCBGTFRfbWFpbl9TMV9jMCgpOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpIAp7CglyZXR1cm4gZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAueHk7Cn0KZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCkgCnsKCXJldHVybiBmbG9hdDJ4MihyYWQueCwgLXJhZC55LCByYWQueSwgcmFkLngpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpIAp7CglmbG9hdCBibHVyX2hhbGYgPSBibHVyICogMC41OwoJZmxvYXQgZCA9IGRpc3RhbmNlKHV2LCB4eSk7CglyZXR1cm4gMS4wIC0gc21vb3Roc3RlcCgxLjAgLSBibHVyX2hhbGYsIDEuMCArIGJsdXJfaGFsZiwgZCAvIHJhZGl1cyk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcikgCnsKCWZsb2F0MiBwYXJhbSA9IHJvdGF0aW9uOwoJcCA9IEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKHBhcmFtKSAqICh4eSAtIHApICsgeHk7CglwID0gbW9kKHAsIGZsb2F0MihjZWxsX2RpYW1ldGVyKSkgLyByZXNvbHV0aW9uOwoJZmxvYXQgY2VsbF91diA9IChjZWxsX2RpYW1ldGVyIC8gcmVzb2x1dGlvbi55KSAqIDAuNTsKCWZsb2F0IHIgPSAwLjY1ICogY2VsbF91djsKCWZsb2F0MiBwYXJhbV8xID0gcDsKCWZsb2F0MiBwYXJhbV8yID0gZmxvYXQyKGNlbGxfdXYpOwoJZmxvYXQgcGFyYW1fMyA9IHI7CglmbG9hdCBwYXJhbV80ID0gciAqIDUwLjA7CglyZXR1cm4gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpIAp7CglmbG9hdDIgdXZfc2NhbGUgPSB1diAqIGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzIgPSB1X2NpcmNsZTFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMyA9IHVfcm90YXRpb24xX1MxX2MwOwoJZmxvYXQgcGFyYW1fNCA9IDAuMTc7CglmbG9hdCBfMzE5ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwoJZmxvYXQgZzEgPSBfMzE5OwoJZmxvYXQyIHBhcmFtXzUgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV82ID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fNyA9IHVfY2lyY2xlMl9TMV9jMDsKCWZsb2F0MiBwYXJhbV84ID0gdV9yb3RhdGlvbjJfUzFfYzA7CglmbG9hdCBwYXJhbV85ID0gMC4yOwoJZmxvYXQgXzMzMSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgZzIgPSBfMzMxOwoJZmxvYXQyIHBhcmFtXzEwID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMTEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8xMiA9IHVfY2lyY2xlM19TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHVfcm90YXRpb24zX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTQgPSAwLjI3NTsKCWZsb2F0IF8zNDQgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyLCBwYXJhbV8xMywgcGFyYW1fMTQpOwoJZmxvYXQgZzMgPSBfMzQ0OwoJZmxvYXQgdiA9ICgoZzEgKiBnMSArIGcyKSAtIGczKSAqIDAuNTsKCXJldHVybiBjbGFtcCgwLjQ1ICsgMC44ICogdiwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0MiBwYXJhbV8xID0geHk7CglmbG9hdCBwYXJhbV8yID0gcmFkaXVzICsgdGhpY2tuZXNzOwoJZmxvYXQgcGFyYW1fMyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfb3V0ZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdDIgcGFyYW1fNCA9IHV2OwoJZmxvYXQyIHBhcmFtXzUgPSB4eTsKCWZsb2F0IHBhcmFtXzYgPSBtYXgocmFkaXVzIC0gdGhpY2tuZXNzLCAwLjApOwoJZmxvYXQgcGFyYW1fNyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfaW5uZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83KTsKCXJldHVybiBjbGFtcChjaXJjbGVfb3V0ZXIgLSBjaXJjbGVfaW5uZXIsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbikgCnsKCW4gPSBmcmFjdChuICogZmxvYXQyKDUuMzk4NywgNS40NDIxKSk7CgluICs9IGZsb2F0Mihkb3Qobi55eCwgbiArIGZsb2F0MigyMS41MzUxLCAxNC4zMTM3KSkpOwoJZmxvYXQgeHkgPSBuLnggKiBuLnk7CglyZXR1cm4gKGZyYWN0KHh5ICogOTUuNDMwNykgKyBmcmFjdCh4eSAqIDc1LjA0OTYxKSkgLSAxLjA7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpIAp7CglyZXR1cm4gc3RlcChsLCB2KSAqICgxLjAgLSBzdGVwKGgsIHYpKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCkgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQgXzI0MiA9IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKHBhcmFtKTsKCWZsb2F0IG4gPSBfMjQyOwoJZmxvYXQgcGFyYW1fMSA9IG47CglmbG9hdCBwYXJhbV8yID0gMC4wOwoJZmxvYXQgcGFyYW1fMyA9IDAuMDU7CglmbG9hdCBzID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQgcGFyYW1fNCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC4zNSkpOwoJZmxvYXQgcGFyYW1fNSA9IDAuMTsKCWZsb2F0IHBhcmFtXzYgPSAwLjE1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNik7CglmbG9hdCBwYXJhbV83ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjcpKTsKCWZsb2F0IHBhcmFtXzggPSAwLjI7CglmbG9hdCBwYXJhbV85ID0gMC4yNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgcGFyYW1fMTAgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDEuMDUpKTsKCWZsb2F0IHBhcmFtXzExID0gMC4zOwoJZmxvYXQgcGFyYW1fMTIgPSAwLjM1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMik7CglyZXR1cm4gY2xhbXAocywgMC4wLCAxLjApICogMC41NTsKfQp2b2lkIEZMVF9tYWluX1MxX2MwKCkgCnsKCXVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLng7Cgl1X3NwYXJrbGVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnk7Cgl1X2JsdXJfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLno7Cgl1X3JhZGl1c19zY2FsZV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAudzsKCWZsb2F0MiBwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwoJZmxvYXQyIHV2XzEgPSBwICogdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwoJZmxvYXQyIGRlbnNpdHlfdXYgPSB1dl8xIC0gbW9kKHAsIHVfbm9pc2Vfc2NhbGVfUzFfYzApOwoJZmxvYXQgcmFkaXVzID0gdV9tYXhfcmFkaXVzX1MxX2MwICogdV9yYWRpdXNfc2NhbGVfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1dl8xOwoJZmxvYXQgdHVyYnVsZW5jZSA9IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAocGFyYW1fMTMpOwoJZmxvYXQyIHBhcmFtXzE0ID0gcDsKCWZsb2F0MiBwYXJhbV8xNSA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTYgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8xNyA9IDAuMDUgKiB1X21heF9yYWRpdXNfUzFfYzA7CglmbG9hdCBwYXJhbV8xOCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHJpbmcgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAocGFyYW1fMTQsIHBhcmFtXzE1LCBwYXJhbV8xNiwgcGFyYW1fMTcsIHBhcmFtXzE4KTsKCWZsb2F0MiBwYXJhbV8xOSA9IGRlbnNpdHlfdXY7CglmbG9hdCBwYXJhbV8yMCA9IHVfbm9pc2VfcGhhc2VfUzFfYzA7CglmbG9hdCBzcGFya2xlID0gKChGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKHBhcmFtXzE5LCBwYXJhbV8yMCkgKiByaW5nKSAqIHR1cmJ1bGVuY2UpICogdV9zcGFya2xlX2FscGhhX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzIxID0gcDsKCWZsb2F0MiBwYXJhbV8yMiA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMjMgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8yNCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHdhdmVfYWxwaGEgPSAoRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMjEsIHBhcmFtXzIyLCBwYXJhbV8yMywgcGFyYW1fMjQpICogdV9hbHBoYV9TMV9jMCkgKiB1X2NvbG9yX1MxX2MwLnc7CglmbG9hdDQgd2F2ZV9jb2xvciA9IGZsb2F0NCh1X2NvbG9yX1MxX2MwLnh5eiAqIHdhdmVfYWxwaGEsIHdhdmVfYWxwaGEpOwoJZnJhZ0NvbG9yX1MxX2MwID0gbWl4KHdhdmVfY29sb3IsIGZsb2F0NCgxLjApLCBmbG9hdDQoc3BhcmtsZSkpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwID0gZmxvYXQ0KF90bXBfMV9jb29yZHMsIDAuMCwgMC4wKTsKCUZMVF9tYWluX1MxX2MwKCk7CglyZXR1cm4gaGFsZjQoaGFsZjQoZnJhZ0NvbG9yX1MxX2MwKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzIoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MyLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzIueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJaGFsZjQgb3V0cHV0X1MyOwoJb3V0cHV0X1MyID0gQ2lyY3VsYXJSUmVjdF9TMihvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0X1MxICogb3V0cHV0X1MyOwoJfQp9CgABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFIBVWKMG7QAAAAAAAEAACAAAAAVREAAQAAAAAQCDAAQQGAABAAAAAAAAHIAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1N6AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAQgQAAHVuaWZvcm0gZmxvYXQ0IHVjbGFtcF9TMV9jMF9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZC54ID0gY2xhbXAoc3Vic2V0Q29vcmQueCwgdWNsYW1wX1MxX2MwX2MwLngsIHVjbGFtcF9TMV9jMF9jMC56KTsKCWNsYW1wZWRDb29yZC55ID0gc3Vic2V0Q29vcmQueTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBNYXRyaXhFZmZlY3RfUzFfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","EADQAAAAAEAAAAAUAABQAAQPAAABCFYMAAKAUEAAAAAAAAABAAAAAAAAAAANAAIAAAABAAAAACAJAAIAAAAA":"DAAAAExTS1NyAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQyIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7CmluIGZsb2F0MyBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiB1c2hvcnQyIGluVGV4dHVyZUNvb3JkczsKb3V0IGZsb2F0MiB2VGV4dHVyZUNvb3Jkc19TMDsKZmxhdCBvdXQgZmxvYXQgdlRleEluZGV4X1MwOwpvdXQgZmxvYXQyIHZJbnRUZXh0dXJlQ29vcmRzX1MwOwpvdXQgaGFsZjQgdmluQ29sb3JfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIERpc3RhbmNlRmllbGRQYXRoCglpbnQgdGV4SWR4ID0gMDsKCWZsb2F0MiB1bm9ybVRleENvb3JkcyA9IGZsb2F0MihpblRleHR1cmVDb29yZHMueCwgaW5UZXh0dXJlQ29vcmRzLnkpOwoJdlRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkcyAqIHVBdGxhc0RpbWVuc2lvbnNJbnZfUzA7Cgl2VGV4SW5kZXhfUzAgPSBmbG9hdCh0ZXhJZHgpOwoJdkludFRleHR1cmVDb29yZHNfUzAgPSB1bm9ybVRleENvb3JkczsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MyBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IGluUG9zaXRpb24ueHkwejsKfQoAAAAAAACXAgAAc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBmbG9hdDIgdlRleHR1cmVDb29yZHNfUzA7CmZsYXQgaW4gZmxvYXQgdlRleEluZGV4X1MwOwppbiBmbG9hdDIgdkludFRleHR1cmVDb29yZHNfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGlzdGFuY2VGaWVsZFBhdGgKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWZsb2F0MiB1diA9IHZUZXh0dXJlQ29vcmRzX1MwOwoJaGFsZjQgdGV4Q29sb3I7Cgl7CgkJdGV4Q29sb3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHV2KS5ycnJyOwoJfQoJaGFsZiBkaXN0YW5jZSA9IDcuOTY4NzUqKHRleENvbG9yLnIgLSAwLjUwMTk2MDc4NDMxKTsKCWhhbGYgYWZ3aWR0aDsKCWFmd2lkdGggPSBhYnMoMC42NSpoYWxmKGRGZHgodkludFRleHR1cmVDb29yZHNfUzAueCkpKTsKCWhhbGYgdmFsID0gc21vb3Roc3RlcCgtYWZ3aWR0aCwgYWZ3aWR0aCwgZGlzdGFuY2UpOwoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCh2YWwpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAA8AAABpblRleHR1cmVDb29yZHMAAAAAAA==","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHQBNGZODK5YIAAAAAAQAAAAAIADCNS4GV3QYBAAAAAAAAAAAIAALIAAAAAUDLMERKGAAAAAMAAAAAIAAAAAAAIBDNIWUYZAUAAAAAAYAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1N0AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfMl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAQAAAJAeAABmbG9hdDQgZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzA7CmZsb2F0NCBmcmFnQ29sb3JfUzFfYzA7CmZsb2F0IHVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9ibHVyX1MxX2MwOwpmbG9hdCB1X3JhZGl1c19zY2FsZV9TMV9jMDsKY29uc3QgaW50IGtGaWxsQUFfUzJfYzAgPSAxOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQldfUzJfYzAgPSAyOwpjb25zdCBpbnQga0ludmVyc2VGaWxsQUFfUzJfYzAgPSAzOwp1bmlmb3JtIGZsb2F0NCB1X2NvbG9yX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbXBvc2l0ZV8xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NlbnRlcl9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X21heF9yYWRpdXNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9ub2lzZV9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X25vaXNlX3BoYXNlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24zX1MxX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gZmxvYXQ0IHVjaXJjbGVfUzJfYzA7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCk7CnZvaWQgRkxUX21haW5fUzFfYzAoKTsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKSAKewoJcmV0dXJuIGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwLnh5Owp9CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpIAp7CglyZXR1cm4gZmxvYXQyeDIocmFkLngsIC1yYWQueSwgcmFkLnksIHJhZC54KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQgYmx1cl9oYWxmID0gYmx1ciAqIDAuNTsKCWZsb2F0IGQgPSBkaXN0YW5jZSh1diwgeHkpOwoJcmV0dXJuIDEuMCAtIHNtb290aHN0ZXAoMS4wIC0gYmx1cl9oYWxmLCAxLjAgKyBibHVyX2hhbGYsIGQgLyByYWRpdXMpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpIAp7CglmbG9hdDIgcGFyYW0gPSByb3RhdGlvbjsKCXAgPSBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChwYXJhbSkgKiAoeHkgLSBwKSArIHh5OwoJcCA9IG1vZChwLCBmbG9hdDIoY2VsbF9kaWFtZXRlcikpIC8gcmVzb2x1dGlvbjsKCWZsb2F0IGNlbGxfdXYgPSAoY2VsbF9kaWFtZXRlciAvIHJlc29sdXRpb24ueSkgKiAwLjU7CglmbG9hdCByID0gMC42NSAqIGNlbGxfdXY7CglmbG9hdDIgcGFyYW1fMSA9IHA7CglmbG9hdDIgcGFyYW1fMiA9IGZsb2F0MihjZWxsX3V2KTsKCWZsb2F0IHBhcmFtXzMgPSByOwoJZmxvYXQgcGFyYW1fNCA9IHIgKiA1MC4wOwoJcmV0dXJuIEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KSAKewoJZmxvYXQyIHV2X3NjYWxlID0gdXYgKiBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8yID0gdV9jaXJjbGUxX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzMgPSB1X3JvdGF0aW9uMV9TMV9jMDsKCWZsb2F0IHBhcmFtXzQgPSAwLjE3OwoJZmxvYXQgXzMxOSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKCWZsb2F0IGcxID0gXzMxOTsKCWZsb2F0MiBwYXJhbV81ID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fNiA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzcgPSB1X2NpcmNsZTJfUzFfYzA7CglmbG9hdDIgcGFyYW1fOCA9IHVfcm90YXRpb24yX1MxX2MwOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjsKCWZsb2F0IF8zMzEgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IGcyID0gXzMzMTsKCWZsb2F0MiBwYXJhbV8xMCA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzExID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMTIgPSB1X2NpcmNsZTNfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1X3JvdGF0aW9uM19TMV9jMDsKCWZsb2F0IHBhcmFtXzE0ID0gMC4yNzU7CglmbG9hdCBfMzQ0ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMiwgcGFyYW1fMTMsIHBhcmFtXzE0KTsKCWZsb2F0IGczID0gXzM0NDsKCWZsb2F0IHYgPSAoKGcxICogZzEgKyBnMikgLSBnMykgKiAwLjU7CglyZXR1cm4gY2xhbXAoMC40NSArIDAuOCAqIHYsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdDIgcGFyYW1fMSA9IHh5OwoJZmxvYXQgcGFyYW1fMiA9IHJhZGl1cyArIHRoaWNrbmVzczsKCWZsb2F0IHBhcmFtXzMgPSBibHVyOwoJZmxvYXQgY2lyY2xlX291dGVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQyIHBhcmFtXzQgPSB1djsKCWZsb2F0MiBwYXJhbV81ID0geHk7CglmbG9hdCBwYXJhbV82ID0gbWF4KHJhZGl1cyAtIHRoaWNrbmVzcywgMC4wKTsKCWZsb2F0IHBhcmFtXzcgPSBibHVyOwoJZmxvYXQgY2lyY2xlX2lubmVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNyk7CglyZXR1cm4gY2xhbXAoY2lyY2xlX291dGVyIC0gY2lyY2xlX2lubmVyLCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pIAp7CgluID0gZnJhY3QobiAqIGZsb2F0Mig1LjM5ODcsIDUuNDQyMSkpOwoJbiArPSBmbG9hdDIoZG90KG4ueXgsIG4gKyBmbG9hdDIoMjEuNTM1MSwgMTQuMzEzNykpKTsKCWZsb2F0IHh5ID0gbi54ICogbi55OwoJcmV0dXJuIChmcmFjdCh4eSAqIDk1LjQzMDcpICsgZnJhY3QoeHkgKiA3NS4wNDk2MSkpIC0gMS4wOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKSAKewoJcmV0dXJuIHN0ZXAobCwgdikgKiAoMS4wIC0gc3RlcChoLCB2KSk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0IF8yNDIgPSBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChwYXJhbSk7CglmbG9hdCBuID0gXzI0MjsKCWZsb2F0IHBhcmFtXzEgPSBuOwoJZmxvYXQgcGFyYW1fMiA9IDAuMDsKCWZsb2F0IHBhcmFtXzMgPSAwLjA1OwoJZmxvYXQgcyA9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0IHBhcmFtXzQgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuMzUpKTsKCWZsb2F0IHBhcmFtXzUgPSAwLjE7CglmbG9hdCBwYXJhbV82ID0gMC4xNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYpOwoJZmxvYXQgcGFyYW1fNyA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC43KSk7CglmbG9hdCBwYXJhbV84ID0gMC4yOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IHBhcmFtXzEwID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAxLjA1KSk7CglmbG9hdCBwYXJhbV8xMSA9IDAuMzsKCWZsb2F0IHBhcmFtXzEyID0gMC4zNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIpOwoJcmV0dXJuIGNsYW1wKHMsIDAuMCwgMS4wKSAqIDAuNTU7Cn0Kdm9pZCBGTFRfbWFpbl9TMV9jMCgpIAp7Cgl1X2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC54OwoJdV9zcGFya2xlX2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC55OwoJdV9ibHVyX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC56OwoJdV9yYWRpdXNfc2NhbGVfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnc7CglmbG9hdDIgcCA9IEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKCWZsb2F0MiB1dl8xID0gcCAqIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKCWZsb2F0MiBkZW5zaXR5X3V2ID0gdXZfMSAtIG1vZChwLCB1X25vaXNlX3NjYWxlX1MxX2MwKTsKCWZsb2F0IHJhZGl1cyA9IHVfbWF4X3JhZGl1c19TMV9jMCAqIHVfcmFkaXVzX3NjYWxlX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdXZfMTsKCWZsb2F0IHR1cmJ1bGVuY2UgPSBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKHBhcmFtXzEzKTsKCWZsb2F0MiBwYXJhbV8xNCA9IHA7CglmbG9hdDIgcGFyYW1fMTUgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzE2ID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMTcgPSAwLjA1ICogdV9tYXhfcmFkaXVzX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTggPSB1X2JsdXJfUzFfYzA7CglmbG9hdCByaW5nID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKHBhcmFtXzE0LCBwYXJhbV8xNSwgcGFyYW1fMTYsIHBhcmFtXzE3LCBwYXJhbV8xOCk7CglmbG9hdDIgcGFyYW1fMTkgPSBkZW5zaXR5X3V2OwoJZmxvYXQgcGFyYW1fMjAgPSB1X25vaXNlX3BoYXNlX1MxX2MwOwoJZmxvYXQgc3BhcmtsZSA9ICgoRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChwYXJhbV8xOSwgcGFyYW1fMjApICogcmluZykgKiB0dXJidWxlbmNlKSAqIHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8yMSA9IHA7CglmbG9hdDIgcGFyYW1fMjIgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzIzID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMjQgPSB1X2JsdXJfUzFfYzA7CglmbG9hdCB3YXZlX2FscGhhID0gKEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzIxLCBwYXJhbV8yMiwgcGFyYW1fMjMsIHBhcmFtXzI0KSAqIHVfYWxwaGFfUzFfYzApICogdV9jb2xvcl9TMV9jMC53OwoJZmxvYXQ0IHdhdmVfY29sb3IgPSBmbG9hdDQodV9jb2xvcl9TMV9jMC54eXogKiB3YXZlX2FscGhhLCB3YXZlX2FscGhhKTsKCWZyYWdDb2xvcl9TMV9jMCA9IG1peCh3YXZlX2NvbG9yLCBmbG9hdDQoMS4wKSwgZmxvYXQ0KHNwYXJrbGUpKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CglmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMCA9IGZsb2F0NChfdG1wXzFfY29vcmRzLCAwLjAsIDAuMCk7CglGTFRfbWFpbl9TMV9jMCgpOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGZyYWdDb2xvcl9TMV9jMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDaXJjbGVfUzJfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8yX2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmIGQ7CglpZiAoaW50KDEpID09IGtJbnZlcnNlRmlsbEJXX1MyX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMCkgCgl7CgkJZCA9IGhhbGYoKGxlbmd0aCgodWNpcmNsZV9TMl9jMC54eSAtIHNrX0ZyYWdDb29yZC54eSkgKiB1Y2lyY2xlX1MyX2MwLncpIC0gMS4wKSAqIHVjaXJjbGVfUzJfYzAueik7Cgl9CgllbHNlIAoJewoJCWQgPSBoYWxmKCgxLjAgLSBsZW5ndGgoKHVjaXJjbGVfUzJfYzAueHkgLSBza19GcmFnQ29vcmQueHkpICogdWNpcmNsZV9TMl9jMC53KSkgKiB1Y2lyY2xlX1MyX2MwLnopOwoJfQoJcmV0dXJuIGhhbGY0KGhhbGY0KGludCgxKSA9PSBrRmlsbEFBX1MyX2MwIHx8IGludCgxKSA9PSBrSW52ZXJzZUZpbGxBQV9TMl9jMCA/IHNhdHVyYXRlKGQpIDogaGFsZihkID4gMC41KSkpOwp9CmhhbGY0IEJsZW5kX1MyKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfbW9kdWxhdGUoX3NyYywgQ2lyY2xlX1MyX2MwKF9zcmMpKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoBAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","HUJAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAQAAAAAAQQGAAZAABQAAAAAAAACAAAAADYCAAIAAAAAWBRAAAABAAAAANAEIQCAAAAAAAAAAAAAUABQAAAAEAAAAAAAEEBQAAAA":"DAAAAExTS1N5AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2bG9jYWxDb29yZF9TMCA9IGxvY2FsQ29vcmQ7Cglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzRfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIHBvc2l0aW9uLnh5MTsKCX0KfQoAAAAAAAAAzAMAAHVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2bG9jYWxDb29yZF9TMDsKaW4gZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwOwpoYWxmNCBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzEsIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwKS4wMDByOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IERldmljZVNwYWNlX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBCbGVuZF9TMShoYWxmNCBfc3JjLCBoYWxmNCBfZHN0KSAKewoJcmV0dXJuIGJsZW5kX2RzdF9pbihEZXZpY2VTcGFjZV9TMV9jMChfc3JjKSwgX3NyYyk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWZsb2F0MiB0ZXhDb29yZDsKCXRleENvb3JkID0gdmxvY2FsQ29vcmRfUzA7CglvdXRwdXRDb2xvcl9TMCA9IChibGVuZF9tb2R1bGF0ZShzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHRleENvb3JkKSwgaGFsZjQoMSkpKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBCbGVuZF9TMShvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0X1MxOwoJfQp9CgAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uCgAAAGxvY2FsQ29vcmQAAAAAAAA=","GEMAAAYAAEHAAAARC4EAAAQWBQAAAAAAAAAQAAAAIBCAAAGQAEAAAAAQAAAABAEQAEAAAAA":"DAAAAExTS1NUAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBoYWxmMyBpblNoYWRvd1BhcmFtczsKb3V0IGhhbGYzIHZpblNoYWRvd1BhcmFtc19TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBSUmVjdFNoYWRvdwoJdmluU2hhZG93UGFyYW1zX1MwID0gaW5TaGFkb3dQYXJhbXM7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAADAgAAc2FtcGxlcjJEIHVUZXh0dXJlU2FtcGxlcl8wX1MwOwppbiBoYWxmMyB2aW5TaGFkb3dQYXJhbXNfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUlJlY3RTaGFkb3cKCWhhbGYzIHNoYWRvd1BhcmFtczsKCXNoYWRvd1BhcmFtcyA9IHZpblNoYWRvd1BhcmFtc19TMDsKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGYgZCA9IGxlbmd0aChzaGFkb3dQYXJhbXMueHkpOwoJZmxvYXQyIHV2ID0gZmxvYXQyKHNoYWRvd1BhcmFtcy56ICogKDEuMCAtIGQpLCAwLjUpOwoJaGFsZiBmYWN0b3IgPSBzYW1wbGUodVRleHR1cmVTYW1wbGVyXzBfUzAsIHV2KS4wMDByLmE7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGZhY3Rvcik7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IADgAAAGluU2hhZG93UGFyYW1zAAAAAAAA","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFIBUAFS7EOCAAAAAGZJY26AAQAAAA2S5KXK4QAAAAAMAAAAAAAAQAABQM74NUDECAAAAAQ5UOAMQAAAAAAAAAAEAAAAAZ3FHDLYADAAAABKDVK5LSCAIAABQAAAAAAACAAAGAT3RWSMYIAAAAADWRYBSQAAAAAAAQAAAAGJJOXLVOIIBAAAGAAAAAAAAIAAAIAPOH2NTBAAAAAAOKFAOLAAAAAAAEAAAAAMQQAIAAAYGP6G2BSBAAACAAAAAAAAM5S4Z4NUDACAAAAAAAAADAIUOKFAOLAAAAAAACAAAAAZGIEENLQDAAAAAAAAAAABQAKAIAAIAAAADIBCEAQAAAAAAAAAAAIADQAAAAIAAAAAAAIIDA":"DAAAAExTS1OGAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMF9jMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfN19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfN19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzBfYzEpICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAABAAAAIQoAAHVuaWZvcm0gaGFsZjQgdXN0YXJ0X1MxX2MwX2MwX2MwX2MwOwp1bmlmb3JtIGhhbGY0IHVlbmRfUzFfYzBfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMF9jMTsKdW5pZm9ybSBoYWxmNCB1bGVmdEJvcmRlckNvbG9yX1MxX2MwX2MwOwp1bmlmb3JtIGhhbGY0IHVyaWdodEJvcmRlckNvbG9yX1MxX2MwX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzE7CnVuaWZvcm0gaGFsZiB1cmFuZ2VfUzE7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfN19TMDsKaGFsZjQgU2luZ2xlSW50ZXJ2YWxDb2xvcml6ZXJfUzFfYzBfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSBfY29vcmRzOwoJcmV0dXJuIGhhbGY0KG1peCh1c3RhcnRfUzFfYzBfYzBfYzBfYzAsIHVlbmRfUzFfYzBfYzBfYzBfYzAsIGhhbGYoX3RtcF8xX2Nvb3Jkcy54KSkpOwp9CmhhbGY0IGNvbG9yX3hmb3JtX1MxX2MwX2MwX2MwKGZsb2F0NCBjb2xvcikgCnsKCWNvbG9yLnJnYiAqPSBjb2xvci5hOwoJcmV0dXJuIGhhbGY0KGNvbG9yKTsKfQpoYWxmNCBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzBfYzAoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBjb2xvcl94Zm9ybV9TMV9jMF9jMF9jMChTaW5nbGVJbnRlcnZhbENvbG9yaXplcl9TMV9jMF9jMF9jMF9jMChfaW5wdXQsIF9jb29yZHMpKTsKfQpoYWxmNCBMaW5lYXJMYXlvdXRfUzFfYzBfYzBfYzFfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF8yX2luQ29sb3IgPSBfaW5wdXQ7CglmbG9hdDIgX3RtcF8zX2Nvb3JkcyA9IHZUcmFuc2Zvcm1lZENvb3Jkc183X1MwOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGhhbGYoX3RtcF8zX2Nvb3Jkcy54KSArIDFlLTA1LCAxLjAsIDAuMCwgMC4wKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwX2MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBMaW5lYXJMYXlvdXRfUzFfYzBfYzBfYzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDbGFtcGVkR3JhZGllbnRfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJaGFsZjQgX3RtcF80X2luQ29sb3IgPSBfaW5wdXQ7CgloYWxmNCB0ID0gTWF0cml4RWZmZWN0X1MxX2MwX2MwX2MxKF90bXBfNF9pbkNvbG9yKTsKCWhhbGY0IG91dENvbG9yOwoJaWYgKCFib29sKGludCgxKSkgJiYgdC55IDwgMC4wKSAKCXsKCQlvdXRDb2xvciA9IGhhbGY0KDAuMCk7Cgl9CgllbHNlIGlmICh0LnggPCAwLjApIAoJewoJCW91dENvbG9yID0gdWxlZnRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKCX0KCWVsc2UgaWYgKHQueCA+IDEuMCkgCgl7CgkJb3V0Q29sb3IgPSB1cmlnaHRCb3JkZXJDb2xvcl9TMV9jMF9jMDsKCX0KCWVsc2UgCgl7CgkJb3V0Q29sb3IgPSBDb2xvclNwYWNlWGZvcm1fUzFfYzBfYzBfYzAoX3RtcF80X2luQ29sb3IsIGZsb2F0MihoYWxmMih0LngsIDAuMCkpKTsKCX0KCXJldHVybiBoYWxmNChvdXRDb2xvcik7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBDbGFtcGVkR3JhZGllbnRfUzFfYzBfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfNV9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0KaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMV9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgX2Nvb3JkcykuMDAwcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzEoaGFsZjQgX2lucHV0LCBmbG9hdDIgX2Nvb3JkcykgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MxX2MwKF9pbnB1dCwgZmxvYXQzeDIodW1hdHJpeF9TMV9jMSkgKiBfY29vcmRzLnh5MSk7Cn0KaGFsZjQgRGl0aGVyX1MxKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfNl9pbkNvbG9yID0gX2lucHV0OwoJaGFsZjQgY29sb3IgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxX2MwKF90bXBfNl9pbkNvbG9yKTsKCWhhbGYgdmFsdWUgPSBNYXRyaXhFZmZlY3RfUzFfYzEoX3RtcF82X2luQ29sb3IsIHNrX0ZyYWdDb29yZC54eSkudyAtIDAuNTsKCXJldHVybiBoYWxmNChoYWxmNChjbGFtcChjb2xvci54eXogKyB2YWx1ZSAqIHVyYW5nZV9TMSwgMC4wLCBjb2xvci53KSwgY29sb3IudykpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gRGl0aGVyX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","BYIBQAAABQAAIAABBYAAAEIXBAAP777777777777AAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1M+AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwpvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gaW5Db2xvcjsKCXZjb2xvcl9TMCA9IGNvbG9yOwoJZmxvYXQyIF90bXBfMV9pblBvc2l0aW9uID0gaW5Qb3NpdGlvbjsKCWZsb2F0MiBfdG1wXzNfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247Cglza19Qb3NpdGlvbiA9IF90bXBfMV9pblBvc2l0aW9uLnh5MDE7Cn0KAAAAAAAABgEAAGluIGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIERlZmF1bHRHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAAAAAA=","B2ABSAAABQAAIAABBYAAB7777777777774ABICAAAAAAAAAAAAAABUABAAAAAEAAAAAIBEABAAAAA":"DAAAAExTS1N4AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gaGFsZjQgdUNvbG9yX1MwOwppbiBmbG9hdDIgaW5Qb3NpdGlvbjsKaW4gaGFsZiBpbkNvdmVyYWdlOwpvdXQgaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBEZWZhdWx0R2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IGNvbG9yID0gdUNvbG9yX1MwOwoJY29sb3IgPSBjb2xvciAqIGluQ292ZXJhZ2U7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCWZsb2F0MiBfdG1wXzFfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8zX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzFfaW5Qb3NpdGlvbi54eTAxOwp9CgAAAAAGAQAAaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgRGVmYXVsdEdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAKAAAAaW5Qb3NpdGlvbgAACgAAAGluQ292ZXJhZ2UAAAAAAAA=","HWQACAAAABAAADAAAIOAAAAADIIAAIRODAAP577774DSAIAA737777YBAAAAAAAAAAAHEADZAAAAAAIAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1ONAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gZmxvYXQgY292ZXJhZ2U7CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDQgZ2VvbVN1YnNldDsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQgdmNvdmVyYWdlX1MwOwpmbGF0IG91dCBmbG9hdDQgdmdlb21TdWJzZXRfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJdmdlb21TdWJzZXRfUzAgPSBnZW9tU3Vic2V0OwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwp9CgAAAAEAAAAIBAAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdCB2Y292ZXJhZ2VfUzA7CmZsYXQgaW4gZmxvYXQ0IHZnZW9tU3Vic2V0X1MwOwpoYWxmNCBDaXJjdWxhclJSZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMS5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMS5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MxLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWZsb2F0NCBnZW9TdWJzZXQ7CglnZW9TdWJzZXQgPSB2Z2VvbVN1YnNldF9TMDsKCWhhbGY0IGRpc3RzNCA9IGNsYW1wKGhhbGY0KDEsIDEsIC0xLCAtMSkgKiBoYWxmNChza19GcmFnQ29vcmQueHl4eSAtIGdlb1N1YnNldCksIDAsIDEpOwoJaGFsZjIgZGlzdHMyID0gZGlzdHM0Lnh5ICsgZGlzdHM0Lnp3IC0gMTsKCWhhbGYgc3Vic2V0Q292ZXJhZ2UgPSBkaXN0czIueCAqIGRpc3RzMi55OwoJY292ZXJhZ2UgPSBtaW4oY292ZXJhZ2UsIHN1YnNldENvdmVyYWdlKTsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQ2lyY3VsYXJSUmVjdF9TMShvdXRwdXRDb3ZlcmFnZV9TMCk7Cgl7CgkJLy8gWGZlciBQcm9jZXNzb3I6IFBvcnRlciBEdWZmCgkJc2tfRnJhZ0NvbG9yID0gb3V0cHV0Q29sb3JfUzAgKiBvdXRwdXRfUzE7Cgl9Cn0KAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAcG9zaXRpb24IAAAAY292ZXJhZ2UFAAAAY29sb3IAAAAKAAAAZ2VvbVN1YnNldAAAAAAAAA==","HTQAAGAABBYAAAEIXBAAAGEAMAAAAAAAAAAAAAAAQAHAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1M/AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBpblBvc2l0aW9uOwppbiBoYWxmNCBpbkNvbG9yOwppbiBmbG9hdDQgaW5RdWFkRWRnZTsKb3V0IGZsb2F0NCB2UXVhZEVkZ2VfUzA7Cm91dCBoYWxmNCB2aW5Db2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZEVkZ2UKCXZRdWFkRWRnZV9TMCA9IGluUXVhZEVkZ2U7Cgl2aW5Db2xvcl9TMCA9IGluQ29sb3I7CglmbG9hdDIgX3RtcF8wX2luUG9zaXRpb24gPSBpblBvc2l0aW9uOwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgABAAAABQMAAGluIGZsb2F0NCB2UXVhZEVkZ2VfUzA7CmluIGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZEVkZ2UKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2aW5Db2xvcl9TMDsKCWhhbGYgZWRnZUFscGhhOwoJaGFsZjIgZHV2ZHggPSBoYWxmMihkRmR4KHZRdWFkRWRnZV9TMC54eSkpOwoJaGFsZjIgZHV2ZHkgPSBoYWxmMihkRmR5KHZRdWFkRWRnZV9TMC54eSkpOwoJaWYgKHZRdWFkRWRnZV9TMC56ID4gMC4wICYmIHZRdWFkRWRnZV9TMC53ID4gMC4wKSAKCXsKCQllZGdlQWxwaGEgPSBoYWxmKG1pbihtaW4odlF1YWRFZGdlX1MwLnosIHZRdWFkRWRnZV9TMC53KSArIDAuNSwgMS4wKSk7Cgl9CgllbHNlIAoJewoJCWhhbGYyIGdGID0gaGFsZjIoaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHgueCAtIGR1dmR4LnkpLCAgICAgICAgICAgICAgICAgaGFsZigyLjAqdlF1YWRFZGdlX1MwLngqZHV2ZHkueCAtIGR1dmR5LnkpKTsKCQllZGdlQWxwaGEgPSBoYWxmKHZRdWFkRWRnZV9TMC54KnZRdWFkRWRnZV9TMC54IC0gdlF1YWRFZGdlX1MwLnkpOwoJCWVkZ2VBbHBoYSA9IHNhdHVyYXRlKDAuNSAtIGVkZ2VBbHBoYSAvIGxlbmd0aChnRikpOwoJfQoJaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNChlZGdlQWxwaGEpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dENvbG9yX1MwICogb3V0cHV0Q292ZXJhZ2VfUzA7Cgl9Cn0KAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAKAAAAaW5Qb3NpdGlvbgAABwAAAGluQ29sb3IACgAAAGluUXVhZEVkZ2UAAAAAAAA=","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUASJ3EZYN2AAAAACAAAEAAAABSCQKL3IYIJ2AAAAACAAAEAAAABLRAABAAAAABAEGABBAMAAQAAAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAAGgHAABjb25zdCBpbnQga01heExvb3BMaW1pdF9TMV9jMCA9IDg7CnVuaWZvcm0gaGFsZjQgdWJvcmRlcl9TMV9jMF9jMF9jMDsKdW5pZm9ybSBmbG9hdDQgdXN1YnNldF9TMV9jMF9jMF9jMDsKdW5pZm9ybSBmbG9hdDQgdWNsYW1wX1MxX2MwX2MwX2MwOwp1bmlmb3JtIGZsb2F0MiB1aWRpbXNfUzFfYzBfYzBfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxNF07CnVuaWZvcm0gaGFsZjIgdWRpcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJZmxvYXQyIGluQ29vcmQgPSBfY29vcmRzOwoJZmxvYXQyIHN1YnNldENvb3JkOwoJc3Vic2V0Q29vcmQueCA9IGluQ29vcmQueDsKCXN1YnNldENvb3JkLnkgPSBpbkNvb3JkLnk7CglmbG9hdDIgY2xhbXBlZENvb3JkOwoJY2xhbXBlZENvb3JkLnggPSBjbGFtcChzdWJzZXRDb29yZC54LCB1Y2xhbXBfUzFfYzBfYzBfYzAueCwgdWNsYW1wX1MxX2MwX2MwX2MwLnopOwoJY2xhbXBlZENvb3JkLnkgPSBzdWJzZXRDb29yZC55OwoJaGFsZjQgdGV4dHVyZUNvbG9yID0gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCAoY2xhbXBlZENvb3JkKSAqIHVpZGltc19TMV9jMF9jMF9jMCk7CgloYWxmIGVyclggPSBoYWxmKHN1YnNldENvb3JkLnggLSBjbGFtcGVkQ29vcmQueCk7Cgl0ZXh0dXJlQ29sb3IgPSBtaXgodGV4dHVyZUNvbG9yLCB1Ym9yZGVyX1MxX2MwX2MwX2MwLCBtaW4oYWJzKGVyclgpLCAxKSk7CglyZXR1cm4gdGV4dHVyZUNvbG9yOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMV9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0LCBmbG9hdDN4Mih1bWF0cml4X1MxX2MwX2MwKSAqIF9jb29yZHMueHkxKTsKfQpoYWxmNCBHYXVzc2lhbkJsdXIxRF9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CgloYWxmNCBzdW0gPSBoYWxmNCgwLjApOwoJZm9yIChpbnQgaSA9IDA7aSA8IGtNYXhMb29wTGltaXRfUzFfYzA7ICsraSkgCgl7CgkJaGFsZjQgcyA9IHVvZmZzZXRzQW5kS2VybmVsX1MxX2MwW2ldOwoJCXN1bSArPSBzLnkgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIocy54ICogdWRpcl9TMV9jMCkpOwoJCXN1bSArPSBzLncgKiBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoX3RtcF8wX2luQ29sb3IsIF90bXBfMV9jb29yZHMgKyBmbG9hdDIocy56ICogdWRpcl9TMV9jMCkpOwoJfQoJcmV0dXJuIGhhbGY0KHN1bSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBHYXVzc2lhbkJsdXIxRF9TMV9jMChfaW5wdXQpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwID0gaGFsZjQoMSk7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgoAAABsb2NhbENvb3JkAAAAAAAA","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAADEAAGAAAAAAAAAIAAAAAPAIABAAAAACYGEAAAAEAAAABUARCAIAAAAAAAAAAAACQAGAAAAAQAAAAAAAQQGAAAAA":"DAAAAExTS1NoAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc180X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCXZjb2xvcl9TMCA9IGNvbG9yOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc180X1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMV9jMF9jMCkgKiBwb3NpdGlvbi54eTE7Cgl9Cn0KAAAAADkDAAB1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzFfYzBfYzA7CnNhbXBsZXIyRCB1VGV4dHVyZVNhbXBsZXJfMF9TMTsKZmxhdCBpbiBoYWxmNCB2Y29sb3JfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gc2FtcGxlKHVUZXh0dXJlU2FtcGxlcl8wX1MxLCB2VHJhbnNmb3JtZWRDb29yZHNfNF9TMCkuMDAwcjsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIFRleHR1cmVFZmZlY3RfUzFfYzBfYzBfYzAoX2lucHV0KTsKfQpoYWxmNCBEZXZpY2VTcGFjZV9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gTWF0cml4RWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgQmxlbmRfUzEoaGFsZjQgX3NyYywgaGFsZjQgX2RzdCkgCnsKCXJldHVybiBibGVuZF9kc3RfaW4oRGV2aWNlU3BhY2VfUzFfYzAoX3NyYyksIF9zcmMpOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7Cgljb25zdCBoYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KDEpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gQmxlbmRfUzEob3V0cHV0Q292ZXJhZ2VfUzAsIGhhbGY0KDEpKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMTsKCX0KfQoAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAAAAAA=","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAFIBVWKMG7QAAAAAAAAAQCAAAAAVQEEAQAAAAAQCDAAQQGAAAAEAAAAAAHIAAAAAAAIAAAAAQGIAAAAAAA":"DAAAAExTS1N6AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMDsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBoYWxmNCBjb2xvcjsKaW4gZmxvYXQyIGxvY2FsQ29vcmQ7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfM19TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzFfYzApICogbG9jYWxDb29yZC54eTE7Cgl9Cn0KAAAAAAAAQgQAAHVuaWZvcm0gZmxvYXQ0IHVjbGFtcF9TMV9jMF9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzNfUzA7CmhhbGY0IFRleHR1cmVFZmZlY3RfUzFfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGluQ29vcmQgPSB2VHJhbnNmb3JtZWRDb29yZHNfM19TMDsKCWZsb2F0MiBzdWJzZXRDb29yZDsKCXN1YnNldENvb3JkLnggPSBpbkNvb3JkLng7CglzdWJzZXRDb29yZC55ID0gaW5Db29yZC55OwoJZmxvYXQyIGNsYW1wZWRDb29yZDsKCWNsYW1wZWRDb29yZC54ID0gc3Vic2V0Q29vcmQueDsKCWNsYW1wZWRDb29yZC55ID0gY2xhbXAoc3Vic2V0Q29vcmQueSwgdWNsYW1wX1MxX2MwX2MwLnksIHVjbGFtcF9TMV9jMF9jMC53KTsKCWhhbGY0IHRleHR1cmVDb2xvciA9IHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgY2xhbXBlZENvb3JkKTsKCXJldHVybiB0ZXh0dXJlQ29sb3I7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MxX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgRGlzYWJsZUNvdmVyYWdlQXNBbHBoYV9TMShoYWxmNCBfaW5wdXQpIAp7CglfaW5wdXQgPSBNYXRyaXhFZmZlY3RfUzFfYzAoX2lucHV0KTsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJcmV0dXJuIGhhbGY0KF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZjb2xvcl9TMDsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBEaXNhYmxlQ292ZXJhZ2VBc0FscGhhX1MxKG91dHB1dENvbG9yX1MwKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRfUzEgKiBvdXRwdXRDb3ZlcmFnZV9TMDsKCX0KfQoAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAACgAAAGxvY2FsQ29vcmQAAAAAAAA=","HVIACAAAABQAAGAAAQ4AAAAAGQQAARC4GAAAIOCAAD6P7777777777YDAAAAAAAAAAAHQBNGZODK5YIAAAAAAQAAAAAIADCNS4GV3QYBAAAAAAAAAAAIAA6IAMAAACAAAAAABUABAAAAAEAAAAAIBEABAA":"DAAAAExTS1PZAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdCBjb3ZlcmFnZTsKaW4gaGFsZjQgY29sb3I7CmluIGZsb2F0MiBsb2NhbENvb3JkOwpmbGF0IG91dCBoYWxmNCB2Y29sb3JfUzA7Cm91dCBmbG9hdCB2Y292ZXJhZ2VfUzA7Cm91dCBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CnZvaWQgbWFpbigpIAp7CgkvLyBQcmltaXRpdmUgUHJvY2Vzc29yIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJZmxvYXQyIHBvc2l0aW9uID0gcG9zaXRpb24ueHk7Cgl2Y29sb3JfUzAgPSBjb2xvcjsKCXZjb3ZlcmFnZV9TMCA9IGNvdmVyYWdlOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KfQoAAAABAAAAWR0AAGZsb2F0NCBmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMDsKZmxvYXQ0IGZyYWdDb2xvcl9TMV9jMDsKZmxvYXQgdV9hbHBoYV9TMV9jMDsKZmxvYXQgdV9zcGFya2xlX2FscGhhX1MxX2MwOwpmbG9hdCB1X2JsdXJfUzFfYzA7CmZsb2F0IHVfcmFkaXVzX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbG9yX1MxX2MwOwp1bmlmb3JtIGZsb2F0NCB1X2NvbXBvc2l0ZV8xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NlbnRlcl9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X21heF9yYWRpdXNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9ub2lzZV9zY2FsZV9TMV9jMDsKdW5pZm9ybSBmbG9hdCB1X25vaXNlX3BoYXNlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUzX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjJfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24zX1MxX2MwOwp1bmlmb3JtIGZsb2F0M3gzIHVtYXRyaXhfUzE7CnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzI7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MyOwpmbGF0IGluIGhhbGY0IHZjb2xvcl9TMDsKaW4gZmxvYXQgdmNvdmVyYWdlX1MwOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmZsb2F0MiBGTFRfZmx1dHRlcl9sb2NhbF9GbHV0dGVyRnJhZ0Nvb3JkX1MxX2MwKCk7CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IHRoaWNrbmVzcywgZmxvYXQgYmx1cik7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKGlub3V0IGZsb2F0MiBuKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCk7CnZvaWQgRkxUX21haW5fUzFfYzAoKTsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKSAKewoJcmV0dXJuIGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwLnh5Owp9CmZsb2F0MngyIEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKGZsb2F0MiByYWQpIAp7CglyZXR1cm4gZmxvYXQyeDIocmFkLngsIC1yYWQueSwgcmFkLnksIHJhZC54KTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQgYmx1cl9oYWxmID0gYmx1ciAqIDAuNTsKCWZsb2F0IGQgPSBkaXN0YW5jZSh1diwgeHkpOwoJcmV0dXJuIDEuMCAtIHNtb290aHN0ZXAoMS4wIC0gYmx1cl9oYWxmLCAxLjAgKyBibHVyX2hhbGYsIGQgLyByYWRpdXMpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKGZsb2F0MiByZXNvbHV0aW9uLCBpbm91dCBmbG9hdDIgcCwgZmxvYXQyIHh5LCBmbG9hdDIgcm90YXRpb24sIGZsb2F0IGNlbGxfZGlhbWV0ZXIpIAp7CglmbG9hdDIgcGFyYW0gPSByb3RhdGlvbjsKCXAgPSBGTFRfZmx1dHRlcl9sb2NhbF9yb3RhdGUyZF9TMV9jMChwYXJhbSkgKiAoeHkgLSBwKSArIHh5OwoJcCA9IG1vZChwLCBmbG9hdDIoY2VsbF9kaWFtZXRlcikpIC8gcmVzb2x1dGlvbjsKCWZsb2F0IGNlbGxfdXYgPSAoY2VsbF9kaWFtZXRlciAvIHJlc29sdXRpb24ueSkgKiAwLjU7CglmbG9hdCByID0gMC42NSAqIGNlbGxfdXY7CglmbG9hdDIgcGFyYW1fMSA9IHA7CglmbG9hdDIgcGFyYW1fMiA9IGZsb2F0MihjZWxsX3V2KTsKCWZsb2F0IHBhcmFtXzMgPSByOwoJZmxvYXQgcGFyYW1fNCA9IHIgKiA1MC4wOwoJcmV0dXJuIEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAoZmxvYXQyIHV2KSAKewoJZmxvYXQyIHV2X3NjYWxlID0gdXYgKiBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbSA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8yID0gdV9jaXJjbGUxX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzMgPSB1X3JvdGF0aW9uMV9TMV9jMDsKCWZsb2F0IHBhcmFtXzQgPSAwLjE3OwoJZmxvYXQgXzMxOSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtLCBwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zLCBwYXJhbV80KTsKCWZsb2F0IGcxID0gXzMxOTsKCWZsb2F0MiBwYXJhbV81ID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fNiA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzcgPSB1X2NpcmNsZTJfUzFfYzA7CglmbG9hdDIgcGFyYW1fOCA9IHVfcm90YXRpb24yX1MxX2MwOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjsKCWZsb2F0IF8zMzEgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IGcyID0gXzMzMTsKCWZsb2F0MiBwYXJhbV8xMCA9IGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtXzExID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fMTIgPSB1X2NpcmNsZTNfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1X3JvdGF0aW9uM19TMV9jMDsKCWZsb2F0IHBhcmFtXzE0ID0gMC4yNzU7CglmbG9hdCBfMzQ0ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMiwgcGFyYW1fMTMsIHBhcmFtXzE0KTsKCWZsb2F0IGczID0gXzM0NDsKCWZsb2F0IHYgPSAoKGcxICogZzEgKyBnMikgLSBnMykgKiAwLjU7CglyZXR1cm4gY2xhbXAoMC40NSArIDAuOCAqIHYsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKSAKewoJZmxvYXQyIHBhcmFtID0gdXY7CglmbG9hdDIgcGFyYW1fMSA9IHh5OwoJZmxvYXQgcGFyYW1fMiA9IHJhZGl1cyArIHRoaWNrbmVzczsKCWZsb2F0IHBhcmFtXzMgPSBibHVyOwoJZmxvYXQgY2lyY2xlX291dGVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQyIHBhcmFtXzQgPSB1djsKCWZsb2F0MiBwYXJhbV81ID0geHk7CglmbG9hdCBwYXJhbV82ID0gbWF4KHJhZGl1cyAtIHRoaWNrbmVzcywgMC4wKTsKCWZsb2F0IHBhcmFtXzcgPSBibHVyOwoJZmxvYXQgY2lyY2xlX2lubmVyID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNiwgcGFyYW1fNyk7CglyZXR1cm4gY2xhbXAoY2lyY2xlX291dGVyIC0gY2lyY2xlX2lubmVyLCAwLjAsIDEuMCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pIAp7CgluID0gZnJhY3QobiAqIGZsb2F0Mig1LjM5ODcsIDUuNDQyMSkpOwoJbiArPSBmbG9hdDIoZG90KG4ueXgsIG4gKyBmbG9hdDIoMjEuNTM1MSwgMTQuMzEzNykpKTsKCWZsb2F0IHh5ID0gbi54ICogbi55OwoJcmV0dXJuIChmcmFjdCh4eSAqIDk1LjQzMDcpICsgZnJhY3QoeHkgKiA3NS4wNDk2MSkpIC0gMS4wOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChmbG9hdCB2LCBmbG9hdCBsLCBmbG9hdCBoKSAKewoJcmV0dXJuIHN0ZXAobCwgdikgKiAoMS4wIC0gc3RlcChoLCB2KSk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChmbG9hdDIgdXYsIGZsb2F0IHQpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0IF8yNDIgPSBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChwYXJhbSk7CglmbG9hdCBuID0gXzI0MjsKCWZsb2F0IHBhcmFtXzEgPSBuOwoJZmxvYXQgcGFyYW1fMiA9IDAuMDsKCWZsb2F0IHBhcmFtXzMgPSAwLjA1OwoJZmxvYXQgcyA9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV8xLCBwYXJhbV8yLCBwYXJhbV8zKTsKCWZsb2F0IHBhcmFtXzQgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDAuMzUpKTsKCWZsb2F0IHBhcmFtXzUgPSAwLjE7CglmbG9hdCBwYXJhbV82ID0gMC4xNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzQsIHBhcmFtXzUsIHBhcmFtXzYpOwoJZmxvYXQgcGFyYW1fNyA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC43KSk7CglmbG9hdCBwYXJhbV84ID0gMC4yOwoJZmxvYXQgcGFyYW1fOSA9IDAuMjU7CglzICs9IEZMVF9mbHV0dGVyX2xvY2FsX3RocmVzaG9sZF9TMV9jMChwYXJhbV83LCBwYXJhbV84LCBwYXJhbV85KTsKCWZsb2F0IHBhcmFtXzEwID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAxLjA1KSk7CglmbG9hdCBwYXJhbV8xMSA9IDAuMzsKCWZsb2F0IHBhcmFtXzEyID0gMC4zNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEwLCBwYXJhbV8xMSwgcGFyYW1fMTIpOwoJcmV0dXJuIGNsYW1wKHMsIDAuMCwgMS4wKSAqIDAuNTU7Cn0Kdm9pZCBGTFRfbWFpbl9TMV9jMCgpIAp7Cgl1X2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC54OwoJdV9zcGFya2xlX2FscGhhX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC55OwoJdV9ibHVyX1MxX2MwID0gdV9jb21wb3NpdGVfMV9TMV9jMC56OwoJdV9yYWRpdXNfc2NhbGVfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnc7CglmbG9hdDIgcCA9IEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKCWZsb2F0MiB1dl8xID0gcCAqIHVfcmVzb2x1dGlvbl9zY2FsZV9TMV9jMDsKCWZsb2F0MiBkZW5zaXR5X3V2ID0gdXZfMSAtIG1vZChwLCB1X25vaXNlX3NjYWxlX1MxX2MwKTsKCWZsb2F0IHJhZGl1cyA9IHVfbWF4X3JhZGl1c19TMV9jMCAqIHVfcmFkaXVzX3NjYWxlX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzEzID0gdXZfMTsKCWZsb2F0IHR1cmJ1bGVuY2UgPSBGTFRfZmx1dHRlcl9sb2NhbF90dXJidWxlbmNlX1MxX2MwKHBhcmFtXzEzKTsKCWZsb2F0MiBwYXJhbV8xNCA9IHA7CglmbG9hdDIgcGFyYW1fMTUgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzE2ID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMTcgPSAwLjA1ICogdV9tYXhfcmFkaXVzX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTggPSB1X2JsdXJfUzFfYzA7CglmbG9hdCByaW5nID0gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9yaW5nX1MxX2MwKHBhcmFtXzE0LCBwYXJhbV8xNSwgcGFyYW1fMTYsIHBhcmFtXzE3LCBwYXJhbV8xOCk7CglmbG9hdDIgcGFyYW1fMTkgPSBkZW5zaXR5X3V2OwoJZmxvYXQgcGFyYW1fMjAgPSB1X25vaXNlX3BoYXNlX1MxX2MwOwoJZmxvYXQgc3BhcmtsZSA9ICgoRkxUX2ZsdXR0ZXJfbG9jYWxfc3BhcmtsZV9TMV9jMChwYXJhbV8xOSwgcGFyYW1fMjApICogcmluZykgKiB0dXJidWxlbmNlKSAqIHVfc3BhcmtsZV9hbHBoYV9TMV9jMDsKCWZsb2F0MiBwYXJhbV8yMSA9IHA7CglmbG9hdDIgcGFyYW1fMjIgPSB1X2NlbnRlcl9TMV9jMDsKCWZsb2F0IHBhcmFtXzIzID0gcmFkaXVzOwoJZmxvYXQgcGFyYW1fMjQgPSB1X2JsdXJfUzFfYzA7CglmbG9hdCB3YXZlX2FscGhhID0gKEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKHBhcmFtXzIxLCBwYXJhbV8yMiwgcGFyYW1fMjMsIHBhcmFtXzI0KSAqIHVfYWxwaGFfUzFfYzApICogdV9jb2xvcl9TMV9jMC53OwoJZmxvYXQ0IHdhdmVfY29sb3IgPSBmbG9hdDQodV9jb2xvcl9TMV9jMC54eXogKiB3YXZlX2FscGhhLCB3YXZlX2FscGhhKTsKCWZyYWdDb2xvcl9TMV9jMCA9IG1peCh3YXZlX2NvbG9yLCBmbG9hdDQoMS4wKSwgZmxvYXQ0KHNwYXJrbGUpKTsKfQpoYWxmNCBydW50aW1lX3NoYWRlcl9TMV9jMChoYWxmNCBfaW5wdXQpIAp7CgloYWxmNCBfdG1wXzBfaW5Db2xvciA9IF9pbnB1dDsKCWZsb2F0MiBfdG1wXzFfY29vcmRzID0gdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CglmbHV0dGVyX0ZyYWdDb29yZF9TMV9jMCA9IGZsb2F0NChfdG1wXzFfY29vcmRzLCAwLjAsIDAuMCk7CglGTFRfbWFpbl9TMV9jMCgpOwoJcmV0dXJuIGhhbGY0KGhhbGY0KGZyYWdDb2xvcl9TMV9jMCkpOwp9CmhhbGY0IE1hdHJpeEVmZmVjdF9TMShoYWxmNCBfaW5wdXQpIAp7CglyZXR1cm4gcnVudGltZV9zaGFkZXJfUzFfYzAoX2lucHV0KTsKfQpoYWxmNCBDaXJjdWxhclJSZWN0X1MyKGhhbGY0IF9pbnB1dCkgCnsKCWZsb2F0MiBkeHkwID0gdWlubmVyUmVjdF9TMi5MVCAtIHNrX0ZyYWdDb29yZC54eTsKCWZsb2F0MiBkeHkxID0gc2tfRnJhZ0Nvb3JkLnh5IC0gdWlubmVyUmVjdF9TMi5SQjsKCWZsb2F0MiBkeHkgPSBtYXgobWF4KGR4eTAsIGR4eTEpLCAwLjApOwoJaGFsZiBhbHBoYSA9IGhhbGYoc2F0dXJhdGUodXJhZGl1c1BsdXNIYWxmX1MyLnggLSBsZW5ndGgoZHh5KSkpOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CnZvaWQgbWFpbigpIAp7CgkvLyBTdGFnZSAwLCBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCWhhbGY0IG91dHB1dENvbG9yX1MwOwoJb3V0cHV0Q29sb3JfUzAgPSB2Y29sb3JfUzA7CglmbG9hdCBjb3ZlcmFnZSA9IHZjb3ZlcmFnZV9TMDsKCWhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoaGFsZihjb3ZlcmFnZSkpOwoJaGFsZjQgb3V0cHV0X1MxOwoJb3V0cHV0X1MxID0gTWF0cml4RWZmZWN0X1MxKG91dHB1dENvbG9yX1MwKTsKCWhhbGY0IG91dHB1dF9TMjsKCW91dHB1dF9TMiA9IENpcmN1bGFyUlJlY3RfUzIob3V0cHV0Q292ZXJhZ2VfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoAAAABAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAgAAABwb3NpdGlvbggAAABjb3ZlcmFnZQUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","AYQA5AADQAAAOAEARAFQJAABBADIB7777777777777777777777767YAAAAAAAAAAAAB3QA6AAAEAAAAAAAMAAPEAEAAABAAAAAAB2AAAAAAACAAAAAEBSAAAA":"DAAAAExTS1PMAQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQ0IHVsb2NhbE1hdHJpeF9TMDsKaW4gZmxvYXQyIGluUG9zaXRpb247CmluIGhhbGY0IGluQ29sb3I7CmluIGZsb2F0NCBpbkNpcmNsZUVkZ2U7Cm91dCBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKb3V0IGhhbGY0IHZpbkNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBDaXJjbGVHZW9tZXRyeVByb2Nlc3NvcgoJdmluQ2lyY2xlRWRnZV9TMCA9IGluQ2lyY2xlRWRnZTsKCXZpbkNvbG9yX1MwID0gaW5Db2xvcjsKCWZsb2F0MiBfdG1wXzBfaW5Qb3NpdGlvbiA9IGluUG9zaXRpb247CglmbG9hdDIgX3RtcF8xX2luUG9zaXRpb24gPSB1bG9jYWxNYXRyaXhfUzAueHogKiBpblBvc2l0aW9uICsgdWxvY2FsTWF0cml4X1MwLnl3OwoJc2tfUG9zaXRpb24gPSBfdG1wXzBfaW5Qb3NpdGlvbi54eTAxOwp9CgEAAAAeBQAAdW5pZm9ybSBmbG9hdDQgdWlubmVyUmVjdF9TMTsKdW5pZm9ybSBoYWxmMiB1cmFkaXVzUGx1c0hhbGZfUzE7CnVuaWZvcm0gZmxvYXQ0IHVpbm5lclJlY3RfUzI7CnVuaWZvcm0gaGFsZjIgdXJhZGl1c1BsdXNIYWxmX1MyOwppbiBmbG9hdDQgdmluQ2lyY2xlRWRnZV9TMDsKaW4gaGFsZjQgdmluQ29sb3JfUzA7CmhhbGY0IENpcmN1bGFyUlJlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MxLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MxLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzEueCAtIGxlbmd0aChkeHkpKSk7CglhbHBoYSA9IDEuMCAtIGFscGhhOwoJcmV0dXJuIF9pbnB1dCAqIGFscGhhOwp9CmhhbGY0IENpcmN1bGFyUlJlY3RfUzIoaGFsZjQgX2lucHV0KSAKewoJZmxvYXQyIGR4eTAgPSB1aW5uZXJSZWN0X1MyLkxUIC0gc2tfRnJhZ0Nvb3JkLnh5OwoJZmxvYXQyIGR4eTEgPSBza19GcmFnQ29vcmQueHkgLSB1aW5uZXJSZWN0X1MyLlJCOwoJZmxvYXQyIGR4eSA9IG1heChtYXgoZHh5MCwgZHh5MSksIDAuMCk7CgloYWxmIGFscGhhID0gaGFsZihzYXR1cmF0ZSh1cmFkaXVzUGx1c0hhbGZfUzIueCAtIGxlbmd0aChkeHkpKSk7CglyZXR1cm4gX2lucHV0ICogYWxwaGE7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIENpcmNsZUdlb21ldHJ5UHJvY2Vzc29yCglmbG9hdDQgY2lyY2xlRWRnZTsKCWNpcmNsZUVkZ2UgPSB2aW5DaXJjbGVFZGdlX1MwOwoJaGFsZjQgb3V0cHV0Q29sb3JfUzA7CglvdXRwdXRDb2xvcl9TMCA9IHZpbkNvbG9yX1MwOwoJZmxvYXQgZCA9IGxlbmd0aChjaXJjbGVFZGdlLnh5KTsKCWhhbGYgZGlzdGFuY2VUb091dGVyRWRnZSA9IGhhbGYoY2lyY2xlRWRnZS56ICogKDEuMCAtIGQpKTsKCWhhbGYgZWRnZUFscGhhID0gc2F0dXJhdGUoZGlzdGFuY2VUb091dGVyRWRnZSk7CgloYWxmNCBvdXRwdXRDb3ZlcmFnZV9TMCA9IGhhbGY0KGVkZ2VBbHBoYSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBDaXJjdWxhclJSZWN0X1MxKG91dHB1dENvdmVyYWdlX1MwKTsKCWhhbGY0IG91dHB1dF9TMjsKCW91dHB1dF9TMiA9IENpcmN1bGFyUlJlY3RfUzIob3V0cHV0X1MxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dF9TMjsKCX0KfQoAAAEAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADAAAACgAAAGluUG9zaXRpb24AAAcAAABpbkNvbG9yAAwAAABpbkNpcmNsZUVkZ2UAAAAA","HUQAAAAAAAMAADAAAIOAAAH677776IZOCAAP577777777777777777YBAAAAAAAAAAAKAAYAAAACAAAAAAACCAYAAA":"DAAAAExTS1PUAAAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CmluIGZsb2F0MiBwb3NpdGlvbjsKaW4gaGFsZjQgY29sb3I7CmZsYXQgb3V0IGhhbGY0IHZjb2xvcl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgl2Y29sb3JfUzAgPSBjb2xvcjsKCXNrX1Bvc2l0aW9uID0gcG9zaXRpb24ueHkwMTsKfQoAAAAAEQEAAGZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCXsKCQkvLyBYZmVyIFByb2Nlc3NvcjogUG9ydGVyIER1ZmYKCQlza19GcmFnQ29sb3IgPSBvdXRwdXRDb2xvcl9TMCAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAAACAAAAHBvc2l0aW9uBQAAAGNvbG9yAAAAAAAAAA==","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUASJ3EZYN2AAAAAAAAAEAAAABSCQKL3IYIJ2AAAAAAAAAEAAAABLBAABAAAAABAEGABBAMAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAACoFAABjb25zdCBpbnQga01heExvb3BMaW1pdF9TMV9jMCA9IDg7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxNF07CnVuaWZvcm0gaGFsZjIgdWRpcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgX2Nvb3Jkcyk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IEdhdXNzaWFuQmx1cjFEX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWhhbGY0IHN1bSA9IGhhbGY0KDAuMCk7Cglmb3IgKGludCBpID0gMDtpIDwga01heExvb3BMaW1pdF9TMV9jMDsgKytpKSAKCXsKCQloYWxmNCBzID0gdW9mZnNldHNBbmRLZXJuZWxfUzFfYzBbaV07CgkJc3VtICs9IHMueSAqIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgX3RtcF8xX2Nvb3JkcyArIGZsb2F0MihzLnggKiB1ZGlyX1MxX2MwKSk7CgkJc3VtICs9IHMudyAqIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgX3RtcF8xX2Nvb3JkcyArIGZsb2F0MihzLnogKiB1ZGlyX1MxX2MwKSk7Cgl9CglyZXR1cm4gaGFsZjQoc3VtKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIEdhdXNzaWFuQmx1cjFEX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA==","HVIAAAAAABIAAGAAAQ4AAAH477776R24EAAAIOBQAD6P7777777777YDAAAAAAAAAAAHQBNGZODK5YIAAAAAAQAAAAAIADCNS4GV3QYBAAAAAAAAAAAIAAJQAAAAAAAACAAAAADYCAAIAAAAACABKAYABAAAAAFAEEQCEAAAAAAAAAAAAAAB2AAAAAAACAAAAAEBSAA":"DAAAAExTS1MIAgAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MyX2MwX2MwOwppbiBmbG9hdDIgcG9zaXRpb247CmluIGhhbGY0IGNvbG9yOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKZmxhdCBvdXQgaGFsZjQgdmNvbG9yX1MwOwpvdXQgZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwOwpvdXQgZmxvYXQyIHZUcmFuc2Zvcm1lZENvb3Jkc182X1MwOwp2b2lkIG1haW4oKSAKewoJLy8gUHJpbWl0aXZlIFByb2Nlc3NvciBRdWFkUGVyRWRnZUFBR2VvbWV0cnlQcm9jZXNzb3IKCXZjb2xvcl9TMCA9IGNvbG9yOwoJc2tfUG9zaXRpb24gPSBwb3NpdGlvbi54eTAxOwoJewoJCXZUcmFuc2Zvcm1lZENvb3Jkc18yX1MwID0gZmxvYXQzeDIodW1hdHJpeF9TMSkgKiBsb2NhbENvb3JkLnh5MTsKCX0KCXsKCQl2VHJhbnNmb3JtZWRDb29yZHNfNl9TMCA9IGZsb2F0M3gyKHVtYXRyaXhfUzJfYzBfYzApICogcG9zaXRpb24ueHkxOwoJfQp9CgAAAAC0HQAAZmxvYXQ0IGZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwOwpmbG9hdDQgZnJhZ0NvbG9yX1MxX2MwOwpmbG9hdCB1X2FscGhhX1MxX2MwOwpmbG9hdCB1X3NwYXJrbGVfYWxwaGFfUzFfYzA7CmZsb2F0IHVfYmx1cl9TMV9jMDsKZmxvYXQgdV9yYWRpdXNfc2NhbGVfUzFfYzA7CnVuaWZvcm0gZmxvYXQ0IHVfY29sb3JfUzFfYzA7CnVuaWZvcm0gZmxvYXQ0IHVfY29tcG9zaXRlXzFfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2VudGVyX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbWF4X3JhZGl1c19TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X25vaXNlX3NjYWxlX1MxX2MwOwp1bmlmb3JtIGZsb2F0IHVfbm9pc2VfcGhhc2VfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfY2lyY2xlMV9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9jaXJjbGUyX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X2NpcmNsZTNfUzFfYzA7CnVuaWZvcm0gZmxvYXQyIHVfcm90YXRpb24xX1MxX2MwOwp1bmlmb3JtIGZsb2F0MiB1X3JvdGF0aW9uMl9TMV9jMDsKdW5pZm9ybSBmbG9hdDIgdV9yb3RhdGlvbjNfUzFfYzA7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MyX2MwX2MwOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzI7CmZsYXQgaW4gaGFsZjQgdmNvbG9yX1MwOwppbiBmbG9hdDIgdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzA7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfNl9TMDsKZmxvYXQyIEZMVF9mbHV0dGVyX2xvY2FsX0ZsdXR0ZXJGcmFnQ29vcmRfUzFfYzAoKTsKZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChmbG9hdDIgcmVzb2x1dGlvbiwgaW5vdXQgZmxvYXQyIHAsIGZsb2F0MiB4eSwgZmxvYXQyIHJvdGF0aW9uLCBmbG9hdCBjZWxsX2RpYW1ldGVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdDIgeHksIGZsb2F0IHJhZGl1cywgZmxvYXQgdGhpY2tuZXNzLCBmbG9hdCBibHVyKTsKZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHJpYW5nbGVfbm9pc2VfUzFfYzAoaW5vdXQgZmxvYXQyIG4pOwpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAoZmxvYXQgdiwgZmxvYXQgbCwgZmxvYXQgaCk7CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NwYXJrbGVfUzFfYzAoZmxvYXQyIHV2LCBmbG9hdCB0KTsKdm9pZCBGTFRfbWFpbl9TMV9jMCgpOwpmbG9hdDIgRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpIAp7CglyZXR1cm4gZmx1dHRlcl9GcmFnQ29vcmRfUzFfYzAueHk7Cn0KZmxvYXQyeDIgRkxUX2ZsdXR0ZXJfbG9jYWxfcm90YXRlMmRfUzFfYzAoZmxvYXQyIHJhZCkgCnsKCXJldHVybiBmbG9hdDJ4MihyYWQueCwgLXJhZC55LCByYWQueSwgcmFkLngpOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfY2lyY2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQyIHh5LCBmbG9hdCByYWRpdXMsIGZsb2F0IGJsdXIpIAp7CglmbG9hdCBibHVyX2hhbGYgPSBibHVyICogMC41OwoJZmxvYXQgZCA9IGRpc3RhbmNlKHV2LCB4eSk7CglyZXR1cm4gMS4wIC0gc21vb3Roc3RlcCgxLjAgLSBibHVyX2hhbGYsIDEuMCArIGJsdXJfaGFsZiwgZCAvIHJhZGl1cyk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAoZmxvYXQyIHJlc29sdXRpb24sIGlub3V0IGZsb2F0MiBwLCBmbG9hdDIgeHksIGZsb2F0MiByb3RhdGlvbiwgZmxvYXQgY2VsbF9kaWFtZXRlcikgCnsKCWZsb2F0MiBwYXJhbSA9IHJvdGF0aW9uOwoJcCA9IEZMVF9mbHV0dGVyX2xvY2FsX3JvdGF0ZTJkX1MxX2MwKHBhcmFtKSAqICh4eSAtIHApICsgeHk7CglwID0gbW9kKHAsIGZsb2F0MihjZWxsX2RpYW1ldGVyKSkgLyByZXNvbHV0aW9uOwoJZmxvYXQgY2VsbF91diA9IChjZWxsX2RpYW1ldGVyIC8gcmVzb2x1dGlvbi55KSAqIDAuNTsKCWZsb2F0IHIgPSAwLjY1ICogY2VsbF91djsKCWZsb2F0MiBwYXJhbV8xID0gcDsKCWZsb2F0MiBwYXJhbV8yID0gZmxvYXQyKGNlbGxfdXYpOwoJZmxvYXQgcGFyYW1fMyA9IHI7CglmbG9hdCBwYXJhbV80ID0gciAqIDUwLjA7CglyZXR1cm4gRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMywgcGFyYW1fNCk7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdHVyYnVsZW5jZV9TMV9jMChmbG9hdDIgdXYpIAp7CglmbG9hdDIgdXZfc2NhbGUgPSB1diAqIGZsb2F0MigwLjgpOwoJZmxvYXQyIHBhcmFtID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMSA9IHV2X3NjYWxlOwoJZmxvYXQyIHBhcmFtXzIgPSB1X2NpcmNsZTFfUzFfYzA7CglmbG9hdDIgcGFyYW1fMyA9IHVfcm90YXRpb24xX1MxX2MwOwoJZmxvYXQgcGFyYW1fNCA9IDAuMTc7CglmbG9hdCBfMzE5ID0gRkxUX2ZsdXR0ZXJfbG9jYWxfY2lyY2xlX2dyaWRfUzFfYzAocGFyYW0sIHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMsIHBhcmFtXzQpOwoJZmxvYXQgZzEgPSBfMzE5OwoJZmxvYXQyIHBhcmFtXzUgPSBmbG9hdDIoMC44KTsKCWZsb2F0MiBwYXJhbV82ID0gdXZfc2NhbGU7CglmbG9hdDIgcGFyYW1fNyA9IHVfY2lyY2xlMl9TMV9jMDsKCWZsb2F0MiBwYXJhbV84ID0gdV9yb3RhdGlvbjJfUzFfYzA7CglmbG9hdCBwYXJhbV85ID0gMC4yOwoJZmxvYXQgXzMzMSA9IEZMVF9mbHV0dGVyX2xvY2FsX2NpcmNsZV9ncmlkX1MxX2MwKHBhcmFtXzUsIHBhcmFtXzYsIHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgZzIgPSBfMzMxOwoJZmxvYXQyIHBhcmFtXzEwID0gZmxvYXQyKDAuOCk7CglmbG9hdDIgcGFyYW1fMTEgPSB1dl9zY2FsZTsKCWZsb2F0MiBwYXJhbV8xMiA9IHVfY2lyY2xlM19TMV9jMDsKCWZsb2F0MiBwYXJhbV8xMyA9IHVfcm90YXRpb24zX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTQgPSAwLjI3NTsKCWZsb2F0IF8zNDQgPSBGTFRfZmx1dHRlcl9sb2NhbF9jaXJjbGVfZ3JpZF9TMV9jMChwYXJhbV8xMCwgcGFyYW1fMTEsIHBhcmFtXzEyLCBwYXJhbV8xMywgcGFyYW1fMTQpOwoJZmxvYXQgZzMgPSBfMzQ0OwoJZmxvYXQgdiA9ICgoZzEgKiBnMSArIGcyKSAtIGczKSAqIDAuNTsKCXJldHVybiBjbGFtcCgwLjQ1ICsgMC44ICogdiwgMC4wLCAxLjApOwp9CmZsb2F0IEZMVF9mbHV0dGVyX2xvY2FsX3NvZnRfcmluZ19TMV9jMChmbG9hdDIgdXYsIGZsb2F0MiB4eSwgZmxvYXQgcmFkaXVzLCBmbG9hdCB0aGlja25lc3MsIGZsb2F0IGJsdXIpIAp7CglmbG9hdDIgcGFyYW0gPSB1djsKCWZsb2F0MiBwYXJhbV8xID0geHk7CglmbG9hdCBwYXJhbV8yID0gcmFkaXVzICsgdGhpY2tuZXNzOwoJZmxvYXQgcGFyYW1fMyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfb3V0ZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbSwgcGFyYW1fMSwgcGFyYW1fMiwgcGFyYW1fMyk7CglmbG9hdDIgcGFyYW1fNCA9IHV2OwoJZmxvYXQyIHBhcmFtXzUgPSB4eTsKCWZsb2F0IHBhcmFtXzYgPSBtYXgocmFkaXVzIC0gdGhpY2tuZXNzLCAwLjApOwoJZmxvYXQgcGFyYW1fNyA9IGJsdXI7CglmbG9hdCBjaXJjbGVfaW5uZXIgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X2NpcmNsZV9TMV9jMChwYXJhbV80LCBwYXJhbV81LCBwYXJhbV82LCBwYXJhbV83KTsKCXJldHVybiBjbGFtcChjaXJjbGVfb3V0ZXIgLSBjaXJjbGVfaW5uZXIsIDAuMCwgMS4wKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF90cmlhbmdsZV9ub2lzZV9TMV9jMChpbm91dCBmbG9hdDIgbikgCnsKCW4gPSBmcmFjdChuICogZmxvYXQyKDUuMzk4NywgNS40NDIxKSk7CgluICs9IGZsb2F0Mihkb3Qobi55eCwgbiArIGZsb2F0MigyMS41MzUxLCAxNC4zMTM3KSkpOwoJZmxvYXQgeHkgPSBuLnggKiBuLnk7CglyZXR1cm4gKGZyYWN0KHh5ICogOTUuNDMwNykgKyBmcmFjdCh4eSAqIDc1LjA0OTYxKSkgLSAxLjA7Cn0KZmxvYXQgRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKGZsb2F0IHYsIGZsb2F0IGwsIGZsb2F0IGgpIAp7CglyZXR1cm4gc3RlcChsLCB2KSAqICgxLjAgLSBzdGVwKGgsIHYpKTsKfQpmbG9hdCBGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKGZsb2F0MiB1diwgZmxvYXQgdCkgCnsKCWZsb2F0MiBwYXJhbSA9IHV2OwoJZmxvYXQgXzI0MiA9IEZMVF9mbHV0dGVyX2xvY2FsX3RyaWFuZ2xlX25vaXNlX1MxX2MwKHBhcmFtKTsKCWZsb2F0IG4gPSBfMjQyOwoJZmxvYXQgcGFyYW1fMSA9IG47CglmbG9hdCBwYXJhbV8yID0gMC4wOwoJZmxvYXQgcGFyYW1fMyA9IDAuMDU7CglmbG9hdCBzID0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzEsIHBhcmFtXzIsIHBhcmFtXzMpOwoJZmxvYXQgcGFyYW1fNCA9IG4gKyBzaW4oMy4xNDE1OTI3NCAqICh0ICsgMC4zNSkpOwoJZmxvYXQgcGFyYW1fNSA9IDAuMTsKCWZsb2F0IHBhcmFtXzYgPSAwLjE1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fNCwgcGFyYW1fNSwgcGFyYW1fNik7CglmbG9hdCBwYXJhbV83ID0gbiArIHNpbigzLjE0MTU5Mjc0ICogKHQgKyAwLjcpKTsKCWZsb2F0IHBhcmFtXzggPSAwLjI7CglmbG9hdCBwYXJhbV85ID0gMC4yNTsKCXMgKz0gRkxUX2ZsdXR0ZXJfbG9jYWxfdGhyZXNob2xkX1MxX2MwKHBhcmFtXzcsIHBhcmFtXzgsIHBhcmFtXzkpOwoJZmxvYXQgcGFyYW1fMTAgPSBuICsgc2luKDMuMTQxNTkyNzQgKiAodCArIDEuMDUpKTsKCWZsb2F0IHBhcmFtXzExID0gMC4zOwoJZmxvYXQgcGFyYW1fMTIgPSAwLjM1OwoJcyArPSBGTFRfZmx1dHRlcl9sb2NhbF90aHJlc2hvbGRfUzFfYzAocGFyYW1fMTAsIHBhcmFtXzExLCBwYXJhbV8xMik7CglyZXR1cm4gY2xhbXAocywgMC4wLCAxLjApICogMC41NTsKfQp2b2lkIEZMVF9tYWluX1MxX2MwKCkgCnsKCXVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLng7Cgl1X3NwYXJrbGVfYWxwaGFfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLnk7Cgl1X2JsdXJfUzFfYzAgPSB1X2NvbXBvc2l0ZV8xX1MxX2MwLno7Cgl1X3JhZGl1c19zY2FsZV9TMV9jMCA9IHVfY29tcG9zaXRlXzFfUzFfYzAudzsKCWZsb2F0MiBwID0gRkxUX2ZsdXR0ZXJfbG9jYWxfRmx1dHRlckZyYWdDb29yZF9TMV9jMCgpOwoJZmxvYXQyIHV2XzEgPSBwICogdV9yZXNvbHV0aW9uX3NjYWxlX1MxX2MwOwoJZmxvYXQyIGRlbnNpdHlfdXYgPSB1dl8xIC0gbW9kKHAsIHVfbm9pc2Vfc2NhbGVfUzFfYzApOwoJZmxvYXQgcmFkaXVzID0gdV9tYXhfcmFkaXVzX1MxX2MwICogdV9yYWRpdXNfc2NhbGVfUzFfYzA7CglmbG9hdDIgcGFyYW1fMTMgPSB1dl8xOwoJZmxvYXQgdHVyYnVsZW5jZSA9IEZMVF9mbHV0dGVyX2xvY2FsX3R1cmJ1bGVuY2VfUzFfYzAocGFyYW1fMTMpOwoJZmxvYXQyIHBhcmFtXzE0ID0gcDsKCWZsb2F0MiBwYXJhbV8xNSA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMTYgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8xNyA9IDAuMDUgKiB1X21heF9yYWRpdXNfUzFfYzA7CglmbG9hdCBwYXJhbV8xOCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHJpbmcgPSBGTFRfZmx1dHRlcl9sb2NhbF9zb2Z0X3JpbmdfUzFfYzAocGFyYW1fMTQsIHBhcmFtXzE1LCBwYXJhbV8xNiwgcGFyYW1fMTcsIHBhcmFtXzE4KTsKCWZsb2F0MiBwYXJhbV8xOSA9IGRlbnNpdHlfdXY7CglmbG9hdCBwYXJhbV8yMCA9IHVfbm9pc2VfcGhhc2VfUzFfYzA7CglmbG9hdCBzcGFya2xlID0gKChGTFRfZmx1dHRlcl9sb2NhbF9zcGFya2xlX1MxX2MwKHBhcmFtXzE5LCBwYXJhbV8yMCkgKiByaW5nKSAqIHR1cmJ1bGVuY2UpICogdV9zcGFya2xlX2FscGhhX1MxX2MwOwoJZmxvYXQyIHBhcmFtXzIxID0gcDsKCWZsb2F0MiBwYXJhbV8yMiA9IHVfY2VudGVyX1MxX2MwOwoJZmxvYXQgcGFyYW1fMjMgPSByYWRpdXM7CglmbG9hdCBwYXJhbV8yNCA9IHVfYmx1cl9TMV9jMDsKCWZsb2F0IHdhdmVfYWxwaGEgPSAoRkxUX2ZsdXR0ZXJfbG9jYWxfc29mdF9jaXJjbGVfUzFfYzAocGFyYW1fMjEsIHBhcmFtXzIyLCBwYXJhbV8yMywgcGFyYW1fMjQpICogdV9hbHBoYV9TMV9jMCkgKiB1X2NvbG9yX1MxX2MwLnc7CglmbG9hdDQgd2F2ZV9jb2xvciA9IGZsb2F0NCh1X2NvbG9yX1MxX2MwLnh5eiAqIHdhdmVfYWxwaGEsIHdhdmVfYWxwaGEpOwoJZnJhZ0NvbG9yX1MxX2MwID0gbWl4KHdhdmVfY29sb3IsIGZsb2F0NCgxLjApLCBmbG9hdDQoc3BhcmtsZSkpOwp9CmhhbGY0IHJ1bnRpbWVfc2hhZGVyX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWZsdXR0ZXJfRnJhZ0Nvb3JkX1MxX2MwID0gZmxvYXQ0KF90bXBfMV9jb29yZHMsIDAuMCwgMC4wKTsKCUZMVF9tYWluX1MxX2MwKCk7CglyZXR1cm4gaGFsZjQoaGFsZjQoZnJhZ0NvbG9yX1MxX2MwKSk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBydW50aW1lX3NoYWRlcl9TMV9jMChfaW5wdXQpOwp9CmhhbGY0IFRleHR1cmVFZmZlY3RfUzJfYzBfYzBfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMiwgdlRyYW5zZm9ybWVkQ29vcmRzXzZfUzApLjAwMHI7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MyX2MwX2MwKGhhbGY0IF9pbnB1dCkgCnsKCXJldHVybiBUZXh0dXJlRWZmZWN0X1MyX2MwX2MwX2MwKF9pbnB1dCk7Cn0KaGFsZjQgRGV2aWNlU3BhY2VfUzJfYzAoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIE1hdHJpeEVmZmVjdF9TMl9jMF9jMChfaW5wdXQpOwp9CmhhbGY0IEJsZW5kX1MyKGhhbGY0IF9zcmMsIGhhbGY0IF9kc3QpIAp7CglyZXR1cm4gYmxlbmRfZHN0X2luKERldmljZVNwYWNlX1MyX2MwKF9zcmMpLCBfc3JjKTsKfQp2b2lkIG1haW4oKSAKewoJLy8gU3RhZ2UgMCwgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCgloYWxmNCBvdXRwdXRDb2xvcl9TMDsKCW91dHB1dENvbG9yX1MwID0gdmNvbG9yX1MwOwoJY29uc3QgaGFsZjQgb3V0cHV0Q292ZXJhZ2VfUzAgPSBoYWxmNCgxKTsKCWhhbGY0IG91dHB1dF9TMTsKCW91dHB1dF9TMSA9IE1hdHJpeEVmZmVjdF9TMShvdXRwdXRDb2xvcl9TMCk7CgloYWxmNCBvdXRwdXRfUzI7CglvdXRwdXRfUzIgPSBCbGVuZF9TMihvdXRwdXRDb3ZlcmFnZV9TMCwgaGFsZjQoMSkpOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dF9TMjsKCX0KfQoAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAgAAABwb3NpdGlvbgUAAABjb2xvcgAAAAoAAABsb2NhbENvb3JkAAAAAAAA","HUIAAAAAAAQAADAAAIOAAAH677777777777QGHAQAD7P7777777777YBAAAAAAAAAAALUAQJALJKL2AAAAAAAAAEAAAABSCQQKAHIKJ2AAAAAAAAAEAAAABLBAABAAAAABAEGABBAMAAAAAAAAAAAOQAAAAAAAQAAAABAMQAAAAAA":"DAAAAExTS1M2AQAAdW5pZm9ybSBmbG9hdDQgc2tfUlRBZGp1c3Q7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMTsKaW4gZmxvYXQyIHBvc2l0aW9uOwppbiBmbG9hdDIgbG9jYWxDb29yZDsKb3V0IGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKdm9pZCBtYWluKCkgCnsKCS8vIFByaW1pdGl2ZSBQcm9jZXNzb3IgUXVhZFBlckVkZ2VBQUdlb21ldHJ5UHJvY2Vzc29yCglza19Qb3NpdGlvbiA9IHBvc2l0aW9uLnh5MDE7Cgl7CgkJdlRyYW5zZm9ybWVkQ29vcmRzXzJfUzAgPSBmbG9hdDN4Mih1bWF0cml4X1MxKSAqIGxvY2FsQ29vcmQueHkxOwoJfQp9CgAAAAAAACoFAABjb25zdCBpbnQga01heExvb3BMaW1pdF9TMV9jMCA9IDY7CnVuaWZvcm0gZmxvYXQzeDMgdW1hdHJpeF9TMV9jMF9jMDsKdW5pZm9ybSBoYWxmNCB1b2Zmc2V0c0FuZEtlcm5lbF9TMV9jMFsxNF07CnVuaWZvcm0gaGFsZjIgdWRpcl9TMV9jMDsKdW5pZm9ybSBmbG9hdDN4MyB1bWF0cml4X1MxOwpzYW1wbGVyMkQgdVRleHR1cmVTYW1wbGVyXzBfUzE7CmluIGZsb2F0MiB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKaGFsZjQgVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChoYWxmNCBfaW5wdXQsIGZsb2F0MiBfY29vcmRzKSAKewoJcmV0dXJuIHNhbXBsZSh1VGV4dHVyZVNhbXBsZXJfMF9TMSwgX2Nvb3Jkcyk7Cn0KaGFsZjQgTWF0cml4RWZmZWN0X1MxX2MwX2MwKGhhbGY0IF9pbnB1dCwgZmxvYXQyIF9jb29yZHMpIAp7CglyZXR1cm4gVGV4dHVyZUVmZmVjdF9TMV9jMF9jMF9jMChfaW5wdXQsIGZsb2F0M3gyKHVtYXRyaXhfUzFfYzBfYzApICogX2Nvb3Jkcy54eTEpOwp9CmhhbGY0IEdhdXNzaWFuQmx1cjFEX1MxX2MwKGhhbGY0IF9pbnB1dCkgCnsKCWhhbGY0IF90bXBfMF9pbkNvbG9yID0gX2lucHV0OwoJZmxvYXQyIF90bXBfMV9jb29yZHMgPSB2VHJhbnNmb3JtZWRDb29yZHNfMl9TMDsKCWhhbGY0IHN1bSA9IGhhbGY0KDAuMCk7Cglmb3IgKGludCBpID0gMDtpIDwga01heExvb3BMaW1pdF9TMV9jMDsgKytpKSAKCXsKCQloYWxmNCBzID0gdW9mZnNldHNBbmRLZXJuZWxfUzFfYzBbaV07CgkJc3VtICs9IHMueSAqIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgX3RtcF8xX2Nvb3JkcyArIGZsb2F0MihzLnggKiB1ZGlyX1MxX2MwKSk7CgkJc3VtICs9IHMudyAqIE1hdHJpeEVmZmVjdF9TMV9jMF9jMChfdG1wXzBfaW5Db2xvciwgX3RtcF8xX2Nvb3JkcyArIGZsb2F0MihzLnogKiB1ZGlyX1MxX2MwKSk7Cgl9CglyZXR1cm4gaGFsZjQoc3VtKTsKfQpoYWxmNCBNYXRyaXhFZmZlY3RfUzEoaGFsZjQgX2lucHV0KSAKewoJcmV0dXJuIEdhdXNzaWFuQmx1cjFEX1MxX2MwKF9pbnB1dCk7Cn0Kdm9pZCBtYWluKCkgCnsKCS8vIFN0YWdlIDAsIFF1YWRQZXJFZGdlQUFHZW9tZXRyeVByb2Nlc3NvcgoJaGFsZjQgb3V0cHV0Q29sb3JfUzAgPSBoYWxmNCgxKTsKCWNvbnN0IGhhbGY0IG91dHB1dENvdmVyYWdlX1MwID0gaGFsZjQoMSk7CgloYWxmNCBvdXRwdXRfUzE7CglvdXRwdXRfUzEgPSBNYXRyaXhFZmZlY3RfUzEob3V0cHV0Q29sb3JfUzApOwoJewoJCS8vIFhmZXIgUHJvY2Vzc29yOiBQb3J0ZXIgRHVmZgoJCXNrX0ZyYWdDb2xvciA9IG91dHB1dF9TMSAqIG91dHB1dENvdmVyYWdlX1MwOwoJfQp9CgAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAIAAAAcG9zaXRpb24KAAAAbG9jYWxDb29yZAAAAAAAAA=="}} \ No newline at end of file diff --git a/test/utils/xmp_utils_test.dart b/test/utils/xmp_utils_test.dart index 0374f1467..d45e13e74 100644 --- a/test/utils/xmp_utils_test.dart +++ b/test/utils/xmp_utils_test.dart @@ -146,7 +146,7 @@ void main() { expect( _toExpect(await XMP.edit( null, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editTagsXmp(descriptions, {'one', 'two'}), modifyDate: modifyDate, )), @@ -177,7 +177,7 @@ void main() { expect( _toExpect(await XMP.edit( inMultiDescriptionRatings, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editTagsXmp(descriptions, {'one', 'two'}), modifyDate: modifyDate, )), @@ -212,7 +212,7 @@ void main() { expect( _toExpect(await XMP.edit( inSubjects, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editTagsXmp(descriptions, {'one', 'two'}), modifyDate: modifyDate, )), @@ -240,7 +240,7 @@ void main() { expect( _toExpect(await XMP.edit( inSubjects, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editTagsXmp(descriptions, {}), )), _toExpect(null)); @@ -253,7 +253,7 @@ void main() { expect( _toExpect(await XMP.edit( inSubjectsCreator, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editTagsXmp(descriptions, {}), modifyDate: modifyDate, )), @@ -283,7 +283,7 @@ void main() { expect( _toExpect(await XMP.edit( null, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editRatingXmp(descriptions, 3), modifyDate: modifyDate, )), @@ -307,7 +307,7 @@ void main() { expect( _toExpect(await XMP.edit( inMultiDescriptionRatings, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editRatingXmp(descriptions, 3), modifyDate: modifyDate, )), @@ -333,7 +333,7 @@ void main() { expect( _toExpect(await XMP.edit( inRatingAttribute, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editRatingXmp(descriptions, 3), modifyDate: modifyDate, )), @@ -357,7 +357,7 @@ void main() { expect( _toExpect(await XMP.edit( inRatingElement, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editRatingXmp(descriptions, 3), modifyDate: modifyDate, )), @@ -381,7 +381,7 @@ void main() { expect( _toExpect(await XMP.edit( inSubjects, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editRatingXmp(descriptions, 3), modifyDate: modifyDate, )), @@ -411,7 +411,7 @@ void main() { expect( _toExpect(await XMP.edit( inSubjects, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editRatingXmp(descriptions, null), modifyDate: modifyDate, )), @@ -424,7 +424,7 @@ void main() { expect( _toExpect(await XMP.edit( inMotionPhotoMicroVideo, - () async => toolkit, + toolkit, ExtraAvesEntryMetadataEdition.removeContainerXmp, modifyDate: modifyDate, )), @@ -437,7 +437,7 @@ void main() { expect( _toExpect(await XMP.edit( inMotionPhotoContainer, - () async => toolkit, + toolkit, ExtraAvesEntryMetadataEdition.removeContainerXmp, modifyDate: modifyDate, )), @@ -450,7 +450,7 @@ void main() { expect( _toExpect(await XMP.edit( inSubjects, - () async => toolkit, + toolkit, ExtraAvesEntryMetadataEdition.removeContainerXmp, modifyDate: modifyDate, )), @@ -463,7 +463,7 @@ void main() { expect( _toExpect(await XMP.edit( inMultiDescriptionRatings, - () async => toolkit, + toolkit, (descriptions) => ExtraAvesEntryMetadataEdition.editRatingXmp(descriptions, null), modifyDate: modifyDate, )), diff --git a/untranslated.json b/untranslated.json index cb38526b1..1421f892a 100644 --- a/untranslated.json +++ b/untranslated.json @@ -51,6 +51,7 @@ "entryActionEdit", "entryActionOpen", "entryActionSetAs", + "entryActionCast", "entryActionOpenMap", "entryActionRotateScreen", "entryActionAddFavourite", @@ -286,6 +287,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -307,6 +309,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -643,31 +646,7 @@ ], "be": [ - "binEntriesConfirmationDialogMessage", - "deleteEntriesConfirmationDialogMessage", - "setCoverDialogCustom", - "hideFilterConfirmationDialogMessage", - "newVaultWarningDialogMessage", - "vaultBinUsageDialogMessage", - "renameAlbumDialogLabel", - "renameAlbumDialogLabelAlreadyExistsHelper", - "renameEntrySetPageTitle", - "renameEntrySetPagePatternFieldLabel", - "renameEntrySetPageInsertTooltip", - "renameEntrySetPagePreviewSectionTitle", - "renameProcessorCounter", - "renameProcessorName", - "deleteSingleAlbumConfirmationDialogMessage", "deleteMultiAlbumConfirmationDialogMessage", - "exportEntryDialogFormat", - "exportEntryDialogWidth", - "exportEntryDialogHeight", - "exportEntryDialogQuality", - "exportEntryDialogWriteMetadata", - "renameEntryDialogLabel", - "editEntryDialogCopyFromItem", - "editEntryDialogTargetFieldsHeader", - "editEntryDateDialogTitle", "editEntryDateDialogSetCustom", "editEntryDateDialogCopyField", "editEntryDateDialogExtractFromTitle", @@ -711,6 +690,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -719,15 +699,8 @@ "aboutPageTitle", "aboutLinkLicense", "aboutLinkPolicy", - "aboutBugSectionTitle", - "aboutBugSaveLogInstruction", - "aboutBugCopyInfoInstruction", - "aboutBugCopyInfoButton", - "aboutBugReportInstruction", - "aboutBugReportButton", "aboutDataUsageSectionTitle", "aboutDataUsageData", - "aboutDataUsageCache", "aboutDataUsageDatabase", "aboutDataUsageMisc", "aboutDataUsageInternal", @@ -847,7 +820,6 @@ "searchRatingSectionTitle", "searchMetadataSectionTitle", "settingsPageTitle", - "settingsSystemDefault", "settingsDefault", "settingsDisabled", "settingsAskEverytime", @@ -993,37 +965,7 @@ "settingsStorageAccessRevokeTooltip", "settingsAccessibilitySectionTitle", "settingsRemoveAnimationsTile", - "settingsRemoveAnimationsDialogTitle", - "settingsTimeToTakeActionTile", - "settingsAccessibilityShowPinchGestureAlternatives", - "settingsDisplaySectionTitle", - "settingsThemeBrightnessTile", - "settingsThemeBrightnessDialogTitle", - "settingsThemeColorHighlights", - "settingsThemeEnableDynamicColor", - "settingsDisplayRefreshRateModeTile", - "settingsDisplayRefreshRateModeDialogTitle", - "settingsDisplayUseTvInterface", - "settingsLanguageSectionTitle", - "settingsLanguageTile", - "settingsLanguagePageTitle", - "settingsCoordinateFormatTile", - "settingsCoordinateFormatDialogTitle", - "settingsUnitSystemTile", - "settingsUnitSystemDialogTitle", - "settingsScreenSaverPageTitle", - "settingsWidgetPageTitle", - "settingsWidgetShowOutline", - "settingsWidgetOpenPage", - "settingsWidgetDisplayedItem", - "settingsCollectionTile", - "statsPageTitle", - "statsWithGps", - "statsTopCountriesSectionTitle", - "statsTopStatesSectionTitle", - "viewerOpenPanoramaButtonLabel", - "viewerInfoLabelUri", - "viewerInfoSearchSuggestionRights" + "statsWithGps" ], "bn": [ @@ -1075,6 +1017,7 @@ "entryActionEdit", "entryActionOpen", "entryActionSetAs", + "entryActionCast", "entryActionOpenMap", "entryActionRotateScreen", "entryActionAddFavourite", @@ -1310,6 +1253,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -1331,6 +1275,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -1686,6 +1631,7 @@ "entryActionFlip", "entryActionShowGeoTiffOnMap", "entryActionConvertMotionPhotoToStillImage", + "entryActionCast", "videoActionCaptureFrame", "videoActionSelectStreams", "viewerActionLock", @@ -1843,6 +1789,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -1864,6 +1811,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -2213,21 +2161,20 @@ "filePickerUseThisFolder" ], - "cs": [ - "overlayHistogramLuminance" - ], - "de": [ + "entryActionCast", "overlayHistogramNone", - "overlayHistogramRGB", - "overlayHistogramLuminance", + "castDialogTitle", + "aboutDataUsageClearCache", "settingsViewerShowHistogram" ], "el": [ + "entryActionCast", "overlayHistogramNone", "overlayHistogramRGB", "overlayHistogramLuminance", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -2235,45 +2182,15 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "settingsViewerShowHistogram" ], "fa": [ - "saveCopyButtonLabel", - "applyTooltip", - "clearTooltip", - "chipActionGoToPlacePage", - "chipActionLock", - "chipActionShowCountryStates", - "chipActionCreateVault", - "chipActionConfigureVault", - "videoActionPause", - "videoActionPlay", - "videoActionSelectStreams", - "viewerActionLock", - "viewerActionUnlock", - "slideshowActionResume", - "editorActionTransform", - "editorTransformCrop", - "editorTransformRotate", - "cropAspectRatioFree", - "cropAspectRatioOriginal", - "cropAspectRatioSquare", - "filterAspectRatioLandscapeLabel", - "filterAspectRatioPortraitLabel", "filterLocatedLabel", "filterTaggedLabel", "filterRatingRejectedLabel", - "filterTypeAnimatedLabel", - "filterTypeRawLabel", - "filterTypeGeotiffLabel", - "accessibilityAnimationsRemove", - "accessibilityAnimationsKeep", - "albumTierNew", - "albumTierPinned", "albumTierSpecial", - "albumTierVaults", - "albumTierRegular", "coordinateFormatDms", "coordinateFormatDecimal", "coordinateDms", @@ -2281,44 +2198,11 @@ "coordinateDmsSouth", "coordinateDmsEast", "coordinateDmsWest", - "keepScreenOnNever", - "keepScreenOnVideoPlayback", - "keepScreenOnViewerOnly", - "keepScreenOnAlways", - "lengthUnitPixel", - "lengthUnitPercent", - "maxBrightnessNever", - "maxBrightnessAlways", - "nameConflictStrategySkip", "overlayHistogramNone", "overlayHistogramRGB", "overlayHistogramLuminance", - "vaultLockTypePattern", - "vaultLockTypePin", - "vaultLockTypePassword", - "settingsVideoEnablePip", - "videoControlsNone", "videoPlaybackSkip", - "videoResumptionModeNever", - "videoResumptionModeAlways", - "viewerTransitionSlide", "viewerTransitionParallax", - "viewerTransitionFade", - "viewerTransitionZoomIn", - "viewerTransitionNone", - "wallpaperTargetHome", - "wallpaperTargetLock", - "wallpaperTargetHomeLock", - "widgetDisplayedItemRandom", - "widgetDisplayedItemMostRecent", - "widgetOpenPageHome", - "widgetOpenPageCollection", - "widgetOpenPageViewer", - "widgetTapUpdateWidget", - "rootDirectoryDescription", - "otherDirectoryDescription", - "restrictedAccessDialogMessage", - "notEnoughSpaceDialogMessage", "missingSystemFilePickerDialogMessage", "unsupportedTypeDialogMessage", "nameConflictDialogSingleSourceMessage", @@ -2402,12 +2286,12 @@ "menuActionSelect", "menuActionSelectAll", "menuActionSelectNone", - "viewDialogSortSectionTitle", "viewDialogLayoutSectionTitle", "viewDialogReverseSortOrder", "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "appPickDialogNone", "aboutLinkLicense", @@ -2421,6 +2305,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -2758,6 +2643,7 @@ "clearTooltip", "chipActionFilterIn", "entryActionSetAs", + "entryActionCast", "videoActionUnmute", "videoActionSelectStreams", "filterTypeRawLabel", @@ -2921,6 +2807,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -2942,6 +2829,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -3302,6 +3190,7 @@ "chipActionConfigureVault", "entryActionShareImageOnly", "entryActionShareVideoOnly", + "entryActionCast", "viewerActionLock", "viewerActionUnlock", "entryInfoActionExportMetadata", @@ -3468,6 +3357,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -3489,6 +3379,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -3908,6 +3799,7 @@ "entryActionEdit", "entryActionOpen", "entryActionSetAs", + "entryActionCast", "entryActionOpenMap", "entryActionRotateScreen", "entryActionAddFavourite", @@ -4143,6 +4035,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -4164,6 +4057,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -4563,6 +4457,7 @@ "entryActionEdit", "entryActionOpen", "entryActionSetAs", + "entryActionCast", "entryActionOpenMap", "entryActionRotateScreen", "entryActionAddFavourite", @@ -4798,6 +4693,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -4819,6 +4715,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -5168,6 +5065,17 @@ "filePickerUseThisFolder" ], + "id": [ + "entryActionCast", + "castDialogTitle" + ], + + "it": [ + "entryActionCast", + "castDialogTitle", + "aboutDataUsageClearCache" + ], + "ja": [ "columnCount", "saveCopyButtonLabel", @@ -5175,6 +5083,7 @@ "chipActionShowCountryStates", "chipActionCreateVault", "chipActionConfigureVault", + "entryActionCast", "viewerActionLock", "viewerActionUnlock", "editorActionTransform", @@ -5198,6 +5107,7 @@ "vaultBinUsageDialogMessage", "exportEntryDialogQuality", "exportEntryDialogWriteMetadata", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -5205,6 +5115,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "stateEmpty", "placeEmpty", "searchStatesSectionTitle", @@ -5273,6 +5184,7 @@ "entryActionEdit", "entryActionOpen", "entryActionSetAs", + "entryActionCast", "entryActionOpenMap", "entryActionRotateScreen", "entryActionAddFavourite", @@ -5508,6 +5420,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -5529,6 +5442,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -5887,6 +5801,7 @@ "chipActionShowCountryStates", "chipActionCreateVault", "chipActionConfigureVault", + "entryActionCast", "viewerActionLock", "viewerActionUnlock", "editorActionTransform", @@ -5930,6 +5845,7 @@ "exportEntryDialogQuality", "exportEntryDialogWriteMetadata", "tooManyItemsErrorDialogMessage", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -5937,6 +5853,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "drawerPlacePage", "statePageTitle", "stateEmpty", @@ -6039,6 +5956,7 @@ "entryActionEdit", "entryActionOpen", "entryActionSetAs", + "entryActionCast", "entryActionOpenMap", "entryActionRotateScreen", "entryActionAddFavourite", @@ -6274,6 +6192,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -6295,6 +6214,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -6645,6 +6565,7 @@ ], "my": [ + "entryActionCast", "accessibilityAnimationsRemove", "accessibilityAnimationsKeep", "overlayHistogramLuminance", @@ -6652,6 +6573,8 @@ "widgetOpenPageCollection", "widgetOpenPageViewer", "menuActionConfigureView", + "castDialogTitle", + "aboutDataUsageClearCache", "newFilterBanner", "settingsDefault", "settingsNavigationDrawerTile", @@ -6754,6 +6677,7 @@ ], "nb": [ + "entryActionCast", "viewerActionLock", "viewerActionUnlock", "editorActionTransform", @@ -6765,8 +6689,10 @@ "settingsVideoEnablePip", "widgetTapUpdateWidget", "patternDialogEnter", + "castDialogTitle", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "settingsCollectionBurstPatternsTile", "settingsCollectionBurstPatternsNone", "settingsViewerShowHistogram", @@ -6780,6 +6706,7 @@ "chipActionShowCountryStates", "entryActionShareImageOnly", "entryActionShareVideoOnly", + "entryActionCast", "viewerActionLock", "viewerActionUnlock", "editorActionTransform", @@ -6818,6 +6745,7 @@ "exportEntryDialogQuality", "exportEntryDialogWriteMetadata", "tooManyItemsErrorDialogMessage", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -6825,6 +6753,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "drawerPlacePage", "statePageTitle", "stateEmpty", @@ -6858,6 +6787,7 @@ "nn": [ "sourceStateCataloguing", + "entryActionCast", "accessibilityAnimationsKeep", "overlayHistogramNone", "overlayHistogramRGB", @@ -6868,6 +6798,7 @@ "authenticateToUnlockVault", "viewDialogSortSectionTitle", "viewDialogReverseSortOrder", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -6875,6 +6806,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutLicensesBanner", "aboutLicensesAndroidLibrariesSectionTitle", @@ -6958,6 +6890,7 @@ "entryActionViewMotionPhotoVideo", "entryActionOpen", "entryActionSetAs", + "entryActionCast", "entryActionOpenMap", "entryActionRotateScreen", "entryActionAddFavourite", @@ -7157,14 +7090,12 @@ "menuActionSelectNone", "menuActionMap", "menuActionSlideshow", - "menuActionStats", - "viewDialogSortSectionTitle", - "viewDialogGroupSectionTitle", "viewDialogLayoutSectionTitle", "viewDialogReverseSortOrder", "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "appPickDialogTitle", "appPickDialogNone", @@ -7182,6 +7113,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", "aboutLicensesSectionTitle", @@ -7502,9 +7434,16 @@ "filePickerUseThisFolder" ], + "pt": [ + "entryActionCast", + "castDialogTitle", + "aboutDataUsageClearCache" + ], + "ro": [ "saveCopyButtonLabel", "applyTooltip", + "entryActionCast", "editorActionTransform", "editorTransformCrop", "editorTransformRotate", @@ -7520,6 +7459,7 @@ "videoResumptionModeAlways", "widgetTapUpdateWidget", "exportEntryDialogQuality", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -7527,6 +7467,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "settingsAskEverytime", "settingsViewerShowHistogram", "settingsVideoPlaybackTile", @@ -7606,6 +7547,7 @@ "entryActionEdit", "entryActionOpen", "entryActionSetAs", + "entryActionCast", "entryActionOpenMap", "entryActionRotateScreen", "entryActionAddFavourite", @@ -7841,6 +7783,7 @@ "tileLayoutMosaic", "tileLayoutGrid", "tileLayoutList", + "castDialogTitle", "coverDialogTabCover", "coverDialogTabApp", "coverDialogTabColor", @@ -7862,6 +7805,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "aboutCreditsSectionTitle", "aboutCreditsWorldAtlas1", "aboutCreditsWorldAtlas2", @@ -8226,6 +8170,7 @@ "chipActionShowCountryStates", "chipActionCreateVault", "chipActionConfigureVault", + "entryActionCast", "viewerActionLock", "viewerActionUnlock", "editorActionTransform", @@ -8269,6 +8214,7 @@ "editEntryDateDialogShift", "removeEntryMetadataDialogTitle", "tooManyItemsErrorDialogMessage", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -8276,6 +8222,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "collectionActionShowTitleSearch", "collectionActionHideTitleSearch", "collectionActionAddShortcut", @@ -8618,6 +8565,7 @@ "chipActionShowCountryStates", "chipActionCreateVault", "chipActionConfigureVault", + "entryActionCast", "viewerActionLock", "viewerActionUnlock", "editorActionTransform", @@ -8657,6 +8605,7 @@ "vaultBinUsageDialogMessage", "exportEntryDialogQuality", "exportEntryDialogWriteMetadata", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -8664,6 +8613,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "drawerPlacePage", "statePageTitle", "stateEmpty", @@ -8688,8 +8638,8 @@ ], "zh": [ - "saveCopyButtonLabel", "chipActionGoToPlacePage", + "entryActionCast", "editorTransformCrop", "cropAspectRatioFree", "cropAspectRatioOriginal", @@ -8721,6 +8671,7 @@ "exportEntryDialogQuality", "exportEntryDialogWriteMetadata", "tooManyItemsErrorDialogMessage", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -8728,6 +8679,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "drawerPlacePage", "statePageTitle", "stateEmpty", @@ -8755,8 +8707,10 @@ ], "zh_Hant": [ + "entryActionCast", "overlayHistogramNone", "overlayHistogramLuminance", + "castDialogTitle", "aboutDataUsageSectionTitle", "aboutDataUsageData", "aboutDataUsageCache", @@ -8764,6 +8718,7 @@ "aboutDataUsageMisc", "aboutDataUsageInternal", "aboutDataUsageExternal", + "aboutDataUsageClearCache", "settingsViewerShowHistogram" ] } diff --git a/whatsnew/whatsnew-en-US b/whatsnew/whatsnew-en-US index 59b146938..3c18fca39 100644 --- a/whatsnew/whatsnew-en-US +++ b/whatsnew/whatsnew-en-US @@ -1,3 +1,4 @@ -In v1.9.7: -- enjoy the app in Slovak & Vietnamese +In v1.10.0: +- cast images via DLNA/UPnP +- enjoy the app in Icelandic Full changelog available on GitHub \ No newline at end of file