diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 6c51209fe..68c6ea2d4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -80,7 +80,7 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload app bundle
- uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1
+ uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: appbundle
path: outputs/app-play-release.aab
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 7bbfaa3a0..6e2676543 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -63,7 +63,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
- uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1
+ uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
with:
name: SARIF file
path: results.sarif
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ab9f589fa..cf952d08f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+## [v1.11.16] - 2024-10-10
+
+### Fixed
+
+- case-insensitive access to restricted directories
+
## [v1.11.15] - 2024-10-09
### Changed
diff --git a/android/app/src/main/res/values-id/strings.xml b/android/app/src/main/res/values-id/strings.xml
index 421cf304c..13708b582 100644
--- a/android/app/src/main/res/values-id/strings.xml
+++ b/android/app/src/main/res/values-id/strings.xml
@@ -8,4 +8,5 @@
Pindai media
Memindai media
Berhenti
+ Peta
\ No newline at end of file
diff --git a/android/app/src/main/res/values-lt/strings.xml b/android/app/src/main/res/values-lt/strings.xml
index d20202da5..c8c6027a6 100644
--- a/android/app/src/main/res/values-lt/strings.xml
+++ b/android/app/src/main/res/values-lt/strings.xml
@@ -8,4 +8,5 @@
Paieška
Sustabdyti
Nuskaitoma medija
+ Žemėlapis
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/135.txt b/fastlane/metadata/android/en-US/changelogs/135.txt
new file mode 100644
index 000000000..3d9256a35
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/135.txt
@@ -0,0 +1,4 @@
+In v1.11.16:
+- enjoy new map layers
+- share "geo" addresses to Aves and see your collection in that area
+Full changelog available on GitHub
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/13501.txt b/fastlane/metadata/android/en-US/changelogs/13501.txt
new file mode 100644
index 000000000..3d9256a35
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/13501.txt
@@ -0,0 +1,4 @@
+In v1.11.16:
+- enjoy new map layers
+- share "geo" addresses to Aves and see your collection in that area
+Full changelog available on GitHub
\ No newline at end of file
diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb
index bbbb43724..22b5ae442 100644
--- a/lib/l10n/app_id.arb
+++ b/lib/l10n/app_id.arb
@@ -1384,5 +1384,13 @@
"chipActionGoToExplorerPage": "Tampilkan di Explorer",
"@chipActionGoToExplorerPage": {},
"mapAttributionOsmData": "Data peta © [OpenStreetMap](https://www.openstreetmap.org/copyright) kontributor",
- "@mapAttributionOsmData": {}
+ "@mapAttributionOsmData": {},
+ "mapStyleOpenTopoMap": "OpenTopoMap",
+ "@mapStyleOpenTopoMap": {},
+ "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Ubin oleh [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)",
+ "@mapAttributionOpenTopoMap": {},
+ "mapStyleOsmLiberty": "OSM Liberty",
+ "@mapStyleOsmLiberty": {},
+ "mapAttributionOsmLiberty": "Ubin oleh [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Disediakan oleh [OSM Americana](https://tile.ourmap.us)",
+ "@mapAttributionOsmLiberty": {}
}
diff --git a/lib/l10n/app_lt.arb b/lib/l10n/app_lt.arb
index 320552585..f46f64c72 100644
--- a/lib/l10n/app_lt.arb
+++ b/lib/l10n/app_lt.arb
@@ -1332,5 +1332,217 @@
"chipActionShowCollection": "Rodyti kolekcijoje",
"@chipActionShowCollection": {},
"mapAttributionOsmData": "Žemėlapio duomenys © [OpenStreetMap](https://www.openstreetmap.org/copyright) bendradarbiai",
- "@mapAttributionOsmData": {}
+ "@mapAttributionOsmData": {},
+ "filterLocatedLabel": "Lokalizuota",
+ "@filterLocatedLabel": {},
+ "aboutDataUsageSectionTitle": "Duomenų naudojimas",
+ "@aboutDataUsageSectionTitle": {},
+ "chipActionConfigureVault": "Konfigūruoti saugyklą",
+ "@chipActionConfigureVault": {},
+ "chipActionGoToPlacePage": "Rodyti Vietose",
+ "@chipActionGoToPlacePage": {},
+ "chipActionLock": "Rakinti",
+ "@chipActionLock": {},
+ "chipActionShowCountryStates": "Rodyti valstybes",
+ "@chipActionShowCountryStates": {},
+ "entryActionCast": "Transliuoti",
+ "@entryActionCast": {},
+ "saveCopyButtonLabel": "IŠSAUGOTI KOPIJĄ",
+ "@saveCopyButtonLabel": {},
+ "applyTooltip": "Taikyti",
+ "@applyTooltip": {},
+ "editorActionTransform": "Transformuoti",
+ "@editorActionTransform": {},
+ "editorTransformCrop": "Apkirpti",
+ "@editorTransformCrop": {},
+ "editorTransformRotate": "Pasukti",
+ "@editorTransformRotate": {},
+ "cropAspectRatioFree": "Laisvai",
+ "@cropAspectRatioFree": {},
+ "cropAspectRatioOriginal": "Originalas",
+ "@cropAspectRatioOriginal": {},
+ "widgetTapUpdateWidget": "Atnaujinti valdiklį",
+ "@widgetTapUpdateWidget": {},
+ "passwordDialogEnter": "Įveskite slaptažodį",
+ "@passwordDialogEnter": {},
+ "settingsAccessibilityShowPinchGestureAlternatives": "Rodyti kelių palietimų gestų alternatyvas",
+ "@settingsAccessibilityShowPinchGestureAlternatives": {},
+ "chipActionCreateVault": "Kurti saugyklą",
+ "@chipActionCreateVault": {},
+ "filterTaggedLabel": "Pažymėti",
+ "@filterTaggedLabel": {},
+ "configureVaultDialogTitle": "Konfigūruoti saugyklą",
+ "@configureVaultDialogTitle": {},
+ "settingsVideoPlaybackTile": "Atkūrimas",
+ "@settingsVideoPlaybackTile": {},
+ "vaultDialogLockTypeLabel": "Užrakto tipas",
+ "@vaultDialogLockTypeLabel": {},
+ "aboutDataUsageData": "Duomenys",
+ "@aboutDataUsageData": {},
+ "vaultDialogLockModeWhenScreenOff": "Užrakinti kai ekranas išsijungia",
+ "@vaultDialogLockModeWhenScreenOff": {},
+ "pinDialogConfirm": "Konfigūruoti PIN",
+ "@pinDialogConfirm": {},
+ "patternDialogEnter": "Įveskite paveikslą",
+ "@patternDialogEnter": {},
+ "pinDialogEnter": "Įveskite PIN",
+ "@pinDialogEnter": {},
+ "passwordDialogConfirm": "Patvirtinti slaptažodį",
+ "@passwordDialogConfirm": {},
+ "renameProcessorHash": "Maiša",
+ "@renameProcessorHash": {},
+ "aboutDataUsageInternal": "Vidaus",
+ "@aboutDataUsageInternal": {},
+ "aboutDataUsageCache": "Talpykla",
+ "@aboutDataUsageCache": {},
+ "sortByDuration": "Pagal trukmę",
+ "@sortByDuration": {},
+ "sortOrderShortestFirst": "Trumpiausi pirmiausiai",
+ "@sortOrderShortestFirst": {},
+ "selectStorageVolumeDialogTitle": "Pasirinkti saugyklą",
+ "@selectStorageVolumeDialogTitle": {},
+ "searchStatesSectionTitle": "Valstybės",
+ "@searchStatesSectionTitle": {},
+ "settingsVideoGestureVerticalDragBrightnessVolume": "Braukite į viršų ar apačią kad keisti šviesumą/garsumą",
+ "@settingsVideoGestureVerticalDragBrightnessVolume": {},
+ "settingsDisablingBinWarningDialogMessage": "Daiktai šiukšlių dėžėje bus ištrinti visam laikui.",
+ "@settingsDisablingBinWarningDialogMessage": {},
+ "settingsDisplayUseTvInterface": "Android TV vaizdas",
+ "@settingsDisplayUseTvInterface": {},
+ "settingsForceWesternArabicNumeralsTile": "Priversti arabų skaitmenys",
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "albumTierVaults": "Seifai",
+ "@albumTierVaults": {},
+ "tooManyItemsErrorDialogMessage": "Bandykite vėl su mažiau daiktų.",
+ "@tooManyItemsErrorDialogMessage": {},
+ "castDialogTitle": "Transliavimo įrenginiai",
+ "@castDialogTitle": {},
+ "aboutDataUsageExternal": "Išorinė",
+ "@aboutDataUsageExternal": {},
+ "statePageTitle": "Valstybės",
+ "@statePageTitle": {},
+ "settingsAskEverytime": "Klausti kiekvieną kartą",
+ "@settingsAskEverytime": {},
+ "settingsCollectionBurstPatternsTile": "Burst modeliai",
+ "@settingsCollectionBurstPatternsTile": {},
+ "settingsCollectionBurstPatternsNone": "Jokio",
+ "@settingsCollectionBurstPatternsNone": {},
+ "settingsViewerShowDescription": "Rodyti aprašymą",
+ "@settingsViewerShowDescription": {},
+ "settingsVideoBackgroundMode": "Fono grojimas",
+ "@settingsVideoBackgroundMode": {},
+ "settingsVideoResumptionModeTile": "Tęsti atkūrimą",
+ "@settingsVideoResumptionModeTile": {},
+ "aboutDataUsageDatabase": "Duombazė",
+ "@aboutDataUsageDatabase": {},
+ "settingsModificationWarningDialogMessage": "Kiti nustatymai bus pakeisti.",
+ "@settingsModificationWarningDialogMessage": {},
+ "patternDialogConfirm": "Konfigūruoti paveikslą",
+ "@patternDialogConfirm": {},
+ "lengthUnitPixel": "px",
+ "@lengthUnitPixel": {},
+ "lengthUnitPercent": "%",
+ "@lengthUnitPercent": {},
+ "collectionActionSetHome": "Nustatyti kaip namus",
+ "@collectionActionSetHome": {},
+ "drawerPlacePage": "Vietos",
+ "@drawerPlacePage": {},
+ "placeEmpty": "Jokių vietų",
+ "@placeEmpty": {},
+ "settingsViewerShowHistogram": "Rodyti histogramą",
+ "@settingsViewerShowHistogram": {},
+ "settingsVideoEnablePip": "Paveikslas paveiksle",
+ "@settingsVideoEnablePip": {},
+ "settingsConfirmationVaultDataLoss": "Rodyti saugyklos duomenų praradimo ispėjimą",
+ "@settingsConfirmationVaultDataLoss": {},
+ "aboutDataUsageClearCache": "Valyti talpyklą",
+ "@aboutDataUsageClearCache": {},
+ "statsTopStatesSectionTitle": "Pagrindinės valstybės",
+ "@statsTopStatesSectionTitle": {},
+ "mapStyleOsmLiberty": "OSM Liberty",
+ "@mapStyleOsmLiberty": {},
+ "mapStyleOpenTopoMap": "OpenTopoMap",
+ "@mapStyleOpenTopoMap": {},
+ "maxBrightnessNever": "Niekada",
+ "@maxBrightnessNever": {},
+ "maxBrightnessAlways": "Visada",
+ "@maxBrightnessAlways": {},
+ "overlayHistogramNone": "Jokios",
+ "@overlayHistogramNone": {},
+ "overlayHistogramRGB": "RGB",
+ "@overlayHistogramRGB": {},
+ "overlayHistogramLuminance": "Skaistis",
+ "@overlayHistogramLuminance": {},
+ "vaultLockTypePassword": "Slaptažodis",
+ "@vaultLockTypePassword": {},
+ "videoResumptionModeNever": "Niekada",
+ "@videoResumptionModeNever": {},
+ "videoResumptionModeAlways": "Visada",
+ "@videoResumptionModeAlways": {},
+ "newVaultWarningDialogMessage": "Daiktai saugyklose yra prieinami tik šiai programėlei ir jokiom kitom.\n\nJei pašalinsite šią programą arba išvalysite jos duomenis, jūs prarasite visus šiuos daiktus.",
+ "@newVaultWarningDialogMessage": {},
+ "authenticateToConfigureVault": "Autentifikuoti, kad konfigūruoti saugyklą",
+ "@authenticateToConfigureVault": {},
+ "authenticateToUnlockVault": "Autentifikuoti, kad atrakinti saugyklą",
+ "@authenticateToUnlockVault": {},
+ "vaultBinUsageDialogMessage": "Kai kurios saugyklos naudoja šiukšlių dėžę.",
+ "@vaultBinUsageDialogMessage": {},
+ "exportEntryDialogQuality": "Kokybė",
+ "@exportEntryDialogQuality": {},
+ "sortOrderLongestFirst": "Ilgiausi pirmiausiai",
+ "@sortOrderLongestFirst": {},
+ "stateEmpty": "Jokių valstybių",
+ "@stateEmpty": {},
+ "placePageTitle": "Vietos",
+ "@placePageTitle": {},
+ "setHomeCustom": "Pritaikytas",
+ "@setHomeCustom": {},
+ "settingsVideoPlaybackPageTitle": "Atkūrimas",
+ "@settingsVideoPlaybackPageTitle": {},
+ "settingsVideoResumptionModeDialogTitle": "Tęsti atkūrimą",
+ "@settingsVideoResumptionModeDialogTitle": {},
+ "settingsVideoBackgroundModeDialogTitle": "Fono grojimas",
+ "@settingsVideoBackgroundModeDialogTitle": {},
+ "newVaultDialogTitle": "Nauja saugykla",
+ "@newVaultDialogTitle": {},
+ "exportEntryDialogWriteMetadata": "Įrašyti metainformaciją",
+ "@exportEntryDialogWriteMetadata": {},
+ "explorerActionSelectStorageVolume": "Pasirinkite saugyklą",
+ "@explorerActionSelectStorageVolume": {},
+ "stopTooltip": "Stabdyti",
+ "@stopTooltip": {},
+ "videoActionABRepeat": "A-B kartoti",
+ "@videoActionABRepeat": {},
+ "videoRepeatActionSetStart": "Nustatyti pradžią",
+ "@videoRepeatActionSetStart": {},
+ "viewerActionLock": "Užrakinti peržiūrą",
+ "@viewerActionLock": {},
+ "viewerActionUnlock": "Atrakinti peržiūrą",
+ "@viewerActionUnlock": {},
+ "cropAspectRatioSquare": "Kvadratas",
+ "@cropAspectRatioSquare": {},
+ "vaultLockTypePattern": "Piešinys",
+ "@vaultLockTypePattern": {},
+ "vaultLockTypePin": "PIN",
+ "@vaultLockTypePin": {},
+ "explorerPageTitle": "Naršyklė",
+ "@explorerPageTitle": {},
+ "settingsThumbnailShowHdrIcon": "Rodyti HDR ikoną",
+ "@settingsThumbnailShowHdrIcon": {},
+ "columnCount": "{count, plural, =1{{count} stulpelis} other{{count} stulpeliai}}",
+ "@columnCount": {
+ "placeholders": {
+ "count": {
+ "format": "decimalPattern"
+ }
+ }
+ },
+ "chipActionGoToExplorerPage": "Rodyti Naršyklėje",
+ "@chipActionGoToExplorerPage": {},
+ "videoRepeatActionSetEnd": "Nustatyti pabaigą",
+ "@videoRepeatActionSetEnd": {},
+ "keepScreenOnVideoPlayback": "Vaizdo įrašo atkūrimo metu",
+ "@keepScreenOnVideoPlayback": {},
+ "aboutDataUsageMisc": "Įvairūs",
+ "@aboutDataUsageMisc": {}
}
diff --git a/lib/l10n/app_vi.arb b/lib/l10n/app_vi.arb
index fbdf4200b..cb9f13382 100644
--- a/lib/l10n/app_vi.arb
+++ b/lib/l10n/app_vi.arb
@@ -1529,7 +1529,7 @@
"@chipActionGoToExplorerPage": {},
"explorerPageTitle": "Khám phá",
"@explorerPageTitle": {},
- "mapAttributionOsmData": "Dữ liệu bản đồ © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors",
+ "mapAttributionOsmData": "Dữ liệu bản đồ © [OpenStreetMap](https://www.openstreetmap.org/copyright) đóng góp",
"@mapAttributionOsmData": {},
"setHomeCustom": "Tùy chỉnh",
"@setHomeCustom": {},
diff --git a/lib/model/app/contributors.dart b/lib/model/app/contributors.dart
index 82d89088c..de0461704 100644
--- a/lib/model/app/contributors.dart
+++ b/lib/model/app/contributors.dart
@@ -103,6 +103,7 @@ class Contributors {
Contributor('Leo Aaua Felix', 'g00g7el@gmail.com'),
Contributor('-J-', 'heyj0e@tuta.io'),
Contributor('bittin1ddc447d824349b2', 'bittin@reimu.nl'),
+ Contributor('splice11', 'trenchedgrandpa@protonmail.com'),
// Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese
// Contributor('Khant', 'khant@users.noreply.hosted.weblate.org'), // Burmese
diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart
index 67302c862..e9e1ff4fa 100644
--- a/lib/model/settings/settings.dart
+++ b/lib/model/settings/settings.dart
@@ -483,6 +483,7 @@ class Settings with ChangeNotifier, SettingsAccess, AppSettings, DisplaySettings
case SettingKeys.collectionBurstPatternsKey:
case SettingKeys.pinnedFiltersKey:
case SettingKeys.hiddenFiltersKey:
+ case SettingKeys.deactivatedHiddenFiltersKey:
case SettingKeys.collectionBrowsingQuickActionsKey:
case SettingKeys.collectionSelectionQuickActionsKey:
case SettingKeys.viewerQuickActionsKey:
diff --git a/lib/widgets/common/action_mixins/permission_aware.dart b/lib/widgets/common/action_mixins/permission_aware.dart
index aea14b4a6..6aa7384b4 100644
--- a/lib/widgets/common/action_mixins/permission_aware.dart
+++ b/lib/widgets/common/action_mixins/permission_aware.dart
@@ -19,15 +19,15 @@ mixin PermissionAwareMixin {
Future checkStoragePermissionForAlbums(BuildContext context, Set storageDirs, {Set? entries}) async {
final restrictedDirsLowerCase = await storageService.getRestrictedDirectoriesLowerCase();
while (true) {
- final dirs = await storageService.getInaccessibleDirectories(storageDirs);
+ final inaccessibleDirs = await storageService.getInaccessibleDirectories(storageDirs);
- final restrictedInaccessibleDirs = dirs
+ final restrictedInaccessibleDirsLowerCase = inaccessibleDirs
.map((dir) => dir.copyWith(
relativeDir: dir.relativeDir.toLowerCase(),
))
.where(restrictedDirsLowerCase.contains)
.toSet();
- if (restrictedInaccessibleDirs.isNotEmpty) {
+ if (restrictedInaccessibleDirsLowerCase.isNotEmpty) {
if (entries != null && await storageService.canRequestMediaFileBulkAccess()) {
// request media file access for items in restricted directories
final uris = [], mimeTypes = [];
@@ -35,27 +35,27 @@ mixin PermissionAwareMixin {
final dirPath = entry.directory;
if (dirPath == null) return false;
final dir = androidFileUtils.relativeDirectoryFromPath(dirPath);
- return restrictedInaccessibleDirs.contains(dir?.copyWith(relativeDir: dir.relativeDir.toLowerCase()));
+ return restrictedInaccessibleDirsLowerCase.contains(dir?.copyWith(relativeDir: dir.relativeDir.toLowerCase()));
}).forEach((entry) {
uris.add(entry.uri);
mimeTypes.add(entry.mimeType);
});
final granted = await storageService.requestMediaFileAccess(uris, mimeTypes);
if (!granted) return false;
- } else if (entries == null && await storageService.canInsertMedia(restrictedInaccessibleDirs)) {
+ } else if (entries == null && await storageService.canInsertMedia(restrictedInaccessibleDirsLowerCase)) {
// insertion in restricted directories
} else {
// cannot proceed further
- await showRestrictedDirectoryDialog(context, restrictedInaccessibleDirs.first);
+ await showRestrictedDirectoryDialog(context, restrictedInaccessibleDirsLowerCase.first);
return false;
}
// clear restricted directories
- dirs.removeAll(restrictedInaccessibleDirs);
+ inaccessibleDirs.removeWhere((dir) => restrictedInaccessibleDirsLowerCase.contains(dir.copyWith(relativeDir: dir.relativeDir.toLowerCase())));
}
- if (dirs.isEmpty) return true;
+ if (inaccessibleDirs.isEmpty) return true;
- final dir = dirs.first;
+ final dir = inaccessibleDirs.first;
final confirmed = await showDialog(
context: context,
builder: (context) {
diff --git a/lib/widgets/navigation/drawer/app_drawer.dart b/lib/widgets/navigation/drawer/app_drawer.dart
index b00591e6d..d6197b44e 100644
--- a/lib/widgets/navigation/drawer/app_drawer.dart
+++ b/lib/widgets/navigation/drawer/app_drawer.dart
@@ -50,7 +50,7 @@ class AppDrawer extends StatefulWidget {
final source = context.read();
final specialAlbums = source.rawAlbums.where((album) {
final type = androidFileUtils.getAlbumType(album);
- return [AlbumType.camera, AlbumType.screenshots].contains(type);
+ return [AlbumType.camera, AlbumType.download, AlbumType.screenshots].contains(type);
}).toList()
..sort(source.compareAlbumsByName);
return specialAlbums;
diff --git a/pubspec.yaml b/pubspec.yaml
index 1f58052bf..1d8cd945c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -7,7 +7,7 @@ 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.11.15+134
+version: 1.11.16+135
publish_to: none
environment:
diff --git a/whatsnew/whatsnew-en-US b/whatsnew/whatsnew-en-US
index 6d1b2633e..3d9256a35 100644
--- a/whatsnew/whatsnew-en-US
+++ b/whatsnew/whatsnew-en-US
@@ -1,4 +1,4 @@
-In v1.11.15:
+In v1.11.16:
- enjoy new map layers
- share "geo" addresses to Aves and see your collection in that area
Full changelog available on GitHub
\ No newline at end of file