Doable 1.17.2 - redistribution notices Original developer: Florian Obernberger Source repository: https://codeberg.org/florian-obernberger/flutter-doable Source tag: v1.17.2 Tag object: 6e9ddb12053d1d913e6e48c873fb05b31e9b913e Declared license: MIT Corresponding source retained: no (not required by this permissive license) APK redistributed without modification. Store media is normalized without cropping. FILE: flutter-doable/LICENSE MIT License Copyright (c) 2022-2024 Florian Obernberger Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. FILE: flutter-doable/CONTRIBUTORS # This is a list of contributors to Doable that would like to # be known by name and have a place in the app. # # If you want to be seen here, please either open an issue about # it or just create a pull request! # # The format of this document is as follows: # # (role[s]) full name # # The `link` element can be any link, email address or phone number. # # The `role[s]` represent your role(s) within this project. # If you have multiple roles, please separate them by a comma and space. # # Here are some examples: # # (Owner) Florian Obernberger # # (Translator, Helper) Max Mustermann # (Owner) Florian Obernberger (Translator) Mondstern (Translator) Andrés Blasco Arnáiz (Translator) Luna (Translator) Eryk Michalak (Translator) Heimen Stoffels (Translator) RavingSombra (Translator) gallegonovato (Translator) alextecplayz (Translator) WesK (Translator) psw1747 (Translator) Sneakyfish (Translator) SomeTr (Translator) Kjev (Translator) reindex (Translator) gamenarkyt (Translator) cultcats (Translator) 0ko (Translator) Dirk (Translator) oakratel (Translator) Outbreak2096 (Translator) Grooty (Helper) CollateralDamage FILE: flutter-doable/packages/like_button/LICENSE MIT License Copyright (c) 2019 zmtzawqlp Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. FILE: flutter-doable/lib/util/contributors_decode.dart import 'package:result/result.dart'; class Contributor implements Comparable { final String name; final String link; final List roles; const Contributor({ required this.name, required this.link, required this.roles, }); static Option fromString(String source) { source = source.trim(); final roles = []; String name; String link; try { if (source.startsWith('(')) { roles.addAll(source .substring(1, source.indexOf(')')) .trim() .split(RegExp(r', |,'))); source = source.replaceFirst(RegExp(r'\(.*?\)'), ''); } link = RegExp('<(.*?)>').firstMatch(source)!.group(1)!.trim(); source = source.replaceAll('<$link>', ''); name = source.trim(); } on Exception catch (_) { return const None(); } return Some(Contributor(name: name, link: link, roles: roles)); } static List fromFile(String source) { final contributors = []; for (final line in source.split('\n')) { if (line.startsWith('#')) continue; if (line.isEmpty) continue; if (Contributor.fromString(line) case Some(value: final contributor)) { contributors.add(contributor); } } return contributors; } @override String toString() => '(${roles.join(', ')}) $name <$link>'; @override int compareTo(Contributor other) { int compare = name.toLowerCase().compareTo(other.name.toLowerCase()); if (compare != 0) return compare; compare = link.compareTo(other.link); if (compare != 0) return compare; return roles.length.compareTo(other.roles.length); } } FILE: flutter-doable/lib/pages/settings_view.d/licenses_view.dart import 'package:flutter/material.dart'; import 'package:material_symbols_icons/symbols.dart'; import '/strings/strings.dart'; import '/resources/oss_licenses.dart'; import '/widgets/search.dart' as search; import '/widgets/back_button.dart' as back; import 'licenses_view.d/item.dart'; import 'licenses_view.d/search.dart'; final packages = ossLicenses.where((p) => p.isDirectDependency).toList() ..sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase())); class SettingsLicensesView extends StatelessWidget { const SettingsLicensesView({ this.showAppBar = true, this.onSelected, this.onSearch, super.key, }); final bool showAppBar; final void Function(Package package)? onSelected; final VoidCallback? onSearch; @override Widget build(BuildContext context) { final strings = Strings.of(context)!; final scheme = Theme.of(context).colorScheme; final text = Theme.of(context).textTheme; final searchDelegate = LicensesSearchDelegate( packages: packages, searchFieldStyle: text.bodyLarge?.copyWith(color: scheme.onSurface), ); return Scaffold( appBar: showAppBar ? AppBar( title: Text(strings.licenses), leading: const back.BackButton(), actions: [ IconButton( onPressed: () { if (onSearch != null) { onSearch!(); } else { search.showSearch( context: context, delegate: searchDelegate, ); } }, icon: const Icon(Symbols.search, fill: 0), ), ], ) : null, body: Scrollbar( interactive: true, child: ListView.builder( padding: const EdgeInsets.all(12).add( EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), ), primary: true, physics: const ClampingScrollPhysics(), itemCount: packages.length, cacheExtent: packages.length * 2, shrinkWrap: false, itemBuilder: (context, i) => LicenseItem( packages[i], onSelected: onSelected, ), ), ), ); } } FILE: flutter-doable/lib/pages/settings_view.d/license_detail_view.dart import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import '/widgets/link.dart'; import '/widgets/nav_button.dart'; import '/widgets/markdown_text.dart'; import '/util/build_text_selection_menu.dart'; import '/resources/oss_licenses.dart'; import 'licenses_view.d/dev_icon.dart'; class LicenseDetailView extends StatelessWidget { const LicenseDetailView( this.package, { this.onBack, this.isDialog = false, this.showAppBar = true, super.key, }); final Package package; final VoidCallback? onBack; final bool isDialog; final bool showAppBar; @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; final text = Theme.of(context).textTheme; final style = text.bodyMedium?.copyWith(color: scheme.onSurfaceVariant); return Scaffold( appBar: showAppBar ? AppBar( title: Row( children: [ if (package.isDevDependency) ...[ const DevIcon(), const Gap(4) ], Expanded(child: Text(package.name.trim())), ], ), leading: NavButton( type: isDialog ? NavButtonType.close : NavButtonType.back, onNav: onBack, ), ) : null, body: ListView( padding: const EdgeInsets.all(16).add( EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), ), physics: const ClampingScrollPhysics(), children: [ MarkdownText( package.description.trim(), baseStyle: style, softWrap: true, ), if (package.homepage != null) ...[ const Divider(height: 32), Link( url: package.homepage!, baseStyle: style!, showIcon: true, maxLines: 2, ), ], if (package.license != null) ...[ const Divider(height: 32), SelectableText( package.license!.trim(), contextMenuBuilder: buildTextSelectionMenu, style: style, ) ], ], ), ); } }