Merge branch 'develop'

This commit is contained in:
Thibault Deckers 2025-03-25 23:38:23 +01:00
commit 30f96334eb
31 changed files with 434 additions and 144 deletions

View file

@ -69,7 +69,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
@ -83,6 +83,6 @@ jobs:
./flutterw build apk --profile -t lib/main_play.dart --flavor play
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13
with:
category: "/language:${{matrix.language}}"

View file

@ -87,7 +87,7 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload app bundle
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: appbundle
path: outputs/app-play-release.aab
@ -106,7 +106,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Get appbundle from artifacts
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806 # v4.1.9
uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
with:
name: appbundle

View file

@ -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@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: SARIF file
path: results.sarif
@ -71,6 +71,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11
uses: github/codeql-action/upload-sarif@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13
with:
sarif_file: results.sarif

29
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,29 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "aves (main play)",
"request": "launch",
"type": "dart",
"program": "lib/main_play.dart",
"args": [
"--flavor",
"play"
]
},
{
"name": "aves (main play) [profile]",
"request": "launch",
"type": "dart",
"program": "lib/main_play.dart",
"args": [
"--flavor",
"play"
],
"flutterMode": "profile"
}
]
}

View file

@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
## <a id="unreleased"></a>[Unreleased]
## <a id="v1.12.8"></a>[v1.12.8] - 2025-03-25
### Fixed
- swiping images for some combinations of screen size, device pixel ratio, and image size
## <a id="v1.12.7"></a>[v1.12.7] - 2025-03-16
### Added

View file

@ -8,4 +8,5 @@
<string name="search_shortcut_short_label">搜尋</string>
<string name="analysis_channel_name">媒體掃描</string>
<string name="analysis_notification_action_stop">停止</string>
<string name="map_shortcut_short_label">地圖</string>
</resources>

View file

@ -1,3 +1,4 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
- provider: true

View file

@ -0,0 +1,4 @@
In v1.12.8:
- play more kinds of motion photos
- enjoy the app in Galician
Full changelog available on GitHub

View file

@ -0,0 +1,4 @@
In v1.12.8:
- play more kinds of motion photos
- enjoy the app in Galician
Full changelog available on GitHub

View file

@ -1196,9 +1196,9 @@
"@settingsNavigationDrawerAddAlbum": {},
"settingsThumbnailSectionTitle": "Миниатюри",
"@settingsThumbnailSectionTitle": {},
"settingsThumbnailOverlayTile": "Наслагване",
"settingsThumbnailOverlayTile": "Повече информация",
"@settingsThumbnailOverlayTile": {},
"settingsThumbnailOverlayPageTitle": "Наслагване",
"settingsThumbnailOverlayPageTitle": "Повече информация",
"@settingsThumbnailOverlayPageTitle": {},
"settingsThumbnailShowHdrIcon": "Показване на HDR икона",
"@settingsThumbnailShowHdrIcon": {},
@ -1258,7 +1258,7 @@
"@settingsViewerMaximumBrightness": {},
"settingsMotionPhotoAutoPlay": "Автоматично възпроизвеждане на снимки с движение",
"@settingsMotionPhotoAutoPlay": {},
"settingsViewerOverlayPageTitle": "Наслагване",
"settingsViewerOverlayPageTitle": "Повече информация",
"@settingsViewerOverlayPageTitle": {},
"settingsViewerShowHistogram": "Показвай хистограма",
"@settingsViewerShowHistogram": {},
@ -1272,7 +1272,7 @@
"@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {},
"settingsViewerQuickActionEmpty": "Без бутони",
"@settingsViewerQuickActionEmpty": {},
"settingsViewerOverlayTile": "Наслагване",
"settingsViewerOverlayTile": "Повече информация",
"@settingsViewerOverlayTile": {},
"settingsViewerShowRatingTags": "Показване на рейтинг и тагове",
"@settingsViewerShowRatingTags": {},

View file

@ -1598,5 +1598,15 @@
"newAlbumDialogAlbumAlreadyExistsHelper": "Albumul există deja",
"@newAlbumDialogAlbumAlreadyExistsHelper": {},
"mapAttributionOsmLiberty": "Plăci de la [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Găzduit de [OSM Americana](https://tile.ourmap.us)",
"@mapAttributionOsmLiberty": {}
"@mapAttributionOsmLiberty": {},
"mapStyleOpenTopoMap": "OpenTopoMap",
"@mapStyleOpenTopoMap": {},
"editEntryLocationDialogTimeShift": "Schimb de timp",
"@editEntryLocationDialogTimeShift": {},
"coordinateFormatDdm": "DDM",
"@coordinateFormatDdm": {},
"mapStyleOsmLiberty": "OSM Liberty",
"@mapStyleOsmLiberty": {},
"removeEntryMetadataDialogAll": "Toate",
"@removeEntryMetadataDialogAll": {}
}

View file

@ -1409,7 +1409,7 @@
"@dynamicAlbumAlreadyExists": {},
"chipActionDecompose": "分割",
"@chipActionDecompose": {},
"coordinateFormatDdm": "DDM",
"coordinateFormatDdm": "度分秒十进制坐标",
"@coordinateFormatDdm": {},
"editEntryLocationDialogImportGpx": "导入 GPX",
"@editEntryLocationDialogImportGpx": {},

View file

@ -1566,5 +1566,47 @@
"albumTierDynamic": "動態",
"@albumTierDynamic": {},
"chipActionRemove": "移除",
"@chipActionRemove": {}
"@chipActionRemove": {},
"editEntryLocationDialogImportGpx": "匯入 GPX",
"@editEntryLocationDialogImportGpx": {},
"editEntryLocationDialogTimeShift": "時光平移",
"@editEntryLocationDialogTimeShift": {},
"videoActionShowPreviousFrame": "顯示前一幀",
"@videoActionShowPreviousFrame": {},
"videoActionShowNextFrame": "顯示下一幀",
"@videoActionShowNextFrame": {},
"mapAttributionOsmLiberty": "地圖由 [OpenMapTiles](https://www.openmaptiles.org/) 所提供,以 [CC BY](http://creativecommons.org/licenses/by/4.0) 授權 • 托管於 [OSM Americana](https://tile.ourmap.us)",
"@mapAttributionOsmLiberty": {},
"newDynamicAlbumDialogTitle": "新動態相簿",
"@newDynamicAlbumDialogTitle": {},
"sortOrderShortestFirst": "先短後長",
"@sortOrderShortestFirst": {},
"appExportDynamicAlbums": "動態相簿",
"@appExportDynamicAlbums": {},
"coordinateFormatDdm": "度分十進制",
"@coordinateFormatDdm": {},
"newAlbumDialogAlbumAlreadyExistsHelper": "相簿已經存在",
"@newAlbumDialogAlbumAlreadyExistsHelper": {},
"dynamicAlbumAlreadyExists": "動態相簿已經存在",
"@dynamicAlbumAlreadyExists": {},
"collectionActionAddDynamicAlbum": "新增動態相簿",
"@collectionActionAddDynamicAlbum": {},
"sortOrderLongestFirst": "先長後短",
"@sortOrderLongestFirst": {},
"setHomeCustom": "自定義",
"@setHomeCustom": {},
"removeEntryMetadataDialogAll": "全部",
"@removeEntryMetadataDialogAll": {},
"explorerActionSelectStorageVolume": "選擇儲存位置",
"@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "選擇儲存位置",
"@selectStorageVolumeDialogTitle": {},
"mapStyleOsmLiberty": "OSM Liberty",
"@mapStyleOsmLiberty": {},
"mapStyleOpenTopoMap": "OpenTopoMap",
"@mapStyleOpenTopoMap": {},
"mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | 地圖由 [OpenTopoMap](https://opentopomap.org/),以 [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/) 授權",
"@mapAttributionOpenTopoMap": {},
"sortByDuration": "按時長",
"@sortByDuration": {}
}

View file

@ -1771,10 +1771,10 @@ class AppLocalizationsBg extends AppLocalizations {
String get settingsThumbnailSectionTitle => 'Миниатюри';
@override
String get settingsThumbnailOverlayTile => 'Наслагване';
String get settingsThumbnailOverlayTile => 'Повече информация';
@override
String get settingsThumbnailOverlayPageTitle => 'Наслагване';
String get settingsThumbnailOverlayPageTitle => 'Повече информация';
@override
String get settingsThumbnailShowHdrIcon => 'Показване на HDR икона';
@ -1861,10 +1861,10 @@ class AppLocalizationsBg extends AppLocalizations {
String get settingsViewerQuickActionEmpty => 'Без бутони';
@override
String get settingsViewerOverlayTile => 'Наслагване';
String get settingsViewerOverlayTile => 'Повече информация';
@override
String get settingsViewerOverlayPageTitle => 'Наслагване';
String get settingsViewerOverlayPageTitle => 'Повече информация';
@override
String get settingsViewerShowOverlayOnOpening => 'Показване при отваряне';

View file

@ -991,7 +991,7 @@ class AppLocalizationsRo extends AppLocalizations {
String get editEntryLocationDialogLongitude => 'Longitudine';
@override
String get editEntryLocationDialogTimeShift => 'Time shift';
String get editEntryLocationDialogTimeShift => 'Schimb de timp';
@override
String get locationPickerUseThisLocationButton => 'Utilizați această locație';
@ -1003,7 +1003,7 @@ class AppLocalizationsRo extends AppLocalizations {
String get removeEntryMetadataDialogTitle => 'Eliminarea metadatelor';
@override
String get removeEntryMetadataDialogAll => 'All';
String get removeEntryMetadataDialogAll => 'Toate';
@override
String get removeEntryMetadataDialogMore => 'Mai mult';

View file

@ -505,7 +505,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get coordinateFormatDms => 'DMS';
@override
String get coordinateFormatDdm => 'DDM';
String get coordinateFormatDdm => '度分秒十进制坐标';
@override
String get coordinateFormatDecimal => '十进制度';
@ -2669,6 +2669,12 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get videoActionSkip10 => '前進 10 秒';
@override
String get videoActionShowPreviousFrame => '顯示前一幀';
@override
String get videoActionShowNextFrame => '顯示下一幀';
@override
String get videoActionSelectStreams => '選擇音軌';
@ -2840,6 +2846,9 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get coordinateFormatDms => 'DMS';
@override
String get coordinateFormatDdm => '度分十進制';
@override
String get coordinateFormatDecimal => '十進制度數';
@ -2893,6 +2902,12 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get mapStyleGoogleTerrain => 'Google 地圖 (地形)';
@override
String get mapStyleOsmLiberty => 'OSM Liberty';
@override
String get mapStyleOpenTopoMap => 'OpenTopoMap';
@override
String get mapStyleOsmHot => 'Humanitarian OSM';
@ -3139,12 +3154,21 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get newAlbumDialogNameLabel => '相簿名稱';
@override
String get newAlbumDialogAlbumAlreadyExistsHelper => '相簿已經存在';
@override
String get newAlbumDialogNameLabelAlreadyExistsHelper => '目錄已存在';
@override
String get newAlbumDialogStorageLabel => '儲存空間:';
@override
String get newDynamicAlbumDialogTitle => '新動態相簿';
@override
String get dynamicAlbumAlreadyExists => '動態相簿已經存在';
@override
String get newVaultWarningDialogMessage => '保險庫中的項目僅供此應用使用,其他應用不可用。\n\n如果您卸載此應用程序或清除此應用程序數據,您將丟失所有這些項目。';
@ -3296,12 +3320,18 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get editEntryLocationDialogChooseOnMap => '從地圖上選擇';
@override
String get editEntryLocationDialogImportGpx => '匯入 GPX';
@override
String get editEntryLocationDialogLatitude => '緯度';
@override
String get editEntryLocationDialogLongitude => '經度';
@override
String get editEntryLocationDialogTimeShift => '時光平移';
@override
String get locationPickerUseThisLocationButton => '使用此座標';
@ -3311,6 +3341,9 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get removeEntryMetadataDialogTitle => '移除元資料';
@override
String get removeEntryMetadataDialogAll => '全部';
@override
String get removeEntryMetadataDialogMore => '更多';
@ -3512,6 +3545,9 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get collectionActionHideTitleSearch => '隱藏標題過濾器';
@override
String get collectionActionAddDynamicAlbum => '新增動態相簿';
@override
String get collectionActionAddShortcut => '新增捷徑';
@ -3741,6 +3777,9 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get sortByRating => '依評分';
@override
String get sortByDuration => '按時長';
@override
String get sortOrderNewestFirst => '由新至舊';
@ -3765,6 +3804,12 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get sortOrderSmallestFirst => '由小到大';
@override
String get sortOrderShortestFirst => '先短後長';
@override
String get sortOrderLongestFirst => '先長後短';
@override
String get albumGroupTier => '依層級';
@ -3849,6 +3894,12 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get explorerPageTitle => '檔案總管';
@override
String get explorerActionSelectStorageVolume => '選擇儲存位置';
@override
String get selectStorageVolumeDialogTitle => '選擇儲存位置';
@override
String get searchCollectionFieldHint => '搜尋收藏品';
@ -3918,6 +3969,9 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get appExportCovers => '封面';
@override
String get appExportDynamicAlbums => '動態相簿';
@override
String get appExportFavourites => '我的最愛';
@ -3933,6 +3987,9 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get settingsHomeDialogTitle => '主畫面';
@override
String get setHomeCustom => '自定義';
@override
String get settingsShowBottomNavigationBar => '顯示底部操作條';
@ -4487,6 +4544,12 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
@override
String get mapAttributionOsmData => '地圖資料由 © [OpenStreetMap](https://www.openstreetmap.org/copyright) 貢獻';
@override
String get mapAttributionOsmLiberty => '地圖由 [OpenMapTiles](https://www.openmaptiles.org/) 所提供,以 [CC BY](http://creativecommons.org/licenses/by/4.0) 授權 • 托管於 [OSM Americana](https://tile.ourmap.us)';
@override
String get mapAttributionOpenTopoMap => '[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | 地圖由 [OpenTopoMap](https://opentopomap.org/),以 [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/) 授權';
@override
String get mapAttributionOsmHot => '繪製於 [HOT](https://www.hotosm.org/) • 主辦方 [OSM France](https://openstreetmap.fr/)';

View file

@ -129,6 +129,7 @@ class Contributors {
Contributor('Josep M. Ferrer', 'txemaq@gmail.com'),
Contributor('pitroig', 'ona@riseup.net'),
Contributor('Rubén Castiñeiras Lorenzo', 'rcasl@outlook.com'),
Contributor('hanyang cheng', 'cinxiafortis@tutanota.de'),
// Contributor('Femini', 'nizamismidov4@gmail.com'), // Azerbaijani
// Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese

View file

@ -1,15 +1,13 @@
import 'package:sqflite/sqflite.dart';
extension ExtraDatabase on Database {
// check table existence
// proper way is to select from `sqlite_master` but this meta table may be missing on some devices
// so we rely on failure check instead
bool tableExists(String table) {
try {
query(table, limit: 1);
return true;
} catch (error) {
return false;
}
// check table existence via `sqlite_master`
// `sqlite_schema` is the alias used in SQLite documentation,
// but it was introduced in SQLite v3.33.0 and it is unavailable on Android API < 34,
// and the historical alias `sqlite_master` is still supported.
// cf https://www.sqlite.org/faq.html#q7
Future<bool> tableExists(String table) async {
final results = await query('sqlite_master', where: 'type = ? AND name = ?', whereArgs: ['table', table]);
return results.isNotEmpty;
}
}

View file

@ -62,7 +62,7 @@ class LocalMediaDbUpgrader {
static Future<void> _sanitize(Database db) async {
// ensure all tables exist
await Future.forEach(SqfliteLocalMediaDbSchema.allTables, (table) async {
if (!db.tableExists(table)) {
if (!(await db.tableExists(table))) {
await SqfliteLocalMediaDbSchema.createTable(db, table);
}
});
@ -464,7 +464,7 @@ class LocalMediaDbUpgrader {
static Future<void> _upgradeFrom13(Database db) async {
debugPrint('upgrading DB from v13');
if (db.tableExists(entryTable)) {
if (await db.tableExists(entryTable)) {
// rename column 'dateModifiedSecs' to 'dateModifiedMillis'
const newEntryTable = '${entryTable}TEMP';
await db.execute('CREATE TABLE $newEntryTable ('
@ -500,7 +500,7 @@ class LocalMediaDbUpgrader {
// so we clear rebuildable tables
final tables = [dateTakenTable, metadataTable, addressTable, trashTable, videoPlaybackTable];
await Future.forEach(tables, (table) async {
if (db.tableExists(table)) {
if (await db.tableExists(table)) {
await db.delete(table, where: '1');
}
});

View file

@ -58,7 +58,6 @@ class Themes {
cardColor: _schemeCardLayer(colors),
colorScheme: colors,
dividerColor: colors.outlineVariant,
indicatorColor: colors.primary,
scaffoldBackgroundColor: _schemeFirstLayer(colors),
// TYPOGRAPHY & ICONOGRAPHY
typography: _typography,
@ -75,6 +74,7 @@ class Themes {
),
radioTheme: _radioTheme(colors),
sliderTheme: _sliderTheme(colors),
tabBarTheme: TabBarThemeData(indicatorColor: colors.primary),
tooltipTheme: _tooltipTheme,
);
}
@ -183,7 +183,7 @@ class Themes {
titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor),
systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, _schemeFirstLayer(colors)) : null,
),
dialogTheme: DialogTheme(
dialogTheme: DialogThemeData(
backgroundColor: _schemeSecondLayer(colors),
titleTextStyle: _titleTextStyle.copyWith(color: _lightTitleColor),
),
@ -236,7 +236,7 @@ class Themes {
titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor),
systemOverlayStyle: deviceInitialized ? AvesApp.systemUIStyleForBrightness(colors.brightness, _schemeFirstLayer(colors)) : null,
),
dialogTheme: DialogTheme(
dialogTheme: DialogThemeData(
backgroundColor: _schemeSecondLayer(colors),
titleTextStyle: _titleTextStyle.copyWith(color: _darkTitleColor),
),

View file

@ -18,6 +18,7 @@ import 'package:aves/widgets/debug/capabilities.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/leaking.dart';
import 'package:aves/widgets/debug/media_store_scan_dialog.dart';
import 'package:aves/widgets/debug/os_apps.dart';
import 'package:aves/widgets/debug/os_codecs.dart';
@ -73,6 +74,7 @@ class AppDebugPage extends StatelessWidget {
padding: const EdgeInsets.all(8),
children: const [
DebugGeneralSection(),
DebugLeakingSection(),
DebugCacheSection(),
DebugCapabilitiesSection(),
DebugColorSection(),

View file

@ -1,14 +1,12 @@
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/services/common/service_policy.dart';
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
import 'package:aves/widgets/debug/overlay.dart';
import 'package:aves/widgets/settings/common/tiles.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 {
@ -31,6 +29,7 @@ class _DebugGeneralSectionState extends State<DebugGeneralSection> with Automati
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: [
@ -55,7 +54,7 @@ class _DebugGeneralSectionState extends State<DebugGeneralSection> with Automati
_taskQueueOverlayEntry = null;
if (v) {
_taskQueueOverlayEntry = OverlayEntry(
builder: (context) => const DebugTaskQueueOverlay(),
builder: (context) => const _TaskQueueOverlay(),
);
Overlay.of(context).insert(_taskQueueOverlayEntry!);
}
@ -68,46 +67,6 @@ class _DebugGeneralSectionState extends State<DebugGeneralSection> with Automati
onChanged: (v) => settings.debugShowViewerTiles = v,
title: 'Show viewer tiles',
),
ElevatedButton(
onPressed: () => LeakTracking.collectLeaks().then((leaks) {
const config = LeakDiagnosticConfig(
collectRetainingPathForNotGCed: true,
collectStackTraceOnStart: true,
collectStackTraceOnDisposal: true,
);
LeakTracking.phase = const PhaseSettings(
leakDiagnosticConfig: config,
);
debugPrint('Setup leak tracking phase with config=$config');
}),
child: const Text('Setup leak tracking phase'),
),
ElevatedButton(
onPressed: () => LeakTracking.collectLeaks().then((leaks) {
leaks.byType.forEach((type, reports) {
// ignore `notGCed` and `gcedLate` for now
if (type != LeakType.notDisposed) return;
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'),
@ -133,3 +92,45 @@ class _DebugGeneralSectionState extends State<DebugGeneralSection> with Automati
@override
bool get wantKeepAlive => true;
}
class _TaskQueueOverlay extends StatelessWidget {
const _TaskQueueOverlay();
@override
Widget build(BuildContext context) {
return IgnorePointer(
child: DefaultTextStyle(
style: const TextStyle(),
child: Align(
alignment: AlignmentDirectional.bottomStart,
child: SafeArea(
child: Container(
color: Colors.indigo.shade900.withAlpha(0xCC),
padding: const EdgeInsets.all(8),
child: StreamBuilder<QueueState>(
stream: servicePolicy.queueStream,
builder: (context, snapshot) {
if (snapshot.hasError) return const SizedBox();
final queuedEntries = <MapEntry<dynamic, int>>[];
if (snapshot.hasData) {
final state = snapshot.data!;
queuedEntries.add(MapEntry('run', state.runningCount));
queuedEntries.add(MapEntry('paused', state.pausedCount));
queuedEntries.addAll(state.queueByPriority.entries.map((kv) => MapEntry(kv.key.toString(), kv.value)));
}
queuedEntries.sort((a, b) => a.key.compareTo(b.key));
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(queuedEntries.map((kv) => '${kv.key}: ${kv.value}').join(', ')),
],
);
}),
),
),
),
),
);
}
}

View file

@ -0,0 +1,180 @@
import 'dart:io';
import 'package:aves/ref/locales.dart';
import 'package:aves/utils/file_utils.dart';
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:leak_tracker/leak_tracker.dart';
class DebugLeakingSection extends StatefulWidget {
const DebugLeakingSection({super.key});
@override
State<DebugLeakingSection> createState() => _DebugLeakingSectionState();
}
class _DebugLeakingSectionState extends State<DebugLeakingSection> with AutomaticKeepAliveClientMixin {
static OverlayEntry? _collectorOverlayEntry;
static const _leakIgnoreConfig = IgnoredLeaks(
experimentalNotGCed: IgnoredLeaksSet(),
notDisposed: IgnoredLeaksSet(),
);
@override
Widget build(BuildContext context) {
super.build(context);
return AvesExpansionTile(
title: 'Leaking',
children: [
SwitchListTile(
value: _collectorOverlayEntry != null,
onChanged: (v) {
_collectorOverlayEntry
?..remove()
..dispose();
_collectorOverlayEntry = null;
if (v) {
_collectorOverlayEntry = OverlayEntry(
builder: (context) => const _CollectorOverlay(),
);
Overlay.of(context).insert(_collectorOverlayEntry!);
}
setState(() {});
},
title: const Text('Show leak report overlay'),
),
ElevatedButton(
onPressed: () => LeakTracking.collectLeaks().then((leaks) {
LeakTracking.phase = const PhaseSettings(
ignoredLeaks: _leakIgnoreConfig,
leakDiagnosticConfig: LeakDiagnosticConfig(
collectRetainingPathForNotGCed: true,
collectStackTraceOnStart: true,
collectStackTraceOnDisposal: true,
),
);
}),
child: const Text('Track leaks with stacks'),
),
ElevatedButton(
onPressed: () => LeakTracking.collectLeaks().then((leaks) {
LeakTracking.phase = const PhaseSettings(
ignoredLeaks: _leakIgnoreConfig,
leakDiagnosticConfig: LeakDiagnosticConfig(
collectRetainingPathForNotGCed: true,
collectStackTraceOnStart: false,
collectStackTraceOnDisposal: false,
),
);
}),
child: const Text('Track leaks without stacks'),
),
],
);
}
@override
bool get wantKeepAlive => true;
}
class _CollectorOverlay extends StatefulWidget {
const _CollectorOverlay();
@override
State<_CollectorOverlay> createState() => _CollectorOverlayState();
}
class _CollectorOverlayState extends State<_CollectorOverlay> {
AlignmentGeometry _alignment = AlignmentDirectional.bottomStart;
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: const TextStyle(),
child: Align(
alignment: _alignment,
child: SafeArea(
child: Container(
color: Colors.indigo.shade900.withAlpha(0xCC),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
IconButton(
onPressed: () => setState(() => _alignment = _alignment == AlignmentDirectional.bottomStart ? AlignmentDirectional.topStart : AlignmentDirectional.bottomStart),
icon: Icon(_alignment == AlignmentDirectional.bottomStart ? Icons.vertical_align_top_outlined : Icons.vertical_align_bottom_outlined),
),
...LeakType.values.map((type) {
return OutlinedButton(
style: ButtonStyle(
padding: WidgetStateProperty.all(const EdgeInsets.all(6)),
minimumSize: WidgetStateProperty.all(Size.zero),
),
onPressed: () => LeakTracking.collectLeaks().then((leaks) {
final reports = leaks.byType[type] ?? [];
_printLeaks(type, reports);
}),
child: Text(type.name),
);
}),
],
),
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
StreamBuilder(
stream: Stream.periodic(const Duration(seconds: 1)),
builder: (context, snapshot) {
final currentRss = formatFileSize(asciiLocale, ProcessInfo.currentRss);
final maxRss = formatFileSize(asciiLocale, ProcessInfo.maxRss);
return Text('RSS: $currentRss / $maxRss');
},
),
],
),
Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
children: [
StreamBuilder(
stream: Stream.periodic(const Duration(seconds: 1)),
builder: (context, snapshot) {
final currentImageCache = formatFileSize(asciiLocale, imageCache.currentSizeBytes);
final maxImageCache = formatFileSize(asciiLocale, imageCache.maximumSizeBytes);
return Text('imageCache: $currentImageCache / $maxImageCache');
},
),
],
),
],
),
),
),
),
);
}
void _printLeaks(LeakType type, List<LeakReport> reports) {
debugPrint('* leak type=$type, ${reports.length} reports');
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');
}
});
});
});
}
}

View file

@ -1,44 +0,0 @@
import 'package:aves/services/common/service_policy.dart';
import 'package:flutter/material.dart';
class DebugTaskQueueOverlay extends StatelessWidget {
const DebugTaskQueueOverlay({super.key});
@override
Widget build(BuildContext context) {
return IgnorePointer(
child: DefaultTextStyle(
style: const TextStyle(),
child: Align(
alignment: AlignmentDirectional.bottomStart,
child: SafeArea(
child: Container(
color: Colors.indigo.shade900.withAlpha(0xCC),
padding: const EdgeInsets.all(8),
child: StreamBuilder<QueueState>(
stream: servicePolicy.queueStream,
builder: (context, snapshot) {
if (snapshot.hasError) return const SizedBox();
final queuedEntries = <MapEntry<dynamic, int>>[];
if (snapshot.hasData) {
final state = snapshot.data!;
queuedEntries.add(MapEntry('run', state.runningCount));
queuedEntries.add(MapEntry('paused', state.pausedCount));
queuedEntries.addAll(state.queueByPriority.entries.map((kv) => MapEntry(kv.key.toString(), kv.value)));
}
queuedEntries.sort((a, b) => a.key.compareTo(b.key));
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(queuedEntries.map((kv) => '${kv.key}: ${kv.value}').join(', ')),
],
);
}),
),
),
),
),
);
}
}

View file

@ -67,7 +67,7 @@ class _EditEntryLocationDialogState extends State<EditEntryLocationDialog> with
final TextEditingController _latitudeController = TextEditingController(), _longitudeController = TextEditingController();
final ValueNotifier<bool> _isValidNotifier = ValueNotifier(false);
NumberFormat get coordinateFormatter => NumberFormat('0.000000', context.locale);
late NumberFormat coordinateFormatter;
static const _minTimeToGpxPoint = Duration(hours: 1);
@override
@ -75,22 +75,10 @@ class _EditEntryLocationDialogState extends State<EditEntryLocationDialog> with
super.initState();
final entries = widget.entries;
mainEntry = entries.firstWhereOrNull((entry) => entry.hasGps) ?? entries.first;
_initMapCoordinates();
_initCopyItem();
_initCustom();
AvesApp.intentEventBus.on<LocationReceivedEvent>().listen((event) => _setCustomLocation(event.location));
}
void _initMapCoordinates() {
_mapCoordinates = mainEntry.latLng;
}
void _initCopyItem() {
_copyItemSource = mainEntry;
}
void _initCustom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
coordinateFormatter = NumberFormat('0.000000', context.locale);
final latLng = mainEntry.latLng;
if (latLng != null) {
_latitudeController.text = coordinateFormatter.format(latLng.latitude);
@ -101,6 +89,7 @@ class _EditEntryLocationDialogState extends State<EditEntryLocationDialog> with
}
setState(_validate);
});
_subscriptions.add(AvesApp.intentEventBus.on<LocationReceivedEvent>().listen((event) => _setCustomLocation(event.location)));
}
@override

View file

@ -274,7 +274,7 @@ class _HomePageState extends State<HomePage> {
));
} catch (error, stack) {
debugPrint('failed to setup app with error=$error\n$stack');
_setupError = (error, stack);
setState(() => _setupError = (error, stack));
}
}

View file

@ -1,5 +1,6 @@
import 'package:aves_magnifier/src/controller/controller_delegate.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
mixin EdgeHitDetector on AvesMagnifierControllerDelegate {
@ -15,7 +16,7 @@ mixin EdgeHitDetector on AvesMagnifierControllerDelegate {
final x = -position.dx;
final range = _boundaries.getXEdges(scale: _scale);
return EdgeHit(x <= range.min, x >= range.max);
return EdgeHit(x <= range.min + precisionErrorTolerance, x >= range.max - precisionErrorTolerance);
}
EdgeHit getYEdgeHit() {
@ -25,7 +26,7 @@ mixin EdgeHitDetector on AvesMagnifierControllerDelegate {
final y = -position.dy;
final range = _boundaries.getYEdges(scale: _scale);
return EdgeHit(y <= range.min, y >= range.max);
return EdgeHit(y <= range.min + precisionErrorTolerance, y >= range.max - precisionErrorTolerance);
}
bool shouldMoveX(Offset move, bool canFling) {

View file

@ -6,6 +6,7 @@ gradle-wrapper.jar
/local.properties
GeneratedPluginRegistrant.java
.cxx/
.kotlin/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore

View file

@ -6,6 +6,7 @@ gradle-wrapper.jar
/local.properties
GeneratedPluginRegistrant.java
.cxx/
.kotlin/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore

View file

@ -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.12.7+147
version: 1.12.8+148
publish_to: none
environment:

View file

@ -1,4 +1,4 @@
In v1.12.7:
In v1.12.8:
- play more kinds of motion photos
- enjoy the app in Galician
Full changelog available on GitHub